@ghl-ai/aw 0.1.72 → 0.1.73-beta.0

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.
@@ -129,29 +129,30 @@ aw_c4_exit=$?
129
129
 
130
130
  # Defense-in-depth: `aw c4` delegates registry sync to `aw init --silent`,
131
131
  # which can fail to fetch in silent mode without surfacing an error. When
132
- # that happens, the registry (~/.aw/.aw_registry) may be empty or incomplete.
133
- # Re-run `aw pull` post-c4 if the skill surface looks too small.
134
- verify_registry_skills() {
135
- local aw_registry_dir="$HOME/.aw/.aw_registry"
136
- if [ ! -d "$aw_registry_dir" ]; then
132
+ # that happens, the registry (~/.aw/.aw_registry) may be empty or incomplete
133
+ # and only the 10 hardcoded stage commands get linked. Re-run `aw pull`
134
+ # post-c4 if the command count looks low.
135
+ verify_registry_commands() {
136
+ local aw_cmd_dir="$HOME/.aw/.aw_registry"
137
+ if [ ! -d "$aw_cmd_dir" ]; then
137
138
  echo "[aw-c4-bootstrap] registry dir missing — running aw init + pull"
138
139
  aw init --no-integrations --silent 2>&1 | tail -3 || true
139
140
  aw pull 2>&1 | tail -5 || true
140
141
  return
141
142
  fi
142
143
 
143
- local skill_count
144
- skill_count=$(find "$aw_registry_dir" -name 'SKILL.md' -path '*/skills/*' ! -path '*/evals/*' 2>/dev/null | wc -l | tr -d ' ')
145
- if [ "${skill_count:-0}" -lt 5 ]; then
146
- echo "[aw-c4-bootstrap] only ${skill_count} registry skills found — running aw pull"
144
+ local cmd_count
145
+ cmd_count=$(find "$aw_cmd_dir" -name '*.md' -path '*/commands/*' ! -path '*/evals/*' 2>/dev/null | wc -l | tr -d ' ')
146
+ if [ "${cmd_count:-0}" -lt 20 ]; then
147
+ echo "[aw-c4-bootstrap] only ${cmd_count} registry commands found — running aw pull"
147
148
  aw pull 2>&1 | tail -5 || true
148
149
  local new_count
149
- new_count=$(find "$aw_registry_dir" -name 'SKILL.md' -path '*/skills/*' ! -path '*/evals/*' 2>/dev/null | wc -l | tr -d ' ')
150
- echo "[aw-c4-bootstrap] registry skills: ${skill_count} → ${new_count}"
150
+ new_count=$(find "$aw_cmd_dir" -name '*.md' -path '*/commands/*' ! -path '*/evals/*' 2>/dev/null | wc -l | tr -d ' ')
151
+ echo "[aw-c4-bootstrap] registry commands: ${cmd_count} → ${new_count}"
151
152
  else
152
- echo "[aw-c4-bootstrap] registry: ${skill_count} skills OK"
153
+ echo "[aw-c4-bootstrap] registry: ${cmd_count} commands OK"
153
154
  fi
154
155
  }
155
- verify_registry_skills || true
156
+ verify_registry_commands || true
156
157
 
157
158
  exit "$aw_c4_exit"
package/commands/c4.mjs CHANGED
@@ -110,11 +110,6 @@ function safeListNamespaceDirs(dir) {
110
110
  } catch { return []; }
111
111
  }
112
112
 
113
- function hasRegistrySkillSurface(registryDir, existsSync = fsExistsSync) {
114
- if (existsSync(join(registryDir, 'platform', 'core', 'skills'))) return true;
115
- return safeListNamespaceDirs(registryDir).length > 0;
116
- }
117
-
118
113
  async function safeAsync(label, fn, writer) {
119
114
  try {
120
115
  return { ok: true, value: await fn() };
@@ -410,17 +405,18 @@ export async function c4Command(rawArgs, overrides = {}) {
410
405
  return exit(1);
411
406
  }
412
407
 
413
- // Step 9a — verify registry has the skill surface. `aw init --silent`
408
+ // Step 9a — verify registry has namespace directories. `aw init --silent`
414
409
  // can exit 0 but fail to fetch (fetchAndMerge swallows the error in silent
415
410
  // mode). Metadata-only artifacts (AW-PROTOCOL.md) pass a simple non-empty
416
- // check, so we specifically look for the platform skill namespace to confirm
417
- // the registry content was actually pulled.
411
+ // check, so we specifically look for directories (namespaces like
412
+ // "platform") to confirm the registry content was actually pulled.
418
413
  {
419
- if (!hasRegistrySkillSurface(awRegistry, fs.existsSync)) {
420
- writer.stderr('[aw-c4] registry has no platform skill surface after init — retrying with aw pull\n');
414
+ const registryNamespaces = safeListNamespaceDirs(awRegistry);
415
+ if (registryNamespaces.length === 0) {
416
+ writer.stderr('[aw-c4] registry has no namespace directories after init — retrying with aw pull\n');
421
417
  const pullRes = spawnSync('aw', ['pull'], { stdio: 'pipe' });
422
- if (pullRes?.status !== 0 || !hasRegistrySkillSurface(awRegistry, fs.existsSync)) {
423
- writer.stderr('[aw-c4] FATAL: registry skills were not fetched\n');
418
+ if (pullRes?.status !== 0 || safeListNamespaceDirs(awRegistry).length === 0) {
419
+ writer.stderr('[aw-c4] FATAL: registry commands were not fetched\n');
424
420
  return exit(1);
425
421
  }
426
422
  }
@@ -443,7 +439,7 @@ export async function c4Command(rawArgs, overrides = {}) {
443
439
  writer.stdout('[aw-c4] MCP disabled; skipping registerGhlAiMcp\n');
444
440
  }
445
441
 
446
- // Step 12 — legacy slash command surface, when provided by the installed ECC.
442
+ // Step 12 — slash command surface (10 stage commands from ECC).
447
443
  safe('ensureCommandSurface', () => c4.ensureCommandSurface({ harness, home, eccHome }), writer);
448
444
 
449
445
  // Step 12a — full registry command surface.
package/commands/init.mjs CHANGED
@@ -25,6 +25,7 @@ import * as fmt from '../fmt.mjs';
25
25
  import { chalk, setSilent } from '../fmt.mjs';
26
26
  import { linkWorkspace } from '../link.mjs';
27
27
  import { generateCommands, copyInstructions, initAwDocs, syncHomeHarnessInstructions } from '../integrate.mjs';
28
+ import { renderRules } from '../render-rules.mjs';
28
29
  import { setupMcp } from '../mcp.mjs';
29
30
  import { isContextModeRequested } from '../integrations/context-mode.mjs';
30
31
  import { applyStoredStartupPreferences, ensureAwRuntimeHook, isDefaultRoutingEnabled } from '../startup.mjs';
@@ -150,6 +151,11 @@ function syncHomeAndProjectInstructions(cwd, namespace) {
150
151
  initAwDocs(HOME);
151
152
  if (cwd !== HOME) {
152
153
  syncInstructionsAndAwDocs(cwd, namespace);
154
+ } else {
155
+ // Running from $HOME (fresh-laptop flow): render global IDE rules directly.
156
+ // The project branch above is otherwise the only renderRules call site, so
157
+ // init from $HOME used to leave ~/.claude/rules and ~/.cursor/rules empty.
158
+ renderRules(HOME, { homeDir: HOME });
153
159
  }
154
160
  }
155
161
 
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.67-beta.0";
15
+ export const AW_ECC_TAG = "v1.4.66";
16
16
  const REQUIRED_ECC_FILES = [
17
17
  "package.json",
18
18
  "scripts/install-apply.js",
package/git.mjs CHANGED
@@ -390,7 +390,8 @@ export async function fetchAndMerge(awHome, { silent = true } = {}) {
390
390
  // drops bare-name patterns (e.g. "content", "CODEOWNERS") when HEAD advances.
391
391
  //
392
392
  // --autostash: AW writes into the registry working tree from external
393
- // sources (transformCursorAwRefs rewrites /aw: → /aw- through Cursor
393
+ // sources (ensureAwRuntimeHook copies ~/.aw-ecc/.../session-start.sh into a
394
+ // tracked path; transformCursorAwRefs rewrites /aw: → /aw- through Cursor
394
395
  // skill directory symlinks that resolve into .aw_registry/). When those
395
396
  // versions drift, the working tree is dirty at rebase time and rebase
396
397
  // refuses to run, silently aborting the entire pull. Autostash stashes the
@@ -119,6 +119,11 @@ function getAwCliVersionDetails() {
119
119
  const candidates = [
120
120
  path.join(AW_HOME, 'node_modules', '@ghl-ai', 'aw', 'package.json'),
121
121
  ];
122
+ // Derive global npm prefix from the running node binary (no shell needed)
123
+ try {
124
+ const nodeDir = path.dirname(process.execPath);
125
+ candidates.push(path.join(nodeDir, '..', 'lib', 'node_modules', '@ghl-ai', 'aw', 'package.json'));
126
+ } catch { /* ignore */ }
122
127
  try {
123
128
  const globalPrefix = execSync('npm prefix -g', { encoding: 'utf8', timeout: 3000 }).trim();
124
129
  candidates.push(path.join(globalPrefix, 'lib', 'node_modules', '@ghl-ai', 'aw', 'package.json'));
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.72",
4
- "description": "Agentic Workspace CLI pull, push & manage agents, skills and commands from the registry",
3
+ "version": "0.1.73-beta.0",
4
+ "description": "Agentic Workspace CLI \u2014 pull, push & manage agents, skills and commands from the registry",
5
5
  "type": "module",
6
6
  "bin": {
7
7
  "aw": "bin.js"
@@ -74,4 +74,4 @@
74
74
  "devDependencies": {
75
75
  "vitest": "^4.1.2"
76
76
  }
77
- }
77
+ }
package/startup.mjs CHANGED
@@ -1,4 +1,4 @@
1
- import { existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
1
+ import { chmodSync, existsSync, mkdirSync, readFileSync, readdirSync, renameSync, rmSync, writeFileSync } from 'node:fs';
2
2
  import { dirname, join } from 'node:path';
3
3
  import { homedir } from 'node:os';
4
4
  import { randomBytes } from 'node:crypto';
@@ -83,6 +83,10 @@ function resolveRegistryRoot(homeDir = homedir()) {
83
83
  ].find(existsSync) || null;
84
84
  }
85
85
 
86
+ function awRuntimeHookSourcePath(homeDir = homedir()) {
87
+ return join(homeDir, '.aw-ecc', 'skills', 'using-aw-skills', 'hooks', 'session-start.sh');
88
+ }
89
+
86
90
  function readJson(filePath, fallback = {}) {
87
91
  if (!existsSync(filePath)) return fallback;
88
92
  try {
@@ -389,10 +393,27 @@ function hasCodexSessionStartScript(homeDir = homedir()) {
389
393
  }
390
394
 
391
395
  export function ensureAwRuntimeHook(homeDir = homedir()) {
392
- resolveRegistryRoot(homeDir);
393
- // The AW router hook is registry-owned. aw-ecc no longer ships SDLC skills
394
- // or using-aw-skills, so init/pull must not recreate it from ~/.aw-ecc.
395
- return [];
396
+ const sourcePath = awRuntimeHookSourcePath(homeDir);
397
+ const registryRoot = resolveRegistryRoot(homeDir);
398
+ if (!existsSync(sourcePath) || !registryRoot) return [];
399
+
400
+ const destinationPath = join(
401
+ registryRoot,
402
+ 'platform',
403
+ 'core',
404
+ 'skills',
405
+ 'using-aw-skills',
406
+ 'hooks',
407
+ 'session-start.sh',
408
+ );
409
+ const sourceContent = readFileSync(sourcePath, 'utf8');
410
+ const existingContent = existsSync(destinationPath) ? readFileSync(destinationPath, 'utf8') : null;
411
+ if (existingContent === sourceContent) return [];
412
+
413
+ mkdirSync(dirname(destinationPath), { recursive: true });
414
+ writeFileSync(destinationPath, sourceContent);
415
+ try { chmodSync(destinationPath, 0o755); } catch { /* best effort */ }
416
+ return [destinationPath];
396
417
  }
397
418
 
398
419
  function hasCodexHooksEnabled(homeDir = homedir()) {