@callmeradical/augy 0.1.0 → 0.2.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/README.md CHANGED
@@ -1,6 +1,10 @@
1
1
  # augy
2
2
 
3
- **Homebrew for AI agent skills** — install, version, update, and rollback skills across OpenCode, Claude, and Codex from a single CLI.
3
+ [![npm](https://img.shields.io/npm/v/@callmeradical/augy?label=%40callmeradical%2Faugy&color=7c3aed)](https://www.npmjs.com/package/@callmeradical/augy)
4
+ [![license](https://img.shields.io/github/license/callmeradical/augy)](LICENSE)
5
+ [![node](https://img.shields.io/node/v/@callmeradical/augy)](package.json)
6
+
7
+ **Homebrew for AI agent skills.** No marketplace. No accounts. No central service. Just Git repos, a lockfile, and full control over what runs in your agents.
4
8
 
5
9
  ```
6
10
  augy install tdd # install by name via a tap
@@ -11,53 +15,87 @@ augy rollback tdd abc1234 # something broke — go back
11
15
 
12
16
  ---
13
17
 
18
+ ## The philosophy
19
+
20
+ Other skill managers route you through a hosted registry — a central service you have to trust, that can go down, that doesn't work with private repos, and that you can't use behind a firewall.
21
+
22
+ augy works differently. **Skills are just files in Git repos. GitHub is the registry.**
23
+
24
+ - Any GitHub repo is a valid skill source — public or private
25
+ - `~/.augy/registry.json` is your lockfile — a plain JSON file you can read, diff, commit, and share
26
+ - No account required. No API key for the registry. No SaaS dependency
27
+ - Works in enterprise environments, behind proxies, with internal repos
28
+ - You decide which repos to trust — not a third-party curation team
29
+
30
+ The tap system is Homebrew's model: register a repo once, install skills by name. Remove the tap and you've unregistered the source. Simple, transparent, yours.
31
+
32
+ ---
33
+
14
34
  ## Install
15
35
 
16
36
  ```bash
17
- npm install -g augy
37
+ npm install -g @callmeradical/augy
18
38
  ```
19
39
 
40
+ > The npm package is `@callmeradical/augy`. The CLI command is `augy`.
41
+
20
42
  Requires Node.js ≥ 18.
21
43
 
22
44
  ---
23
45
 
24
46
  ## How it works
25
47
 
26
- Skills are directories containing a `SKILL.md` file that an AI agent loads as context. augy tracks which skills you have installed, where they came from (GitHub), and what version (commit SHA) is on disk — then lets you update, diff, and roll back just like a package manager.
48
+ Skills are directories containing a `SKILL.md` file that an AI agent loads as context. augy tracks which skills you have installed, where they came from (a GitHub path), and what exact version (commit SHA) is on disk — then lets you update, diff, and roll back just like a package manager.
27
49
 
28
50
  **Supported agents**
29
51
 
30
- | Agent | Default skills path |
31
- |---|---|
32
- | OpenCode | `~/.opencode/skills/` |
33
- | Claude | `~/.claude/skills/` |
34
- | Codex | `~/.codex/skills/` (or `$CODEX_HOME/skills`) |
52
+ augy auto-detects which agents are installed on your machine and pre-selects them in the install prompt. All 33 are supported:
35
53
 
36
- A single skill can be deployed to multiple agents simultaneously.
54
+ Claude · OpenCode · Codex · Cursor · Windsurf · GitHub Copilot · Gemini CLI · Goose · Amp · Roo Code · Cline · Kiro · Kimi CLI · Kilo Code · Trae · Trae CN · Augment · OpenHands · Replit · Crush · Antigravity · Droid · OpenClaw · CodeBuddy · Command Code · Kode · Mistral Vibe · Mux · OpenClaude IDE · Qoder · Qwen Code · Neovate · AdaL
55
+
56
+ A single skill can be deployed to multiple agents simultaneously and stays in sync on every update.
57
+
58
+ ---
59
+
60
+ ## Quick start
61
+
62
+ ```bash
63
+ # 1. Add a tap — a GitHub repo you trust
64
+ augy tap add mattpocock/skills
65
+
66
+ # 2. See what's available
67
+ augy search
68
+
69
+ # 3. Install by name
70
+ augy install tdd
71
+
72
+ # Already have skills installed manually? Bring them under augy management:
73
+ augy scan
74
+ ```
37
75
 
38
76
  ---
39
77
 
40
78
  ## Commands
41
79
 
42
80
  ### `augy install [url]`
43
- Install skills from a GitHub URL, `owner/repo[/path]` shorthand, or a bare name resolved via a registered tap.
81
+ Install skills from a GitHub URL, `owner/repo[/path]` shorthand, or a bare name resolved via a tap. When a repo contains multiple skills, shows a filterable picker.
44
82
 
45
83
  ```bash
84
+ augy install tdd # via tap
85
+ augy install mattpocock/skills # all skills in a repo
46
86
  augy install https://github.com/mattpocock/skills/tree/main
47
- augy install mattpocock/skills/skills/engineering/tdd
48
- augy install tdd # resolves via taps
49
- augy install tdd --agent opencode # target a specific agent
87
+ augy install tdd --agent opencode claude # specific agents
50
88
  ```
51
89
 
52
90
  ### `augy scan`
53
- Find skills already on disk that augy doesn't know about. Auto-detects provenance via git remotes and `SKILL.md` frontmatter, groups results into *detected* vs *no provenance found* with filesystem paths shown for unknown skills. Imports them into the registry.
91
+ Walk all agent skill directories, find skills augy doesn't know about, auto-detect their GitHub source via git remotes and `SKILL.md` frontmatter, and import them into the registry. Skills without a detectable source show their filesystem path so you can look them up.
54
92
 
55
93
  ```bash
56
94
  augy scan
57
95
  ```
58
96
 
59
97
  ### `augy update [skill]`
60
- Check all installed skills for upstream SHA drift. Shows a list of available upgrades, lets you select which to apply, archives the current version before overwriting.
98
+ Check all installed skills for upstream SHA drift. Archives the current version, then deploys the new one.
61
99
 
62
100
  ```bash
63
101
  augy update # check + upgrade everything
@@ -70,19 +108,19 @@ Interactive file-level diff browser. Three modes:
70
108
  ```bash
71
109
  augy diff tdd # installed ↔ upstream HEAD
72
110
  augy diff tdd abc1234 # installed ↔ specific SHA
73
- augy diff tdd abc1234 def5678 # two local archives
111
+ augy diff tdd abc1234 def5678 # two local archives side-by-side
74
112
  ```
75
113
 
76
114
  ### `augy rollback <skill> [sha]`
77
- Restore a skill to any previously archived version.
115
+ Restore a skill to any previously archived version. Without a SHA, shows an interactive picker.
78
116
 
79
117
  ```bash
80
- augy rollback tdd # interactive version picker
81
- augy rollback tdd abc1234 # specific SHA (short or full)
118
+ augy rollback tdd
119
+ augy rollback tdd abc1234
82
120
  ```
83
121
 
84
122
  ### `augy list`
85
- Show all installed skills, their SHAs, agents, and update status.
123
+ Show all installed skills, their SHAs, agents, and version history.
86
124
 
87
125
  ```bash
88
126
  augy list
@@ -90,32 +128,63 @@ augy list --json # raw registry JSON
90
128
  ```
91
129
 
92
130
  ### `augy info <skill>`
93
- Full metadata: source, SHA, agents with paths, version history, and a preview of the skill description.
131
+ Full metadata: source, SHA, agents with paths, version history, and a description preview from `SKILL.md`.
94
132
 
95
133
  ```bash
96
134
  augy info tdd
97
135
  ```
98
136
 
99
137
  ### `augy search [query]`
100
- Search all registered taps for available skills.
138
+ Search all registered taps for available skills. Shows install status and whether each skill is up to date.
101
139
 
102
140
  ```bash
103
- augy search # full index
141
+ augy search # full index across all taps
104
142
  augy search tdd # filter by name
105
143
  ```
106
144
 
107
145
  ### `augy tap add|remove|list`
108
- Manage trusted repos (taps) — once added, skills can be installed by bare name.
146
+ Manage trusted repos. Once tapped, install by bare name.
109
147
 
110
148
  ```bash
111
149
  augy tap add mattpocock/skills
150
+ augy tap add org/internal-skills # private repos work too
112
151
  augy tap add mattpocock/skills --path skills/engineering
113
152
  augy tap list
114
153
  augy tap remove mattpocock/skills
115
154
  ```
116
155
 
156
+ ### `augy bundle`
157
+ Generate an `augy.json` manifest from your currently installed skills. Commit this file so teammates can reproduce your exact skill set with `augy sync`.
158
+
159
+ ```bash
160
+ augy bundle # writes ./augy.json
161
+ augy bundle --output ~/augy.json # custom path
162
+ augy bundle --include-untracked # include skills with no known source
163
+ ```
164
+
165
+ `augy.json` format:
166
+ ```json
167
+ {
168
+ "version": 1,
169
+ "skills": {
170
+ "tdd": "mattpocock/skills/skills/engineering/tdd",
171
+ "commit": "anomalyco/agent-skills/skills/commit"
172
+ }
173
+ }
174
+ ```
175
+
176
+ ### `augy sync [path]`
177
+ Install and update skills from an `augy.json` manifest. Skills in your registry but not in the manifest are left alone — nothing is auto-removed.
178
+
179
+ ```bash
180
+ augy sync # reads ./augy.json
181
+ augy sync ~/team/augy.json # explicit path
182
+ augy sync --dry-run # preview changes without applying
183
+ augy sync --agent opencode claude # target specific agents
184
+ ```
185
+
117
186
  ### `augy set-source <skill> <url>`
118
- Attach a GitHub source to a skill imported without one (e.g. via `augy scan`). Enables updates and diffs. Accepts tree and blob URLs.
187
+ Attach a GitHub source to a skill imported without one. Accepts tree and blob URLs. Enables updates, diffs, and rollbacks.
119
188
 
120
189
  ```bash
121
190
  augy set-source commit https://github.com/owner/repo/tree/main/skills/commit
@@ -129,7 +198,7 @@ augy uninstall tdd
129
198
  ```
130
199
 
131
200
  ### `augy pin|unpin <skill>`
132
- Pin a skill to skip it during `augy update`.
201
+ Pin a skill to freeze it at the current version, skipping it during `augy update`.
133
202
 
134
203
  ```bash
135
204
  augy pin tdd
@@ -140,22 +209,25 @@ augy unpin tdd
140
209
 
141
210
  ## Taps
142
211
 
143
- Taps are trusted GitHub repos containing skills. Add one and install by name without knowing the full URL:
212
+ Taps are GitHub repos you trust as skill sources. The tap system is how augy stays decentralized there is no central index, just repos you choose to register.
144
213
 
145
214
  ```bash
146
- augy tap add mattpocock/skills
147
- augy install tdd # resolves to mattpocock/skills automatically
215
+ augy tap add mattpocock/skills # public repo
216
+ augy tap add your-org/private-skills # private repo works the same way
217
+ augy install tdd # resolves via taps automatically
148
218
  ```
149
219
 
220
+ Any GitHub repo containing a `SKILL.md` file (or subdirectories that do) is a valid tap. The source of truth is GitHub. augy just tracks what you've installed and from where.
221
+
150
222
  ---
151
223
 
152
224
  ## Version storage
153
225
 
154
- Every upgrade archives the current skill to `~/.augy/versions/<skill>/<sha>/` before overwriting it giving you a full local snapshot history to diff or rollback to at any time.
226
+ Every upgrade archives the current skill files to `~/.augy/versions/<skill>/<sha>/` before overwriting. No external service required snapshots live on your machine.
155
227
 
156
228
  ```
157
229
  ~/.augy/
158
- registry.json ← lockfile (human-readable JSON)
230
+ registry.json ← lockfile — plain JSON, human-readable
159
231
  versions/
160
232
  tdd/
161
233
  7afa86d.../ ← snapshot before last upgrade
@@ -170,13 +242,13 @@ Every upgrade archives the current skill to `~/.augy/versions/<skill>/<sha>/` be
170
242
  |---|---|---|
171
243
  | `AUGY_HOME` | `~/.augy` | Override augy's home directory |
172
244
  | `CODEX_HOME` | `~/.codex` | Override Codex agent path |
173
- | `GITHUB_TOKEN` | — | Raise GitHub API rate limit from 60 to 5000 req/hr |
245
+ | `GITHUB_TOKEN` | — | Raise GitHub API rate limit from 60 5,000 req/hr |
174
246
 
175
247
  ---
176
248
 
177
249
  ## Docs
178
250
 
179
- Full documentation at **[augy.dev](https://augy.dev)** *(coming soon — see `docs/` for the source)*.
251
+ Full documentation at **[callmeradical.github.io/augy](https://callmeradical.github.io/augy)**
180
252
 
181
253
  ---
182
254
 
@@ -0,0 +1,49 @@
1
+ import {
2
+ listSkills,
3
+ readRegistry
4
+ } from "./chunk-ZW6ZKHTF.js";
5
+
6
+ // src/commands/bundle.ts
7
+ import { outro, spinner } from "@clack/prompts";
8
+ import chalk from "chalk";
9
+ import { writeFile } from "fs/promises";
10
+ import { join } from "path";
11
+ import { cwd } from "process";
12
+ var DEFAULT_FILENAME = "augy.json";
13
+ async function bundleCommand(opts) {
14
+ const registry = await readRegistry();
15
+ const skills = listSkills(registry);
16
+ if (!skills.length) {
17
+ console.log(chalk.dim("No skills installed. Nothing to bundle."));
18
+ return;
19
+ }
20
+ const tracked = skills.filter((s2) => s2.source);
21
+ const untracked = skills.filter((s2) => !s2.source);
22
+ const bundle = {
23
+ version: 1,
24
+ skills: {}
25
+ };
26
+ for (const skill of tracked) {
27
+ bundle.skills[skill.name] = skill.source;
28
+ }
29
+ if (opts.includeUntracked) {
30
+ for (const skill of untracked) {
31
+ bundle.skills[skill.name] = "";
32
+ }
33
+ }
34
+ const outputPath = opts.output ?? join(cwd(), DEFAULT_FILENAME);
35
+ const s = spinner();
36
+ s.start(`Writing ${chalk.cyan(outputPath)}\u2026`);
37
+ await writeFile(outputPath, JSON.stringify(bundle, null, 2) + "\n", "utf8");
38
+ s.stop(`${chalk.green("\u2713")} Bundle written ${chalk.dim(outputPath)}`);
39
+ const skillCount = Object.keys(bundle.skills).length;
40
+ const skippedNote = !opts.includeUntracked && untracked.length > 0 ? chalk.dim(`
41
+ ${untracked.length} untracked skill(s) omitted \u2014 use --include-untracked to add them`) : "";
42
+ outro(
43
+ `${chalk.bold(String(skillCount))} skill(s) bundled${skippedNote}
44
+ ` + chalk.dim(" Commit augy.json to share this skill set with your team.\n") + chalk.dim(" Teammates run `augy sync` to install from it.")
45
+ );
46
+ }
47
+ export {
48
+ bundleCommand
49
+ };
@@ -0,0 +1,65 @@
1
+ // src/agents.ts
2
+ import { homedir } from "os";
3
+ import { join } from "path";
4
+ import { existsSync } from "fs";
5
+ function h(p) {
6
+ return p.startsWith("~") ? join(homedir(), p.slice(1)) : p;
7
+ }
8
+ function codexSkillsPath() {
9
+ const codexHome = process.env["CODEX_HOME"];
10
+ return codexHome ? join(codexHome, "skills") : h("~/.codex/skills");
11
+ }
12
+ var AGENTS = [
13
+ // ── Tier 1: most widely used ────────────────────────────────────
14
+ { id: "opencode", name: "OpenCode", skillsPath: h("~/.opencode/skills"), skillFile: "SKILL.md" },
15
+ { id: "claude", name: "Claude", skillsPath: h("~/.claude/skills"), skillFile: "SKILL.md" },
16
+ { id: "codex", name: "Codex", skillsPath: codexSkillsPath(), skillFile: "SKILL.md" },
17
+ { id: "cursor", name: "Cursor", skillsPath: h("~/.cursor/skills"), skillFile: "SKILL.md" },
18
+ { id: "windsurf", name: "Windsurf", skillsPath: h("~/.codeium/windsurf/skills"), skillFile: "SKILL.md" },
19
+ { id: "copilot", name: "GitHub Copilot", skillsPath: h("~/.github/skills"), skillFile: "SKILL.md" },
20
+ { id: "gemini", name: "Gemini CLI", skillsPath: h("~/.gemini/skills"), skillFile: "SKILL.md" },
21
+ { id: "goose", name: "Goose", skillsPath: h("~/.goose/skills"), skillFile: "SKILL.md" },
22
+ { id: "amp", name: "Amp", skillsPath: h("~/.agents/skills"), skillFile: "SKILL.md" },
23
+ { id: "roo", name: "Roo Code", skillsPath: h("~/.roo/skills"), skillFile: "SKILL.md" },
24
+ { id: "cline", name: "Cline", skillsPath: h("~/.cline/skills"), skillFile: "SKILL.md" },
25
+ // ── Tier 2: growing ─────────────────────────────────────────────
26
+ { id: "kiro", name: "Kiro", skillsPath: h("~/.kiro/skills"), skillFile: "SKILL.md" },
27
+ { id: "kimi", name: "Kimi CLI", skillsPath: h("~/.kimi/skills"), skillFile: "SKILL.md" },
28
+ { id: "kilocode", name: "Kilo Code", skillsPath: h("~/.kilocode/skills"), skillFile: "SKILL.md" },
29
+ { id: "trae", name: "Trae", skillsPath: h("~/.trae/skills"), skillFile: "SKILL.md" },
30
+ { id: "trae-cn", name: "Trae CN", skillsPath: h("~/.trae-cn/skills"), skillFile: "SKILL.md" },
31
+ { id: "augment", name: "Augment", skillsPath: h("~/.augment/rules"), skillFile: "SKILL.md" },
32
+ { id: "openhands", name: "OpenHands", skillsPath: h("~/.openhands/skills"), skillFile: "SKILL.md" },
33
+ { id: "replit", name: "Replit", skillsPath: h("~/.replit/skills"), skillFile: "SKILL.md" },
34
+ { id: "crush", name: "Crush", skillsPath: h("~/.config/crush/skills"), skillFile: "SKILL.md" },
35
+ // ── Tier 3: long tail ───────────────────────────────────────────
36
+ { id: "antigravity", name: "Antigravity", skillsPath: h("~/.gemini/antigravity/skills"), skillFile: "SKILL.md" },
37
+ { id: "droid", name: "Droid", skillsPath: h("~/.factory/skills"), skillFile: "SKILL.md" },
38
+ { id: "openclaw", name: "OpenClaw", skillsPath: h("~/.openclaw/skills"), skillFile: "SKILL.md" },
39
+ { id: "codebuddy", name: "CodeBuddy", skillsPath: h("~/.codebuddy/skills"), skillFile: "SKILL.md" },
40
+ { id: "commandcode", name: "Command Code", skillsPath: h("~/.commandcode/skills"), skillFile: "SKILL.md" },
41
+ { id: "kode", name: "Kode", skillsPath: h("~/.kode/skills"), skillFile: "SKILL.md" },
42
+ { id: "mistralvibe", name: "Mistral Vibe", skillsPath: h("~/.vibe/skills"), skillFile: "SKILL.md" },
43
+ { id: "mux", name: "Mux", skillsPath: h("~/.mux/skills"), skillFile: "SKILL.md" },
44
+ { id: "openclaude", name: "OpenClaude IDE", skillsPath: h("~/.openclaude/skills"), skillFile: "SKILL.md" },
45
+ { id: "qoder", name: "Qoder", skillsPath: h("~/.qoder/skills"), skillFile: "SKILL.md" },
46
+ { id: "qwen", name: "Qwen Code", skillsPath: h("~/.qwen/skills"), skillFile: "SKILL.md" },
47
+ { id: "neovate", name: "Neovate", skillsPath: h("~/.neovate/skills"), skillFile: "SKILL.md" },
48
+ { id: "adal", name: "AdaL", skillsPath: h("~/.adal/skills"), skillFile: "SKILL.md" }
49
+ ];
50
+ function agentById(id) {
51
+ return AGENTS.find((a) => a.id === id);
52
+ }
53
+ function agentSkillPath(agent, skillName) {
54
+ return join(agent.skillsPath, skillName);
55
+ }
56
+ function detectInstalledAgents() {
57
+ return AGENTS.filter((a) => existsSync(a.skillsPath));
58
+ }
59
+
60
+ export {
61
+ AGENTS,
62
+ agentById,
63
+ agentSkillPath,
64
+ detectInstalledAgents
65
+ };
package/dist/index.js CHANGED
@@ -10,29 +10,37 @@ 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-FPAGHWX2.js");
13
+ const { installCommand } = await import("./install-WXJUM6CM.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) => {
17
- const { updateCommand } = await import("./update-A3PSROUK.js");
17
+ const { updateCommand } = await import("./update-NIBPLLHU.js");
18
18
  await updateCommand(skill);
19
19
  });
20
20
  program.command("list").description("Show all installed skills with version + agent info").option("--json", "Output raw JSON registry").action(async (opts) => {
21
- const { listCommand } = await import("./list-UJX2MYXK.js");
21
+ const { listCommand } = await import("./list-ZTG6RVGD.js");
22
22
  await listCommand(opts ?? {});
23
23
  });
24
24
  program.command("diff <skill> [sha1] [sha2]").description(
25
25
  "Browse file-level diffs for a skill\n augy diff <skill> installed \u2194 upstream HEAD\n augy diff <skill> <sha> installed \u2194 specific SHA (archive or GitHub)\n augy diff <skill> <sha1> <sha2> two local archives side-by-side"
26
26
  ).action(async (skill, sha1, sha2) => {
27
- const { diffCommand } = await import("./diff-KYSWUXMH.js");
27
+ const { diffCommand } = await import("./diff-VQU63ORX.js");
28
28
  await diffCommand(skill, sha1, sha2);
29
29
  });
30
+ program.command("bundle").description("Write an augy.json manifest from installed skills for team sharing").option("-o, --output <path>", "Output path (default: ./augy.json)").option("--include-untracked", "Include skills without a known source").action(async (opts) => {
31
+ const { bundleCommand } = await import("./bundle-76RCX45C.js");
32
+ await bundleCommand(opts);
33
+ });
34
+ program.command("sync [path]").description("Install/update skills from an augy.json manifest (default: ./augy.json)").option("--dry-run", "Preview changes without applying them").option("-a, --agent <agents...>", "Target agent(s) (default: all detected)").action(async (path, opts) => {
35
+ const { syncCommand } = await import("./sync-VPBC2PBH.js");
36
+ await syncCommand(path, opts ?? {});
37
+ });
30
38
  program.command("scan").description("Find skills installed outside augy and optionally import them into the registry").action(async () => {
31
- const { scanCommand } = await import("./scan-DKN7YBT5.js");
39
+ const { scanCommand } = await import("./scan-MWUT7G4V.js");
32
40
  await scanCommand();
33
41
  });
34
42
  program.command("info <skill>").description("Show full metadata, version history, and description for an installed skill").action(async (skill) => {
35
- const { infoCommand } = await import("./info-7BO4LXXS.js");
43
+ const { infoCommand } = await import("./info-JKGYNOPV.js");
36
44
  await infoCommand(skill);
37
45
  });
38
46
  program.command("search [query]").description("Search all taps for available skills (optionally filter by name)").action(async (query) => {
@@ -61,7 +69,7 @@ program.command("uninstall <skill>").description("Remove a skill from all agent
61
69
  await uninstallCommand(skill);
62
70
  });
63
71
  program.command("rollback <skill> [sha]").description("Restore a skill to a previous archived version").action(async (skill, sha) => {
64
- const { rollbackCommand } = await import("./rollback-WAZV5HNG.js");
72
+ const { rollbackCommand } = await import("./rollback-QXPWOF3Q.js");
65
73
  await rollbackCommand(skill, sha);
66
74
  });
67
75
  program.command("pin <skill>").description("Pin a skill so it is skipped during `augy update`").action(async (skill) => {
@@ -71,7 +79,7 @@ program.command("unpin <skill>").description("Allow a pinned skill to receive up
71
79
  await setPinned(skill, false);
72
80
  });
73
81
  program.action(async () => {
74
- const { installCommand } = await import("./install-FPAGHWX2.js");
82
+ const { installCommand } = await import("./install-WXJUM6CM.js");
75
83
  await installCommand();
76
84
  });
77
85
  program.parse();
@@ -1,9 +1,9 @@
1
+ import {
2
+ AGENTS
3
+ } from "./chunk-V5GA2ZHU.js";
1
4
  import {
2
5
  archiveExists
3
6
  } from "./chunk-PX2LVUHV.js";
4
- import {
5
- AGENTS
6
- } from "./chunk-R2TJ3UDO.js";
7
7
  import {
8
8
  getSkill,
9
9
  readRegistry,
@@ -6,8 +6,9 @@ import {
6
6
  } from "./chunk-YXCEZ3EY.js";
7
7
  import {
8
8
  AGENTS,
9
- agentSkillPath
10
- } from "./chunk-R2TJ3UDO.js";
9
+ agentSkillPath,
10
+ detectInstalledAgents
11
+ } from "./chunk-V5GA2ZHU.js";
11
12
  import {
12
13
  discoverSkills,
13
14
  downloadSkill,
@@ -27,7 +28,6 @@ import {
27
28
  confirm,
28
29
  intro,
29
30
  isCancel,
30
- multiselect,
31
31
  outro,
32
32
  select,
33
33
  spinner,
@@ -173,14 +173,15 @@ async function promptSkillSelection(skills) {
173
173
  });
174
174
  }
175
175
  async function promptAgentSelection() {
176
- return multiselect({
176
+ const detected = new Set(detectInstalledAgents().map((a) => a.id));
177
+ return filterableMultiselect({
177
178
  message: "Install for which agents?",
178
179
  options: AGENTS.map((a) => ({
179
180
  value: a,
180
181
  label: a.name,
181
- hint: chalk.dim(a.skillsPath)
182
- })),
183
- required: true
182
+ hint: detected.has(a.id) ? chalk.dim(a.skillsPath) : chalk.dim("not detected"),
183
+ selected: detected.has(a.id)
184
+ }))
184
185
  });
185
186
  }
186
187
  function bail() {
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  AGENTS
3
- } from "./chunk-R2TJ3UDO.js";
3
+ } from "./chunk-V5GA2ZHU.js";
4
4
  import {
5
5
  listSkills,
6
6
  readRegistry
@@ -1,11 +1,11 @@
1
+ import {
2
+ agentById,
3
+ agentSkillPath
4
+ } from "./chunk-V5GA2ZHU.js";
1
5
  import {
2
6
  archiveExists,
3
7
  restoreVersion
4
8
  } from "./chunk-PX2LVUHV.js";
5
- import {
6
- agentById,
7
- agentSkillPath
8
- } from "./chunk-R2TJ3UDO.js";
9
9
  import {
10
10
  getSkill,
11
11
  readRegistry,
@@ -10,7 +10,7 @@ import {
10
10
  } from "./chunk-YXCEZ3EY.js";
11
11
  import {
12
12
  AGENTS
13
- } from "./chunk-R2TJ3UDO.js";
13
+ } from "./chunk-V5GA2ZHU.js";
14
14
  import {
15
15
  discoverSkills,
16
16
  parseGitHubUrl
@@ -0,0 +1,189 @@
1
+ import {
2
+ AGENTS,
3
+ agentSkillPath,
4
+ detectInstalledAgents
5
+ } from "./chunk-V5GA2ZHU.js";
6
+ import {
7
+ discoverSkills,
8
+ downloadSkill,
9
+ latestShaForPath,
10
+ parseGitHubUrl
11
+ } from "./chunk-EU54UQ4C.js";
12
+ import {
13
+ createSkillRecord,
14
+ getSkill,
15
+ readRegistry,
16
+ shortSha,
17
+ writeRegistry
18
+ } from "./chunk-ZW6ZKHTF.js";
19
+
20
+ // src/commands/sync.ts
21
+ import {
22
+ confirm,
23
+ intro,
24
+ isCancel,
25
+ outro,
26
+ spinner
27
+ } from "@clack/prompts";
28
+ import chalk from "chalk";
29
+ import { existsSync } from "fs";
30
+ import { readFile } from "fs/promises";
31
+ import { join } from "path";
32
+ import { cwd } from "process";
33
+ import { mkdir, rm } from "fs/promises";
34
+ var DEFAULT_FILENAME = "augy.json";
35
+ async function syncCommand(pathArg, opts = {}) {
36
+ intro(chalk.bold("augy") + chalk.dim(" \u2014 sync"));
37
+ const manifestPath = pathArg ?? join(cwd(), DEFAULT_FILENAME);
38
+ if (!existsSync(manifestPath)) {
39
+ console.error(
40
+ chalk.red(`No manifest found at ${manifestPath}
41
+ `) + chalk.dim("Run `augy bundle` to create one.")
42
+ );
43
+ process.exit(1);
44
+ }
45
+ let bundle;
46
+ try {
47
+ bundle = JSON.parse(await readFile(manifestPath, "utf8"));
48
+ } catch {
49
+ console.error(chalk.red(`Could not parse ${manifestPath}`));
50
+ process.exit(1);
51
+ }
52
+ const entries = Object.entries(bundle.skills);
53
+ if (!entries.length) {
54
+ outro(chalk.dim("Manifest is empty \u2014 nothing to sync."));
55
+ return;
56
+ }
57
+ console.log(
58
+ chalk.dim(`
59
+ Manifest: ${manifestPath}`) + chalk.dim(` (${entries.length} skill(s))
60
+ `)
61
+ );
62
+ const targetAgents = opts.agent?.length ? AGENTS.filter((a) => opts.agent.includes(a.id)) : detectInstalledAgents();
63
+ if (!targetAgents.length) {
64
+ console.error(chalk.red("No agents detected. Install an agent or use --agent to specify one."));
65
+ process.exit(1);
66
+ }
67
+ const registry = await readRegistry();
68
+ const s = spinner();
69
+ s.start("Resolving skills\u2026");
70
+ const plan = [];
71
+ await Promise.allSettled(
72
+ entries.map(async ([name, source]) => {
73
+ if (!source) {
74
+ plan.push({ name, source: "", action: "no-source" });
75
+ return;
76
+ }
77
+ const installed = getSkill(registry, name);
78
+ try {
79
+ const coords = parseGitHubUrl(source);
80
+ const remoteSha = await latestShaForPath(
81
+ coords.owner,
82
+ coords.repo,
83
+ coords.path || ".",
84
+ coords.ref
85
+ );
86
+ if (!installed) {
87
+ plan.push({ name, source, action: "install", remoteSha });
88
+ } else if (installed.sha !== remoteSha) {
89
+ plan.push({ name, source, action: "upgrade", remoteSha });
90
+ } else {
91
+ plan.push({ name, source, action: "up-to-date", remoteSha });
92
+ }
93
+ } catch {
94
+ plan.push({ name, source, action: "no-source" });
95
+ }
96
+ })
97
+ );
98
+ const order = { install: 0, upgrade: 1, "up-to-date": 2, "no-source": 3 };
99
+ plan.sort((a, b) => order[a.action] - order[b.action]);
100
+ s.stop("Resolved");
101
+ const toInstall = plan.filter((p) => p.action === "install");
102
+ const toUpgrade = plan.filter((p) => p.action === "upgrade");
103
+ const upToDate = plan.filter((p) => p.action === "up-to-date");
104
+ const noSource = plan.filter((p) => p.action === "no-source");
105
+ if (toInstall.length) {
106
+ console.log(chalk.bold(` Install (${toInstall.length})`));
107
+ for (const p of toInstall) {
108
+ console.log(` ${chalk.cyan(p.name)} ${chalk.dim(p.remoteSha ? "@" + shortSha(p.remoteSha) : "")}`);
109
+ }
110
+ console.log();
111
+ }
112
+ if (toUpgrade.length) {
113
+ console.log(chalk.bold(` Upgrade (${toUpgrade.length})`));
114
+ for (const p of toUpgrade) {
115
+ const installed = getSkill(registry, p.name);
116
+ console.log(
117
+ ` ${chalk.cyan(p.name)} ${chalk.dim(installed.shortSha)} \u2192 ${chalk.green(shortSha(p.remoteSha))}`
118
+ );
119
+ }
120
+ console.log();
121
+ }
122
+ if (upToDate.length) {
123
+ console.log(chalk.dim(` Up to date (${upToDate.length}): `) + chalk.dim(upToDate.map((p) => p.name).join(", ")));
124
+ console.log();
125
+ }
126
+ if (noSource.length) {
127
+ console.log(chalk.yellow(` Skipped \u2014 no source (${noSource.length}): `) + chalk.dim(noSource.map((p) => p.name).join(", ")));
128
+ console.log();
129
+ }
130
+ if (!toInstall.length && !toUpgrade.length) {
131
+ outro(chalk.green("Everything is up to date."));
132
+ return;
133
+ }
134
+ if (opts.dryRun) {
135
+ outro(chalk.dim("Dry run \u2014 no changes made."));
136
+ return;
137
+ }
138
+ const actionCount = toInstall.length + toUpgrade.length;
139
+ const ok = await confirm({
140
+ message: `Apply ${actionCount} change(s) to ${targetAgents.map((a) => a.name).join(", ")}?`
141
+ });
142
+ if (isCancel(ok) || !ok) {
143
+ console.log(chalk.dim("Cancelled."));
144
+ process.exit(0);
145
+ }
146
+ for (const entry of [...toInstall, ...toUpgrade]) {
147
+ const s2 = spinner();
148
+ s2.start(`${entry.action === "install" ? "Installing" : "Upgrading"} ${chalk.cyan(entry.name)}\u2026`);
149
+ try {
150
+ const coords = parseGitHubUrl(entry.source);
151
+ const remote = await discoverSkills(coords);
152
+ const match = remote.find((r) => r.name === entry.name) ?? remote[0];
153
+ if (!match) throw new Error("Skill not found at source");
154
+ const agentPaths = {};
155
+ for (const agent of targetAgents) {
156
+ const dest = agentSkillPath(agent, entry.name);
157
+ agentPaths[agent.id] = dest;
158
+ await rm(dest, { recursive: true, force: true });
159
+ await mkdir(dest, { recursive: true });
160
+ await downloadSkill(match.gigetSource, dest);
161
+ }
162
+ const existing = getSkill(registry, entry.name);
163
+ const record = createSkillRecord({
164
+ name: entry.name,
165
+ source: entry.source,
166
+ gigetSource: match.gigetSource,
167
+ sha: match.sha,
168
+ agentIds: targetAgents.map((a) => a.id),
169
+ agentPaths
170
+ });
171
+ if (existing) {
172
+ record.installedAt = existing.installedAt;
173
+ record.pinned = existing.pinned;
174
+ record.history = existing.history;
175
+ }
176
+ registry.skills[entry.name] = record;
177
+ await writeRegistry(registry);
178
+ s2.stop(
179
+ `${chalk.green("\u2713")} ${chalk.cyan(entry.name)} ${chalk.dim("@" + shortSha(match.sha))}`
180
+ );
181
+ } catch (err) {
182
+ s2.stop(`${chalk.red("\u2717")} ${chalk.cyan(entry.name)} \u2014 ${String(err)}`);
183
+ }
184
+ }
185
+ outro(chalk.green(`Sync complete ${chalk.dim(`(${actionCount} change(s) applied)`)}`));
186
+ }
187
+ export {
188
+ syncCommand
189
+ };
@@ -1,15 +1,15 @@
1
- import {
2
- archiveVersion
3
- } from "./chunk-PX2LVUHV.js";
4
1
  import {
5
2
  agentById,
6
3
  agentSkillPath
7
- } from "./chunk-R2TJ3UDO.js";
4
+ } from "./chunk-V5GA2ZHU.js";
8
5
  import {
9
6
  discoverSkills,
10
7
  downloadSkill,
11
8
  parseGitHubUrl
12
9
  } from "./chunk-EU54UQ4C.js";
10
+ import {
11
+ archiveVersion
12
+ } from "./chunk-PX2LVUHV.js";
13
13
  import {
14
14
  listSkills,
15
15
  readRegistry,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@callmeradical/augy",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Homebrew for AI agent skills — install, version, update, rollback",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -35,7 +35,7 @@
35
35
  "build": "tsup src/index.ts --format esm --outDir dist --clean",
36
36
  "dev": "tsx src/index.ts",
37
37
  "typecheck": "tsc --noEmit",
38
- "prepublishOnly": "npm run typecheck && npm run build"
38
+ "prepublishOnly": "npm run build"
39
39
  },
40
40
  "dependencies": {
41
41
  "@clack/prompts": "^0.9.0",
@@ -1,43 +0,0 @@
1
- // src/agents.ts
2
- import { homedir } from "os";
3
- import { join } from "path";
4
- function resolveHome(p) {
5
- return p.startsWith("~") ? join(homedir(), p.slice(1)) : p;
6
- }
7
- function codexSkillsPath() {
8
- const codexHome = process.env["CODEX_HOME"];
9
- if (codexHome) return join(codexHome, "skills");
10
- return resolveHome("~/.codex/skills");
11
- }
12
- var AGENTS = [
13
- {
14
- id: "opencode",
15
- name: "OpenCode",
16
- skillsPath: resolveHome("~/.opencode/skills"),
17
- skillFile: "SKILL.md"
18
- },
19
- {
20
- id: "claude",
21
- name: "Claude",
22
- skillsPath: resolveHome("~/.claude/skills"),
23
- skillFile: "SKILL.md"
24
- },
25
- {
26
- id: "codex",
27
- name: "Codex",
28
- skillsPath: codexSkillsPath(),
29
- skillFile: "SKILL.md"
30
- }
31
- ];
32
- function agentById(id) {
33
- return AGENTS.find((a) => a.id === id);
34
- }
35
- function agentSkillPath(agent, skillName) {
36
- return join(agent.skillsPath, skillName);
37
- }
38
-
39
- export {
40
- AGENTS,
41
- agentById,
42
- agentSkillPath
43
- };
@@ -1,10 +1,10 @@
1
- import {
2
- archiveExists
3
- } from "./chunk-PX2LVUHV.js";
4
1
  import {
5
2
  discoverSkills,
6
3
  parseGitHubUrl
7
4
  } from "./chunk-EU54UQ4C.js";
5
+ import {
6
+ archiveExists
7
+ } from "./chunk-PX2LVUHV.js";
8
8
  import {
9
9
  getSkill,
10
10
  readRegistry,