awesome-agents 0.1.0 → 0.1.3
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 +9 -3
- package/CHANGELOG.md +51 -0
- package/README.md +66 -29
- package/docs/cli.md +44 -4
- package/docs/product/README.md +13 -2
- package/docs/product/command-model.md +63 -0
- package/docs/product/harness-targets.md +59 -0
- package/docs/product/open-questions.md +21 -0
- package/docs/product/product-scope.md +42 -0
- package/docs/product/profile-source-format.md +64 -0
- package/docs/product/safety-and-publishing.md +64 -0
- package/package.json +7 -2
- package/scripts/changelog.js +227 -0
- package/scripts/release.js +219 -0
- package/src/cli.js +56 -19
- package/src/constants.js +1 -2
- package/src/help.js +530 -0
- package/src/installer.js +200 -31
- package/src/renderers.js +5 -8
- package/src/source.js +200 -23
- package/docs/product/awesome-agents-flow-notes.md +0 -45
|
@@ -0,0 +1,227 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath, pathToFileURL } from "node:url";
|
|
6
|
+
|
|
7
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
8
|
+
const root = path.resolve(scriptDir, "..");
|
|
9
|
+
const changelogPath = path.join(root, "CHANGELOG.md");
|
|
10
|
+
const packagePath = path.join(root, "package.json");
|
|
11
|
+
const changelogHeader = "# Changelog\n\nThis file is maintained by `npm run changelog` and `npm run release`. Release entries are generated from git commit history.\n\n";
|
|
12
|
+
const categoryOrder = ["Breaking", "Added", "Fixed", "Changed", "Documentation", "Tests", "Maintenance"];
|
|
13
|
+
|
|
14
|
+
export async function updateChangelog(options = {}) {
|
|
15
|
+
const version = options.version ?? await readPackageVersion();
|
|
16
|
+
const date = options.date ?? new Date().toISOString().slice(0, 10);
|
|
17
|
+
const from = options.from ?? latestVersionTag();
|
|
18
|
+
const to = options.to ?? "HEAD";
|
|
19
|
+
const commits = readCommits(from, to);
|
|
20
|
+
const entry = formatEntry({ version, date, from, commits });
|
|
21
|
+
|
|
22
|
+
if (!options.write) {
|
|
23
|
+
return { changelogPath, entry, commits, from, to, version };
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
const existing = await readExistingChangelog();
|
|
27
|
+
await fs.writeFile(changelogPath, upsertEntry(existing, entry, version));
|
|
28
|
+
return { changelogPath, entry, commits, from, to, version };
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function readCommits(from, to) {
|
|
32
|
+
const range = from ? `${from}..${to}` : to;
|
|
33
|
+
const output = git(["log", "--no-merges", "--pretty=format:%H%x1f%s", range], { allowEmpty: true });
|
|
34
|
+
if (!output) {
|
|
35
|
+
return [];
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return output.split("\n").map((line) => {
|
|
39
|
+
const [hash, subject] = line.split("\x1f");
|
|
40
|
+
return {
|
|
41
|
+
hash,
|
|
42
|
+
shortHash: hash.slice(0, 7),
|
|
43
|
+
subject: subject.trim()
|
|
44
|
+
};
|
|
45
|
+
}).filter((commit) => commit.subject);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
function formatEntry({ version, date, from, commits }) {
|
|
49
|
+
const lines = [`## ${version} - ${date}`, ""];
|
|
50
|
+
lines.push(from ? `Changes since \`${from}\`.` : "Initial tracked release.");
|
|
51
|
+
lines.push("");
|
|
52
|
+
|
|
53
|
+
if (commits.length === 0) {
|
|
54
|
+
lines.push("- No commits found for this release.");
|
|
55
|
+
return `${lines.join("\n")}\n`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const groups = new Map(categoryOrder.map((category) => [category, []]));
|
|
59
|
+
for (const commit of commits) {
|
|
60
|
+
groups.get(categoryFor(commit.subject)).push(commit);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
for (const category of categoryOrder) {
|
|
64
|
+
const entries = groups.get(category);
|
|
65
|
+
if (entries.length === 0) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
lines.push(`### ${category}`, "");
|
|
70
|
+
for (const commit of entries) {
|
|
71
|
+
lines.push(`- ${cleanSubject(commit.subject)} (${commit.shortHash})`);
|
|
72
|
+
}
|
|
73
|
+
lines.push("");
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
return `${lines.join("\n").trimEnd()}\n`;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function upsertEntry(existing, entry, version) {
|
|
80
|
+
const body = normalizeBody(existing);
|
|
81
|
+
const escapedVersion = escapeRegExp(version);
|
|
82
|
+
const existingEntry = new RegExp(`^## ${escapedVersion} - [^\\n]*\\n[\\s\\S]*?(?=^## \\d+\\.\\d+\\.\\d+ - |(?![\\s\\S]))`, "m");
|
|
83
|
+
|
|
84
|
+
if (existingEntry.test(body)) {
|
|
85
|
+
return `${changelogHeader}${body.replace(existingEntry, entry.trimEnd()).trimStart()}`;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return `${changelogHeader}${entry.trimEnd()}\n\n${body.trimStart()}`.trimEnd() + "\n";
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function normalizeBody(existing) {
|
|
92
|
+
if (!existing) {
|
|
93
|
+
return "";
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
if (existing.startsWith(changelogHeader)) {
|
|
97
|
+
return existing.slice(changelogHeader.length);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
return existing.replace(/^# Changelog\s*/i, "").trimStart();
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function categoryFor(subject) {
|
|
104
|
+
const normalized = subject.toLowerCase();
|
|
105
|
+
if (/^[a-z]+(?:\([^)]+\))?!:/.test(normalized) || normalized.includes("breaking")) {
|
|
106
|
+
return "Breaking";
|
|
107
|
+
}
|
|
108
|
+
if (/^(feat|add)(?:\([^)]+\))?:?\s/.test(normalized)) {
|
|
109
|
+
return "Added";
|
|
110
|
+
}
|
|
111
|
+
if (/^(fix|repair)(?:\([^)]+\))?:?\s/.test(normalized)) {
|
|
112
|
+
return "Fixed";
|
|
113
|
+
}
|
|
114
|
+
if (/^(docs?|readme)(?:\([^)]+\))?:?\s/.test(normalized)) {
|
|
115
|
+
return "Documentation";
|
|
116
|
+
}
|
|
117
|
+
if (/^(test|tests)(?:\([^)]+\))?:?\s/.test(normalized)) {
|
|
118
|
+
return "Tests";
|
|
119
|
+
}
|
|
120
|
+
if (/^(build|chore|ci|release|bump)(?:\([^)]+\))?:?\s/.test(normalized)) {
|
|
121
|
+
return "Maintenance";
|
|
122
|
+
}
|
|
123
|
+
return "Changed";
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function cleanSubject(subject) {
|
|
127
|
+
return subject
|
|
128
|
+
.replace(/^[a-z]+(?:\([^)]+\))?!?:\s*/i, "")
|
|
129
|
+
.trim();
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function latestVersionTag() {
|
|
133
|
+
return git(["tag", "--list", "v[0-9]*", "--sort=-version:refname"], { allowEmpty: true })
|
|
134
|
+
.split("\n")
|
|
135
|
+
.find(Boolean);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function readPackageVersion() {
|
|
139
|
+
const pkg = JSON.parse(await fs.readFile(packagePath, "utf8"));
|
|
140
|
+
return pkg.version;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function readExistingChangelog() {
|
|
144
|
+
try {
|
|
145
|
+
return await fs.readFile(changelogPath, "utf8");
|
|
146
|
+
} catch (error) {
|
|
147
|
+
if (error.code === "ENOENT") {
|
|
148
|
+
return "";
|
|
149
|
+
}
|
|
150
|
+
throw error;
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
function git(args, options = {}) {
|
|
155
|
+
const result = spawnSync("git", args, {
|
|
156
|
+
cwd: root,
|
|
157
|
+
encoding: "utf8"
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
if (result.status !== 0 && !(options.allowEmpty && result.status === 128)) {
|
|
161
|
+
const detail = result.stderr.trim() || result.stdout.trim() || `git exited with ${result.status}`;
|
|
162
|
+
throw new Error(detail);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
return result.stdout.trim();
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function escapeRegExp(value) {
|
|
169
|
+
return String(value).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
function parseArgs(argv) {
|
|
173
|
+
const options = { write: false };
|
|
174
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
175
|
+
const arg = argv[index];
|
|
176
|
+
if (arg === "--write") {
|
|
177
|
+
options.write = true;
|
|
178
|
+
} else if (arg === "--version") {
|
|
179
|
+
options.version = argv[++index];
|
|
180
|
+
} else if (arg === "--from") {
|
|
181
|
+
options.from = argv[++index];
|
|
182
|
+
} else if (arg === "--to") {
|
|
183
|
+
options.to = argv[++index];
|
|
184
|
+
} else if (arg === "--date") {
|
|
185
|
+
options.date = argv[++index];
|
|
186
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
187
|
+
options.help = true;
|
|
188
|
+
} else {
|
|
189
|
+
throw new Error(`Unknown option ${arg}`);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
return options;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function printHelp() {
|
|
196
|
+
console.log(`Usage: node scripts/changelog.js [options]
|
|
197
|
+
|
|
198
|
+
Options:
|
|
199
|
+
--write Update CHANGELOG.md instead of printing the next entry
|
|
200
|
+
--version <version> Release version to write; defaults to package.json
|
|
201
|
+
--from <ref> Starting git ref; defaults to latest v* tag
|
|
202
|
+
--to <ref> Ending git ref; defaults to HEAD
|
|
203
|
+
--date <date> Release date; defaults to today
|
|
204
|
+
`);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async function main() {
|
|
208
|
+
const options = parseArgs(process.argv.slice(2));
|
|
209
|
+
if (options.help) {
|
|
210
|
+
printHelp();
|
|
211
|
+
return;
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
const result = await updateChangelog(options);
|
|
215
|
+
if (options.write) {
|
|
216
|
+
console.log(`Updated ${path.relative(root, result.changelogPath)} for ${result.version} with ${result.commits.length} commit(s).`);
|
|
217
|
+
} else {
|
|
218
|
+
process.stdout.write(result.entry);
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
if (import.meta.url === pathToFileURL(process.argv[1]).href) {
|
|
223
|
+
main().catch((error) => {
|
|
224
|
+
console.error(error.message);
|
|
225
|
+
process.exitCode = 1;
|
|
226
|
+
});
|
|
227
|
+
}
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { spawnSync } from "node:child_process";
|
|
3
|
+
import fs from "node:fs/promises";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { fileURLToPath } from "node:url";
|
|
6
|
+
import { updateChangelog } from "./changelog.js";
|
|
7
|
+
|
|
8
|
+
const scriptDir = path.dirname(fileURLToPath(import.meta.url));
|
|
9
|
+
const root = path.resolve(scriptDir, "..");
|
|
10
|
+
const packagePath = path.join(root, "package.json");
|
|
11
|
+
const lockPath = path.join(root, "package-lock.json");
|
|
12
|
+
const constantsPath = path.join(root, "src", "constants.js");
|
|
13
|
+
const trackedReleaseFiles = [
|
|
14
|
+
"package.json",
|
|
15
|
+
"package-lock.json",
|
|
16
|
+
"src/constants.js",
|
|
17
|
+
"CHANGELOG.md"
|
|
18
|
+
];
|
|
19
|
+
|
|
20
|
+
async function main() {
|
|
21
|
+
const options = parseArgs(process.argv.slice(2));
|
|
22
|
+
if (options.help) {
|
|
23
|
+
printHelp();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const currentVersion = await readPackageVersion();
|
|
28
|
+
const nextVersion = resolveNextVersion(currentVersion, options.bump);
|
|
29
|
+
const tag = `v${nextVersion}`;
|
|
30
|
+
|
|
31
|
+
if (!options.dryRun) {
|
|
32
|
+
assertCleanWorktree();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
console.log(`Release plan: ${currentVersion} -> ${nextVersion}`);
|
|
36
|
+
|
|
37
|
+
if (options.dryRun) {
|
|
38
|
+
const changelog = await updateChangelog({ version: nextVersion, write: false });
|
|
39
|
+
console.log("\nFiles that would be updated:");
|
|
40
|
+
for (const file of trackedReleaseFiles) {
|
|
41
|
+
console.log(`- ${file}`);
|
|
42
|
+
}
|
|
43
|
+
console.log("\nChangelog entry preview:\n");
|
|
44
|
+
process.stdout.write(changelog.entry);
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
await updatePackageJson(nextVersion);
|
|
49
|
+
await updatePackageLock(nextVersion);
|
|
50
|
+
await updateConstants(nextVersion);
|
|
51
|
+
await updateChangelog({ version: nextVersion, write: true });
|
|
52
|
+
|
|
53
|
+
if (!options.skipChecks) {
|
|
54
|
+
run("npm", ["run", "lint"]);
|
|
55
|
+
run("npm", ["test"]);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
git(["add", ...trackedReleaseFiles]);
|
|
59
|
+
git(["commit", "-m", `Release ${tag}`]);
|
|
60
|
+
git(["tag", "-a", tag, "-m", `Release ${tag}`]);
|
|
61
|
+
|
|
62
|
+
if (options.push) {
|
|
63
|
+
const branch = git(["branch", "--show-current"], { capture: true });
|
|
64
|
+
if (!branch) {
|
|
65
|
+
throw new Error("Cannot push from a detached HEAD.");
|
|
66
|
+
}
|
|
67
|
+
git(["push", "origin", branch]);
|
|
68
|
+
git(["push", "origin", tag]);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
console.log(`Cut ${tag}.`);
|
|
72
|
+
if (!options.push) {
|
|
73
|
+
console.log(`Push with: git push origin HEAD && git push origin ${tag}`);
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function parseArgs(argv) {
|
|
78
|
+
const options = {
|
|
79
|
+
bump: "patch",
|
|
80
|
+
dryRun: false,
|
|
81
|
+
push: false,
|
|
82
|
+
skipChecks: false
|
|
83
|
+
};
|
|
84
|
+
const positional = [];
|
|
85
|
+
|
|
86
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
87
|
+
const arg = argv[index];
|
|
88
|
+
if (arg === "--dry-run") {
|
|
89
|
+
options.dryRun = true;
|
|
90
|
+
} else if (arg === "--push") {
|
|
91
|
+
options.push = true;
|
|
92
|
+
} else if (arg === "--skip-checks") {
|
|
93
|
+
options.skipChecks = true;
|
|
94
|
+
} else if (arg === "-h" || arg === "--help") {
|
|
95
|
+
options.help = true;
|
|
96
|
+
} else if (arg.startsWith("-")) {
|
|
97
|
+
throw new Error(`Unknown option ${arg}`);
|
|
98
|
+
} else {
|
|
99
|
+
positional.push(arg);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
if (positional.length > 1) {
|
|
104
|
+
throw new Error(`Expected one bump argument, received: ${positional.join(", ")}`);
|
|
105
|
+
}
|
|
106
|
+
if (positional[0]) {
|
|
107
|
+
options.bump = positional[0];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return options;
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function resolveNextVersion(currentVersion, bump) {
|
|
114
|
+
const explicit = bump.replace(/^v/, "");
|
|
115
|
+
if (/^\d+\.\d+\.\d+$/.test(explicit)) {
|
|
116
|
+
return explicit;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const match = currentVersion.match(/^(\d+)\.(\d+)\.(\d+)$/);
|
|
120
|
+
if (!match) {
|
|
121
|
+
throw new Error(`Cannot bump non-standard version ${currentVersion}. Pass an explicit x.y.z version.`);
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const parts = match.slice(1).map(Number);
|
|
125
|
+
if (bump === "major") {
|
|
126
|
+
return `${parts[0] + 1}.0.0`;
|
|
127
|
+
}
|
|
128
|
+
if (bump === "minor") {
|
|
129
|
+
return `${parts[0]}.${parts[1] + 1}.0`;
|
|
130
|
+
}
|
|
131
|
+
if (bump === "patch") {
|
|
132
|
+
return `${parts[0]}.${parts[1]}.${parts[2] + 1}`;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
throw new Error(`Unsupported release bump "${bump}". Use patch, minor, major, or x.y.z.`);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async function readPackageVersion() {
|
|
139
|
+
const pkg = JSON.parse(await fs.readFile(packagePath, "utf8"));
|
|
140
|
+
return pkg.version;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
async function updatePackageJson(version) {
|
|
144
|
+
const pkg = JSON.parse(await fs.readFile(packagePath, "utf8"));
|
|
145
|
+
pkg.version = version;
|
|
146
|
+
await writeJson(packagePath, pkg);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
async function updatePackageLock(version) {
|
|
150
|
+
const lock = JSON.parse(await fs.readFile(lockPath, "utf8"));
|
|
151
|
+
lock.version = version;
|
|
152
|
+
if (lock.packages?.[""]) {
|
|
153
|
+
lock.packages[""].version = version;
|
|
154
|
+
}
|
|
155
|
+
await writeJson(lockPath, lock);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
async function updateConstants(version) {
|
|
159
|
+
const content = await fs.readFile(constantsPath, "utf8");
|
|
160
|
+
const next = content.replace(
|
|
161
|
+
/export const PACKAGE_VERSION = "[^"]+";/,
|
|
162
|
+
`export const PACKAGE_VERSION = "${version}";`
|
|
163
|
+
);
|
|
164
|
+
if (next === content) {
|
|
165
|
+
throw new Error("Could not find PACKAGE_VERSION in src/constants.js.");
|
|
166
|
+
}
|
|
167
|
+
await fs.writeFile(constantsPath, next);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function writeJson(filePath, value) {
|
|
171
|
+
await fs.writeFile(filePath, `${JSON.stringify(value, null, 2)}\n`);
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
function assertCleanWorktree() {
|
|
175
|
+
const status = git(["status", "--short"], { capture: true });
|
|
176
|
+
if (status) {
|
|
177
|
+
throw new Error(`Release requires a clean worktree:\n${status}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function run(command, args) {
|
|
182
|
+
const result = spawnSync(command, args, {
|
|
183
|
+
cwd: root,
|
|
184
|
+
stdio: "inherit"
|
|
185
|
+
});
|
|
186
|
+
if (result.status !== 0) {
|
|
187
|
+
throw new Error(`${command} ${args.join(" ")} failed with ${result.status}`);
|
|
188
|
+
}
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
function git(args, options = {}) {
|
|
192
|
+
const result = spawnSync("git", args, {
|
|
193
|
+
cwd: root,
|
|
194
|
+
encoding: "utf8",
|
|
195
|
+
stdio: options.capture ? ["ignore", "pipe", "pipe"] : "inherit"
|
|
196
|
+
});
|
|
197
|
+
if (result.status !== 0) {
|
|
198
|
+
const detail = result.stderr?.trim() || result.stdout?.trim() || `git exited with ${result.status}`;
|
|
199
|
+
throw new Error(detail);
|
|
200
|
+
}
|
|
201
|
+
return options.capture ? result.stdout.trim() : "";
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function printHelp() {
|
|
205
|
+
console.log(`Usage: npm run release -- [patch|minor|major|x.y.z] [options]
|
|
206
|
+
|
|
207
|
+
Cuts a release commit and annotated git tag. It does not publish to npm.
|
|
208
|
+
|
|
209
|
+
Options:
|
|
210
|
+
--dry-run Preview version and changelog changes
|
|
211
|
+
--push Push the current branch and release tag after tagging
|
|
212
|
+
--skip-checks Skip npm run lint and npm test
|
|
213
|
+
`);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
main().catch((error) => {
|
|
217
|
+
console.error(error.message);
|
|
218
|
+
process.exitCode = 1;
|
|
219
|
+
});
|
package/src/cli.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { Command } from "commander";
|
|
2
2
|
import { PACKAGE_NAME, PACKAGE_VERSION, SUPPORTED_AGENTS } from "./constants.js";
|
|
3
|
+
import { configureHelp, formatMissingSourceError, ui } from "./help.js";
|
|
3
4
|
import {
|
|
4
5
|
initProfile,
|
|
5
6
|
installFromSource,
|
|
@@ -19,16 +20,20 @@ export async function run(argv = process.argv) {
|
|
|
19
20
|
.helpOption("-h, --help", "Show this help message")
|
|
20
21
|
.showHelpAfterError();
|
|
21
22
|
|
|
23
|
+
configureHelp(program);
|
|
24
|
+
|
|
22
25
|
addInstallCommand(program, "add");
|
|
23
26
|
addInstallCommand(program, "install");
|
|
24
27
|
|
|
25
28
|
program
|
|
26
29
|
.command("use")
|
|
27
|
-
.argument("
|
|
30
|
+
.argument("<source>", "Source plus profile, for example owner/repo@profile")
|
|
28
31
|
.description("Print one rendered agent profile without installing it")
|
|
29
32
|
.option("-s, --profile <profile>", "Profile slug to use")
|
|
30
33
|
.option("--skill <profile>", "Compatibility alias for --profile")
|
|
31
|
-
.option("-a, --agent <agent>",
|
|
34
|
+
.option("-a, --agent <agent>", "Agent profile slug to use; accepts a harness name for backward compatibility")
|
|
35
|
+
.option("--harness <harness>", `Target harness (${SUPPORTED_AGENTS.join(", ")})`)
|
|
36
|
+
.option("--target <harness>", "Compatibility alias for --harness")
|
|
32
37
|
.option("--json", "Output JSON")
|
|
33
38
|
.option("--home <dir>", "Override HOME for path expansion")
|
|
34
39
|
.action(async (source = undefined, options) => {
|
|
@@ -77,7 +82,7 @@ export async function run(argv = process.argv) {
|
|
|
77
82
|
if (options.json) {
|
|
78
83
|
printJson(result);
|
|
79
84
|
} else {
|
|
80
|
-
printOperations(result.operations, result.registryPath);
|
|
85
|
+
printOperations(result.operations, result.registryPath, result.runInstructions);
|
|
81
86
|
}
|
|
82
87
|
});
|
|
83
88
|
|
|
@@ -98,7 +103,7 @@ export async function run(argv = process.argv) {
|
|
|
98
103
|
if (options.json) {
|
|
99
104
|
printJson(result);
|
|
100
105
|
} else {
|
|
101
|
-
printOperations(result.operations, result.registryPath);
|
|
106
|
+
printOperations(result.operations, result.registryPath, result.runInstructions);
|
|
102
107
|
}
|
|
103
108
|
});
|
|
104
109
|
|
|
@@ -114,9 +119,9 @@ export async function run(argv = process.argv) {
|
|
|
114
119
|
if (options.json) {
|
|
115
120
|
printJson(result);
|
|
116
121
|
} else {
|
|
117
|
-
console.log(`${result.action}:`);
|
|
122
|
+
console.log(`${ui.success(result.action)}:`);
|
|
118
123
|
for (const file of result.files) {
|
|
119
|
-
console.log(` ${file}`);
|
|
124
|
+
console.log(` ${ui.path(file)}`);
|
|
120
125
|
}
|
|
121
126
|
}
|
|
122
127
|
});
|
|
@@ -135,8 +140,10 @@ function addInstallCommand(program, commandName) {
|
|
|
135
140
|
.argument("[source]", "Local path, GitHub owner/repo, or GitHub URL")
|
|
136
141
|
.description(commandName === "install" ? "Alias for add" : "Install agent profiles from a source")
|
|
137
142
|
.option("-g, --global", "Install globally")
|
|
138
|
-
.option("-p, --project", "Install into the current project
|
|
139
|
-
.option("-a, --agent <agents...>",
|
|
143
|
+
.option("-p, --project", "Install into the current project; not supported for Codex profiles")
|
|
144
|
+
.option("-a, --agent <agents...>", "Agent profile slugs to install; accepts harness names for backward compatibility")
|
|
145
|
+
.option("--harness <harnesses...>", `Target harnesses (${SUPPORTED_AGENTS.join(", ")}, or *)`)
|
|
146
|
+
.option("--target <harnesses...>", "Compatibility alias for --harness")
|
|
140
147
|
.option("-s, --profile <profiles...>", "Profile slugs to install (or *)")
|
|
141
148
|
.option("--skill <profiles...>", "Compatibility alias for --profile")
|
|
142
149
|
.option("-l, --list", "List available profiles in the source without installing")
|
|
@@ -147,6 +154,12 @@ function addInstallCommand(program, commandName) {
|
|
|
147
154
|
.option("--json", "Output JSON")
|
|
148
155
|
.option("--home <dir>", "Override HOME for path expansion")
|
|
149
156
|
.action(async (source = undefined, options) => {
|
|
157
|
+
if (!source) {
|
|
158
|
+
process.stderr.write(formatMissingSourceError(commandName));
|
|
159
|
+
process.exitCode = 1;
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
|
|
150
163
|
if (options.list) {
|
|
151
164
|
const profiles = await listAvailable(source, options);
|
|
152
165
|
if (options.json) {
|
|
@@ -161,48 +174,72 @@ function addInstallCommand(program, commandName) {
|
|
|
161
174
|
if (options.json) {
|
|
162
175
|
printJson(result);
|
|
163
176
|
} else {
|
|
164
|
-
printOperations(result.operations, result.registryPath);
|
|
177
|
+
printOperations(result.operations, result.registryPath, result.runInstructions);
|
|
165
178
|
}
|
|
166
179
|
});
|
|
167
180
|
}
|
|
168
181
|
|
|
169
182
|
function printAvailable(profiles) {
|
|
170
183
|
if (profiles.length === 0) {
|
|
171
|
-
console.log("No profiles found.");
|
|
184
|
+
console.log(ui.warning("No profiles found."));
|
|
172
185
|
return;
|
|
173
186
|
}
|
|
187
|
+
console.log(ui.bold("Available profiles:"));
|
|
174
188
|
for (const profile of profiles) {
|
|
175
|
-
console.log(
|
|
189
|
+
console.log(` ${ui.profile(profile.slug)} ${profile.summary || profile.name}`);
|
|
176
190
|
}
|
|
177
191
|
}
|
|
178
192
|
|
|
179
193
|
function printInstalled(result) {
|
|
180
194
|
if (result.installs.length === 0) {
|
|
181
|
-
console.log(`No ${result.scope} profiles installed.`);
|
|
195
|
+
console.log(ui.warning(`No ${result.scope} profiles installed.`));
|
|
182
196
|
return;
|
|
183
197
|
}
|
|
184
198
|
|
|
199
|
+
console.log(ui.bold(`${result.scope} profiles:`));
|
|
185
200
|
for (const install of result.installs) {
|
|
186
|
-
const missing = install.exists ? "" :
|
|
187
|
-
console.log(
|
|
201
|
+
const missing = install.exists ? "" : ` ${ui.warning("(missing target)")}`;
|
|
202
|
+
console.log(` ${ui.profile(install.profile)} ${ui.command(install.harness)} ${ui.path(install.target)}${missing}`);
|
|
188
203
|
}
|
|
189
|
-
console.log(
|
|
204
|
+
console.log(`${ui.bold("Registry:")} ${ui.path(result.registryPath)}`);
|
|
190
205
|
}
|
|
191
206
|
|
|
192
|
-
function printOperations(operations, registryPath) {
|
|
207
|
+
function printOperations(operations, registryPath, runInstructions = []) {
|
|
193
208
|
if (operations.length === 0) {
|
|
194
|
-
console.log("No matching profiles.");
|
|
209
|
+
console.log(ui.warning("No matching profiles."));
|
|
195
210
|
return;
|
|
196
211
|
}
|
|
197
212
|
|
|
198
213
|
for (const operation of operations) {
|
|
199
|
-
console.log(`${operation.action}: ${operation.profile} -> ${operation.harness} at ${operation.target}`);
|
|
214
|
+
console.log(`${formatAction(operation.action)}: ${ui.profile(operation.profile)} -> ${ui.command(operation.harness)} at ${ui.path(operation.target)}`);
|
|
200
215
|
}
|
|
201
216
|
if (registryPath) {
|
|
202
|
-
console.log(
|
|
217
|
+
console.log(`${ui.bold("Registry:")} ${ui.path(registryPath)}`);
|
|
218
|
+
}
|
|
219
|
+
printRunInstructions(runInstructions);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
function printRunInstructions(runInstructions = []) {
|
|
223
|
+
if (runInstructions.length === 0) {
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
console.log(ui.bold("Run installed profiles:"));
|
|
228
|
+
for (const instruction of runInstructions) {
|
|
229
|
+
console.log(` ${ui.profile(instruction.profile)} via ${ui.command(instruction.harness)}: ${ui.command(instruction.command)}`);
|
|
230
|
+
if (instruction.note) {
|
|
231
|
+
console.log(` ${instruction.note}`);
|
|
232
|
+
}
|
|
203
233
|
}
|
|
204
234
|
}
|
|
205
235
|
|
|
206
236
|
function printJson(value) {
|
|
207
237
|
console.log(JSON.stringify(value, null, 2));
|
|
208
238
|
}
|
|
239
|
+
|
|
240
|
+
function formatAction(action) {
|
|
241
|
+
if (action.startsWith("would-")) {
|
|
242
|
+
return ui.warning(action);
|
|
243
|
+
}
|
|
244
|
+
return ui.success(action);
|
|
245
|
+
}
|
package/src/constants.js
CHANGED
|
@@ -1,6 +1,5 @@
|
|
|
1
1
|
export const PACKAGE_NAME = "awesome-agents";
|
|
2
|
-
export const PACKAGE_VERSION = "0.1.
|
|
3
|
-
export const DEFAULT_SOURCE = "pablof7z/touch-grass";
|
|
2
|
+
export const PACKAGE_VERSION = "0.1.3";
|
|
4
3
|
export const DEFAULT_AGENT = "codex";
|
|
5
4
|
export const SUPPORTED_AGENTS = ["codex", "claude-code", "opencode"];
|
|
6
5
|
export const REGISTRY_DIRNAME = ".awesome-agents";
|