Skip to content
Draft
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
27 changes: 25 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
@@ -1,21 +1,44 @@
{
"name": "helixlab",
"description": "A growing collection of developer tools and skills for AI-powered coding workflows",
"description": "AI agent tooling marketplace. Plugins, skills, hooks, agents, and MCP servers for Claude Code, Claude Desktop, claude.ai, and Codex CLI.",
"owner": {
"name": "Robert Kent Jr.",
"url": "https://helixlab.app"
},
"metadata": {
"version": "1.3.0",
"description": "AI agent tooling marketplace. Plugins, skills, hooks, agents, and MCP servers for Claude Code, Claude Desktop, claude.ai, and Codex CLI."
},
"plugins": [
{
"name": "helixlab",
"description": "A growing collection of developer tools and skills for AI-powered coding workflows",
"version": "1.2.0",
"source": "./",
"author": {
"name": "Robert Kent Jr.",
"url": "https://helixlab.app"
},
"category": "development"
},
{
"name": "no-ai-slop",
"description": "Removes 20+ patterns of AI slop from any piece of writing while preserving the writer's voice",
"source": "./plugins/no-ai-slop",
"author": {
"name": "Robert Kent Jr.",
"url": "https://helixlab.app"
},
"homepage": "https://github.com/mrrobertkent/helixlab-plugin/tree/main/plugins/no-ai-slop",
"license": "MIT",
"keywords": [
"writing",
"editing",
"voice",
"ai-slop",
"prose",
"ai-skills"
],
"category": "productivity"
}
]
}
13 changes: 11 additions & 2 deletions .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,13 +1,22 @@
{
"$schema": "https://json.schemastore.org/claude-code-plugin-manifest.json",
"name": "helixlab",
"description": "A growing collection of developer tools and skills for AI-powered coding workflows",
"version": "1.2.0",
"version": "1.2.1",
"author": {
"name": "Robert Kent Jr.",
"url": "https://helixlab.app"
},
"homepage": "https://github.com/mrrobertkent/helixlab-plugin",
"repository": "https://github.com/mrrobertkent/helixlab-plugin",
"license": "MIT",
"keywords": ["developer-tools", "video", "vision", "frames", "analysis", "ffmpeg", "ai-skills"]
"keywords": [
"developer-tools",
"video",
"vision",
"frames",
"analysis",
"ffmpeg",
"ai-skills"
]
}
93 changes: 12 additions & 81 deletions .github/workflows/validate-plugin.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,90 +12,21 @@ jobs:
steps:
- uses: actions/checkout@v4

- name: Validate JSON syntax
run: |
echo "=== Validating plugin.json ==="
python3 -c "import json; json.load(open('.claude-plugin/plugin.json'))"
echo " PASS: plugin.json is valid JSON"

echo ""
echo "=== Validating marketplace.json ==="
python3 -c "import json; json.load(open('.claude-plugin/marketplace.json'))"
echo " PASS: marketplace.json is valid JSON"
- name: Install Claude Code
run: curl -fsSL https://claude.ai/install.sh | bash

- name: Check required fields
- name: Validate marketplace and every plugin
run: |
echo "=== Checking plugin.json required fields ==="
python3 -c "
import json, sys
data = json.load(open('.claude-plugin/plugin.json'))
errors = []
if 'name' not in data:
errors.append('Missing required field: name')
if 'version' not in data:
errors.append('Missing required field: version')
if errors:
for e in errors:
print(f' FAIL: {e}')
sys.exit(1)
print(f' PASS: name={data[\"name\"]}, version={data[\"version\"]}')
"

export PATH="$HOME/.local/bin:$PATH"
echo "=== marketplace ==="
claude plugin validate . --strict
echo ""
echo "=== Checking marketplace.json required fields ==="
python3 -c "
import json, sys
data = json.load(open('.claude-plugin/marketplace.json'))
errors = []
if 'name' not in data:
errors.append('Missing required field: name')
if 'plugins' not in data or len(data['plugins']) == 0:
errors.append('Missing or empty plugins array')
if errors:
for e in errors:
print(f' FAIL: {e}')
sys.exit(1)
print(f' PASS: marketplace={data[\"name\"]}, plugins={len(data[\"plugins\"])}')
"

- name: Check version sync
run: |
echo "=== Checking version sync between plugin.json and marketplace.json ==="
python3 -c "
import json, sys
plugin = json.load(open('.claude-plugin/plugin.json'))
marketplace = json.load(open('.claude-plugin/marketplace.json'))

plugin_version = plugin.get('version')
marketplace_version = None
for p in marketplace.get('plugins', []):
if p.get('name') == plugin.get('name'):
marketplace_version = p.get('version')
break

if not plugin_version:
print(' FAIL: No version in plugin.json')
sys.exit(1)
if not marketplace_version:
print(' FAIL: No version in marketplace.json for plugin')
sys.exit(1)
if plugin_version != marketplace_version:
print(f' FAIL: Version mismatch — plugin.json={plugin_version}, marketplace.json={marketplace_version}')
sys.exit(1)
print(f' PASS: Versions in sync at {plugin_version}')
"

- name: Validate semver format
run: |
echo "=== Checking semver format ==="
python3 -c "
import json, re, sys
version = json.load(open('.claude-plugin/plugin.json')).get('version', '')
if not re.match(r'^\d+\.\d+\.\d+(-[a-zA-Z0-9.]+)?$', version):
print(f' FAIL: \"{version}\" is not valid semver (expected MAJOR.MINOR.PATCH)')
sys.exit(1)
print(f' PASS: {version} is valid semver')
"
for manifest in plugins/*/.claude-plugin/plugin.json; do
[ -e "$manifest" ] || continue
plugin_dir=$(dirname "$(dirname "$manifest")")
echo "=== $plugin_dir ==="
claude plugin validate "$plugin_dir" --strict
done

test-scripts:
runs-on: ubuntu-latest
Expand Down
15 changes: 15 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,18 @@ Print the saved file path FIRST, then call AskUserQuestion using **Template 1**
**Step 6: Post-analysis cleanup (MANDATORY — wait for response)**

Call AskUserQuestion using **Template 4** from the same file. Only delete if user chooses "Delete recording".

---

## No AI Slop (separate plugin)

The HelixLab marketplace also ships a `no-ai-slop` plugin at `plugins/no-ai-slop/`. It needs no scripts and no dependencies.

To use it with a non-Claude agent, point your agent at `plugins/no-ai-slop/skills/no-ai-slop/SKILL.md` and follow it directly:

- **Edit (default):** return the edited draft plus a **What changed** section
- **Detect:** name each slop pattern with a quoted line and a short fix, without rewriting

Check the result against `plugins/no-ai-slop/skills/no-ai-slop/eval.md` before returning it, and fix any failed check.

The skill is by Peter Yang (https://github.com/petergyang/no-ai-slop), MIT licensed and included unmodified.
75 changes: 68 additions & 7 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ All scripts must:
- Start with `#!/bin/bash` and `set -euo pipefail`
- Print usage to stderr and exit 1 when called with no or invalid arguments
- Be executable (`chmod +x`)
- Accept any ffmpeg-supported video format — never filter by file extension
- Accept any ffmpeg-supported video format. Never filter by file extension
- Use `/tmp/claude-video-frames/<timestamp>/` for frame output directories
- Include a header comment with script name, purpose, and usage

Expand All @@ -81,9 +81,9 @@ All scripts must:
2. Add any scripts to `skills/<skill-name>/scripts/`
3. Add workflow guides to `skills/<skill-name>/workflows/` if applicable
4. Update these files with your skill's instructions:
- `AGENTS.md` — used by Cursor, Codex, Kiro, and most agents
- `GEMINI.md` — used by Google Gemini
- `.cursor/rules/` — add a `.mdc` file for automatic Cursor context injection
- `AGENTS.md` for Cursor, Codex, Kiro, and most agents
- `GEMINI.md` for Google Gemini
- `.cursor/rules/`, add a `.mdc` file for automatic Cursor context injection
5. Add your skill to the table in `skills/help/SKILL.md`
6. Add tests to `tests/`
7. Update `README.md` with a section under "Available Skills"
Expand All @@ -99,21 +99,82 @@ skills/<skill-name>/
examples/ # Example output reports (if applicable)
```

### Adding a New Plugin

This repository is a marketplace. The root directory is the `helixlab` plugin; every other plugin lives in its own folder under `plugins/`.

1. Create the plugin folder:
```
plugins/<plugin-name>/
.claude-plugin/plugin.json # name, version, description, author, license
skills/<skill-name>/SKILL.md
README.md # what it does, install, usage, versioning
LICENSE # if the plugin carries a different copyright
```
2. Add an entry to `.claude-plugin/marketplace.json` with a relative source:
```json
{
"name": "<plugin-name>",
"description": "...",
"version": "1.0.0",
"source": "./plugins/<plugin-name>",
"category": "..."
}
```
Set the version in the plugin's `plugin.json` only, never in the marketplace entry. See [Versioning](#versioning).
3. Bump `metadata.version` in `.claude-plugin/marketplace.json` (MINOR for a new plugin)
4. Add the plugin to the **Plugins** table and the **Repository Layout** block in `README.md`
5. Add it to the marketplace table in `skills/help/SKILL.md`
6. Validate:
```bash
claude plugin validate . --strict
```

> [!IMPORTANT]
> When a plugin repackages third-party work, keep the original `LICENSE` and credit the author in the plugin `README.md`.

### Versioning

We use [Semantic Versioning](https://semver.org/) in `.claude-plugin/plugin.json`:
We use [Semantic Versioning](https://semver.org/) in each plugin's `plugin.json`:

| Change Type | Bump | Example |
|------------|------|---------|
| Bug fixes, doc tweaks | PATCH (0.0.x) | `1.0.0` → `1.0.1` |
| New skills, new scripts, new capabilities | MINOR (0.x.0) | `1.0.1` → `1.1.0` |
| Breaking changes (removed/renamed commands) | MAJOR (x.0.0) | `1.1.0` → `2.0.0` |

Maintainers handle version bumps — contributors do not need to update the version number in PRs.
A version belongs in **one** place: the plugin's own `.claude-plugin/plugin.json`.

Do not repeat it in the plugin's `marketplace.json` entry. Claude Code resolves a plugin's version from the first source that is set, in this order:

1. `version` in the plugin's `plugin.json`
2. `version` in the plugin's marketplace entry
3. The git commit SHA of the plugin's source

Because `plugin.json` always wins, a version left behind in `marketplace.json` is dead text that can silently contradict the real one. The Claude Code docs state this directly:

> Avoid setting `version` in both `plugin.json` and the marketplace entry. Claude Code always uses the `plugin.json` value without warning, so a stale manifest version can mask a version you set in `marketplace.json`.

Bumping `plugin.json` is what reaches installed users. Note that auto-update is off by default for non-Anthropic marketplaces, so users pick up a release either by enabling auto-update for the marketplace in `/plugin`, or by running `claude plugin update <plugin>`.

Maintainers handle version bumps. Contributors do not need to update the version number in PRs.

### Tagging a release

Tag each release as `{plugin-name}--v{version}`. This is the naming convention Claude Code uses to resolve plugin dependency version constraints, and it lets one marketplace repository host several plugins with independent version lines.

From the plugin directory:

```bash
claude plugin tag --dry-run # preview
claude plugin tag --push # create and push
```

The command derives the tag from the manifest, validates the plugin, and refuses on a dirty working tree or an existing tag.

## Pull Request Guidelines

- Keep PRs focused — one feature or fix per PR
- Keep PRs focused on one feature or fix per PR
- Include a clear description of what changed and why
- Link any related issues
- Ensure all tests pass before requesting review
Expand Down
Loading
Loading