@ghl-ai/aw 0.1.39-beta.0 → 0.1.39-beta.2

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/commands/init.mjs CHANGED
@@ -47,6 +47,23 @@ import { syncFileTree } from '../file-tree.mjs';
47
47
 
48
48
  const __dirname = dirname(fileURLToPath(import.meta.url));
49
49
  const VERSION = JSON.parse(readFileSync(join(__dirname, '..', 'package.json'), 'utf8')).version;
50
+ const DEBUG_INIT_LOG = process.env.AW_DEBUG_INIT_LOG;
51
+
52
+ function debugInit(message) {
53
+ if (!DEBUG_INIT_LOG) return;
54
+ try {
55
+ appendFileSync(DEBUG_INIT_LOG, `${new Date().toISOString()} ${message}\n`);
56
+ } catch { /* best effort */ }
57
+ }
58
+
59
+ function createSpinner(enabled) {
60
+ if (enabled) return fmt.spinner();
61
+ return {
62
+ start() {},
63
+ stop() {},
64
+ message() {},
65
+ };
66
+ }
50
67
 
51
68
  // Resolve HOME to the real path — on macOS /var is a symlink to /private/var,
52
69
  // so homedir() returns /var/... while process.cwd() returns /private/var/...
@@ -121,8 +138,12 @@ export async function initCommand(args) {
121
138
  let namespace = args['--namespace'] || null;
122
139
  let user = args['--user'] || '';
123
140
  const silent = args['--silent'] === true;
141
+ const interactiveUi = !silent && Boolean(process.stdout.isTTY);
124
142
 
125
- fmt.intro(`aw init ${chalk.dim('v' + VERSION)}`);
143
+ if (interactiveUi) {
144
+ fmt.intro(`aw init ${chalk.dim('v' + VERSION)}`);
145
+ }
146
+ debugInit('init:start');
126
147
 
127
148
  // ── Validate ──────────────────────────────────────────────────────────
128
149
 
@@ -170,7 +191,7 @@ export async function initCommand(args) {
170
191
 
171
192
  let namespaceExistsInRemote = false;
172
193
  if (folderName && !silent && !isGitNative && !isLegacy) {
173
- const probeSpinner = fmt.spinner();
194
+ const probeSpinner = createSpinner(interactiveUi);
174
195
  probeSpinner.start(`Checking namespace ${chalk.cyan(folderName)} in registry...`);
175
196
  try {
176
197
  const probePaths = includeToSparsePaths([folderName]);
@@ -183,11 +204,15 @@ export async function initCommand(args) {
183
204
  } finally {
184
205
  cleanup(probeDir);
185
206
  }
186
- probeSpinner.stop(namespaceExistsInRemote
187
- ? `Namespace ${chalk.cyan(folderName)} found in registry`
188
- : `Namespace ${chalk.cyan(folderName)} not yet in registry`);
207
+ if (interactiveUi) {
208
+ probeSpinner.stop(namespaceExistsInRemote
209
+ ? `Namespace ${chalk.cyan(folderName)} found in registry`
210
+ : `Namespace ${chalk.cyan(folderName)} not yet in registry`);
211
+ }
189
212
  } catch {
190
- probeSpinner.stop(chalk.dim('Could not verify namespace (continuing)'));
213
+ if (interactiveUi) {
214
+ probeSpinner.stop(chalk.dim('Could not verify namespace (continuing)'));
215
+ }
191
216
  namespaceExistsInRemote = true;
192
217
  }
193
218
  }
@@ -229,7 +254,7 @@ export async function initCommand(args) {
229
254
  if (!silent) fmt.logStep('Already initialized — syncing...');
230
255
  }
231
256
 
232
- const s = fmt.spinner();
257
+ const s = createSpinner(interactiveUi);
233
258
  if (!silent) s.start('Fetching latest from registry...');
234
259
  try {
235
260
  const { conflicts } = await fetchAndMerge(AW_HOME, { silent });
@@ -338,21 +363,26 @@ export async function initCommand(args) {
338
363
  sparsePaths.push(`.aw_registry/${folderName}`);
339
364
  }
340
365
 
341
- fmt.note([
342
- `${chalk.dim('source:')} ~/.aw/ → ~/.aw_registry/ (symlink)`,
343
- folderName ? `${chalk.dim('namespace:')} ${folderName}` : `${chalk.dim('namespace:')} ${chalk.dim('none')}`,
344
- user ? `${chalk.dim('user:')} ${user}` : null,
345
- `${chalk.dim('version:')} v${VERSION}`,
346
- ].filter(Boolean).join('\n'), 'Config');
366
+ if (interactiveUi) {
367
+ fmt.note([
368
+ `${chalk.dim('source:')} ~/.aw/ ~/.aw_registry/ (symlink)`,
369
+ folderName ? `${chalk.dim('namespace:')} ${folderName}` : `${chalk.dim('namespace:')} ${chalk.dim('none')}`,
370
+ user ? `${chalk.dim('user:')} ${user}` : null,
371
+ `${chalk.dim('version:')} v${VERSION}`,
372
+ ].filter(Boolean).join('\n'), 'Config');
373
+ }
347
374
 
348
- const s = fmt.spinner();
375
+ const s = createSpinner(interactiveUi);
349
376
  s.start(`Cloning registry...`);
350
377
 
351
378
  try {
379
+ debugInit(`clone:start repo=${repoUrl}`);
352
380
  await initPersistentClone(repoUrl, AW_HOME, sparsePaths);
381
+ debugInit('clone:done');
353
382
  ensureAwGitignore(AW_HOME);
354
383
  s.stop('Registry cloned');
355
384
  } catch (e) {
385
+ debugInit(`clone:error ${e.message}`);
356
386
  s.stop(chalk.red('Clone failed'));
357
387
  fmt.cancel(e.message);
358
388
  }
@@ -363,38 +393,55 @@ export async function initCommand(args) {
363
393
  try { awRegistryLstat = lstatSync(GLOBAL_AW_DIR); } catch { /* doesn't exist */ }
364
394
  if (!awRegistryLstat) {
365
395
  try {
396
+ debugInit('symlink:create:start');
366
397
  symlinkSync(join(AW_HOME, REGISTRY_DIR), GLOBAL_AW_DIR);
398
+ debugInit('symlink:create:done');
367
399
  fmt.logStep('Created ~/.aw_registry/ symlink');
368
400
  } catch (e) {
401
+ debugInit(`symlink:create:error ${e.message}`);
369
402
  fmt.logWarn(`Could not create symlink ~/.aw_registry/: ${e.message}`);
370
403
  }
371
404
  } else if (awRegistryLstat.isSymbolicLink()) {
372
405
  // Stale or dangling — re-point to the new clone
373
406
  try {
407
+ debugInit('symlink:update:start');
374
408
  rmSync(GLOBAL_AW_DIR);
375
409
  symlinkSync(join(AW_HOME, REGISTRY_DIR), GLOBAL_AW_DIR);
410
+ debugInit('symlink:update:done');
376
411
  fmt.logStep('Updated ~/.aw_registry/ symlink');
377
412
  } catch (e) {
413
+ debugInit(`symlink:update:error ${e.message}`);
378
414
  fmt.logWarn(`Could not update symlink ~/.aw_registry/: ${e.message}`);
379
415
  }
380
416
  }
381
417
 
382
418
  // Create sync config — default to 'platform' when no namespace specified
383
419
  const cfg = config.create(GLOBAL_AW_DIR, { namespace: team || 'platform', user });
420
+ debugInit('config:create:done');
384
421
  if (folderName) {
385
422
  config.addPattern(GLOBAL_AW_DIR, folderName);
423
+ debugInit('config:add-pattern:done');
386
424
  }
387
425
  if (existsSync(GLOBAL_AW_DIR)) {
426
+ debugInit('sync-file-tree:start');
388
427
  syncFileTree(join(AW_HOME, RULES_SOURCE_DIR), join(GLOBAL_AW_DIR, RULES_SOURCE_DIR));
428
+ debugInit('sync-file-tree:done');
389
429
  }
390
430
 
391
431
  // Step 3: Setup tasks, MCP, hooks
432
+ debugInit('ecc:start');
392
433
  await installAwEcc(cwd, { silent });
434
+ debugInit('ecc:done');
393
435
  const instructionFiles = copyInstructions(HOME, null, team) || [];
436
+ debugInit(`instructions:done count=${instructionFiles.length}`);
394
437
  initAwDocs(HOME);
438
+ debugInit('aw-docs:done');
395
439
  const mcpFiles = await setupMcp(HOME, team, { silent }) || [];
440
+ debugInit(`mcp:done count=${mcpFiles.length}`);
396
441
  const hooksInstalled = installGlobalHooks();
442
+ debugInit(`hooks:done installed=${hooksInstalled}`);
397
443
  installIdeTasks();
444
+ debugInit('ide-tasks:done');
398
445
 
399
446
  // Remove old local .git/hooks/post-checkout that pre-dates core.hooksPath
400
447
  if (cwd !== HOME) {
@@ -413,24 +460,33 @@ export async function initCommand(args) {
413
460
  const isAlreadySymlinkFresh = (() => { try { return lstatSync(awLinkFresh).isSymbolicLink() && existsSync(awLinkFresh); } catch { return false; } })();
414
461
  if (cwd !== HOME && !isInsideAw && !isAlreadySymlinkFresh) {
415
462
  try {
463
+ debugInit('link-project:start');
416
464
  addProjectWorktree(AW_HOME, cwd);
465
+ debugInit('link-project:done');
417
466
  fmt.logStep('Linked current project');
418
467
  } catch { /* best effort */ }
419
468
  }
420
469
 
421
470
  // Step 5: Wire ~/.claude/.cursor/.codex to the project's registry when in a project,
422
471
  // so edits to project/.aw/.aw_registry/ are instantly visible in global IDE dirs.
423
- const ideSpinner = fmt.spinner();
472
+ const ideSpinner = createSpinner(interactiveUi);
424
473
  ideSpinner.start('Wiring IDE symlinks...');
474
+ debugInit('ide-wire:start');
425
475
  const projectRegistryDir = cwd !== HOME ? join(cwd, '.aw', REGISTRY_DIR) : null;
426
476
  const awDirForLinks = (projectRegistryDir && existsSync(projectRegistryDir)) ? projectRegistryDir : null;
427
477
  const symlinks = linkWorkspace(HOME, awDirForLinks, { silent: true });
428
478
  if (cwd !== HOME) installLocalCommitHook(cwd);
429
479
  ideSpinner.message('Generating commands...');
430
480
  const commands = generateCommands(HOME, { silent: true });
481
+ debugInit(`ide-wire:done symlinks=${symlinks} commands=${commands}`);
431
482
  ideSpinner.stop(`IDE wired — ${chalk.bold(symlinks)} symlinks · ${chalk.bold(commands)} commands`);
432
483
 
433
484
  // Offer to update if a newer version is available
485
+ if (silent) {
486
+ autoUpdate(await args._updateCheck);
487
+ return;
488
+ }
489
+
434
490
  await promptUpdate(await args._updateCheck);
435
491
 
436
492
  fmt.outro([
package/ecc.mjs CHANGED
@@ -9,7 +9,7 @@ import * as fmt from "./fmt.mjs";
9
9
 
10
10
  const AW_ECC_REPO_SSH = "git@github.com:shreyansh-ghl/aw-ecc.git";
11
11
  const AW_ECC_REPO_HTTPS = "https://github.com/shreyansh-ghl/aw-ecc.git";
12
- const AW_ECC_TAG = "v1.4.24";
12
+ const AW_ECC_TAG = "v1.4.25";
13
13
 
14
14
  const MARKETPLACE_NAME = "aw-marketplace";
15
15
  const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
package/git.mjs CHANGED
@@ -124,15 +124,34 @@ export async function initPersistentClone(repoUrl, awHome, sparsePaths) {
124
124
  }
125
125
 
126
126
  try {
127
- await exec(`git clone --filter=blob:none --no-checkout "${repoUrl}" "${awHome}"`);
127
+ execSync(`git clone --filter=blob:none --no-checkout "${repoUrl}" "${awHome}"`, {
128
+ stdio: 'pipe',
129
+ encoding: 'utf8',
130
+ env: {
131
+ ...process.env,
132
+ GIT_TERMINAL_PROMPT: process.env.GIT_TERMINAL_PROMPT ?? '0',
133
+ },
134
+ });
128
135
  } catch (e) {
129
136
  throw new Error(`Failed to clone ${repoUrl}: ${e.message}`);
130
137
  }
131
138
 
132
139
  try {
133
- await exec('git sparse-checkout init --no-cone', { cwd: awHome });
134
- await exec(`git sparse-checkout set ${sparsePaths.map(p => `"${p}"`).join(' ')}`, { cwd: awHome });
135
- await exec(`git checkout ${REGISTRY_BASE_BRANCH}`, { cwd: awHome });
140
+ execSync('git sparse-checkout init --no-cone', {
141
+ cwd: awHome,
142
+ stdio: 'pipe',
143
+ encoding: 'utf8',
144
+ });
145
+ execSync(`git sparse-checkout set ${sparsePaths.map(p => `"${p}"`).join(' ')}`, {
146
+ cwd: awHome,
147
+ stdio: 'pipe',
148
+ encoding: 'utf8',
149
+ });
150
+ execSync(`git checkout ${REGISTRY_BASE_BRANCH}`, {
151
+ cwd: awHome,
152
+ stdio: 'pipe',
153
+ encoding: 'utf8',
154
+ });
136
155
  } catch (e) {
137
156
  throw new Error(`Failed to configure sparse checkout: ${e.message}`);
138
157
  }
package/mcp.mjs CHANGED
@@ -168,8 +168,16 @@ async function resolveClickUpToken(silent = false, cwd = process.cwd()) {
168
168
  return existing.token;
169
169
  }
170
170
 
171
- // In silent mode (git hooks, auto-pull) there is no terminal — skip prompt
172
- if (silent) return null;
171
+ // In silent mode (git hooks, auto-pull) there is no terminal — skip prompt.
172
+ // Also skip in any non-TTY context so automation and CI runs never crash
173
+ // while trying to render interactive prompts for an optional integration.
174
+ const hasInteractiveTty = Boolean(process.stdin.isTTY && process.stdout.isTTY);
175
+ if (silent || !hasInteractiveTty) {
176
+ if (!silent && !hasInteractiveTty) {
177
+ fmt.logInfo('Skipping ClickUp setup — no interactive terminal detected');
178
+ }
179
+ return null;
180
+ }
173
181
 
174
182
  // 3. Ask first — don't assume the user wants ClickUp
175
183
  const wantsClickUp = await p.select({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.39-beta.0",
3
+ "version": "0.1.39-beta.2",
4
4
  "description": "Agentic Workspace CLI — pull, push & manage agents, skills and commands from the registry",
5
5
  "type": "module",
6
6
  "bin": "bin.js",