@ghl-ai/aw 0.1.70-beta.1 → 0.1.70-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.
@@ -24,6 +24,7 @@ import {
24
24
  mkdirSync,
25
25
  readdirSync,
26
26
  readlinkSync,
27
+ rmSync,
27
28
  symlinkSync,
28
29
  unlinkSync,
29
30
  } from 'node:fs';
@@ -124,7 +125,7 @@ function linkSkillRoute(route, targetDir) {
124
125
 
125
126
  try {
126
127
  if (existsSync(linkPath) || lstatExists(linkPath)) {
127
- try { unlinkSync(linkPath); } catch { /* may be a directory */ }
128
+ try { rmSync(linkPath, { recursive: true, force: true }); } catch { /* best effort */ }
128
129
  }
129
130
  symlinkSync(route.sourcePath, linkPath);
130
131
  return true;
@@ -133,6 +134,25 @@ function linkSkillRoute(route, targetDir) {
133
134
  }
134
135
  }
135
136
 
137
+ function pruneStaleSlashAdapters(targetDir, routes) {
138
+ const expectedFiles = new Set(routes.map((route) => `${route.slashName}.md`));
139
+ let pruned = 0;
140
+
141
+ for (const entry of safeReaddir(targetDir)) {
142
+ if (entry.startsWith('.')) continue;
143
+ if (expectedFiles.has(entry)) continue;
144
+
145
+ try {
146
+ rmSync(join(targetDir, entry), { recursive: true, force: true });
147
+ pruned++;
148
+ } catch {
149
+ // Best effort: stale command cleanup must not block skill adapters.
150
+ }
151
+ }
152
+
153
+ return pruned;
154
+ }
155
+
136
156
  /**
137
157
  * Ensure the harness slash surface for active AW skills.
138
158
  *
@@ -146,6 +166,7 @@ function linkSkillRoute(route, targetDir) {
146
166
  * expectedSkills: string[],
147
167
  * found: string[],
148
168
  * missing: string[],
169
+ * pruned: number,
149
170
  * installedAction: 'symlink' | 'noop' | 'unsupported',
150
171
  * }}
151
172
  */
@@ -172,6 +193,7 @@ export function ensureCommandSurface(opts) {
172
193
  expectedSkills,
173
194
  found: [...expectedCommands],
174
195
  missing: [],
196
+ pruned: 0,
175
197
  installedAction: 'noop',
176
198
  };
177
199
  }
@@ -184,6 +206,7 @@ export function ensureCommandSurface(opts) {
184
206
  expectedSkills,
185
207
  found: [],
186
208
  missing: [],
209
+ pruned: 0,
187
210
  installedAction: 'unsupported',
188
211
  };
189
212
  }
@@ -197,6 +220,7 @@ export function ensureCommandSurface(opts) {
197
220
  expectedSkills,
198
221
  found: [],
199
222
  missing: [...expectedCommands],
223
+ pruned: 0,
200
224
  installedAction: 'symlink',
201
225
  };
202
226
  }
@@ -208,10 +232,12 @@ export function ensureCommandSurface(opts) {
208
232
  expectedSkills,
209
233
  found: [],
210
234
  missing: [...expectedCommands],
235
+ pruned: 0,
211
236
  installedAction: 'symlink',
212
237
  };
213
238
  }
214
239
 
240
+ const pruned = pruneStaleSlashAdapters(targetDir, routes);
215
241
  const found = [];
216
242
  const missing = [];
217
243
  for (const route of routes) {
@@ -226,6 +252,7 @@ export function ensureCommandSurface(opts) {
226
252
  expectedSkills,
227
253
  found,
228
254
  missing,
255
+ pruned,
229
256
  installedAction: 'symlink',
230
257
  };
231
258
  }
package/integrate.mjs CHANGED
@@ -1,10 +1,9 @@
1
1
  // integrate.mjs — Generate commands for all IDEs, instructions (CLAUDE.md, AGENTS.md)
2
2
 
3
- import { existsSync, mkdirSync, writeFileSync, readFileSync, readdirSync, rmSync } from 'node:fs';
3
+ import { existsSync, mkdirSync, writeFileSync, readFileSync, rmSync } from 'node:fs';
4
4
  import { dirname, join } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
  import * as fmt from './fmt.mjs';
7
- import * as config from './config.mjs';
8
7
  import { getLocalRegistryDir } from './git.mjs';
9
8
  import {
10
9
  generateAgentsMdRulesSection,
@@ -174,8 +173,8 @@ function applyManagedInstructionSections(content, file, rulesSections = {}, opti
174
173
  }
175
174
 
176
175
  /**
177
- * Count hand-written commands already present in the registry.
178
- * No CLI stub generation all commands come from the registry itself.
176
+ * Command slash adapters are deprecated. Keep this function as a compatibility
177
+ * no-op for older call sites and clean the removed generated-command folder.
179
178
  */
180
179
  export function generateCommands(cwd, { silent = false } = {}) {
181
180
  const awDir = getLocalRegistryDir(cwd, join(homedir(), '.aw_registry'));
@@ -184,47 +183,8 @@ export function generateCommands(cwd, { silent = false } = {}) {
184
183
  const oldGenDir = join(awDir, '.generated-commands');
185
184
  if (existsSync(oldGenDir)) rmSync(oldGenDir, { recursive: true, force: true });
186
185
 
187
- // Count hand-written commands across all namespaces for reporting
188
- let count = 0;
189
- const namespaces = getTeamNamespaces(awDir);
190
- for (const ns of namespaces) {
191
- const nsDir = join(awDir, ns);
192
- if (existsSync(nsDir)) {
193
- const cmdFiles = findFiles(nsDir, 'commands');
194
- count += cmdFiles.length;
195
- }
196
- }
197
-
198
- if (count > 0 && !silent) {
199
- fmt.logSuccess(`Generated ${count} aw commands`);
200
- }
201
-
202
- return count;
203
- }
204
-
205
- function findFiles(dir, typeName) {
206
- const results = [];
207
- function walk(d) {
208
- let entries;
209
- try {
210
- entries = readdirSync(d, { withFileTypes: true });
211
- } catch { return; } // not a directory — skip gracefully
212
- for (const entry of entries) {
213
- if (entry.name.startsWith('.')) continue;
214
- const full = join(d, entry.name);
215
- if (entry.isDirectory()) {
216
- if (entry.name === typeName) {
217
- for (const f of readdirSync(full)) {
218
- if (f.endsWith('.md')) results.push(join(full, f));
219
- }
220
- } else {
221
- walk(full);
222
- }
223
- }
224
- }
225
- }
226
- walk(dir);
227
- return results;
186
+ if (!silent) fmt.logSuccess('Command adapters are disabled; use registry-backed AW skills');
187
+ return 0;
228
188
  }
229
189
 
230
190
  /**
@@ -711,19 +671,3 @@ No active tasks. Tasks are created during workflow execution.
711
671
  fmt.logSuccess('Orchestration state ready');
712
672
  }
713
673
  }
714
-
715
- /**
716
- * Return top-level team namespace names from config (excludes 'platform').
717
- * cfg.include may contain full paths like 'mobile/core/backend/agents/dev.md'
718
- * so we extract only the first path segment (the actual namespace folder).
719
- * E.g. ['mobile/core/backend/agents/dev.md', 'revex'] → ['mobile', 'revex']
720
- */
721
- function getTeamNamespaces(awDir) {
722
- const cfg = config.load(awDir);
723
- if (!cfg || !cfg.include) return [];
724
- return [...new Set(
725
- cfg.include
726
- .filter(p => p !== 'platform')
727
- .map(p => p.split('/')[0]),
728
- )];
729
- }
package/link.mjs CHANGED
@@ -1,9 +1,10 @@
1
1
  // link.mjs — Create symlinks from IDE dirs → .aw_registry/
2
2
 
3
- import { existsSync, lstatSync, mkdirSync, readdirSync, unlinkSync, symlinkSync, rmdirSync, realpathSync } from 'node:fs';
4
- import { join, relative } from 'node:path';
3
+ import { existsSync, lstatSync, mkdirSync, readdirSync, unlinkSync, symlinkSync, rmdirSync, realpathSync, rmSync } from 'node:fs';
4
+ import { dirname, join, relative } from 'node:path';
5
5
  import { homedir } from 'node:os';
6
6
  import * as fmt from './fmt.mjs';
7
+ import { ensureCommandSurface } from './c4/commandSurface.mjs';
7
8
  import { getLocalRegistryDir } from './git.mjs';
8
9
 
9
10
  function forceSymlink(target, linkPath) {
@@ -130,6 +131,31 @@ function cleanIdeSymlinks(cwd) {
130
131
  }
131
132
  }
132
133
 
134
+ function pruneManagedAwCommandNamespaces(cwd) {
135
+ let pruned = 0;
136
+ for (const ide of IDE_DIRS) {
137
+ const commandDir = join(cwd, ide, 'commands', 'aw');
138
+ if (!existsSync(commandDir)) continue;
139
+ try {
140
+ rmSync(commandDir, { recursive: true, force: true });
141
+ pruned++;
142
+ } catch {
143
+ // Best effort: stale command cleanup should not block registry linking.
144
+ }
145
+ }
146
+ return pruned;
147
+ }
148
+
149
+ function ensureSkillSlashAdapters(cwd, awDir) {
150
+ const awHome = dirname(awDir);
151
+ let linked = 0;
152
+ for (const harness of ['cursor-cloud', 'codex-web']) {
153
+ const result = ensureCommandSurface({ harness, home: cwd, awHome });
154
+ linked += result.found.length;
155
+ }
156
+ return linked;
157
+ }
158
+
133
159
  /**
134
160
  * Remove all symlinks in a directory, then prune empty subdirectories.
135
161
  * Walks depth-first so children are cleaned before parents.
@@ -196,6 +222,9 @@ export function linkWorkspace(cwd, awDirOverride = null, { silent = false } = {}
196
222
 
197
223
  // Clean old symlinks first
198
224
  cleanIdeSymlinks(cwd);
225
+ if (cwd === HOME) {
226
+ pruneManagedAwCommandNamespaces(cwd);
227
+ }
199
228
 
200
229
  const namespaces = listNamespaceDirs(awDir);
201
230
 
@@ -344,22 +373,10 @@ export function linkWorkspace(cwd, awDirOverride = null, { silent = false } = {}
344
373
  }
345
374
  }
346
375
 
347
- // Commands: per-file symlinks (recursive for nested domain dirs)
348
- for (const ns of namespaces) {
349
- for (const { typeDirPath: commandsDir, segments } of findNestedTypeDirs(join(awDir, ns), 'commands')) {
350
- for (const file of readdirSync(commandsDir).filter(f => f.endsWith('.md') && !f.startsWith('.'))) {
351
- const cmdFileName = flatRegistryName(ns, ...segments, file);
352
-
353
- for (const ide of IDE_DIRS) {
354
- const linkDir = join(cwd, ide, 'commands', 'aw');
355
- mkdirSync(linkDir, { recursive: true });
356
- const linkPath = join(linkDir, cmdFileName);
357
- const targetPath = join(commandsDir, file);
358
- const relTarget = relative(linkDir, targetPath);
359
- try { forceSymlink(relTarget, linkPath); created++; } catch { /* best effort */ }
360
- }
361
- }
362
- }
376
+ // Slash adapters are skills-only. Keep the harness discovery paths populated,
377
+ // but point every /aw:* file at .aw_registry/aw/skills/*/SKILL.md.
378
+ if (cwd === HOME) {
379
+ try { created += ensureSkillSlashAdapters(cwd, awDir); } catch { /* best effort */ }
363
380
  }
364
381
 
365
382
  // AW-PROTOCOL.md: symlink into each IDE dir root so commands can always find it
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ghl-ai/aw",
3
- "version": "0.1.70-beta.1",
3
+ "version": "0.1.70-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": {