@ghl-ai/aw 0.1.50-beta.0 → 0.1.50-beta.1

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
@@ -40,6 +40,7 @@ import {
40
40
  isValidClone,
41
41
  fetchAndMerge,
42
42
  addToSparseCheckout,
43
+ setSparseCheckout,
43
44
  addProjectWorktree,
44
45
  isWorktree,
45
46
  includeToSparsePaths,
@@ -137,6 +138,27 @@ function getSkillInitSpecs(args) {
137
138
  }
138
139
  }
139
140
 
141
+ function skillInitSparsePaths(skillSpecs) {
142
+ const paths = new Set([
143
+ DOCS_SOURCE_DIR,
144
+ AW_DOCS_DIR,
145
+ RULES_SOURCE_DIR,
146
+ `${REGISTRY_DIR}/AW-PROTOCOL.md`,
147
+ `CODEOWNERS`,
148
+ ]);
149
+
150
+ for (const spec of skillSpecs) {
151
+ paths.add(`${REGISTRY_DIR}/${spec.registryPath}`);
152
+
153
+ for (let i = 0; i <= spec.segments.length; i++) {
154
+ const scope = [spec.namespace, ...spec.segments.slice(0, i)].join('/');
155
+ paths.add(`${REGISTRY_DIR}/${scope}/references`);
156
+ }
157
+ }
158
+
159
+ return [...paths];
160
+ }
161
+
140
162
  // ── Ensure ~/.aw/.git/info/exclude has the whitelist block ─────────────
141
163
  //
142
164
  // Strategy: only .aw_registry/, .aw_rules/, content/, and .aw_docs/ are
@@ -360,24 +382,17 @@ export async function initCommand(args) {
360
382
  if (isGitNative) {
361
383
  const cfg = config.load(GLOBAL_AW_DIR);
362
384
 
363
- const newSkillTargets = isSkillInit && cfg
364
- ? skillTargets.filter(target => !cfg.include.some(pattern => target === pattern || target.startsWith(pattern + '/')))
365
- : [];
366
385
  const isNewSubTeam = !isSkillInit && folderName && cfg && !cfg.include.includes(folderName);
367
386
 
368
387
  if (isSkillInit) {
369
- if (newSkillTargets.length > 0) {
370
- if (!silent) fmt.logStep(`Adding ${chalk.cyan(newSkillTargets.length)} skill${newSkillTargets.length > 1 ? 's' : ''}...`);
371
- addToSparseCheckout(AW_HOME, [
372
- ...newSkillTargets.map(target => `.aw_registry/${target}`),
373
- DOCS_SOURCE_DIR,
374
- AW_DOCS_DIR,
375
- RULES_SOURCE_DIR,
376
- `.aw_registry/AW-PROTOCOL.md`,
377
- ]);
378
- for (const target of newSkillTargets) config.addPattern(GLOBAL_AW_DIR, target);
379
- } else if (!silent) {
380
- fmt.logStep('Already initialized — syncing selected skills...');
388
+ if (!silent) fmt.logStep('Syncing selected skills...');
389
+ setSparseCheckout(AW_HOME, skillInitSparsePaths(skillSpecs));
390
+ if (cfg) {
391
+ config.save(GLOBAL_AW_DIR, {
392
+ ...cfg,
393
+ namespace: skillNamespace || cfg.namespace || 'platform',
394
+ include: skillTargets,
395
+ });
381
396
  }
382
397
  } else if (isNewSubTeam) {
383
398
  if (!silent) fmt.logStep(`Adding sub-team ${chalk.cyan(folderName)}...`);
@@ -470,7 +485,7 @@ export async function initCommand(args) {
470
485
  : linkWorkspace(HOME, awDirForLinks, { silent: true });
471
486
  const commands = isSkillInit ? 0 : generateCommands(HOME, { silent: true });
472
487
  if (cwd !== HOME) installLocalCommitHook(cwd);
473
- if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} ${isSkillInit ? 'skill symlinks' : 'symlinks'} · ${chalk.bold(commands)} commands`);
488
+ if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} ${isSkillInit ? 'targeted symlinks' : 'symlinks'} · ${chalk.bold(commands)} commands`);
474
489
 
475
490
  // Write hook manifest after all hook installation is complete
476
491
  try { writeHookManifest({ eccVersion: AW_ECC_TAG, awVersion: VERSION }); } catch { /* best effort */ }
@@ -489,7 +504,7 @@ export async function initCommand(args) {
489
504
  `⟁ ${isNewSubTeam ? `Sub-team ${chalk.cyan(folderName)} added` : 'Up to date'}`,
490
505
  '',
491
506
  ` ${chalk.green('✓')} Registry synced`,
492
- ` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)} ${isSkillInit ? 'skill symlinks' : 'symlinks'} · ${chalk.bold(commands)} commands`,
507
+ ` ${chalk.green('✓')} IDE refreshed — ${chalk.bold(symlinks)} ${isSkillInit ? 'targeted symlinks' : 'symlinks'} · ${chalk.bold(commands)} commands`,
493
508
  removedLegacyStartupFiles.length > 0
494
509
  ? ` ${chalk.green('✓')} Removed ${removedLegacyStartupFiles.length} legacy repo startup file${removedLegacyStartupFiles.length > 1 ? 's' : ''}`
495
510
  : null,
@@ -524,14 +539,7 @@ export async function initCommand(args) {
524
539
 
525
540
  // Determine sparse paths
526
541
  const sparsePaths = isSkillInit
527
- ? [
528
- ...skillTargets.map(target => `.aw_registry/${target}`),
529
- DOCS_SOURCE_DIR,
530
- AW_DOCS_DIR,
531
- RULES_SOURCE_DIR,
532
- `.aw_registry/AW-PROTOCOL.md`,
533
- `CODEOWNERS`,
534
- ]
542
+ ? skillInitSparsePaths(skillSpecs)
535
543
  : [`.aw_registry/platform`, DOCS_SOURCE_DIR, AW_DOCS_DIR, RULES_SOURCE_DIR, `.aw_registry/AW-PROTOCOL.md`, `CODEOWNERS`];
536
544
  if (!isSkillInit && folderName) {
537
545
  sparsePaths.push(`.aw_registry/${folderName}`);
@@ -650,7 +658,7 @@ export async function initCommand(args) {
650
658
  linkWorkspace(HOME, awDirForLinks, { silent: true }),
651
659
  generateCommands(HOME, { silent: true }),
652
660
  ];
653
- if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} ${isSkillInit ? 'skill symlinks' : 'symlinks'} · ${chalk.bold(commands)} commands`);
661
+ if (!silent) fmt.logStep(`IDE wired — ${chalk.bold(symlinks)} ${isSkillInit ? 'targeted symlinks' : 'symlinks'} · ${chalk.bold(commands)} commands`);
654
662
 
655
663
  // Write hook manifest after all hook installation is complete
656
664
  try { writeHookManifest({ eccVersion: AW_ECC_TAG, awVersion: VERSION }); } catch { /* best effort */ }
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.55";
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/git.mjs CHANGED
@@ -251,6 +251,19 @@ export function addToSparseCheckout(awHome, newPaths) {
251
251
  }
252
252
  }
253
253
 
254
+ /**
255
+ * Replace the current sparse checkout with an exact path set.
256
+ */
257
+ export function setSparseCheckout(awHome, sparsePaths) {
258
+ try {
259
+ execSync('git sparse-checkout init --no-cone', { cwd: awHome, stdio: 'pipe' });
260
+ execSync(`git sparse-checkout set ${sparsePaths.map(p => `"${p}"`).join(' ')}`, { cwd: awHome, stdio: 'pipe' });
261
+ execSync(`git checkout ${REGISTRY_BASE_BRANCH}`, { cwd: awHome, stdio: 'pipe' });
262
+ } catch (e) {
263
+ throw new Error(`Failed to set sparse checkout: ${e.message}`);
264
+ }
265
+ }
266
+
254
267
  /**
255
268
  * Mirror the main clone's sparse-checkout paths into a project worktree.
256
269
  * The worktree stays on its own branch — only the checked-out file set is updated.
package/link.mjs CHANGED
@@ -160,11 +160,15 @@ function cleanSkillSymlinks(cwd) {
160
160
  for (const ide of IDE_DIRS) {
161
161
  const skillsDir = join(cwd, ide, 'skills');
162
162
  if (existsSync(skillsDir)) cleanSymlinksRecursive(skillsDir);
163
+ const referencesDir = join(cwd, ide, 'references');
164
+ if (existsSync(referencesDir)) cleanSymlinksRecursive(referencesDir);
163
165
  }
164
166
 
165
167
  if (cwd === realHomeDir()) {
166
168
  const agentsSkillsDir = join(cwd, '.agents', 'skills');
167
169
  if (existsSync(agentsSkillsDir)) cleanSymlinksRecursive(agentsSkillsDir);
170
+ const agentsReferencesDir = join(cwd, '.agents', 'references');
171
+ if (existsSync(agentsReferencesDir)) cleanSymlinksRecursive(agentsReferencesDir);
168
172
  }
169
173
  }
170
174
 
@@ -196,6 +200,40 @@ function linkOneSkill(cwd, awDir, target) {
196
200
  return created;
197
201
  }
198
202
 
203
+ function linkReferenceFiles(cwd, awDir, namespaces = null) {
204
+ const allowedNamespaces = namespaces ? new Set(namespaces) : null;
205
+ const namespacesToLink = listNamespaceDirs(awDir)
206
+ .filter(ns => !allowedNamespaces || allowedNamespaces.has(ns));
207
+ let created = 0;
208
+
209
+ for (const ns of namespacesToLink) {
210
+ for (const { typeDirPath: referencesDir } of findNestedTypeDirs(join(awDir, ns), 'references')) {
211
+ for (const file of readdirSync(referencesDir).filter(f => !f.startsWith('.'))) {
212
+ const targetPath = join(referencesDir, file);
213
+ if (lstatSync(targetPath).isDirectory()) continue;
214
+
215
+ for (const ide of IDE_DIRS) {
216
+ const linkDir = join(cwd, ide, 'references');
217
+ mkdirSync(linkDir, { recursive: true });
218
+ const linkPath = join(linkDir, file);
219
+ const relTarget = relative(linkDir, targetPath);
220
+ try { forceSymlink(relTarget, linkPath); created++; } catch { /* best effort */ }
221
+ }
222
+
223
+ if (cwd === realHomeDir()) {
224
+ const agentsReferencesDir = join(cwd, '.agents', 'references');
225
+ mkdirSync(agentsReferencesDir, { recursive: true });
226
+ const linkPath = join(agentsReferencesDir, file);
227
+ const relTarget = relative(agentsReferencesDir, targetPath);
228
+ try { forceSymlink(relTarget, linkPath); created++; } catch { /* best effort */ }
229
+ }
230
+ }
231
+ }
232
+ }
233
+
234
+ return created;
235
+ }
236
+
199
237
  /**
200
238
  * Create/refresh symlinks for specific skills only.
201
239
  *
@@ -228,9 +266,10 @@ export function linkSkills(cwd, skillTargets, awDirOverride = null, { silent = f
228
266
  for (const target of targets) {
229
267
  created += linkOneSkill(cwd, awDir, target);
230
268
  }
269
+ created += linkReferenceFiles(cwd, awDir, targets.map(target => target.namespace));
231
270
 
232
271
  if (created > 0 && !silent) {
233
- fmt.logSuccess(`Linked ${created} skill symlink${created > 1 ? 's' : ''}`);
272
+ fmt.logSuccess(`Linked ${created} targeted symlink${created > 1 ? 's' : ''}`);
234
273
  }
235
274
 
236
275
  return created;
@@ -344,22 +383,7 @@ export function linkWorkspace(cwd, awDirOverride = null, { silent = false } = {}
344
383
 
345
384
  // Shared references: flatten namespace references into each IDE's references/
346
385
  // so links like ../../references/foo.md continue to work from flattened skill dirs.
347
- for (const ns of namespaces) {
348
- for (const { typeDirPath: referencesDir } of findNestedTypeDirs(join(awDir, ns), 'references')) {
349
- for (const file of readdirSync(referencesDir).filter(f => !f.startsWith('.'))) {
350
- const targetPath = join(referencesDir, file);
351
- if (lstatSync(targetPath).isDirectory()) continue;
352
-
353
- for (const ide of IDE_DIRS) {
354
- const linkDir = join(cwd, ide, 'references');
355
- mkdirSync(linkDir, { recursive: true });
356
- const linkPath = join(linkDir, file);
357
- const relTarget = relative(linkDir, targetPath);
358
- try { forceSymlink(relTarget, linkPath); created++; } catch { /* best effort */ }
359
- }
360
- }
361
- }
362
- }
386
+ created += linkReferenceFiles(cwd, awDir);
363
387
 
364
388
  // Codex per-skill symlinks: ~/.agents/skills/<name> (global only)
365
389
  if (cwd === realHomeDir()) {
@@ -376,20 +400,6 @@ export function linkWorkspace(cwd, awDirOverride = null, { silent = false } = {}
376
400
  }
377
401
  }
378
402
  }
379
-
380
- const agentsReferencesDir = join(cwd, '.agents', 'references');
381
- for (const ns of namespaces) {
382
- for (const { typeDirPath: referencesDir } of findNestedTypeDirs(join(awDir, ns), 'references')) {
383
- mkdirSync(agentsReferencesDir, { recursive: true });
384
- for (const file of readdirSync(referencesDir).filter(f => !f.startsWith('.'))) {
385
- const targetPath = join(referencesDir, file);
386
- if (lstatSync(targetPath).isDirectory()) continue;
387
- const linkPath = join(agentsReferencesDir, file);
388
- const relTarget = relative(agentsReferencesDir, targetPath);
389
- try { forceSymlink(relTarget, linkPath); created++; } catch { /* best effort */ }
390
- }
391
- }
392
- }
393
403
  }
394
404
 
395
405
  // Commands: per-file symlinks (recursive for nested domain dirs)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.50-beta.0",
3
+ "version": "0.1.50-beta.1",
4
4
  "description": "Agentic Workspace CLI — pull, push & manage agents, skills and commands from the registry",
5
5
  "type": "module",
6
6
  "bin": {