@callmeradical/augy 0.1.0 → 0.3.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-JM4VVAHN.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
+ };
@@ -1,11 +1,11 @@
1
1
  import {
2
2
  discoverSkills,
3
3
  parseGitHubUrl
4
- } from "./chunk-EU54UQ4C.js";
4
+ } from "./chunk-2E5SVRSR.js";
5
5
  import {
6
6
  listTaps,
7
7
  tapKey
8
- } from "./chunk-ZW6ZKHTF.js";
8
+ } from "./chunk-JM4VVAHN.js";
9
9
 
10
10
  // src/taps.ts
11
11
  async function searchTaps(registry, query) {
@@ -112,6 +112,12 @@ function addTap(registry, tap) {
112
112
  function removeTap(registry, key) {
113
113
  delete registry.taps[key];
114
114
  }
115
+ function getHomeConfig(registry) {
116
+ return registry.home;
117
+ }
118
+ function setHomeConfig(registry, config) {
119
+ registry.home = config;
120
+ }
115
121
 
116
122
  export {
117
123
  __commonJS,
@@ -132,5 +138,7 @@ export {
132
138
  getTap,
133
139
  listTaps,
134
140
  addTap,
135
- removeTap
141
+ removeTap,
142
+ getHomeConfig,
143
+ setHomeConfig
136
144
  };
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  __commonJS,
3
3
  __toESM
4
- } from "./chunk-ZW6ZKHTF.js";
4
+ } from "./chunk-JM4VVAHN.js";
5
5
 
6
6
  // node_modules/sisteransi/src/index.js
7
7
  var require_src = __commonJS({
@@ -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
+ };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  versionArchivePath
3
- } from "./chunk-ZW6ZKHTF.js";
3
+ } from "./chunk-JM4VVAHN.js";
4
4
 
5
5
  // src/versions.ts
6
6
  import { cp, mkdir, rm } from "fs/promises";
@@ -32,7 +32,7 @@ Archive path: ${src}`
32
32
  }
33
33
  async function pruneVersions(skillName, keepShas = []) {
34
34
  const { join } = await import("path");
35
- const { versionArchivePath: archivePath, versionsDir } = await import("./registry-QVCNZXBZ.js");
35
+ const { versionArchivePath: archivePath, versionsDir } = await import("./registry-2B52E3ME.js");
36
36
  const skillVersionsDir = join(versionsDir(), skillName);
37
37
  if (!existsSync(skillVersionsDir)) return;
38
38
  const { readdir } = await import("fs/promises");
@@ -1,16 +1,16 @@
1
- import {
2
- archiveExists
3
- } from "./chunk-PX2LVUHV.js";
4
1
  import {
5
2
  discoverSkills,
6
3
  parseGitHubUrl
7
- } from "./chunk-EU54UQ4C.js";
4
+ } from "./chunk-2E5SVRSR.js";
5
+ import {
6
+ archiveExists
7
+ } from "./chunk-Z4R7NYGP.js";
8
8
  import {
9
9
  getSkill,
10
10
  readRegistry,
11
11
  shortSha,
12
12
  versionArchivePath
13
- } from "./chunk-ZW6ZKHTF.js";
13
+ } from "./chunk-JM4VVAHN.js";
14
14
 
15
15
  // src/commands/diff.ts
16
16
  import { cancel, intro, isCancel, outro, select, spinner } from "@clack/prompts";
@@ -0,0 +1,205 @@
1
+ import {
2
+ AGENTS,
3
+ agentSkillPath,
4
+ detectInstalledAgents
5
+ } from "./chunk-V5GA2ZHU.js";
6
+ import {
7
+ createSkillRecord,
8
+ getHomeConfig,
9
+ listSkills,
10
+ readRegistry,
11
+ setHomeConfig,
12
+ upsertSkill,
13
+ writeRegistry
14
+ } from "./chunk-JM4VVAHN.js";
15
+
16
+ // src/commands/home.ts
17
+ import { intro, outro, spinner } from "@clack/prompts";
18
+ import chalk from "chalk";
19
+ import { cp, mkdir, readdir, writeFile } from "fs/promises";
20
+ import { existsSync } from "fs";
21
+ import { join } from "path";
22
+ import { tmpdir } from "os";
23
+
24
+ // src/git.ts
25
+ import { execFile } from "child_process";
26
+ function run(cmd, args) {
27
+ return new Promise((resolve, reject) => {
28
+ execFile(cmd, args, (err) => {
29
+ if (err) reject(err);
30
+ else resolve();
31
+ });
32
+ });
33
+ }
34
+ function repoToUrl(ownerRepoOrUrl) {
35
+ if (ownerRepoOrUrl.startsWith("https://") || ownerRepoOrUrl.startsWith("git@")) {
36
+ return ownerRepoOrUrl;
37
+ }
38
+ return `https://github.com/${ownerRepoOrUrl}.git`;
39
+ }
40
+ async function cloneRepo(url, destDir) {
41
+ await run("git", ["clone", "--depth=1", url, destDir]);
42
+ }
43
+ async function gitAddAll(dir) {
44
+ await run("git", ["-C", dir, "add", "-A"]);
45
+ }
46
+ async function gitCommit(dir, message) {
47
+ await run("git", ["-C", dir, "commit", "-m", message]);
48
+ }
49
+ async function gitPush(dir) {
50
+ await run("git", ["-C", dir, "push"]);
51
+ }
52
+
53
+ // src/commands/home.ts
54
+ var DEFAULT_PATH = "augy.json";
55
+ var DEFAULT_SKILLS_DIR = "skills";
56
+ async function homeSetCommand(repo, opts = {}) {
57
+ const parts = repo.split("/");
58
+ if (parts.length !== 2 || !parts[0] || !parts[1]) {
59
+ throw new Error(`Invalid repo "${repo}" \u2014 expected owner/repo format (e.g. alice/my-skills)`);
60
+ }
61
+ const path = opts.path ?? DEFAULT_PATH;
62
+ const skillsPath = opts.skillsPath ?? DEFAULT_SKILLS_DIR;
63
+ const registry = await readRegistry();
64
+ setHomeConfig(registry, { repo, path, skillsPath });
65
+ await writeRegistry(registry);
66
+ outro(
67
+ `${chalk.green("\u2713")} Home repo set to ${chalk.cyan(repo)}
68
+ ` + chalk.dim(` manifest : ${path}
69
+ `) + chalk.dim(` skills : ${skillsPath}/`)
70
+ );
71
+ }
72
+ async function homeShowCommand() {
73
+ const registry = await readRegistry();
74
+ const home = getHomeConfig(registry);
75
+ if (!home) {
76
+ console.log(chalk.dim("No home repo configured. Run `augy home set <owner/repo>` to set one."));
77
+ return;
78
+ }
79
+ console.log(`${chalk.bold("Home repo")} ${chalk.cyan(home.repo)}`);
80
+ console.log(`${chalk.dim("manifest ")} ${home.path}`);
81
+ console.log(`${chalk.dim("skills ")} ${home.skillsPath}/`);
82
+ }
83
+ async function homePushCommand() {
84
+ intro(chalk.bold("augy") + chalk.dim(" \u2014 home push"));
85
+ const registry = await readRegistry();
86
+ const home = getHomeConfig(registry);
87
+ if (!home) {
88
+ console.error(
89
+ chalk.red("No home repo configured.") + "\nRun `augy home set <owner/repo>` first."
90
+ );
91
+ process.exit(1);
92
+ }
93
+ const allSkills = listSkills(registry);
94
+ const authored = allSkills.filter((s3) => !s3.source);
95
+ const external = allSkills.filter((s3) => s3.source);
96
+ const cloneDir = join(tmpdir(), `augy-home-push-${Date.now()}`);
97
+ const s = spinner();
98
+ s.start(`Cloning ${chalk.cyan(home.repo)}\u2026`);
99
+ await cloneRepo(repoToUrl(home.repo), cloneDir);
100
+ s.stop(`${chalk.green("\u2713")} Cloned`);
101
+ const homeRepo = home.repo;
102
+ for (const skill of authored) {
103
+ const sourcePath = Object.values(skill.agents).find((a) => a.active && existsSync(a.path))?.path;
104
+ if (!sourcePath) continue;
105
+ const destPath = home.skillsPath ? join(cloneDir, home.skillsPath, skill.name) : join(cloneDir, skill.name);
106
+ await mkdir(destPath, { recursive: true });
107
+ 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
+ }
112
+ const bundle = { version: 1, skills: {} };
113
+ for (const skill of allSkills) {
114
+ bundle.skills[skill.name] = skill.source;
115
+ }
116
+ await writeFile(
117
+ join(cloneDir, home.path),
118
+ JSON.stringify(bundle, null, 2) + "\n",
119
+ "utf8"
120
+ );
121
+ const s2 = spinner();
122
+ s2.start("Committing and pushing\u2026");
123
+ await gitAddAll(cloneDir);
124
+ const skillCount = allSkills.length;
125
+ const authoredNote = authored.length ? ` (${authored.length} authored)` : "";
126
+ await gitCommit(cloneDir, `chore: update skills via augy \u2014 ${skillCount} skill(s)${authoredNote}`);
127
+ await gitPush(cloneDir);
128
+ s2.stop(`${chalk.green("\u2713")} Pushed`);
129
+ if (authored.length) await writeRegistry(registry);
130
+ outro(
131
+ `${chalk.bold(String(skillCount))} skill(s) saved to ${chalk.cyan(home.repo)}
132
+ ` + (authored.length ? chalk.dim(` ${authored.length} authored skill(s) committed + source registered
133
+ `) : "") + chalk.dim(` Run \`augy home pull\` on a new machine to restore.`)
134
+ );
135
+ }
136
+ async function homePullCommand(opts = {}) {
137
+ intro(chalk.bold("augy") + chalk.dim(" \u2014 home pull"));
138
+ const registry = await readRegistry();
139
+ const home = getHomeConfig(registry);
140
+ if (!home) {
141
+ console.error(
142
+ chalk.red("No home repo configured.") + "\nRun `augy home set <owner/repo>` first."
143
+ );
144
+ process.exit(1);
145
+ }
146
+ const cloneDir = join(tmpdir(), `augy-home-pull-${Date.now()}`);
147
+ const s = spinner();
148
+ s.start(`Cloning ${chalk.cyan(home.repo)}\u2026`);
149
+ await cloneRepo(repoToUrl(home.repo), cloneDir);
150
+ s.stop(`${chalk.green("\u2713")} Cloned`);
151
+ const targetAgents = opts.agent?.length ? AGENTS.filter((a) => opts.agent.includes(a.id)) : detectInstalledAgents();
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
+ }
156
+ const skillsDir = home.skillsPath ? join(cloneDir, home.skillsPath) : cloneDir;
157
+ const authoredInstalled = [];
158
+ if (existsSync(skillsDir)) {
159
+ const entries = await readdir(skillsDir, { withFileTypes: true });
160
+ const skillDirs = entries.filter((e) => e.isDirectory());
161
+ for (const dir of skillDirs) {
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);
186
+ }
187
+ }
188
+ const manifestPath = join(cloneDir, home.path);
189
+ if (existsSync(manifestPath)) {
190
+ const { syncCommand } = await import("./sync-QQFXUXCN.js");
191
+ await syncCommand(manifestPath, opts);
192
+ }
193
+ if (!opts.dryRun && authoredInstalled.length) {
194
+ await writeRegistry(registry);
195
+ }
196
+ if (opts.dryRun) {
197
+ outro(chalk.dim("Dry run \u2014 no changes made."));
198
+ }
199
+ }
200
+ export {
201
+ homePullCommand,
202
+ homePushCommand,
203
+ homeSetCommand,
204
+ homeShowCommand
205
+ };