diff --git a/.github/workflows/pre-commit.yml b/.github/workflows/pre-commit.yml
new file mode 100644
index 0000000..edc5e8c
--- /dev/null
+++ b/.github/workflows/pre-commit.yml
@@ -0,0 +1,35 @@
+name: Pre-commit
+
+on:
+ pull_request:
+ push:
+ branches:
+ - main
+
+jobs:
+ pre-commit:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Check out repository
+ uses: actions/checkout@v4
+
+ - name: Set up Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: "3.x"
+
+ - name: Set up Go
+ uses: actions/setup-go@v5
+ with:
+ go-version: stable
+
+ - name: Install gitleaks
+ run: |
+ go install github.com/zricethezav/gitleaks/v8@latest
+ echo "$(go env GOPATH)/bin" >> "$GITHUB_PATH"
+
+ - name: Install pre-commit
+ run: python -m pip install pre-commit
+
+ - name: Run pre-commit
+ run: pre-commit run --all-files
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..5979fb5
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,13 @@
+repos:
+ - repo: local
+ hooks:
+ - id: repo-hygiene
+ name: repo hygiene checks
+ entry: bash tools/pre_commit_checks.sh
+ language: system
+ pass_filenames: false
+ - id: public-readiness
+ name: public readiness audit
+ entry: bash tools/audit_public.sh
+ language: system
+ pass_filenames: false
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2eb96d6..5936787 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -137,8 +137,8 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
- New plugin with two skills for browser testing against any URL
- `cmux-browser` skill: interactive browser testing via `cmux browser` (visible split pane)
- `playwright-testing` skill: headless browser testing via `playwright-cli`
-- URL-agnostic: works with local dev servers, staging, and production sites (e.g. `https://mon.eurekabuilder.xyz`)
-- Migrated from project-local skills in `trader-dashboard/.claude/skills/`
+- URL-agnostic: works with local dev servers, staging, and production sites (e.g. `https://staging.example.com`)
+- Migrated from project-local `.claude/skills/` content
### Technical Details
- Marketplace: 1.10.0 -> 1.11.0
diff --git a/CLAUDE.md b/CLAUDE.md
index 849216e..920a77b 100644
--- a/CLAUDE.md
+++ b/CLAUDE.md
@@ -40,6 +40,6 @@ Follow [Semantic Versioning](https://semver.org/). Users update via `/plugin upd
### Testing
```bash
-/plugin marketplace add /Users/amit/src/amitkot/claude-code-tools
+/plugin marketplace add .
/plugin install plugin-name@amitkot
```
diff --git a/README.md b/README.md
index ac7f18b..d9f3349 100644
--- a/README.md
+++ b/README.md
@@ -88,6 +88,19 @@ Extracted documentation optimized for AI context:
2. **GitButler CLI** (for GitButler skill) - Install from [gitbutlerapp/gitbutler](https://github.com/gitbutlerapp/gitbutler)
+## Public Readiness
+
+Before changing repository visibility or publishing a release, run the local audit:
+
+```bash
+pre-commit run --all-files
+just audit-public
+gitleaks detect --source . --redact --no-banner
+git status --short --ignored
+```
+
+The public-readiness gate targets the current tree and future commits. Repository history is not rewritten.
+
### Installing Plugins
#### Option 1: Via Plugin Marketplace (Recommended)
diff --git a/ai_docs/claude-code/plugins-documentation.md b/ai_docs/claude-code/plugins-documentation.md
index 5e002f9..9239ba4 100644
--- a/ai_docs/claude-code/plugins-documentation.md
+++ b/ai_docs/claude-code/plugins-documentation.md
@@ -1322,7 +1322,7 @@ const customServer = createSdkMcpServer({
`https://api.weather.com/v1/current?q=${args.location}&units=${args.units}`
);
const data = await response.json();
-
+
return {
content: [{
type: "text",
@@ -1440,7 +1440,7 @@ tool(
// args is fully typed based on the schema
// TypeScript knows: args.data.name is string, args.data.age is number, etc.
console.log(`Processing ${args.data.name}'s data as ${args.format}`);
-
+
// Your processing logic here
return {
content: [{
@@ -1464,7 +1464,7 @@ tool(
async (args) => {
try {
const response = await fetch(args.endpoint);
-
+
if (!response.ok) {
return {
content: [{
@@ -1473,7 +1473,7 @@ tool(
}]
};
}
-
+
const data = await response.json();
return {
content: [{
@@ -1547,20 +1547,20 @@ const apiGatewayServer = createSdkMcpServer({
openai: { baseUrl: "https://api.openai.com/v1", key: process.env.OPENAI_KEY },
slack: { baseUrl: "https://slack.com/api", key: process.env.SLACK_TOKEN }
};
-
+
const { baseUrl, key } = config[args.service];
const url = new URL(`${baseUrl}${args.endpoint}`);
-
+
if (args.query) {
Object.entries(args.query).forEach(([k, v]) => url.searchParams.set(k, v));
}
-
+
const response = await fetch(url, {
method: args.method,
headers: { Authorization: `Bearer ${key}`, "Content-Type": "application/json" },
body: args.body ? JSON.stringify(args.body) : undefined
});
-
+
const data = await response.json();
return {
content: [{
@@ -1593,7 +1593,7 @@ const calculatorServer = createSdkMcpServer({
// Use a safe math evaluation library in production
const result = eval(args.expression); // Example only!
const formatted = Number(result).toFixed(args.precision);
-
+
return {
content: [{
type: "text",
@@ -1622,7 +1622,7 @@ const calculatorServer = createSdkMcpServer({
async (args) => {
const amount = args.principal * Math.pow(1 + args.rate / args.n, args.n * args.time);
const interest = amount - args.principal;
-
+
return {
content: [{
type: "text",
diff --git a/beads-orchestration/skills/execute-beads-plan/SKILL.md b/beads-orchestration/skills/execute-beads-plan/SKILL.md
index 040e2f6..326f528 100644
--- a/beads-orchestration/skills/execute-beads-plan/SKILL.md
+++ b/beads-orchestration/skills/execute-beads-plan/SKILL.md
@@ -248,7 +248,7 @@ Execution complete.
Results:
- Tasks completed: N
- Verification passed: N
--
+-
Implementation plan: [path]
Source plan: [path]
diff --git a/beads-orchestration/skills/execute-beads-plan/references/agent-coordination.md b/beads-orchestration/skills/execute-beads-plan/references/agent-coordination.md
index 4e72ca2..74a9e07 100644
--- a/beads-orchestration/skills/execute-beads-plan/references/agent-coordination.md
+++ b/beads-orchestration/skills/execute-beads-plan/references/agent-coordination.md
@@ -24,7 +24,7 @@
When Task Agents complete work, they leave breadcrumbs:
```markdown
-**Notes:** Implemented validation in `src/api/validate.rs:15-45`.
+**Notes:** Implemented validation in `src/api/validate.rs:15-45`.
Added ValidationError enum in `src/errors.rs:20-30`.
Uses existing `EmailValidator` from `src/utils/validators.rs`.
```
@@ -68,7 +68,7 @@ To run agents in parallel, use single message with multiple Task tool calls:
subagent_type: general-purpose
- model: sonnet
+ model: sonnet
prompt: [Task Agent prompt for br-105]
diff --git a/beads-orchestration/skills/plans-to-beads/SKILL.md b/beads-orchestration/skills/plans-to-beads/SKILL.md
index 78d5477..464e8fb 100644
--- a/beads-orchestration/skills/plans-to-beads/SKILL.md
+++ b/beads-orchestration/skills/plans-to-beads/SKILL.md
@@ -84,7 +84,7 @@ Analyze the plan deeply before creating any beads:
```bash
br dep add
```
-
+
4. **Link tasks to epic**:
```bash
br dep add -t parent-child
diff --git a/justfile b/justfile
index f9301c8..1a8eb0f 100644
--- a/justfile
+++ b/justfile
@@ -3,3 +3,7 @@
# Run session recovery TUI
session-recover *ARGS:
cd session_recovery && uv run --with textual --with typer python __main__.py {{ARGS}}
+
+# Run public-readiness checks used by pre-commit and CI
+audit-public:
+ bash tools/audit_public.sh
diff --git a/marketplace-author/skills/marketplace-author/SKILL.md b/marketplace-author/skills/marketplace-author/SKILL.md
index a016e67..f2a489a 100644
--- a/marketplace-author/skills/marketplace-author/SKILL.md
+++ b/marketplace-author/skills/marketplace-author/SKILL.md
@@ -11,7 +11,7 @@ Authoring skills in the [`amitkot/claude-code-tools`](https://github.com/amitkot
## CRITICAL: Repo path
-The marketplace repo is at `/Users/amit/src/amitkot/claude-code-tools/`. Run all commands from there. If a different machine path is needed, ask once before guessing.
+Run all commands from the repository root. If the current working directory is unclear, ask once before guessing.
## Workflow
diff --git a/tools/audit_public.sh b/tools/audit_public.sh
new file mode 100644
index 0000000..256bfa0
--- /dev/null
+++ b/tools/audit_public.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
+ echo "audit-public: must run inside a git repository" >&2
+ exit 1
+fi
+
+cd "$(git rev-parse --show-toplevel)"
+
+status=0
+
+check_pattern() {
+ local label="$1"
+ local pattern="$2"
+
+ if git grep -n -I -E "$pattern" -- . \
+ ':(exclude).git' \
+ ':(exclude)tools/audit_public.sh' \
+ ':(exclude).pre-commit-config.yaml' >/tmp/audit-public-matches.txt; then
+ echo "Blocked public-readiness pattern: ${label}" >&2
+ cat /tmp/audit-public-matches.txt >&2
+ status=1
+ fi
+}
+
+check_pattern "internal project/domain references" "eurekabuilder|mon\\.eurekabuilder\\.xyz|trader-dashboard"
+check_pattern "absolute local repo path" "/Users/amit/src/amitkot/claude-code-tools"
+
+rm -f /tmp/audit-public-matches.txt
+
+if command -v gitleaks >/dev/null 2>&1; then
+ gitleaks detect --source . --redact --no-banner
+else
+ echo "gitleaks not found; install it and run: gitleaks detect --source . --redact --no-banner" >&2
+fi
+
+exit "$status"
diff --git a/tools/pre_commit_checks.sh b/tools/pre_commit_checks.sh
new file mode 100644
index 0000000..463d56a
--- /dev/null
+++ b/tools/pre_commit_checks.sh
@@ -0,0 +1,70 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+if ! git rev-parse --show-toplevel >/dev/null 2>&1; then
+ echo "pre-commit checks: must run inside a git repository" >&2
+ exit 1
+fi
+
+cd "$(git rev-parse --show-toplevel)"
+
+status=0
+max_bytes=$((1024 * 1024))
+
+files=()
+while IFS= read -r -d '' file; do
+ files+=("$file")
+done < <(git ls-files -z)
+
+for file in "${files[@]}"; do
+ [[ -f "$file" ]] || continue
+
+ size=$(wc -c <"$file" | tr -d ' ')
+ if [[ "$size" -gt "$max_bytes" ]]; then
+ echo "Large tracked file exceeds 1 MiB: $file (${size} bytes)" >&2
+ status=1
+ fi
+
+ if LC_ALL=C grep -Iq . "$file"; then
+ if LC_ALL=C grep -n '[[:blank:]]$' "$file" >/tmp/pre-commit-trailing.txt; then
+ echo "Trailing whitespace in $file:" >&2
+ cat /tmp/pre-commit-trailing.txt >&2
+ status=1
+ fi
+
+ if [[ -s "$file" ]] && [[ "$(tail -c 1 "$file")" != "" ]]; then
+ echo "Missing final newline: $file" >&2
+ status=1
+ fi
+
+ if LC_ALL=C grep -n -E -e '-----BEGIN (RSA |DSA |EC |OPENSSH |PGP )?PRIVATE KEY-----' "$file" >/tmp/pre-commit-private-key.txt; then
+ echo "Private key material detected in $file:" >&2
+ cat /tmp/pre-commit-private-key.txt >&2
+ status=1
+ fi
+ fi
+
+ case "$file" in
+ *.json)
+ if ! python3 -m json.tool "$file" >/dev/null; then
+ echo "Invalid JSON: $file" >&2
+ status=1
+ fi
+ ;;
+ *.yaml|*.yml)
+ if command -v ruby >/dev/null 2>&1; then
+ if ! ruby -e 'require "yaml"; YAML.load_file(ARGV.fetch(0))' "$file"; then
+ echo "Invalid YAML: $file" >&2
+ status=1
+ fi
+ else
+ echo "ruby not found; cannot validate YAML file: $file" >&2
+ status=1
+ fi
+ ;;
+ esac
+done
+
+rm -f /tmp/pre-commit-trailing.txt /tmp/pre-commit-private-key.txt
+
+exit "$status"