refactor: 合并 claude-marketplace,重构目录结构为单一仓库

- 重命名 plugins/ → skills/,个人插件迁移到 skills-personal/(gitignore)
- 更新 generate-marketplace.py 支持 config 读取和 skills-personal 扫描
- 新增 claude-config.yaml(技能启用/禁用 + MCP 配置)
- 新增 init.sh(交互式 MCP 初始化,支持 stdio/SSE 模式)
- 新增 CLAUDE.md 项目说明
- 重写 README.md 反映新结构
- 删除过时脚本:PUSH.sh、generate-marketplace.sh、convert-skills.sh

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-14 11:11:59 +10:30
parent f7f5428812
commit 99881e268a
191 changed files with 1131 additions and 492 deletions

View File

@@ -0,0 +1,409 @@
#!/usr/bin/env python3
"""
飞书多维表格 Demo
使用 zhiyun.ai 凭证演示多维表格的完整操作流程
"""
import requests
from datetime import datetime, timedelta
from typing import Optional, List, Dict
# ========== 配置 ==========
ZHIYUN_APP_ID = "cli_a9f29dca82b9dbef"
ZHIYUN_APP_SECRET = "sDfhjG7QT1S4gfHiMVYSygmPQPN1R2Ho"
BASE_URL = "https://open.feishu.cn/open-apis"
# ========== 工具类 ==========
class FeishuBitable:
"""飞书多维表格操作工具类"""
def __init__(self, app_id: str = ZHIYUN_APP_ID, app_secret: str = ZHIYUN_APP_SECRET):
self.app_id = app_id
self.app_secret = app_secret
self._token = None
self._token_expires = None
@property
def token(self) -> str:
"""获取或刷新 access token"""
if self._token and self._token_expires and datetime.now() < self._token_expires:
return self._token
url = f"{BASE_URL}/auth/v3/tenant_access_token/internal"
response = requests.post(url, json={
"app_id": self.app_id,
"app_secret": self.app_secret
})
data = response.json()
if data.get("code") != 0:
raise Exception(f"获取 token 失败: {data}")
self._token = data["tenant_access_token"]
self._token_expires = datetime.now() + timedelta(seconds=data.get("expire", 7200) - 60)
print(f"[OK] Token 获取成功,有效期至 {self._token_expires}")
return self._token
@property
def headers(self) -> Dict[str, str]:
return {
"Authorization": f"Bearer {self.token}",
"Content-Type": "application/json"
}
# ========== 多维表格管理 ==========
def create_bitable(self, name: str, folder_token: str = None) -> Dict:
"""
创建新的多维表格
Args:
name: 多维表格名称
folder_token: 文件夹 token可选不指定则创建在根目录
"""
url = f"{BASE_URL}/bitable/v1/apps"
payload = {"name": name}
if folder_token:
payload["folder_token"] = folder_token
response = requests.post(url, headers=self.headers, json=payload)
data = response.json()
if data.get("code") != 0:
raise Exception(f"创建多维表格失败: {data}")
app_info = data["data"]["app"]
print(f"[OK] 多维表格创建成功")
print(f" 名称: {app_info['name']}")
print(f" app_token: {app_info['app_token']}")
print(f" URL: {app_info.get('url', 'N/A')}")
return app_info
def list_tables(self, app_token: str) -> List[Dict]:
"""列出多维表格中的所有数据表"""
url = f"{BASE_URL}/bitable/v1/apps/{app_token}/tables"
response = requests.get(url, headers=self.headers)
data = response.json()
if data.get("code") != 0:
raise Exception(f"获取数据表列表失败: {data}")
tables = data["data"].get("items", [])
print(f"[OK] 找到 {len(tables)} 个数据表")
for t in tables:
print(f" - {t['name']} (table_id: {t['table_id']})")
return tables
def create_table(self, app_token: str, name: str, fields: List[Dict]) -> Dict:
"""
在多维表格中创建数据表
Args:
app_token: 多维表格 app_token
name: 数据表名称
fields: 字段定义列表
"""
url = f"{BASE_URL}/bitable/v1/apps/{app_token}/tables"
payload = {
"table": {
"name": name,
"default_view_name": "默认视图",
"fields": fields
}
}
response = requests.post(url, headers=self.headers, json=payload)
data = response.json()
if data.get("code") != 0:
raise Exception(f"创建数据表失败: {data}")
table_info = data["data"]
print(f"[OK] 数据表创建成功")
print(f" 名称: {name}")
print(f" table_id: {table_info['table_id']}")
return table_info
# ========== 记录操作 ==========
def list_records(self, app_token: str, table_id: str,
filter_str: str = None, page_size: int = 100) -> List[Dict]:
"""列出所有记录(自动分页)"""
url = f"{BASE_URL}/bitable/v1/apps/{app_token}/tables/{table_id}/records"
all_records = []
page_token = None
while True:
params = {"page_size": min(page_size, 500)}
if page_token:
params["page_token"] = page_token
if filter_str:
params["filter"] = filter_str
response = requests.get(url, headers=self.headers, params=params)
data = response.json()
if data.get("code") != 0:
raise Exception(f"查询失败: {data}")
items = data["data"].get("items", [])
all_records.extend(items)
if not data["data"].get("has_more"):
break
page_token = data["data"]["page_token"]
return all_records
def create_record(self, app_token: str, table_id: str, fields: Dict) -> Dict:
"""创建单条记录"""
url = f"{BASE_URL}/bitable/v1/apps/{app_token}/tables/{table_id}/records"
response = requests.post(url, headers=self.headers, json={"fields": fields})
data = response.json()
if data.get("code") != 0:
raise Exception(f"创建失败: {data}")
return data["data"]["record"]
def batch_create(self, app_token: str, table_id: str,
records: List[Dict], batch_size: int = 500) -> List[Dict]:
"""批量创建记录"""
url = f"{BASE_URL}/bitable/v1/apps/{app_token}/tables/{table_id}/records/batch_create"
created = []
for i in range(0, len(records), batch_size):
batch = [{"fields": r} if "fields" not in r else r for r in records[i:i+batch_size]]
response = requests.post(url, headers=self.headers, json={"records": batch})
data = response.json()
if data.get("code") != 0:
raise Exception(f"批量创建失败: {data}")
created.extend(data["data"]["records"])
return created
def get_fields(self, app_token: str, table_id: str) -> List[Dict]:
"""获取字段定义"""
url = f"{BASE_URL}/bitable/v1/apps/{app_token}/tables/{table_id}/fields"
response = requests.get(url, headers=self.headers)
data = response.json()
if data.get("code") != 0:
raise Exception(f"获取字段失败: {data}")
return data["data"]["items"]
# ========== Demo 函数 ==========
def demo_verify_credentials():
"""验证凭证是否有效"""
print("\n" + "="*50)
print("Step 1: 验证飞书应用凭证")
print("="*50)
bitable = FeishuBitable()
token = bitable.token # 触发 token 获取
print(f" Token 前缀: {token[:20]}...")
return bitable
def demo_create_bitable(bitable: FeishuBitable) -> str:
"""创建新的多维表格"""
print("\n" + "="*50)
print("Step 2: 创建多维表格")
print("="*50)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
name = f"Claude Code Demo - {timestamp}"
app_info = bitable.create_bitable(name)
return app_info["app_token"]
def demo_create_task_table(bitable: FeishuBitable, app_token: str) -> str:
"""创建任务管理数据表"""
print("\n" + "="*50)
print("Step 3: 创建任务管理数据表")
print("="*50)
# 定义字段
fields = [
{
"field_name": "任务名称",
"type": 1 # 文本
},
{
"field_name": "状态",
"type": 3, # 单选
"property": {
"options": [
{"name": "待处理", "color": 0},
{"name": "进行中", "color": 1},
{"name": "已完成", "color": 2},
{"name": "已取消", "color": 3}
]
}
},
{
"field_name": "优先级",
"type": 3, # 单选
"property": {
"options": [
{"name": "", "color": 4},
{"name": "", "color": 5},
{"name": "", "color": 6}
]
}
},
{
"field_name": "负责人",
"type": 1 # 文本
},
{
"field_name": "截止日期",
"type": 5, # 日期
"property": {
"date_formatter": "yyyy/MM/dd"
}
},
{
"field_name": "工时(小时)",
"type": 2 # 数字
},
{
"field_name": "备注",
"type": 1 # 文本
}
]
table_info = bitable.create_table(app_token, "任务看板", fields)
return table_info["table_id"]
def demo_add_sample_data(bitable: FeishuBitable, app_token: str, table_id: str):
"""添加示例数据"""
print("\n" + "="*50)
print("Step 4: 添加示例数据")
print("="*50)
# 准备示例数据
now = datetime.now()
sample_tasks = [
{
"任务名称": "完成产品需求文档",
"状态": "已完成",
"优先级": "",
"负责人": "张三",
"截止日期": int((now - timedelta(days=2)).timestamp() * 1000),
"工时(小时)": 8,
"备注": "PRD 已评审通过"
},
{
"任务名称": "设计系统架构方案",
"状态": "进行中",
"优先级": "",
"负责人": "李四",
"截止日期": int((now + timedelta(days=3)).timestamp() * 1000),
"工时(小时)": 16,
"备注": "正在编写技术方案"
},
{
"任务名称": "开发用户登录模块",
"状态": "待处理",
"优先级": "",
"负责人": "王五",
"截止日期": int((now + timedelta(days=7)).timestamp() * 1000),
"工时(小时)": 24,
"备注": "等待架构方案确定"
},
{
"任务名称": "编写单元测试",
"状态": "待处理",
"优先级": "",
"负责人": "赵六",
"截止日期": int((now + timedelta(days=10)).timestamp() * 1000),
"工时(小时)": 12,
"备注": ""
},
{
"任务名称": "部署测试环境",
"状态": "待处理",
"优先级": "",
"负责人": "钱七",
"截止日期": int((now + timedelta(days=14)).timestamp() * 1000),
"工时(小时)": 4,
"备注": "需要申请服务器资源"
}
]
# 批量创建记录
created = bitable.batch_create(app_token, table_id, sample_tasks)
print(f"[OK] 成功创建 {len(created)} 条示例记录")
# 显示创建的记录
for i, record in enumerate(created, 1):
fields = record["fields"]
print(f" {i}. {fields.get('任务名称')} - {fields.get('状态')} ({fields.get('优先级')}优先级)")
return created
def demo_query_data(bitable: FeishuBitable, app_token: str, table_id: str):
"""查询数据演示"""
print("\n" + "="*50)
print("Step 5: 查询数据演示")
print("="*50)
# 查询所有记录
all_records = bitable.list_records(app_token, table_id)
print(f"[OK] 共 {len(all_records)} 条记录")
# 获取字段定义
fields = bitable.get_fields(app_token, table_id)
print(f"[OK] 共 {len(fields)} 个字段:")
for f in fields:
print(f" - {f['field_name']} (类型: {f['type']})")
return all_records
def main():
"""运行完整 Demo"""
print("\n" + "#"*60)
print("# 飞书多维表格 Demo - zhiyun.ai")
print("#"*60)
try:
# Step 1: 验证凭证
bitable = demo_verify_credentials()
# Step 2: 创建多维表格
app_token = demo_create_bitable(bitable)
# Step 3: 创建数据表
table_id = demo_create_task_table(bitable, app_token)
# Step 4: 添加示例数据
demo_add_sample_data(bitable, app_token, table_id)
# Step 5: 查询数据
demo_query_data(bitable, app_token, table_id)
# 完成
print("\n" + "="*60)
print("Demo 完成!")
print("="*60)
print(f"\n多维表格信息:")
print(f" app_token: {app_token}")
print(f" table_id: {table_id}")
print(f"\n访问地址:")
print(f" https://zhiyun-ai.feishu.cn/base/{app_token}?table={table_id}")
print()
return app_token, table_id
except Exception as e:
print(f"\n[ERROR] {e}")
raise
if __name__ == "__main__":
main()