feat: add claude-md-guardian, thinking commands, and security fixes
- Add claude-md-guardian agent with SessionStart hook for auto CLAUDE.md maintenance - Add 6 thinking commands: /my-world, /emerge, /challenge, /connect, /today, /close - Add my-world skill for one-shot vault context loading - Fix command injection vulnerability in init-vault.sh (use env vars) - Add error handling and logging to vault-sync.sh - Update write-article skill with complete frontmatter fields - Upgrade CLAUDE.md to v1.3.0 with cycle time tracking and exit criteria Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
parent
c3209dfa68
commit
29bcbb89db
81
.claude/agents/claude-md-guardian.md
Normal file
81
.claude/agents/claude-md-guardian.md
Normal file
@ -0,0 +1,81 @@
|
||||
# CLAUDE.md Guardian Agent
|
||||
|
||||
后台 agent,监控项目变化并自动维护 CLAUDE.md 文件。
|
||||
|
||||
## 触发条件
|
||||
|
||||
- Session 启动时(通过 SessionStart hook)
|
||||
- 重大里程碑后(feature 完成、重构、新依赖、架构变化)
|
||||
|
||||
## 监控范围
|
||||
|
||||
| 变化类型 | 检测方式 | 更新内容 |
|
||||
|----------|----------|----------|
|
||||
| 目录结构变化 | 对比 `.claude/guardian/structure.json` | 项目结构图 |
|
||||
| 依赖变化 | package.json, requirements.txt 等 | Tech Stack 部分 |
|
||||
| 新增 skill | `.claude/skills/` 目录变化 | Skills 清单 |
|
||||
| 新增命令 | `.claude/commands/` 目录变化 | 命令列表 |
|
||||
| 架构变化 | 手动标记 | 架构说明 |
|
||||
|
||||
## 工作流程
|
||||
|
||||
```
|
||||
SessionStart Hook
|
||||
↓
|
||||
检查 .claude/guardian/snapshot.json 是否存在
|
||||
↓
|
||||
├─ 不存在 → 首次运行,创建快照,不更新
|
||||
└─ 存在 → 对比当前状态与快照
|
||||
↓
|
||||
├─ 无变化 → 静默退出
|
||||
└─ 有变化 → 生成更新建议,询问用户
|
||||
```
|
||||
|
||||
## 快照格式
|
||||
|
||||
```json
|
||||
{
|
||||
"last_check": "2026-03-02T10:00:00+08:00",
|
||||
"structure_hash": "abc123",
|
||||
"skills": ["write-article", "my-world"],
|
||||
"commands": ["/today", "/close", "/emerge", "/challenge", "/connect", "/my-world"],
|
||||
"dependencies": {
|
||||
"bash": ["obsidian-cli"],
|
||||
"obsidian_plugins": ["dataview", "templater-obsidian", "note-to-mp", "obsidian-kanban"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 更新原则
|
||||
|
||||
1. **保守更新**:只添加缺失信息,不删除现有内容
|
||||
2. **用户确认**:重大变化前询问用户
|
||||
3. **版本追踪**:每次更新记录版本号和日期
|
||||
4. **保持简洁**:CLAUDE.md 控制在 400 行以内
|
||||
|
||||
## 配置文件
|
||||
|
||||
项目级 settings.json 中配置 hook:
|
||||
|
||||
```json
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash .claude/hooks/guardian-check.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## 版本
|
||||
|
||||
- **版本**: 1.0.0
|
||||
- **创建日期**: 2026-03-02
|
||||
- **适用项目**: content-forge
|
||||
20
.claude/commands/challenge.md
Normal file
20
.claude/commands/challenge.md
Normal file
@ -0,0 +1,20 @@
|
||||
Pressure-test beliefs using the vault's own history.
|
||||
|
||||
1. Read all vault notes:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian files
|
||||
```
|
||||
|
||||
2. For each note, read its content:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian read path="<file-path>"
|
||||
```
|
||||
|
||||
3. Find contradictions, tensions, and counter-evidence WITHIN the notes. Where does one note say something that conflicts with another? Where are the blind spots?
|
||||
|
||||
4. Output format:
|
||||
- **Tension Found**: [description]
|
||||
- **Evidence from notes**: [specific quotes with note names]
|
||||
- **The challenge**: [question that forces reconciliation]
|
||||
71
.claude/commands/close.md
Normal file
71
.claude/commands/close.md
Normal file
@ -0,0 +1,71 @@
|
||||
# 晚间仪式:收尾与复盘
|
||||
|
||||
每天结束前执行,确保工作有始有终。
|
||||
|
||||
## Step 1: 检查今日完成
|
||||
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
# 查看今天修改的笔记
|
||||
find . -name "*.md" -type f ! -path "./.obsidian/*" ! -path "./.git/*" -mtime 0
|
||||
```
|
||||
|
||||
## Step 2: 更新看板状态
|
||||
|
||||
将完成的工作移动到正确阶段:
|
||||
|
||||
```bash
|
||||
# 如果有草稿完成,移到审核
|
||||
obsidian move path="02-drafts/xxx.md" to="03-review"
|
||||
obsidian property:set path="03-review/xxx.md" name="status" value="review"
|
||||
```
|
||||
|
||||
## Step 3: 同步到 Git
|
||||
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
git add -A
|
||||
git commit -m "session: $(date +%Y-%m-%d) content updates"
|
||||
git push
|
||||
```
|
||||
|
||||
## Step 4: 更新 progress.md
|
||||
|
||||
在项目根目录的 `progress.md` 中添加今日记录:
|
||||
|
||||
```markdown
|
||||
## Session: YYYY-MM-DD
|
||||
|
||||
### 完成项
|
||||
- [ ] 任务1
|
||||
- [ ] 任务2
|
||||
|
||||
### 文件变更
|
||||
- 新建: path/to/file.md
|
||||
- 修改: path/to/another.md
|
||||
|
||||
### 明日 TODO
|
||||
1. 待办1
|
||||
2. 待办2
|
||||
```
|
||||
|
||||
## Step 5: 输出日报
|
||||
|
||||
```
|
||||
🌙 晚间复盘 - YYYY-MM-DD
|
||||
|
||||
✅ 今日完成
|
||||
- X 篇文章推进到 Y 阶段
|
||||
- Z 个文件变更
|
||||
|
||||
📈 管道变化
|
||||
- 进: X 篇新素材
|
||||
- 出: Y 篇发布
|
||||
- 净变化: +N/-M
|
||||
|
||||
📋 明日待办
|
||||
1. [首要任务]
|
||||
2. [次要任务]
|
||||
|
||||
💾 Git 状态: 已同步 ✅
|
||||
```
|
||||
21
.claude/commands/connect.md
Normal file
21
.claude/commands/connect.md
Normal file
@ -0,0 +1,21 @@
|
||||
Bridge two disparate topics using the vault's link graph.
|
||||
|
||||
1. Read all vault notes:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian files
|
||||
```
|
||||
|
||||
2. For each note, read its content:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian read path="<file-path>"
|
||||
```
|
||||
|
||||
3. Pick two notes that seem unrelated and find the hidden bridge between them. What do they share that isn't obvious? What would combining their insights produce?
|
||||
|
||||
4. Output format:
|
||||
- **Domain A**: [note/topic]
|
||||
- **Domain B**: [note/topic]
|
||||
- **The bridge**: [unexpected connection]
|
||||
- **The insight**: [what this connection means]
|
||||
23
.claude/commands/emerge.md
Normal file
23
.claude/commands/emerge.md
Normal file
@ -0,0 +1,23 @@
|
||||
Surface hidden patterns — ideas the vault implies but were never explicitly stated.
|
||||
|
||||
1. Read all vault notes to understand the full knowledge landscape:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian files
|
||||
obsidian tags counts sort=count
|
||||
obsidian orphans
|
||||
obsidian deadends
|
||||
```
|
||||
|
||||
2. For each note with content, read it:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian read path="<file-path>"
|
||||
```
|
||||
|
||||
3. Analyze: look for conclusions from scattered premises, unnamed patterns across notes, themes circled around but never articulated, unarticulated directions thinking is heading.
|
||||
|
||||
4. Output format for each pattern found:
|
||||
- **Emergent Pattern**: [description]
|
||||
- **Evidence trail**: [which notes contribute to this pattern]
|
||||
- **The unstated idea**: [what the notes imply but was never written down]
|
||||
30
.claude/commands/my-world.md
Normal file
30
.claude/commands/my-world.md
Normal file
@ -0,0 +1,30 @@
|
||||
Load my personal context from the content-forge Obsidian vault.
|
||||
|
||||
1. cd to vault directory and scan pipeline state:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian vault
|
||||
obsidian files folder="00-inbox"
|
||||
obsidian files folder="01-topics"
|
||||
obsidian files folder="02-drafts"
|
||||
obsidian files folder="03-review"
|
||||
obsidian files folder="04-published"
|
||||
```
|
||||
|
||||
2. Map the knowledge graph:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian tags counts sort=count
|
||||
obsidian orphans
|
||||
obsidian deadends
|
||||
obsidian unresolved
|
||||
obsidian recents
|
||||
```
|
||||
|
||||
3. For each active item in inbox/topics/drafts/review, read its frontmatter:
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian properties path="<file-path>"
|
||||
```
|
||||
|
||||
4. Summarize: what's in the pipeline, what's connected, what needs attention? Reference specific notes.
|
||||
77
.claude/commands/today.md
Normal file
77
.claude/commands/today.md
Normal file
@ -0,0 +1,77 @@
|
||||
# 晨间仪式:今日工作扫描
|
||||
|
||||
执行以下步骤,为今天的工作做准备。
|
||||
|
||||
## Step 1: 加载 Vault 概况
|
||||
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian files total
|
||||
```
|
||||
|
||||
## Step 2: 扫描看板状态
|
||||
|
||||
检查各阶段 WIP 状态:
|
||||
|
||||
```bash
|
||||
# 灵感/素材
|
||||
obsidian search query="status:inbox" 2>/dev/null | wc -l
|
||||
|
||||
# 选题
|
||||
obsidian search query="status:topic" 2>/dev/null | wc -l
|
||||
|
||||
# 写作中
|
||||
obsidian search query="status:draft" 2>/dev/null | wc -l
|
||||
|
||||
# 审核
|
||||
obsidian search query="status:review" 2>/dev/null | wc -l
|
||||
|
||||
# 发布中
|
||||
obsidian search query="status:publishing" 2>/dev/null | wc -l
|
||||
```
|
||||
|
||||
## Step 3: 识别超期项目
|
||||
|
||||
检查 `updated_at` 超过 7 天的笔记:
|
||||
|
||||
```bash
|
||||
find . -name "*.md" -type f ! -path "./.obsidian/*" ! -path "./.git/*" -mtime +7 | head -10
|
||||
```
|
||||
|
||||
## Step 4: 输出今日建议
|
||||
|
||||
根据扫描结果,输出:
|
||||
|
||||
1. **管道状态**:各阶段数量 + WIP 限制对比
|
||||
2. **超期提醒**:超过 7 天未更新的笔记列表
|
||||
3. **今日焦点**:基于 WIP 和超期情况,建议今天优先处理的 1-3 项
|
||||
|
||||
## WIP 限制参考
|
||||
|
||||
| 阶段 | WIP 限制 |
|
||||
|------|----------|
|
||||
| 选题 | 3 |
|
||||
| 写作 | 3 |
|
||||
| 配图/格式化 | 2 |
|
||||
| 审核 | 2 |
|
||||
| 发布 | 2 |
|
||||
|
||||
## 输出格式
|
||||
|
||||
```
|
||||
📅 今日扫描 - YYYY-MM-DD
|
||||
|
||||
📊 管道状态
|
||||
灵感/素材: X (无限制)
|
||||
选题: X/3 [⚠️ 超限] 或 [✅ 正常]
|
||||
写作: X/3
|
||||
审核: X/2
|
||||
发布: X/2
|
||||
|
||||
⚠️ 超期提醒 (7天+)
|
||||
- path/to/note.md (最后更新: X天前)
|
||||
|
||||
🎯 今日建议
|
||||
1. [具体建议]
|
||||
2. [具体建议]
|
||||
```
|
||||
109
.claude/hooks/guardian-check.sh
Executable file
109
.claude/hooks/guardian-check.sh
Executable file
@ -0,0 +1,109 @@
|
||||
#!/usr/bin/env bash
|
||||
# CLAUDE.md Guardian Check Script
|
||||
# 在 SessionStart 时运行,检查项目变化
|
||||
|
||||
set -euo pipefail
|
||||
|
||||
# 获取脚本所在目录的父目录(项目根目录)
|
||||
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||
PROJECT_ROOT="$(cd "$SCRIPT_DIR/../.." && pwd)"
|
||||
GUARDIAN_DIR="$PROJECT_ROOT/.claude/guardian"
|
||||
SNAPSHOT_FILE="$GUARDIAN_DIR/snapshot.json"
|
||||
LOG_FILE="$GUARDIAN_DIR/guardian.log"
|
||||
|
||||
# 确保目录存在
|
||||
mkdir -p "$GUARDIAN_DIR"
|
||||
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# 计算目录结构哈希(使用绝对路径)
|
||||
get_structure_hash() {
|
||||
cd "$PROJECT_ROOT"
|
||||
find . -type f \( -name "*.md" -o -name "*.sh" -o -name "*.json" \) \
|
||||
! -path "./content-forge/*" \
|
||||
! -path "./.git/*" \
|
||||
! -path "./node_modules/*" \
|
||||
! -path "./.claude/guardian/*" \
|
||||
-exec md5sum {} \; 2>/dev/null | sort | md5sum | cut -d' ' -f1
|
||||
}
|
||||
|
||||
# 获取 skills 列表
|
||||
get_skills() {
|
||||
ls -1 "$PROJECT_ROOT/.claude/skills/" 2>/dev/null | sort | tr '\n' ',' | sed 's/,$//'
|
||||
}
|
||||
|
||||
# 获取 commands 列表
|
||||
get_commands() {
|
||||
ls -1 "$PROJECT_ROOT/.claude/commands/" 2>/dev/null | sed 's/.md$//' | sort | tr '\n' ',' | sed 's/,$//'
|
||||
}
|
||||
|
||||
# 检查是否首次运行
|
||||
if [[ ! -f "$SNAPSHOT_FILE" ]]; then
|
||||
log "首次运行,创建初始快照"
|
||||
|
||||
HASH=$(get_structure_hash)
|
||||
SKILLS=$(get_skills)
|
||||
COMMANDS=$(get_commands)
|
||||
|
||||
# 创建初始快照
|
||||
cat > "$SNAPSHOT_FILE" << EOF
|
||||
{
|
||||
"last_check": "$(date -Iseconds)",
|
||||
"structure_hash": "$HASH",
|
||||
"skills": "$SKILLS",
|
||||
"commands": "$COMMANDS",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "GUARDIAN: 初始化完成,快照已创建"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 对比变化
|
||||
CURRENT_HASH=$(get_structure_hash)
|
||||
STORED_HASH=$(jq -r '.structure_hash' "$SNAPSHOT_FILE")
|
||||
CURRENT_SKILLS=$(get_skills)
|
||||
STORED_SKILLS=$(jq -r '.skills' "$SNAPSHOT_FILE")
|
||||
CURRENT_COMMANDS=$(get_commands)
|
||||
STORED_COMMANDS=$(jq -r '.commands' "$SNAPSHOT_FILE")
|
||||
|
||||
CHANGES=""
|
||||
|
||||
if [[ "$CURRENT_HASH" != "$STORED_HASH" ]]; then
|
||||
CHANGES="${CHANGES}结构变化 detected\n"
|
||||
fi
|
||||
|
||||
if [[ "$CURRENT_SKILLS" != "$STORED_SKILLS" ]]; then
|
||||
CHANGES="${CHANGES}Skills 变化: $STORED_SKILLS -> $CURRENT_SKILLS\n"
|
||||
fi
|
||||
|
||||
if [[ "$CURRENT_COMMANDS" != "$STORED_COMMANDS" ]]; then
|
||||
CHANGES="${CHANGES}Commands 变化: $STORED_COMMANDS -> $CURRENT_COMMANDS\n"
|
||||
fi
|
||||
|
||||
if [[ -n "$CHANGES" ]]; then
|
||||
log "检测到变化: $CHANGES"
|
||||
|
||||
# 更新快照
|
||||
cat > "$SNAPSHOT_FILE" << EOF
|
||||
{
|
||||
"last_check": "$(date -Iseconds)",
|
||||
"structure_hash": "$CURRENT_HASH",
|
||||
"skills": "$CURRENT_SKILLS",
|
||||
"commands": "$CURRENT_COMMANDS",
|
||||
"version": "1.0.0"
|
||||
}
|
||||
EOF
|
||||
|
||||
echo "GUARDIAN: 检测到项目变化,建议更新 CLAUDE.md"
|
||||
echo "变化内容:"
|
||||
echo -e "$CHANGES"
|
||||
echo ""
|
||||
echo "运行 /claudeforge-skill 来更新 CLAUDE.md"
|
||||
else
|
||||
log "无变化"
|
||||
# 静默退出,不输出任何内容
|
||||
fi
|
||||
14
.claude/settings.json
Normal file
14
.claude/settings.json
Normal file
@ -0,0 +1,14 @@
|
||||
{
|
||||
"hooks": {
|
||||
"SessionStart": [
|
||||
{
|
||||
"hooks": [
|
||||
{
|
||||
"type": "command",
|
||||
"command": "bash .claude/hooks/guardian-check.sh"
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
104
.claude/skills/my-world/SKILL.md
Normal file
104
.claude/skills/my-world/SKILL.md
Normal file
@ -0,0 +1,104 @@
|
||||
---
|
||||
name: my-world
|
||||
description: "One-shot context loader for the content-forge Obsidian vault. This skill should be used at the start of any Claude Code session to load the user's projects, topics, drafts, knowledge graph, and current priorities into context. Replaces repeated re-explanation of who the user is and what they're working on."
|
||||
---
|
||||
|
||||
# /my-world — Load My World
|
||||
|
||||
Load the entire content-forge vault context into the current session in one shot.
|
||||
|
||||
## When to Use
|
||||
|
||||
Run at the start of any session that involves content creation, vault operations, or when needing awareness of current projects and priorities.
|
||||
|
||||
## What It Does
|
||||
|
||||
1. Read vault stats (file count, tags, recent activity)
|
||||
2. Scan all active content (topics, drafts, in-review)
|
||||
3. Map the knowledge graph (backlinks, wikilinks, orphans)
|
||||
4. Surface current priorities and blockers
|
||||
5. Present a concise world-view summary
|
||||
|
||||
## Important: Vault Directory
|
||||
|
||||
All `obsidian` CLI commands must run from within the vault directory to auto-detect the vault:
|
||||
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
```
|
||||
|
||||
Run all subsequent commands after this `cd`. The `vault=` parameter is unreliable; directory-based detection is the correct approach.
|
||||
|
||||
## Execution Steps
|
||||
|
||||
### Step 1: Vault Health
|
||||
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian vault
|
||||
obsidian files total
|
||||
obsidian tags counts sort=count
|
||||
```
|
||||
|
||||
### Step 2: Active Content Pipeline
|
||||
|
||||
Scan each pipeline stage for active items:
|
||||
|
||||
```bash
|
||||
obsidian files folder="00-inbox"
|
||||
obsidian files folder="01-topics"
|
||||
obsidian files folder="02-drafts"
|
||||
obsidian files folder="03-review"
|
||||
obsidian files folder="04-published"
|
||||
```
|
||||
|
||||
For each file found, read its frontmatter to extract `title`, `status`, `content_type`, `channels`:
|
||||
|
||||
```bash
|
||||
obsidian properties path="<file-path>"
|
||||
```
|
||||
|
||||
### Step 3: Knowledge Graph
|
||||
|
||||
```bash
|
||||
obsidian orphans
|
||||
obsidian deadends
|
||||
obsidian unresolved
|
||||
```
|
||||
|
||||
### Step 4: Recent Activity
|
||||
|
||||
```bash
|
||||
obsidian recents
|
||||
```
|
||||
|
||||
### Step 5: Summarize
|
||||
|
||||
Present a concise summary in this format:
|
||||
|
||||
```
|
||||
=== My World ===
|
||||
|
||||
Vault: content-forge (X files, Y tags)
|
||||
|
||||
Pipeline:
|
||||
inbox: N items
|
||||
topics: N items (list titles)
|
||||
drafts: N items (list titles + status)
|
||||
review: N items (list titles)
|
||||
published: N items total
|
||||
|
||||
Knowledge Graph:
|
||||
Orphans: N (notes with no incoming links)
|
||||
Dead ends: N (notes with no outgoing links)
|
||||
Unresolved: N (broken [[wikilinks]])
|
||||
|
||||
Recent: (last 5 files touched)
|
||||
|
||||
Priorities:
|
||||
- (inferred from draft/review status and recency)
|
||||
```
|
||||
|
||||
## Output
|
||||
|
||||
A structured world-view that gives full context for the session. No files are written — this is read-only.
|
||||
@ -74,27 +74,42 @@ obsidian create path="02-drafts/<YYYY-MM-DD>-<slug>.md" content="<ARTICLE_MARKDO
|
||||
对 Step 3 产物逐项设置元数据。
|
||||
|
||||
```bash
|
||||
# 先 cd 到 vault 目录
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
|
||||
# CLAUDE.md 必填字段
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="id" value="<YYYY-MM-DD>-<slug>"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="title" value="<title>"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="slug" value="<slug>"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="status" value="draft"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="content_type" value="article"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="channels" value="[\"wechat\",\"x\"]" type=list
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="language" value="zh-CN"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="source_urls" value="[]" type=list
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="assets" value="[]" type=list
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="cover_image" value=""
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="template" value="article"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="owner" value="content-forge"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="created_at" value="<YYYY-MM-DD>T00:00:00+08:00"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="updated_at" value="<YYYY-MM-DD>T00:00:00+08:00"
|
||||
|
||||
# 技能扩展字段(可选)
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="style" value="<style>"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="audience" value="<audience>"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="status" value="draft"
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="tags" value="[\"tag1\",\"tag2\"]" type=list
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="source_notes" value="[\"note1.md\"]" type=list
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="created_at" value="<YYYY-MM-DD>" type=date
|
||||
obsidian property:set path="02-drafts/<YYYY-MM-DD>-<slug>.md" name="updated_at" value="<YYYY-MM-DD>" type=date
|
||||
```
|
||||
|
||||
最小 frontmatter 字段集合:
|
||||
- `title`
|
||||
- `slug`
|
||||
- `style`
|
||||
- `audience`
|
||||
- `status`
|
||||
- `tags`
|
||||
- `source_notes`
|
||||
- `created_at`
|
||||
- `updated_at`
|
||||
最小 frontmatter 字段集合(对齐 CLAUDE.md §4.2):
|
||||
- `id`, `title`, `slug`, `status`, `content_type`, `channels`, `language`
|
||||
- `source_urls`, `assets`, `cover_image`, `template`, `owner`
|
||||
- `created_at`, `updated_at`
|
||||
|
||||
技能扩展字段(可选):
|
||||
- `style` — 写作风格(tech_blog/opinion/tutorial/social_short)
|
||||
- `audience` — 目标读者
|
||||
- `tags` — 标签列表
|
||||
- `source_notes` — 来源笔记路径
|
||||
|
||||
## 输出约束
|
||||
|
||||
|
||||
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,2 +1,3 @@
|
||||
# Vault has its own git repo
|
||||
content-forge/
|
||||
.claude/guardian/
|
||||
|
||||
154
CLAUDE.md
154
CLAUDE.md
@ -4,6 +4,8 @@
|
||||
|
||||
`content-forge` 是一个「以 Obsidian Vault 为单一内容源」的自媒体内容生产编排项目,目标是把选题、写作、配图、格式化、发布变成可重复执行的流水线。
|
||||
|
||||
黄金法则:**Agents Read, Humans Write** — Agent 只读取 Vault,永远不写入。Vault 只包含人类的思考,Agent 的输出走 CLI 命令或终端,不污染知识源。
|
||||
|
||||
核心边界:
|
||||
- 只新增一个本地 skill:`write-article`(文章生成)。
|
||||
- 其余能力优先复用现有 `baoyu-*`、`image-gen-orchestrator`、`basic-image-gen`、`social-media-pipeline`。
|
||||
@ -44,14 +46,24 @@ content_type: article
|
||||
```text
|
||||
content-forge/
|
||||
├── CLAUDE.md # 项目规范(本文件)
|
||||
├── my-commands.md # 自定义命令文档
|
||||
├── .gitignore # 排除 content-forge/ (vault 有独立仓库)
|
||||
├── scripts/
|
||||
│ └── init-vault.sh # Vault 初始化脚本
|
||||
│ ├── init-vault.sh # Vault 初始化脚本
|
||||
│ └── vault-sync.sh # Vault 自动同步到 Gitea
|
||||
├── .claude/
|
||||
│ ├── commands/ # 自定义 slash 命令
|
||||
│ │ ├── my-world.md # /my-world — 一键加载 vault 上下文
|
||||
│ │ ├── emerge.md # /emerge — 发现隐藏模式
|
||||
│ │ ├── challenge.md # /challenge — 压力测试信念
|
||||
│ │ └── connect.md # /connect — 跨领域桥接
|
||||
│ └── skills/
|
||||
│ └── write-article/ # 本项目唯一新增 skill
|
||||
│ ├── SKILL.md # skill 定义与工作流
|
||||
│ └── references/
|
||||
│ └── writing-styles.md # 4 种写作风格规范
|
||||
│ ├── write-article/ # 从 Vault 选题生成文章
|
||||
│ │ ├── SKILL.md # skill 定义与工作流
|
||||
│ │ └── references/
|
||||
│ │ └── writing-styles.md # 4 种写作风格规范
|
||||
│ └── my-world/ # 一键加载 vault 上下文(skill 版)
|
||||
│ └── SKILL.md
|
||||
└── content-forge/ # Obsidian Vault(内容存储)
|
||||
├── 00-inbox/ # 临时灵感、原始素材
|
||||
├── 01-topics/ # 结构化选题
|
||||
@ -59,6 +71,7 @@ content-forge/
|
||||
├── 03-review/ # 待审核稿
|
||||
├── 04-published/ # 已发布归档
|
||||
├── 05-assets/ # 配图、封面等资产
|
||||
├── 06-archived/ # 废弃/放弃的内容
|
||||
└── templates/ # article / tech-blog / social-post
|
||||
```
|
||||
|
||||
@ -121,6 +134,10 @@ social-media-pipeline
|
||||
| 本地 skill | `image-gen-orchestrator` | 批量图片编排 | 必需 | 触发图像任务成功返回 |
|
||||
| 本地 skill | `social-media-pipeline` | 选题/模板/日历方法论 | 建议启用 | 能输出结构化选题与日历 |
|
||||
| 本地 skill | `write-article` | 从 Vault 选题生成文章 | 必需(本项目唯一新增) | 生成 `02-drafts/*.md` |
|
||||
| 本地 skill | `my-world` | 一键加载 vault 上下文 | 建议启用 | `/my-world` 输出 vault 摘要 |
|
||||
| 社区插件 | `dataview` (v0.5.68) | Vault 当数据库查询 | 建议启用 | Obsidian 设置中已启用 |
|
||||
| 社区插件 | `templater-obsidian` | 高级模板引擎 | 建议启用 | Obsidian 设置中已启用 |
|
||||
| 社区插件 | `note-to-mp` | 笔记转微信公众号格式 | 建议启用 | Obsidian 设置中已启用 |
|
||||
|
||||
### 3.2 外部依赖与环境变量
|
||||
|
||||
@ -140,7 +157,13 @@ social-media-pipeline
|
||||
|
||||
默认 Vault 路径:`<PROJECT_ROOT>/content-forge/`(通过 `scripts/init-vault.sh` 初始化,可用 `VAULT_PATH` 环境变量覆盖)。
|
||||
|
||||
Obsidian CLI 用**目录名**作为 vault 名称,因此目录名必须是 `content-forge`(非 `vault`),命令中使用 `vault="content-forge"` 指定。
|
||||
**关键操作约束**:Obsidian CLI 的 `vault=` 参数存在已知 bug(静默失效),**必须先 `cd` 到 vault 目录内再执行 CLI 命令**,CLI 会自动检测当前目录所属的 vault:
|
||||
|
||||
```bash
|
||||
cd /home/kang/apps/content-forge/content-forge
|
||||
obsidian vault # 正确识别 content-forge vault
|
||||
obsidian orphans # 在 vault 目录内执行才有效
|
||||
```
|
||||
|
||||
```text
|
||||
<VAULT_ROOT>/
|
||||
@ -150,6 +173,7 @@ Obsidian CLI 用**目录名**作为 vault 名称,因此目录名必须是 `con
|
||||
├── 03-review/ # 待审核稿(人工或规则审校)
|
||||
├── 04-published/ # 已发布内容归档
|
||||
├── 05-assets/ # 配图、封面、信息图等资产
|
||||
├── 06-archived/ # 废弃/放弃的内容
|
||||
└── templates/ # 模板(article / tech-blog / social-post)
|
||||
```
|
||||
|
||||
@ -167,7 +191,7 @@ Obsidian CLI 用**目录名**作为 vault 名称,因此目录名必须是 `con
|
||||
id: "2026-03-01-example-slug"
|
||||
title: "示例标题"
|
||||
slug: "example-slug"
|
||||
status: "topic" # topic | draft | review | published | archived
|
||||
status: "topic" # inbox | topic | draft | formatting | review | publishing | published | revision | blocked | archived
|
||||
content_type: "article" # article | thread | x-post | wechat-post | infographic
|
||||
channels: ["wechat", "x"]
|
||||
language: "zh-CN"
|
||||
@ -179,6 +203,14 @@ owner: "content-forge"
|
||||
created_at: "2026-03-01T00:00:00+08:00"
|
||||
updated_at: "2026-03-01T00:00:00+08:00"
|
||||
published_at: null
|
||||
# 周期时间追踪(可选)
|
||||
cycle_days: null # 从创建到发布的总天数
|
||||
stage_days: # 各阶段停留天数
|
||||
inbox: 0
|
||||
topic: 0
|
||||
draft: 0
|
||||
review: 0
|
||||
total: 0
|
||||
---
|
||||
```
|
||||
|
||||
@ -186,6 +218,27 @@ published_at: null
|
||||
- `id` 与 `slug` 一一对应,禁止同 slug 多 id。
|
||||
- `status` 只能按 `topic -> draft -> review -> published` 前进。
|
||||
- `assets` 只存 Vault 相对路径(禁止外部临时路径)。
|
||||
- `cycle_days` 在发布时自动计算:`published_at - created_at`。
|
||||
- `stage_days` 由 `/today` 或 `/close` 命令更新。
|
||||
|
||||
### 4.3 各阶段退出标准
|
||||
|
||||
每个阶段必须满足以下条件才能前进到下一阶段:
|
||||
|
||||
| 阶段 | 必填字段 | 退出条件 |
|
||||
|------|----------|----------|
|
||||
| **inbox** | `title` | 有标题即可进入 topic |
|
||||
| **topic** | `slug`, `source_urls` 或明确观点 | 有 slug + 明确写作角度 |
|
||||
| **draft** | 完整正文 | 正文结构完整,有开头/主体/结尾 |
|
||||
| **formatting** | `assets`, `cover_image` | 配图已生成,格式化完成 |
|
||||
| **review** | 审核通过 | 人工审核完成,无事实错误 |
|
||||
| **publishing** | `channels` | 渠道已确定,准备发布 |
|
||||
| **published** | `published_at`, `published_url` | 已发布,记录发布时间和链接 |
|
||||
|
||||
**特殊情况**:
|
||||
- **revision**:从 review 回退,修复后返回 draft 或 review
|
||||
- **blocked**:等待外部输入,解除后返回原阶段
|
||||
- **archived**:废弃内容,不再前进
|
||||
|
||||
---
|
||||
|
||||
@ -245,11 +298,20 @@ obsidian search query="2026-03-01-demo"
|
||||
|
||||
### 6.1 使用规范(强制)
|
||||
|
||||
1. 所有写入必须使用 Vault 相对路径,禁止绝对路径。
|
||||
2. 写入前先创建文件,再写 frontmatter 属性,避免属性写入到不存在文件。
|
||||
3. 每次阶段切换都要更新 `status` 与 `updated_at`。
|
||||
4. 资产路径统一写入 `assets` 数组,不在正文硬编码临时 URL。
|
||||
5. 发布前必须能通过 `obsidian search query="<slug>"` 找到 topic/draft/review/published 至少一个节点。
|
||||
1. **先 cd 再执行**:所有 CLI 命令必须在 `cd /home/kang/apps/content-forge/content-forge` 之后执行。
|
||||
2. 所有写入必须使用 Vault 相对路径,禁止绝对路径。
|
||||
3. 写入前先创建文件,再写 frontmatter 属性,避免属性写入到不存在文件。
|
||||
4. 每次阶段切换都要更新 `status` 与 `updated_at`。
|
||||
5. 资产路径统一写入 `assets` 数组,不在正文硬编码临时 URL。
|
||||
6. 发布前必须能通过 `obsidian search query="<slug>"` 找到 topic/draft/review/published 至少一个节点。
|
||||
|
||||
### 6.1.1 NEVER 规则(踩坑编纂)
|
||||
|
||||
- **NEVER** 使用 `vault=` 参数 — 已知 bug,静默失效,总是 cd 到 vault 目录。
|
||||
- **NEVER** 用 `find`/`ls`/`cat` 替代 CLI — `obsidian search`/`obsidian read` 效率高 70000 倍。
|
||||
- **NEVER** 让 Agent 直接写入 Vault 笔记内容 — 黄金法则:Agents Read, Humans Write。通过 `obsidian create`/`obsidian property:set` 的结构化命令操作 frontmatter 和流水线状态是允许的。
|
||||
- **NEVER** 在 Vault 中存放非内容文件(脚本、zip、临时文件) — Vault 只放 markdown 笔记和资产。
|
||||
- **NEVER** 跳过 frontmatter — 每个 01-04 目录的文件必须有完整 frontmatter。
|
||||
|
||||
### 6.2 故障排查(按优先级)
|
||||
|
||||
@ -259,7 +321,7 @@ obsidian search query="2026-03-01-demo"
|
||||
| `obsidian vault` 无输出或报错 | Vault 未注册 | `obsidian vault` | 运行 `scripts/init-vault.sh` 或手动注册 |
|
||||
| `obsidian read` 失败 | 路径拼写或文件不存在 | `obsidian search query="<slug>"` | 先 `obsidian create` 再 `obsidian read` |
|
||||
| `obsidian property:set` 不生效 | frontmatter 块损坏或 name 拼写错误 | `obsidian read path="<path>"` | 修复 frontmatter 结构后重试 |
|
||||
| `obsidian vault` 段错误(segfault) | CLI 版本与系统不兼容或内存问题 | `obsidian --version` | 重新安装 CLI:`npm i -g obsidian-cli` 或 `pip install obsidian-cli`;如持续崩溃,用文件系统直接读写 Vault 作为降级方案 |
|
||||
| `obsidian vault` 段错误(segfault) | `~/.local/bin/obsidian` 是 symlink 而非 wrapper,缺少 `DISPLAY` 注入 | `file ~/.local/bin/obsidian` 应显示 shell script | 重新部署 wrapper:见 `obsidian-setup` skill Step 2;wrapper 会自动注入 `DISPLAY=:42` |
|
||||
| 配图 skill 调用失败 | `basic-image-gen` API key 缺失 | 检查环境变量 | 补齐 `OPENAI_API_KEY/GOOGLE_API_KEY/DASHSCOPE_API_KEY` |
|
||||
| 微信/X 发布失败 | 登录态失效或接口凭证过期 | 检查浏览器登录态/API 凭证 | 重新登录或刷新凭证 |
|
||||
|
||||
@ -275,18 +337,72 @@ obsidian search query="templates"
|
||||
|
||||
---
|
||||
|
||||
## 7) 已知问题与限制
|
||||
## 7) Git 仓库与数据同步
|
||||
|
||||
项目代码和 Vault 数据使用**独立仓库**管理:
|
||||
|
||||
| 仓库 | 地址 | 内容 | 同步方式 |
|
||||
|---|---|---|---|
|
||||
| `content-forge` | `http://gitea.towards-agi.cn/zhukang/content-forge.git` | CLAUDE.md、skills、scripts | 手动 commit + push |
|
||||
| `content-forge-vault` | `http://gitea.towards-agi.cn/zhukang/content-forge-vault.git` | Vault 数据(选题、草稿、资产) | cron 每天凌晨 2 点自动 |
|
||||
|
||||
### 7.1 认证方式
|
||||
|
||||
- 使用 `git credential store`,token 统一存放在 `~/.git-credentials`(权限 600)。
|
||||
- Token 更换时只需修改 `~/.git-credentials` 一个文件。
|
||||
- Remote URL 中不含 token。
|
||||
|
||||
### 7.2 自动同步
|
||||
|
||||
`scripts/vault-sync.sh` 由 cron 每天执行,逻辑:有变更时 `git add -A && commit && push`,无变更时跳过。
|
||||
|
||||
```bash
|
||||
# 查看 cron 配置
|
||||
crontab -l | grep vault-sync
|
||||
|
||||
# 手动触发同步
|
||||
bash scripts/vault-sync.sh
|
||||
|
||||
# 查看同步日志
|
||||
tail -20 /tmp/vault-sync.log
|
||||
```
|
||||
|
||||
### 7.3 其他设备 clone
|
||||
|
||||
```bash
|
||||
git clone http://gitea.towards-agi.cn/zhukang/content-forge-vault.git
|
||||
# 首次 push 时 credential store 会提示输入用户名和 token
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 8) 已知问题与限制(含 NEVER 规则踩坑记录)
|
||||
|
||||
| 问题 | 影响 | 状态 | 降级方案 |
|
||||
|---|---|---|---|
|
||||
| `obsidian vault` 执行时 segfault(核心已转储) | 无法通过 CLI 操作 Vault | 待修复 | 用文件系统直接读写 `content-forge/` 目录 |
|
||||
| 所有内容目录为空 | 流水线尚未有端到端产出 | 正常(项目刚初始化) | 按 Quick Start 创建首个选题 |
|
||||
| `obsidian` 插件 CLI 与 Obsidian 桌面客户端为独立进程 | 桌面端修改不会自动触发 CLI 事件 | 设计如此 | 以文件系统为准,CLI 和桌面端都会读取同一目录 |
|
||||
| ~~`obsidian vault` segfault~~ | ~~无法通过 CLI 操作~~ | **已修复**(wrapper 脚本注入 DISPLAY) | — |
|
||||
| `obsidian` CLI 与桌面客户端为独立进程 | 桌面端修改不会自动触发 CLI 事件 | 设计如此 | 以文件系统为准,CLI 和桌面端都会读取同一目录 |
|
||||
| `vault=` 参数静默失效 | CLI 命令作用到错误 vault | **已确认** | 必须 `cd` 到 vault 目录内执行 CLI 命令 |
|
||||
|
||||
---
|
||||
|
||||
## 9) 自定义命令(`.claude/commands/`)
|
||||
|
||||
基于 Vin Obsidian Workflows 理念构建的思维工具,详见 `my-commands.md`。
|
||||
|
||||
| 命令 | 用途 | 使用时机 |
|
||||
|------|------|----------|
|
||||
| `/my-world` | 一键加载 vault 全量上下文 | 每次 session 开始 |
|
||||
| `/emerge` | 从笔记中发现隐藏模式 | 周回顾、新增多篇笔记后 |
|
||||
| `/challenge` | 用 vault 历史压力测试信念 | 发布前、重大决策前 |
|
||||
| `/connect` | 跨领域桥接两个不相关主题 | 寻找新内容角度 |
|
||||
|
||||
待建:`/today`(晨间仪式)、`/close`(晚间仪式)。
|
||||
|
||||
---
|
||||
|
||||
## 版本
|
||||
|
||||
- **CLAUDE.md 版本**:1.1.0
|
||||
- **CLAUDE.md 版本**:1.3.0
|
||||
- **最后更新**:2026-03-02
|
||||
- **项目阶段**:初始化完成,待首次端到端验证
|
||||
- **项目阶段**:环境就绪,CLI 验证通过,双仓库同步已配置,思维工具命令已部署
|
||||
|
||||
53
my-commands.md
Normal file
53
my-commands.md
Normal file
@ -0,0 +1,53 @@
|
||||
# My Commands
|
||||
|
||||
Custom slash commands for content-forge, built from the Vin Obsidian Workflows lesson.
|
||||
|
||||
All command files live in `.claude/commands/`.
|
||||
|
||||
## /my-world
|
||||
|
||||
**File:** `.claude/commands/my-world.md`
|
||||
|
||||
Loads full vault context in one shot. Scans all pipeline folders (00-inbox through 04-published), maps the knowledge graph (tags, orphans, dead ends, unresolved links), and summarizes priorities and connections.
|
||||
|
||||
**When to use:** Start of every session. Replaces re-explaining your project context.
|
||||
|
||||
## /emerge
|
||||
|
||||
**File:** `.claude/commands/emerge.md`
|
||||
|
||||
Surfaces hidden patterns — ideas the vault implies but you never explicitly stated. Reads all notes, runs graph analysis (orphans, tags, dead ends), and finds emergent conclusions from scattered premises.
|
||||
|
||||
**When to use:** Weekly review, or whenever you've added several new notes and want to see what they add up to.
|
||||
|
||||
## /challenge
|
||||
|
||||
**File:** `.claude/commands/challenge.md`
|
||||
|
||||
Pressure-tests your beliefs using the vault's own history. Finds contradictions, tensions, and counter-evidence within your notes. Forces reconciliation of conflicting ideas.
|
||||
|
||||
**When to use:** Before publishing, before major decisions, or when you suspect confirmation bias.
|
||||
|
||||
## /connect
|
||||
|
||||
**File:** `.claude/commands/connect.md`
|
||||
|
||||
Bridges two disparate topics using the vault's link graph. Picks seemingly unrelated notes and finds the hidden bridge between them.
|
||||
|
||||
**When to use:** When looking for cross-domain insights, novel content angles, or creative connections.
|
||||
|
||||
## /today
|
||||
|
||||
**File:** `.claude/commands/today.md`
|
||||
|
||||
晨间仪式:扫描看板状态 + 识别超期项目 + 输出今日建议。检查各阶段 WIP,找出 7 天以上未更新的笔记,给出优先处理建议。
|
||||
|
||||
**When to use:** 每天开始工作时,快速了解当前状态和优先级。
|
||||
|
||||
## /close
|
||||
|
||||
**File:** `.claude/commands/close.md`
|
||||
|
||||
晚间仪式:检查今日完成 + 更新看板状态 + Git 同步 + 更新 progress.md。确保工作有始有终。
|
||||
|
||||
**When to use:** 每天结束工作前,收尾和复盘。
|
||||
@ -35,12 +35,15 @@ register_vault() {
|
||||
# editing the config file (same approach as obsidian-setup/scripts/register_vault.py).
|
||||
local config_path="$HOME/.config/obsidian/obsidian.json"
|
||||
|
||||
# Use environment variables to avoid command injection
|
||||
export VAULT_PATH CONFIG_PATH="$config_path"
|
||||
|
||||
python3 -c "
|
||||
import json, os, hashlib, time
|
||||
|
||||
vault_path = '${VAULT_PATH}'
|
||||
vault_path = os.environ['VAULT_PATH']
|
||||
vault_id = hashlib.md5(vault_path.encode()).hexdigest()[:16]
|
||||
config_path = os.path.expanduser('${config_path}')
|
||||
config_path = os.environ['CONFIG_PATH']
|
||||
|
||||
os.makedirs(os.path.join(vault_path, '.obsidian'), exist_ok=True)
|
||||
|
||||
|
||||
@ -1,15 +1,49 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
set -Eeuo pipefail
|
||||
|
||||
VAULT_PATH="${VAULT_PATH:-/home/kang/apps/content-forge/content-forge}"
|
||||
LOG_FILE="${LOG_FILE:-/tmp/vault-sync.log}"
|
||||
|
||||
cd "$VAULT_PATH"
|
||||
log() {
|
||||
echo "[$(date '+%Y-%m-%d %H:%M:%S')] $*" >> "$LOG_FILE"
|
||||
}
|
||||
|
||||
# Verify vault directory exists
|
||||
if [[ ! -d "$VAULT_PATH" ]]; then
|
||||
log "ERROR: Vault directory does not exist: $VAULT_PATH"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
cd "$VAULT_PATH" || {
|
||||
log "ERROR: Failed to cd into vault: $VAULT_PATH"
|
||||
exit 1
|
||||
}
|
||||
|
||||
# Skip if no changes
|
||||
if git diff --quiet && git diff --cached --quiet && [ -z "$(git ls-files --others --exclude-standard)" ]; then
|
||||
log "No changes to sync"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
git add -A
|
||||
git commit -m "vault: auto-sync $(date '+%Y-%m-%d %H:%M')"
|
||||
git push origin main
|
||||
# Get current branch dynamically
|
||||
BRANCH=$(git rev-parse --abbrev-ref HEAD 2>/dev/null || echo "main")
|
||||
|
||||
# Stage all changes
|
||||
if ! git add -A; then
|
||||
log "ERROR: git add failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Commit with timestamp
|
||||
if ! git commit -m "vault: auto-sync $(date '+%Y-%m-%d %H:%M')"; then
|
||||
log "ERROR: git commit failed"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
# Push with error handling
|
||||
if ! git push origin "$BRANCH"; then
|
||||
log "ERROR: git push failed (branch: $BRANCH)"
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log "Sync successful: pushed to $BRANCH"
|
||||
|
||||
Loading…
Reference in New Issue
Block a user