chore: import upstream snapshot with attribution
This commit is contained in:
@@ -0,0 +1,6 @@
|
||||
# Protect CI/CD and security-sensitive paths
|
||||
.github/ @danipower
|
||||
scripts/ @danipower
|
||||
.npmrc @danipower
|
||||
CLAUDE.md @danipower
|
||||
dashboard/src/pages/api/ @danipower
|
||||
@@ -0,0 +1,4 @@
|
||||
# These are supported funding model platforms
|
||||
|
||||
github: davila7
|
||||
buy_me_a_coffee: daniavila
|
||||
@@ -0,0 +1,88 @@
|
||||
# GitHub Workflows Reference
|
||||
|
||||
## ✅ Active Workflows
|
||||
|
||||
### `deploy.yml` - **VERCEL DEPLOYMENT**
|
||||
- **Status**: ✅ ACTIVE - Production deployment
|
||||
- **Purpose**: Deploys main site to Vercel production
|
||||
- **Trigger**: Push to main branch
|
||||
- **Features**:
|
||||
- Automated Vercel deployment
|
||||
- Production environment setup
|
||||
- Zero-downtime deployment
|
||||
|
||||
### `deploy-docusaurus.yml` - **GITHUB PAGES DEPLOYMENT**
|
||||
- **Status**: ✅ ACTIVE - Documentation site
|
||||
- **Purpose**: Deploys main site to GitHub Pages
|
||||
- **Trigger**: Push to main branch or manual dispatch
|
||||
- **Features**:
|
||||
- Builds and deploys documentation
|
||||
- GitHub Pages integration
|
||||
- Jekyll disabled (.nojekyll)
|
||||
|
||||
### `publish-package.yml` - **PACKAGE PUBLISHING**
|
||||
- **Status**: ✅ ACTIVE - Package distribution
|
||||
- **Purpose**: Publishes CLI package to GitHub Packages
|
||||
- **Trigger**: Release published or manual dispatch
|
||||
- **Features**:
|
||||
- Automated version management
|
||||
- GitHub Packages publishing
|
||||
- NPM registry integration
|
||||
|
||||
## 📊 Download Tracking System
|
||||
|
||||
**Current Architecture**: Direct Supabase database integration
|
||||
|
||||
- **Method**: CLI directly sends tracking data to Supabase API endpoint
|
||||
- **Endpoint**: `https://www.aitmpl.com/api/track-download-supabase`
|
||||
- **Database**: Supabase PostgreSQL with anonymous data collection
|
||||
- **Real-time**: Immediate tracking on component installation
|
||||
- **Privacy**: Completely anonymous, no personal data collected
|
||||
|
||||
## Usage Instructions
|
||||
|
||||
### For Production Deployment
|
||||
**Use**: `deploy.yml` (triggers automatically on main branch)
|
||||
|
||||
### For Documentation Updates
|
||||
**Use**: `deploy-docusaurus.yml` (triggers automatically on main branch)
|
||||
|
||||
### For Package Publishing
|
||||
**Use**: `publish-package.yml`
|
||||
|
||||
```bash
|
||||
# Trigger manual package publishing
|
||||
gh workflow run "Publish Package to GitHub Packages" \
|
||||
--field version=patch
|
||||
```
|
||||
|
||||
## Migration History
|
||||
|
||||
**Previous Tracking System**: The project previously used multiple GitHub Actions workflows for download tracking:
|
||||
- `analytics-processor.yml` - Processed GitHub Issues as data backend
|
||||
- `tracking-dispatch.yml` - Handled repository dispatch events
|
||||
- `process-tracking-logs.yml` - Processed GitHub Pages access logs
|
||||
- `simple-tracking.yml` - Manual workflow dispatch system
|
||||
|
||||
**Current System**: All tracking workflows have been **removed** and replaced with direct Supabase database integration from the CLI. This provides:
|
||||
- ✅ Real-time tracking (no workflow delays)
|
||||
- ✅ Better performance (no GitHub API rate limits)
|
||||
- ✅ Simpler maintenance (no complex workflow logic)
|
||||
- ✅ Higher reliability (no dependency on GitHub Actions)
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Workflow Issues
|
||||
- **Deployment failed**: Check Vercel token and environment variables
|
||||
- **Pages not updating**: Verify GitHub Pages settings and branch configuration
|
||||
- **Package publishing failed**: Ensure GitHub token has packages:write permission
|
||||
|
||||
### Download Tracking Issues
|
||||
- **Tracking not working**: Verify CLI is updated and Supabase endpoint is accessible
|
||||
- **Debug tracking**: Run CLI with `CCT_DEBUG=true` environment variable
|
||||
- **Opt-out**: Set `CCT_NO_TRACKING=true` to disable tracking
|
||||
|
||||
---
|
||||
|
||||
Last updated: 2025-08-19
|
||||
Active tracking system: Direct Supabase database integration
|
||||
@@ -0,0 +1,25 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/cli-tool"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- "dependencies"
|
||||
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/dashboard"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- "dependencies"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
open-pull-requests-limit: 5
|
||||
labels:
|
||||
- "ci"
|
||||
@@ -0,0 +1,82 @@
|
||||
name: Build Rust CLI
|
||||
|
||||
# Builds the Rust `cct` binary for all supported targets and (on a tag) uploads
|
||||
# per-target tarballs to the GitHub Release. Those tarballs are the source of
|
||||
# truth for npm platform packages, Homebrew, cargo-binstall and the install.sh
|
||||
# script.
|
||||
#
|
||||
# Tarball naming matches the cargo-binstall metadata in cli-rust/Cargo.toml:
|
||||
# cct-<rust-target>.tgz (contains a single `cct` / `cct.exe`)
|
||||
|
||||
on:
|
||||
push:
|
||||
tags:
|
||||
- 'cli-rust-v*'
|
||||
workflow_dispatch:
|
||||
|
||||
# Needed for softprops/action-gh-release to create/update the Release on tags.
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
defaults:
|
||||
run:
|
||||
working-directory: cli-rust
|
||||
|
||||
jobs:
|
||||
build:
|
||||
name: build ${{ matrix.target }}
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include:
|
||||
- { os: macos-latest, target: aarch64-apple-darwin }
|
||||
- { os: macos-latest, target: x86_64-apple-darwin }
|
||||
- { os: ubuntu-latest, target: x86_64-unknown-linux-gnu }
|
||||
- { os: ubuntu-latest, target: aarch64-unknown-linux-gnu }
|
||||
- { os: windows-latest, target: x86_64-pc-windows-msvc }
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
targets: ${{ matrix.target }}
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: cli-rust
|
||||
key: ${{ matrix.target }}
|
||||
|
||||
- name: Install cross-linker (aarch64 linux)
|
||||
if: matrix.target == 'aarch64-unknown-linux-gnu'
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install -y gcc-aarch64-linux-gnu
|
||||
mkdir -p .cargo
|
||||
printf '[target.aarch64-unknown-linux-gnu]\nlinker = "aarch64-linux-gnu-gcc"\n' >> .cargo/config.toml
|
||||
|
||||
- name: Build
|
||||
run: cargo build --release --target ${{ matrix.target }}
|
||||
|
||||
- name: Package tarball
|
||||
shell: bash
|
||||
run: |
|
||||
BIN=cct
|
||||
[ "${{ matrix.target }}" = "x86_64-pc-windows-msvc" ] && BIN=cct.exe
|
||||
mkdir -p dist
|
||||
cp "target/${{ matrix.target }}/release/$BIN" "dist/$BIN"
|
||||
tar -czf "dist/cct-${{ matrix.target }}.tgz" -C dist "$BIN"
|
||||
|
||||
- name: Upload artifact
|
||||
uses: actions/upload-artifact@v4
|
||||
with:
|
||||
name: cct-${{ matrix.target }}
|
||||
path: cli-rust/dist/cct-${{ matrix.target }}.tgz
|
||||
|
||||
- name: Attach to release
|
||||
if: startsWith(github.ref, 'refs/tags/')
|
||||
uses: softprops/action-gh-release@v2
|
||||
with:
|
||||
files: cli-rust/dist/cct-${{ matrix.target }}.tgz
|
||||
@@ -0,0 +1,88 @@
|
||||
name: Component PR Welcome
|
||||
|
||||
on:
|
||||
pull_request_target:
|
||||
types: [opened, reopened, synchronize]
|
||||
paths:
|
||||
- 'cli-tool/components/**'
|
||||
|
||||
permissions:
|
||||
pull-requests: write
|
||||
issues: write
|
||||
|
||||
jobs:
|
||||
welcome:
|
||||
name: Welcome & Label
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: Add review-pending label and welcome comment
|
||||
uses: actions/github-script@v8
|
||||
with:
|
||||
script: |
|
||||
const { owner, repo } = context.repo;
|
||||
const prNumber = context.issue.number;
|
||||
const author = context.payload.pull_request.user.login;
|
||||
|
||||
const labelName = 'review-pending';
|
||||
const labelColor = 'fbca04';
|
||||
const labelDescription = 'Component PR awaiting maintainer review';
|
||||
const marker = '<!-- component-pr-welcome:v1 -->';
|
||||
|
||||
try {
|
||||
await github.rest.issues.getLabel({ owner, repo, name: labelName });
|
||||
} catch (err) {
|
||||
if (err.status === 404) {
|
||||
await github.rest.issues.createLabel({
|
||||
owner,
|
||||
repo,
|
||||
name: labelName,
|
||||
color: labelColor,
|
||||
description: labelDescription,
|
||||
});
|
||||
} else {
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
|
||||
await github.rest.issues.addLabels({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
labels: [labelName],
|
||||
});
|
||||
|
||||
const existing = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{ owner, repo, issue_number: prNumber, per_page: 100 }
|
||||
);
|
||||
if (existing.some(c => c.body && c.body.includes(marker))) {
|
||||
core.info('Welcome comment already posted — skipping comment, label ensured.');
|
||||
return;
|
||||
}
|
||||
|
||||
const body = [
|
||||
marker,
|
||||
`## 👋 Thanks for contributing, @${author}!`,
|
||||
``,
|
||||
`This PR touches \`cli-tool/components/**\` and has been marked **\`review-pending\`**.`,
|
||||
``,
|
||||
`### What happens next`,
|
||||
`1. 🤖 **Automated security audit** runs and posts results on this PR.`,
|
||||
`2. 👀 **Maintainer review** — a human reviewer validates the component with the \`component-reviewer\` agent (format, naming, security, clarity).`,
|
||||
`3. ✅ **Merge** — once approved, your PR is merged to \`main\`.`,
|
||||
`4. 📦 **Catalog regeneration** — the component catalog is rebuilt automatically.`,
|
||||
`5. 🚀 **Live on [aitmpl.com](https://www.aitmpl.com)** — your component appears on the website after deploy.`,
|
||||
``,
|
||||
`### While you wait`,
|
||||
`- Check the **Security Audit** comment below for any issues to fix.`,
|
||||
`- Make sure your component follows the [contribution guide](https://github.com/${owner}/${repo}/blob/main/CLAUDE.md#component-system).`,
|
||||
``,
|
||||
`_This is an automated message. No action is required from you right now — a maintainer will review soon._`,
|
||||
].join('\n');
|
||||
|
||||
await github.rest.issues.createComment({
|
||||
owner,
|
||||
repo,
|
||||
issue_number: prNumber,
|
||||
body,
|
||||
});
|
||||
@@ -0,0 +1,128 @@
|
||||
name: Component Security Validation
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'cli-tool/components/**/*.md'
|
||||
- 'cli-tool/src/validation/**'
|
||||
- '.github/workflows/component-security-validation.yml'
|
||||
push:
|
||||
branches:
|
||||
- main
|
||||
paths:
|
||||
- 'cli-tool/components/**/*.md'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
security-audit:
|
||||
name: Security Audit
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0 # Full history for git metadata
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '18'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: cli-tool/package-lock.json
|
||||
|
||||
- name: Install dependencies
|
||||
run: |
|
||||
cd cli-tool
|
||||
npm ci --ignore-scripts
|
||||
|
||||
- name: Run Security Audit
|
||||
id: audit
|
||||
run: |
|
||||
cd cli-tool
|
||||
npm run security-audit:ci
|
||||
continue-on-error: true
|
||||
|
||||
- name: Generate JSON Report
|
||||
if: always()
|
||||
run: |
|
||||
cd cli-tool
|
||||
npm run security-audit:json
|
||||
|
||||
- name: Upload Security Report
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: security-audit-report
|
||||
path: cli-tool/security-report.json
|
||||
retention-days: 30
|
||||
|
||||
- name: Comment PR with Results
|
||||
if: github.event_name == 'pull_request' && always()
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const reportPath = 'cli-tool/security-report.json';
|
||||
|
||||
if (!fs.existsSync(reportPath)) {
|
||||
console.log('No security report found');
|
||||
return;
|
||||
}
|
||||
|
||||
const report = JSON.parse(fs.readFileSync(reportPath, 'utf8'));
|
||||
|
||||
const passed = report.summary.passed;
|
||||
const failed = report.summary.failed;
|
||||
const warnings = report.summary.warnings;
|
||||
const total = report.summary.total;
|
||||
|
||||
const status = failed === 0 ? '✅ PASSED' : '❌ FAILED';
|
||||
const emoji = failed === 0 ? '🎉' : '⚠️';
|
||||
|
||||
let comment = `## ${emoji} Security Audit Report\n\n`;
|
||||
comment += `**Status**: ${status}\n\n`;
|
||||
|
||||
// Summary table
|
||||
comment += `| Metric | Count |\n`;
|
||||
comment += `|--------|-------|\n`;
|
||||
comment += `| Total Components | ${total} |\n`;
|
||||
comment += `| ✅ Passed | ${passed} |\n`;
|
||||
comment += `| ❌ Failed | ${failed} |\n`;
|
||||
comment += `| ⚠️ Warnings | ${warnings} |\n\n`;
|
||||
|
||||
if (failed > 0) {
|
||||
comment += `### ❌ Failed Components (Top 5)\n\n`;
|
||||
comment += `| Component | Errors | Warnings | Score |\n`;
|
||||
comment += `|-----------|--------|----------|-------|\n`;
|
||||
|
||||
const failedComponents = report.components
|
||||
.filter(c => !c.overall.valid)
|
||||
.sort((a, b) => b.overall.errorCount - a.overall.errorCount)
|
||||
.slice(0, 5);
|
||||
|
||||
for (const component of failedComponents) {
|
||||
const name = component.component.path.split('/').pop().replace('.md', '');
|
||||
comment += `| \`${name}\` | ${component.overall.errorCount} | ${component.overall.warningCount} | ${component.overall.score}/100 |\n`;
|
||||
}
|
||||
|
||||
if (report.components.filter(c => !c.overall.valid).length > 5) {
|
||||
const remaining = report.components.filter(c => !c.overall.valid).length - 5;
|
||||
comment += `\n*...and ${remaining} more failed component(s)*\n`;
|
||||
}
|
||||
}
|
||||
|
||||
comment += `\n---\n`;
|
||||
comment += `📊 **[View Full Report](https://github.com/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.runId})** for detailed error messages and all components`;
|
||||
|
||||
// Post comment
|
||||
github.rest.issues.createComment({
|
||||
issue_number: context.issue.number,
|
||||
owner: context.repo.owner,
|
||||
repo: context.repo.repo,
|
||||
body: comment
|
||||
});
|
||||
|
||||
@@ -0,0 +1,128 @@
|
||||
name: Daily Blog Share
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 15 * * *' # 15:00 UTC (12:00 PM Chile) - 1 hour after component pick
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
share-blog:
|
||||
name: Share Random Blog Article on Discord
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Pick random blog article and send to Discord
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Count total articles and pick a random index
|
||||
TOTAL=$(jq '.articles | length' docs/blog/blog-articles.json)
|
||||
RANDOM_INDEX=$(( RANDOM % TOTAL ))
|
||||
|
||||
# Extract the random article
|
||||
ARTICLE_JSON=$(jq --argjson idx "$RANDOM_INDEX" '.articles[$idx]' docs/blog/blog-articles.json)
|
||||
|
||||
# Parse fields
|
||||
TITLE=$(echo "$ARTICLE_JSON" | jq -r '.title')
|
||||
DESCRIPTION=$(echo "$ARTICLE_JSON" | jq -r '.description // "No description available"' | head -c 400)
|
||||
URL_RAW=$(echo "$ARTICLE_JSON" | jq -r '.url')
|
||||
IMAGE=$(echo "$ARTICLE_JSON" | jq -r '.image // empty')
|
||||
CATEGORY=$(echo "$ARTICLE_JSON" | jq -r '.category // "General"')
|
||||
READ_TIME=$(echo "$ARTICLE_JSON" | jq -r '.readTime // "5 min read"')
|
||||
DIFFICULTY=$(echo "$ARTICLE_JSON" | jq -r '.difficulty // "basic"')
|
||||
TAGS=$(echo "$ARTICLE_JSON" | jq -r '[.tags[]? // empty] | join(", ")')
|
||||
|
||||
# Build full URL
|
||||
if echo "$URL_RAW" | grep -q "^http"; then
|
||||
ARTICLE_URL="$URL_RAW"
|
||||
else
|
||||
ARTICLE_URL="https://aitmpl.com/blog/${URL_RAW}"
|
||||
fi
|
||||
|
||||
# Build full image URL
|
||||
if [ -n "$IMAGE" ]; then
|
||||
if echo "$IMAGE" | grep -q "^http"; then
|
||||
IMAGE_URL="$IMAGE"
|
||||
else
|
||||
IMAGE_URL="https://aitmpl.com/blog/${IMAGE}"
|
||||
fi
|
||||
else
|
||||
IMAGE_URL="https://aitmpl.com/img/logo.png"
|
||||
fi
|
||||
|
||||
# Emoji and color based on difficulty
|
||||
case "$DIFFICULTY" in
|
||||
basic) DIFF_EMOJI="🟢"; COLOR=3066993 ;;
|
||||
intermediate) DIFF_EMOJI="🟡"; COLOR=16750848 ;;
|
||||
advanced) DIFF_EMOJI="🔴"; COLOR=15158332 ;;
|
||||
*) DIFF_EMOJI="📖"; COLOR=5793266 ;;
|
||||
esac
|
||||
|
||||
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Build payload with jq
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg title "$TITLE" \
|
||||
--arg desc "$DESCRIPTION" \
|
||||
--arg url "$ARTICLE_URL" \
|
||||
--arg image "$IMAGE_URL" \
|
||||
--argjson color "$COLOR" \
|
||||
--arg category "$CATEGORY" \
|
||||
--arg read_time "$READ_TIME" \
|
||||
--arg diff_emoji "$DIFF_EMOJI" \
|
||||
--arg difficulty "$DIFFICULTY" \
|
||||
--arg tags "$TAGS" \
|
||||
--arg timestamp "$TIMESTAMP" \
|
||||
'{
|
||||
username: "Claude Code Templates",
|
||||
avatar_url: "https://aitmpl.com/img/logo.png",
|
||||
content: "**📝 Blog of the Day** — Expand your Claude Code knowledge!",
|
||||
embeds: [
|
||||
{
|
||||
title: ("📝 " + $title),
|
||||
description: $desc,
|
||||
url: $url,
|
||||
color: $color,
|
||||
thumbnail: {
|
||||
url: $image
|
||||
},
|
||||
fields: [
|
||||
{ name: "📂 Category", value: $category, inline: true },
|
||||
{ name: "⏱️ Read Time", value: $read_time, inline: true },
|
||||
{ name: ($diff_emoji + " Difficulty"), value: ($difficulty | ascii_upcase[:1] + $difficulty[1:]), inline: true },
|
||||
{ name: "🏷️ Tags", value: $tags, inline: false },
|
||||
{ name: "🔗 Read Article", value: ("[Read on aitmpl.com](" + $url + ")"), inline: false }
|
||||
],
|
||||
footer: {
|
||||
text: "Daily Blog Pick • aitmpl.com/blog",
|
||||
icon_url: "https://aitmpl.com/img/logo.png"
|
||||
},
|
||||
timestamp: $timestamp
|
||||
}
|
||||
]
|
||||
}')
|
||||
|
||||
# Send to Discord
|
||||
HTTP_STATUS=$(curl -s -o response.txt -w "%{http_code}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" \
|
||||
"$DISCORD_WEBHOOK_URL")
|
||||
|
||||
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
|
||||
echo "✅ Discord blog notification sent successfully!"
|
||||
echo "Article: ${TITLE}"
|
||||
echo "Category: ${CATEGORY}"
|
||||
echo "URL: ${ARTICLE_URL}"
|
||||
else
|
||||
echo "❌ Failed to send Discord notification (HTTP ${HTTP_STATUS})"
|
||||
cat response.txt
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL_BLOG }}
|
||||
@@ -0,0 +1,389 @@
|
||||
name: Daily Community Help
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 17 * * *' # 17:00 UTC (14:00 PM Chile) - 3 hours after component pick
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
community-help:
|
||||
name: Post Daily Help Question on Discord
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Pick random help question and send to Discord
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Large pool of help/tutorial questions about Claude Code and Claude Code Templates
|
||||
QUESTIONS=$(cat <<'QUESTIONS_EOF'
|
||||
[
|
||||
{
|
||||
"emoji": "🤖",
|
||||
"title": "How to Install Your First Agent",
|
||||
"question": "Did you know you can install specialized AI agents with a single command?\n\n```bash\nnpx claude-code-templates@latest --agent frontend-developer\n```\n\nAgents are saved to `.claude/agents/` and give Claude deep expertise in specific areas. Try `--agent code-reviewer` for automated code reviews!",
|
||||
"difficulty": "basic",
|
||||
"topic": "agents"
|
||||
},
|
||||
{
|
||||
"emoji": "⚡",
|
||||
"title": "Creating Custom Slash Commands",
|
||||
"question": "Slash commands let you create reusable workflows! Install one:\n\n```bash\nnpx claude-code-templates@latest --command setup-testing\n```\n\nOr create your own: add a `.md` file to `.claude/commands/` and use it with `/your-command` in Claude Code.",
|
||||
"difficulty": "basic",
|
||||
"topic": "commands"
|
||||
},
|
||||
{
|
||||
"emoji": "🔌",
|
||||
"title": "Connecting MCP Servers",
|
||||
"question": "MCP (Model Context Protocol) servers give Claude Code access to external services. Install one:\n\n```bash\nnpx claude-code-templates@latest --mcp context7\n```\n\nPopular MCPs: `context7` (live docs), `github` (repo management), `supabase` (database). What MCPs are you using?",
|
||||
"difficulty": "basic",
|
||||
"topic": "mcps"
|
||||
},
|
||||
{
|
||||
"emoji": "🪝",
|
||||
"title": "Automating with Hooks",
|
||||
"question": "Hooks run shell commands automatically when Claude Code performs actions. Example: get notified when a task completes!\n\n```bash\nnpx claude-code-templates@latest --hook automation/simple-notifications\n```\n\nHook types: `PreToolUse`, `PostToolUse`, `Notification`. What would you automate?",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "hooks"
|
||||
},
|
||||
{
|
||||
"emoji": "⚙️",
|
||||
"title": "Customizing Claude Code Settings",
|
||||
"question": "Settings let you configure Claude Code's behavior. Try read-only mode for safe exploration:\n\n```bash\nnpx claude-code-templates@latest --setting read-only-mode\n```\n\nSettings are stored in `.claude/settings.json`. You can also configure allowed/denied tools and custom permissions.",
|
||||
"difficulty": "basic",
|
||||
"topic": "settings"
|
||||
},
|
||||
{
|
||||
"emoji": "📚",
|
||||
"title": "Using Skills for Complex Workflows",
|
||||
"question": "Skills are advanced components that provide multi-step workflows with progressive context loading.\n\n```bash\nnpx claude-code-templates@latest --skill react-best-practices\n```\n\nSkills are invoked with `/skill-name` and expand into detailed instructions. Great for consistent coding standards!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "skills"
|
||||
},
|
||||
{
|
||||
"emoji": "📂",
|
||||
"title": "Setting Up Your CLAUDE.md",
|
||||
"question": "The `CLAUDE.md` file is your project's instruction manual for Claude Code. Place it in your project root with:\n\n- Project architecture overview\n- Build/test commands\n- Coding standards\n- Important file locations\n\nClaude reads it automatically on every session. Have you set yours up?",
|
||||
"difficulty": "basic",
|
||||
"topic": "setup"
|
||||
},
|
||||
{
|
||||
"emoji": "🔧",
|
||||
"title": "Batch Installing Components",
|
||||
"question": "You can install multiple components at once! Combine any types in a single command:\n\n```bash\nnpx claude-code-templates@latest \\\n --agent security-auditor \\\n --command security-audit \\\n --hook security/secret-scanner\n```\n\nPerfect for setting up a complete workflow in seconds!",
|
||||
"difficulty": "basic",
|
||||
"topic": "installation"
|
||||
},
|
||||
{
|
||||
"emoji": "🧪",
|
||||
"title": "Test-Driven Development with Claude Code",
|
||||
"question": "Claude Code excels at TDD! Try this workflow:\n\n1. Write test specs first\n2. Ask Claude to implement the code to pass tests\n3. Use `/test` command to run and verify\n\n```bash\nnpx claude-code-templates@latest --command setup-testing\n```\n\nWhat testing frameworks do you use with Claude Code?",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "testing"
|
||||
},
|
||||
{
|
||||
"emoji": "🛡️",
|
||||
"title": "Secret Scanner Hook",
|
||||
"question": "Prevent accidentally committing API keys and secrets!\n\n```bash\nnpx claude-code-templates@latest --hook security/secret-scanner\n```\n\nThis hook runs before every commit and blocks files containing patterns like API keys, tokens, and passwords. Essential for any project!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "security"
|
||||
},
|
||||
{
|
||||
"emoji": "📊",
|
||||
"title": "Using the Task Tool with SubAgents",
|
||||
"question": "Claude Code's Task tool launches specialized subagents for complex work:\n\n- `Explore` agent: Fast codebase exploration\n- `Plan` agent: Architecture planning\n- Custom agents: Your installed agents\n\nSubAgents run autonomously and report back. Great for parallel investigation of large codebases!",
|
||||
"difficulty": "advanced",
|
||||
"topic": "subagents"
|
||||
},
|
||||
{
|
||||
"emoji": "🔄",
|
||||
"title": "Git Workflow with Claude Code",
|
||||
"question": "Claude Code handles git operations natively! It can:\n\n- Stage, commit, and push changes\n- Create branches and PRs with `gh`\n- Write meaningful commit messages\n- Handle merge conflicts\n\nTip: Ask Claude to 'create a PR' and it will handle the entire flow!",
|
||||
"difficulty": "basic",
|
||||
"topic": "git"
|
||||
},
|
||||
{
|
||||
"emoji": "🌐",
|
||||
"title": "Interactive Component Browser",
|
||||
"question": "Not sure what to install? Browse all 1000+ components at:\n\n**https://aitmpl.com**\n\nOr use interactive mode in the CLI:\n\n```bash\nnpx claude-code-templates@latest\n```\n\nThis launches an interactive menu where you can search and browse components by type!",
|
||||
"difficulty": "basic",
|
||||
"topic": "discovery"
|
||||
},
|
||||
{
|
||||
"emoji": "🏗️",
|
||||
"title": "Project Templates",
|
||||
"question": "Templates provide complete project configurations with multiple components pre-configured:\n\n```bash\nnpx claude-code-templates@latest --template fullstack-starter\n```\n\nA template can include agents, commands, hooks, settings, and skills — everything for a specific workflow!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "templates"
|
||||
},
|
||||
{
|
||||
"emoji": "📝",
|
||||
"title": "Writing Custom Agents",
|
||||
"question": "Create your own specialized agent! Add a `.md` file to `.claude/agents/`:\n\n```markdown\n# My Custom Agent\n\nYou are an expert in [domain].\n\n## Capabilities\n- Specific task 1\n- Specific task 2\n\n## Rules\n- Always do X\n- Never do Y\n```\n\nThen invoke it with Claude Code's agent system. What agent would you create?",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "agents"
|
||||
},
|
||||
{
|
||||
"emoji": "🔍",
|
||||
"title": "Efficient Codebase Exploration",
|
||||
"question": "Claude Code has powerful search tools:\n\n- **Glob**: Find files by pattern (`**/*.tsx`)\n- **Grep**: Search content with regex\n- **Read**: View file contents\n- **Task/Explore**: Autonomous codebase exploration\n\nTip: Ask 'explain the architecture of this project' and Claude will explore systematically!",
|
||||
"difficulty": "basic",
|
||||
"topic": "exploration"
|
||||
},
|
||||
{
|
||||
"emoji": "🎯",
|
||||
"title": "Using Plan Mode",
|
||||
"question": "For complex tasks, use Plan Mode! Claude Code will:\n\n1. Explore the codebase thoroughly\n2. Identify all files that need changes\n3. Design an implementation approach\n4. Present the plan for your approval\n5. Execute only after you confirm\n\nGreat for large refactors or new features with architectural decisions!",
|
||||
"difficulty": "advanced",
|
||||
"topic": "workflow"
|
||||
},
|
||||
{
|
||||
"emoji": "📈",
|
||||
"title": "Statuslines for Real-Time Info",
|
||||
"question": "Statuslines display live info in your Claude Code terminal!\n\n```bash\nnpx claude-code-templates@latest --setting statusline/vercel-monitor\n```\n\nAvailable statuslines show: git status, deployment status, test results, and more. Some even run Python scripts for live data!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "settings"
|
||||
},
|
||||
{
|
||||
"emoji": "🔒",
|
||||
"title": "Permission Management",
|
||||
"question": "Control what Claude Code can do with permissions in `.claude/settings.json`:\n\n```json\n{\n \"permissions\": {\n \"allow\": [\"Read\", \"Glob\", \"Grep\"],\n \"deny\": [\"Bash(rm *)\", \"Write(.env)\"]\n }\n}\n```\n\nUse `--setting read-only-mode` for safe exploration. How do you configure permissions?",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "security"
|
||||
},
|
||||
{
|
||||
"emoji": "🚀",
|
||||
"title": "Deploying with Claude Code",
|
||||
"question": "Claude Code can manage your deployments! Common patterns:\n\n- `vercel --prod` for Vercel\n- `gh workflow run` for GitHub Actions\n- `docker build && docker push` for containers\n\nTip: Create a `/deploy` slash command that runs your full deployment pipeline!",
|
||||
"difficulty": "advanced",
|
||||
"topic": "devops"
|
||||
},
|
||||
{
|
||||
"emoji": "💡",
|
||||
"title": "TodoWrite for Task Tracking",
|
||||
"question": "Claude Code has a built-in task tracker! When working on complex features, it uses `TodoWrite` to:\n\n- Break tasks into steps\n- Track progress in real-time\n- Show you what's done and what's pending\n\nTip: Ask Claude to 'plan this out' and it will create a structured todo list before coding!",
|
||||
"difficulty": "basic",
|
||||
"topic": "workflow"
|
||||
},
|
||||
{
|
||||
"emoji": "🧩",
|
||||
"title": "Multi-File Editing",
|
||||
"question": "Claude Code can edit multiple files in parallel! It uses:\n\n- **Edit**: Precise string replacements\n- **Write**: Create new files\n- **MultiEdit**: Multiple changes in one file\n\nTip: Describe the change you want across your project and Claude will identify and update all relevant files!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "editing"
|
||||
},
|
||||
{
|
||||
"emoji": "🌟",
|
||||
"title": "WebFetch for Live Data",
|
||||
"question": "Claude Code can fetch and analyze web content!\n\n- Fetch API docs for reference\n- Check live endpoints\n- Read GitHub issues and PRs\n- Analyze error pages\n\nCombined with MCP servers, this makes Claude Code a powerful integration tool!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "tools"
|
||||
},
|
||||
{
|
||||
"emoji": "🎨",
|
||||
"title": "Frontend Development Workflow",
|
||||
"question": "Best agents for frontend work:\n\n```bash\nnpx claude-code-templates@latest \\\n --agent frontend-developer \\\n --agent accessibility-expert \\\n --skill react-best-practices\n```\n\nThis combo gives Claude expertise in React/Vue/Next.js, accessibility standards, and performance optimization!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "agents"
|
||||
},
|
||||
{
|
||||
"emoji": "🗄️",
|
||||
"title": "Database Development Setup",
|
||||
"question": "Set up Claude Code for database work:\n\n```bash\nnpx claude-code-templates@latest \\\n --mcp supabase \\\n --agent database-architect\n```\n\nThe Supabase MCP gives direct database access, and the architect agent helps design schemas, write migrations, and optimize queries!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "database"
|
||||
},
|
||||
{
|
||||
"emoji": "📦",
|
||||
"title": "Exploring Components by Category",
|
||||
"question": "Components are organized by category:\n\n**Agents**: `development-team/`, `devops/`, `security/`, `data/`\n**Commands**: `git/`, `testing/`, `documentation/`\n**Hooks**: `automation/`, `security/`, `git/`\n**MCPs**: `ai/`, `cloud/`, `database/`, `dev-tools/`\n\nBrowse all at https://aitmpl.com or use the interactive CLI!",
|
||||
"difficulty": "basic",
|
||||
"topic": "discovery"
|
||||
},
|
||||
{
|
||||
"emoji": "🔮",
|
||||
"title": "Advanced: Worktree Development",
|
||||
"question": "Use git worktrees for parallel development! Claude Code supports:\n\n- Creating worktrees for different tasks\n- Switching between worktrees\n- Parallel development on multiple features\n\nTip: Use `/worktree-init task1 | task2 | task3` to set up multiple parallel workspaces!",
|
||||
"difficulty": "advanced",
|
||||
"topic": "advanced"
|
||||
},
|
||||
{
|
||||
"emoji": "⚡",
|
||||
"title": "Speed Up Your Workflow",
|
||||
"question": "Pro tips for faster Claude Code sessions:\n\n1. Use a detailed `CLAUDE.md` (less back-and-forth)\n2. Install relevant agents for your stack\n3. Use slash commands for repetitive tasks\n4. Enable hooks for automation\n5. Use `Shift+Tab` for auto-accept mode\n\nWhat's your speed optimization?",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "tips"
|
||||
},
|
||||
{
|
||||
"emoji": "🐍",
|
||||
"title": "Python Development Setup",
|
||||
"question": "Set up Claude Code for Python projects:\n\n```bash\nnpx claude-code-templates@latest \\\n --agent python-expert \\\n --command setup-testing \\\n --hook automation/simple-notifications\n```\n\nAdd your Python-specific commands, virtual env paths, and test runners to `CLAUDE.md`!",
|
||||
"difficulty": "basic",
|
||||
"topic": "setup"
|
||||
},
|
||||
{
|
||||
"emoji": "🦀",
|
||||
"title": "Rust Development Setup",
|
||||
"question": "Claude Code works great with Rust! Install the Rust-specific components:\n\n```bash\nnpx claude-code-templates@latest --agent rust-expert\n```\n\nTip: Add your `cargo` commands and project structure to `CLAUDE.md` for better context!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "setup"
|
||||
},
|
||||
{
|
||||
"emoji": "🤝",
|
||||
"title": "Contributing Components",
|
||||
"question": "Want to contribute your own components to Claude Code Templates?\n\n1. Fork the repo: `github.com/danilovilhena/claude-code-templates`\n2. Add your component to `cli-tool/components/{type}/{category}/`\n3. Follow the naming conventions (kebab-case)\n4. Submit a PR!\n\nThe community grows with your contributions!",
|
||||
"difficulty": "advanced",
|
||||
"topic": "contributing"
|
||||
},
|
||||
{
|
||||
"emoji": "📋",
|
||||
"title": "Notification Hooks",
|
||||
"question": "Never miss when Claude finishes a long task!\n\n```bash\nnpx claude-code-templates@latest --hook automation/simple-notifications\n```\n\nThis sends desktop notifications when operations complete. Works on macOS (osascript) and Linux (notify-send). What notifications would be most useful?",
|
||||
"difficulty": "basic",
|
||||
"topic": "hooks"
|
||||
},
|
||||
{
|
||||
"emoji": "🔄",
|
||||
"title": "Keeping Components Updated",
|
||||
"question": "Components improve over time! To get the latest version:\n\n```bash\nnpx claude-code-templates@latest --agent frontend-developer\n```\n\nUsing `@latest` always fetches the newest CLI version. Re-installing a component overwrites it with the latest version!",
|
||||
"difficulty": "basic",
|
||||
"topic": "installation"
|
||||
},
|
||||
{
|
||||
"emoji": "🧠",
|
||||
"title": "Context Management Tips",
|
||||
"question": "Keep Claude Code effective in long sessions:\n\n- Start with clear goals in your first message\n- Use `/clear` to reset context when switching tasks\n- Keep `CLAUDE.md` focused and up-to-date\n- Use subagents for isolated subtasks\n- Break large tasks into smaller ones\n\nHow do you manage context in long sessions?",
|
||||
"difficulty": "advanced",
|
||||
"topic": "tips"
|
||||
},
|
||||
{
|
||||
"emoji": "📊",
|
||||
"title": "Code Review Workflow",
|
||||
"question": "Set up automated code reviews:\n\n```bash\nnpx claude-code-templates@latest \\\n --agent code-reviewer \\\n --command review-pr\n```\n\nThen use `/review-pr 123` to review any PR! The code-reviewer agent checks for bugs, security issues, performance, and best practices.",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "review"
|
||||
},
|
||||
{
|
||||
"emoji": "🎓",
|
||||
"title": "Learning Resources",
|
||||
"question": "Best resources for mastering Claude Code:\n\n- **Blog**: https://aitmpl.com/blog — Tutorials and guides\n- **Docs**: https://docs.anthropic.com/en/docs/claude-code\n- **Components**: https://aitmpl.com — Browse all 1000+ components\n- **Discord**: You're here! Ask questions anytime\n\nWhat topic would you like a tutorial on?",
|
||||
"difficulty": "basic",
|
||||
"topic": "learning"
|
||||
},
|
||||
{
|
||||
"emoji": "🔥",
|
||||
"title": "Hot Component Combos",
|
||||
"question": "Popular component combinations:\n\n**Full-Stack Dev:**\n```bash\n--agent frontend-developer --agent api-developer --mcp supabase\n```\n\n**Security Setup:**\n```bash\n--agent security-auditor --hook security/secret-scanner --setting read-only-mode\n```\n\nWhat's your go-to combo?",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "templates"
|
||||
},
|
||||
{
|
||||
"emoji": "💻",
|
||||
"title": "Running Background Tasks",
|
||||
"question": "Claude Code can run long-running processes in the background!\n\nUse `run_in_background: true` for:\n- Dev servers (`npm run dev`)\n- Watch mode (`npm run test:watch`)\n- Build processes\n\nThen use `BashOutput` to check results later. Great for not blocking your workflow!",
|
||||
"difficulty": "advanced",
|
||||
"topic": "tools"
|
||||
},
|
||||
{
|
||||
"emoji": "🌍",
|
||||
"title": "WebSearch for Current Info",
|
||||
"question": "Claude Code can search the web for up-to-date information!\n\nUseful for:\n- Latest library versions and docs\n- Current best practices\n- Debugging error messages\n- Finding recent solutions\n\nCombined with WebFetch, Claude can read and analyze any public webpage!",
|
||||
"difficulty": "basic",
|
||||
"topic": "tools"
|
||||
},
|
||||
{
|
||||
"emoji": "🏠",
|
||||
"title": "Project vs User Settings",
|
||||
"question": "Claude Code has two settings scopes:\n\n**Project** (`.claude/settings.json`):\n- Shared with team via git\n- Project-specific rules\n\n**User** (`~/.claude/settings.json`):\n- Personal preferences\n- Global across all projects\n\nTip: Put team standards in project settings and personal preferences in user settings!",
|
||||
"difficulty": "intermediate",
|
||||
"topic": "settings"
|
||||
},
|
||||
{
|
||||
"emoji": "🔧",
|
||||
"title": "Debugging with Claude Code",
|
||||
"question": "Effective debugging workflow:\n\n1. Describe the bug or paste the error\n2. Claude explores the codebase for context\n3. Identifies root cause\n4. Proposes and implements the fix\n5. Runs tests to verify\n\nTip: Include error messages and reproduction steps for fastest results!",
|
||||
"difficulty": "basic",
|
||||
"topic": "debugging"
|
||||
}
|
||||
]
|
||||
QUESTIONS_EOF
|
||||
)
|
||||
|
||||
# Pick a random question
|
||||
TOTAL=$(echo "$QUESTIONS" | jq 'length')
|
||||
RANDOM_INDEX=$(( RANDOM % TOTAL ))
|
||||
SELECTED=$(echo "$QUESTIONS" | jq --argjson idx "$RANDOM_INDEX" '.[$idx]')
|
||||
|
||||
EMOJI=$(echo "$SELECTED" | jq -r '.emoji')
|
||||
TITLE=$(echo "$SELECTED" | jq -r '.title')
|
||||
QUESTION=$(echo "$SELECTED" | jq -r '.question')
|
||||
DIFFICULTY=$(echo "$SELECTED" | jq -r '.difficulty')
|
||||
TOPIC=$(echo "$SELECTED" | jq -r '.topic')
|
||||
|
||||
# Color and badge based on difficulty
|
||||
case "$DIFFICULTY" in
|
||||
basic) DIFF_BADGE="🟢 Beginner"; COLOR=3066993 ;;
|
||||
intermediate) DIFF_BADGE="🟡 Intermediate"; COLOR=16750848 ;;
|
||||
advanced) DIFF_BADGE="🔴 Advanced"; COLOR=15158332 ;;
|
||||
*) DIFF_BADGE="📖 General"; COLOR=5793266 ;;
|
||||
esac
|
||||
|
||||
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Build payload (thread_name required for forum channels)
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg emoji "$EMOJI" \
|
||||
--arg title "$TITLE" \
|
||||
--arg question "$QUESTION" \
|
||||
--argjson color "$COLOR" \
|
||||
--arg diff_badge "$DIFF_BADGE" \
|
||||
--arg topic "$TOPIC" \
|
||||
--arg timestamp "$TIMESTAMP" \
|
||||
'{
|
||||
username: "Claude Code Templates",
|
||||
avatar_url: "https://aitmpl.com/img/logo.png",
|
||||
thread_name: ($emoji + " " + $title),
|
||||
content: ($emoji + " **Community Help** — Learn something new today!"),
|
||||
embeds: [
|
||||
{
|
||||
title: ($emoji + " " + $title),
|
||||
description: ($question + "\n\n---\n*Have questions? Ask below! The community is here to help.*"),
|
||||
color: $color,
|
||||
fields: [
|
||||
{ name: "📊 Difficulty", value: $diff_badge, inline: true },
|
||||
{ name: "🏷️ Topic", value: ($topic | ascii_upcase[:1] + $topic[1:]), inline: true },
|
||||
{ name: "🔗 Browse Components", value: "[aitmpl.com](https://aitmpl.com)", inline: true }
|
||||
],
|
||||
footer: {
|
||||
text: "Daily Help • Claude Code Community",
|
||||
icon_url: "https://aitmpl.com/img/logo.png"
|
||||
},
|
||||
timestamp: $timestamp
|
||||
}
|
||||
]
|
||||
}')
|
||||
|
||||
# Send to Discord
|
||||
HTTP_STATUS=$(curl -s -o response.txt -w "%{http_code}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" \
|
||||
"$DISCORD_WEBHOOK_URL")
|
||||
|
||||
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
|
||||
echo "✅ Discord community help sent successfully!"
|
||||
echo "Title: ${EMOJI} ${TITLE}"
|
||||
echo "Difficulty: ${DIFFICULTY}"
|
||||
echo "Topic: ${TOPIC}"
|
||||
else
|
||||
echo "❌ Failed to send Discord notification (HTTP ${HTTP_STATUS})"
|
||||
cat response.txt
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL_COMMUNITY_HELP }}
|
||||
@@ -0,0 +1,118 @@
|
||||
name: Daily Component Pick
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 14 * * *' # 14:00 UTC (11:00 AM Chile)
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
share-component:
|
||||
name: Share Random Component on Discord
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Pick random component and send to Discord
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Count total components and pick a random index
|
||||
TOTAL=$(jq '[(.agents // []), (.commands // []), (.mcps // []), (.settings // []), (.hooks // [])] | map(length) | add' docs/components.json)
|
||||
RANDOM_INDEX=$(( RANDOM % TOTAL ))
|
||||
|
||||
# Extract the random component
|
||||
COMPONENT_JSON=$(jq --argjson idx "$RANDOM_INDEX" '
|
||||
[
|
||||
(.agents // [] | .[]),
|
||||
(.commands // [] | .[]),
|
||||
(.mcps // [] | .[]),
|
||||
(.settings // [] | .[]),
|
||||
(.hooks // [] | .[])
|
||||
] | .[$idx]
|
||||
' docs/components.json)
|
||||
|
||||
# Parse fields
|
||||
NAME=$(echo "$COMPONENT_JSON" | jq -r '.name')
|
||||
TYPE=$(echo "$COMPONENT_JSON" | jq -r '.type')
|
||||
CATEGORY=$(echo "$COMPONENT_JSON" | jq -r '.category')
|
||||
DOWNLOADS=$(echo "$COMPONENT_JSON" | jq -r '.downloads // 0')
|
||||
PATH_VALUE=$(echo "$COMPONENT_JSON" | jq -r '.path')
|
||||
DESCRIPTION=$(echo "$COMPONENT_JSON" | jq -r '.description // "No description available"' | head -c 300)
|
||||
|
||||
# Emoji and color based on type
|
||||
case "$TYPE" in
|
||||
agent) EMOJI="🤖"; COLOR=5793266 ;;
|
||||
command) EMOJI="⚡"; COLOR=16750848 ;;
|
||||
mcp) EMOJI="🔌"; COLOR=10181046 ;;
|
||||
setting) EMOJI="⚙️"; COLOR=3066993 ;;
|
||||
hook) EMOJI="🪝"; COLOR=15277667 ;;
|
||||
*) EMOJI="📦"; COLOR=9807270 ;;
|
||||
esac
|
||||
|
||||
# Build derived values
|
||||
TYPE_CAPITALIZED="$(echo "$TYPE" | sed 's/^./\U&/')"
|
||||
INSTALL_CMD="npx claude-code-templates@latest --${TYPE} ${CATEGORY}/${NAME}"
|
||||
WEBSITE_URL="https://aitmpl.com/component.html?type=${TYPE}&name=${NAME}&path=${PATH_VALUE}"
|
||||
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Build payload with jq for proper JSON escaping
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg emoji "$EMOJI" \
|
||||
--arg name "$NAME" \
|
||||
--arg desc "$DESCRIPTION" \
|
||||
--arg url "$WEBSITE_URL" \
|
||||
--argjson color "$COLOR" \
|
||||
--arg type_cap "$TYPE_CAPITALIZED" \
|
||||
--arg category "$CATEGORY" \
|
||||
--arg downloads "$DOWNLOADS" \
|
||||
--arg install "$INSTALL_CMD" \
|
||||
--arg timestamp "$TIMESTAMP" \
|
||||
'{
|
||||
username: "Claude Code Templates",
|
||||
avatar_url: "https://aitmpl.com/img/logo.png",
|
||||
content: ("**" + $emoji + " Component of the Day** — Check out today'\''s pick!"),
|
||||
embeds: [
|
||||
{
|
||||
title: ($emoji + " " + $name),
|
||||
description: $desc,
|
||||
url: $url,
|
||||
color: $color,
|
||||
fields: [
|
||||
{ name: "📂 Type", value: $type_cap, inline: true },
|
||||
{ name: "🏷️ Category", value: $category, inline: true },
|
||||
{ name: "📊 Downloads", value: $downloads, inline: true },
|
||||
{ name: "📥 Install", value: ("```bash\n" + $install + "\n```"), inline: false },
|
||||
{ name: "🔗 View on Website", value: ("[Open in aitmpl.com](" + $url + ")"), inline: false }
|
||||
],
|
||||
footer: {
|
||||
text: "Daily Component Pick • aitmpl.com",
|
||||
icon_url: "https://aitmpl.com/img/logo.png"
|
||||
},
|
||||
timestamp: $timestamp
|
||||
}
|
||||
]
|
||||
}')
|
||||
|
||||
# Send to Discord
|
||||
HTTP_STATUS=$(curl -s -o response.txt -w "%{http_code}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" \
|
||||
"$DISCORD_WEBHOOK_URL")
|
||||
|
||||
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
|
||||
echo "✅ Discord notification sent successfully!"
|
||||
echo "Component: ${EMOJI} ${NAME} (${TYPE}/${CATEGORY})"
|
||||
echo "Downloads: ${DOWNLOADS}"
|
||||
echo "URL: ${WEBSITE_URL}"
|
||||
else
|
||||
echo "❌ Failed to send Discord notification (HTTP ${HTTP_STATUS})"
|
||||
cat response.txt
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL_DAILY }}
|
||||
@@ -0,0 +1,373 @@
|
||||
name: Daily General Engagement
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '0 16 * * *' # 16:00 UTC (13:00 PM Chile) - 2 hours after component pick
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
engage-community:
|
||||
name: Post Daily Engagement Question on Discord
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 5
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v6
|
||||
|
||||
- name: Pick random question and send to Discord
|
||||
run: |
|
||||
set -euo pipefail
|
||||
|
||||
# Large pool of engagement questions organized by category
|
||||
QUESTIONS=$(cat <<'QUESTIONS_EOF'
|
||||
[
|
||||
{
|
||||
"emoji": "👋",
|
||||
"question": "Welcome to everyone who joined recently! What project are you currently working on with Claude Code?",
|
||||
"category": "welcome"
|
||||
},
|
||||
{
|
||||
"emoji": "🤖",
|
||||
"question": "Are you using SubAgents in your workflow? Which specialized agents have been the most useful for you?",
|
||||
"category": "subagents"
|
||||
},
|
||||
{
|
||||
"emoji": "⚡",
|
||||
"question": "What's the most creative slash command you've set up in Claude Code? Share your `/command` ideas!",
|
||||
"category": "commands"
|
||||
},
|
||||
{
|
||||
"emoji": "🪝",
|
||||
"question": "Hooks can automate so much! Are you using any PreToolUse or PostToolUse hooks? What do they do?",
|
||||
"category": "hooks"
|
||||
},
|
||||
{
|
||||
"emoji": "⚙️",
|
||||
"question": "What's your Claude Code settings setup? Do you use read-only mode, custom permissions, or statuslines?",
|
||||
"category": "settings"
|
||||
},
|
||||
{
|
||||
"emoji": "🔌",
|
||||
"question": "Which MCP servers have you connected to Claude Code? (GitHub, Supabase, Context7, Playwright...)",
|
||||
"category": "mcps"
|
||||
},
|
||||
{
|
||||
"emoji": "🎯",
|
||||
"question": "What's your #1 productivity tip when using Claude Code? Drop it below!",
|
||||
"category": "tips"
|
||||
},
|
||||
{
|
||||
"emoji": "📚",
|
||||
"question": "Are you using Skills in Claude Code? Which ones have leveled up your workflow the most?",
|
||||
"category": "skills"
|
||||
},
|
||||
{
|
||||
"emoji": "🏗️",
|
||||
"question": "What type of project are you building with Claude Code? (Web app, CLI tool, API, mobile, game...)",
|
||||
"category": "projects"
|
||||
},
|
||||
{
|
||||
"emoji": "💡",
|
||||
"question": "What feature do you wish Claude Code had that doesn't exist yet?",
|
||||
"category": "wishlist"
|
||||
},
|
||||
{
|
||||
"emoji": "🔧",
|
||||
"question": "Do you use Claude Code for debugging? What's the trickiest bug it helped you solve?",
|
||||
"category": "debugging"
|
||||
},
|
||||
{
|
||||
"emoji": "📂",
|
||||
"question": "How do you structure your CLAUDE.md file? Do you include project context, coding standards, or custom rules?",
|
||||
"category": "setup"
|
||||
},
|
||||
{
|
||||
"emoji": "🚀",
|
||||
"question": "What was your 'aha moment' with Claude Code? When did it click for you?",
|
||||
"category": "experience"
|
||||
},
|
||||
{
|
||||
"emoji": "🧪",
|
||||
"question": "Do you use Claude Code for writing tests? Unit tests, integration tests, E2E? What's your approach?",
|
||||
"category": "testing"
|
||||
},
|
||||
{
|
||||
"emoji": "🔄",
|
||||
"question": "How do you handle code reviews with Claude Code? Do you use a code-reviewer agent or custom commands?",
|
||||
"category": "review"
|
||||
},
|
||||
{
|
||||
"emoji": "🌐",
|
||||
"question": "What's your tech stack? Let's see what the community is building with! (React, Python, Rust, Go...)",
|
||||
"category": "techstack"
|
||||
},
|
||||
{
|
||||
"emoji": "📊",
|
||||
"question": "Do you use Claude Code's multi-agent architecture with the Task tool? How do you organize your subagents?",
|
||||
"category": "subagents"
|
||||
},
|
||||
{
|
||||
"emoji": "🛡️",
|
||||
"question": "Security first! Are you using any security hooks or secret scanners in your Claude Code setup?",
|
||||
"category": "security"
|
||||
},
|
||||
{
|
||||
"emoji": "📝",
|
||||
"question": "Do you use Claude Code for documentation? What's your approach: inline comments, README files, or full docs sites?",
|
||||
"category": "docs"
|
||||
},
|
||||
{
|
||||
"emoji": "🎨",
|
||||
"question": "Frontend devs: are you using Claude Code for UI/UX work? How does it handle CSS, components, and responsive design?",
|
||||
"category": "frontend"
|
||||
},
|
||||
{
|
||||
"emoji": "🗄️",
|
||||
"question": "What database tools do you use with Claude Code? (Supabase MCP, Neon, SQLite, Prisma...)",
|
||||
"category": "database"
|
||||
},
|
||||
{
|
||||
"emoji": "⏱️",
|
||||
"question": "How much time has Claude Code saved you this week? What task would have taken you the longest manually?",
|
||||
"category": "productivity"
|
||||
},
|
||||
{
|
||||
"emoji": "🔮",
|
||||
"question": "What's the most complex thing you've built entirely with Claude Code assistance?",
|
||||
"category": "showcase"
|
||||
},
|
||||
{
|
||||
"emoji": "🤝",
|
||||
"question": "Do you use Claude Code in a team? How do you share agents, commands, and settings across your team?",
|
||||
"category": "team"
|
||||
},
|
||||
{
|
||||
"emoji": "📦",
|
||||
"question": "How many components from Claude Code Templates have you installed? Which category do you use the most?",
|
||||
"category": "templates"
|
||||
},
|
||||
{
|
||||
"emoji": "🐛",
|
||||
"question": "What's the funniest or most unexpected thing Claude Code has done in your project?",
|
||||
"category": "fun"
|
||||
},
|
||||
{
|
||||
"emoji": "🏠",
|
||||
"question": "New here? Introduce yourself! What's your background and what brought you to Claude Code?",
|
||||
"category": "welcome"
|
||||
},
|
||||
{
|
||||
"emoji": "💻",
|
||||
"question": "What's your development environment? (VS Code, Terminal, Neovim, JetBrains...) How does Claude Code fit in?",
|
||||
"category": "setup"
|
||||
},
|
||||
{
|
||||
"emoji": "🔗",
|
||||
"question": "Are you using Claude Code with CI/CD pipelines? How have you integrated it into your deployment workflow?",
|
||||
"category": "devops"
|
||||
},
|
||||
{
|
||||
"emoji": "📱",
|
||||
"question": "Mobile developers: are you using Claude Code for React Native, Flutter, or native iOS/Android development?",
|
||||
"category": "mobile"
|
||||
},
|
||||
{
|
||||
"emoji": "🧠",
|
||||
"question": "What prompting techniques work best for you in Claude Code? Long detailed prompts vs short and iterative?",
|
||||
"category": "tips"
|
||||
},
|
||||
{
|
||||
"emoji": "🎓",
|
||||
"question": "Are you learning a new language or framework with Claude Code? Which one and how is it going?",
|
||||
"category": "learning"
|
||||
},
|
||||
{
|
||||
"emoji": "🔥",
|
||||
"question": "What component from Claude Code Templates should everyone install? Share your must-have recommendation!",
|
||||
"category": "templates"
|
||||
},
|
||||
{
|
||||
"emoji": "⚡",
|
||||
"question": "Quick poll: do you prefer using Claude Code in the terminal or through an IDE extension? Why?",
|
||||
"category": "setup"
|
||||
},
|
||||
{
|
||||
"emoji": "🌍",
|
||||
"question": "Where are you coding from today? Let's see how global our community is!",
|
||||
"category": "welcome"
|
||||
},
|
||||
{
|
||||
"emoji": "🛠️",
|
||||
"question": "What's in your Claude Code toolbox? Share your top 3 installed components!",
|
||||
"category": "templates"
|
||||
},
|
||||
{
|
||||
"emoji": "📈",
|
||||
"question": "How has Claude Code changed your development workflow compared to 6 months ago?",
|
||||
"category": "experience"
|
||||
},
|
||||
{
|
||||
"emoji": "🎯",
|
||||
"question": "Do you use Claude Code's /init command to set up new projects? What's your project initialization workflow?",
|
||||
"category": "commands"
|
||||
},
|
||||
{
|
||||
"emoji": "🧩",
|
||||
"question": "What API integrations have you built with Claude Code? REST, GraphQL, WebSockets?",
|
||||
"category": "projects"
|
||||
},
|
||||
{
|
||||
"emoji": "🔒",
|
||||
"question": "How do you handle secrets and API keys in your Claude Code projects? .env files, environment variables, vaults?",
|
||||
"category": "security"
|
||||
},
|
||||
{
|
||||
"emoji": "🎉",
|
||||
"question": "Share a win! What did Claude Code help you accomplish this week that you're proud of?",
|
||||
"category": "showcase"
|
||||
},
|
||||
{
|
||||
"emoji": "📖",
|
||||
"question": "Have you read any of our blog articles? Which one was the most helpful for your workflow?",
|
||||
"category": "blog"
|
||||
},
|
||||
{
|
||||
"emoji": "🤔",
|
||||
"question": "What's the biggest challenge you face when using Claude Code? Maybe someone here has a solution!",
|
||||
"category": "help"
|
||||
},
|
||||
{
|
||||
"emoji": "🚀",
|
||||
"question": "Are you using Claude Code's worktree support for parallel development? How do you manage multiple tasks?",
|
||||
"category": "advanced"
|
||||
},
|
||||
{
|
||||
"emoji": "🔄",
|
||||
"question": "Do you use git hooks with Claude Code? Auto-formatting, linting, commit message validation?",
|
||||
"category": "hooks"
|
||||
},
|
||||
{
|
||||
"emoji": "💬",
|
||||
"question": "What's one thing you learned about Claude Code this week that surprised you?",
|
||||
"category": "learning"
|
||||
},
|
||||
{
|
||||
"emoji": "🏆",
|
||||
"question": "If you could teach one Claude Code trick to a beginner, what would it be?",
|
||||
"category": "tips"
|
||||
},
|
||||
{
|
||||
"emoji": "🎨",
|
||||
"question": "Do you customize your Claude Code statusline? What information do you display?",
|
||||
"category": "settings"
|
||||
},
|
||||
{
|
||||
"emoji": "🧪",
|
||||
"question": "Have you tried creating your own custom agents? What specialized task does your agent handle?",
|
||||
"category": "subagents"
|
||||
},
|
||||
{
|
||||
"emoji": "📋",
|
||||
"question": "How do you manage large refactoring tasks with Claude Code? TodoWrite, plan mode, or something else?",
|
||||
"category": "advanced"
|
||||
},
|
||||
{
|
||||
"emoji": "🌟",
|
||||
"question": "Rate your Claude Code experience from 1-10. What would make it a 10 for you?",
|
||||
"category": "feedback"
|
||||
}
|
||||
]
|
||||
QUESTIONS_EOF
|
||||
)
|
||||
|
||||
# Pick a random question
|
||||
TOTAL=$(echo "$QUESTIONS" | jq 'length')
|
||||
RANDOM_INDEX=$(( RANDOM % TOTAL ))
|
||||
SELECTED=$(echo "$QUESTIONS" | jq --argjson idx "$RANDOM_INDEX" '.[$idx]')
|
||||
|
||||
EMOJI=$(echo "$SELECTED" | jq -r '.emoji')
|
||||
QUESTION=$(echo "$SELECTED" | jq -r '.question')
|
||||
CATEGORY=$(echo "$SELECTED" | jq -r '.category')
|
||||
|
||||
# Color based on category
|
||||
case "$CATEGORY" in
|
||||
welcome) COLOR=3066993 ;; # green
|
||||
subagents) COLOR=5793266 ;; # blue
|
||||
commands) COLOR=16750848 ;; # orange
|
||||
hooks) COLOR=15277667 ;; # pink
|
||||
settings) COLOR=10181046 ;; # purple
|
||||
mcps) COLOR=1752220 ;; # teal
|
||||
skills) COLOR=15844367 ;; # gold
|
||||
tips) COLOR=3447003 ;; # dark blue
|
||||
projects) COLOR=2067276 ;; # dark green
|
||||
wishlist) COLOR=9807270 ;; # gray
|
||||
debugging) COLOR=15158332 ;; # red
|
||||
setup) COLOR=2123412 ;; # dark teal
|
||||
experience) COLOR=7419530 ;; # dark purple
|
||||
testing) COLOR=11342935 ;; # dark gold
|
||||
review) COLOR=2899536 ;; # green
|
||||
techstack) COLOR=3426654 ;; # blue-green
|
||||
security) COLOR=15158332 ;; # red
|
||||
docs) COLOR=5763719 ;; # indigo
|
||||
frontend) COLOR=15277667 ;; # pink
|
||||
database) COLOR=2067276 ;; # dark green
|
||||
productivity) COLOR=16750848;; # orange
|
||||
showcase) COLOR=15844367 ;; # gold
|
||||
team) COLOR=3447003 ;; # dark blue
|
||||
templates) COLOR=5793266 ;; # blue
|
||||
fun) COLOR=15844367 ;; # gold
|
||||
devops) COLOR=10181046 ;; # purple
|
||||
mobile) COLOR=1752220 ;; # teal
|
||||
learning) COLOR=3066993 ;; # green
|
||||
blog) COLOR=5763719 ;; # indigo
|
||||
help) COLOR=15158332 ;; # red
|
||||
advanced) COLOR=7419530 ;; # dark purple
|
||||
feedback) COLOR=16750848 ;; # orange
|
||||
*) COLOR=5793266 ;; # default blue
|
||||
esac
|
||||
|
||||
TIMESTAMP=$(date -u +%Y-%m-%dT%H:%M:%SZ)
|
||||
|
||||
# Build payload
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg emoji "$EMOJI" \
|
||||
--arg question "$QUESTION" \
|
||||
--argjson color "$COLOR" \
|
||||
--arg timestamp "$TIMESTAMP" \
|
||||
'{
|
||||
username: "Claude Code Templates",
|
||||
avatar_url: "https://aitmpl.com/img/logo.png",
|
||||
content: ($emoji + " **Daily Discussion** " + $emoji),
|
||||
embeds: [
|
||||
{
|
||||
description: ("### " + $question + "\n\n*Share your experience with the community! Every answer helps someone learn something new.*"),
|
||||
color: $color,
|
||||
footer: {
|
||||
text: "Daily Discussion • Claude Code Community",
|
||||
icon_url: "https://aitmpl.com/img/logo.png"
|
||||
},
|
||||
timestamp: $timestamp
|
||||
}
|
||||
]
|
||||
}')
|
||||
|
||||
# Send to Discord
|
||||
HTTP_STATUS=$(curl -s -o response.txt -w "%{http_code}" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" \
|
||||
"$DISCORD_WEBHOOK_URL")
|
||||
|
||||
if [ "$HTTP_STATUS" -ge 200 ] && [ "$HTTP_STATUS" -lt 300 ]; then
|
||||
echo "✅ Discord general engagement sent successfully!"
|
||||
echo "Question: ${EMOJI} ${QUESTION}"
|
||||
echo "Category: ${CATEGORY}"
|
||||
else
|
||||
echo "❌ Failed to send Discord notification (HTTP ${HTTP_STATUS})"
|
||||
cat response.txt
|
||||
exit 1
|
||||
fi
|
||||
env:
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL_GENERAL }}
|
||||
@@ -0,0 +1,40 @@
|
||||
name: Deploy to Cloudflare Pages
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [main]
|
||||
paths:
|
||||
- 'dashboard/**'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: deploy-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
deploy:
|
||||
runs-on: ubuntu-latest
|
||||
environment: production
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: 22
|
||||
- name: Install dependencies
|
||||
run: npm install
|
||||
working-directory: dashboard
|
||||
- name: Build
|
||||
run: npm run build
|
||||
working-directory: dashboard
|
||||
env:
|
||||
PUBLIC_CLERK_PUBLISHABLE_KEY: "pk_live_Y2xlcmsuYWl0bXBsLmNvbSQ"
|
||||
PUBLIC_GITHUB_CLIENT_ID: "Ov23li3KjfA4cuLi5c0k"
|
||||
PUBLIC_COMPONENTS_JSON_URL: "/components.json"
|
||||
- name: Deploy www + app.aitmpl.com
|
||||
run: npx wrangler pages deploy dist --project-name=aitmpl-dashboard --commit-dirty=true
|
||||
working-directory: dashboard
|
||||
env:
|
||||
CLOUDFLARE_API_TOKEN: ${{ secrets.CLOUDFLARE_API_TOKEN }}
|
||||
CLOUDFLARE_ACCOUNT_ID: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
|
||||
@@ -0,0 +1,81 @@
|
||||
name: Discord Release Notification
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
notify-discord:
|
||||
name: Send Discord Notification
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Send Discord Webhook
|
||||
env:
|
||||
RELEASE_NAME: ${{ github.event.release.name }}
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
RELEASE_URL: ${{ github.event.release.html_url }}
|
||||
RELEASE_AUTHOR: ${{ github.event.release.author.login }}
|
||||
RELEASE_AVATAR: ${{ github.event.release.author.avatar_url }}
|
||||
RELEASE_DATE: ${{ github.event.release.published_at }}
|
||||
RELEASE_BODY: ${{ github.event.release.body }}
|
||||
REPO_NAME: ${{ github.repository }}
|
||||
DISCORD_WEBHOOK_URL: ${{ secrets.DISCORD_WEBHOOK_URL }}
|
||||
run: |
|
||||
# Truncate body if too long (Discord limit is 4096 characters for description)
|
||||
TRUNCATED_BODY=$(printf '%s' "$RELEASE_BODY" | head -c 2000)
|
||||
BODY_LENGTH=$(printf '%s' "$RELEASE_BODY" | wc -c | tr -d ' ')
|
||||
if [ "$BODY_LENGTH" -gt 2000 ]; then
|
||||
TRUNCATED_BODY="${TRUNCATED_BODY}...\n\n[Read more](${RELEASE_URL})"
|
||||
fi
|
||||
|
||||
# Build payload safely with jq (no shell interpolation in JSON)
|
||||
PAYLOAD=$(jq -n \
|
||||
--arg tag "$RELEASE_TAG" \
|
||||
--arg body "$TRUNCATED_BODY" \
|
||||
--arg url "$RELEASE_URL" \
|
||||
--arg author "$RELEASE_AUTHOR" \
|
||||
--arg repo "$REPO_NAME" \
|
||||
--arg date "$RELEASE_DATE" \
|
||||
--arg avatar "$RELEASE_AVATAR" \
|
||||
'{
|
||||
username: "GitHub Release Bot",
|
||||
avatar_url: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png",
|
||||
embeds: [
|
||||
{
|
||||
title: ("🚀 New Release: " + $tag),
|
||||
description: $body,
|
||||
url: $url,
|
||||
color: 5814783,
|
||||
fields: [
|
||||
{ name: "📦 Version", value: $tag, inline: true },
|
||||
{ name: "👤 Author", value: $author, inline: true },
|
||||
{ name: "📚 Repository", value: ("[\($repo)](https://github.com/\($repo))"), inline: false },
|
||||
{ name: "📥 Installation", value: "```bash\nnpm install -g claude-code-templates\n# or with npx\nnpx claude-code-templates@latest --help\n```", inline: false }
|
||||
],
|
||||
footer: {
|
||||
text: "GitHub Release Notification",
|
||||
icon_url: "https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png"
|
||||
},
|
||||
timestamp: $date,
|
||||
thumbnail: { url: $avatar }
|
||||
}
|
||||
]
|
||||
}')
|
||||
|
||||
# Send webhook
|
||||
curl -sf -H "Content-Type: application/json" \
|
||||
-d "$PAYLOAD" \
|
||||
"$DISCORD_WEBHOOK_URL"
|
||||
|
||||
- name: Verify notification
|
||||
env:
|
||||
RELEASE_TAG: ${{ github.event.release.tag_name }}
|
||||
RELEASE_URL: ${{ github.event.release.html_url }}
|
||||
run: |
|
||||
echo "✅ Discord notification sent successfully!"
|
||||
echo "Release: $RELEASE_TAG"
|
||||
echo "URL: $RELEASE_URL"
|
||||
@@ -0,0 +1,80 @@
|
||||
name: Publish Package to GitHub Packages
|
||||
|
||||
on:
|
||||
release:
|
||||
types: [published]
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
version:
|
||||
description: 'Version to publish (e.g., patch, minor, major, or specific version like 1.15.0)'
|
||||
required: true
|
||||
default: 'patch'
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
packages: write
|
||||
|
||||
jobs:
|
||||
publish:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '18'
|
||||
registry-url: 'https://npm.pkg.github.com'
|
||||
scope: '@davila7'
|
||||
|
||||
- name: Configure package for GitHub Packages
|
||||
working-directory: ./cli-tool
|
||||
run: |
|
||||
# Update package.json for GitHub Packages
|
||||
npm pkg set name="@davila7/claude-code-templates"
|
||||
npm pkg set repository.type="git"
|
||||
npm pkg set repository.url="git+https://github.com/davila7/claude-code-templates.git"
|
||||
npm pkg set publishConfig.registry="https://npm.pkg.github.com"
|
||||
|
||||
echo "Updated package.json for GitHub Packages:"
|
||||
cat package.json
|
||||
|
||||
- name: Install dependencies
|
||||
working-directory: ./cli-tool
|
||||
run: npm ci --ignore-scripts
|
||||
|
||||
- name: Run tests
|
||||
working-directory: ./cli-tool
|
||||
run: npm test
|
||||
|
||||
- name: Get current version
|
||||
working-directory: ./cli-tool
|
||||
run: |
|
||||
# Use existing version from package.json (no bump)
|
||||
CURRENT_VERSION=$(node -p "require('./package.json').version")
|
||||
echo "NEW_VERSION=$CURRENT_VERSION" >> $GITHUB_ENV
|
||||
echo "Using version: $CURRENT_VERSION"
|
||||
|
||||
- name: Publish to GitHub Packages
|
||||
working-directory: ./cli-tool
|
||||
run: npm publish
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Summary
|
||||
run: |
|
||||
echo "🎉 Package Published to GitHub Packages!"
|
||||
echo "========================================"
|
||||
echo "Package: @davila7/claude-code-templates"
|
||||
echo "Version: ${{ env.NEW_VERSION }}"
|
||||
echo "Registry: GitHub Packages"
|
||||
echo ""
|
||||
echo "Install with:"
|
||||
echo "npm install -g @davila7/claude-code-templates --registry=https://npm.pkg.github.com"
|
||||
echo ""
|
||||
echo "Note: This workflow only publishes to GitHub Packages."
|
||||
echo "Main npm registry publication is handled manually."
|
||||
@@ -0,0 +1,114 @@
|
||||
name: Rust CI
|
||||
|
||||
# Runs ONLY when the Rust implementation changes. Checks formatting, lints, and
|
||||
# tests, then posts (and updates) a single sticky comment on the PR with the
|
||||
# results. The job status (the required check) reflects fmt + clippy + the
|
||||
# offline test suite; the network integration tests are informational only.
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'cli-rust/**'
|
||||
- '.github/workflows/rust-ci.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
|
||||
concurrency:
|
||||
group: rust-ci-${{ github.event.pull_request.number }}
|
||||
cancel-in-progress: true
|
||||
|
||||
jobs:
|
||||
test:
|
||||
name: fmt + clippy + test
|
||||
runs-on: ubuntu-latest
|
||||
defaults:
|
||||
run:
|
||||
working-directory: cli-rust
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
|
||||
- name: Install Rust toolchain
|
||||
uses: dtolnay/rust-toolchain@stable
|
||||
with:
|
||||
components: rustfmt, clippy
|
||||
|
||||
- name: Cache cargo build
|
||||
uses: Swatinem/rust-cache@v2
|
||||
with:
|
||||
workspaces: cli-rust
|
||||
|
||||
- name: Format check
|
||||
id: fmt
|
||||
continue-on-error: true
|
||||
run: cargo fmt --check
|
||||
|
||||
- name: Clippy
|
||||
id: clippy
|
||||
continue-on-error: true
|
||||
run: cargo clippy --all-targets -- -D warnings
|
||||
|
||||
- name: Test (offline)
|
||||
id: test
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: |
|
||||
set -o pipefail
|
||||
cargo test --no-fail-fast 2>&1 | tee "$GITHUB_WORKSPACE/rust-test-output.txt"
|
||||
|
||||
- name: Test (network integration)
|
||||
id: test_net
|
||||
continue-on-error: true
|
||||
shell: bash
|
||||
run: |
|
||||
set -o pipefail
|
||||
cargo test --no-fail-fast -- --ignored 2>&1 | tee "$GITHUB_WORKSPACE/rust-test-net-output.txt"
|
||||
|
||||
- name: Build PR comment
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
icon() { [ "$1" = "success" ] && echo "✅ Passed" || echo "❌ Failed"; }
|
||||
{
|
||||
echo "## 🦀 Rust CI"
|
||||
echo ""
|
||||
echo "| Check | Result |"
|
||||
echo "|---|---|"
|
||||
echo "| \`cargo fmt --check\` | $(icon '${{ steps.fmt.outcome }}') |"
|
||||
echo "| \`cargo clippy -D warnings\` | $(icon '${{ steps.clippy.outcome }}') |"
|
||||
echo "| \`cargo test\` (offline) | $(icon '${{ steps.test.outcome }}') |"
|
||||
echo "| \`cargo test -- --ignored\` (network) | $(icon '${{ steps.test_net.outcome }}') |"
|
||||
echo ""
|
||||
echo "<details><summary>Test summary</summary>"
|
||||
echo ""
|
||||
echo '```'
|
||||
grep -E "running [0-9]+ test|test result:" "$GITHUB_WORKSPACE/rust-test-output.txt" 2>/dev/null || echo "no offline output"
|
||||
echo "--- network ---"
|
||||
grep -E "running [0-9]+ test|test result:" "$GITHUB_WORKSPACE/rust-test-net-output.txt" 2>/dev/null || echo "no network output"
|
||||
echo '```'
|
||||
echo "</details>"
|
||||
echo ""
|
||||
echo "_Commit \`${GITHUB_SHA:0:7}\` • workflow run [#${GITHUB_RUN_ID}](${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}/actions/runs/${GITHUB_RUN_ID})_"
|
||||
} > "$GITHUB_WORKSPACE/rust-ci-comment.md"
|
||||
|
||||
- name: Post sticky PR comment
|
||||
if: always()
|
||||
uses: marocchino/sticky-pull-request-comment@v2
|
||||
with:
|
||||
header: rust-ci
|
||||
path: rust-ci-comment.md
|
||||
|
||||
- name: Enforce check status
|
||||
if: always()
|
||||
shell: bash
|
||||
run: |
|
||||
fmt='${{ steps.fmt.outcome }}'
|
||||
clippy='${{ steps.clippy.outcome }}'
|
||||
test='${{ steps.test.outcome }}'
|
||||
echo "fmt=$fmt clippy=$clippy test=$test (network is informational)"
|
||||
if [ "$fmt" != "success" ] || [ "$clippy" != "success" ] || [ "$test" != "success" ]; then
|
||||
echo "::error::One or more required Rust checks failed."
|
||||
exit 1
|
||||
fi
|
||||
echo "All required Rust checks passed."
|
||||
@@ -0,0 +1,64 @@
|
||||
name: Skill Security Scan (Full)
|
||||
|
||||
# Scans EVERY skill component with SkillSpector (NVIDIA, Apache-2.0) static
|
||||
# analysis. Runs weekly and on demand. Reports only — never blocks. Uploads
|
||||
# an aggregated SARIF to code scanning and a Markdown summary to the run.
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
schedule:
|
||||
- cron: '0 6 * * 1' # Mondays 06:00 UTC
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
skill-scan-all:
|
||||
name: SkillSpector (all skills)
|
||||
runs-on: ubuntu-latest
|
||||
timeout-minutes: 90
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install SkillSpector
|
||||
run: pip install "git+https://github.com/NVIDIA/skillspector.git@main"
|
||||
|
||||
- name: Run SkillSpector on all skills
|
||||
id: scan
|
||||
continue-on-error: true
|
||||
run: |
|
||||
python scripts/skillspector_scan.py \
|
||||
--all \
|
||||
--threshold 50 \
|
||||
--output-md skillspector-report.md \
|
||||
--output-sarif skillspector.sarif
|
||||
|
||||
- name: Publish summary
|
||||
if: always()
|
||||
run: cat skillspector-report.md >> "$GITHUB_STEP_SUMMARY"
|
||||
|
||||
- name: Upload report artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: skillspector-full-report
|
||||
path: |
|
||||
skillspector-report.md
|
||||
skillspector.sarif
|
||||
retention-days: 90
|
||||
|
||||
- name: Upload SARIF to code scanning
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: skillspector.sarif
|
||||
category: skillspector-full
|
||||
@@ -0,0 +1,103 @@
|
||||
name: Skill Security Scan
|
||||
|
||||
# Runs SkillSpector (NVIDIA, Apache-2.0) static analysis on skill components
|
||||
# changed in a PR. Posts a report comment and blocks the PR if any changed
|
||||
# skill scores HIGH/CRITICAL (risk score > 50).
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
paths:
|
||||
- 'cli-tool/components/skills/**'
|
||||
- 'scripts/skillspector_scan.py'
|
||||
- '.github/workflows/skill-security-scan.yml'
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
pull-requests: write
|
||||
security-events: write
|
||||
|
||||
jobs:
|
||||
skill-scan:
|
||||
name: SkillSpector (changed skills)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
fetch-depth: 0 # full history so git diff against the base ref works
|
||||
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v5
|
||||
with:
|
||||
python-version: '3.12'
|
||||
|
||||
- name: Install SkillSpector
|
||||
run: pip install "git+https://github.com/NVIDIA/skillspector.git@main"
|
||||
|
||||
- name: Run SkillSpector on changed skills
|
||||
id: scan
|
||||
continue-on-error: true
|
||||
run: |
|
||||
python scripts/skillspector_scan.py \
|
||||
--changed \
|
||||
--base-ref "origin/${{ github.base_ref }}" \
|
||||
--fail-on-risk \
|
||||
--threshold 50 \
|
||||
--output-md skillspector-report.md \
|
||||
--output-sarif skillspector.sarif
|
||||
|
||||
- name: Upload report artifacts
|
||||
if: always()
|
||||
uses: actions/upload-artifact@v7
|
||||
with:
|
||||
name: skillspector-report
|
||||
path: |
|
||||
skillspector-report.md
|
||||
skillspector.sarif
|
||||
retention-days: 30
|
||||
|
||||
- name: Upload SARIF to code scanning
|
||||
if: always()
|
||||
continue-on-error: true
|
||||
uses: github/codeql-action/upload-sarif@v3
|
||||
with:
|
||||
sarif_file: skillspector.sarif
|
||||
category: skillspector
|
||||
|
||||
- name: Comment PR with results
|
||||
if: always()
|
||||
uses: actions/github-script@v9
|
||||
with:
|
||||
script: |
|
||||
const fs = require('fs');
|
||||
const reportPath = 'skillspector-report.md';
|
||||
if (!fs.existsSync(reportPath)) {
|
||||
console.log('No SkillSpector report found.');
|
||||
return;
|
||||
}
|
||||
const body = fs.readFileSync(reportPath, 'utf8');
|
||||
const marker = '<!-- skillspector-report:v1 -->';
|
||||
const { owner, repo } = context.repo;
|
||||
const issue_number = context.issue.number;
|
||||
|
||||
const existing = await github.paginate(
|
||||
github.rest.issues.listComments,
|
||||
{ owner, repo, issue_number, per_page: 100 }
|
||||
);
|
||||
const prev = existing.find(c => c.body && c.body.includes(marker));
|
||||
if (prev) {
|
||||
await github.rest.issues.updateComment({
|
||||
owner, repo, comment_id: prev.id, body,
|
||||
});
|
||||
} else {
|
||||
await github.rest.issues.createComment({
|
||||
owner, repo, issue_number, body,
|
||||
});
|
||||
}
|
||||
|
||||
- name: Enforce gate
|
||||
if: steps.scan.outputs.failed == 'true'
|
||||
run: |
|
||||
echo "::error::SkillSpector found HIGH/CRITICAL findings in changed skills (${{ steps.scan.outputs.high_critical_count }})."
|
||||
exit 1
|
||||
@@ -0,0 +1,54 @@
|
||||
name: Update Star History
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Every Monday at 4:00 AM UTC
|
||||
- cron: '0 4 * * 1'
|
||||
workflow_dispatch: # Allow manual runs from the GitHub UI
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-star-history:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install requests
|
||||
|
||||
- name: Generate star-history.svg
|
||||
run: python scripts/generate_star_history.py
|
||||
env:
|
||||
# The repo's own GITHUB_TOKEN can read its own stargazers, which
|
||||
# GitHub now restricts to the repository's admins and collaborators.
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
git diff --quiet docs/star-history.svg || echo "changed=true" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit and push changes
|
||||
if: steps.check_changes.outputs.changed == 'true'
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add docs/star-history.svg
|
||||
git commit -m "chore: Update star history chart
|
||||
|
||||
🤖 Generated with [GitHub Actions](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
||||
git push
|
||||
@@ -0,0 +1,83 @@
|
||||
name: Update JSON Data
|
||||
|
||||
on:
|
||||
schedule:
|
||||
# Ejecutar todos los días a las 3:00 AM UTC
|
||||
- cron: '0 3 * * *'
|
||||
workflow_dispatch: # Permite ejecutar manualmente desde GitHub UI
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
|
||||
jobs:
|
||||
update-json:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v6
|
||||
with:
|
||||
token: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
- name: Set up Python
|
||||
uses: actions/setup-python@v6
|
||||
with:
|
||||
python-version: '3.11'
|
||||
cache: 'pip'
|
||||
|
||||
- name: Set up Node.js
|
||||
uses: actions/setup-node@v6
|
||||
with:
|
||||
node-version: '20'
|
||||
cache: 'npm'
|
||||
cache-dependency-path: 'cli-tool/package-lock.json'
|
||||
|
||||
- name: Install Python dependencies
|
||||
run: |
|
||||
pip install --upgrade pip
|
||||
pip install requests python-dotenv
|
||||
|
||||
- name: Install Node.js dependencies
|
||||
run: |
|
||||
cd cli-tool
|
||||
npm ci --ignore-scripts
|
||||
|
||||
- name: Create .env file with secrets
|
||||
run: |
|
||||
echo "SUPABASE_URL=${{ secrets.SUPABASE_URL }}" >> .env
|
||||
echo "SUPABASE_API_KEY=${{ secrets.SUPABASE_API_KEY }}" >> .env
|
||||
|
||||
- name: Generate components.json
|
||||
run: python scripts/generate_components_json.py
|
||||
env:
|
||||
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
||||
SUPABASE_API_KEY: ${{ secrets.SUPABASE_API_KEY }}
|
||||
|
||||
- name: Generate trending-data.json
|
||||
run: python scripts/generate_trending_data.py
|
||||
env:
|
||||
SUPABASE_URL: ${{ secrets.SUPABASE_URL }}
|
||||
SUPABASE_API_KEY: ${{ secrets.SUPABASE_API_KEY }}
|
||||
|
||||
- name: Generate claude-jobs.json
|
||||
run: python scripts/generate_claude_jobs.py
|
||||
|
||||
- name: Copy data to dashboard
|
||||
run: |
|
||||
cp docs/claude-jobs.json dashboard/public/claude-jobs.json
|
||||
|
||||
- name: Check for changes
|
||||
id: check_changes
|
||||
run: |
|
||||
git diff --quiet docs/components.json docs/trending-data.json docs/claude-jobs.json dashboard/public/claude-jobs.json || echo "changed=true" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Commit and push changes
|
||||
if: steps.check_changes.outputs.changed == 'true'
|
||||
run: |
|
||||
git config --local user.email "github-actions[bot]@users.noreply.github.com"
|
||||
git config --local user.name "github-actions[bot]"
|
||||
git add docs/components.json docs/trending-data.json docs/claude-jobs.json dashboard/public/claude-jobs.json
|
||||
git commit -m "chore: Update components and trending data
|
||||
|
||||
🤖 Generated with [GitHub Actions](https://github.com/${{ github.repository }}/actions/runs/${{ github.run_id }})"
|
||||
git push
|
||||
Reference in New Issue
Block a user