@callmeradical/augy 0.4.0 → 0.6.0
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/dist/author-G46NJ3OW.js +109 -0
- package/dist/{chunk-RBF4Q6N4.js → chunk-A3USXURD.js} +43 -14
- package/dist/{home-TWK3PMCL.js → home-AEZ5IVC3.js} +89 -47
- package/dist/index.js +16 -7
- package/dist/{install-7ZWHGCFN.js → install-27YDQ46H.js} +1 -1
- package/dist/{scan-4KFMTKHO.js → scan-6HFU774V.js} +1 -1
- package/package.json +2 -1
- package/skills/author/SKILL.md +81 -0
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENTS,
|
|
3
|
+
agentSkillPath,
|
|
4
|
+
detectInstalledAgents
|
|
5
|
+
} from "./chunk-V5GA2ZHU.js";
|
|
6
|
+
import {
|
|
7
|
+
createSkillRecord,
|
|
8
|
+
getSkill,
|
|
9
|
+
readRegistry,
|
|
10
|
+
upsertSkill,
|
|
11
|
+
writeRegistry
|
|
12
|
+
} from "./chunk-JM4VVAHN.js";
|
|
13
|
+
|
|
14
|
+
// src/commands/author.ts
|
|
15
|
+
import { intro, outro, multiselect, isCancel } from "@clack/prompts";
|
|
16
|
+
import chalk from "chalk";
|
|
17
|
+
import { mkdir, writeFile } from "fs/promises";
|
|
18
|
+
import { join } from "path";
|
|
19
|
+
import { spawn } from "child_process";
|
|
20
|
+
function skillTemplate(name) {
|
|
21
|
+
return `# Skill: ${name}
|
|
22
|
+
|
|
23
|
+
## Overview
|
|
24
|
+
|
|
25
|
+
<!-- Describe what this skill does and when to use it. -->
|
|
26
|
+
|
|
27
|
+
## Workflow
|
|
28
|
+
|
|
29
|
+
<!-- Step-by-step instructions for the agent. -->
|
|
30
|
+
|
|
31
|
+
1.
|
|
32
|
+
|
|
33
|
+
## Notes
|
|
34
|
+
|
|
35
|
+
<!-- Tips, caveats, or references. -->
|
|
36
|
+
`;
|
|
37
|
+
}
|
|
38
|
+
async function authorNewCommand(name, opts = {}) {
|
|
39
|
+
intro(chalk.bold("augy") + chalk.dim(` \u2014 author new ${name}`));
|
|
40
|
+
const registry = await readRegistry();
|
|
41
|
+
if (getSkill(registry, name)) {
|
|
42
|
+
throw new Error(`Skill "${name}" already exists in the registry. Use \`augy author edit ${name}\` to open it.`);
|
|
43
|
+
}
|
|
44
|
+
const available = detectInstalledAgents();
|
|
45
|
+
if (!available.length) {
|
|
46
|
+
throw new Error("No agents detected. Install an agent first.");
|
|
47
|
+
}
|
|
48
|
+
const selected = available.length === 1 ? [available[0].id] : await multiselect({
|
|
49
|
+
message: "Install for which agents?",
|
|
50
|
+
options: available.map((a) => ({ value: a.id, label: a.name })),
|
|
51
|
+
initialValues: available.map((a) => a.id)
|
|
52
|
+
});
|
|
53
|
+
if (isCancel(selected)) {
|
|
54
|
+
console.log(chalk.dim("Cancelled."));
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
const targetAgents = AGENTS.filter((a) => selected.includes(a.id));
|
|
58
|
+
const agentPaths = {};
|
|
59
|
+
for (const agent of targetAgents) {
|
|
60
|
+
const dest = agentSkillPath(agent, name);
|
|
61
|
+
agentPaths[agent.id] = dest;
|
|
62
|
+
await mkdir(dest, { recursive: true });
|
|
63
|
+
await writeFile(join(dest, "SKILL.md"), skillTemplate(name), "utf8");
|
|
64
|
+
}
|
|
65
|
+
const record = createSkillRecord({
|
|
66
|
+
name,
|
|
67
|
+
source: "",
|
|
68
|
+
gigetSource: "",
|
|
69
|
+
sha: "unversioned",
|
|
70
|
+
agentIds: targetAgents.map((a) => a.id),
|
|
71
|
+
agentPaths
|
|
72
|
+
});
|
|
73
|
+
upsertSkill(registry, record);
|
|
74
|
+
await writeRegistry(registry);
|
|
75
|
+
const firstPath = agentPaths[targetAgents[0].id];
|
|
76
|
+
outro(
|
|
77
|
+
`${chalk.green("\u2713")} ${chalk.cyan(name)} created
|
|
78
|
+
` + chalk.dim(` ${firstPath}
|
|
79
|
+
`) + chalk.dim(" Run `augy home push` when ready to back it up.")
|
|
80
|
+
);
|
|
81
|
+
if (!opts.noEdit) {
|
|
82
|
+
openInEditor(firstPath);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
async function authorEditCommand(name) {
|
|
86
|
+
const registry = await readRegistry();
|
|
87
|
+
const skill = getSkill(registry, name);
|
|
88
|
+
if (!skill) {
|
|
89
|
+
throw new Error(`Skill "${name}" not found in registry. Run \`augy list\` to see installed skills.`);
|
|
90
|
+
}
|
|
91
|
+
const agentPath = Object.values(skill.agents).find((a) => a.active)?.path;
|
|
92
|
+
if (!agentPath) {
|
|
93
|
+
throw new Error(`No active agent path found for "${name}".`);
|
|
94
|
+
}
|
|
95
|
+
console.log(chalk.dim(`Opening ${agentPath}\u2026`));
|
|
96
|
+
openInEditor(agentPath);
|
|
97
|
+
}
|
|
98
|
+
function openInEditor(path) {
|
|
99
|
+
const editor = process.env["EDITOR"] ?? process.env["VISUAL"] ?? "vi";
|
|
100
|
+
const child = spawn(editor, [path], {
|
|
101
|
+
stdio: "inherit",
|
|
102
|
+
detached: true
|
|
103
|
+
});
|
|
104
|
+
child.unref();
|
|
105
|
+
}
|
|
106
|
+
export {
|
|
107
|
+
authorEditCommand,
|
|
108
|
+
authorNewCommand
|
|
109
|
+
};
|
|
@@ -416,6 +416,7 @@ async function filterableMultiselect(opts) {
|
|
|
416
416
|
const allOptions = opts.options.map((o) => ({ value: o.value, label: o.label, hint: o.hint }));
|
|
417
417
|
const initialValues = opts.options.filter((o) => o.selected).map((o) => o.value);
|
|
418
418
|
let filterText = "";
|
|
419
|
+
let filterMode = false;
|
|
419
420
|
let matchCount = allOptions.length;
|
|
420
421
|
const pageSize = opts.pageSize ?? Math.min(14, process2.stdout.rows - 6);
|
|
421
422
|
const prompt = new wD({
|
|
@@ -438,11 +439,11 @@ ${headerPrefix} ${opts.message}` + chalk.dim(` (${allOptions.length} total)
|
|
|
438
439
|
${chalk.gray(S_BAR)}
|
|
439
440
|
`;
|
|
440
441
|
}
|
|
441
|
-
const cursor = filterText.length > 0 ? chalk.inverse(" ") : chalk.inverse(chalk.hidden("_"));
|
|
442
442
|
const noMatch = matchCount === 0 && filterText.length > 0;
|
|
443
443
|
const matchLabel = noMatch ? chalk.red("no matches") : filterText ? chalk.dim(`${matchCount} matched`) : chalk.dim(`${allOptions.length} skills`);
|
|
444
|
-
const hint = chalk.dim("
|
|
445
|
-
const
|
|
444
|
+
const hint = filterMode ? chalk.dim("esc to clear \xB7 enter confirm") : chalk.dim("/ filter \xB7 a all \xB7 space select \xB7 enter confirm");
|
|
445
|
+
const filterDisplay = filterMode ? chalk.white(filterText) + chalk.inverse(" ") : filterText ? chalk.dim("/ ") + chalk.white(filterText) + chalk.dim(" (/ to edit)") : chalk.dim("/ to filter");
|
|
446
|
+
const filterLine = `${filterMode ? chalk.cyan(S_BAR) : chalk.gray(S_BAR)} ` + filterDisplay + ` ${matchLabel} ${hint}`;
|
|
446
447
|
const visibleOptions = paginate(
|
|
447
448
|
this.options,
|
|
448
449
|
this.cursor,
|
|
@@ -471,19 +472,47 @@ ${chalk.cyan(S_BAR_END)}
|
|
|
471
472
|
}
|
|
472
473
|
});
|
|
473
474
|
prompt.on("key", (key) => {
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
475
|
+
const p = prompt;
|
|
476
|
+
if (filterMode) {
|
|
477
|
+
if (key === "\x1B") {
|
|
478
|
+
filterMode = false;
|
|
479
|
+
filterText = "";
|
|
480
|
+
} else if (key === "\b" || key === "\x7F" || key === "backspace") {
|
|
481
|
+
if (filterText.length > 0) {
|
|
482
|
+
filterText = filterText.slice(0, -1);
|
|
483
|
+
} else {
|
|
484
|
+
filterMode = false;
|
|
485
|
+
}
|
|
486
|
+
} else if (key === "/") {
|
|
487
|
+
filterMode = false;
|
|
488
|
+
return;
|
|
489
|
+
} else if (key && key.length === 1 && key.charCodeAt(0) >= 32 && key !== " ") {
|
|
490
|
+
filterText += key;
|
|
491
|
+
} else {
|
|
492
|
+
return;
|
|
493
|
+
}
|
|
494
|
+
const filtered = filterText ? allOptions.filter((o) => o.label.toLowerCase().includes(filterText.toLowerCase())) : allOptions;
|
|
495
|
+
matchCount = filtered.length;
|
|
496
|
+
p.options = filtered;
|
|
497
|
+
if (p.cursor >= filtered.length) p.cursor = Math.max(0, filtered.length - 1);
|
|
498
|
+
return;
|
|
499
|
+
}
|
|
500
|
+
if (key === "/") {
|
|
501
|
+
filterMode = true;
|
|
480
502
|
return;
|
|
481
503
|
}
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
504
|
+
if (key === "a") {
|
|
505
|
+
const visibleValues = p.options.map((o) => o.value);
|
|
506
|
+
const currentSelected = Array.isArray(p.value) ? p.value : [];
|
|
507
|
+
const allVisible = visibleValues.every((v2) => currentSelected.includes(v2));
|
|
508
|
+
if (allVisible) {
|
|
509
|
+
p.value = currentSelected.filter((v2) => !visibleValues.includes(v2));
|
|
510
|
+
} else {
|
|
511
|
+
const union = [...currentSelected];
|
|
512
|
+
for (const v2 of visibleValues) if (!union.includes(v2)) union.push(v2);
|
|
513
|
+
p.value = union;
|
|
514
|
+
}
|
|
515
|
+
return;
|
|
487
516
|
}
|
|
488
517
|
});
|
|
489
518
|
const result = await prompt.prompt();
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import {
|
|
2
|
+
filterableMultiselect
|
|
3
|
+
} from "./chunk-A3USXURD.js";
|
|
1
4
|
import {
|
|
2
5
|
AGENTS,
|
|
3
6
|
agentSkillPath,
|
|
@@ -14,7 +17,7 @@ import {
|
|
|
14
17
|
} from "./chunk-JM4VVAHN.js";
|
|
15
18
|
|
|
16
19
|
// src/commands/home.ts
|
|
17
|
-
import { intro, outro, spinner } from "@clack/prompts";
|
|
20
|
+
import { intro, isCancel, multiselect, outro, spinner } from "@clack/prompts";
|
|
18
21
|
import chalk from "chalk";
|
|
19
22
|
import { cp, mkdir, readdir, writeFile } from "fs/promises";
|
|
20
23
|
import { existsSync } from "fs";
|
|
@@ -98,21 +101,20 @@ async function homePushCommand() {
|
|
|
98
101
|
s.start(`Cloning ${chalk.cyan(home.repo)}\u2026`);
|
|
99
102
|
await cloneRepo(repoToUrl(home.repo), cloneDir);
|
|
100
103
|
s.stop(`${chalk.green("\u2713")} Cloned`);
|
|
101
|
-
const homeRepo = home.repo;
|
|
102
104
|
for (const skill of authored) {
|
|
103
105
|
const sourcePath = Object.values(skill.agents).find((a) => a.active && existsSync(a.path))?.path;
|
|
104
106
|
if (!sourcePath) continue;
|
|
105
107
|
const destPath = home.skillsPath ? join(cloneDir, home.skillsPath, skill.name) : join(cloneDir, skill.name);
|
|
106
108
|
await mkdir(destPath, { recursive: true });
|
|
107
109
|
await cp(sourcePath, destPath, { recursive: true, force: true });
|
|
108
|
-
const newSource = `https://github.com/${homeRepo}/tree/main/${home.skillsPath ? home.skillsPath + "/" : ""}${skill.name}`;
|
|
109
|
-
skill.source = newSource;
|
|
110
|
-
upsertSkill(registry, skill);
|
|
111
110
|
}
|
|
112
111
|
const bundle = { version: 1, skills: {} };
|
|
113
|
-
for (const skill of
|
|
112
|
+
for (const skill of external) {
|
|
114
113
|
bundle.skills[skill.name] = skill.source;
|
|
115
114
|
}
|
|
115
|
+
for (const skill of authored) {
|
|
116
|
+
bundle.skills[skill.name] = "";
|
|
117
|
+
}
|
|
116
118
|
await writeFile(
|
|
117
119
|
join(cloneDir, home.path),
|
|
118
120
|
JSON.stringify(bundle, null, 2) + "\n",
|
|
@@ -125,8 +127,6 @@ async function homePushCommand() {
|
|
|
125
127
|
const authoredNote = authored.length ? ` (${authored.length} authored)` : "";
|
|
126
128
|
await gitCommit(cloneDir, `chore: update skills via augy \u2014 ${skillCount} skill(s)${authoredNote}`);
|
|
127
129
|
await gitPush(cloneDir);
|
|
128
|
-
s2.stop(`${chalk.green("\u2713")} Pushed`);
|
|
129
|
-
if (authored.length) await writeRegistry(registry);
|
|
130
130
|
outro(
|
|
131
131
|
`${chalk.bold(String(skillCount))} skill(s) saved to ${chalk.cyan(home.repo)}
|
|
132
132
|
` + (authored.length ? chalk.dim(` ${authored.length} authored skill(s) committed + source registered
|
|
@@ -138,9 +138,7 @@ async function homePullCommand(opts = {}) {
|
|
|
138
138
|
const registry = await readRegistry();
|
|
139
139
|
const home = getHomeConfig(registry);
|
|
140
140
|
if (!home) {
|
|
141
|
-
console.error(
|
|
142
|
-
chalk.red("No home repo configured.") + "\nRun `augy home set <owner/repo>` first."
|
|
143
|
-
);
|
|
141
|
+
console.error(chalk.red("No home repo configured.") + "\nRun `augy home set <owner/repo>` first.");
|
|
144
142
|
process.exit(1);
|
|
145
143
|
}
|
|
146
144
|
const cloneDir = join(tmpdir(), `augy-home-pull-${Date.now()}`);
|
|
@@ -148,53 +146,97 @@ async function homePullCommand(opts = {}) {
|
|
|
148
146
|
s.start(`Cloning ${chalk.cyan(home.repo)}\u2026`);
|
|
149
147
|
await cloneRepo(repoToUrl(home.repo), cloneDir);
|
|
150
148
|
s.stop(`${chalk.green("\u2713")} Cloned`);
|
|
151
|
-
const
|
|
152
|
-
if (!targetAgents.length) {
|
|
153
|
-
console.error(chalk.red("No agents detected. Install an agent or use --agent to specify one."));
|
|
154
|
-
process.exit(1);
|
|
155
|
-
}
|
|
149
|
+
const available = [];
|
|
156
150
|
const skillsDir = home.skillsPath ? join(cloneDir, home.skillsPath) : cloneDir;
|
|
157
|
-
const authoredInstalled = [];
|
|
158
151
|
if (existsSync(skillsDir)) {
|
|
159
152
|
const entries = await readdir(skillsDir, { withFileTypes: true });
|
|
160
|
-
const
|
|
161
|
-
|
|
162
|
-
const skillName = dir.name;
|
|
163
|
-
const srcPath = join(skillsDir, skillName);
|
|
164
|
-
const source = `https://github.com/${home.repo}/tree/main/${home.skillsPath ? home.skillsPath + "/" : ""}${skillName}`;
|
|
165
|
-
if (opts.dryRun) {
|
|
166
|
-
console.log(` ${chalk.cyan("+")} ${skillName} ${chalk.dim("(authored)")}`);
|
|
167
|
-
continue;
|
|
168
|
-
}
|
|
169
|
-
const agentPaths = {};
|
|
170
|
-
for (const agent of targetAgents) {
|
|
171
|
-
const dest = agentSkillPath(agent, skillName);
|
|
172
|
-
await mkdir(dest, { recursive: true });
|
|
173
|
-
await cp(srcPath, dest, { recursive: true, force: true });
|
|
174
|
-
agentPaths[agent.id] = dest;
|
|
175
|
-
}
|
|
176
|
-
const record = createSkillRecord({
|
|
177
|
-
name: skillName,
|
|
178
|
-
source,
|
|
179
|
-
gigetSource: "",
|
|
180
|
-
sha: "home",
|
|
181
|
-
agentIds: targetAgents.map((a) => a.id),
|
|
182
|
-
agentPaths
|
|
183
|
-
});
|
|
184
|
-
upsertSkill(registry, record);
|
|
185
|
-
authoredInstalled.push(skillName);
|
|
153
|
+
for (const e of entries) {
|
|
154
|
+
if (e.isDirectory()) available.push({ name: e.name, source: "", isAuthored: true });
|
|
186
155
|
}
|
|
187
156
|
}
|
|
188
157
|
const manifestPath = join(cloneDir, home.path);
|
|
189
158
|
if (existsSync(manifestPath)) {
|
|
190
|
-
const {
|
|
191
|
-
await
|
|
159
|
+
const { readFile } = await import("fs/promises");
|
|
160
|
+
const bundle = JSON.parse(await readFile(manifestPath, "utf8"));
|
|
161
|
+
for (const [name, source] of Object.entries(bundle.skills)) {
|
|
162
|
+
if (source && !available.find((s2) => s2.name === name)) {
|
|
163
|
+
available.push({ name, source, isAuthored: false });
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (!available.length) {
|
|
168
|
+
outro(chalk.dim("No skills found in home repo."));
|
|
169
|
+
return;
|
|
170
|
+
}
|
|
171
|
+
const selected = await filterableMultiselect({
|
|
172
|
+
message: `Select skills to install ${chalk.dim(`(${available.length} available)`)}`,
|
|
173
|
+
options: available.map((sk) => ({
|
|
174
|
+
value: sk,
|
|
175
|
+
label: sk.name,
|
|
176
|
+
hint: sk.isAuthored ? chalk.dim("authored") : chalk.dim(sk.source),
|
|
177
|
+
selected: true
|
|
178
|
+
}))
|
|
179
|
+
});
|
|
180
|
+
if (isCancel(selected) || !selected.length) {
|
|
181
|
+
console.log(chalk.dim("Cancelled."));
|
|
182
|
+
process.exit(0);
|
|
183
|
+
}
|
|
184
|
+
const toInstall = selected;
|
|
185
|
+
let targetAgents = opts.agent?.length ? AGENTS.filter((a) => opts.agent.includes(a.id)) : detectInstalledAgents();
|
|
186
|
+
if (!opts.agent?.length) {
|
|
187
|
+
const agentResult = await multiselect({
|
|
188
|
+
message: "Install to which agents?",
|
|
189
|
+
options: targetAgents.map((a) => ({ value: a.id, label: a.name })),
|
|
190
|
+
initialValues: targetAgents.map((a) => a.id)
|
|
191
|
+
});
|
|
192
|
+
if (isCancel(agentResult)) {
|
|
193
|
+
console.log(chalk.dim("Cancelled."));
|
|
194
|
+
process.exit(0);
|
|
195
|
+
}
|
|
196
|
+
targetAgents = AGENTS.filter((a) => agentResult.includes(a.id));
|
|
192
197
|
}
|
|
193
|
-
if (!
|
|
194
|
-
|
|
198
|
+
if (!targetAgents.length) {
|
|
199
|
+
console.error(chalk.red("No agents selected."));
|
|
200
|
+
process.exit(1);
|
|
195
201
|
}
|
|
196
202
|
if (opts.dryRun) {
|
|
203
|
+
for (const sk of toInstall) {
|
|
204
|
+
console.log(` ${chalk.cyan("+")} ${sk.name} ${chalk.dim(sk.isAuthored ? "authored" : sk.source)}`);
|
|
205
|
+
}
|
|
197
206
|
outro(chalk.dim("Dry run \u2014 no changes made."));
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
const authoredToInstall = toInstall.filter((sk) => sk.isAuthored);
|
|
210
|
+
for (const sk of authoredToInstall) {
|
|
211
|
+
const srcPath = join(skillsDir, sk.name);
|
|
212
|
+
const agentPaths = {};
|
|
213
|
+
for (const agent of targetAgents) {
|
|
214
|
+
const dest = agentSkillPath(agent, sk.name);
|
|
215
|
+
await mkdir(dest, { recursive: true });
|
|
216
|
+
await cp(srcPath, dest, { recursive: true, force: true });
|
|
217
|
+
agentPaths[agent.id] = dest;
|
|
218
|
+
}
|
|
219
|
+
const record = createSkillRecord({
|
|
220
|
+
name: sk.name,
|
|
221
|
+
source: "",
|
|
222
|
+
gigetSource: "",
|
|
223
|
+
sha: "home",
|
|
224
|
+
agentIds: targetAgents.map((a) => a.id),
|
|
225
|
+
agentPaths
|
|
226
|
+
});
|
|
227
|
+
upsertSkill(registry, record);
|
|
228
|
+
}
|
|
229
|
+
if (authoredToInstall.length) await writeRegistry(registry);
|
|
230
|
+
const externalToInstall = toInstall.filter((sk) => !sk.isAuthored);
|
|
231
|
+
if (externalToInstall.length) {
|
|
232
|
+
const { tmpdir: td } = await import("os");
|
|
233
|
+
const { writeFile: wf } = await import("fs/promises");
|
|
234
|
+
const filteredBundle = { version: 1, skills: {} };
|
|
235
|
+
for (const sk of externalToInstall) filteredBundle.skills[sk.name] = sk.source;
|
|
236
|
+
const tmpManifest = join(td(), `augy-home-pull-manifest-${Date.now()}.json`);
|
|
237
|
+
await wf(tmpManifest, JSON.stringify(filteredBundle, null, 2) + "\n", "utf8");
|
|
238
|
+
const { syncCommand } = await import("./sync-QQFXUXCN.js");
|
|
239
|
+
await syncCommand(tmpManifest, { ...opts, agent: targetAgents.map((a) => a.id) });
|
|
198
240
|
}
|
|
199
241
|
}
|
|
200
242
|
export {
|
package/dist/index.js
CHANGED
|
@@ -10,7 +10,7 @@ var pkg = JSON.parse(readFileSync(join(__dirname, "..", "package.json"), "utf8")
|
|
|
10
10
|
var program = new Command();
|
|
11
11
|
program.name("augy").description("Homebrew for AI agent skills \u2014 install, version, update, rollback").version(pkg.version);
|
|
12
12
|
program.command("install [url]").description("Install skills from a GitHub URL or owner/repo[/path]").option("-a, --agent <agents...>", "Target agent(s): opencode, claude, codex").action(async (url, opts) => {
|
|
13
|
-
const { installCommand } = await import("./install-
|
|
13
|
+
const { installCommand } = await import("./install-27YDQ46H.js");
|
|
14
14
|
await installCommand(url, opts ?? {});
|
|
15
15
|
});
|
|
16
16
|
program.command("update [skill]").description("Check for upstream changes and upgrade installed skills").action(async (skill) => {
|
|
@@ -36,7 +36,7 @@ program.command("sync [path]").description("Install/update skills from an augy.j
|
|
|
36
36
|
await syncCommand(path, opts ?? {});
|
|
37
37
|
});
|
|
38
38
|
program.command("scan").description("Find skills installed outside augy and optionally import them into the registry").action(async () => {
|
|
39
|
-
const { scanCommand } = await import("./scan-
|
|
39
|
+
const { scanCommand } = await import("./scan-6HFU774V.js");
|
|
40
40
|
await scanCommand();
|
|
41
41
|
});
|
|
42
42
|
program.command("info <skill>").description("Show full metadata, version history, and description for an installed skill").action(async (skill) => {
|
|
@@ -78,25 +78,34 @@ program.command("pin <skill>").description("Pin a skill so it is skipped during
|
|
|
78
78
|
program.command("unpin <skill>").description("Allow a pinned skill to receive updates again").action(async (skill) => {
|
|
79
79
|
await setPinned(skill, false);
|
|
80
80
|
});
|
|
81
|
+
var author = program.command("author").description("Create and edit self-authored skills");
|
|
82
|
+
author.command("new <name>").description("Scaffold a new skill and open it in $EDITOR").option("--no-edit", "Skip opening the editor after creating").action(async (name, opts) => {
|
|
83
|
+
const { authorNewCommand } = await import("./author-G46NJ3OW.js");
|
|
84
|
+
await authorNewCommand(name, { noEdit: !opts.edit });
|
|
85
|
+
});
|
|
86
|
+
author.command("edit <name>").description("Open an existing skill in $EDITOR").action(async (name) => {
|
|
87
|
+
const { authorEditCommand } = await import("./author-G46NJ3OW.js");
|
|
88
|
+
await authorEditCommand(name);
|
|
89
|
+
});
|
|
81
90
|
var home = program.command("home").description("Manage a personal GitHub repo for backing up your skills manifest");
|
|
82
91
|
home.command("set <repo>").description("Set the home repo e.g. augy home set alice/my-skills").option("--path <file>", "Manifest path within the repo (default: augy.json)").option("--skills-path <dir>", "Dir for authored skills in the repo (default: skills)").action(async (repo, opts) => {
|
|
83
|
-
const { homeSetCommand } = await import("./home-
|
|
92
|
+
const { homeSetCommand } = await import("./home-AEZ5IVC3.js");
|
|
84
93
|
await homeSetCommand(repo, opts);
|
|
85
94
|
});
|
|
86
95
|
home.command("push").description("Push your installed skills manifest to the home repo").action(async () => {
|
|
87
|
-
const { homePushCommand } = await import("./home-
|
|
96
|
+
const { homePushCommand } = await import("./home-AEZ5IVC3.js");
|
|
88
97
|
await homePushCommand();
|
|
89
98
|
});
|
|
90
99
|
home.command("pull").description("Fetch the manifest from the home repo and sync skills").option("--dry-run", "Preview changes without applying them").option("-a, --agent <agents...>", "Target agent(s) (default: all detected)").action(async (opts) => {
|
|
91
|
-
const { homePullCommand } = await import("./home-
|
|
100
|
+
const { homePullCommand } = await import("./home-AEZ5IVC3.js");
|
|
92
101
|
await homePullCommand(opts);
|
|
93
102
|
});
|
|
94
103
|
home.command("show").description("Show the current home repo configuration").action(async () => {
|
|
95
|
-
const { homeShowCommand } = await import("./home-
|
|
104
|
+
const { homeShowCommand } = await import("./home-AEZ5IVC3.js");
|
|
96
105
|
await homeShowCommand();
|
|
97
106
|
});
|
|
98
107
|
program.action(async () => {
|
|
99
|
-
const { installCommand } = await import("./install-
|
|
108
|
+
const { installCommand } = await import("./install-27YDQ46H.js");
|
|
100
109
|
await installCommand();
|
|
101
110
|
});
|
|
102
111
|
program.parse();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@callmeradical/augy",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.6.0",
|
|
4
4
|
"description": "Homebrew for AI agent skills — install, version, update, rollback",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -25,6 +25,7 @@
|
|
|
25
25
|
],
|
|
26
26
|
"files": [
|
|
27
27
|
"dist/**/*",
|
|
28
|
+
"skills/**/*",
|
|
28
29
|
"README.md",
|
|
29
30
|
"LICENSE"
|
|
30
31
|
],
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
# Skill: author
|
|
2
|
+
|
|
3
|
+
A meta-skill for authoring new augy skills. Use this when the user wants
|
|
4
|
+
to create a new skill for their AI agents.
|
|
5
|
+
|
|
6
|
+
## What is an augy skill?
|
|
7
|
+
|
|
8
|
+
A skill is a directory containing a `SKILL.md` file. That file is loaded
|
|
9
|
+
by an AI agent as context — giving it specialised knowledge, workflows,
|
|
10
|
+
or tool integrations for a specific task.
|
|
11
|
+
|
|
12
|
+
Skills are installed per-agent:
|
|
13
|
+
```
|
|
14
|
+
~/.opencode/skills/<name>/SKILL.md
|
|
15
|
+
~/.claude/skills/<name>/SKILL.md
|
|
16
|
+
~/.codex/skills/<name>/SKILL.md
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
## Workflow
|
|
20
|
+
|
|
21
|
+
### 1. Scaffold the skill
|
|
22
|
+
|
|
23
|
+
```sh
|
|
24
|
+
augy author new <name>
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
This creates the skill directory for your installed agents, registers it
|
|
28
|
+
in the augy registry, and opens it in `$EDITOR`.
|
|
29
|
+
|
|
30
|
+
### 2. Write the SKILL.md
|
|
31
|
+
|
|
32
|
+
A good SKILL.md has:
|
|
33
|
+
|
|
34
|
+
- **One-line description** — what this skill does and when to invoke it
|
|
35
|
+
- **Trigger phrases** — phrases the user says that should activate this skill
|
|
36
|
+
- **Workflow** — numbered steps the agent follows
|
|
37
|
+
- **Examples** — concrete inputs and expected outputs
|
|
38
|
+
- **References** — links to docs, files, or other skills
|
|
39
|
+
|
|
40
|
+
Keep it focused. One skill = one capability. If it grows beyond ~150 lines,
|
|
41
|
+
consider splitting it.
|
|
42
|
+
|
|
43
|
+
### 3. Test it
|
|
44
|
+
|
|
45
|
+
Load the skill into your agent and try the trigger phrases. Check that the
|
|
46
|
+
agent follows the workflow correctly.
|
|
47
|
+
|
|
48
|
+
### 4. Back it up
|
|
49
|
+
|
|
50
|
+
```sh
|
|
51
|
+
augy home push
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
This commits the skill to your home repo so it's available on all your machines.
|
|
55
|
+
|
|
56
|
+
## SKILL.md template
|
|
57
|
+
|
|
58
|
+
```markdown
|
|
59
|
+
# Skill: <name>
|
|
60
|
+
|
|
61
|
+
<One-sentence description of what this skill does.>
|
|
62
|
+
Trigger on: <phrase 1>, <phrase 2>, <phrase 3>.
|
|
63
|
+
|
|
64
|
+
## Workflow
|
|
65
|
+
|
|
66
|
+
1. <First step>
|
|
67
|
+
2. <Second step>
|
|
68
|
+
3. <Third step>
|
|
69
|
+
|
|
70
|
+
## Notes
|
|
71
|
+
|
|
72
|
+
<!-- Tips, caveats, edge cases -->
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
## Tips
|
|
76
|
+
|
|
77
|
+
- **Verb-first steps**: "Run the tests", not "Tests should be run"
|
|
78
|
+
- **Concrete over abstract**: Show exact commands, not just concepts
|
|
79
|
+
- **Fail states**: Tell the agent what to do when things go wrong
|
|
80
|
+
- **Scope**: If you find yourself writing "unless X, in which case Y", that's
|
|
81
|
+
a second skill
|