content-forge/scripts/init-vault.sh

261 lines
5.1 KiB
Bash
Executable File

#!/usr/bin/env bash
set -Eeuo pipefail
SCRIPT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd)"
PROJECT_ROOT="$(cd -- "${SCRIPT_DIR}/.." && pwd)"
VAULT_NAME="${VAULT_NAME:-content-forge}"
VAULT_PATH="${VAULT_PATH:-${PROJECT_ROOT}/content-forge}"
readonly SCRIPT_DIR
readonly PROJECT_ROOT
readonly VAULT_NAME
readonly VAULT_PATH
log() {
printf '[init-vault] %s\n' "$*"
}
die() {
printf '[init-vault] ERROR: %s\n' "$*" >&2
exit 1
}
trap 'die "Command failed at line ${LINENO}: ${BASH_COMMAND}"' ERR
require_obsidian() {
if ! command -v obsidian >/dev/null 2>&1; then
die "Missing required command: obsidian. Run the obsidian-setup skill first."
fi
}
register_vault() {
# Vault registration writes directly to ~/.config/obsidian/obsidian.json.
# The obsidian CLI has no vault-add subcommand; registration is done by
# 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 = os.environ['VAULT_PATH']
vault_id = hashlib.md5(vault_path.encode()).hexdigest()[:16]
config_path = os.environ['CONFIG_PATH']
os.makedirs(os.path.join(vault_path, '.obsidian'), exist_ok=True)
if os.path.exists(config_path):
with open(config_path) as f:
config = json.load(f)
else:
os.makedirs(os.path.dirname(config_path), exist_ok=True)
config = {}
config['cli'] = True
config.setdefault('vaults', {})
# Check if already registered
for vid, vdata in config['vaults'].items():
if vdata.get('path') == vault_path:
print(f'Vault already registered: {vault_path} (id: {vid})')
exit(0)
config['vaults'][vault_id] = {'path': vault_path, 'ts': int(time.time() * 1000)}
with open(config_path, 'w') as f:
json.dump(config, f, indent=2)
print(f'Registered vault: {vault_path} (id: {vault_id})')
"
}
create_directories() {
local dirs=(
"00-inbox"
"01-topics"
"02-drafts"
"03-review"
"04-published"
"05-assets"
"06-archived"
"07-leads"
"08-events"
"09-viral-examples"
"templates"
)
local dir
for dir in "${dirs[@]}"; do
mkdir -p "${VAULT_PATH}/${dir}"
done
log "Ensured vault directories exist."
}
create_templates() {
local t="${VAULT_PATH}/templates"
mkdir -p "${t}"
# article template — matches writing-styles.md:tech_blog structure
if [[ ! -f "${t}/article.md" ]]; then
cat >"${t}/article.md" <<'EOF'
---
id: ""
title: ""
slug: ""
status: "topic"
content_type: "article"
channels: []
language: "zh-CN"
source_urls: []
assets: []
cover_image: ""
template: "article"
owner: "content-forge"
created_at: ""
updated_at: ""
published_at: null
---
# 问题背景
# 核心方案
# 实现细节
# 验证结果
# 总结与扩展
EOF
log "Created template: article"
else
log "Template already exists, skip: article"
fi
# tech-blog template — same frontmatter, tech_blog structure
if [[ ! -f "${t}/tech-blog.md" ]]; then
cat >"${t}/tech-blog.md" <<'EOF'
---
id: ""
title: ""
slug: ""
status: "topic"
content_type: "article"
channels: []
language: "zh-CN"
source_urls: []
assets: []
cover_image: ""
template: "tech-blog"
owner: "content-forge"
created_at: ""
updated_at: ""
published_at: null
---
# 问题背景
# 核心方案
# 实现细节
# 验证结果
# 总结与扩展
EOF
log "Created template: tech-blog"
else
log "Template already exists, skip: tech-blog"
fi
# social-post template — matches writing-styles.md:social_short structure
if [[ ! -f "${t}/social-post.md" ]]; then
cat >"${t}/social-post.md" <<'EOF'
---
id: ""
title: ""
slug: ""
status: "topic"
content_type: "x-post"
channels: ["x"]
language: "zh-CN"
source_urls: []
assets: []
cover_image: ""
template: "social-post"
owner: "content-forge"
created_at: ""
updated_at: ""
published_at: null
---
<!-- Hook: 首句提出冲突、反差或问题 -->
<!-- 核心信息: 1-2 个关键观点 -->
<!-- 行动结尾: 提问、CTA 或可转发结论 -->
EOF
log "Created template: social-post"
else
log "Template already exists, skip: social-post"
fi
# viral-example template — for 09-viral-examples/ collection
if [[ ! -f "${t}/viral-example.md" ]]; then
cat >"${t}/viral-example.md" <<'EOF'
---
id: ""
title: ""
source_url: ""
platform: "" # x | wechat | xiaohongshu | douyin | bilibili | medium | other
author: ""
author_url: ""
published_at: ""
collected_at: ""
metrics:
likes: null
comments: null
shares: null
views: null
viral_score: null
tags: []
language: "zh-CN"
related_topics: []
analysis: null
takeaways: []
---
# 原文(或摘要)
[原文内容或核心观点摘要]
# 为什么火
[你的分析:时机、情绪、表达、平台特性等]
# 可复用点
-
# 关联选题
- [[01-topics/xxx|相关选题]]
EOF
log "Created template: viral-example"
else
log "Template already exists, skip: viral-example"
fi
}
main() {
require_obsidian
mkdir -p "${VAULT_PATH}"
register_vault
create_directories
create_templates
log "Vault initialization complete: ${VAULT_PATH}"
}
main "$@"