@solaqua/skul 0.1.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/LICENSE +15 -0
- package/README.md +282 -0
- package/bin/skul.js +13 -0
- package/dist/bundle-discovery.d.ts +30 -0
- package/dist/bundle-discovery.js +284 -0
- package/dist/bundle-discovery.js.map +1 -0
- package/dist/bundle-fetch.d.ts +67 -0
- package/dist/bundle-fetch.js +378 -0
- package/dist/bundle-fetch.js.map +1 -0
- package/dist/bundle-items.d.ts +34 -0
- package/dist/bundle-items.js +149 -0
- package/dist/bundle-items.js.map +1 -0
- package/dist/bundle-manifest.d.ts +26 -0
- package/dist/bundle-manifest.js +196 -0
- package/dist/bundle-manifest.js.map +1 -0
- package/dist/bundle-materialization.d.ts +52 -0
- package/dist/bundle-materialization.js +587 -0
- package/dist/bundle-materialization.js.map +1 -0
- package/dist/bundle-translation.d.ts +38 -0
- package/dist/bundle-translation.js +502 -0
- package/dist/bundle-translation.js.map +1 -0
- package/dist/cli.d.ts +126 -0
- package/dist/cli.js +648 -0
- package/dist/cli.js.map +1 -0
- package/dist/fs-utils.d.ts +7 -0
- package/dist/fs-utils.js +27 -0
- package/dist/fs-utils.js.map +1 -0
- package/dist/git-context.d.ts +14 -0
- package/dist/git-context.js +53 -0
- package/dist/git-context.js.map +1 -0
- package/dist/git-exclude.d.ts +13 -0
- package/dist/git-exclude.js +76 -0
- package/dist/git-exclude.js.map +1 -0
- package/dist/git-index.d.ts +106 -0
- package/dist/git-index.js +224 -0
- package/dist/git-index.js.map +1 -0
- package/dist/index.d.ts +21 -0
- package/dist/index.js +4190 -0
- package/dist/index.js.map +1 -0
- package/dist/registry.d.ts +91 -0
- package/dist/registry.js +551 -0
- package/dist/registry.js.map +1 -0
- package/dist/root-instruction-content.d.ts +11 -0
- package/dist/root-instruction-content.js +61 -0
- package/dist/root-instruction-content.js.map +1 -0
- package/dist/root-instruction-render.d.ts +31 -0
- package/dist/root-instruction-render.js +121 -0
- package/dist/root-instruction-render.js.map +1 -0
- package/dist/root-instruction-state.d.ts +41 -0
- package/dist/root-instruction-state.js +273 -0
- package/dist/root-instruction-state.js.map +1 -0
- package/dist/state-layout.d.ts +11 -0
- package/dist/state-layout.js +26 -0
- package/dist/state-layout.js.map +1 -0
- package/dist/tool-mapping.d.ts +35 -0
- package/dist/tool-mapping.js +227 -0
- package/dist/tool-mapping.js.map +1 -0
- package/package.json +72 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
ISC License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026
|
|
4
|
+
|
|
5
|
+
Permission to use, copy, modify, and/or distribute this software for any
|
|
6
|
+
purpose with or without fee is hereby granted, provided that the above
|
|
7
|
+
copyright notice and this permission notice appear in all copies.
|
|
8
|
+
|
|
9
|
+
THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
|
10
|
+
REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
|
|
11
|
+
AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
|
12
|
+
INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
|
13
|
+
LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
|
14
|
+
OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
|
15
|
+
PERFORMANCE OF THIS SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
# Skul — AI Configuration Bundle Manager for AI Coding Tools
|
|
2
|
+
|
|
3
|
+
Apply reusable AI bundles — skills, slash commands, agents, and root instructions — into tool-native directories without committing them to Git. Skul fetches bundles from a GitHub repository, writes files where each tool expects them, and hides everything via `.git/info/exclude`.
|
|
4
|
+
|
|
5
|
+
Including `AGENTS.md` and `CLAUDE.md`: Skul can layer your personal instructions on top of a team-committed `AGENTS.md` (or `CLAUDE.md`) without dirtying the working tree, and can materialize the equivalent file for every supported tool from a single bundle source.
|
|
6
|
+
|
|
7
|
+
[](https://nodejs.org)
|
|
8
|
+
[](https://www.typescriptlang.org)
|
|
9
|
+
[](LICENSE)
|
|
10
|
+
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
## Quick Start
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Fetch from a GitHub registry and materialize a bundle (first use clones the repo)
|
|
17
|
+
skul add github.com/sjquant/ai-bundles react-expert
|
|
18
|
+
|
|
19
|
+
# GitHub is the default registry — owner/repo shorthand works too
|
|
20
|
+
skul add acme/shared-bundles core --agent codex
|
|
21
|
+
|
|
22
|
+
# Overlay your personal AGENTS.md / CLAUDE.md on top of the repo's tracked
|
|
23
|
+
# version — without showing up in git status
|
|
24
|
+
skul add acme/personal-instructions --agent codex
|
|
25
|
+
|
|
26
|
+
# Re-apply from cache, no network needed
|
|
27
|
+
skul add react-expert
|
|
28
|
+
|
|
29
|
+
# See what's cached
|
|
30
|
+
skul list
|
|
31
|
+
|
|
32
|
+
# Check materialization state
|
|
33
|
+
skul status
|
|
34
|
+
|
|
35
|
+
# Remove a bundle and its managed files
|
|
36
|
+
skul remove react-expert
|
|
37
|
+
|
|
38
|
+
# Re-materialize all desired bundles (e.g. after cloning a fresh worktree)
|
|
39
|
+
skul apply
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## Commands
|
|
45
|
+
|
|
46
|
+
| Command | Description |
|
|
47
|
+
|---|---|
|
|
48
|
+
| `skul add <source> [bundle]` | Fetch (when needed) and materialize a bundle from a source |
|
|
49
|
+
| `skul add <bundle>` | Materialize a uniquely named cached bundle |
|
|
50
|
+
| `skul list` | List cached bundles |
|
|
51
|
+
| `skul status` | Show desired state and materialization status |
|
|
52
|
+
| `skul remove <bundle>` | Remove a bundle and delete its managed files |
|
|
53
|
+
| `skul apply` | Re-materialize all desired bundles in the current worktree |
|
|
54
|
+
|
|
55
|
+
All mutating commands accept `--dry-run`. `skul list` and `skul status` accept `--json`. Bare `owner/repo` sources default to `github.com/owner/repo`. For scripting and agent use, set `SKUL_NO_TUI=1` to suppress all interactive prompts.
|
|
56
|
+
|
|
57
|
+
See [docs/advanced.md](docs/advanced.md) for maintenance, recovery, and cleanup commands (`check`, `update`, `sync`, `shadow`, `reset`).
|
|
58
|
+
|
|
59
|
+
---
|
|
60
|
+
|
|
61
|
+
## `skul add`
|
|
62
|
+
|
|
63
|
+
```text
|
|
64
|
+
skul add [options] [source] [bundle]
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
`source` is a GitHub registry like `github.com/owner/repo`, the `owner/repo` shorthand, or a `git@github.com:owner/repo` SSH URL. `bundle` is the bundle name; omit it when the source is a single-bundle repo or when you want the interactive picker.
|
|
68
|
+
|
|
69
|
+
| Option | Description |
|
|
70
|
+
|---|---|
|
|
71
|
+
| `-a, --agent <name>` | Materialize for one tool only. Repeat to target multiple tools. Defaults to every tool the bundle ships content for. |
|
|
72
|
+
| `--ref <selector>` | Track a specific branch, tag, or commit instead of remote `HEAD`. Persisted in the registry and reused by `skul apply`. |
|
|
73
|
+
| `--pin <commit>` | Pin the bundle to one commit SHA (7–40 hex chars). Mutually exclusive with `--ref`. |
|
|
74
|
+
| `--include <item>` | Install only a specific bundle item. Repeat for multiple. Selectors: `skills/<name>`, `commands/<name>`, `agents/<name>`, `root-instruction` (`AGENTS.md` / `CLAUDE.md` also accepted). |
|
|
75
|
+
| `--select-items` | Open an interactive picker for bundle items. When combined with `--include`, the included items are preselected. |
|
|
76
|
+
| `-s, --ssh` | Clone the source via SSH instead of HTTPS. `git@host:owner/repo` URLs are auto-detected as SSH. Protocol is persisted and reused by `skul apply`. |
|
|
77
|
+
| `-g, --global` | Install to global tool config under `~/` instead of the current worktree. |
|
|
78
|
+
| `-n, --dry-run` | Preview what would be written without making any changes. |
|
|
79
|
+
|
|
80
|
+
### Examples
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
# Track a branch or tag instead of HEAD
|
|
84
|
+
skul add github.com/sjquant/ai-bundles react-expert --ref stable
|
|
85
|
+
|
|
86
|
+
# Pin to an exact commit
|
|
87
|
+
skul add github.com/sjquant/ai-bundles react-expert --pin 2813b88
|
|
88
|
+
|
|
89
|
+
# Install only the diagnose skill, for Codex only
|
|
90
|
+
skul add react-expert --agent codex --include skills/diagnose
|
|
91
|
+
|
|
92
|
+
# Install only the bundle's root instruction
|
|
93
|
+
skul add react-expert --agent codex --include root-instruction
|
|
94
|
+
|
|
95
|
+
# Pick items interactively
|
|
96
|
+
skul add react-expert --agent codex --select-items
|
|
97
|
+
|
|
98
|
+
# Clone over SSH
|
|
99
|
+
skul add --ssh github.com/sjquant/ai-bundles react-expert
|
|
100
|
+
skul add git@github.com:sjquant/ai-bundles react-expert
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
If SSH authentication fails, Skul prints a hint with the HTTPS equivalent command.
|
|
104
|
+
|
|
105
|
+
---
|
|
106
|
+
|
|
107
|
+
## Supported Tools
|
|
108
|
+
|
|
109
|
+
| Tool | Skills | Commands | Agents | Root Instructions |
|
|
110
|
+
|---|---|---|---|---|
|
|
111
|
+
| **[Claude Code](https://docs.anthropic.com/en/docs/claude-code)** | `.claude/skills` | `.claude/commands` | `.claude/agents` | `CLAUDE.md` |
|
|
112
|
+
| **[Cursor](https://cursor.sh)** | `.cursor/skills` | `.cursor/commands` | `.cursor/agents` | `CLAUDE.md` |
|
|
113
|
+
| **[OpenCode](https://opencode.ai)** | `.opencode/skills` | `.opencode/commands` | `.opencode/agents` | `CLAUDE.md` |
|
|
114
|
+
| **[Codex](https://openai.com/index/openai-codex)** | `.agents/skills` | — | `.codex/agents` | `AGENTS.md` |
|
|
115
|
+
| **[GitHub Copilot](https://github.com/features/copilot)** | `.github/skills` | — | `.github/agents` | `.github/copilot-instructions.md` |
|
|
116
|
+
| **[Kiro](https://kiro.dev)** | `.kiro/skills` | — | `.kiro/agents` | `AGENTS.md` |
|
|
117
|
+
|
|
118
|
+
Use `--agent <name>` to target a single tool. Repeat the flag to target multiple tools.
|
|
119
|
+
|
|
120
|
+
---
|
|
121
|
+
|
|
122
|
+
## Bundle Structure
|
|
123
|
+
|
|
124
|
+
A bundle source is a GitHub repository. Skul clones it once into `~/.skul/library/` and reuses the cache for subsequent `add` calls. Two repository layouts are supported:
|
|
125
|
+
|
|
126
|
+
**Multi-bundle** — each subdirectory is its own bundle, identified by the directory name:
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
github.com/sjquant/ai-bundles
|
|
130
|
+
├── react-expert/
|
|
131
|
+
│ ├── skills/
|
|
132
|
+
│ ├── commands/
|
|
133
|
+
│ └── agents/
|
|
134
|
+
└── python-data/
|
|
135
|
+
└── skills/
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
**Repo-as-bundle** — the repository root is a single bundle. No `manifest.json` is needed; Skul infers the tool targets from the directory structure. The bundle name defaults to the repository slug:
|
|
139
|
+
|
|
140
|
+
```
|
|
141
|
+
github.com/sjquant/react-bundle
|
|
142
|
+
├── skills/
|
|
143
|
+
└── commands/
|
|
144
|
+
```
|
|
145
|
+
|
|
146
|
+
Inside a bundle, two content layouts are supported:
|
|
147
|
+
|
|
148
|
+
**Canonical** — `skills/`, `commands/`, `agents/`, `AGENTS.md`, and `CLAUDE.md` at the top level. Skul copies each directory to every tool that supports it, and treats root instruction files as generic cross-tool sources.
|
|
149
|
+
|
|
150
|
+
**Native** — tool-specific dotdirs (`.claude/skills/`, `.cursor/commands/`, `.github/agents/`, `.kiro/skills/`, etc.) for content targeting a single tool only.
|
|
151
|
+
|
|
152
|
+
### Root Instruction Targets
|
|
153
|
+
|
|
154
|
+
Skul supports three root instruction target files:
|
|
155
|
+
|
|
156
|
+
| Target file | Native tools |
|
|
157
|
+
|---|---|
|
|
158
|
+
| `CLAUDE.md` | `claude-code`, `cursor`, `opencode` |
|
|
159
|
+
| `AGENTS.md` | `codex`, `kiro` |
|
|
160
|
+
| `.github/copilot-instructions.md` | `copilot` |
|
|
161
|
+
|
|
162
|
+
Root instruction bundles are compatible across those targets. If a bundle only ships `CLAUDE.md`, Skul can still materialize `AGENTS.md` for Codex or Kiro and `.github/copilot-instructions.md` for GitHub Copilot. If a bundle only ships `AGENTS.md` or `.github/copilot-instructions.md`, Skul can still materialize the equivalent target file for the other tools, as long as the instruction body can be reused as-is.
|
|
163
|
+
|
|
164
|
+
Examples:
|
|
165
|
+
|
|
166
|
+
```bash
|
|
167
|
+
# Bundle only provides CLAUDE.md, but Codex and Kiro still get AGENTS.md
|
|
168
|
+
skul add github.com/sjquant/ai-bundles repo-standards --agent codex
|
|
169
|
+
skul add github.com/sjquant/ai-bundles repo-standards --agent kiro
|
|
170
|
+
|
|
171
|
+
# Bundle only provides AGENTS.md, but Claude Code still gets CLAUDE.md
|
|
172
|
+
skul add github.com/sjquant/ai-bundles repo-standards --agent claude-code
|
|
173
|
+
|
|
174
|
+
# Bundle only provides AGENTS.md or CLAUDE.md, but Copilot still gets its native target
|
|
175
|
+
skul add github.com/sjquant/ai-bundles repo-standards --agent copilot
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
---
|
|
179
|
+
|
|
180
|
+
## How It Works
|
|
181
|
+
|
|
182
|
+
- **`~/.skul/library/`** — cached bundle sources (cloned Git repos or local directories)
|
|
183
|
+
- **`~/.skul/registry.json`** — repo-level desired state + per-worktree materialization records
|
|
184
|
+
|
|
185
|
+
The registry tracks two things separately: which bundles a repo *wants*, and which files were actually *written* in each worktree. A new linked worktree inherits the desired state immediately — run `skul apply` to materialize.
|
|
186
|
+
|
|
187
|
+
Skul writes ignore rules to `.git/info/exclude` only — never `.gitignore`, never Git history.
|
|
188
|
+
|
|
189
|
+
### Root Instruction Behavior
|
|
190
|
+
|
|
191
|
+
Skul uses two different workflows for root instruction files, depending on whether the target file is already tracked by Git.
|
|
192
|
+
|
|
193
|
+
**Untracked stealth**
|
|
194
|
+
|
|
195
|
+
- If the target root instruction file is not tracked, Skul materializes it like any other managed file and hides it through `.git/info/exclude`.
|
|
196
|
+
- If the file already existed locally, Skul preserves that pre-existing content as the base and appends bundle content inside explicit `BEGIN/END SKUL BUNDLE` markers.
|
|
197
|
+
- Multiple bundles can share the same untracked root instruction file. Skul recomposes the file in desired-state order and restores the preserved base content when the last contributing bundle is removed or `skul reset` runs.
|
|
198
|
+
|
|
199
|
+
Example:
|
|
200
|
+
|
|
201
|
+
```bash
|
|
202
|
+
skul add github.com/sjquant/ai-bundles repo-standards --agent codex
|
|
203
|
+
|
|
204
|
+
# Result: untracked AGENTS.md
|
|
205
|
+
# - hidden through .git/info/exclude
|
|
206
|
+
# - existing local AGENTS.md content preserved at the top, if present
|
|
207
|
+
# - bundle content appended inside SKUL bundle markers
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
**Tracked shadow**
|
|
211
|
+
|
|
212
|
+
- If the repo already tracks the target root instruction file, Skul does not use `.git/info/exclude`.
|
|
213
|
+
- Instead, Skul treats the worktree copy as generated output: it renders `HEAD:<path>` plus the bundle overlay, writes the effective file, and sets `git update-index --skip-worktree`.
|
|
214
|
+
- `skul status` reports tracked root instructions in a separate `Shadowed Instructions` section, including whether the base blob is current, whether the overlay still matches, whether `skip-worktree` is set, and whether manual edits are suspected.
|
|
215
|
+
- A tracked root instruction target has one active shadow owner at a time. Multi-bundle composition is supported for untracked root files, not for tracked shadows.
|
|
216
|
+
|
|
217
|
+
Example:
|
|
218
|
+
|
|
219
|
+
```bash
|
|
220
|
+
# Team policy is already committed in CLAUDE.md
|
|
221
|
+
skul add github.com/sjquant/ai-bundles claude-standards --agent claude-code
|
|
222
|
+
|
|
223
|
+
# Result: tracked CLAUDE.md
|
|
224
|
+
# - rendered as committed team base + SKUL shadow block
|
|
225
|
+
# - hidden from git status with skip-worktree
|
|
226
|
+
# - recorded in the worktree registry as a shadowed file
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
For tracked shadow lifecycle (`shadow --suspend`/`--refresh`, `sync`), safety limits, recovery flows, and SSH cloning, see [docs/advanced.md](docs/advanced.md).
|
|
230
|
+
|
|
231
|
+
---
|
|
232
|
+
|
|
233
|
+
## Installation
|
|
234
|
+
|
|
235
|
+
```bash
|
|
236
|
+
npm install --global @solaqua/skul
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
**Requirements:** Node.js >=20, git
|
|
240
|
+
|
|
241
|
+
### Local development install
|
|
242
|
+
|
|
243
|
+
```bash
|
|
244
|
+
git clone https://github.com/sjquant/skul
|
|
245
|
+
cd skul
|
|
246
|
+
pnpm install && pnpm run build
|
|
247
|
+
pnpm link --global
|
|
248
|
+
```
|
|
249
|
+
|
|
250
|
+
---
|
|
251
|
+
|
|
252
|
+
## Development
|
|
253
|
+
|
|
254
|
+
```bash
|
|
255
|
+
pnpm install
|
|
256
|
+
pnpm run typecheck
|
|
257
|
+
pnpm run test
|
|
258
|
+
pnpm run build
|
|
259
|
+
pnpm run dev -- --help
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
---
|
|
263
|
+
|
|
264
|
+
## FAQ
|
|
265
|
+
|
|
266
|
+
**Does Skul modify `.gitignore`?**
|
|
267
|
+
No. Ignore rules go to `.git/info/exclude` — a local, per-clone file that is never committed or pushed.
|
|
268
|
+
|
|
269
|
+
**How do I publish a bundle?**
|
|
270
|
+
Two options: (1) create a GitHub repo with one subdirectory per bundle, each containing `skills/`, `commands/`, and/or `agents/` — users run `skul add github.com/your-org/ai-bundles <bundle>`; or (2) place `skills/`, `commands/`, and/or `agents/` directly at the repository root — users run `skul add github.com/your-org/my-bundle`, and Skul uses the repo slug as the bundle name. No `manifest.json` required.
|
|
271
|
+
|
|
272
|
+
**What happens if I edit a Skul-managed file?**
|
|
273
|
+
Skul fingerprints files on write. Edited files require explicit confirmation before removal, or fail fast with `SKUL_NO_TUI=1`.
|
|
274
|
+
|
|
275
|
+
**How do I keep bundles up to date, customize cloning, or clean up?**
|
|
276
|
+
See [docs/advanced.md](docs/advanced.md) for `check`/`update`, tracked-shadow lifecycle (`shadow`, `sync`), SSH cloning, and `skul reset`.
|
|
277
|
+
|
|
278
|
+
---
|
|
279
|
+
|
|
280
|
+
## License
|
|
281
|
+
|
|
282
|
+
[ISC](LICENSE)
|
package/bin/skul.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const { run } = require("../dist/index.js");
|
|
4
|
+
|
|
5
|
+
void run(process.argv.slice(2))
|
|
6
|
+
.then((output) => {
|
|
7
|
+
console.log(output);
|
|
8
|
+
})
|
|
9
|
+
.catch((error) => {
|
|
10
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
11
|
+
console.error(message);
|
|
12
|
+
process.exitCode = 1;
|
|
13
|
+
});
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { type BundleManifest } from "./bundle-manifest";
|
|
2
|
+
export interface CachedBundle {
|
|
3
|
+
source: string;
|
|
4
|
+
bundle: string;
|
|
5
|
+
manifestFile: string;
|
|
6
|
+
manifest: BundleManifest;
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* Infers the preferred clone protocol from a raw user-supplied source string.
|
|
10
|
+
*
|
|
11
|
+
* Returns "ssh" when the input starts with `git@` (the standard SCP-style SSH
|
|
12
|
+
* syntax, e.g. `git@github.com:owner/repo.git`). All other forms — HTTPS URLs
|
|
13
|
+
* (`https://…`) and plain source shorthand — return "https".
|
|
14
|
+
*
|
|
15
|
+
* This function operates on the *raw* input before normalization, so the `git@`
|
|
16
|
+
* prefix is still present and unambiguous.
|
|
17
|
+
*/
|
|
18
|
+
export declare function detectSourceProtocol(input: string): "https" | "ssh";
|
|
19
|
+
/** Normalizes a user-supplied git source into `host/owner/repo` form. */
|
|
20
|
+
export declare function normalizeBundleSource(input: string): string;
|
|
21
|
+
/** Lists every bundle currently discoverable in the local cache. */
|
|
22
|
+
export declare function listCachedBundles(options: {
|
|
23
|
+
libraryDir: string;
|
|
24
|
+
}): CachedBundle[];
|
|
25
|
+
/** Resolves one cached bundle by source and bundle name, inferring repo bundles when needed. */
|
|
26
|
+
export declare function findCachedBundle(options: {
|
|
27
|
+
libraryDir: string;
|
|
28
|
+
bundle: string;
|
|
29
|
+
source?: string;
|
|
30
|
+
}): CachedBundle;
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.detectSourceProtocol = detectSourceProtocol;
|
|
7
|
+
exports.normalizeBundleSource = normalizeBundleSource;
|
|
8
|
+
exports.listCachedBundles = listCachedBundles;
|
|
9
|
+
exports.findCachedBundle = findCachedBundle;
|
|
10
|
+
const node_fs_1 = __importDefault(require("node:fs"));
|
|
11
|
+
const node_path_1 = __importDefault(require("node:path"));
|
|
12
|
+
const bundle_manifest_1 = require("./bundle-manifest");
|
|
13
|
+
const fs_utils_1 = require("./fs-utils");
|
|
14
|
+
/**
|
|
15
|
+
* Infers the preferred clone protocol from a raw user-supplied source string.
|
|
16
|
+
*
|
|
17
|
+
* Returns "ssh" when the input starts with `git@` (the standard SCP-style SSH
|
|
18
|
+
* syntax, e.g. `git@github.com:owner/repo.git`). All other forms — HTTPS URLs
|
|
19
|
+
* (`https://…`) and plain source shorthand — return "https".
|
|
20
|
+
*
|
|
21
|
+
* This function operates on the *raw* input before normalization, so the `git@`
|
|
22
|
+
* prefix is still present and unambiguous.
|
|
23
|
+
*/
|
|
24
|
+
function detectSourceProtocol(input) {
|
|
25
|
+
return /^git@/.test(input.trim()) ? "ssh" : "https";
|
|
26
|
+
}
|
|
27
|
+
/** Normalizes a user-supplied git source into `host/owner/repo` form. */
|
|
28
|
+
function normalizeBundleSource(input) {
|
|
29
|
+
const value = input.trim();
|
|
30
|
+
if (!value) {
|
|
31
|
+
throw new Error("source is required");
|
|
32
|
+
}
|
|
33
|
+
if (/^https?:\/\//.test(value)) {
|
|
34
|
+
const url = new URL(value);
|
|
35
|
+
if (url.search || url.hash) {
|
|
36
|
+
throw new Error(`Unsupported git source: ${input}`);
|
|
37
|
+
}
|
|
38
|
+
return normalizeSourceParts(url.hostname, url.pathname.replace(/^\//, "").replace(/\.git$/, ""));
|
|
39
|
+
}
|
|
40
|
+
const sshMatch = value.match(/^git@([^:]+):(.+)$/);
|
|
41
|
+
if (sshMatch) {
|
|
42
|
+
return normalizeSourceParts(sshMatch[1], sshMatch[2].replace(/\.git$/, ""));
|
|
43
|
+
}
|
|
44
|
+
if (value.includes("://") || value.includes("?") || value.includes("#")) {
|
|
45
|
+
throw new Error(`Unsupported git source: ${input}`);
|
|
46
|
+
}
|
|
47
|
+
const shorthandSource = normalizeGitHubShortcut(value);
|
|
48
|
+
if (shorthandSource) {
|
|
49
|
+
return shorthandSource;
|
|
50
|
+
}
|
|
51
|
+
const [host, owner, repo, ...rest] = value.split("/");
|
|
52
|
+
if (!host || !owner || !repo || rest.length > 0) {
|
|
53
|
+
throw new Error(`Unsupported git source: ${input}`);
|
|
54
|
+
}
|
|
55
|
+
return `${host}/${owner}/${repo}`;
|
|
56
|
+
}
|
|
57
|
+
function normalizeGitHubShortcut(input) {
|
|
58
|
+
const [owner, repo, ...rest] = input.split("/");
|
|
59
|
+
if (!owner || !repo || rest.length > 0) {
|
|
60
|
+
return undefined;
|
|
61
|
+
}
|
|
62
|
+
if (owner.includes(".") || owner.includes(":")) {
|
|
63
|
+
return undefined;
|
|
64
|
+
}
|
|
65
|
+
const normalizedRepo = repo.replace(/\.git$/, "");
|
|
66
|
+
if (!normalizedRepo) {
|
|
67
|
+
return undefined;
|
|
68
|
+
}
|
|
69
|
+
return `github.com/${owner}/${normalizedRepo}`;
|
|
70
|
+
}
|
|
71
|
+
/** Lists every bundle currently discoverable in the local cache. */
|
|
72
|
+
function listCachedBundles(options) {
|
|
73
|
+
if (!node_fs_1.default.existsSync(options.libraryDir)) {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
const manifestFiles = findManifestFiles(options.libraryDir);
|
|
77
|
+
const explicit = manifestFiles.flatMap((manifestFile) => {
|
|
78
|
+
try {
|
|
79
|
+
const manifest = (0, bundle_manifest_1.parseBundleManifest)(JSON.parse(node_fs_1.default.readFileSync(manifestFile, "utf8")));
|
|
80
|
+
const relativeManifestFile = node_path_1.default.relative(options.libraryDir, manifestFile);
|
|
81
|
+
const segments = relativeManifestFile.split(node_path_1.default.sep);
|
|
82
|
+
if (segments.at(-1) !== bundle_manifest_1.MANIFEST_FILE_NAME) {
|
|
83
|
+
return [];
|
|
84
|
+
}
|
|
85
|
+
// Subdirectory bundle: host/owner/repo/bundle-name/manifest.json (5 segments)
|
|
86
|
+
if (segments.length === 5) {
|
|
87
|
+
const source = segments.slice(0, 3).join("/");
|
|
88
|
+
const bundle = segments[3];
|
|
89
|
+
return [{ source, bundle, manifestFile, manifest }];
|
|
90
|
+
}
|
|
91
|
+
return [];
|
|
92
|
+
}
|
|
93
|
+
catch {
|
|
94
|
+
return [];
|
|
95
|
+
}
|
|
96
|
+
});
|
|
97
|
+
const explicitBundleKeys = new Set(explicit.map((bundle) => `${bundle.source}::${bundle.bundle}`));
|
|
98
|
+
const inferredSubdirectory = findSourceDirs(options.libraryDir).flatMap((sourceDir) => inferSubdirectoryBundles(sourceDir, explicitBundleKeys));
|
|
99
|
+
// Repos with any valid or inferred bundle subdirectory are treated as multi-bundle
|
|
100
|
+
// sources and excluded from repo-root inference.
|
|
101
|
+
const sourceDirsWithSubdirectoryBundle = new Set([...explicit, ...inferredSubdirectory].map((bundle) => node_path_1.default.join(options.libraryDir, ...bundle.source.split("/"))));
|
|
102
|
+
// Inferred repo-as-bundle: repos without subdirectory bundle manifests but with
|
|
103
|
+
// recognisable bundle directories (skills/, commands/, agents/, .claude/, etc.).
|
|
104
|
+
// The bundle name defaults to the repository slug.
|
|
105
|
+
const inferred = findSourceDirs(options.libraryDir).flatMap((sourceDir) => {
|
|
106
|
+
if (sourceDirsWithSubdirectoryBundle.has(sourceDir)) {
|
|
107
|
+
return [];
|
|
108
|
+
}
|
|
109
|
+
try {
|
|
110
|
+
const relativeSourceDir = node_path_1.default.relative(options.libraryDir, sourceDir);
|
|
111
|
+
const sourceSegments = relativeSourceDir.split(node_path_1.default.sep);
|
|
112
|
+
const bundleName = sourceSegments[2];
|
|
113
|
+
const manifest = (0, bundle_manifest_1.inferBundleManifest)(sourceDir);
|
|
114
|
+
if (Object.keys(manifest.tools).length === 0) {
|
|
115
|
+
return [];
|
|
116
|
+
}
|
|
117
|
+
return [
|
|
118
|
+
{
|
|
119
|
+
source: sourceSegments.join("/"),
|
|
120
|
+
bundle: bundleName,
|
|
121
|
+
manifestFile: node_path_1.default.join(sourceDir, bundle_manifest_1.MANIFEST_FILE_NAME),
|
|
122
|
+
manifest,
|
|
123
|
+
},
|
|
124
|
+
];
|
|
125
|
+
}
|
|
126
|
+
catch {
|
|
127
|
+
return [];
|
|
128
|
+
}
|
|
129
|
+
});
|
|
130
|
+
return [...explicit, ...inferredSubdirectory, ...inferred].sort((left, right) => left.source.localeCompare(right.source) ||
|
|
131
|
+
left.bundle.localeCompare(right.bundle));
|
|
132
|
+
}
|
|
133
|
+
/** Resolves one cached bundle by source and bundle name, inferring repo bundles when needed. */
|
|
134
|
+
function findCachedBundle(options) {
|
|
135
|
+
if (options.source) {
|
|
136
|
+
const source = normalizeBundleSource(options.source);
|
|
137
|
+
const layout = (0, bundle_manifest_1.resolveCachedBundleLayout)({
|
|
138
|
+
libraryDir: options.libraryDir,
|
|
139
|
+
source,
|
|
140
|
+
bundle: options.bundle,
|
|
141
|
+
});
|
|
142
|
+
// Try subdirectory bundle first: libraryDir/host/owner/repo/bundle-name/manifest.json
|
|
143
|
+
if (node_fs_1.default.existsSync(layout.manifestFile)) {
|
|
144
|
+
return {
|
|
145
|
+
source,
|
|
146
|
+
bundle: options.bundle,
|
|
147
|
+
manifestFile: layout.manifestFile,
|
|
148
|
+
manifest: (0, bundle_manifest_1.parseBundleManifest)(JSON.parse(node_fs_1.default.readFileSync(layout.manifestFile, "utf8"))),
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
if (node_fs_1.default.existsSync(layout.bundleDir)) {
|
|
152
|
+
const manifest = (0, bundle_manifest_1.inferBundleManifest)(layout.bundleDir);
|
|
153
|
+
if (Object.keys(manifest.tools).length > 0) {
|
|
154
|
+
return {
|
|
155
|
+
source,
|
|
156
|
+
bundle: options.bundle,
|
|
157
|
+
manifestFile: layout.manifestFile,
|
|
158
|
+
manifest,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
// Fall back to inferred repo-as-bundle: repo slug must match the requested bundle name,
|
|
163
|
+
// and the repo must not expose valid subdirectory bundle manifests.
|
|
164
|
+
const repoBundleManifestFile = node_path_1.default.join(layout.sourceDir, bundle_manifest_1.MANIFEST_FILE_NAME);
|
|
165
|
+
const repoSlug = source.split("/").at(-1);
|
|
166
|
+
if (repoSlug === options.bundle && node_fs_1.default.existsSync(layout.sourceDir)) {
|
|
167
|
+
const hasNestedBundle = hasAnySubdirectoryBundle(layout.sourceDir);
|
|
168
|
+
if (!hasNestedBundle) {
|
|
169
|
+
const manifest = (0, bundle_manifest_1.inferBundleManifest)(layout.sourceDir);
|
|
170
|
+
if (Object.keys(manifest.tools).length > 0) {
|
|
171
|
+
return {
|
|
172
|
+
source,
|
|
173
|
+
bundle: repoSlug,
|
|
174
|
+
manifestFile: repoBundleManifestFile,
|
|
175
|
+
manifest,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
throw new Error(`Bundle not found: ${options.bundle}`);
|
|
181
|
+
}
|
|
182
|
+
const matches = listCachedBundles({ libraryDir: options.libraryDir }).filter((bundle) => bundle.bundle === options.bundle);
|
|
183
|
+
if (matches.length === 0) {
|
|
184
|
+
throw new Error(`Bundle not found: ${options.bundle}`);
|
|
185
|
+
}
|
|
186
|
+
if (matches.length > 1) {
|
|
187
|
+
throw new Error(`Bundle name is ambiguous: ${options.bundle}`);
|
|
188
|
+
}
|
|
189
|
+
return matches[0];
|
|
190
|
+
}
|
|
191
|
+
function normalizeSourceParts(host, repoPath) {
|
|
192
|
+
const normalizedRepoPath = repoPath.replace(/^\/+|\/+$/g, "");
|
|
193
|
+
const [owner, repo, ...rest] = normalizedRepoPath.split("/");
|
|
194
|
+
if (!host || !owner || !repo || rest.length > 0) {
|
|
195
|
+
throw new Error(`Unsupported git source: ${host}/${repoPath}`);
|
|
196
|
+
}
|
|
197
|
+
return `${host}/${owner}/${repo}`;
|
|
198
|
+
}
|
|
199
|
+
function findSourceDirs(libraryDir) {
|
|
200
|
+
const sourceDirs = [];
|
|
201
|
+
for (const hostEntry of (0, fs_utils_1.safeReaddirSync)(libraryDir)) {
|
|
202
|
+
if (!hostEntry.isDirectory())
|
|
203
|
+
continue;
|
|
204
|
+
const hostDir = node_path_1.default.join(libraryDir, hostEntry.name);
|
|
205
|
+
for (const ownerEntry of (0, fs_utils_1.safeReaddirSync)(hostDir)) {
|
|
206
|
+
if (!ownerEntry.isDirectory())
|
|
207
|
+
continue;
|
|
208
|
+
const ownerDir = node_path_1.default.join(hostDir, ownerEntry.name);
|
|
209
|
+
for (const repoEntry of (0, fs_utils_1.safeReaddirSync)(ownerDir)) {
|
|
210
|
+
if (!repoEntry.isDirectory())
|
|
211
|
+
continue;
|
|
212
|
+
sourceDirs.push(node_path_1.default.join(ownerDir, repoEntry.name));
|
|
213
|
+
}
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return sourceDirs;
|
|
217
|
+
}
|
|
218
|
+
function findManifestFiles(rootDir) {
|
|
219
|
+
const manifestFiles = [];
|
|
220
|
+
const queue = [rootDir];
|
|
221
|
+
while (queue.length > 0) {
|
|
222
|
+
const currentDir = queue.shift();
|
|
223
|
+
for (const entry of node_fs_1.default.readdirSync(currentDir, { withFileTypes: true })) {
|
|
224
|
+
const entryPath = node_path_1.default.join(currentDir, entry.name);
|
|
225
|
+
if (entry.isDirectory()) {
|
|
226
|
+
queue.push(entryPath);
|
|
227
|
+
continue;
|
|
228
|
+
}
|
|
229
|
+
if (entry.isFile() && entry.name === bundle_manifest_1.MANIFEST_FILE_NAME) {
|
|
230
|
+
manifestFiles.push(entryPath);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
return manifestFiles;
|
|
235
|
+
}
|
|
236
|
+
function inferSubdirectoryBundles(sourceDir, explicitBundleKeys) {
|
|
237
|
+
const sourceSegments = node_path_1.default.normalize(sourceDir).split(node_path_1.default.sep).slice(-3);
|
|
238
|
+
const source = sourceSegments.join("/");
|
|
239
|
+
return (0, fs_utils_1.safeReaddirSync)(sourceDir).flatMap((entry) => {
|
|
240
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
241
|
+
return [];
|
|
242
|
+
}
|
|
243
|
+
const bundleDir = node_path_1.default.join(sourceDir, entry.name);
|
|
244
|
+
const manifest = (0, bundle_manifest_1.inferBundleManifest)(bundleDir);
|
|
245
|
+
if (Object.keys(manifest.tools).length === 0) {
|
|
246
|
+
return [];
|
|
247
|
+
}
|
|
248
|
+
const bundleKey = `${source}::${entry.name}`;
|
|
249
|
+
if (explicitBundleKeys.has(bundleKey)) {
|
|
250
|
+
return [];
|
|
251
|
+
}
|
|
252
|
+
return [
|
|
253
|
+
{
|
|
254
|
+
source,
|
|
255
|
+
bundle: entry.name,
|
|
256
|
+
manifestFile: node_path_1.default.join(bundleDir, bundle_manifest_1.MANIFEST_FILE_NAME),
|
|
257
|
+
manifest,
|
|
258
|
+
},
|
|
259
|
+
];
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
function hasAnySubdirectoryBundle(sourceDir) {
|
|
263
|
+
return (hasValidSubdirectoryBundleManifest(sourceDir) ||
|
|
264
|
+
inferSubdirectoryBundles(sourceDir, new Set()).length > 0);
|
|
265
|
+
}
|
|
266
|
+
function hasValidSubdirectoryBundleManifest(sourceDir) {
|
|
267
|
+
return (0, fs_utils_1.safeReaddirSync)(sourceDir).some((entry) => {
|
|
268
|
+
if (!entry.isDirectory() || entry.name.startsWith(".")) {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
const manifestFile = node_path_1.default.join(sourceDir, entry.name, bundle_manifest_1.MANIFEST_FILE_NAME);
|
|
272
|
+
if (!node_fs_1.default.existsSync(manifestFile)) {
|
|
273
|
+
return false;
|
|
274
|
+
}
|
|
275
|
+
try {
|
|
276
|
+
(0, bundle_manifest_1.parseBundleManifest)(JSON.parse(node_fs_1.default.readFileSync(manifestFile, "utf8")));
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
catch {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
//# sourceMappingURL=bundle-discovery.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bundle-discovery.js","sourceRoot":"","sources":["../src/bundle-discovery.ts"],"names":[],"mappings":"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"}
|