Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
37 changes: 32 additions & 5 deletions .claude/agents/marketplace-package-installer.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ This agent handles package installation from marketplace registries, including s
- Copy package artifacts (commands, skills, agents, scripts)
- Set correct file permissions (especially for scripts)
- Update registry.yaml with installed agents
- Run a package's optional `install.py` hooks (`prepare`/`complete`/`cleanup`) around the copy and uninstall steps
- Pass package-specific `--set KEY=VALUE` args through to those hooks when a package's hook requires them
- Verify installation success
- Provide detailed installation feedback

Expand All @@ -43,6 +45,7 @@ Use this agent when the user wants to:
- `registry`: Specific registry to use (default: auto-detect)
- `force`: Overwrite existing files (default: false)
- `no_expand`: Skip token expansion (default: false)
- `set_args`: Extra `KEY=VALUE` pairs (repeatable) forwarded to the package's `install.py` hooks as `options["args"]`, only needed when a package's hook reports a missing required value

## Outputs

Expand Down Expand Up @@ -132,11 +135,15 @@ result = install_marketplace_package(
**Installation steps:**
1. Validate package and scope
2. Create destination directories
3. Copy artifacts (commands, skills, agents, scripts)
4. Set executable permissions on scripts
5. Perform token expansion (if enabled)
6. Update ~/.claude/agents/registry.yaml
7. Verify all files copied successfully
3. If the package ships an `install.py`, run its `prepare(source_path, destination_path, options)` hook (per install target: `.claude`, and `.codex` when both are installed) — a failing `prepare()` stops the install for that target before anything is copied
4. Copy artifacts (commands, skills, agents, scripts)
5. Set executable permissions on scripts
6. Perform token expansion (if enabled)
7. Update ~/.claude/agents/registry.yaml
8. If the package ships an `install.py`, run its `complete(source_path, destination_path, options)` hook — this is where a package renders repo-specific output (e.g. via `sc-compose render`) or does its own version-to-version cleanup; a failure here is also reported and stops that target
9. Verify all files copied successfully

`options` passed to every hook: `global`, `local`, `user`, `project`, `codex`, `force`, `expand`, plus `args` (the `set_args` dict, empty unless the caller supplied `--set`). A hook's result is `{"result": "success"}` or `{"result": "fail", "message": "<reason, how to fix>"}` — surface that `message` directly to the user/caller rather than a generic failure.

### 5. Verify Installation

Expand Down Expand Up @@ -251,6 +258,26 @@ Troubleshooting:
4. Wait and retry if registry server is down
```

### install.py Hook Failure

A package's `prepare()`, `complete()`, or (on uninstall) `cleanup()` hook can
fail with `{"result": "fail", "message": "<reason, how to fix>"}`. Relay that
`message` verbatim — it's written to carry both the reason and the fix. If it
names a missing value, retry the install with the matching `--set KEY=VALUE`:

```
Error: install.py complete() failed: sc-compose render failed for
commands/sc-git-worktree.md: template requires REPO_NAME
-- suggested fix: pass --set REPO_NAME=<name> or run from inside a git repo

Troubleshooting:
1. Follow the fix instructions in the message above
2. Retry with the suggested flag, e.g.:
/marketplace install <package> --local --set REPO_NAME=my-project
3. If the hook itself raised an unhandled exception, treat it as a bug in
the package (not a user-fixable input problem) and report it
```

## Integration with CLI

This agent uses the skill integration module:
Expand Down
18 changes: 14 additions & 4 deletions .claude/skills/marketplace/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,10 +193,20 @@ When you view package details, you'll see:

1. **Validation**: Checks package exists and is accessible
2. **Dependency Check**: Verifies required dependencies
3. **File Copy**: Installs commands, skills, agents, and scripts
4. **Configuration**: Updates registry and config files
5. **Verification**: Confirms all files installed correctly
6. **Feedback**: Shows installation summary
3. **Prepare Hook**: Runs the package's `install.py` `prepare()`, if it ships one (before anything is copied)
4. **File Copy**: Installs commands, skills, agents, and scripts
5. **Configuration**: Updates registry and config files
6. **Complete Hook**: Runs the package's `install.py` `complete()`, if it ships one
7. **Verification**: Confirms all files installed correctly
8. **Feedback**: Shows installation summary

Most packages have no `install.py` and these hook steps are simply skipped.
A package that does ship one may need extra info sc-install has no generic
way to know; if a hook fails for that reason, it reports a message naming
what's missing, and you retry with the named flag, e.g.:
```bash
/marketplace install some-package --local --set TARGET_ENV=staging
```

## Registry Management

Expand Down
3 changes: 3 additions & 0 deletions .claude/skills/marketplace/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ Discover and manage Synaptic Canvas marketplace packages through natural convers
- **Scope Control**: Install globally (--global) or locally (--local)
- **Version Management**: Track installed package versions
- **Dependency Resolution**: View and manage package dependencies
- **Install-Time Hooks**: Runs a package's optional `install.py` `prepare()`/`complete()` hooks around the copy step, and `cleanup()` on uninstall, if the package ships one
- **Installation Verification**: Confirm successful installation with file checks

### Registry Management
Expand Down Expand Up @@ -165,6 +166,7 @@ Unified command interface for marketplace operations.
- `--local` - Install to ./.claude-local (project-level)
- `--force` - Overwrite existing files
- `--registry <name>` - Use specific registry
- `--set KEY=VALUE` - Repeatable; extra info for a package's optional `install.py` hooks (see Troubleshooting below)

## Examples

Expand Down Expand Up @@ -251,6 +253,7 @@ See [TROUBLESHOOTING.md](./TROUBLESHOOTING.md) for common issues and solutions.
- **Package not found**: Verify package name and registry configuration
- **Installation fails**: Check write permissions and disk space
- **Command not found**: Ensure `sc-install` is in PATH
- **`install.py` hook failed**: the error message names the reason and the fix; if it names a missing value, retry with `--set KEY=VALUE`

## Use Cases

Expand Down
57 changes: 57 additions & 0 deletions .claude/skills/marketplace/TROUBLESHOOTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -511,6 +511,63 @@ echo "cache_enabled: true" >> ~/.claude/config.yaml

---

### Issue 9: "install.py hook failed" or "missing required value"

**Symptoms:**
- Error: "install.py complete() failed: <reason> -- suggested fix: pass --set KEY=VALUE ..."
- Installation aborts after files were validated but before/after the copy step
- Works for `--global`/`--user` but fails for `--local`/`--project` (or vice versa)

**Possible Causes:**
1. The package ships an optional `install.py` (`prepare()`/`complete()`/`cleanup()`
hooks) and one of them needs information `sc-install` has no generic way to
know (e.g. a target environment name, a repo-specific value)
2. The required `--set KEY=VALUE` wasn't passed
3. The hook itself is buggy (raises an exception, or returns something other
than `{"result": "success"}` / `{"result": "fail", "message": "..."}`)

**Solutions:**

**Solution 9A: Read the Failure Message**
The hook's `message` is required to combine *why it failed* and *what to do
about it* in one string. Most of the time the fix is right there:
```
Error: install.py complete() failed: sc-compose render failed for
commands/sc-example.md: template requires TARGET_ENV -- suggested fix:
pass --set TARGET_ENV=<value>
```

**Solution 9B: Retry with `--set`**
```bash
# Add the named key/value and retry
/marketplace install some-package --local --set TARGET_ENV=staging

# --set is repeatable for hooks that need more than one value
sc-install install some-package --local --set TARGET_ENV=staging --set REGION=us-east-1
```

**Solution 9C: Uninstall Cleanup Also Runs Hooks**
`sc-install uninstall` runs the package's `cleanup()` hook (if it has one)
after removing the manifest artifacts. If uninstall reports a hook failure,
the same `--set KEY=VALUE` pattern applies:
```bash
sc-install uninstall some-package --dest ~/.claude --set TARGET_ENV=staging
```

**Solution 9D: If the Message Doesn't Name a Fix**
That's a bug in the package's `install.py`, not a missing input on your end -
every hook result must be `{"result": "success"}` or `{"result": "fail",
"message": "<reason, instructions to fix>"}`. Report it against the package
(see "Reporting Issues" below) rather than retrying blindly.

**Prevention:**
- Read the package's own README before installing if it documents required
`--set` keys
- See `src/sc_cli/README.md` in the repository for the full `install.py`
hook contract

---

## Diagnostic Commands

### Check System Health
Expand Down
57 changes: 57 additions & 0 deletions .claude/skills/marketplace/USE-CASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -509,6 +509,63 @@ Issue resolved ✓

---

## Use Case 7b: Install a Package That Requires Hook-Based Customization

**Scenario**: A package ships an optional `install.py` with `prepare()`/
`complete()`/`cleanup()` hooks, and one of them needs a value `sc-install`
has no generic way to know (for example, a target environment name).

**Goal**: Successfully install the package by supplying the missing value
via `--set KEY=VALUE`, without needing to understand the package's internals.

### Steps

1. **Attempt a normal install:**
```
/marketplace install some-package --local
```

2. **The hook fails with a message naming what's missing:**
```
Error: install.py complete() failed: sc-compose render failed for
commands/sc-example.md: template requires TARGET_ENV -- suggested fix:
pass --set TARGET_ENV=<value>
```

3. **Retry with the named value:**
```
/marketplace install some-package --local --set TARGET_ENV=staging
```

4. **`--set` is repeatable if more than one value is required:**
```
sc-install install some-package --local --set TARGET_ENV=staging --set REGION=us-east-1
```

5. **The same pattern applies to uninstall**, since `cleanup()` hooks
receive `--set` the same way:
```
sc-install uninstall some-package --dest ~/.claude --set TARGET_ENV=staging
```

### Expected Outcome

- Installation succeeds once the required value is supplied
- No need to read the package's `install.py` source to know what to pass -
the failure message names it
- Understanding that `sc-install` itself never interprets `--set` values;
only the package's own hook does

### Variations

- **Package needs no hook values**: most packages have no `install.py` at
all, or one that derives everything it needs automatically (e.g.
`sc-git-worktree` infers the repo name); these steps are simply skipped
- **Hook fails without naming a fix**: that's a bug in the package, not a
missing input - see Use Case 7 (Troubleshoot Package Issues)

---

## Advanced Use Cases

### Use Case 8: Create Custom Registry for Team Packages
Expand Down
12 changes: 12 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,18 @@ requires:
- Optionally specify version constraints
- Document installation instructions in README

### install.py (Tier 3: install-time hooks)

Not a manifest field - a package that needs more than token substitution or
runtime-dependency checks may ship `packages/<pkg>/install.py`, discovered by
convention (sc-install checks for the file's presence, nothing declares it
in manifest.yaml). It defines any of `prepare()`, `complete()`, `cleanup()`,
run around the artifact copy step and around uninstall, and can require
extra `--set KEY=VALUE` info from the caller by failing with an actionable
message when it's missing. Full contract, the result shape, and the
idempotency/cleanup requirements every `install.py` must meet: see
`src/sc_cli/README.md`.

## Version Management

### Three-Layer Versioning System
Expand Down
13 changes: 10 additions & 3 deletions docs/MARKETPLACE-INFRASTRUCTURE.md
Original file line number Diff line number Diff line change
Expand Up @@ -847,6 +847,11 @@ requires:
- python3 >= 3.10
```

Not a manifest field, but a package may also ship `install.py` at its root
(Tier 3, discovered by presence rather than declared in manifest.yaml) with
`prepare()`/`complete()`/`cleanup()` hooks run by sc-install around the copy
and uninstall steps. See `src/sc_cli/README.md` for the full contract.

### Package Installation Flow

When a user installs a package:
Expand All @@ -859,9 +864,11 @@ When a user installs a package:
b. Finds package entry
c. Fetches manifest.yaml from package path
d. Downloads all artifacts listed in manifest
e. Performs token substitution (Tier 1)
f. Validates dependencies (Tier 2)
g. Copies artifacts to ~/.claude/ or ./.claude/
e. Runs the package's install.py prepare() hook, if present (Tier 3)
f. Performs token substitution (Tier 1)
g. Validates dependencies (Tier 2)
h. Copies artifacts to ~/.claude/ or ./.claude/
i. Runs the package's install.py complete() hook, if present (Tier 3)

3. Result:
.claude/
Expand Down
8 changes: 4 additions & 4 deletions packages/sc-git-worktree/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,8 @@ Create, scan, clean up, and abort worktrees using predictable paths and safe def
- `/sc-git-worktree --abort <branch>`

Defaults
- Worktree base: `../{{REPO_NAME}}-worktrees/<branch>`
- Tracking file: `../{{REPO_NAME}}-worktrees/worktree-tracking.jsonl`
- Worktree base: `../<repo-name>-worktrees/<branch>` where `<repo-name>` is derived at runtime from `basename $(git rev-parse --show-toplevel)`.
- Tracking file: `../<repo-name>-worktrees/worktree-tracking.jsonl`

Safety
- Never delete unmerged branches without explicit approval
Expand Down Expand Up @@ -71,11 +71,11 @@ See [DESIGN.md](DESIGN.md) for detailed requirements including:
## Troubleshooting
- "Path exists": Ensure `../<repo>-worktrees/<branch>` is not present (or choose a different branch)
- "Dirty worktree": Commit or stash before cleanup/abort
- Token expansion: `{{REPO_NAME}}` is auto-detected from the repo toplevel (via git)
- `<repo-name>` is derived at runtime by the agent (`basename $(git rev-parse --show-toplevel)`), not substituted at install time — this keeps global/user installs safe (see issue #112).

## Components
- Command: `commands/sc-git-worktree.md`
- Skill: `skills/sc-managing-worktrees/SKILL.md`
- Skill: `skills/sc-git-worktree/SKILL.md`
- Agents: `sc-worktree-create`, `sc-worktree-create-stacked`, `sc-worktree-scan`, `sc-worktree-cleanup`, `sc-worktree-abort`, `sc-worktree-update`
- Stack layers: `skills/sc-git-worktree/references/stack-layers.md` (pairs with the `sc-gh-stack` package)

Expand Down
2 changes: 1 addition & 1 deletion packages/sc-git-worktree/agents/sc-git-worktree-update.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ Safely update protected branches (main, develop, master) in their worktrees by p
## Inputs (required unless noted)
- branch: protected branch name to update (optional; if omitted, update all protected branches that have worktrees)
- path: worktree path (default `<worktree_base>/<branch>`)
- worktree_base (optional): defaults to `../{{REPO_NAME}}-worktrees`
- worktree_base (optional): defaults to `../<repo-name>-worktrees` where `<repo-name>` is derived at runtime from `basename $(git rev-parse --show-toplevel)`
- protected_branches: list of protected branch names (required for validation)
- tracking_enabled: true/false (default true)
- tracking_path (optional): defaults to `<worktree_base>/worktree-tracking.jsonl` when tracking is enabled
Expand Down
Loading
Loading