@ghl-ai/aw 0.1.42 → 0.1.43-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 +19 -0
- package/commands/push.mjs +42 -6
- package/ecc.mjs +1 -1
- package/link.mjs +26 -5
- package/package.json +1 -1
- package/registry.mjs +28 -9
package/commands/init.mjs
CHANGED
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
import {
|
|
8
8
|
existsSync,
|
|
9
|
+
mkdirSync,
|
|
9
10
|
writeFileSync,
|
|
10
11
|
symlinkSync,
|
|
11
12
|
lstatSync,
|
|
@@ -90,6 +91,22 @@ function syncHomeAndProjectInstructions(cwd, namespace) {
|
|
|
90
91
|
}
|
|
91
92
|
}
|
|
92
93
|
|
|
94
|
+
/**
|
|
95
|
+
* Create scaffold directories for a new team namespace so the developer
|
|
96
|
+
* has the standard folder structure ready for agents, skills, commands, and evals.
|
|
97
|
+
* No-op if the directories already exist.
|
|
98
|
+
*/
|
|
99
|
+
function scaffoldNamespace(awHome, folderName) {
|
|
100
|
+
const nsDir = join(awHome, REGISTRY_DIR, ...folderName.split('/'));
|
|
101
|
+
for (const type of ['agents', 'skills', 'commands', 'evals']) {
|
|
102
|
+
const dir = join(nsDir, type);
|
|
103
|
+
if (!existsSync(dir)) {
|
|
104
|
+
mkdirSync(dir, { recursive: true });
|
|
105
|
+
writeFileSync(join(dir, '.gitkeep'), '');
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
93
110
|
// ── Ensure ~/.aw/.git/info/exclude has the whitelist block ─────────────
|
|
94
111
|
//
|
|
95
112
|
// Strategy: only .aw_registry/, .aw_rules/, content/ are tracked — everything
|
|
@@ -309,6 +326,7 @@ export async function initCommand(args) {
|
|
|
309
326
|
const newSparsePaths = [`.aw_registry/${folderName}`, 'content', RULES_SOURCE_DIR];
|
|
310
327
|
addToSparseCheckout(AW_HOME, newSparsePaths);
|
|
311
328
|
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
329
|
+
scaffoldNamespace(AW_HOME, folderName);
|
|
312
330
|
} else {
|
|
313
331
|
if (!silent) fmt.logStep('Already initialized — syncing...');
|
|
314
332
|
}
|
|
@@ -486,6 +504,7 @@ export async function initCommand(args) {
|
|
|
486
504
|
const cfg = config.create(GLOBAL_AW_DIR, { namespace: team || 'platform', user });
|
|
487
505
|
if (folderName) {
|
|
488
506
|
config.addPattern(GLOBAL_AW_DIR, folderName);
|
|
507
|
+
scaffoldNamespace(AW_HOME, folderName);
|
|
489
508
|
}
|
|
490
509
|
// Parallel batch A: rules sync (HOME + cwd are independent targets)
|
|
491
510
|
syncRulesTargets(HOME);
|
package/commands/push.mjs
CHANGED
|
@@ -13,7 +13,7 @@ import * as fmt from '../fmt.mjs';
|
|
|
13
13
|
import { chalk } from '../fmt.mjs';
|
|
14
14
|
import { REGISTRY_REPO, REGISTRY_URL, REGISTRY_BASE_BRANCH, REGISTRY_DIR, AW_CO_AUTHOR } from '../constants.mjs';
|
|
15
15
|
import { resolveInput } from '../paths.mjs';
|
|
16
|
-
import { walkRegistryTree } from '../registry.mjs';
|
|
16
|
+
import { walkRegistryTree, getAllFiles } from '../registry.mjs';
|
|
17
17
|
import {
|
|
18
18
|
detectChanges,
|
|
19
19
|
getStagedFiles,
|
|
@@ -236,9 +236,11 @@ function collectBatchFiles(folderAbsPath, registrySubDir) {
|
|
|
236
236
|
return true;
|
|
237
237
|
})
|
|
238
238
|
.map(entry => {
|
|
239
|
-
const registryTarget =
|
|
240
|
-
? `${REGISTRY_DIR}/${entry.namespacePath}/${entry.type}/${entry.
|
|
241
|
-
:
|
|
239
|
+
const registryTarget = entry.evalRelPath
|
|
240
|
+
? `${REGISTRY_DIR}/${entry.namespacePath}/${entry.type}/${entry.evalRelPath}`
|
|
241
|
+
: (entry.type === 'skills' || entry.type === 'evals')
|
|
242
|
+
? `${REGISTRY_DIR}/${entry.namespacePath}/${entry.type}/${entry.slug}/${entry.skillRelPath || entry.filename}`
|
|
243
|
+
: `${REGISTRY_DIR}/${entry.namespacePath}/${entry.type}/${entry.filename}`;
|
|
242
244
|
return {
|
|
243
245
|
absPath: entry.sourcePath,
|
|
244
246
|
registryTarget,
|
|
@@ -267,6 +269,36 @@ function parseRegistryPath(relPath) {
|
|
|
267
269
|
return null;
|
|
268
270
|
}
|
|
269
271
|
|
|
272
|
+
// ── Colocated eval resolution ─────────────────────────────────────────
|
|
273
|
+
|
|
274
|
+
/**
|
|
275
|
+
* Find colocated eval files for a given agent or command.
|
|
276
|
+
* Evals live at <type>/evals/<slug>/eval-*.md alongside their parent artifact.
|
|
277
|
+
* Only returns files that have pending changes (present in changedPaths).
|
|
278
|
+
*/
|
|
279
|
+
function resolveColocatedEvals(registrySubDir, namespace, parentType, slug, changedPaths) {
|
|
280
|
+
if (parentType !== 'agents' && parentType !== 'commands') return [];
|
|
281
|
+
|
|
282
|
+
const evalsDir = join(registrySubDir, namespace, parentType, 'evals', slug);
|
|
283
|
+
if (!existsSync(evalsDir) || !statSync(evalsDir).isDirectory()) return [];
|
|
284
|
+
|
|
285
|
+
return getAllFiles(evalsDir)
|
|
286
|
+
.map(file => {
|
|
287
|
+
const relFromRegistryBase = file.slice(registrySubDir.length + 1);
|
|
288
|
+
const registryTarget = `${REGISTRY_DIR}/${relFromRegistryBase}`;
|
|
289
|
+
return { absPath: file, registryTarget };
|
|
290
|
+
})
|
|
291
|
+
.filter(f => changedPaths.has(f.registryTarget))
|
|
292
|
+
.map(f => ({
|
|
293
|
+
...f,
|
|
294
|
+
type: parentType,
|
|
295
|
+
namespace,
|
|
296
|
+
slug,
|
|
297
|
+
isDir: false,
|
|
298
|
+
deleted: false,
|
|
299
|
+
}));
|
|
300
|
+
}
|
|
301
|
+
|
|
270
302
|
// ── CODEOWNERS helpers ────────────────────────────────────────────────
|
|
271
303
|
|
|
272
304
|
async function getGitHubUser() {
|
|
@@ -727,7 +759,7 @@ export async function pushCommand(args) {
|
|
|
727
759
|
return;
|
|
728
760
|
}
|
|
729
761
|
|
|
730
|
-
|
|
762
|
+
const mainFile = {
|
|
731
763
|
absPath,
|
|
732
764
|
registryTarget,
|
|
733
765
|
type: parentDir,
|
|
@@ -735,7 +767,11 @@ export async function pushCommand(args) {
|
|
|
735
767
|
slug,
|
|
736
768
|
isDir,
|
|
737
769
|
deleted: isDeletedFile,
|
|
738
|
-
}
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
// Include colocated evals for agents/commands (agents/evals/<slug>/*, commands/evals/<slug>/*)
|
|
773
|
+
const colocatedEvals = resolveColocatedEvals(registrySubDir, namespacePath, parentDir, slug, singleChangedPaths);
|
|
774
|
+
await doPush([mainFile, ...colocatedEvals], awHome, dryRun, worktreeFlow);
|
|
739
775
|
}
|
|
740
776
|
|
|
741
777
|
// ── Utilities ─────────────────────────────────────────────────────────
|
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.
|
|
15
|
+
export const AW_ECC_TAG = "v1.4.42";
|
|
16
16
|
|
|
17
17
|
const MARKETPLACE_NAME = "aw-marketplace";
|
|
18
18
|
const PLUGIN_KEY = `aw@${MARKETPLACE_NAME}`;
|
package/link.mjs
CHANGED
|
@@ -47,6 +47,8 @@ function listDirs(dir) {
|
|
|
47
47
|
* platform/frontend/agents/ → { typeDirPath: '.../frontend/agents', segments: ['frontend'] }
|
|
48
48
|
*/
|
|
49
49
|
function findNestedTypeDirs(nsDir, typeName) {
|
|
50
|
+
// Types whose directories may contain colocated evals
|
|
51
|
+
const COLOCATED_EVAL_PARENTS = new Set(['agents', 'commands']);
|
|
50
52
|
const results = [];
|
|
51
53
|
function walk(dir, segments) {
|
|
52
54
|
if (!existsSync(dir)) return;
|
|
@@ -56,6 +58,8 @@ function findNestedTypeDirs(nsDir, typeName) {
|
|
|
56
58
|
results.push({ typeDirPath: join(dir, entry.name), segments });
|
|
57
59
|
} else if (!ALL_KNOWN_TYPES.has(entry.name)) {
|
|
58
60
|
walk(join(dir, entry.name), [...segments, entry.name]);
|
|
61
|
+
} else if (typeName === 'evals' && COLOCATED_EVAL_PARENTS.has(entry.name)) {
|
|
62
|
+
walk(join(dir, entry.name), [...segments, entry.name]);
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
}
|
|
@@ -187,20 +191,37 @@ export function linkWorkspace(cwd, awDirOverride = null, { silent = false } = {}
|
|
|
187
191
|
// Evals: per-eval-dir symlinks (recursive for nested domain dirs)
|
|
188
192
|
for (const ns of namespaces) {
|
|
189
193
|
for (const { typeDirPath: evalsDir, segments } of findNestedTypeDirs(join(awDir, ns), 'evals')) {
|
|
190
|
-
|
|
191
|
-
const subDir = join(evalsDir, subType);
|
|
192
|
-
for (const evalName of listDirs(subDir)) {
|
|
193
|
-
const flat = [ns, ...segments, subType, evalName].join('-');
|
|
194
|
+
const isColocated = segments.some(s => s === 'agents' || s === 'commands');
|
|
194
195
|
|
|
196
|
+
if (isColocated) {
|
|
197
|
+
// Colocated evals: agents/evals/<slug>/ — one level of slug dirs
|
|
198
|
+
for (const evalSlug of listDirs(evalsDir)) {
|
|
199
|
+
const flat = [ns, ...segments, evalSlug].join('-');
|
|
195
200
|
for (const ide of IDE_DIRS) {
|
|
196
201
|
const linkDir = join(cwd, ide, 'evals');
|
|
197
202
|
mkdirSync(linkDir, { recursive: true });
|
|
198
203
|
const linkPath = join(linkDir, flat);
|
|
199
|
-
const targetPath = join(
|
|
204
|
+
const targetPath = join(evalsDir, evalSlug);
|
|
200
205
|
const relTarget = relative(linkDir, targetPath);
|
|
201
206
|
try { forceSymlink(relTarget, linkPath); created++; } catch { /* best effort */ }
|
|
202
207
|
}
|
|
203
208
|
}
|
|
209
|
+
} else {
|
|
210
|
+
// Standard evals: evals/<subType>/<slug>/ — two levels of dirs
|
|
211
|
+
for (const subType of listDirs(evalsDir)) {
|
|
212
|
+
const subDir = join(evalsDir, subType);
|
|
213
|
+
for (const evalName of listDirs(subDir)) {
|
|
214
|
+
const flat = [ns, ...segments, subType, evalName].join('-');
|
|
215
|
+
for (const ide of IDE_DIRS) {
|
|
216
|
+
const linkDir = join(cwd, ide, 'evals');
|
|
217
|
+
mkdirSync(linkDir, { recursive: true });
|
|
218
|
+
const linkPath = join(linkDir, flat);
|
|
219
|
+
const targetPath = join(subDir, evalName);
|
|
220
|
+
const relTarget = relative(linkDir, targetPath);
|
|
221
|
+
try { forceSymlink(relTarget, linkPath); created++; } catch { /* best effort */ }
|
|
222
|
+
}
|
|
223
|
+
}
|
|
224
|
+
}
|
|
204
225
|
}
|
|
205
226
|
}
|
|
206
227
|
}
|
package/package.json
CHANGED
package/registry.mjs
CHANGED
|
@@ -84,18 +84,37 @@ export function walkRegistryTree(baseDir, baseName) {
|
|
|
84
84
|
});
|
|
85
85
|
}
|
|
86
86
|
} else {
|
|
87
|
-
// Agents, commands — flat files
|
|
87
|
+
// Agents, commands — flat files + colocated evals (same pattern as skills)
|
|
88
88
|
for (const fileEntry of readdirSync(fullPath)) {
|
|
89
89
|
if (fileEntry === '.gitkeep' || fileEntry.startsWith('.')) continue;
|
|
90
90
|
const filePath = join(fullPath, fileEntry);
|
|
91
|
-
if (
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
91
|
+
if (statSync(filePath).isFile()) {
|
|
92
|
+
const slug = fileEntry.replace(/\.md$/, '');
|
|
93
|
+
const registryPath = `${namespace}/${typeDir}/${slug}`;
|
|
94
|
+
entries.push({
|
|
95
|
+
slug, type: typeDir, sourcePath: filePath,
|
|
96
|
+
isDirectory: false, namespacePath: namespace, registryPath,
|
|
97
|
+
filename: fileEntry,
|
|
98
|
+
});
|
|
99
|
+
} else if (fileEntry === 'evals' && statSync(filePath).isDirectory()) {
|
|
100
|
+
for (const file of getAllFiles(filePath)) {
|
|
101
|
+
const relFromType = relative(fullPath, file);
|
|
102
|
+
const relFromEvals = relative(filePath, file);
|
|
103
|
+
const parts = relFromEvals.split('/');
|
|
104
|
+
const parentSlug = parts.length > 1 ? parts[0] : '';
|
|
105
|
+
const filename = parts[parts.length - 1];
|
|
106
|
+
entries.push({
|
|
107
|
+
slug: parentSlug || filename.replace(/\.md$/, ''),
|
|
108
|
+
type: typeDir,
|
|
109
|
+
sourcePath: file,
|
|
110
|
+
isDirectory: false,
|
|
111
|
+
namespacePath: namespace,
|
|
112
|
+
registryPath: `${namespace}/${typeDir}/${relFromType.replace(/\.md$/, '')}`,
|
|
113
|
+
filename,
|
|
114
|
+
evalRelPath: relFromType,
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
}
|
|
99
118
|
}
|
|
100
119
|
}
|
|
101
120
|
} else if (entry.isDirectory() && !SKIP_DIRS.has(entry.name)) {
|