Skip to content
Open
Show file tree
Hide file tree
Changes from 53 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
23aa976
feat: WIP PR Message
EriiYenn Jan 29, 2026
dfc2f46
feat: Introduce Cursor and Claude hooks as new asset kinds
EriiYenn Jan 30, 2026
8c30989
feat: Implement hooks validation module
EriiYenn Jan 30, 2026
6ea8496
feat: Implement hooks installation helpers and conflict handling
EriiYenn Jan 30, 2026
9b1c1c9
feat: Integrate hooks validation and installation into core logic
EriiYenn Jan 30, 2026
91cb725
feat: Enable catalog generation for hook scripts
EriiYenn Jan 30, 2026
8d4a7e7
test: Add CLI tests for Cursor and Claude hooks
EriiYenn Jan 30, 2026
62d5879
docs: Update PR message for hooks feature
EriiYenn Jan 30, 2026
b23f3bd
chore: Bump version to 0.1.8
EriiYenn Jan 30, 2026
53f6062
docs: Update PR message for hooks feature pt.2
EriiYenn Jan 30, 2026
652e13e
feat: Remove PR Message
EriiYenn Jan 30, 2026
e4cba8b
Merge branch 'feat/synchronization-improvements'
EriiYenn Jan 30, 2026
d6a560f
feat: Implement per-run Git clone caching mechanism
EriiYenn Jan 30, 2026
2ab83d3
feat: Integrate Git clone cache into sync command execution
EriiYenn Jan 30, 2026
4c804a5
test: Add integration tests for Git clone caching
EriiYenn Jan 30, 2026
81a71ca
docs: Document Git clone caching in architecture.md
EriiYenn Jan 30, 2026
0ed0f50
chore: Bump version to 0.1.9
EriiYenn Jan 30, 2026
5b52ff8
Merge branch 'main' into feat/synchronization-improvements
westonplatter Jan 31, 2026
642d01b
fix: PR Changes
EriiYenn Feb 1, 2026
74fe381
fix: PR Changes pt.2
EriiYenn Feb 1, 2026
06f04cb
Merge branch 'westonplatter:main' into main
EriiYenn Feb 3, 2026
86bf5a3
Merge branch 'main' into feat/synchronization-improvements
EriiYenn Feb 3, 2026
e805d58
feat: Scope Down to Cursor Hooks
EriiYenn Feb 3, 2026
55c9f66
fix: PR Changes
EriiYenn Feb 3, 2026
5dd7439
fix: Remove Version Bump
EriiYenn Feb 4, 2026
d0a66f4
Merge branch 'main' into feat/synchronization-improvements
EriiYenn Feb 4, 2026
861fc59
feat: Lint issues
EriiYenn Feb 4, 2026
2a0b58b
Merge branch 'feat/synchronization-improvements'
EriiYenn Feb 5, 2026
dda59cc
Merge branch 'main' into feat/git-improvements
EriiYenn Feb 5, 2026
38bdfaa
feat: Verification
EriiYenn Feb 5, 2026
8d7f805
fix: Clippy CI Error
EriiYenn Feb 5, 2026
6bb49d0
fix: Comment on Directory Merge
EriiYenn Feb 5, 2026
fa3c809
fix: Directory Merge Issue
EriiYenn Feb 5, 2026
e980443
fix: Directory Merge Conflict Detect
EriiYenn Feb 5, 2026
af8b528
fix: Hook Install Logic
EriiYenn Feb 5, 2026
5cc831b
fix: Hooks Dir Walk
EriiYenn Feb 5, 2026
a027edf
fix: Hooks Dir Walk pt.2
EriiYenn Feb 5, 2026
ae0e663
fix: Permissions & Pre-copy Cleanup
EriiYenn Feb 5, 2026
d374572
doc: Remove ClaudeHooks from AssetKind enum
westonplatter Feb 6, 2026
252e545
Merge branch 'feat/synchronization-improvements'
EriiYenn Feb 6, 2026
ee693cf
Merge branch 'westonplatter:main' into main
EriiYenn Feb 6, 2026
fdafdf4
Merge branch 'main' into feat/git-improvements
EriiYenn Feb 6, 2026
3ead5bf
feat: APS Add Improvements
EriiYenn Feb 6, 2026
6af90b4
feat: APS Lockfile Changes
EriiYenn Feb 10, 2026
92378de
feat: Lockfile README Extension
EriiYenn Feb 10, 2026
a9d4710
feat: Sync Progress
EriiYenn Feb 10, 2026
ea094d3
fix: Clippy Lint
EriiYenn Feb 10, 2026
38c6619
Merge branch 'westonplatter:main' into main
EriiYenn Feb 13, 2026
0f90b02
Merge branch 'main' into feat/git-improvements
EriiYenn Feb 13, 2026
1464365
Merge branch 'westonplatter:main' into main
EriiYenn Feb 16, 2026
62c34c3
Merge branch 'main' into feat/git-improvements
EriiYenn Feb 16, 2026
0fa7cbd
fix: Conflicts
EriiYenn Feb 16, 2026
f1147ef
fix: Unused Error
EriiYenn Feb 17, 2026
91f478d
Merge branch 'main' into feat/git-improvements
EriiYenn Feb 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -5,4 +5,5 @@
aps.catalog.yaml

.cursor
.claude
_bmad*
4 changes: 2 additions & 2 deletions .trunk/trunk.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,15 @@ lint:
- CHANGELOG.md # Auto-generated by release-please
- AGENTS.md
enabled:
- checkov@3.2.497
- checkov@3.2.500
- clippy@1.83.0
- git-diff-check
- markdownlint@0.47.0
- osv-scanner@2.3.2
- prettier@3.8.0
- rustfmt@1.83.0
- taplo@0.10.0
- trufflehog@3.92.5
- trufflehog@3.93.0
Comment thread
coderabbitai[bot] marked this conversation as resolved.
- yamllint@1.38.0
actions:
enabled:
Expand Down
68 changes: 56 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
- **Composable AGENTS.md** - Merge multiple AGENTS.md files from local or remote sources into one
- **Safe installs** - Automatic conflict detection and backup creation
- **Deterministic lockfile** - Idempotent syncs that only update when needed
- **Optimized Git Operations** - Smart caching reuses clones and skips unchanged commits for fast syncs

## Installation

Expand Down Expand Up @@ -78,17 +79,23 @@ cargo build --release

This creates a `aps.yaml` manifest file with an example entry.

2. **Add skills directly from GitHub URLs:**
2. **Add assets directly from git repositories:**

```bash
# Add a skill from a GitHub URL - automatically syncs the skill
aps add https://github.com/hashicorp/agent-skills/blob/main/terraform/module-generation/skills/refactor-module/SKILL.md
aps add agent_skill https://github.com/hashicorp/agent-skills/blob/main/terraform/module-generation/skills/refactor-module/SKILL.md

# Or use the folder URL (SKILL.md is auto-detected)
aps add https://github.com/hashicorp/agent-skills/tree/main/terraform/module-generation/skills/refactor-module
aps add agent_skill https://github.com/hashicorp/agent-skills/tree/main/terraform/module-generation/skills/refactor-module

# Add Cursor rules from a private repo (SSH)
aps add cursor_rules git@github.com:org/private-rules.git

# Add a private skill with an explicit path
aps add agent_skill git@github.com:org/private-skills.git --path skills/refactor-module
```

This parses the GitHub URL, adds an entry to `aps.yaml`, and syncs **only that skill** immediately (other entries are not affected).
This parses the repository identifier, adds an entry to `aps.yaml`, and syncs **only that asset** immediately (other entries are not affected).

3. **Or manually edit the manifest** to define your assets:

Expand Down Expand Up @@ -120,7 +127,7 @@ cargo build --release
| Command | Description |
| -------------- | ------------------------------------------------- |
| `aps init` | Create a new manifest file and update .gitignore |
| `aps add` | Add a skill from a GitHub URL and sync it |
| `aps add` | Add an asset from a git repo and sync it |
| `aps sync` | Sync all entries from manifest and install assets |
| `aps validate` | Validate manifest schema and check sources |
| `aps status` | Display last sync information from lockfile |
Expand All @@ -132,8 +139,10 @@ cargo build --release

### Add Options

- `--id <name>` - Custom entry ID (defaults to skill folder name)
- `--kind <type>` - Asset kind: `agent-skill`, `cursor-rules`, `cursor-skills-root`, `agents-md` (default: `agent-skill`)
- `aps add <asset_type> <repo_url_or_path>` - Asset types: `agent_skill`, `cursor_rules`, `cursor_skills_root`, `agents_md`
- `--id <name>` - Custom entry ID (defaults to repo or path name)
- `--path <path>` - Path within the repository (required for `agent_skill` when using SSH/HTTPS repo URLs)
- `--ref <ref>` - Git ref (branch/tag/commit) to use
- `--no-sync` - Only add to manifest, don't sync immediately
- `--all` - Add all discovered skills without prompting (for repo-level URLs or directories)
- `--yes` / `-y` - Skip confirmation prompts
Expand Down Expand Up @@ -202,12 +211,35 @@ aps add --yes https://github.com/anthropics/skills

When you run `aps sync`:

1. **Entries are synced** - Each entry in `aps.yaml` is installed to its destination
2. **Stale entries are cleaned** - Entries in the lockfile that no longer exist in `aps.yaml` are automatically removed
3. **Lockfile is saved** - The updated lockfile is written to disk
1. **Entries are synced** - Each entry in your manifest (by default `aps.yaml`) is installed to its destination (git sources reuse cached clones for speed)
2. **Stale entries are cleaned** - Entries in the lockfile that no longer exist in the manifest are automatically removed
3. **Lockfile is saved** - The updated lockfile is written to disk, next to the manifest

Note: Stale entry cleanup only happens during a full sync. When using `--only <id>` to sync specific entries, other lockfile entries are preserved.

### Example Sync Output

When syncing a manifest with multiple entries, `aps sync` now shows per-entry progress so you can see work as it happens:

```bash
$ aps sync

(1/3) Syncing my-agents from filesystem $HOME/agents-md-partials (AGENTS.md)...
(1/3) Finished my-agents in 0.12s [synced]
(2/3) Syncing composite-agents from 3 composite source(s)...
(2/3) Finished composite-agents in 0.45s [copied]
(3/3) Syncing company-rules from git repo git@github.com:your-username/dotfiles.git...
(3/3) Finished company-rules in 1.23s [synced]

Syncing from aps.yaml

✓ my-agents → ./AGENTS.md [synced]
· composite-agents → ./AGENTS.md [current]
✓ company-rules → ./.cursor/rules/ [synced]

2 synced, 1 current
```

## Configuration

### Manifest File (`aps.yaml`)
Expand Down Expand Up @@ -341,9 +373,19 @@ Key features:
- **Order preserved**: Files are merged in the order specified in `sources`
- **Auto-generated header**: Output includes a comment indicating it was composed by aps

### Lockfile (`aps.lock.yaml`)
### Lockfile (manifest-based)

The lockfile tracks installed assets and is automatically created/updated by `aps sync`. **This file should be committed to version control** to ensure reproducible installations across your team.

The lockfile tracks installed assets and is automatically created/updated by `aps sync`. **This file should be committed to version control** to ensure reproducible installations across your team. It stores:
The lockfile name is derived from the manifest filename:

- `aps.yaml` → `aps.lock.yaml`
- `aps-rules.yaml` → `aps-rules.lock.yaml`
- `custom` → `custom.lock.yaml`

This means you can keep multiple manifests in the same repository, each with its own lockfile, without conflicts.

Each lockfile stores:

- APS version that generated/modified the lockfile
- Source information
Expand All @@ -353,6 +395,8 @@ The lockfile tracks installed assets and is automatically created/updated by `ap

**Environment Variables Are Preserved**: Unlike other package managers (npm, uv, bundler) that expand environment variables to concrete paths, `aps` preserves shell variables like `$HOME` in the lockfile. This makes lockfiles portable across different machines and users who have the same relative directory structure.

**Legacy lockfiles**: For backward compatibility, `aps` still recognizes historical lockfile names (`aps.lock.yaml`, `aps.manifest.lock`) when loading. On the next successful `aps sync`, these legacy files are migrated to the manifest-based name and the old files are cleaned up.

## Examples

### Non-interactive sync for CI/CD
Expand Down
24 changes: 12 additions & 12 deletions aps.lock.yaml
Original file line number Diff line number Diff line change
@@ -1,28 +1,28 @@
version: 1
aps_version: 0.1.11
entries:
skill-creator:
source: https://github.com/anthropics/skills.git
dest: .claude/skills/skill-creator/
resolved_ref: main
commit: 1ed29a03dc852d30fa6ef2ca53a67dc2c2c2c563
checksum: sha256:bdd5389ea8390277b8310b83eaa561a703c2fa08bbcf770e6394d42571f7445f
anthropic-skills:
source: https://github.com/anthropics/skills.git
dest: ./.claude/skills/
resolved_ref: main
commit: 1ed29a03dc852d30fa6ef2ca53a67dc2c2c2c563
checksum: sha256:3bd37a1929706db495f2609910316bb1a385a057556deb151f968e877aef39e9
composite-agents-md:
source:
composite:
- https://github.com/westonplatter/agentically.git:agents-md-partials/AGENTS.rust.md
- https://github.com/westonplatter/agentically.git:agents-md-partials/AGENTS.pull-requests.md
dest: ./AGENTS.md
checksum: sha256:11eedede54fa5217e041f613adc9f692b985972e8487261e720fc553d707bdbf
anthropic-skills:
source: https://github.com/anthropics/skills.git
dest: ./.claude/skills/
resolved_ref: main
commit: 1ed29a03dc852d30fa6ef2ca53a67dc2c2c2c563
checksum: sha256:3bd37a1929706db495f2609910316bb1a385a057556deb151f968e877aef39e9
internal-comms:
source: https://github.com/anthropics/skills.git
dest: .claude/skills/internal-comms/
resolved_ref: main
commit: 1ed29a03dc852d30fa6ef2ca53a67dc2c2c2c563
checksum: sha256:b353d2d5223c9b5154f52a1d1b87c65bd0f2ed9eac5e541e88e72b4c4a614743
skill-creator:
source: https://github.com/anthropics/skills.git
dest: .claude/skills/skill-creator/
resolved_ref: main
commit: 1ed29a03dc852d30fa6ef2ca53a67dc2c2c2c563
checksum: sha256:bdd5389ea8390277b8310b83eaa561a703c2fa08bbcf770e6394d42571f7445f
6 changes: 6 additions & 0 deletions docs/architecture.md
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,12 @@ pub struct GitSource {
4. If mismatch → clone and install
```

**Per-run clone reuse:**

- During `aps sync`, git clones are cached in-memory per run.
- Entries that share the same repo/ref/shallow (or repo/commit in locked mode) reuse the same temporary clone.
- The cache is dropped at the end of the run, so temp directories are cleaned up as usual.

### Enum-to-Adapter Bridge

The manifest uses a `Source` enum for YAML serialization, which bridges to the trait implementations:
Expand Down
37 changes: 13 additions & 24 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -52,20 +52,22 @@ pub struct InitArgs {

#[derive(Parser, Debug)]
pub struct AddArgs {
/// GitHub URL or local filesystem path to a skill folder or repository.
/// Supports: GitHub URLs (https://github.com/owner/repo/...) and local
/// paths ($HOME/skills, ~/skills, ./skills). For repo-level URLs or
/// directories without SKILL.md, discovers skills and prompts for selection.
#[arg(value_name = "URL_OR_PATH")]
pub url: String,

/// Custom entry ID (defaults to skill folder name)
/// Optional asset type (agent_skill, cursor_rules, cursor_skills_root, agents_md).
/// If omitted, defaults to agent_skill. When given, must be followed by URL or path.
#[arg(value_name = "ASSET_TYPE_OR_URL", num_args = 1..=2, required = true)]
pub positionals: Vec<String>,

/// Custom entry ID (defaults to repo or path name)
#[arg(long)]
pub id: Option<String>,

/// Asset kind (defaults to agent_skill)
#[arg(long, value_enum, default_value = "agent-skill")]
pub kind: AddAssetKind,
/// Path within the repository (overrides any path from a GitHub URL)
#[arg(long)]
pub path: Option<String>,

/// Git ref (branch/tag/commit) to use (defaults to auto for repo URLs)
#[arg(long = "ref")]
pub git_ref: Option<String>,

/// Path to the manifest file
#[arg(long)]
Expand All @@ -84,19 +86,6 @@ pub struct AddArgs {
pub yes: bool,
}

#[derive(ValueEnum, Clone, Debug, Default)]
pub enum AddAssetKind {
#[default]
#[value(name = "agent-skill")]
AgentSkill,
#[value(name = "cursor-rules")]
CursorRules,
#[value(name = "cursor-skills-root")]
CursorSkillsRoot,
#[value(name = "agents-md")]
AgentsMd,
}

#[derive(ValueEnum, Clone, Debug, Default)]
pub enum ManifestFormat {
#[default]
Expand Down
Loading