@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.
- package/c4/commandSurface.mjs +28 -1
- package/integrate.mjs +5 -61
- package/link.mjs +35 -18
- package/package.json +1 -1
package/c4/commandSurface.mjs
CHANGED
|
@@ -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 {
|
|
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,
|
|
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
|
-
*
|
|
178
|
-
*
|
|
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
|
-
|
|
188
|
-
|
|
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
|
-
//
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
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
|