Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9332e4cffa | ||
|
|
c53b23b6fd |
No files matched your search
@@ -0,0 +1,70 @@
|
|||||||
|
name: Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
release:
|
||||||
|
types: [published]
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
build:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
include:
|
||||||
|
- target: x86_64-unknown-linux-gnu
|
||||||
|
binary: ccsm
|
||||||
|
archive: ccsm-linux-amd64.tar.gz
|
||||||
|
- target: x86_64-apple-darwin
|
||||||
|
binary: ccsm
|
||||||
|
archive: ccsm-darwin-amd64.tar.gz
|
||||||
|
- target: x86_64-pc-windows-gnu
|
||||||
|
binary: ccsm.exe
|
||||||
|
archive: ccsm-windows-amd64.zip
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Install Rust
|
||||||
|
run: |
|
||||||
|
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y --default-toolchain stable
|
||||||
|
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Install Rust targets
|
||||||
|
run: |
|
||||||
|
rustup target add ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Build (Linux)
|
||||||
|
if: matrix.target == 'x86_64-unknown-linux-gnu'
|
||||||
|
run: cargo build --release --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Install Zig
|
||||||
|
if: matrix.target != 'x86_64-unknown-linux-gnu'
|
||||||
|
run: |
|
||||||
|
curl -L -o zig.tar.xz https://ziglang.org/download/0.13.0/zig-linux-x86_64-0.13.0.tar.xz
|
||||||
|
tar xf zig.tar.xz
|
||||||
|
echo "$PWD/zig-linux-x86_64-0.13.0" >> "$GITHUB_PATH"
|
||||||
|
|
||||||
|
- name: Install cargo-zigbuild
|
||||||
|
if: matrix.target != 'x86_64-unknown-linux-gnu'
|
||||||
|
run: cargo install cargo-zigbuild
|
||||||
|
|
||||||
|
- name: Build with zigbuild
|
||||||
|
if: matrix.target != 'x86_64-unknown-linux-gnu'
|
||||||
|
run: cargo zigbuild --release --target ${{ matrix.target }}
|
||||||
|
|
||||||
|
- name: Package
|
||||||
|
run: |
|
||||||
|
cd "target/${{ matrix.target }}/release"
|
||||||
|
if [[ "${{ matrix.target }}" == *-windows-* ]]; then
|
||||||
|
zip "${{ matrix.archive }}" "${{ matrix.binary }}"
|
||||||
|
else
|
||||||
|
tar czf "${{ matrix.archive }}" "${{ matrix.binary }}"
|
||||||
|
fi
|
||||||
|
|
||||||
|
- name: Upload to release
|
||||||
|
run: |
|
||||||
|
curl --fail -X POST \
|
||||||
|
-H "Authorization: Bearer ${{ secrets.GITEA_TOKEN }}" \
|
||||||
|
-H "Content-Type: application/octet-stream" \
|
||||||
|
--data-binary @"target/${{ matrix.target }}/release/${{ matrix.archive }}" \
|
||||||
|
"${{ github.server_url }}/api/v1/repos/${{ github.repository }}/releases/${{ github.event.release.id }}/assets?name=${{ matrix.archive }}"
|
||||||
@@ -1,64 +0,0 @@
|
|||||||
name: Build
|
|
||||||
|
|
||||||
on:
|
|
||||||
release:
|
|
||||||
types: [published]
|
|
||||||
workflow_dispatch: # manual: build + package only (no version override, no upload)
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
contents: write # so GITHUB_TOKEN can upload assets to the release
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build:
|
|
||||||
name: ${{ matrix.archive }}
|
|
||||||
runs-on: ${{ matrix.os }}
|
|
||||||
strategy:
|
|
||||||
fail-fast: false
|
|
||||||
matrix:
|
|
||||||
include:
|
|
||||||
- os: ubuntu-latest
|
|
||||||
target: x86_64-unknown-linux-gnu
|
|
||||||
binary: ccsm
|
|
||||||
archive: ccsm-linux-amd64.tar.gz
|
|
||||||
- os: macos-latest
|
|
||||||
target: x86_64-apple-darwin
|
|
||||||
binary: ccsm
|
|
||||||
archive: ccsm-darwin-amd64.tar.gz
|
|
||||||
- os: windows-latest
|
|
||||||
target: x86_64-pc-windows-msvc
|
|
||||||
binary: ccsm.exe
|
|
||||||
archive: ccsm-windows-amd64.zip
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Checkout
|
|
||||||
uses: actions/checkout@v7
|
|
||||||
|
|
||||||
- name: Set version from release tag
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
run: |
|
|
||||||
VERSION="${GITHUB_REF_NAME#v}"
|
|
||||||
echo "CCSM_VERSION=$VERSION" >> "$GITHUB_ENV"
|
|
||||||
|
|
||||||
- name: Install Rust toolchain (+ target)
|
|
||||||
uses: dtolnay/rust-toolchain@stable
|
|
||||||
with:
|
|
||||||
targets: ${{ matrix.target }}
|
|
||||||
|
|
||||||
- name: Build release binary
|
|
||||||
run: cargo build --release --target ${{ matrix.target }}
|
|
||||||
|
|
||||||
- name: Package archive
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
cd "target/${{ matrix.target }}/release"
|
|
||||||
if [[ "${{ runner.os }}" == 'Windows' ]]; then
|
|
||||||
powershell -NoProfile -Command "Compress-Archive -Path '${{ matrix.binary }}' -DestinationPath '${{ matrix.archive }}' -Force"
|
|
||||||
else
|
|
||||||
tar czf "${{ matrix.archive }}" "${{ matrix.binary }}"
|
|
||||||
fi
|
|
||||||
|
|
||||||
- name: Upload to GitHub release
|
|
||||||
if: github.event_name == 'release'
|
|
||||||
env:
|
|
||||||
GH_TOKEN: ${{ github.token }}
|
|
||||||
run: gh release upload "${{ github.event.release.tag_name }}" "target/${{ matrix.target }}/release/${{ matrix.archive }}" --clobber
|
|
||||||
+1
-134
@@ -1,134 +1 @@
|
|||||||
### Linux
|
/target
|
||||||
|
|
||||||
*~
|
|
||||||
|
|
||||||
# temporary files which can be created if a process still has a handle open of a deleted file
|
|
||||||
.fuse_hidden*
|
|
||||||
|
|
||||||
# Metadata left by Dolphin file manager, which comes with KDE Plasma
|
|
||||||
.directory
|
|
||||||
|
|
||||||
# Linux trash folder which might appear on any partition or disk
|
|
||||||
.Trash-*
|
|
||||||
|
|
||||||
# .nfs files are created when an open file is removed but is still being accessed
|
|
||||||
.nfs*
|
|
||||||
|
|
||||||
# Log files created by default by the nohup command
|
|
||||||
nohup.out
|
|
||||||
|
|
||||||
### macOS
|
|
||||||
|
|
||||||
# General
|
|
||||||
.DS_Store
|
|
||||||
.localized
|
|
||||||
__MACOSX/
|
|
||||||
.AppleDouble
|
|
||||||
.LSOverride
|
|
||||||
Icon[
|
|
||||||
]
|
|
||||||
|
|
||||||
# Resource forks
|
|
||||||
._*
|
|
||||||
|
|
||||||
# Files and directories that might appear in the root of a volume
|
|
||||||
.DocumentRevisions-V100
|
|
||||||
.fseventsd
|
|
||||||
.Spotlight-V100
|
|
||||||
.TemporaryItems
|
|
||||||
.Trashes
|
|
||||||
.VolumeIcon.icns
|
|
||||||
.com.apple.timemachine.donotpresent
|
|
||||||
.com.apple.timemachine.supported
|
|
||||||
.PKInstallSandboxManager
|
|
||||||
.PKInstallSandboxManager-SystemSoftware
|
|
||||||
.hotfiles.btree
|
|
||||||
.vol
|
|
||||||
.file
|
|
||||||
.disk_label*
|
|
||||||
lost+found
|
|
||||||
.HFS+ Private Directory Data[
|
|
||||||
]
|
|
||||||
|
|
||||||
# Directories potentially created on remote AFP share
|
|
||||||
.AppleDB
|
|
||||||
.AppleDesktop
|
|
||||||
Network Trash Folder
|
|
||||||
Temporary Items
|
|
||||||
.apdisk
|
|
||||||
|
|
||||||
# Mac OS 6 to 9
|
|
||||||
Desktop DB
|
|
||||||
Desktop DF
|
|
||||||
TheFindByContentFolder
|
|
||||||
TheVolumeSettingsFolder
|
|
||||||
.FBCIndex
|
|
||||||
.FBCSemaphoreFile
|
|
||||||
.FBCLockFolder
|
|
||||||
|
|
||||||
# Quota system
|
|
||||||
.quota.group
|
|
||||||
.quota.user
|
|
||||||
.quota.ops.group
|
|
||||||
.quota.ops.user
|
|
||||||
|
|
||||||
# TimeMachine
|
|
||||||
Backups.backupdb
|
|
||||||
.MobileBackups
|
|
||||||
.MobileBackups.trash
|
|
||||||
MobileBackups.trash
|
|
||||||
tmbootpicker.efi
|
|
||||||
|
|
||||||
### Windows
|
|
||||||
|
|
||||||
# Windows thumbnail cache files
|
|
||||||
Thumbs.db
|
|
||||||
Thumbs.db:encryptable
|
|
||||||
ehthumbs.db
|
|
||||||
ehthumbs_vista.db
|
|
||||||
|
|
||||||
# Dump file
|
|
||||||
*.stackdump
|
|
||||||
|
|
||||||
# Folder config file
|
|
||||||
[Dd]esktop.ini
|
|
||||||
|
|
||||||
# Recycle Bin used on file shares
|
|
||||||
$RECYCLE.BIN/
|
|
||||||
|
|
||||||
# Windows Installer files
|
|
||||||
*.cab
|
|
||||||
*.msi
|
|
||||||
*.msix
|
|
||||||
*.msm
|
|
||||||
*.msp
|
|
||||||
|
|
||||||
# Windows shortcuts
|
|
||||||
*.lnk
|
|
||||||
|
|
||||||
### Rust
|
|
||||||
|
|
||||||
# Generated by Cargo
|
|
||||||
# will have compiled files and executables
|
|
||||||
debug
|
|
||||||
target
|
|
||||||
|
|
||||||
# These are backup files generated by rustfmt
|
|
||||||
**/*.rs.bk
|
|
||||||
|
|
||||||
# MSVC Windows builds of rustc generate these, which store debugging information
|
|
||||||
*.pdb
|
|
||||||
|
|
||||||
# Generated by cargo mutants
|
|
||||||
# Contains mutation testing data
|
|
||||||
**/mutants.out*/
|
|
||||||
|
|
||||||
# rustc will dump stack traces when hitting an internal compiler error to PWD
|
|
||||||
rustc-ice-*.txt
|
|
||||||
|
|
||||||
# RustRover
|
|
||||||
# JetBrains specific template is maintained in a separate JetBrains.gitignore that can
|
|
||||||
# be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
|
|
||||||
# and can be added to the global gitignore or merged into this file. For a more nuclear
|
|
||||||
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
|
|
||||||
.idea/
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
# Changelog
|
|
||||||
|
|
||||||
## v0.3.0
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- **Detailed `--version` output** — shows version, commit SHA, project name, and attribution (`by OnixByte`); version can be injected at build time via `CCSM_VERSION` env var
|
|
||||||
- **Marketplace creation guide** — README now documents how to create and publish a skill marketplace, including the `skills.json` format, skill entry fields, monorepo `path` support, and skill repository structure
|
|
||||||
|
|
||||||
## v0.2.0
|
|
||||||
|
|
||||||
### Added
|
|
||||||
|
|
||||||
- **Monorepo skill support** — skills can now live in subdirectories of a single repository via the `path` field in `skills.json`
|
|
||||||
- **Release-triggered CI** — workflow runs on `release: published` and uploads built artifacts directly to the release
|
|
||||||
- **Project documentation** — README, CONTRIBUTING, and LICENCE (MIT) files
|
|
||||||
|
|
||||||
### Fixed
|
|
||||||
|
|
||||||
- CI artifact upload compatibility with self-hosted Gitea (downgrade `upload-artifact` to v3)
|
|
||||||
- Cross-compilation for macOS and Windows targets via `cargo-zigbuild`
|
|
||||||
|
|
||||||
## v0.1.0
|
|
||||||
|
|
||||||
Initial release of Claude Code Skill Manager (CCSM) — a CLI tool for discovering, installing, and managing Claude Code skills from Git-based marketplace repositories.
|
|
||||||
|
|
||||||
### Features
|
|
||||||
|
|
||||||
- **Marketplace management** — register, list, update, and remove skill marketplaces from any Git repository exposing a `skills.json` manifest
|
|
||||||
- **Skill search** — browse and search available skills by name or description across all registered marketplaces
|
|
||||||
- **Skill installation** — download and deploy skills to `~/.claude/skills/` with copy or symlink modes
|
|
||||||
- **Skill lifecycle** — update installed skills to latest versions, remove unwanted skills, and inspect skill details
|
|
||||||
- **Cross-platform** — pre-built binaries for Linux, macOS, and Windows (amd64)
|
|
||||||
|
|
||||||
### Commands
|
|
||||||
|
|
||||||
| Command | Description |
|
|
||||||
|---|---|
|
|
||||||
| `ccsm marketplace add <url>` | Register a skill marketplace |
|
|
||||||
| `ccsm marketplace remove <name>` | Unregister a marketplace |
|
|
||||||
| `ccsm marketplace list` | List registered marketplaces |
|
|
||||||
| `ccsm marketplace update [name]` | Refresh marketplace manifests |
|
|
||||||
| `ccsm search [query]` | Search available skills |
|
|
||||||
| `ccsm install <name>` | Install a skill |
|
|
||||||
| `ccsm list` | Show installed skills |
|
|
||||||
| `ccsm info <name>` | Show skill details |
|
|
||||||
| `ccsm update [name]` | Update installed skills |
|
|
||||||
| `ccsm remove <name>` | Uninstall a skill |
|
|
||||||
|
|
||||||
### Installation
|
|
||||||
|
|
||||||
```bash
|
|
||||||
cargo install --path .
|
|
||||||
```
|
|
||||||
|
|
||||||
Or download a pre-built binary from the [Releases](https://onixbyte.dev/onixbyte/claude-code-skill-manager/releases) page.
|
|
||||||
Generated
+1
-1
@@ -218,7 +218,7 @@ checksum = "c8d4a3bb8b1e0c1050499d1815f5ab16d04f0959b233085fb31653fbfc9d98f9"
|
|||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "claude-code-skill-manager"
|
name = "claude-code-skill-manager"
|
||||||
version = "0.3.0"
|
version = "0.1.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"clap",
|
"clap",
|
||||||
"dirs",
|
"dirs",
|
||||||
|
|||||||
+1
-1
@@ -1,6 +1,6 @@
|
|||||||
[package]
|
[package]
|
||||||
name = "claude-code-skill-manager"
|
name = "claude-code-skill-manager"
|
||||||
version = "0.3.0"
|
version = "0.1.0"
|
||||||
edition = "2021"
|
edition = "2021"
|
||||||
|
|
||||||
[[bin]]
|
[[bin]]
|
||||||
|
|||||||
@@ -7,14 +7,14 @@ A CLI tool for discovering, installing, and managing [Claude Code](https://claud
|
|||||||
### From source
|
### From source
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
git clone https://github.com/onixbyte/claude-code-skill-manager.git
|
git clone https://onixbyte.dev/onixbyte/claude-code-skill-manager.git
|
||||||
cd claude-code-skill-manager
|
cd claude-code-skill-manager
|
||||||
cargo install --path .
|
cargo install --path .
|
||||||
```
|
```
|
||||||
|
|
||||||
### Pre-built binaries
|
### Pre-built binaries
|
||||||
|
|
||||||
Download the latest binary for your platform from the [Releases](https://github.com/onixbyte/claude-code-skill-manager/releases) page.
|
Download the latest binary for your platform from the [Releases](https://onixbyte.dev/onixbyte/claude-code-skill-manager/releases) page.
|
||||||
|
|
||||||
## Quick start
|
## Quick start
|
||||||
|
|
||||||
@@ -81,80 +81,6 @@ Skills are directories placed under `~/.claude/skills/` that Claude Code reads a
|
|||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
## Creating a skill marketplace
|
|
||||||
|
|
||||||
A marketplace is simply a public Git repository that contains a `skills.json` manifest at its root. CCSM reads this file to discover what skills are available.
|
|
||||||
|
|
||||||
### Repository structure
|
|
||||||
|
|
||||||
```
|
|
||||||
your-marketplace-repo/
|
|
||||||
skills.json ← manifest (required)
|
|
||||||
README.md ← optional landing page
|
|
||||||
```
|
|
||||||
|
|
||||||
### skills.json format
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"name": "My Marketplace",
|
|
||||||
"version": "1.0.0",
|
|
||||||
"skills": [
|
|
||||||
{
|
|
||||||
"name": "my-skill",
|
|
||||||
"description": "A short description shown in search results",
|
|
||||||
"repository": "https://github.com/user/skill-repo",
|
|
||||||
"branch": "main"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"name": "another-skill",
|
|
||||||
"description": "A skill that lives in a monorepo subdirectory",
|
|
||||||
"repository": "https://github.com/user/monorepo",
|
|
||||||
"branch": "main",
|
|
||||||
"path": "skills/another-skill"
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
| Field | Required | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `name` | Yes | Marketplace display name |
|
|
||||||
| `version` | No | Arbitrary version string for your own tracking |
|
|
||||||
| `skills` | No | Array of skill entries (defaults to `[]`) |
|
|
||||||
|
|
||||||
**Skill entry fields:**
|
|
||||||
|
|
||||||
| Field | Required | Description |
|
|
||||||
|---|---|---|
|
|
||||||
| `name` | Yes | Unique skill identifier (used with `ccsm install <name>`) |
|
|
||||||
| `description` | Yes | One-line summary shown in `ccsm search` output |
|
|
||||||
| `repository` | Yes | Public Git repository URL for the skill |
|
|
||||||
| `branch` | No | Git branch to install from (defaults to `main` or `master`, auto-detected by the marketplace host) |
|
|
||||||
| `path` | No | Subdirectory within the repository where the skill files live — use for monorepos hosting multiple skills |
|
|
||||||
|
|
||||||
### What goes in a skill repository
|
|
||||||
|
|
||||||
Each skill repository (the one referenced by `repository`) is a standard Claude Code skill directory. At minimum it should contain one of:
|
|
||||||
|
|
||||||
- `SKILL.md` — the skill manifest (preferred)
|
|
||||||
- `CLAUDE.md` — alternative manifest filename
|
|
||||||
- Supporting files referenced by the manifest (scripts, templates, etc.)
|
|
||||||
|
|
||||||
See the [Claude Code skills documentation](https://docs.anthropic.com/en/docs/claude-code/skills) for details on writing skill manifests.
|
|
||||||
|
|
||||||
### Publishing your marketplace
|
|
||||||
|
|
||||||
1. Create a public Git repository with a `skills.json` following the format above.
|
|
||||||
2. Push it — make sure `skills.json` is at the repository root.
|
|
||||||
3. Anyone can register it with:
|
|
||||||
|
|
||||||
```bash
|
|
||||||
ccsm marketplace add https://github.com/your-username/your-marketplace
|
|
||||||
```
|
|
||||||
|
|
||||||
CCSM fetches `skills.json` from the raw `main` or `master` branch automatically, so both of those branch names work out of the box.
|
|
||||||
|
|
||||||
## Deploy modes
|
## Deploy modes
|
||||||
|
|
||||||
- **Copy** (default): Skill files are physically copied to `~/.claude/skills/<name>/`.
|
- **Copy** (default): Skill files are physically copied to `~/.claude/skills/<name>/`.
|
||||||
@@ -166,7 +92,7 @@ CCSM fetches `skills.json` from the raw `main` or `master` branch automatically,
|
|||||||
cargo build --release
|
cargo build --release
|
||||||
```
|
```
|
||||||
|
|
||||||
Release binaries for Linux, macOS, and Windows (amd64) are built natively per platform by GitHub Actions and attached to each GitHub Release.
|
Cross-compilation targets: Linux (amd64), macOS (amd64), and Windows (amd64) via `cargo-zigbuild`.
|
||||||
|
|
||||||
## Contributing
|
## Contributing
|
||||||
|
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
use std::process::Command;
|
|
||||||
|
|
||||||
fn main() {
|
|
||||||
let version = std::env::var("CCSM_VERSION")
|
|
||||||
.unwrap_or_else(|_| env!("CARGO_PKG_VERSION").to_string());
|
|
||||||
|
|
||||||
let commit = Command::new("git")
|
|
||||||
.args(["rev-parse", "--short", "HEAD"])
|
|
||||||
.output()
|
|
||||||
.ok()
|
|
||||||
.and_then(|o| String::from_utf8(o.stdout).ok())
|
|
||||||
.map(|s| s.trim().to_string())
|
|
||||||
.unwrap_or_else(|| "unknown".to_string());
|
|
||||||
|
|
||||||
println!("cargo:rustc-env=CCSM_VERSION={version}");
|
|
||||||
println!("cargo:rustc-env=GIT_COMMIT_SHORT={commit}");
|
|
||||||
println!("cargo:rerun-if-changed=.git/HEAD");
|
|
||||||
println!("cargo:rerun-if-env-changed=CCSM_VERSION");
|
|
||||||
}
|
|
||||||
+1
-9
@@ -1,18 +1,10 @@
|
|||||||
use clap::{Args, Parser, Subcommand};
|
use clap::{Args, Parser, Subcommand};
|
||||||
|
|
||||||
const VERSION_TEXT: &str = concat!(
|
|
||||||
env!("CCSM_VERSION"),
|
|
||||||
" -- Claude Code Skill Manager",
|
|
||||||
" -- ",
|
|
||||||
env!("GIT_COMMIT_SHORT"),
|
|
||||||
"\nby OnixByte"
|
|
||||||
);
|
|
||||||
|
|
||||||
#[derive(Parser)]
|
#[derive(Parser)]
|
||||||
#[command(
|
#[command(
|
||||||
name = "ccsm",
|
name = "ccsm",
|
||||||
about = "Claude Code Skill Marketplace — discover and install skills from Git marketplaces",
|
about = "Claude Code Skill Marketplace — discover and install skills from Git marketplaces",
|
||||||
version = VERSION_TEXT,
|
version
|
||||||
)]
|
)]
|
||||||
pub struct Cli {
|
pub struct Cli {
|
||||||
#[command(subcommand)]
|
#[command(subcommand)]
|
||||||
|
|||||||
Reference in new issue
Block a user