awesome-agents 0.1.3 → 0.1.6
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/AGENTS.md +7 -4
- package/CHANGELOG.md +35 -0
- package/README.md +45 -17
- package/docs/cli.md +29 -8
- package/docs/product/README.md +1 -1
- package/docs/product/command-model.md +14 -9
- package/docs/product/harness-targets.md +26 -3
- package/docs/product/product-scope.md +7 -2
- package/docs/product/profile-source-format.md +28 -12
- package/package.json +3 -2
- package/src/cli.js +140 -10
- package/src/constants.js +14 -3
- package/src/help.js +14 -12
- package/src/installer.js +148 -14
- package/src/renderers.js +117 -4
- package/src/skills.js +397 -0
- package/src/source.js +162 -20
package/src/source.js
CHANGED
|
@@ -18,6 +18,26 @@ const PROFILE_SUFFIXES = [
|
|
|
18
18
|
".agent.md",
|
|
19
19
|
".md"
|
|
20
20
|
];
|
|
21
|
+
const AGENT_DEFINITION_NAMES = [
|
|
22
|
+
"agent.agent.yaml",
|
|
23
|
+
"agent.agent.yml",
|
|
24
|
+
"agent.agf.yaml",
|
|
25
|
+
"agent.agf.yml",
|
|
26
|
+
"agent.yaml",
|
|
27
|
+
"agent.yml",
|
|
28
|
+
"agent.agent.md",
|
|
29
|
+
"agent.md",
|
|
30
|
+
"profile.agent.yaml",
|
|
31
|
+
"profile.agent.yml",
|
|
32
|
+
"profile.agf.yaml",
|
|
33
|
+
"profile.agf.yml",
|
|
34
|
+
"profile.yaml",
|
|
35
|
+
"profile.yml",
|
|
36
|
+
"profile.agent.md",
|
|
37
|
+
"profile.md"
|
|
38
|
+
];
|
|
39
|
+
const RESERVED_AGENT_DIRS = new Set(["profiles", "adapters"]);
|
|
40
|
+
const SUPPORT_DIRS = ["references", "scripts"];
|
|
21
41
|
|
|
22
42
|
export function splitSourceSpec(spec) {
|
|
23
43
|
if (!spec) {
|
|
@@ -70,6 +90,7 @@ export function normalizeGithubUrl(source) {
|
|
|
70
90
|
export async function materializeSource(sourceInput, options = {}) {
|
|
71
91
|
const home = options.home ?? os.homedir();
|
|
72
92
|
const source = sourceInput;
|
|
93
|
+
const requireAgentProfileLayout = options.requireAgentProfileLayout ?? true;
|
|
73
94
|
|
|
74
95
|
if (!source) {
|
|
75
96
|
throw new Error("Specify a source. Use a local path, owner/repo, or GitHub URL.");
|
|
@@ -77,7 +98,9 @@ export async function materializeSource(sourceInput, options = {}) {
|
|
|
77
98
|
|
|
78
99
|
if (isLocalSource(source, home)) {
|
|
79
100
|
const sourcePath = path.resolve(expandHome(source, home));
|
|
80
|
-
|
|
101
|
+
if (requireAgentProfileLayout) {
|
|
102
|
+
await assertAgentProfileLayout(sourcePath);
|
|
103
|
+
}
|
|
81
104
|
return {
|
|
82
105
|
kind: "local",
|
|
83
106
|
source: sourcePath,
|
|
@@ -86,14 +109,20 @@ export async function materializeSource(sourceInput, options = {}) {
|
|
|
86
109
|
};
|
|
87
110
|
}
|
|
88
111
|
|
|
89
|
-
const
|
|
112
|
+
const { sourceWithoutRef, ref } = splitGitRef(source);
|
|
113
|
+
const gitUrl = normalizeGithubUrl(sourceWithoutRef);
|
|
90
114
|
if (!gitUrl) {
|
|
91
115
|
throw new Error(`Unsupported source "${source}". Use a local path, owner/repo, or GitHub URL.`);
|
|
92
116
|
}
|
|
93
117
|
|
|
94
118
|
const tmpRoot = options.tmpRoot ?? os.tmpdir();
|
|
95
119
|
const cloneDir = await fs.mkdtemp(path.join(tmpRoot, "awesome-agents-source-"));
|
|
96
|
-
const
|
|
120
|
+
const cloneArgs = ["clone", "--depth=1"];
|
|
121
|
+
if (ref) {
|
|
122
|
+
cloneArgs.push("--branch", ref);
|
|
123
|
+
}
|
|
124
|
+
cloneArgs.push(gitUrl, cloneDir);
|
|
125
|
+
const clone = spawnSync("git", cloneArgs, {
|
|
97
126
|
encoding: "utf8"
|
|
98
127
|
});
|
|
99
128
|
|
|
@@ -103,7 +132,9 @@ export async function materializeSource(sourceInput, options = {}) {
|
|
|
103
132
|
throw new Error(`Could not clone ${source}: ${detail}`);
|
|
104
133
|
}
|
|
105
134
|
|
|
106
|
-
|
|
135
|
+
if (requireAgentProfileLayout) {
|
|
136
|
+
await assertAgentProfileLayout(cloneDir);
|
|
137
|
+
}
|
|
107
138
|
|
|
108
139
|
return {
|
|
109
140
|
kind: "git",
|
|
@@ -116,29 +147,42 @@ export async function materializeSource(sourceInput, options = {}) {
|
|
|
116
147
|
};
|
|
117
148
|
}
|
|
118
149
|
|
|
150
|
+
function splitGitRef(source) {
|
|
151
|
+
const hashIndex = source.lastIndexOf("#");
|
|
152
|
+
if (hashIndex <= 0 || hashIndex === source.length - 1) {
|
|
153
|
+
return { sourceWithoutRef: source, ref: undefined };
|
|
154
|
+
}
|
|
155
|
+
return {
|
|
156
|
+
sourceWithoutRef: source.slice(0, hashIndex),
|
|
157
|
+
ref: source.slice(hashIndex + 1)
|
|
158
|
+
};
|
|
159
|
+
}
|
|
160
|
+
|
|
119
161
|
async function assertAgentProfileLayout(sourcePath) {
|
|
120
|
-
const
|
|
121
|
-
if (
|
|
122
|
-
throw new Error(`No
|
|
162
|
+
const entries = await discoverProfileEntries(sourcePath);
|
|
163
|
+
if (entries.length === 0) {
|
|
164
|
+
throw new Error(`No agent profiles found in ${sourcePath}. Expected agents/<slug>/agent.yaml.`);
|
|
123
165
|
}
|
|
124
166
|
}
|
|
125
167
|
|
|
126
168
|
export async function loadCatalog(sourcePath) {
|
|
127
|
-
const
|
|
128
|
-
const adapterRoot = path.join(sourcePath, "agents", "adapters");
|
|
129
|
-
const files = await fs.readdir(profilesDir, { withFileTypes: true });
|
|
169
|
+
const entries = await discoverProfileEntries(sourcePath);
|
|
130
170
|
const profiles = [];
|
|
171
|
+
const seen = new Set();
|
|
131
172
|
|
|
132
|
-
for (const
|
|
133
|
-
|
|
173
|
+
for (const entry of entries) {
|
|
174
|
+
const profile = await loadProfileFile(entry.filePath, sourcePath, {
|
|
175
|
+
slugHint: entry.slugHint,
|
|
176
|
+
agentRoot: entry.agentRoot
|
|
177
|
+
});
|
|
178
|
+
if (seen.has(profile.slug)) {
|
|
134
179
|
continue;
|
|
135
180
|
}
|
|
136
|
-
|
|
137
|
-
const filePath = path.join(profilesDir, file.name);
|
|
138
|
-
const profile = await loadProfileFile(filePath, sourcePath);
|
|
181
|
+
seen.add(profile.slug);
|
|
139
182
|
profiles.push({
|
|
140
183
|
...profile,
|
|
141
|
-
adapters: await
|
|
184
|
+
adapters: await loadProfileAdapters(entry, profile.slug, sourcePath),
|
|
185
|
+
supportDirs: await loadSupportDirs(entry.agentRoot)
|
|
142
186
|
});
|
|
143
187
|
}
|
|
144
188
|
|
|
@@ -146,8 +190,104 @@ export async function loadCatalog(sourcePath) {
|
|
|
146
190
|
return { sourcePath, profiles };
|
|
147
191
|
}
|
|
148
192
|
|
|
193
|
+
async function discoverProfileEntries(sourcePath) {
|
|
194
|
+
return [
|
|
195
|
+
...await discoverAgentDirectoryProfiles(sourcePath),
|
|
196
|
+
...await discoverLegacyProfileDirectory(sourcePath)
|
|
197
|
+
];
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
async function discoverAgentDirectoryProfiles(sourcePath) {
|
|
201
|
+
const agentsRoot = path.join(sourcePath, "agents");
|
|
202
|
+
if (!existsSync(agentsRoot)) {
|
|
203
|
+
return [];
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const entries = [];
|
|
207
|
+
const files = await fs.readdir(agentsRoot, { withFileTypes: true });
|
|
208
|
+
for (const file of files) {
|
|
209
|
+
if (!file.isDirectory() || RESERVED_AGENT_DIRS.has(file.name)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
const agentRoot = path.join(agentsRoot, file.name);
|
|
213
|
+
const filePath = await findAgentDefinitionFile(agentRoot, file.name);
|
|
214
|
+
if (!filePath) {
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
entries.push({
|
|
218
|
+
layout: "agent-directory",
|
|
219
|
+
filePath,
|
|
220
|
+
slugHint: file.name,
|
|
221
|
+
agentRoot,
|
|
222
|
+
adapterRoot: path.join(agentRoot, "adapters")
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
return entries;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
async function discoverLegacyProfileDirectory(sourcePath) {
|
|
229
|
+
const profilesDir = path.join(sourcePath, "agents", "profiles");
|
|
230
|
+
if (!existsSync(profilesDir)) {
|
|
231
|
+
return [];
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
const entries = [];
|
|
235
|
+
const files = await fs.readdir(profilesDir, { withFileTypes: true });
|
|
236
|
+
for (const file of files) {
|
|
237
|
+
if (!file.isFile() || !isProfileFile(file.name)) {
|
|
238
|
+
continue;
|
|
239
|
+
}
|
|
240
|
+
entries.push({
|
|
241
|
+
layout: "legacy-profiles-directory",
|
|
242
|
+
filePath: path.join(profilesDir, file.name),
|
|
243
|
+
slugHint: profileSlugFromFilename(file.name),
|
|
244
|
+
adapterRoot: path.join(sourcePath, "agents", "adapters")
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
return entries;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
async function findAgentDefinitionFile(agentRoot, slug) {
|
|
251
|
+
for (const name of AGENT_DEFINITION_NAMES) {
|
|
252
|
+
const candidate = path.join(agentRoot, name);
|
|
253
|
+
if (existsSync(candidate)) {
|
|
254
|
+
return candidate;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
for (const suffix of PROFILE_SUFFIXES) {
|
|
258
|
+
const candidate = path.join(agentRoot, `${slug}${suffix}`);
|
|
259
|
+
if (existsSync(candidate)) {
|
|
260
|
+
return candidate;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
return undefined;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
async function loadProfileAdapters(entry, slug, sourcePath) {
|
|
267
|
+
const adapters = {};
|
|
268
|
+
Object.assign(adapters, await loadAdapters(entry.adapterRoot, slug, sourcePath));
|
|
269
|
+
if (entry.layout === "agent-directory") {
|
|
270
|
+
Object.assign(adapters, await loadAdapters(path.join(sourcePath, "agents", "adapters"), slug, sourcePath));
|
|
271
|
+
}
|
|
272
|
+
return adapters;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
async function loadSupportDirs(agentRoot) {
|
|
276
|
+
if (!agentRoot) {
|
|
277
|
+
return [];
|
|
278
|
+
}
|
|
279
|
+
const dirs = [];
|
|
280
|
+
for (const kind of SUPPORT_DIRS) {
|
|
281
|
+
const sourcePath = path.join(agentRoot, kind);
|
|
282
|
+
if (existsSync(sourcePath)) {
|
|
283
|
+
dirs.push({ kind, sourcePath });
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
return dirs;
|
|
287
|
+
}
|
|
288
|
+
|
|
149
289
|
async function loadAdapters(adapterRoot, slug, sourcePath) {
|
|
150
|
-
if (!existsSync(adapterRoot)) {
|
|
290
|
+
if (!adapterRoot || !existsSync(adapterRoot)) {
|
|
151
291
|
return {};
|
|
152
292
|
}
|
|
153
293
|
|
|
@@ -178,10 +318,10 @@ async function loadAdapters(adapterRoot, slug, sourcePath) {
|
|
|
178
318
|
return adapters;
|
|
179
319
|
}
|
|
180
320
|
|
|
181
|
-
async function loadProfileFile(filePath, sourcePath) {
|
|
321
|
+
async function loadProfileFile(filePath, sourcePath, options = {}) {
|
|
182
322
|
const raw = await fs.readFile(filePath, "utf8");
|
|
183
323
|
const parsed = await loadDefinitionFile(filePath, raw);
|
|
184
|
-
const fallbackSlug = profileSlugFromFilename(path.basename(filePath));
|
|
324
|
+
const fallbackSlug = options.slugHint ?? profileSlugFromFilename(path.basename(filePath));
|
|
185
325
|
const slug = parsed.attributes.slug ?? parsed.attributes.id ?? fallbackSlug;
|
|
186
326
|
const name = parsed.attributes.name ?? titleize(slug);
|
|
187
327
|
const summary = parsed.attributes.summary ?? parsed.attributes.description ?? "";
|
|
@@ -200,7 +340,9 @@ async function loadProfileFile(filePath, sourcePath) {
|
|
|
200
340
|
raw,
|
|
201
341
|
filePath,
|
|
202
342
|
relativePath: path.relative(sourcePath, filePath),
|
|
203
|
-
format: parsed.format
|
|
343
|
+
format: parsed.format,
|
|
344
|
+
agentRoot: options.agentRoot,
|
|
345
|
+
layout: options.agentRoot ? "agent-directory" : "legacy-profiles-directory"
|
|
204
346
|
};
|
|
205
347
|
}
|
|
206
348
|
|