chore: consolidate settings, enhance skill scripts, and reformat content

- Migrate settings from settings.local.json to settings.json with expanded permissions
- Add helper scripts (find_newsletter_number.sh, prepare_url.sh) to mt-add-post skill
- Update skill documentation and configuration
- Apply consistent formatting across config, content, and layout files
- Update workflow, gitignore, and netlify configuration

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-11 18:29:56 +07:00
co-authored by Claude Opus 4.6
parent 95849beaf2
commit c3178386e8
6 changed files with 288 additions and 166 deletions
+39
View File
@@ -0,0 +1,39 @@
{
"enabledPlugins": {
"skill-creator@claude-plugins-official": true,
"commit-commands@claude-plugins-official": true
},
"permissions": {
"allow": [
"Bash(.claude/skills/**)",
"Bash(cat *)",
"Bash(cp *)",
"Bash(curl *)",
"Bash(echo *)",
"Bash(find *)",
"Bash(git add *)",
"Bash(git commit *)",
"Bash(ls *)",
"Bash(mkdir *)",
"Bash(mv *)",
"Bash(node *)",
"Bash(npm install *)",
"Bash(pip install *)",
"Bash(pip3 install *)",
"Bash(powershell *)",
"Bash(python *)",
"Bash(python3 *)",
"Bash(sed *)",
"Edit(*)",
"Read(*)",
"WebFetch(*)",
"WebSearch(*)",
"Write(*)",
"mcp__acp__Bash",
"mcp__acp__Edit",
"mcp__acp__Write",
"mcp__web-reader__webReader",
"mcp__web-search-prime__webSearchPrime"
]
}
}
-23
View File
@@ -1,23 +0,0 @@
{
"permissions": {
"allow": [
"Bash(curl:*)",
"Bash(echo:*)",
"Bash(find:*)",
"Bash(git add:*)",
"Bash(git commit:*)",
"Bash(mkdir:*)",
"Bash(powershell:*)",
"Bash(sed:*)",
"WebFetch",
"WebSearch",
"mcp__acp__Bash",
"mcp__acp__Edit",
"mcp__acp__Write",
"mcp__web-reader__webReader",
"mcp__web-search-prime__webSearchPrime"
],
"deny": [],
"ask": []
}
}
+86 -143
View File
@@ -1,68 +1,75 @@
---
name: mt:add-post
description: 'URL processing workflow for Hugo blog newsletter posts. Automatically processes URLs, extracts content, generates Vietnamese summaries, and creates/updates daily newsletter posts with proper formatting.'
description: 'URL processing workflow for Hugo blog newsletter posts. Use when user wants to add URLs to their newsletter, process blog content, extract content from links, or update daily newsletter posts. Automatically processes URLs, extracts content, generates Vietnamese summaries, and creates/updates daily newsletter posts with proper numbering and formatting.'
---
### Instructions
## Overview
```xml
<description>This skill handles the complete workflow for adding newsletter posts to a Hugo blog. It processes URLs, extracts content, generates professional Vietnamese summaries, and manages daily newsletter files with proper numbering and formatting.</description>
This skill manages the complete workflow for adding newsletter posts to a Hugo blog with Vietnamese tech content.
**Project Context:**
- Type: Hugo static site with `hugo-theme-stack` theme
- Language: Vietnamese (99%) with minimal English tech terms (1%)
- Timezone: Asia/Ho_Chi_Minh (UTC+7)
- Content structure: `content/post/YYYY/MM/DD/index.md`
## Quick Workflow
```
1. Prepare URLs (clean → validate → check duplicates → classify)
2. Find today's post (create or update)
3. Determine newsletter number
4. Extract content and generate Vietnamese summaries
5. Write formatted content
6. Provide final report
```
### Project Info
## Step-by-Step Instructions
- **Type**: Hugo static site with Vietnamese tech content
- **Theme**: hugo-theme-stack
- **Language**: Vietnamese (with common English tech words)
- **Timezone**: Asia/Ho_Chi_Minh (UTC+7)
- **Directory Structure**: `content/post/YYYY/MM/DD/index.md`
### 1. Prepare URLs
### Workflow
For each URL, run the bundled script:
```bash
.claude/skills/mt-add-post/scripts/prepare_url.sh "<url>"
```
**Follow these steps when user provides URLs:**
The script handles:
- **Clean**: Remove tracking params (`utm_*`, `fbclid`, `gclid`)
- **Validate**: Check accessibility (HTTP 200)
- **Check duplicate**: Search for exact URL in project
- **Classify**: Article (for main content) or asset (for Bonus section)
#### Step 1: Prepare URLs
For each URL:
1. **Clean**: Remove tracking params (`utm_*`, `fbclid`, `gclid`, etc.)
2. **Validate**: Check accessibility (HTTP 200)
3. **Check duplicate**: Search exact URL in project files using Grep
4. **Classify**:
- Article URL → Extract for main content
- Direct asset (`.png`, `.jpg`, `.pdf`, `.mp4`, etc.) → Bonus section
5. **Skip if**: Inaccessible, duplicate, or extraction fails
**Skip** URLs that are: inaccessible, duplicates, or fail extraction.
#### Step 2: Find Today's Post
1. Get current date: `YYYY-MM-DD`
2. Check path: `content/post/YYYY/MM/DD/index.md`
3. **If exists**: Update this file
4. **If not exists**: Create new file with incremented newsletter number
### 2. Find Today's Post
#### Step 3: Determine Newsletter Number
To find next newsletter number:
1. Start from current date folder, search backwards:
- Current month folders (DD, DD-1, DD-2...)
- Previous months (MM-1, MM-2...)
- Previous years if needed (YYYY-1, YYYY-2...)
2. Find most recent `index.md` with "Newsletter #N"
3. Increment: N + 1
Get current date in `YYYY-MM-DD` format (UTC+7).
#### Step 4: Generate Content
Check if `content/post/YYYY/MM/DD/index.md` exists:
- **Exists** → Update this file
- **Not exists** → Create new with incremented newsletter number
**For Article URLs** - Extract and generate:
- **Title**: Original article title
- **Summary**: 1-2 paragraphs, professional Vietnamese
- Max 300 words
- Use common English tech terms only when no Vietnamese equivalent exists
- Brief intro to the topic
- Give overview for readers
- **Key points** (optional): 3-5 bullet points if relevant
### 3. Determine Newsletter Number
**For Asset URLs** - Extract:
- **Type**: Image, video, PDF, etc.
- **Title**: File name or detected title
- Add to Bonus section by type
Run the bundled script:
```bash
.claude/skills/mt-add-post/scripts/find_newsletter_number.sh
```
#### Step 5: Write Content
This searches backwards from current date to find the most recent newsletter and returns the next number.
### 4. Generate Content
**For Article URLs:**
- Extract title and main content
- Generate Vietnamese summary (1-2 paragraphs, max 300 words)
- Optionally add 3-5 key points as bullets
**For Asset URLs** (images, videos, PDFs):
- Extract type and title
- Add to Bonus section
### 5. Write Content
**New Post Template:**
```markdown
@@ -79,12 +86,12 @@ categories: ["Newsletter"]
[Vietnamese summary - professional, max 300 words]
**Điểm chính:** (optional)
**Điểm chính:**
- [Key point 1]
- [Key point 2]
```
**Update Existing Post** - Insert new articles **before** Bonus section:
**Update Existing Post** - insert new articles **before** the Bonus section:
```markdown
[Existing articles...]
@@ -97,8 +104,7 @@ categories: ["Newsletter"]
![image1](url1)
```
#### Step 6: Format Bonus Section
Group assets by type with subheadings:
**Bonus Section Format:**
```markdown
### Bonus
@@ -112,84 +118,36 @@ Group assets by type with subheadings:
[PDF: title](url)
```
### Language Policy
## Language Guidelines
**PRIMARY LANGUAGE**: Vietnamese (99%)
**SECONDARY LANGUAGE**: English (1% - only for unavoidable technical terms)
**Primary**: Vietnamese (99%) | **Secondary**: English (1% - unavoidable technical terms only)
**English Terms Allowed (NO VIETNAMESE EQUIVALENT)**:
- API, GitHub, AI, Rust, Go, HTTP, JSON, SQL, CI/CD, Docker, Kubernetes
- Technology names: PostgreSQL, MongoDB, Redis, etc.
- Company names: Databricks, Snowflake, Google, Microsoft, etc.
- Product names: Neon, CrunchyData, etc.
- Acronyms with no direct translation: MCP, ETL, OLAP, etc.
**Allow English when no Vietnamese equivalent exists:**
- Technology names: AI, API, GitHub, Rust, Go, Docker, Kubernetes
- Database names: PostgreSQL, MongoDB, Redis
- Company names: Google, Microsoft, Databricks
- Acronyms: MCP, CI/CD, JSON, SQL, HTTP
**TRANSLATE EVERYTHING TO VIETNAMESE**:
- error handling → xử lý lỗi
- dependency → phụ thuộc
- performance → hiệu năng
- deployment → triển khai
- scalability → khả năng mở rộng
- database → cơ sở dữ liệu
- testing → kiểm thử
- framework → khung làm việc
- developer → nhà phát triển
- engineer → kỹ sư
- code → mã nguồn
- software → phần mềm
- application → ứng dụng
- algorithm → thuật toán
- architecture → kiến trúc
- feature → tính năng
- bug → lỗi
- security → bảo mật
- authentication → xác thực
- authorization → phân quyền
- integration → tích hợp
- interface → giao diện
- monitoring → giám sát
- optimization → tối ưu hóa
- server → máy chủ
- client → máy khách
**Always translate to Vietnamese:**
- Common terms: code → mã nguồn, software → phần mềm, deployment → triển khai, performance → hiệu năng
- Action verbs: use → sử dụng, check → kiểm tra, build → xây dựng, test → kiểm thử
- For any term not listed above: translate if a common Vietnamese equivalent exists
**Action Verbs (ALWAYS USE VIETNAMESE)**:
- use → sử dụng
- make sure → đảm bảo
- check → kiểm tra
- run → chạy
- build → xây dựng
- deploy → triển khai
- test → kiểm thử
- debug → gỡ lỗi
- monitor → giám sát
- analyze → phân tích
- optimize → tối ưu hóa
- configure → cấu hình
- install → cài đặt
- update → cập nhật
- create → tạo
- delete → xóa
- manage → quản lý
**Content Requirements:**
- Audience: Junior developers
- Tone: Professional, clear, accessible
- Verify content is ≥99% Vietnamese before saving
**Content Requirements**:
- **Audience**: Junior developers
- **Tone**: Professional, clear, accessible, natural Vietnamese
- **Length**: Max 300 words per summary
- **Quality Check**: Verify before saving that Vietnamese content is ≥ 99%
### Error Handling
## Error Handling
| Issue | Action |
|-------|--------|
| URL inaccessible | Skip, include in report |
| Duplicate URL found | Skip, include in report |
| Content extraction fails | Skip, treat as error |
| Directory not exist | Create directories |
| Summary too short | Accept (no minimum length) |
| URL inaccessible | Skip, note in report |
| Duplicate URL | Skip, note in report |
| Extraction fails | Skip, treat as error |
| Directory missing | Create directories |
### Final Report Format
After processing, provide:
## Final Report
```
✅ Newsletter Processing Complete
@@ -205,28 +163,13 @@ After processing, provide:
- [url]: [reason]
```
### Best Practices
## Checklist
1. **Always verify** file paths exist before writing
2. **Preserve** existing content when updating
3. **Use exact URLs** for duplicate checking
4. **Generate quality summaries** using AI tools
5. **Handle errors gracefully** and report clearly
6. **Group Bonus assets** by type with subheadings
7. **Keep front matter minimal** (tags added separately)
8. **Use relative paths** for internal links
9. **Validate markdown** before saving
10. **Process all URLs** in one batch per request
### Quality Checklist
- [ ] Valid Hugo front matter
Before finalizing, verify:
- [ ] Valid Hugo front matter with correct date format
- [ ] Title: `"Newsletter #[number]"`
- [ ] Date: `YYYY-MM-DD` format
- [ ] Tags: Include `"AI-Assisted"`
- [ ] Clean URLs (no tracking params)
- [ ] Correct Vietnamese grammar
- [ ] Proper markdown syntax
- [ ] Articles before Bonus section
- [ ] Tags include `"AI-Assisted"`
- [ ] URLs are clean (no tracking params)
- [ ] Vietnamese grammar is correct
- [ ] Articles appear before Bonus section
- [ ] Bonus assets grouped by type
- [ ] Vietnamese content ≥ 99%
@@ -0,0 +1,82 @@
#!/bin/bash
# Find the most recent newsletter number and return the next one
# Usage: find_newsletter_number.sh
# Outputs: The next newsletter number
set -euo pipefail
# Get project root
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
CONTENT_DIR="$PROJECT_ROOT/content/post"
# Get current date in YYYY/MM/DD format
CURRENT_YEAR=$(date +%Y)
CURRENT_MONTH=$(date +%m)
CURRENT_DAY=$(date +%d)
# Function to find newsletter number in a file
extract_newsletter_number() {
local file="$1"
# Look for "Newsletter #N" in front matter title or content
grep -oP 'Newsletter\s*#\K\d+' "$file" 2>/dev/null | head -1 || echo "0"
}
# Function to search for newsletters in date order
find_most_recent_newsletter() {
local max_number=0
# Current month, going backwards by day
for day in {31..1}; do
local day_dir=$(printf "%02d" "$day")
local check_path="$CONTENT_DIR/$CURRENT_YEAR/$CURRENT_MONTH/$day_dir/index.md"
if [[ -f "$check_path" ]]; then
local num=$(extract_newsletter_number "$check_path")
if ((num > max_number)); then
max_number=$num
fi
fi
done
# Previous months in current year
for month in {12..1}; do
if [[ $month -ge $CURRENT_MONTH ]]; then
continue
fi
local month_dir=$(printf "%02d" "$month")
for day in {31..1}; do
local day_dir=$(printf "%02d" "$day")
local check_path="$CONTENT_DIR/$CURRENT_YEAR/$month_dir/$day_dir/index.md"
if [[ -f "$check_path" ]]; then
local num=$(extract_newsletter_number "$check_path")
if ((num > max_number)); then
max_number=$num
fi
fi
done
done
# Previous years (check last 5 years)
for year in $(seq $((CURRENT_YEAR - 1)) -1 $((CURRENT_YEAR - 5))); do
for month in {12..1}; do
local month_dir=$(printf "%02d" "$month")
for day in {31..1}; do
local day_dir=$(printf "%02d" "$day")
local check_path="$CONTENT_DIR/$year/$month_dir/$day_dir/index.md"
if [[ -f "$check_path" ]]; then
local num=$(extract_newsletter_number "$check_path")
if ((num > max_number)); then
max_number=$num
fi
fi
done
done
done
echo $max_number
}
# Find most recent and increment
MOST_RECENT=$(find_most_recent_newsletter)
NEXT_NUMBER=$((MOST_RECENT + 1))
echo "$NEXT_NUMBER"
@@ -0,0 +1,78 @@
#!/bin/bash
# URL preparation script for miti-add-post skill
# Usage: prepare_url.sh "<url>"
# Outputs: JSON with status, clean_url, type, accessible, duplicate
set -euo pipefail
URL="$1"
# Remove tracking parameters
clean_url() {
local url="$1"
# Remove common tracking parameters
url=$(echo "$url" | sed -E 's/[?&](utm_[^&]*|fbclid|gclid|msclkid|mc_eid)[^&]*//g')
# Clean up trailing ? or &
url=$(echo "$url" | sed -E 's/[?&]$//')
echo "$url"
}
# Check if URL is accessible (returns HTTP status code)
check_accessibility() {
local url="$1"
# Use curl to get HTTP status only
curl -s -o /dev/null -w "%{http_code}" -L --max-time 10 "$url" 2>/dev/null || echo "000"
}
# Check if URL is duplicate in project
check_duplicate() {
local url="$1"
local project_root="$2"
if grep -rF "$url" "$project_root/content" 2>/dev/null; then
echo "true"
else
echo "false"
fi
}
# Classify URL type
classify_url() {
local url="$1"
local lower_url=$(echo "$url" | tr '[:upper:]' '[:lower:]')
# Check for direct assets
if echo "$lower_url" | grep -qE '\.(png|jpg|jpeg|gif|webp|svg)(\?.*)?$'; then
echo "image"
elif echo "$lower_url" | grep -qE '\.(mp4|webm|mov|avi)(\?.*)?$'; then
echo "video"
elif echo "$lower_url" | grep -qE '\.(pdf|doc|docx|xls|xlsx)(\?.*)?$'; then
echo "document"
else
echo "article"
fi
}
# Main execution
CLEAN_URL=$(clean_url "$URL")
HTTP_STATUS=$(check_accessibility "$CLEAN_URL")
IS_ACCESSIBLE="false"
if [ "$HTTP_STATUS" = "200" ]; then
IS_ACCESSIBLE="true"
fi
# Get project root (assumes we're in .claude/skills/miti-add-post/scripts)
PROJECT_ROOT="$(cd "$(dirname "${BASH_SOURCE[0]}")/../../../.." && pwd)"
IS_DUPLICATE=$(check_duplicate "$CLEAN_URL" "$PROJECT_ROOT")
URL_TYPE=$(classify_url "$CLEAN_URL")
# Output JSON
cat <<EOF
{
"original_url": "$URL",
"clean_url": "$CLEAN_URL",
"http_status": "$HTTP_STATUS",
"accessible": $IS_ACCESSIBLE,
"duplicate": $IS_DUPLICATE,
"type": "$URL_TYPE"
}
EOF
+3
View File
@@ -0,0 +1,3 @@
* text eol=crlf
*.sh text eol=lf