Merge remote-tracking branch 'origin/master'
This commit is contained in:
41
.claude/skills/defuddle/SKILL.md
Normal file
41
.claude/skills/defuddle/SKILL.md
Normal file
@@ -0,0 +1,41 @@
|
||||
---
|
||||
name: defuddle
|
||||
description: Extract clean markdown content from web pages using Defuddle CLI, removing clutter and navigation to save tokens. Use instead of WebFetch when the user provides a URL to read or analyze, for online documentation, articles, blog posts, or any standard web page. Do NOT use for URLs ending in .md — those are already markdown, use WebFetch directly.
|
||||
---
|
||||
|
||||
# Defuddle
|
||||
|
||||
Use Defuddle CLI to extract clean readable content from web pages. Prefer over WebFetch for standard web pages — it removes navigation, ads, and clutter, reducing token usage.
|
||||
|
||||
If not installed: `npm install -g defuddle`
|
||||
|
||||
## Usage
|
||||
|
||||
Always use `--md` for markdown output:
|
||||
|
||||
```bash
|
||||
defuddle parse <url> --md
|
||||
```
|
||||
|
||||
Save to file:
|
||||
|
||||
```bash
|
||||
defuddle parse <url> --md -o content.md
|
||||
```
|
||||
|
||||
Extract specific metadata:
|
||||
|
||||
```bash
|
||||
defuddle parse <url> -p title
|
||||
defuddle parse <url> -p description
|
||||
defuddle parse <url> -p domain
|
||||
```
|
||||
|
||||
## Output formats
|
||||
|
||||
| Flag | Format |
|
||||
|------|--------|
|
||||
| `--md` | Markdown (default choice) |
|
||||
| `--json` | JSON with both HTML and markdown |
|
||||
| (none) | HTML |
|
||||
| `-p <name>` | Specific metadata property |
|
||||
476
.claude/skills/excalidraw-diagram/SKILL.md
Normal file
476
.claude/skills/excalidraw-diagram/SKILL.md
Normal file
@@ -0,0 +1,476 @@
|
||||
---
|
||||
name: excalidraw-diagram
|
||||
description: Generate Excalidraw diagrams from text content. Supports three output modes - Obsidian (.md), Standard (.excalidraw), and Animated (.excalidraw with animation order). Triggers on "Excalidraw", "画图", "流程图", "思维导图", "可视化", "diagram", "标准Excalidraw", "standard excalidraw", "Excalidraw动画", "动画图", "animate".
|
||||
metadata:
|
||||
version: 1.2.1
|
||||
---
|
||||
|
||||
# Excalidraw Diagram Generator
|
||||
|
||||
Create Excalidraw diagrams from text content with multiple output formats.
|
||||
|
||||
## Output Modes
|
||||
|
||||
根据用户的触发词选择输出模式:
|
||||
|
||||
| 触发词 | 输出模式 | 文件格式 | 用途 |
|
||||
|--------|----------|----------|------|
|
||||
| `Excalidraw`、`画图`、`流程图`、`思维导图` | **Obsidian**(默认) | `.md` | 在 Obsidian 中直接打开 |
|
||||
| `标准Excalidraw`、`standard excalidraw` | **Standard** | `.excalidraw` | 在 excalidraw.com 打开/编辑/分享 |
|
||||
| `Excalidraw动画`、`动画图`、`animate` | **Animated** | `.excalidraw` | 拖到 excalidraw-animate 生成动画 |
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Detect output mode** from trigger words (see Output Modes table above)
|
||||
2. Analyze content - identify concepts, relationships, hierarchy
|
||||
3. Choose diagram type (see Diagram Types below)
|
||||
4. Generate Excalidraw JSON (add animation order if Animated mode)
|
||||
5. Output in correct format based on mode
|
||||
6. **Automatically save to current working directory**
|
||||
7. Notify user with file path and usage instructions
|
||||
|
||||
## Output Formats
|
||||
|
||||
### Mode 1: Obsidian Format (Default)
|
||||
|
||||
**严格按照以下结构输出,不得有任何修改:**
|
||||
|
||||
```markdown
|
||||
---
|
||||
excalidraw-plugin: parsed
|
||||
tags: [excalidraw]
|
||||
---
|
||||
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving'
|
||||
|
||||
# Excalidraw Data
|
||||
|
||||
## Text Elements
|
||||
%%
|
||||
## Drawing
|
||||
\`\`\`json
|
||||
{JSON 完整数据}
|
||||
\`\`\`
|
||||
%%
|
||||
```
|
||||
|
||||
**关键要点:**
|
||||
- Frontmatter 必须包含 `tags: [excalidraw]`
|
||||
- 警告信息必须完整
|
||||
- JSON 必须被 `%%` 标记包围
|
||||
- 不能使用 `excalidraw-plugin: parsed` 以外的其他 frontmatter 设置
|
||||
- **文件扩展名**:`.md`
|
||||
|
||||
### Mode 2: Standard Excalidraw Format
|
||||
|
||||
直接输出纯 JSON 文件,可在 excalidraw.com 打开:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [...],
|
||||
"appState": {
|
||||
"gridSize": null,
|
||||
"viewBackgroundColor": "#ffffff"
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
```
|
||||
|
||||
**关键要点:**
|
||||
- `source` 使用 `https://excalidraw.com`(不是 Obsidian 插件)
|
||||
- 纯 JSON,无 Markdown 包装
|
||||
- **文件扩展名**:`.excalidraw`
|
||||
|
||||
### Mode 3: Animated Excalidraw Format
|
||||
|
||||
与 Standard 格式相同,但每个元素添加 `customData.animate` 字段控制动画顺序:
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "element-1",
|
||||
"type": "rectangle",
|
||||
"customData": {
|
||||
"animate": {
|
||||
"order": 1,
|
||||
"duration": 500
|
||||
}
|
||||
},
|
||||
...其他标准字段
|
||||
}
|
||||
```
|
||||
|
||||
**动画顺序规则:**
|
||||
- `order`: 动画播放顺序(1, 2, 3...),数字越小越先出现
|
||||
- `duration`: 该元素的绘制时长(毫秒),默认 500
|
||||
- 相同 `order` 的元素同时出现
|
||||
- 建议顺序:标题 → 主要框架 → 连接线 → 细节文字
|
||||
|
||||
**使用方法:**
|
||||
1. 生成 `.excalidraw` 文件
|
||||
2. 拖到 https://dai-shi.github.io/excalidraw-animate/
|
||||
3. 点击 Animate 预览,然后导出 SVG 或 WebM
|
||||
|
||||
**文件扩展名**:`.excalidraw`
|
||||
|
||||
---
|
||||
|
||||
## Diagram Types & Selection Guide
|
||||
|
||||
选择合适的图表形式,以提升理解力与视觉吸引力。
|
||||
|
||||
| 类型 | 英文 | 使用场景 | 做法 |
|
||||
|------|------|---------|------|
|
||||
| **流程图** | Flowchart | 步骤说明、工作流程、任务执行顺序 | 用箭头连接各步骤,清晰表达流程走向 |
|
||||
| **思维导图** | Mind Map | 概念发散、主题分类、灵感捕捉 | 以中心为核心向外发散,放射状结构 |
|
||||
| **层级图** | Hierarchy | 组织结构、内容分级、系统拆解 | 自上而下或自左至右构建层级节点 |
|
||||
| **关系图** | Relationship | 要素之间的影响、依赖、互动 | 图形间用连线表示关联,箭头与说明 |
|
||||
| **对比图** | Comparison | 两种以上方案或观点的对照分析 | 左右两栏或表格形式,标明比较维度 |
|
||||
| **时间线图** | Timeline | 事件发展、项目进度、模型演化 | 以时间为轴,标出关键时间点与事件 |
|
||||
| **矩阵图** | Matrix | 双维度分类、任务优先级、定位 | 建立 X 与 Y 两个维度,坐标平面安置 |
|
||||
| **自由布局** | Freeform | 内容零散、灵感记录、初步信息收集 | 无需结构限制,自由放置图块与箭头 |
|
||||
|
||||
## Design Rules
|
||||
|
||||
### Text & Format
|
||||
- **所有文本元素必须使用** `fontFamily: 5`(Excalifont 手写字体)
|
||||
- **文本中的双引号替换规则**:`"` 替换为 `『』`
|
||||
- **文本中的圆括号替换规则**:`()` 替换为 `「」`
|
||||
- **字体大小规则**(硬性下限,低于此值在正常缩放下不可读):
|
||||
- 标题:20-28px(最小 20px)
|
||||
- 副标题:18-20px
|
||||
- 正文/标签:16-18px(最小 16px)
|
||||
- 次要注释:14px(仅限不重要的辅助说明,慎用)
|
||||
- **绝对禁止低于 14px**
|
||||
- **行高**:所有文本使用 `lineHeight: 1.25`
|
||||
- **文字居中估算**:独立文本元素没有自动居中,需手动计算 x 坐标:
|
||||
- 估算文字宽度:`estimatedWidth = text.length * fontSize * 0.5`(CJK 字符用 `* 1.0`)
|
||||
- 居中公式:`x = centerX - estimatedWidth / 2`
|
||||
- 示例:文字 "Hello"(5字符, fontSize 20)居中于 x=300 → `estimatedWidth = 5 * 20 * 0.5 = 50` → `x = 300 - 25 = 275`
|
||||
|
||||
### Layout & Design
|
||||
- **画布范围**:建议所有元素在 0-1200 x 0-800 区域内
|
||||
- **最小形状尺寸**:带文字的矩形/椭圆不小于 120x60px
|
||||
- **元素间距**:最小 20-30px 间距,防止重叠
|
||||
- **层次清晰**:使用不同颜色和形状区分不同层级的信息
|
||||
- **图形元素**:适当使用矩形框、圆形、箭头等元素来组织信息
|
||||
- **禁止 Emoji**:不要在图表文本中使用任何 Emoji 符号,如需视觉标记请使用简单图形(圆形、方形、箭头)或颜色区分
|
||||
|
||||
### Color Palette
|
||||
|
||||
**文字颜色(strokeColor for text):**
|
||||
|
||||
| 用途 | 色值 | 说明 |
|
||||
|------|------|------|
|
||||
| 标题 | `#1e40af` | 深蓝 |
|
||||
| 副标题/连接线 | `#3b82f6` | 亮蓝 |
|
||||
| 正文文字 | `#374151` | 深灰(白底最浅不低于 `#757575`) |
|
||||
| 强调/重点 | `#f59e0b` | 金色 |
|
||||
|
||||
**形状填充色(backgroundColor, fillStyle: "solid"):**
|
||||
|
||||
| 色值 | 语义 | 适用场景 |
|
||||
|------|------|---------|
|
||||
| `#a5d8ff` | 浅蓝 | 输入、数据源、主要节点 |
|
||||
| `#b2f2bb` | 浅绿 | 成功、输出、已完成 |
|
||||
| `#ffd8a8` | 浅橙 | 警告、待处理、外部依赖 |
|
||||
| `#d0bfff` | 浅紫 | 处理中、中间件、特殊项 |
|
||||
| `#ffc9c9` | 浅红 | 错误、关键、告警 |
|
||||
| `#fff3bf` | 浅黄 | 备注、决策、规划 |
|
||||
| `#c3fae8` | 浅青 | 存储、数据、缓存 |
|
||||
| `#eebefa` | 浅粉 | 分析、指标、统计 |
|
||||
|
||||
**区域背景色(大矩形 + opacity: 30,用于分层图表):**
|
||||
|
||||
| 色值 | 语义 |
|
||||
|------|------|
|
||||
| `#dbe4ff` | 前端/UI 层 |
|
||||
| `#e5dbff` | 逻辑/处理层 |
|
||||
| `#d3f9d8` | 数据/工具层 |
|
||||
|
||||
**对比度规则:**
|
||||
- 白底上文字最浅不低于 `#757575`,否则不可读
|
||||
- 浅色填充上用深色变体文字(如浅绿底用 `#15803d`,不用 `#22c55e`)
|
||||
- 避免浅灰色文字(`#b0b0b0`、`#999`)出现在白底上
|
||||
|
||||
参考:[references/excalidraw-schema.md](references/excalidraw-schema.md)
|
||||
|
||||
## JSON Structure
|
||||
|
||||
**Obsidian 模式:**
|
||||
```json
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://github.com/zsviczian/obsidian-excalidraw-plugin",
|
||||
"elements": [...],
|
||||
"appState": { "gridSize": null, "viewBackgroundColor": "#ffffff" },
|
||||
"files": {}
|
||||
}
|
||||
```
|
||||
|
||||
**Standard / Animated 模式:**
|
||||
```json
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [...],
|
||||
"appState": { "gridSize": null, "viewBackgroundColor": "#ffffff" },
|
||||
"files": {}
|
||||
}
|
||||
```
|
||||
|
||||
## Element Template
|
||||
|
||||
Each element requires these fields (do NOT add extra fields like `frameId`, `index`, `versionNonce`, `rawText` -- they may cause issues on excalidraw.com. `boundElements` must be `null` not `[]`, `updated` must be `1` not timestamps):
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-id",
|
||||
"type": "rectangle",
|
||||
"x": 100, "y": 100,
|
||||
"width": 200, "height": 50,
|
||||
"angle": 0,
|
||||
"strokeColor": "#1e1e1e",
|
||||
"backgroundColor": "transparent",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"roundness": {"type": 3},
|
||||
"seed": 123456789,
|
||||
"version": 1,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false
|
||||
}
|
||||
```
|
||||
|
||||
`strokeStyle` values: `"solid"`(实线,默认)| `"dashed"`(虚线)| `"dotted"`(点线)。虚线适合表示可选路径、异步流、弱关联等。
|
||||
|
||||
Text elements add:
|
||||
```json
|
||||
{
|
||||
"text": "显示文本",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "显示文本",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
```
|
||||
|
||||
**Animated 模式额外添加** `customData` 字段:
|
||||
```json
|
||||
{
|
||||
"id": "title-1",
|
||||
"type": "text",
|
||||
"customData": {
|
||||
"animate": {
|
||||
"order": 1,
|
||||
"duration": 500
|
||||
}
|
||||
},
|
||||
...其他字段
|
||||
}
|
||||
```
|
||||
|
||||
See [references/excalidraw-schema.md](references/excalidraw-schema.md) for all element types.
|
||||
|
||||
---
|
||||
|
||||
## Additional Technical Requirements
|
||||
|
||||
### Text Elements 处理
|
||||
- `## Text Elements` 部分在 Markdown 中**必须留空**,仅用 `%%` 作为分隔符
|
||||
- Obsidian ExcaliDraw 插件会根据 JSON 数据**自动填充文本元素**
|
||||
- 不需要手动列出所有文本内容
|
||||
|
||||
### 坐标与布局
|
||||
- **坐标系统**:左上角为原点 (0,0)
|
||||
- **推荐范围**:所有元素在 0-1200 x 0-800 像素范围内
|
||||
- **元素 ID**:每个元素需要唯一的 `id`(可以是字符串,如「title」「box1」等)
|
||||
|
||||
### Required Fields for All Elements
|
||||
|
||||
**IMPORTANT**: Do NOT include `frameId`, `index`, `versionNonce`, or `rawText` fields. Use `boundElements: null` (not `[]`), and `updated: 1` (not timestamps).
|
||||
|
||||
```json
|
||||
{
|
||||
"id": "unique-identifier",
|
||||
"type": "rectangle|text|arrow|ellipse|diamond",
|
||||
"x": 100, "y": 100,
|
||||
"width": 200, "height": 50,
|
||||
"angle": 0,
|
||||
"strokeColor": "#color-hex",
|
||||
"backgroundColor": "transparent|#color-hex",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"strokeStyle": "solid|dashed|dotted",
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"groupIds": [],
|
||||
"roundness": {"type": 3},
|
||||
"seed": 123456789,
|
||||
"version": 1,
|
||||
"isDeleted": false,
|
||||
"boundElements": null,
|
||||
"updated": 1,
|
||||
"link": null,
|
||||
"locked": false
|
||||
}
|
||||
```
|
||||
|
||||
### Text-Specific Properties
|
||||
文本元素 (type: "text") 需要额外属性(do NOT include `rawText`):
|
||||
```json
|
||||
{
|
||||
"text": "显示文本",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"containerId": null,
|
||||
"originalText": "显示文本",
|
||||
"autoResize": true,
|
||||
"lineHeight": 1.25
|
||||
}
|
||||
```
|
||||
|
||||
### appState 配置
|
||||
```json
|
||||
"appState": {
|
||||
"gridSize": null,
|
||||
"viewBackgroundColor": "#ffffff"
|
||||
}
|
||||
```
|
||||
|
||||
### files 字段
|
||||
```json
|
||||
"files": {}
|
||||
```
|
||||
|
||||
## Common Mistakes to Avoid
|
||||
|
||||
- **文字偏移** — 独立 text 元素的 `x` 是左边缘,不是中心。必须用居中公式手动计算,否则文字会偏到一边
|
||||
- **元素重叠** — y 坐标相近的元素容易堆叠。放置新元素前检查与周围元素是否有至少 20px 间距
|
||||
- **画布留白不足** — 内容不要贴着画布边缘。在四周留 50-80px 的 padding
|
||||
- **标题没有居中于图表** — 标题应居中于下方图表的整体宽度,不是固定在 x=0
|
||||
- **箭头标签溢出** — 长文字标签(如 "ATP + NADPH")会超出短箭头。保持标签简短或加大箭头长度
|
||||
- **对比度不够** — 浅色文字在白底上几乎不可见。文字颜色不低于 `#757575`,有色文字用深色变体
|
||||
- **字号太小** — 低于 14px 在正常缩放下不可读,正文最小 16px
|
||||
|
||||
## Implementation Notes
|
||||
|
||||
### Auto-save & File Generation Workflow
|
||||
|
||||
当生成 Excalidraw 图表时,**必须自动执行以下步骤**:
|
||||
|
||||
#### 1. 选择合适的图表类型
|
||||
- 根据用户提供的内容特性,参考上方 「Diagram Types & Selection Guide」 表
|
||||
- 分析内容的核心诉求,选择最合适的可视化形式
|
||||
|
||||
#### 2. 生成有意义的文件名
|
||||
|
||||
根据输出模式选择文件扩展名:
|
||||
|
||||
| 模式 | 文件名格式 | 示例 |
|
||||
|------|-----------|------|
|
||||
| Obsidian | `[主题].[类型].md` | `商业模式.relationship.md` |
|
||||
| Standard | `[主题].[类型].excalidraw` | `商业模式.relationship.excalidraw` |
|
||||
| Animated | `[主题].[类型].animate.excalidraw` | `商业模式.relationship.animate.excalidraw` |
|
||||
|
||||
- 优先使用中文以提高清晰度
|
||||
|
||||
#### 3. 使用 Write 工具自动保存文件
|
||||
- **保存位置**:当前工作目录(自动检测环境变量)
|
||||
- **完整路径**:`{current_directory}/[filename].md`
|
||||
- 这样可以实现灵活迁移,无需硬编码路径
|
||||
|
||||
#### 4. 确保 Markdown 结构完全正确
|
||||
**必须按以下格式生成**(不能有任何修改):
|
||||
|
||||
```markdown
|
||||
---
|
||||
excalidraw-plugin: parsed
|
||||
tags: [excalidraw]
|
||||
---
|
||||
==⚠ Switch to EXCALIDRAW VIEW in the MORE OPTIONS menu of this document. ⚠== You can decompress Drawing data with the command palette: 'Decompress current Excalidraw file'. For more info check in plugin settings under 'Saving'
|
||||
|
||||
# Excalidraw Data
|
||||
|
||||
## Text Elements
|
||||
%%
|
||||
## Drawing
|
||||
\`\`\`json
|
||||
{完整的 JSON 数据}
|
||||
\`\`\`
|
||||
%%
|
||||
```
|
||||
|
||||
#### 5. JSON 数据要求
|
||||
- 包含完整的 Excalidraw JSON 结构
|
||||
- 所有文本元素使用 `fontFamily: 5`
|
||||
- 文本中的 `"` 替换为 `『』`
|
||||
- 文本中的 `()` 替换为 `「」`
|
||||
- JSON 格式必须有效,通过语法检查
|
||||
- 所有元素有唯一的 `id`
|
||||
- 包含 `appState` 和 `files: {}` 字段
|
||||
|
||||
#### 6. 用户反馈与确认
|
||||
向用户报告:
|
||||
- 图表已生成
|
||||
- 精确的保存位置
|
||||
- 如何在 Obsidian 中查看
|
||||
- 图表的设计选择说明(选择了什么类型的图表、为什么)
|
||||
- 是否需要调整或修改
|
||||
|
||||
### Example Output Messages
|
||||
|
||||
**Obsidian 模式:**
|
||||
```
|
||||
Excalidraw 图已生成!
|
||||
|
||||
保存位置:商业模式.relationship.md
|
||||
|
||||
使用方法:
|
||||
1. 在 Obsidian 中打开此文件
|
||||
2. 点击右上角 MORE OPTIONS 菜单
|
||||
3. 选择 Switch to EXCALIDRAW VIEW
|
||||
```
|
||||
|
||||
**Standard 模式:**
|
||||
```
|
||||
Excalidraw 图已生成!
|
||||
|
||||
保存位置:商业模式.relationship.excalidraw
|
||||
|
||||
使用方法:
|
||||
1. 打开 https://excalidraw.com
|
||||
2. 点击左上角菜单 → Open → 选择此文件
|
||||
3. 或直接拖拽文件到 excalidraw.com 页面
|
||||
```
|
||||
|
||||
**Animated 模式:**
|
||||
```
|
||||
Excalidraw 动画图已生成!
|
||||
|
||||
保存位置:商业模式.relationship.animate.excalidraw
|
||||
|
||||
动画顺序:标题(1) → 主框架(2-4) → 连接线(5-7) → 说明文字(8-10)
|
||||
|
||||
生成动画:
|
||||
1. 打开 https://dai-shi.github.io/excalidraw-animate/
|
||||
2. 点击 Load File 选择此文件
|
||||
3. 预览动画效果
|
||||
4. 点击 Export 导出 SVG 或 WebM
|
||||
```
|
||||
@@ -0,0 +1,201 @@
|
||||
# Excalidraw JSON Schema Reference
|
||||
|
||||
## Color Palette
|
||||
|
||||
### Primary Colors
|
||||
| Purpose | Color | Hex |
|
||||
|---------|-------|-----|
|
||||
| Main Title | Deep Blue | `#1e40af` |
|
||||
| Subtitle | Medium Blue | `#3b82f6` |
|
||||
| Body Text | Dark Gray | `#374151` |
|
||||
| Emphasis | Orange | `#f59e0b` |
|
||||
| Success | Green | `#10b981` |
|
||||
| Warning | Red | `#ef4444` |
|
||||
|
||||
### Background Colors
|
||||
| Purpose | Color | Hex |
|
||||
|---------|-------|-----|
|
||||
| Light Blue | Background | `#dbeafe` |
|
||||
| Light Gray | Neutral | `#f3f4f6` |
|
||||
| Light Orange | Highlight | `#fef3c7` |
|
||||
| Light Green | Success | `#d1fae5` |
|
||||
| Light Purple | Accent | `#ede9fe` |
|
||||
|
||||
## Element Types
|
||||
|
||||
### Rectangle
|
||||
```json
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "unique-id",
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"width": 200,
|
||||
"height": 80,
|
||||
"strokeColor": "#1e40af",
|
||||
"backgroundColor": "#dbeafe",
|
||||
"fillStyle": "solid",
|
||||
"strokeWidth": 2,
|
||||
"roughness": 1,
|
||||
"opacity": 100,
|
||||
"roundness": { "type": 3 }
|
||||
}
|
||||
```
|
||||
|
||||
### Text
|
||||
```json
|
||||
{
|
||||
"type": "text",
|
||||
"id": "unique-id",
|
||||
"x": 150,
|
||||
"y": 130,
|
||||
"text": "Content here",
|
||||
"fontSize": 20,
|
||||
"fontFamily": 5,
|
||||
"textAlign": "center",
|
||||
"verticalAlign": "middle",
|
||||
"strokeColor": "#1e40af",
|
||||
"backgroundColor": "transparent"
|
||||
}
|
||||
```
|
||||
|
||||
### Arrow
|
||||
```json
|
||||
{
|
||||
"type": "arrow",
|
||||
"id": "unique-id",
|
||||
"x": 300,
|
||||
"y": 140,
|
||||
"width": 100,
|
||||
"height": 0,
|
||||
"points": [[0, 0], [100, 0]],
|
||||
"strokeColor": "#374151",
|
||||
"strokeWidth": 2,
|
||||
"startArrowhead": null,
|
||||
"endArrowhead": "arrow"
|
||||
}
|
||||
```
|
||||
|
||||
### Ellipse
|
||||
```json
|
||||
{
|
||||
"type": "ellipse",
|
||||
"id": "unique-id",
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"width": 120,
|
||||
"height": 120,
|
||||
"strokeColor": "#10b981",
|
||||
"backgroundColor": "#d1fae5",
|
||||
"fillStyle": "solid"
|
||||
}
|
||||
```
|
||||
|
||||
### Diamond
|
||||
```json
|
||||
{
|
||||
"type": "diamond",
|
||||
"id": "unique-id",
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"width": 150,
|
||||
"height": 100,
|
||||
"strokeColor": "#f59e0b",
|
||||
"backgroundColor": "#fef3c7",
|
||||
"fillStyle": "solid"
|
||||
}
|
||||
```
|
||||
|
||||
### Line
|
||||
```json
|
||||
{
|
||||
"type": "line",
|
||||
"id": "unique-id",
|
||||
"x": 100,
|
||||
"y": 100,
|
||||
"points": [[0, 0], [200, 100]],
|
||||
"strokeColor": "#374151",
|
||||
"strokeWidth": 2
|
||||
}
|
||||
```
|
||||
|
||||
## Full JSON Structure
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "excalidraw",
|
||||
"version": 2,
|
||||
"source": "https://excalidraw.com",
|
||||
"elements": [
|
||||
// Array of elements
|
||||
],
|
||||
"appState": {
|
||||
"gridSize": null,
|
||||
"viewBackgroundColor": "#ffffff"
|
||||
},
|
||||
"files": {}
|
||||
}
|
||||
```
|
||||
|
||||
## Font Family Values
|
||||
|
||||
| Value | Font Name |
|
||||
|-------|-----------|
|
||||
| 1 | Virgil (hand-drawn) |
|
||||
| 2 | Helvetica |
|
||||
| 3 | Cascadia |
|
||||
| 4 | Assistant |
|
||||
| 5 | Excalifont (recommended) |
|
||||
|
||||
## Fill Styles
|
||||
|
||||
- `solid` - Solid fill
|
||||
- `hachure` - Hatched lines
|
||||
- `cross-hatch` - Cross-hatched
|
||||
- `dots` - Dotted pattern
|
||||
|
||||
## Roundness Types
|
||||
|
||||
- `{ "type": 1 }` - Sharp corners
|
||||
- `{ "type": 2 }` - Slight rounding
|
||||
- `{ "type": 3 }` - Full rounding (recommended)
|
||||
|
||||
## Element Binding
|
||||
|
||||
To connect text to a container:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "rectangle",
|
||||
"id": "container-id",
|
||||
"boundElements": [{ "id": "text-id", "type": "text" }]
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "text",
|
||||
"id": "text-id",
|
||||
"containerId": "container-id"
|
||||
}
|
||||
```
|
||||
|
||||
## Arrow Binding
|
||||
|
||||
To connect arrows to shapes:
|
||||
|
||||
```json
|
||||
{
|
||||
"type": "arrow",
|
||||
"startBinding": {
|
||||
"elementId": "source-shape-id",
|
||||
"focus": 0,
|
||||
"gap": 5
|
||||
},
|
||||
"endBinding": {
|
||||
"elementId": "target-shape-id",
|
||||
"focus": 0,
|
||||
"gap": 5
|
||||
}
|
||||
}
|
||||
```
|
||||
276
.claude/skills/mermaid-visualizer/SKILL.md
Normal file
276
.claude/skills/mermaid-visualizer/SKILL.md
Normal file
@@ -0,0 +1,276 @@
|
||||
---
|
||||
name: mermaid-visualizer
|
||||
description: Transform text content into professional Mermaid diagrams for presentations and documentation. Use when users ask to visualize concepts, create flowcharts, or make diagrams from text. Supports process flows, system architectures, comparisons, mindmaps, and more with built-in syntax error prevention.
|
||||
---
|
||||
|
||||
# Mermaid Visualizer
|
||||
|
||||
## Overview
|
||||
|
||||
Convert text content into clean, professional Mermaid diagrams optimized for presentations and documentation. Automatically handles common syntax pitfalls (list syntax conflicts, subgraph naming, spacing issues) to ensure diagrams render correctly in Obsidian, GitHub, and other Mermaid-compatible platforms.
|
||||
|
||||
## Quick Start
|
||||
|
||||
When creating a Mermaid diagram:
|
||||
|
||||
1. **Analyze the content** - Identify key concepts, relationships, and flow
|
||||
2. **Choose diagram type** - Select the most appropriate visualization (see Diagram Types below)
|
||||
3. **Select configuration** - Determine layout, detail level, and styling
|
||||
4. **Generate diagram** - Create syntactically correct Mermaid code
|
||||
5. **Output in markdown** - Wrap in proper code fence with optional explanation
|
||||
|
||||
**Default assumptions:**
|
||||
- Vertical layout (TB) unless horizontal requested
|
||||
- Medium detail level (balanced between simplicity and information)
|
||||
- Professional color scheme with semantic colors
|
||||
- Obsidian/GitHub compatible syntax
|
||||
|
||||
## Diagram Types
|
||||
|
||||
### 1. Process Flow (graph TB/LR)
|
||||
**Best for:** Workflows, decision trees, sequential processes, AI agent architectures
|
||||
|
||||
**Use when:** Content describes steps, stages, or a sequence of actions
|
||||
|
||||
**Key features:**
|
||||
- Swimlanes via subgraph for grouping related steps
|
||||
- Arrow labels for transitions
|
||||
- Feedback loops and branches
|
||||
- Color-coded stages
|
||||
|
||||
**Configuration options:**
|
||||
- `layout`: "vertical" (TB), "horizontal" (LR)
|
||||
- `detail`: "simple" (core steps only), "standard" (with descriptions), "detailed" (with annotations)
|
||||
- `style`: "minimal", "professional", "colorful"
|
||||
|
||||
### 2. Circular Flow (graph TD with circular layout)
|
||||
**Best for:** Cyclic processes, continuous improvement loops, agent feedback systems
|
||||
|
||||
**Use when:** Content emphasizes iteration, feedback, or circular relationships
|
||||
|
||||
**Key features:**
|
||||
- Central hub with radiating elements
|
||||
- Curved feedback arrows
|
||||
- Clear cycle indicators
|
||||
|
||||
### 3. Comparison Diagram (graph TB with parallel paths)
|
||||
**Best for:** Before/after comparisons, A vs B analysis, traditional vs modern systems
|
||||
|
||||
**Use when:** Content contrasts two or more approaches or systems
|
||||
|
||||
**Key features:**
|
||||
- Side-by-side layout
|
||||
- Central comparison node
|
||||
- Clear differentiation via color/style
|
||||
|
||||
### 4. Mindmap
|
||||
**Best for:** Hierarchical concepts, knowledge organization, topic breakdowns
|
||||
|
||||
**Use when:** Content is hierarchical with clear parent-child relationships
|
||||
|
||||
**Key features:**
|
||||
- Radial tree structure
|
||||
- Multiple levels of nesting
|
||||
- Clean visual hierarchy
|
||||
|
||||
### 5. Sequence Diagram
|
||||
**Best for:** Interactions between components, API calls, message flows
|
||||
|
||||
**Use when:** Content involves communication between actors/systems over time
|
||||
|
||||
**Key features:**
|
||||
- Timeline-based layout
|
||||
- Clear actor separation
|
||||
- Activation boxes for processes
|
||||
|
||||
### 6. State Diagram
|
||||
**Best for:** System states, status transitions, lifecycle stages
|
||||
|
||||
**Use when:** Content describes states and transitions between them
|
||||
|
||||
**Key features:**
|
||||
- Clear state nodes
|
||||
- Labeled transitions
|
||||
- Start and end states
|
||||
|
||||
## Critical Syntax Rules
|
||||
|
||||
**Always follow these rules to prevent parsing errors:**
|
||||
|
||||
### Rule 1: Avoid List Syntax Conflicts
|
||||
```
|
||||
❌ WRONG: [1. Perception] → Triggers "Unsupported markdown: list"
|
||||
✅ RIGHT: [1.Perception] → Remove space after period
|
||||
✅ RIGHT: [① Perception] → Use circled numbers (①②③④⑤⑥⑦⑧⑨⑩)
|
||||
✅ RIGHT: [(1) Perception] → Use parentheses
|
||||
✅ RIGHT: [Step 1: Perception] → Use "Step" prefix
|
||||
```
|
||||
|
||||
### Rule 2: Subgraph Naming
|
||||
```
|
||||
❌ WRONG: subgraph AI Agent Core → Space in name without quotes
|
||||
✅ RIGHT: subgraph agent["AI Agent Core"] → Use ID with display name
|
||||
✅ RIGHT: subgraph agent → Use simple ID only
|
||||
```
|
||||
|
||||
### Rule 3: Node References
|
||||
```
|
||||
❌ WRONG: Title --> AI Agent Core → Reference display name directly
|
||||
✅ RIGHT: Title --> agent → Reference subgraph ID
|
||||
```
|
||||
|
||||
### Rule 4: Special Characters in Node Text
|
||||
```
|
||||
✅ Use quotes for text with spaces: ["Text with spaces"]
|
||||
✅ Escape or avoid: quotation marks → use 『』instead
|
||||
✅ Escape or avoid: parentheses → use 「」instead
|
||||
✅ Line breaks in circle nodes only: ((Text<br/>Break))
|
||||
```
|
||||
|
||||
### Rule 5: Arrow Types
|
||||
- `-->` solid arrow
|
||||
- `-.->` dashed arrow (for supporting systems, optional paths)
|
||||
- `==>` thick arrow (for emphasis)
|
||||
- `~~~` invisible link (for layout only)
|
||||
|
||||
For complete syntax reference and edge cases, see [references/syntax-rules.md](references/syntax-rules.md)
|
||||
|
||||
## Configuration Options
|
||||
|
||||
All diagrams accept these parameters:
|
||||
|
||||
**Layout:**
|
||||
- `direction`: "vertical" (TB), "horizontal" (LR), "right-to-left" (RL), "bottom-to-top" (BT)
|
||||
- `aspect`: "portrait" (default), "landscape" (wide), "square"
|
||||
|
||||
**Detail Level:**
|
||||
- `simple`: Core elements only, minimal labels
|
||||
- `standard`: Balanced detail with key descriptions (default)
|
||||
- `detailed`: Full annotations, explanations, and metadata
|
||||
- `presentation`: Optimized for slides (larger text, fewer details)
|
||||
|
||||
**Style:**
|
||||
- `minimal`: Monochrome, clean lines
|
||||
- `professional`: Semantic colors, clear hierarchy (default)
|
||||
- `colorful`: Vibrant colors, high contrast
|
||||
- `academic`: Formal styling for papers/documentation
|
||||
|
||||
**Additional Options:**
|
||||
- `show_legend`: true/false - Include color/symbol legend
|
||||
- `numbered`: true/false - Add sequence numbers to steps
|
||||
- `title`: string - Add diagram title
|
||||
|
||||
## Example Usage Patterns
|
||||
|
||||
**Pattern 1: Basic request**
|
||||
```
|
||||
User: "Visualize the software development lifecycle"
|
||||
Response: [Analyze → Choose graph TB → Generate with standard detail]
|
||||
```
|
||||
|
||||
**Pattern 2: With configuration**
|
||||
```
|
||||
User: "Create a horizontal flowchart of our sales process with lots of detail"
|
||||
Response: [Analyze → Choose graph LR → Generate with detailed level]
|
||||
```
|
||||
|
||||
**Pattern 3: Comparison**
|
||||
```
|
||||
User: "Compare traditional AI vs AI agents"
|
||||
Response: [Analyze → Choose comparison layout → Generate with contrasting styles]
|
||||
```
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Understand the content**
|
||||
- Identify main concepts, entities, and relationships
|
||||
- Determine hierarchy or sequence
|
||||
- Note any comparisons or contrasts
|
||||
|
||||
2. **Select diagram type**
|
||||
- Match content structure to diagram type
|
||||
- Consider user's presentation context
|
||||
- Default to process flow if ambiguous
|
||||
|
||||
3. **Choose configuration**
|
||||
- Apply user-specified options
|
||||
- Use sensible defaults for unspecified options
|
||||
- Optimize for readability
|
||||
|
||||
4. **Generate Mermaid code**
|
||||
- Follow all syntax rules strictly
|
||||
- Use semantic naming (descriptive IDs)
|
||||
- Apply consistent styling
|
||||
- Test for common errors:
|
||||
* No "number. space" patterns in node text
|
||||
* All subgraphs use ID["display name"] format
|
||||
* All node references use IDs not display names
|
||||
|
||||
5. **Output with context**
|
||||
- Wrap in ```mermaid code fence
|
||||
- Add brief explanation of diagram structure
|
||||
- Mention rendering compatibility (Obsidian, GitHub, etc.)
|
||||
- Offer to adjust or create variations
|
||||
|
||||
## Color Scheme Defaults
|
||||
|
||||
Standard professional palette:
|
||||
- Green (#d3f9d8/#2f9e44): Input, perception, start states
|
||||
- Red (#ffe3e3/#c92a2a): Planning, decision points
|
||||
- Purple (#e5dbff/#5f3dc4): Processing, reasoning
|
||||
- Orange (#ffe8cc/#d9480f): Actions, tool usage
|
||||
- Cyan (#c5f6fa/#0c8599): Output, execution, results
|
||||
- Yellow (#fff4e6/#e67700): Storage, memory, data
|
||||
- Pink (#f3d9fa/#862e9c): Learning, optimization
|
||||
- Blue (#e7f5ff/#1971c2): Metadata, definitions, titles
|
||||
- Gray (#f8f9fa/#868e96): Neutral elements, traditional systems
|
||||
|
||||
## Common Patterns
|
||||
|
||||
### Swimlane Pattern (Grouping)
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph core["Core Process"]
|
||||
A --> B --> C
|
||||
end
|
||||
subgraph support["Supporting Systems"]
|
||||
D
|
||||
E
|
||||
end
|
||||
core -.-> support
|
||||
```
|
||||
|
||||
### Feedback Loop Pattern
|
||||
```mermaid
|
||||
graph TB
|
||||
A[Start] --> B[Process]
|
||||
B --> C[End]
|
||||
C -.->|Feedback| A
|
||||
```
|
||||
|
||||
### Hub and Spoke Pattern
|
||||
```mermaid
|
||||
graph TB
|
||||
Central[Hub]
|
||||
A[Spoke 1] --> Central
|
||||
B[Spoke 2] --> Central
|
||||
C[Spoke 3] --> Central
|
||||
```
|
||||
|
||||
## Quality Checklist
|
||||
|
||||
Before outputting, verify:
|
||||
- [ ] No "number. space" patterns in any node text
|
||||
- [ ] All subgraphs use proper ID syntax
|
||||
- [ ] All arrows use correct syntax (-->, -.->)
|
||||
- [ ] Colors applied consistently
|
||||
- [ ] Layout direction specified
|
||||
- [ ] Style declarations present
|
||||
- [ ] No ambiguous node references
|
||||
- [ ] Compatible with Obsidian/GitHub renderers
|
||||
- [ ] **No Emoji** in any node text - use text labels or color coding instead
|
||||
|
||||
## References
|
||||
|
||||
For detailed syntax rules and troubleshooting, see:
|
||||
- [references/syntax-rules.md](references/syntax-rules.md) - Complete syntax reference and error prevention
|
||||
484
.claude/skills/mermaid-visualizer/references/syntax-rules.md
Normal file
484
.claude/skills/mermaid-visualizer/references/syntax-rules.md
Normal file
@@ -0,0 +1,484 @@
|
||||
# Mermaid Syntax Rules Reference
|
||||
|
||||
This reference provides comprehensive syntax rules and error prevention strategies for Mermaid diagrams. Load this when encountering syntax errors or needing detailed syntax information.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
1. [Critical Error Prevention](#critical-error-prevention)
|
||||
2. [Node Syntax](#node-syntax)
|
||||
3. [Subgraph Syntax](#subgraph-syntax)
|
||||
4. [Arrow and Connection Types](#arrow-and-connection-types)
|
||||
5. [Styling and Colors](#styling-and-colors)
|
||||
6. [Layout and Direction](#layout-and-direction)
|
||||
7. [Advanced Patterns](#advanced-patterns)
|
||||
8. [Troubleshooting](#troubleshooting)
|
||||
|
||||
## Critical Error Prevention
|
||||
|
||||
### List Syntax Conflict (Most Common Error)
|
||||
|
||||
**Problem:** Mermaid parser interprets `number. space` as Markdown ordered list syntax.
|
||||
|
||||
**Error Message:** `Parse error: Unsupported markdown: list`
|
||||
|
||||
**Solutions:**
|
||||
|
||||
```mermaid
|
||||
❌ [1. Perception]
|
||||
❌ [2. Planning]
|
||||
❌ [3. Reasoning]
|
||||
|
||||
✅ [1.Perception] # Remove space
|
||||
✅ [① Perception] # Use circled numbers
|
||||
✅ [(1) Perception] # Use parentheses
|
||||
✅ [Step 1: Perception] # Use prefix
|
||||
✅ [Step 1 - Perception] # Use dash
|
||||
✅ [Perception] # Remove numbering
|
||||
```
|
||||
|
||||
**Circled number reference:**
|
||||
```
|
||||
① ② ③ ④ ⑤ ⑥ ⑦ ⑧ ⑨ ⑩ ⑪ ⑫ ⑬ ⑭ ⑮ ⑯ ⑰ ⑱ ⑲ ⑳
|
||||
```
|
||||
|
||||
### Subgraph Naming Rules
|
||||
|
||||
**Rule:** Subgraphs with spaces must use ID + display name format.
|
||||
|
||||
```mermaid
|
||||
❌ subgraph Core Process
|
||||
A --> B
|
||||
end
|
||||
|
||||
✅ subgraph core["Core Process"]
|
||||
A --> B
|
||||
end
|
||||
|
||||
✅ subgraph core_process
|
||||
A --> B
|
||||
end
|
||||
```
|
||||
|
||||
**Referencing subgraphs:**
|
||||
```mermaid
|
||||
❌ Title --> Core Process # Cannot reference display name
|
||||
✅ Title --> core # Must reference ID
|
||||
```
|
||||
|
||||
### Node Reference Rules
|
||||
|
||||
**Rule:** Always reference nodes by ID, never by display text.
|
||||
|
||||
```mermaid
|
||||
# Define nodes
|
||||
A[Display Text A]
|
||||
B["Display Text B"]
|
||||
|
||||
# Reference nodes
|
||||
A --> B ✅ Use node IDs
|
||||
Display Text A --> Display Text B ❌ Cannot use display text
|
||||
```
|
||||
|
||||
## Node Syntax
|
||||
|
||||
### Basic Node Types
|
||||
|
||||
```mermaid
|
||||
# Rectangle (default)
|
||||
A[Rectangle Text]
|
||||
|
||||
# Rectangle with rounded corners
|
||||
B(Rounded Text)
|
||||
|
||||
# Stadium shape
|
||||
C([Stadium Text])
|
||||
|
||||
# Circle
|
||||
D((Circle<br/>Text))
|
||||
|
||||
# Asymmetric shape
|
||||
E>Right Arrow]
|
||||
|
||||
# Rhombus (decision)
|
||||
F{Decision?}
|
||||
|
||||
# Hexagon
|
||||
G{{Hexagon}}
|
||||
|
||||
# Parallelogram
|
||||
H[/Parallelogram/]
|
||||
|
||||
# Database
|
||||
I[(Database)]
|
||||
|
||||
# Trapezoid
|
||||
J[/Trapezoid\]
|
||||
```
|
||||
|
||||
### Node Text Rules
|
||||
|
||||
**Line breaks:**
|
||||
- `<br/>` only works in circle nodes: `((Text<br/>Break))`
|
||||
- For other nodes, use separate annotation nodes or keep text concise
|
||||
|
||||
**Special characters:**
|
||||
- Spaces: Use quotes if needed: `["Text with spaces"]`
|
||||
- Quotes: Replace with 『』or avoid
|
||||
- Parentheses: Replace with 「」or avoid
|
||||
- Colons: Generally safe but avoid if causing issues
|
||||
- Hyphens/dashes: Safe to use
|
||||
|
||||
**Length guidelines:**
|
||||
- Keep node text under 50 characters
|
||||
- Use multiple lines (circle nodes) or separate annotation nodes for longer content
|
||||
- Consider splitting into multiple nodes if text is too long
|
||||
|
||||
## Subgraph Syntax
|
||||
|
||||
### Basic Structure
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
# Correct format with ID and display name
|
||||
subgraph id["Display Name"]
|
||||
direction TB
|
||||
A --> B
|
||||
end
|
||||
|
||||
# Simple ID only (no spaces)
|
||||
subgraph simple
|
||||
C --> D
|
||||
end
|
||||
|
||||
# Can set direction inside subgraph
|
||||
subgraph horiz["Horizontal"]
|
||||
direction LR
|
||||
E --> F
|
||||
end
|
||||
```
|
||||
|
||||
### Nested Subgraphs
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph outer["Outer Group"]
|
||||
direction TB
|
||||
|
||||
subgraph inner1["Inner 1"]
|
||||
A --> B
|
||||
end
|
||||
|
||||
subgraph inner2["Inner 2"]
|
||||
C --> D
|
||||
end
|
||||
|
||||
inner1 -.-> inner2
|
||||
end
|
||||
```
|
||||
|
||||
**Limitation:** Keep nesting to 2 levels maximum for readability.
|
||||
|
||||
### Connecting Subgraphs
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph g1["Group 1"]
|
||||
A[Node A]
|
||||
end
|
||||
|
||||
subgraph g2["Group 2"]
|
||||
B[Node B]
|
||||
end
|
||||
|
||||
# Connect individual nodes (recommended)
|
||||
A --> B
|
||||
|
||||
# Connect subgraphs (creates invisible link for layout)
|
||||
g1 -.-> g2
|
||||
```
|
||||
|
||||
## Arrow and Connection Types
|
||||
|
||||
### Basic Arrows
|
||||
|
||||
```mermaid
|
||||
A --> B # Solid arrow
|
||||
A -.-> B # Dashed arrow
|
||||
A ==> B # Thick arrow
|
||||
A ~~~> B # Invisible link (layout only, not rendered)
|
||||
```
|
||||
|
||||
### Arrow Labels
|
||||
|
||||
```mermaid
|
||||
A -->|Label Text| B
|
||||
A -.->|Optional| B
|
||||
A ==>|Important| B
|
||||
```
|
||||
|
||||
### Multi-target Connections
|
||||
|
||||
```mermaid
|
||||
# One to many
|
||||
A --> B & C & D
|
||||
|
||||
# Many to one
|
||||
A & B & C --> D
|
||||
|
||||
# Chaining
|
||||
A --> B --> C --> D
|
||||
```
|
||||
|
||||
### Bidirectional
|
||||
|
||||
```mermaid
|
||||
A <--> B # Bidirectional solid
|
||||
A <-.-> B # Bidirectional dashed
|
||||
```
|
||||
|
||||
## Styling and Colors
|
||||
|
||||
### Inline Styling
|
||||
|
||||
```mermaid
|
||||
style NodeID fill:#color,stroke:#color,stroke-width:2px
|
||||
```
|
||||
|
||||
### Color Format
|
||||
|
||||
- Hex colors: `#ff0000` or `#f00`
|
||||
- RGB: `rgb(255,0,0)`
|
||||
- Color names: `red`, `blue`, etc. (limited support)
|
||||
|
||||
### Common Style Patterns
|
||||
|
||||
```mermaid
|
||||
# Professional look
|
||||
style A fill:#d3f9d8,stroke:#2f9e44,stroke-width:2px
|
||||
|
||||
# Emphasis
|
||||
style B fill:#ffe3e3,stroke:#c92a2a,stroke-width:3px
|
||||
|
||||
# Muted/secondary
|
||||
style C fill:#f8f9fa,stroke:#dee2e6,stroke-width:1px
|
||||
|
||||
# Title/header
|
||||
style D fill:#1971c2,stroke:#1971c2,stroke-width:3px,color:#ffffff
|
||||
```
|
||||
|
||||
### Styling Multiple Nodes
|
||||
|
||||
```mermaid
|
||||
# Apply same style to multiple nodes
|
||||
style A,B,C fill:#d3f9d8,stroke:#2f9e44,stroke-width:2px
|
||||
```
|
||||
|
||||
## Layout and Direction
|
||||
|
||||
### Direction Codes
|
||||
|
||||
```mermaid
|
||||
graph TB # Top to Bottom (vertical)
|
||||
graph BT # Bottom to Top
|
||||
graph LR # Left to Right (horizontal)
|
||||
graph RL # Right to Left
|
||||
graph TD # Top Down (same as TB)
|
||||
```
|
||||
|
||||
### Layout Control Tips
|
||||
|
||||
1. **Vertical layouts (TB/BT):** Best for sequential processes, hierarchies
|
||||
2. **Horizontal layouts (LR/RL):** Best for timelines, wide displays
|
||||
3. **Mixed directions:** Set different directions in subgraphs
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph vertical["Vertical Flow"]
|
||||
direction TB
|
||||
A --> B --> C
|
||||
end
|
||||
|
||||
subgraph horizontal["Horizontal Flow"]
|
||||
direction LR
|
||||
D --> E --> F
|
||||
end
|
||||
```
|
||||
|
||||
## Advanced Patterns
|
||||
|
||||
### Feedback Loop Pattern
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
A[Start] --> B[Process]
|
||||
B --> C[Output]
|
||||
C -.->|Feedback| A
|
||||
|
||||
style A fill:#d3f9d8,stroke:#2f9e44,stroke-width:2px
|
||||
style B fill:#e5dbff,stroke:#5f3dc4,stroke-width:2px
|
||||
style C fill:#c5f6fa,stroke:#0c8599,stroke-width:2px
|
||||
```
|
||||
|
||||
### Swimlane Pattern
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
subgraph lane1["Lane 1"]
|
||||
A[Step 1] --> B[Step 2]
|
||||
end
|
||||
|
||||
subgraph lane2["Lane 2"]
|
||||
C[Step 3] --> D[Step 4]
|
||||
end
|
||||
|
||||
B --> C
|
||||
```
|
||||
|
||||
### Hub and Spoke
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Hub[Central Hub]
|
||||
|
||||
A[Spoke 1] --> Hub
|
||||
B[Spoke 2] --> Hub
|
||||
C[Spoke 3] --> Hub
|
||||
Hub --> D[Output]
|
||||
```
|
||||
|
||||
### Decision Tree
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Start[Start] --> Decision{Decision Point?}
|
||||
Decision -->|Option A| PathA[Path A]
|
||||
Decision -->|Option B| PathB[Path B]
|
||||
Decision -->|Option C| PathC[Path C]
|
||||
|
||||
PathA --> End[End]
|
||||
PathB --> End
|
||||
PathC --> End
|
||||
```
|
||||
|
||||
### Comparison Layout
|
||||
|
||||
```mermaid
|
||||
graph TB
|
||||
Title[Comparison]
|
||||
|
||||
subgraph left["System A"]
|
||||
A1[Feature 1]
|
||||
A2[Feature 2]
|
||||
A3[Feature 3]
|
||||
end
|
||||
|
||||
subgraph right["System B"]
|
||||
B1[Feature 1]
|
||||
B2[Feature 2]
|
||||
B3[Feature 3]
|
||||
end
|
||||
|
||||
Title --> left
|
||||
Title --> right
|
||||
|
||||
subgraph compare["Key Differences"]
|
||||
Diff[Difference Summary]
|
||||
end
|
||||
|
||||
left --> compare
|
||||
right --> compare
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Common Errors and Solutions
|
||||
|
||||
#### Error: "Parse error on line X: Expecting 'SEMI', 'NEWLINE', 'EOF'"
|
||||
|
||||
**Causes:**
|
||||
1. Subgraph name with spaces not using ID format
|
||||
2. Node reference using display text instead of ID
|
||||
3. Invalid special characters in node text
|
||||
|
||||
**Solutions:**
|
||||
- Use `subgraph id["Display Name"]` format
|
||||
- Reference nodes by ID only
|
||||
- Quote node text with special characters
|
||||
|
||||
#### Error: "Unsupported markdown: list"
|
||||
|
||||
**Cause:** Using `number. space` pattern in node text
|
||||
|
||||
**Solution:** Remove space or use alternatives (①, (1), Step 1:)
|
||||
|
||||
#### Error: "Parse error: unexpected character"
|
||||
|
||||
**Causes:**
|
||||
1. Unescaped special characters
|
||||
2. Improper quotes
|
||||
3. Invalid Mermaid syntax
|
||||
|
||||
**Solutions:**
|
||||
- Replace problematic characters (quotes → 『』, parens → 「」)
|
||||
- Use proper node definition syntax
|
||||
- Check arrow syntax
|
||||
|
||||
#### Diagram doesn't render correctly
|
||||
|
||||
**Causes:**
|
||||
1. Missing style declarations
|
||||
2. Incorrect direction specification
|
||||
3. Invalid connections
|
||||
|
||||
**Solutions:**
|
||||
- Verify all style declarations use valid syntax
|
||||
- Check direction is set in graph declaration or subgraph
|
||||
- Ensure all node IDs are defined before referencing
|
||||
|
||||
### Validation Checklist
|
||||
|
||||
Before finalizing any diagram:
|
||||
|
||||
- [ ] No `number. space` patterns in node text
|
||||
- [ ] All subgraphs use proper ID syntax if they contain spaces
|
||||
- [ ] All node references use IDs not display text
|
||||
- [ ] All arrows use valid syntax (-->, -.->)
|
||||
- [ ] All style declarations are syntactically correct
|
||||
- [ ] Direction is explicitly set
|
||||
- [ ] No unescaped special characters in node text
|
||||
- [ ] All connections reference defined nodes
|
||||
|
||||
### Platform-Specific Notes
|
||||
|
||||
**Obsidian:**
|
||||
- Older Mermaid version, more strict parsing
|
||||
- Limited support for `<br/>` (only in circle nodes)
|
||||
- Test diagrams before finalizing
|
||||
|
||||
**GitHub:**
|
||||
- Good Mermaid support
|
||||
- Renders most modern syntax
|
||||
- May differ slightly from Obsidian rendering
|
||||
|
||||
**Mermaid Live Editor:**
|
||||
- Most up-to-date parser
|
||||
- Best for testing new syntax
|
||||
- May support features not available in Obsidian/GitHub
|
||||
|
||||
## Quick Reference
|
||||
|
||||
### Safe Numbering Methods
|
||||
✅ `1.Text` `①Text` `(1)Text` `Step 1:Text`
|
||||
❌ `1. Text`
|
||||
|
||||
### Safe Subgraph Syntax
|
||||
✅ `subgraph id["Name"]` `subgraph simple_name`
|
||||
❌ `subgraph Name With Spaces`
|
||||
|
||||
### Safe Node References
|
||||
✅ `NodeID --> AnotherID`
|
||||
❌ `"Display Text" --> "Other Text"`
|
||||
|
||||
### Safe Special Characters
|
||||
✅ `『』` for quotes, `「」` for parentheses
|
||||
❌ `"` unescaped quotes, `()` in problematic contexts
|
||||
497
.claude/skills/obsidian-bases/SKILL.md
Normal file
497
.claude/skills/obsidian-bases/SKILL.md
Normal file
@@ -0,0 +1,497 @@
|
||||
---
|
||||
name: obsidian-bases
|
||||
description: Create and edit Obsidian Bases (.base files) with views, filters, formulas, and summaries. Use when working with .base files, creating database-like views of notes, or when the user mentions Bases, table views, card views, filters, or formulas in Obsidian.
|
||||
---
|
||||
|
||||
# Obsidian Bases Skill
|
||||
|
||||
## Workflow
|
||||
|
||||
1. **Create the file**: Create a `.base` file in the vault with valid YAML content
|
||||
2. **Define scope**: Add `filters` to select which notes appear (by tag, folder, property, or date)
|
||||
3. **Add formulas** (optional): Define computed properties in the `formulas` section
|
||||
4. **Configure views**: Add one or more views (`table`, `cards`, `list`, or `map`) with `order` specifying which properties to display
|
||||
5. **Validate**: Verify the file is valid YAML with no syntax errors. Check that all referenced properties and formulas exist. Common issues: unquoted strings containing special YAML characters, mismatched quotes in formula expressions, referencing `formula.X` without defining `X` in `formulas`
|
||||
6. **Test in Obsidian**: Open the `.base` file in Obsidian to confirm the view renders correctly. If it shows a YAML error, check quoting rules below
|
||||
|
||||
## Schema
|
||||
|
||||
Base files use the `.base` extension and contain valid YAML.
|
||||
|
||||
```yaml
|
||||
# Global filters apply to ALL views in the base
|
||||
filters:
|
||||
# Can be a single filter string
|
||||
# OR a recursive filter object with and/or/not
|
||||
and: []
|
||||
or: []
|
||||
not: []
|
||||
|
||||
# Define formula properties that can be used across all views
|
||||
formulas:
|
||||
formula_name: 'expression'
|
||||
|
||||
# Configure display names and settings for properties
|
||||
properties:
|
||||
property_name:
|
||||
displayName: "Display Name"
|
||||
formula.formula_name:
|
||||
displayName: "Formula Display Name"
|
||||
file.ext:
|
||||
displayName: "Extension"
|
||||
|
||||
# Define custom summary formulas
|
||||
summaries:
|
||||
custom_summary_name: 'values.mean().round(3)'
|
||||
|
||||
# Define one or more views
|
||||
views:
|
||||
- type: table | cards | list | map
|
||||
name: "View Name"
|
||||
limit: 10 # Optional: limit results
|
||||
groupBy: # Optional: group results
|
||||
property: property_name
|
||||
direction: ASC | DESC
|
||||
filters: # View-specific filters
|
||||
and: []
|
||||
order: # Properties to display in order
|
||||
- file.name
|
||||
- property_name
|
||||
- formula.formula_name
|
||||
summaries: # Map properties to summary formulas
|
||||
property_name: Average
|
||||
```
|
||||
|
||||
## Filter Syntax
|
||||
|
||||
Filters narrow down results. They can be applied globally or per-view.
|
||||
|
||||
### Filter Structure
|
||||
|
||||
```yaml
|
||||
# Single filter
|
||||
filters: 'status == "done"'
|
||||
|
||||
# AND - all conditions must be true
|
||||
filters:
|
||||
and:
|
||||
- 'status == "done"'
|
||||
- 'priority > 3'
|
||||
|
||||
# OR - any condition can be true
|
||||
filters:
|
||||
or:
|
||||
- 'file.hasTag("book")'
|
||||
- 'file.hasTag("article")'
|
||||
|
||||
# NOT - exclude matching items
|
||||
filters:
|
||||
not:
|
||||
- 'file.hasTag("archived")'
|
||||
|
||||
# Nested filters
|
||||
filters:
|
||||
or:
|
||||
- file.hasTag("tag")
|
||||
- and:
|
||||
- file.hasTag("book")
|
||||
- file.hasLink("Textbook")
|
||||
- not:
|
||||
- file.hasTag("book")
|
||||
- file.inFolder("Required Reading")
|
||||
```
|
||||
|
||||
### Filter Operators
|
||||
|
||||
| Operator | Description |
|
||||
|----------|-------------|
|
||||
| `==` | equals |
|
||||
| `!=` | not equal |
|
||||
| `>` | greater than |
|
||||
| `<` | less than |
|
||||
| `>=` | greater than or equal |
|
||||
| `<=` | less than or equal |
|
||||
| `&&` | logical and |
|
||||
| `\|\|` | logical or |
|
||||
| <code>!</code> | logical not |
|
||||
|
||||
## Properties
|
||||
|
||||
### Three Types of Properties
|
||||
|
||||
1. **Note properties** - From frontmatter: `note.author` or just `author`
|
||||
2. **File properties** - File metadata: `file.name`, `file.mtime`, etc.
|
||||
3. **Formula properties** - Computed values: `formula.my_formula`
|
||||
|
||||
### File Properties Reference
|
||||
|
||||
| Property | Type | Description |
|
||||
|----------|------|-------------|
|
||||
| `file.name` | String | File name |
|
||||
| `file.basename` | String | File name without extension |
|
||||
| `file.path` | String | Full path to file |
|
||||
| `file.folder` | String | Parent folder path |
|
||||
| `file.ext` | String | File extension |
|
||||
| `file.size` | Number | File size in bytes |
|
||||
| `file.ctime` | Date | Created time |
|
||||
| `file.mtime` | Date | Modified time |
|
||||
| `file.tags` | List | All tags in file |
|
||||
| `file.links` | List | Internal links in file |
|
||||
| `file.backlinks` | List | Files linking to this file |
|
||||
| `file.embeds` | List | Embeds in the note |
|
||||
| `file.properties` | Object | All frontmatter properties |
|
||||
|
||||
### The `this` Keyword
|
||||
|
||||
- In main content area: refers to the base file itself
|
||||
- When embedded: refers to the embedding file
|
||||
- In sidebar: refers to the active file in main content
|
||||
|
||||
## Formula Syntax
|
||||
|
||||
Formulas compute values from properties. Defined in the `formulas` section.
|
||||
|
||||
```yaml
|
||||
formulas:
|
||||
# Simple arithmetic
|
||||
total: "price * quantity"
|
||||
|
||||
# Conditional logic
|
||||
status_icon: 'if(done, "✅", "⏳")'
|
||||
|
||||
# String formatting
|
||||
formatted_price: 'if(price, price.toFixed(2) + " dollars")'
|
||||
|
||||
# Date formatting
|
||||
created: 'file.ctime.format("YYYY-MM-DD")'
|
||||
|
||||
# Calculate days since created (use .days for Duration)
|
||||
days_old: '(now() - file.ctime).days'
|
||||
|
||||
# Calculate days until due date
|
||||
days_until_due: 'if(due_date, (date(due_date) - today()).days, "")'
|
||||
```
|
||||
|
||||
## Key Functions
|
||||
|
||||
Most commonly used functions. For the complete reference of all types (Date, String, Number, List, File, Link, Object, RegExp), see [FUNCTIONS_REFERENCE.md](references/FUNCTIONS_REFERENCE.md).
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `date()` | `date(string): date` | Parse string to date (`YYYY-MM-DD HH:mm:ss`) |
|
||||
| `now()` | `now(): date` | Current date and time |
|
||||
| `today()` | `today(): date` | Current date (time = 00:00:00) |
|
||||
| `if()` | `if(condition, trueResult, falseResult?)` | Conditional |
|
||||
| `duration()` | `duration(string): duration` | Parse duration string |
|
||||
| `file()` | `file(path): file` | Get file object |
|
||||
| `link()` | `link(path, display?): Link` | Create a link |
|
||||
|
||||
### Duration Type
|
||||
|
||||
When subtracting two dates, the result is a **Duration** type (not a number).
|
||||
|
||||
**Duration Fields:** `duration.days`, `duration.hours`, `duration.minutes`, `duration.seconds`, `duration.milliseconds`
|
||||
|
||||
**IMPORTANT:** Duration does NOT support `.round()`, `.floor()`, `.ceil()` directly. Access a numeric field first (like `.days`), then apply number functions.
|
||||
|
||||
```yaml
|
||||
# CORRECT: Calculate days between dates
|
||||
"(date(due_date) - today()).days" # Returns number of days
|
||||
"(now() - file.ctime).days" # Days since created
|
||||
"(date(due_date) - today()).days.round(0)" # Rounded days
|
||||
|
||||
# WRONG - will cause error:
|
||||
# "((date(due) - today()) / 86400000).round(0)" # Duration doesn't support division then round
|
||||
```
|
||||
|
||||
### Date Arithmetic
|
||||
|
||||
```yaml
|
||||
# Duration units: y/year/years, M/month/months, d/day/days,
|
||||
# w/week/weeks, h/hour/hours, m/minute/minutes, s/second/seconds
|
||||
"now() + \"1 day\"" # Tomorrow
|
||||
"today() + \"7d\"" # A week from today
|
||||
"now() - file.ctime" # Returns Duration
|
||||
"(now() - file.ctime).days" # Get days as number
|
||||
```
|
||||
|
||||
## View Types
|
||||
|
||||
### Table View
|
||||
|
||||
```yaml
|
||||
views:
|
||||
- type: table
|
||||
name: "My Table"
|
||||
order:
|
||||
- file.name
|
||||
- status
|
||||
- due_date
|
||||
summaries:
|
||||
price: Sum
|
||||
count: Average
|
||||
```
|
||||
|
||||
### Cards View
|
||||
|
||||
```yaml
|
||||
views:
|
||||
- type: cards
|
||||
name: "Gallery"
|
||||
order:
|
||||
- file.name
|
||||
- cover_image
|
||||
- description
|
||||
```
|
||||
|
||||
### List View
|
||||
|
||||
```yaml
|
||||
views:
|
||||
- type: list
|
||||
name: "Simple List"
|
||||
order:
|
||||
- file.name
|
||||
- status
|
||||
```
|
||||
|
||||
### Map View
|
||||
|
||||
Requires latitude/longitude properties and the Maps community plugin.
|
||||
|
||||
```yaml
|
||||
views:
|
||||
- type: map
|
||||
name: "Locations"
|
||||
# Map-specific settings for lat/lng properties
|
||||
```
|
||||
|
||||
## Default Summary Formulas
|
||||
|
||||
| Name | Input Type | Description |
|
||||
|------|------------|-------------|
|
||||
| `Average` | Number | Mathematical mean |
|
||||
| `Min` | Number | Smallest number |
|
||||
| `Max` | Number | Largest number |
|
||||
| `Sum` | Number | Sum of all numbers |
|
||||
| `Range` | Number | Max - Min |
|
||||
| `Median` | Number | Mathematical median |
|
||||
| `Stddev` | Number | Standard deviation |
|
||||
| `Earliest` | Date | Earliest date |
|
||||
| `Latest` | Date | Latest date |
|
||||
| `Range` | Date | Latest - Earliest |
|
||||
| `Checked` | Boolean | Count of true values |
|
||||
| `Unchecked` | Boolean | Count of false values |
|
||||
| `Empty` | Any | Count of empty values |
|
||||
| `Filled` | Any | Count of non-empty values |
|
||||
| `Unique` | Any | Count of unique values |
|
||||
|
||||
## Complete Examples
|
||||
|
||||
### Task Tracker Base
|
||||
|
||||
```yaml
|
||||
filters:
|
||||
and:
|
||||
- file.hasTag("task")
|
||||
- 'file.ext == "md"'
|
||||
|
||||
formulas:
|
||||
days_until_due: 'if(due, (date(due) - today()).days, "")'
|
||||
is_overdue: 'if(due, date(due) < today() && status != "done", false)'
|
||||
priority_label: 'if(priority == 1, "🔴 High", if(priority == 2, "🟡 Medium", "🟢 Low"))'
|
||||
|
||||
properties:
|
||||
status:
|
||||
displayName: Status
|
||||
formula.days_until_due:
|
||||
displayName: "Days Until Due"
|
||||
formula.priority_label:
|
||||
displayName: Priority
|
||||
|
||||
views:
|
||||
- type: table
|
||||
name: "Active Tasks"
|
||||
filters:
|
||||
and:
|
||||
- 'status != "done"'
|
||||
order:
|
||||
- file.name
|
||||
- status
|
||||
- formula.priority_label
|
||||
- due
|
||||
- formula.days_until_due
|
||||
groupBy:
|
||||
property: status
|
||||
direction: ASC
|
||||
summaries:
|
||||
formula.days_until_due: Average
|
||||
|
||||
- type: table
|
||||
name: "Completed"
|
||||
filters:
|
||||
and:
|
||||
- 'status == "done"'
|
||||
order:
|
||||
- file.name
|
||||
- completed_date
|
||||
```
|
||||
|
||||
### Reading List Base
|
||||
|
||||
```yaml
|
||||
filters:
|
||||
or:
|
||||
- file.hasTag("book")
|
||||
- file.hasTag("article")
|
||||
|
||||
formulas:
|
||||
reading_time: 'if(pages, (pages * 2).toString() + " min", "")'
|
||||
status_icon: 'if(status == "reading", "📖", if(status == "done", "✅", "📚"))'
|
||||
year_read: 'if(finished_date, date(finished_date).year, "")'
|
||||
|
||||
properties:
|
||||
author:
|
||||
displayName: Author
|
||||
formula.status_icon:
|
||||
displayName: ""
|
||||
formula.reading_time:
|
||||
displayName: "Est. Time"
|
||||
|
||||
views:
|
||||
- type: cards
|
||||
name: "Library"
|
||||
order:
|
||||
- cover
|
||||
- file.name
|
||||
- author
|
||||
- formula.status_icon
|
||||
filters:
|
||||
not:
|
||||
- 'status == "dropped"'
|
||||
|
||||
- type: table
|
||||
name: "Reading List"
|
||||
filters:
|
||||
and:
|
||||
- 'status == "to-read"'
|
||||
order:
|
||||
- file.name
|
||||
- author
|
||||
- pages
|
||||
- formula.reading_time
|
||||
```
|
||||
|
||||
### Daily Notes Index
|
||||
|
||||
```yaml
|
||||
filters:
|
||||
and:
|
||||
- file.inFolder("Daily Notes")
|
||||
- '/^\d{4}-\d{2}-\d{2}$/.matches(file.basename)'
|
||||
|
||||
formulas:
|
||||
word_estimate: '(file.size / 5).round(0)'
|
||||
day_of_week: 'date(file.basename).format("dddd")'
|
||||
|
||||
properties:
|
||||
formula.day_of_week:
|
||||
displayName: "Day"
|
||||
formula.word_estimate:
|
||||
displayName: "~Words"
|
||||
|
||||
views:
|
||||
- type: table
|
||||
name: "Recent Notes"
|
||||
limit: 30
|
||||
order:
|
||||
- file.name
|
||||
- formula.day_of_week
|
||||
- formula.word_estimate
|
||||
- file.mtime
|
||||
```
|
||||
|
||||
## Embedding Bases
|
||||
|
||||
Embed in Markdown files:
|
||||
|
||||
```markdown
|
||||
![[MyBase.base]]
|
||||
|
||||
<!-- Specific view -->
|
||||
![[MyBase.base#View Name]]
|
||||
```
|
||||
|
||||
## YAML Quoting Rules
|
||||
|
||||
- Use single quotes for formulas containing double quotes: `'if(done, "Yes", "No")'`
|
||||
- Use double quotes for simple strings: `"My View Name"`
|
||||
- Escape nested quotes properly in complex expressions
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### YAML Syntax Errors
|
||||
|
||||
**Unquoted special characters**: Strings containing `:`, `{`, `}`, `[`, `]`, `,`, `&`, `*`, `#`, `?`, `|`, `-`, `<`, `>`, `=`, `!`, `%`, `@`, `` ` `` must be quoted.
|
||||
|
||||
```yaml
|
||||
# WRONG - colon in unquoted string
|
||||
displayName: Status: Active
|
||||
|
||||
# CORRECT
|
||||
displayName: "Status: Active"
|
||||
```
|
||||
|
||||
**Mismatched quotes in formulas**: When a formula contains double quotes, wrap the entire formula in single quotes.
|
||||
|
||||
```yaml
|
||||
# WRONG - double quotes inside double quotes
|
||||
formulas:
|
||||
label: "if(done, "Yes", "No")"
|
||||
|
||||
# CORRECT - single quotes wrapping double quotes
|
||||
formulas:
|
||||
label: 'if(done, "Yes", "No")'
|
||||
```
|
||||
|
||||
### Common Formula Errors
|
||||
|
||||
**Duration math without field access**: Subtracting dates returns a Duration, not a number. Always access `.days`, `.hours`, etc.
|
||||
|
||||
```yaml
|
||||
# WRONG - Duration is not a number
|
||||
"(now() - file.ctime).round(0)"
|
||||
|
||||
# CORRECT - access .days first, then round
|
||||
"(now() - file.ctime).days.round(0)"
|
||||
```
|
||||
|
||||
**Missing null checks**: Properties may not exist on all notes. Use `if()` to guard.
|
||||
|
||||
```yaml
|
||||
# WRONG - crashes if due_date is empty
|
||||
"(date(due_date) - today()).days"
|
||||
|
||||
# CORRECT - guard with if()
|
||||
'if(due_date, (date(due_date) - today()).days, "")'
|
||||
```
|
||||
|
||||
**Referencing undefined formulas**: Ensure every `formula.X` in `order` or `properties` has a matching entry in `formulas`.
|
||||
|
||||
```yaml
|
||||
# This will fail silently if 'total' is not defined in formulas
|
||||
order:
|
||||
- formula.total
|
||||
|
||||
# Fix: define it
|
||||
formulas:
|
||||
total: "price * quantity"
|
||||
```
|
||||
|
||||
## References
|
||||
|
||||
- [Bases Syntax](https://help.obsidian.md/bases/syntax)
|
||||
- [Functions](https://help.obsidian.md/bases/functions)
|
||||
- [Views](https://help.obsidian.md/bases/views)
|
||||
- [Formulas](https://help.obsidian.md/formulas)
|
||||
- [Complete Functions Reference](references/FUNCTIONS_REFERENCE.md)
|
||||
173
.claude/skills/obsidian-bases/references/FUNCTIONS_REFERENCE.md
Normal file
173
.claude/skills/obsidian-bases/references/FUNCTIONS_REFERENCE.md
Normal file
@@ -0,0 +1,173 @@
|
||||
# Functions Reference
|
||||
|
||||
## Global Functions
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `date()` | `date(string): date` | Parse string to date. Format: `YYYY-MM-DD HH:mm:ss` |
|
||||
| `duration()` | `duration(string): duration` | Parse duration string |
|
||||
| `now()` | `now(): date` | Current date and time |
|
||||
| `today()` | `today(): date` | Current date (time = 00:00:00) |
|
||||
| `if()` | `if(condition, trueResult, falseResult?)` | Conditional |
|
||||
| `min()` | `min(n1, n2, ...): number` | Smallest number |
|
||||
| `max()` | `max(n1, n2, ...): number` | Largest number |
|
||||
| `number()` | `number(any): number` | Convert to number |
|
||||
| `link()` | `link(path, display?): Link` | Create a link |
|
||||
| `list()` | `list(element): List` | Wrap in list if not already |
|
||||
| `file()` | `file(path): file` | Get file object |
|
||||
| `image()` | `image(path): image` | Create image for rendering |
|
||||
| `icon()` | `icon(name): icon` | Lucide icon by name |
|
||||
| `html()` | `html(string): html` | Render as HTML |
|
||||
| `escapeHTML()` | `escapeHTML(string): string` | Escape HTML characters |
|
||||
|
||||
## Any Type Functions
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `isTruthy()` | `any.isTruthy(): boolean` | Coerce to boolean |
|
||||
| `isType()` | `any.isType(type): boolean` | Check type |
|
||||
| `toString()` | `any.toString(): string` | Convert to string |
|
||||
|
||||
## Date Functions & Fields
|
||||
|
||||
**Fields:** `date.year`, `date.month`, `date.day`, `date.hour`, `date.minute`, `date.second`, `date.millisecond`
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `date()` | `date.date(): date` | Remove time portion |
|
||||
| `format()` | `date.format(string): string` | Format with Moment.js pattern |
|
||||
| `time()` | `date.time(): string` | Get time as string |
|
||||
| `relative()` | `date.relative(): string` | Human-readable relative time |
|
||||
| `isEmpty()` | `date.isEmpty(): boolean` | Always false for dates |
|
||||
|
||||
## Duration Type
|
||||
|
||||
When subtracting two dates, the result is a **Duration** type (not a number). Duration has its own properties and methods.
|
||||
|
||||
**Duration Fields:**
|
||||
| Field | Type | Description |
|
||||
|-------|------|-------------|
|
||||
| `duration.days` | Number | Total days in duration |
|
||||
| `duration.hours` | Number | Total hours in duration |
|
||||
| `duration.minutes` | Number | Total minutes in duration |
|
||||
| `duration.seconds` | Number | Total seconds in duration |
|
||||
| `duration.milliseconds` | Number | Total milliseconds in duration |
|
||||
|
||||
**IMPORTANT:** Duration does NOT support `.round()`, `.floor()`, `.ceil()` directly. You must access a numeric field first (like `.days`), then apply number functions.
|
||||
|
||||
```yaml
|
||||
# CORRECT: Calculate days between dates
|
||||
"(date(due_date) - today()).days" # Returns number of days
|
||||
"(now() - file.ctime).days" # Days since created
|
||||
|
||||
# CORRECT: Round the numeric result if needed
|
||||
"(date(due_date) - today()).days.round(0)" # Rounded days
|
||||
"(now() - file.ctime).hours.round(0)" # Rounded hours
|
||||
|
||||
# WRONG - will cause error:
|
||||
# "((date(due) - today()) / 86400000).round(0)" # Duration doesn't support division then round
|
||||
```
|
||||
|
||||
## Date Arithmetic
|
||||
|
||||
```yaml
|
||||
# Duration units: y/year/years, M/month/months, d/day/days,
|
||||
# w/week/weeks, h/hour/hours, m/minute/minutes, s/second/seconds
|
||||
|
||||
# Add/subtract durations
|
||||
"date + \"1M\"" # Add 1 month
|
||||
"date - \"2h\"" # Subtract 2 hours
|
||||
"now() + \"1 day\"" # Tomorrow
|
||||
"today() + \"7d\"" # A week from today
|
||||
|
||||
# Subtract dates returns Duration type
|
||||
"now() - file.ctime" # Returns Duration
|
||||
"(now() - file.ctime).days" # Get days as number
|
||||
"(now() - file.ctime).hours" # Get hours as number
|
||||
|
||||
# Complex duration arithmetic
|
||||
"now() + (duration('1d') * 2)"
|
||||
```
|
||||
|
||||
## String Functions
|
||||
|
||||
**Field:** `string.length`
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `contains()` | `string.contains(value): boolean` | Check substring |
|
||||
| `containsAll()` | `string.containsAll(...values): boolean` | All substrings present |
|
||||
| `containsAny()` | `string.containsAny(...values): boolean` | Any substring present |
|
||||
| `startsWith()` | `string.startsWith(query): boolean` | Starts with query |
|
||||
| `endsWith()` | `string.endsWith(query): boolean` | Ends with query |
|
||||
| `isEmpty()` | `string.isEmpty(): boolean` | Empty or not present |
|
||||
| `lower()` | `string.lower(): string` | To lowercase |
|
||||
| `title()` | `string.title(): string` | To Title Case |
|
||||
| `trim()` | `string.trim(): string` | Remove whitespace |
|
||||
| `replace()` | `string.replace(pattern, replacement): string` | Replace pattern |
|
||||
| `repeat()` | `string.repeat(count): string` | Repeat string |
|
||||
| `reverse()` | `string.reverse(): string` | Reverse string |
|
||||
| `slice()` | `string.slice(start, end?): string` | Substring |
|
||||
| `split()` | `string.split(separator, n?): list` | Split to list |
|
||||
|
||||
## Number Functions
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `abs()` | `number.abs(): number` | Absolute value |
|
||||
| `ceil()` | `number.ceil(): number` | Round up |
|
||||
| `floor()` | `number.floor(): number` | Round down |
|
||||
| `round()` | `number.round(digits?): number` | Round to digits |
|
||||
| `toFixed()` | `number.toFixed(precision): string` | Fixed-point notation |
|
||||
| `isEmpty()` | `number.isEmpty(): boolean` | Not present |
|
||||
|
||||
## List Functions
|
||||
|
||||
**Field:** `list.length`
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `contains()` | `list.contains(value): boolean` | Element exists |
|
||||
| `containsAll()` | `list.containsAll(...values): boolean` | All elements exist |
|
||||
| `containsAny()` | `list.containsAny(...values): boolean` | Any element exists |
|
||||
| `filter()` | `list.filter(expression): list` | Filter by condition (uses `value`, `index`) |
|
||||
| `map()` | `list.map(expression): list` | Transform elements (uses `value`, `index`) |
|
||||
| `reduce()` | `list.reduce(expression, initial): any` | Reduce to single value (uses `value`, `index`, `acc`) |
|
||||
| `flat()` | `list.flat(): list` | Flatten nested lists |
|
||||
| `join()` | `list.join(separator): string` | Join to string |
|
||||
| `reverse()` | `list.reverse(): list` | Reverse order |
|
||||
| `slice()` | `list.slice(start, end?): list` | Sublist |
|
||||
| `sort()` | `list.sort(): list` | Sort ascending |
|
||||
| `unique()` | `list.unique(): list` | Remove duplicates |
|
||||
| `isEmpty()` | `list.isEmpty(): boolean` | No elements |
|
||||
|
||||
## File Functions
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `asLink()` | `file.asLink(display?): Link` | Convert to link |
|
||||
| `hasLink()` | `file.hasLink(otherFile): boolean` | Has link to file |
|
||||
| `hasTag()` | `file.hasTag(...tags): boolean` | Has any of the tags |
|
||||
| `hasProperty()` | `file.hasProperty(name): boolean` | Has property |
|
||||
| `inFolder()` | `file.inFolder(folder): boolean` | In folder or subfolder |
|
||||
|
||||
## Link Functions
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `asFile()` | `link.asFile(): file` | Get file object |
|
||||
| `linksTo()` | `link.linksTo(file): boolean` | Links to file |
|
||||
|
||||
## Object Functions
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `isEmpty()` | `object.isEmpty(): boolean` | No properties |
|
||||
| `keys()` | `object.keys(): list` | List of keys |
|
||||
| `values()` | `object.values(): list` | List of values |
|
||||
|
||||
## Regular Expression Functions
|
||||
|
||||
| Function | Signature | Description |
|
||||
|----------|-----------|-------------|
|
||||
| `matches()` | `regexp.matches(string): boolean` | Test if matches |
|
||||
211
.claude/skills/obsidian-canvas-creator/SKILL.md
Normal file
211
.claude/skills/obsidian-canvas-creator/SKILL.md
Normal file
@@ -0,0 +1,211 @@
|
||||
---
|
||||
name: obsidian-canvas-creator
|
||||
description: Create Obsidian Canvas files from text content, supporting both MindMap and freeform layouts. Use this skill when users want to visualize content as an interactive canvas, create mind maps, or organize information spatially in Obsidian format.
|
||||
---
|
||||
|
||||
# Obsidian Canvas Creator
|
||||
|
||||
Transform text content into structured Obsidian Canvas files with support for MindMap and freeform layouts.
|
||||
|
||||
## When to Use This Skill
|
||||
|
||||
- User requests to create a canvas, mind map, or visual diagram from text
|
||||
- User wants to organize information spatially
|
||||
- User mentions "Obsidian Canvas" or similar visualization tools
|
||||
- Converting structured content (articles, notes, outlines) into visual format
|
||||
|
||||
## Core Workflow
|
||||
|
||||
### 1. Analyze Content
|
||||
|
||||
Read and understand the input content:
|
||||
- Identify main topics and hierarchical relationships
|
||||
- Extract key points, facts, and supporting details
|
||||
- Note any existing structure (headings, lists, sections)
|
||||
|
||||
### 2. Determine Layout Type
|
||||
|
||||
Ask user to choose or infer from context:
|
||||
|
||||
**MindMap Layout:**
|
||||
- Radial structure from center
|
||||
- Parent-child relationships
|
||||
- Clear hierarchy
|
||||
- Good for: brainstorming, topic exploration, hierarchical content
|
||||
|
||||
**Freeform Layout:**
|
||||
- Custom positioning
|
||||
- Flexible relationships
|
||||
- Multiple connection types
|
||||
- Good for: complex networks, non-hierarchical content, custom arrangements
|
||||
|
||||
### 3. Plan Structure
|
||||
|
||||
**For MindMap:**
|
||||
- Identify central concept (root node)
|
||||
- Map primary branches (main topics)
|
||||
- Organize secondary branches (subtopics)
|
||||
- Position leaf nodes (details)
|
||||
|
||||
**For Freeform:**
|
||||
- Group related concepts
|
||||
- Identify connection patterns
|
||||
- Plan spatial zones
|
||||
- Consider visual flow
|
||||
|
||||
### 4. Generate Canvas
|
||||
|
||||
Create JSON following the Canvas specification:
|
||||
|
||||
**Node Creation:**
|
||||
- Assign unique 8-12 character hex IDs
|
||||
- Set appropriate dimensions based on content length
|
||||
- Apply consistent color schemes
|
||||
- Ensure no coordinate overlaps
|
||||
|
||||
**Edge Creation:**
|
||||
- Connect parent-child relationships
|
||||
- Use appropriate arrow styles
|
||||
- Add labels for complex relationships
|
||||
- Choose line styles (straight for hierarchy, curved for cross-references)
|
||||
|
||||
**Grouping (Optional):**
|
||||
- Create visual containers for related nodes
|
||||
- Use subtle background colors
|
||||
- Add descriptive labels
|
||||
|
||||
### 5. Apply Layout Algorithm
|
||||
|
||||
**MindMap Layout Calculations:**
|
||||
|
||||
Refer to `references/layout-algorithms.md` for detailed algorithms. Key principles:
|
||||
|
||||
- Center root at (0, 0)
|
||||
- Distribute primary nodes radially
|
||||
- Space secondary nodes based on sibling count
|
||||
- Maintain minimum spacing: 320px horizontal, 200px vertical
|
||||
|
||||
**Freeform Layout Principles:**
|
||||
|
||||
- Start with logical groupings
|
||||
- Position groups with clear separation
|
||||
- Connect across groups with curved edges
|
||||
- Balance visual weight across canvas
|
||||
|
||||
### 6. Validate and Output
|
||||
|
||||
Before outputting:
|
||||
|
||||
**Validation Checklist:**
|
||||
- All nodes have unique IDs
|
||||
- No coordinate overlaps (check distance > node dimensions + spacing)
|
||||
- All edges reference valid node IDs
|
||||
- Groups (if any) have labels
|
||||
- Colors use consistent format (hex or preset numbers)
|
||||
- JSON is properly escaped (Chinese quotes: 『』 for double, 「」 for single)
|
||||
|
||||
**Output Format:**
|
||||
- Complete, valid JSON Canvas file
|
||||
- No additional explanation text
|
||||
- Directly importable into Obsidian
|
||||
|
||||
## Node Sizing Guidelines
|
||||
|
||||
**Text Length-Based Sizing:**
|
||||
- Short text (<30 chars): 220 × 100 px
|
||||
- Medium text (30-60 chars): 260 × 120 px
|
||||
- Long text (60-100 chars): 320 × 140 px
|
||||
- Very long text (>100 chars): 320 × 180 px
|
||||
|
||||
## Color Schemes
|
||||
|
||||
**Preset Colors (Recommended):**
|
||||
- `"1"` - Red (warnings, important)
|
||||
- `"2"` - Orange (action items)
|
||||
- `"3"` - Yellow (questions, notes)
|
||||
- `"4"` - Green (positive, completed)
|
||||
- `"5"` - Cyan (information, details)
|
||||
- `"6"` - Purple (concepts, abstract)
|
||||
|
||||
**Custom Hex Colors:**
|
||||
Use for brand consistency or specific themes. Always use uppercase format: `"#4A90E2"`
|
||||
|
||||
## Critical Rules
|
||||
|
||||
1. **Quote Handling:**
|
||||
- Chinese double quotes → 『』
|
||||
- Chinese single quotes → 「」
|
||||
- English double quotes → `\"`
|
||||
|
||||
2. **ID Generation:**
|
||||
- 8-12 character random hex strings
|
||||
- Must be unique across all nodes and edges
|
||||
|
||||
3. **Z-Index Order:**
|
||||
- Output groups first (bottom layer)
|
||||
- Then subgroups
|
||||
- Finally text/link nodes (top layer)
|
||||
|
||||
4. **Spacing Requirements:**
|
||||
- Minimum horizontal: 320px between node centers
|
||||
- Minimum vertical: 200px between node centers
|
||||
- Account for node dimensions when calculating
|
||||
|
||||
5. **JSON Structure:**
|
||||
- Top level contains only `nodes` and `edges` arrays
|
||||
- No extra wrapping objects
|
||||
- No comments in output
|
||||
|
||||
6. **No Emoji:**
|
||||
- Do not use any Emoji symbols in node text
|
||||
- Use color coding or text labels for visual distinction instead
|
||||
|
||||
## Examples
|
||||
|
||||
### Simple MindMap Request
|
||||
User: "Create a mind map about solar system planets"
|
||||
|
||||
Process:
|
||||
1. Identify center: "Solar System"
|
||||
2. Primary branches: Inner Planets, Outer Planets, Dwarf Planets
|
||||
3. Secondary nodes: Individual planets with key facts
|
||||
4. Apply radial layout
|
||||
5. Generate JSON with proper spacing
|
||||
|
||||
### Freeform Content Request
|
||||
User: "Turn this article into a canvas" + [article text]
|
||||
|
||||
Process:
|
||||
1. Extract article structure (intro, body sections, conclusion)
|
||||
2. Identify key concepts and relationships
|
||||
3. Group related sections spatially
|
||||
4. Connect with labeled edges
|
||||
5. Apply freeform layout with clear zones
|
||||
|
||||
## Reference Documents
|
||||
|
||||
- **Canvas Specification**: `references/canvas-spec.md` - Complete JSON Canvas format specification
|
||||
- **Layout Algorithms**: `references/layout-algorithms.md` - Detailed positioning algorithms for both layout types
|
||||
|
||||
Load these references when:
|
||||
- Need specification details for edge cases
|
||||
- Implementing complex layout calculations
|
||||
- Troubleshooting validation errors
|
||||
|
||||
## Tips for Quality Canvases
|
||||
|
||||
1. **Keep text concise**: Each node should be scannable (<2 lines preferred)
|
||||
2. **Use hierarchy**: Group by importance and relationship
|
||||
3. **Balance the canvas**: Distribute nodes to avoid clustering
|
||||
4. **Strategic colors**: Use colors to encode meaning, not just decoration
|
||||
5. **Meaningful connections**: Only add edges that clarify relationships
|
||||
6. **Test in Obsidian**: Verify the output opens correctly
|
||||
|
||||
## Common Pitfalls to Avoid
|
||||
|
||||
- Overlapping nodes (always check distances)
|
||||
- Inconsistent quote escaping (breaks JSON parsing)
|
||||
- Missing group labels (causes sidebar navigation issues)
|
||||
- Too much text in nodes (use file nodes for long content)
|
||||
- Duplicate IDs (each must be unique)
|
||||
- Unconnected nodes (unless intentional islands)
|
||||
@@ -0,0 +1,132 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "group01",
|
||||
"type": "group",
|
||||
"x": -50,
|
||||
"y": -50,
|
||||
"width": 600,
|
||||
"height": 400,
|
||||
"label": "Group 1 - Core Concepts",
|
||||
"color": "4"
|
||||
},
|
||||
{
|
||||
"id": "group02",
|
||||
"type": "group",
|
||||
"x": 650,
|
||||
"y": -50,
|
||||
"width": 600,
|
||||
"height": 400,
|
||||
"label": "Group 2 - Applications",
|
||||
"color": "5"
|
||||
},
|
||||
{
|
||||
"id": "node01",
|
||||
"type": "text",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 240,
|
||||
"height": 100,
|
||||
"text": "Concept A",
|
||||
"color": "4"
|
||||
},
|
||||
{
|
||||
"id": "node02",
|
||||
"type": "text",
|
||||
"x": 290,
|
||||
"y": 0,
|
||||
"width": 240,
|
||||
"height": 100,
|
||||
"text": "Concept B",
|
||||
"color": "4"
|
||||
},
|
||||
{
|
||||
"id": "node03",
|
||||
"type": "text",
|
||||
"x": 0,
|
||||
"y": 150,
|
||||
"width": 240,
|
||||
"height": 100,
|
||||
"text": "Concept C",
|
||||
"color": "4"
|
||||
},
|
||||
{
|
||||
"id": "node04",
|
||||
"type": "text",
|
||||
"x": 290,
|
||||
"y": 150,
|
||||
"width": 240,
|
||||
"height": 100,
|
||||
"text": "Concept D",
|
||||
"color": "4"
|
||||
},
|
||||
{
|
||||
"id": "node05",
|
||||
"type": "text",
|
||||
"x": 700,
|
||||
"y": 0,
|
||||
"width": 240,
|
||||
"height": 100,
|
||||
"text": "Application 1",
|
||||
"color": "5"
|
||||
},
|
||||
{
|
||||
"id": "node06",
|
||||
"type": "text",
|
||||
"x": 990,
|
||||
"y": 0,
|
||||
"width": 240,
|
||||
"height": 100,
|
||||
"text": "Application 2",
|
||||
"color": "5"
|
||||
},
|
||||
{
|
||||
"id": "node07",
|
||||
"type": "text",
|
||||
"x": 700,
|
||||
"y": 150,
|
||||
"width": 240,
|
||||
"height": 100,
|
||||
"text": "Application 3",
|
||||
"color": "5"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "e1",
|
||||
"fromNode": "node01",
|
||||
"fromSide": "bottom",
|
||||
"toNode": "node03",
|
||||
"toSide": "top",
|
||||
"toEnd": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "e2",
|
||||
"fromNode": "node02",
|
||||
"fromSide": "bottom",
|
||||
"toNode": "node04",
|
||||
"toSide": "top",
|
||||
"toEnd": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "e3",
|
||||
"fromNode": "node01",
|
||||
"fromSide": "right",
|
||||
"toNode": "node05",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow",
|
||||
"label": "leads to",
|
||||
"color": "3"
|
||||
},
|
||||
{
|
||||
"id": "e4",
|
||||
"fromNode": "node02",
|
||||
"fromSide": "right",
|
||||
"toNode": "node06",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow",
|
||||
"label": "enables",
|
||||
"color": "3"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "root001",
|
||||
"type": "text",
|
||||
"x": -150,
|
||||
"y": -60,
|
||||
"width": 300,
|
||||
"height": 120,
|
||||
"text": "# Central Topic\n\nMain concept goes here",
|
||||
"color": "4"
|
||||
},
|
||||
{
|
||||
"id": "branch01",
|
||||
"type": "text",
|
||||
"x": 250,
|
||||
"y": -200,
|
||||
"width": 220,
|
||||
"height": 100,
|
||||
"text": "Branch 1\n\nFirst main idea",
|
||||
"color": "5"
|
||||
},
|
||||
{
|
||||
"id": "branch02",
|
||||
"type": "text",
|
||||
"x": 250,
|
||||
"y": -50,
|
||||
"width": 220,
|
||||
"height": 100,
|
||||
"text": "Branch 2\n\nSecond main idea",
|
||||
"color": "5"
|
||||
},
|
||||
{
|
||||
"id": "branch03",
|
||||
"type": "text",
|
||||
"x": 250,
|
||||
"y": 100,
|
||||
"width": 220,
|
||||
"height": 100,
|
||||
"text": "Branch 3\n\nThird main idea",
|
||||
"color": "5"
|
||||
},
|
||||
{
|
||||
"id": "detail01",
|
||||
"type": "text",
|
||||
"x": 550,
|
||||
"y": -200,
|
||||
"width": 200,
|
||||
"height": 80,
|
||||
"text": "Detail A",
|
||||
"color": "6"
|
||||
},
|
||||
{
|
||||
"id": "detail02",
|
||||
"type": "text",
|
||||
"x": 550,
|
||||
"y": -100,
|
||||
"width": 200,
|
||||
"height": 80,
|
||||
"text": "Detail B",
|
||||
"color": "6"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "e1",
|
||||
"fromNode": "root001",
|
||||
"fromSide": "right",
|
||||
"toNode": "branch01",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "e2",
|
||||
"fromNode": "root001",
|
||||
"fromSide": "right",
|
||||
"toNode": "branch02",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "e3",
|
||||
"fromNode": "root001",
|
||||
"fromSide": "right",
|
||||
"toNode": "branch03",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "e4",
|
||||
"fromNode": "branch01",
|
||||
"fromSide": "right",
|
||||
"toNode": "detail01",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "e5",
|
||||
"fromNode": "branch01",
|
||||
"fromSide": "right",
|
||||
"toNode": "detail02",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow"
|
||||
}
|
||||
]
|
||||
}
|
||||
403
.claude/skills/obsidian-canvas-creator/references/canvas-spec.md
Normal file
403
.claude/skills/obsidian-canvas-creator/references/canvas-spec.md
Normal file
@@ -0,0 +1,403 @@
|
||||
# JSON Canvas Specification for Obsidian
|
||||
|
||||
Version 1.0 — 2024-03-11
|
||||
|
||||
## Overview
|
||||
|
||||
JSON Canvas is a format for representing infinite canvas documents. This specification defines the structure for creating canvas files compatible with Obsidian.
|
||||
|
||||
## Top Level Structure
|
||||
|
||||
The root JSON object contains two optional arrays:
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [...],
|
||||
"edges": [...]
|
||||
}
|
||||
```
|
||||
|
||||
- `nodes` (optional, array): All canvas objects (text, files, links, groups)
|
||||
- `edges` (optional, array): All connections between nodes
|
||||
|
||||
## Node Types
|
||||
|
||||
### Common Attributes
|
||||
|
||||
All nodes share these required attributes:
|
||||
|
||||
- `id` (required, string): Unique identifier for the node
|
||||
- `type` (required, string): Node type (`text`, `file`, `link`, `group`)
|
||||
- `x` (required, integer): X position in pixels
|
||||
- `y` (required, integer): Y position in pixels
|
||||
- `width` (required, integer): Width in pixels
|
||||
- `height` (required, integer): Height in pixels
|
||||
- `color` (optional, string/number): Color (hex `"#FF0000"` or preset `"1"`)
|
||||
|
||||
### Text Nodes
|
||||
|
||||
Store plain text with Markdown formatting.
|
||||
|
||||
**Required Attributes:**
|
||||
- `text` (string): Content in Markdown syntax
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"id": "abc123",
|
||||
"type": "text",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 250,
|
||||
"height": 100,
|
||||
"text": "# Main Topic\n\nKey point here",
|
||||
"color": "4"
|
||||
}
|
||||
```
|
||||
|
||||
### File Nodes
|
||||
|
||||
Reference other files or attachments (images, PDFs, etc.).
|
||||
|
||||
**Required Attributes:**
|
||||
- `file` (string): Path to file in the vault
|
||||
|
||||
**Optional Attributes:**
|
||||
- `subpath` (string): Link to specific heading/block (starts with `#`)
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"id": "def456",
|
||||
"type": "file",
|
||||
"x": 300,
|
||||
"y": 0,
|
||||
"width": 400,
|
||||
"height": 300,
|
||||
"file": "Images/diagram.png"
|
||||
}
|
||||
```
|
||||
|
||||
**With Subpath:**
|
||||
```json
|
||||
{
|
||||
"id": "ghi789",
|
||||
"type": "file",
|
||||
"x": 0,
|
||||
"y": 200,
|
||||
"width": 250,
|
||||
"height": 100,
|
||||
"file": "Notes/Meeting Notes.md",
|
||||
"subpath": "#Action Items"
|
||||
}
|
||||
```
|
||||
|
||||
### Link Nodes
|
||||
|
||||
Reference external URLs.
|
||||
|
||||
**Required Attributes:**
|
||||
- `url` (string): Full URL including protocol
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"id": "jkl012",
|
||||
"type": "link",
|
||||
"x": 0,
|
||||
"y": -200,
|
||||
"width": 250,
|
||||
"height": 100,
|
||||
"url": "https://obsidian.md",
|
||||
"color": "5"
|
||||
}
|
||||
```
|
||||
|
||||
### Group Nodes
|
||||
|
||||
Visual containers for organizing related nodes.
|
||||
|
||||
**Optional Attributes:**
|
||||
- `label` (string): Text label for the group (recommended)
|
||||
- `background` (string): Path to background image
|
||||
- `backgroundStyle` (string): Image rendering style
|
||||
- `cover`: Fill entire node
|
||||
- `ratio`: Maintain aspect ratio
|
||||
- `repeat`: Tile as pattern
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"id": "group1",
|
||||
"type": "group",
|
||||
"x": -50,
|
||||
"y": -50,
|
||||
"width": 600,
|
||||
"height": 400,
|
||||
"label": "Main Concepts",
|
||||
"color": "4"
|
||||
}
|
||||
```
|
||||
|
||||
**With Background:**
|
||||
```json
|
||||
{
|
||||
"id": "group2",
|
||||
"type": "group",
|
||||
"x": 700,
|
||||
"y": 0,
|
||||
"width": 500,
|
||||
"height": 600,
|
||||
"label": "Reference Materials",
|
||||
"background": "Images/texture.png",
|
||||
"backgroundStyle": "repeat"
|
||||
}
|
||||
```
|
||||
|
||||
## Z-Index and Layering
|
||||
|
||||
Nodes are displayed in array order:
|
||||
- **First node**: Bottom layer (rendered below others)
|
||||
- **Last node**: Top layer (rendered above others)
|
||||
|
||||
**Best Practice Order:**
|
||||
1. Group nodes (backgrounds)
|
||||
2. Sub-groups
|
||||
3. Regular nodes (text, file, link)
|
||||
|
||||
This ensures groups appear behind content.
|
||||
|
||||
## Edges (Connections)
|
||||
|
||||
Edges connect nodes with lines.
|
||||
|
||||
**Required Attributes:**
|
||||
- `id` (required, string): Unique identifier
|
||||
- `fromNode` (required, string): Starting node ID
|
||||
- `toNode` (required, string): Ending node ID
|
||||
|
||||
**Optional Attributes:**
|
||||
- `fromSide` (string): Starting edge side
|
||||
- Values: `top`, `right`, `bottom`, `left`
|
||||
- `fromEnd` (string): Start endpoint shape
|
||||
- Values: `none` (default), `arrow`
|
||||
- `toSide` (string): Ending edge side
|
||||
- Values: `top`, `right`, `bottom`, `left`
|
||||
- `toEnd` (string): End endpoint shape
|
||||
- Values: `arrow` (default), `none`
|
||||
- `color` (string/number): Edge color
|
||||
- `label` (string): Text label on edge
|
||||
|
||||
**Example - Simple Connection:**
|
||||
```json
|
||||
{
|
||||
"id": "edge1",
|
||||
"fromNode": "abc123",
|
||||
"toNode": "def456"
|
||||
}
|
||||
```
|
||||
|
||||
**Example - Fully Specified:**
|
||||
```json
|
||||
{
|
||||
"id": "edge2",
|
||||
"fromNode": "def456",
|
||||
"fromSide": "bottom",
|
||||
"fromEnd": "none",
|
||||
"toNode": "ghi789",
|
||||
"toSide": "top",
|
||||
"toEnd": "arrow",
|
||||
"color": "3",
|
||||
"label": "leads to"
|
||||
}
|
||||
```
|
||||
|
||||
## Color System
|
||||
|
||||
### Preset Colors
|
||||
|
||||
Use string numbers `"1"` through `"6"`:
|
||||
|
||||
- `"1"` - Red
|
||||
- `"2"` - Orange
|
||||
- `"3"` - Yellow
|
||||
- `"4"` - Green
|
||||
- `"5"` - Cyan
|
||||
- `"6"` - Purple
|
||||
|
||||
**Note:** Exact colors adapt to Obsidian's theme. These provide semantic meaning across light/dark modes.
|
||||
|
||||
### Custom Hex Colors
|
||||
|
||||
Use hex format: `"#RRGGBB"`
|
||||
|
||||
**Examples:**
|
||||
- `"#4A90E2"` (blue)
|
||||
- `"#50E3C2"` (teal)
|
||||
- `"#F5A623"` (orange)
|
||||
|
||||
**Best Practice:** Use consistent format within a canvas (all hex OR all presets).
|
||||
|
||||
## Complete Example
|
||||
|
||||
```json
|
||||
{
|
||||
"nodes": [
|
||||
{
|
||||
"id": "group001",
|
||||
"type": "group",
|
||||
"x": -50,
|
||||
"y": -50,
|
||||
"width": 700,
|
||||
"height": 500,
|
||||
"label": "Core Concepts",
|
||||
"color": "4"
|
||||
},
|
||||
{
|
||||
"id": "center01",
|
||||
"type": "text",
|
||||
"x": 0,
|
||||
"y": 0,
|
||||
"width": 300,
|
||||
"height": 120,
|
||||
"text": "# Central Topic\n\nMain idea here",
|
||||
"color": "4"
|
||||
},
|
||||
{
|
||||
"id": "branch01",
|
||||
"type": "text",
|
||||
"x": 400,
|
||||
"y": -100,
|
||||
"width": 220,
|
||||
"height": 100,
|
||||
"text": "Subtopic A",
|
||||
"color": "5"
|
||||
},
|
||||
{
|
||||
"id": "branch02",
|
||||
"type": "text",
|
||||
"x": 400,
|
||||
"y": 100,
|
||||
"width": 220,
|
||||
"height": 100,
|
||||
"text": "Subtopic B",
|
||||
"color": "5"
|
||||
},
|
||||
{
|
||||
"id": "detail01",
|
||||
"type": "text",
|
||||
"x": 700,
|
||||
"y": -100,
|
||||
"width": 200,
|
||||
"height": 80,
|
||||
"text": "Detail 1",
|
||||
"color": "6"
|
||||
}
|
||||
],
|
||||
"edges": [
|
||||
{
|
||||
"id": "e1",
|
||||
"fromNode": "center01",
|
||||
"fromSide": "right",
|
||||
"toNode": "branch01",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "e2",
|
||||
"fromNode": "center01",
|
||||
"fromSide": "right",
|
||||
"toNode": "branch02",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow"
|
||||
},
|
||||
{
|
||||
"id": "e3",
|
||||
"fromNode": "branch01",
|
||||
"fromSide": "right",
|
||||
"toNode": "detail01",
|
||||
"toSide": "left",
|
||||
"toEnd": "arrow",
|
||||
"color": "3"
|
||||
}
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
## Validation Requirements
|
||||
|
||||
When creating canvas files, ensure:
|
||||
|
||||
1. **Unique IDs**: All `id` values must be unique across nodes and edges
|
||||
2. **Valid References**: All edge `fromNode` and `toNode` must reference existing node IDs
|
||||
3. **Required Fields**: All required attributes are present for each type
|
||||
4. **Valid Coordinates**: All position/dimension values are integers
|
||||
5. **Color Format**: Colors use either hex (`"#RRGGBB"`) or preset strings (`"1"` to `"6"`)
|
||||
6. **Quote Escaping**: Special characters properly escaped in JSON strings
|
||||
|
||||
## Common Issues and Solutions
|
||||
|
||||
### Issue: Canvas won't open in Obsidian
|
||||
**Solutions:**
|
||||
- Validate JSON syntax (use JSON validator)
|
||||
- Check all IDs are unique
|
||||
- Verify all edge references exist
|
||||
- Ensure required fields present
|
||||
|
||||
### Issue: Nodes appear overlapped
|
||||
**Solutions:**
|
||||
- Increase spacing between coordinates
|
||||
- Account for node dimensions in positioning
|
||||
- Use minimum spacing: 320px horizontal, 200px vertical
|
||||
|
||||
### Issue: Groups don't show properly
|
||||
**Solutions:**
|
||||
- Ensure groups appear before content nodes in array
|
||||
- Add explicit `label` to all groups
|
||||
- Check group dimensions encompass child nodes
|
||||
|
||||
### Issue: Colors don't match expectations
|
||||
**Solutions:**
|
||||
- Use consistent color format (all hex OR all presets)
|
||||
- Remember presets adapt to theme
|
||||
- Test in both light and dark mode if using custom colors
|
||||
|
||||
### Issue: Text appears truncated
|
||||
**Solutions:**
|
||||
- Increase node dimensions
|
||||
- Break long text into multiple nodes
|
||||
- Use file nodes for lengthy content
|
||||
|
||||
## Character Encoding for Chinese Content
|
||||
|
||||
When canvas contains Chinese text, apply these transformations:
|
||||
|
||||
- Chinese double quotes `"` → `『』`
|
||||
- Chinese single quotes `'` → `「」`
|
||||
- English double quotes must be escaped: `\"`
|
||||
|
||||
**Example:**
|
||||
```json
|
||||
{
|
||||
"text": "『核心概念』包含:「子概念A」和「子概念B」"
|
||||
}
|
||||
```
|
||||
|
||||
This prevents JSON parsing errors with mixed-language content.
|
||||
|
||||
## Performance Considerations
|
||||
|
||||
- **Large Canvases**: Keep node count reasonable (<500 for smooth performance)
|
||||
- **Image Files**: Use compressed images for backgrounds
|
||||
- **Text Length**: Keep node text concise; use file nodes for long content
|
||||
- **Edge Complexity**: Minimize crossing edges for clarity
|
||||
|
||||
## Future Extensions
|
||||
|
||||
This specification may be extended with:
|
||||
- Additional node types
|
||||
- More edge styling options
|
||||
- Animation properties
|
||||
- Interactive behaviors
|
||||
|
||||
Always check Obsidian documentation for latest Canvas features.
|
||||
@@ -0,0 +1,614 @@
|
||||
# Layout Algorithms for Obsidian Canvas
|
||||
|
||||
Detailed algorithms for positioning nodes in MindMap and Freeform layouts.
|
||||
|
||||
## Layout Principles
|
||||
|
||||
### Universal Spacing Constants
|
||||
|
||||
```
|
||||
HORIZONTAL_SPACING = 320 // Minimum horizontal space between node centers
|
||||
VERTICAL_SPACING = 200 // Minimum vertical space between node centers
|
||||
NODE_PADDING = 20 // Internal padding within nodes
|
||||
```
|
||||
|
||||
### Collision Detection
|
||||
|
||||
Before finalizing any node position, verify:
|
||||
|
||||
```python
|
||||
def check_collision(node1, node2):
|
||||
"""Returns True if nodes overlap or are too close"""
|
||||
center1_x = node1.x + node1.width / 2
|
||||
center1_y = node1.y + node1.height / 2
|
||||
center2_x = node2.x + node2.width / 2
|
||||
center2_y = node2.y + node2.height / 2
|
||||
|
||||
dx = abs(center1_x - center2_x)
|
||||
dy = abs(center1_y - center2_y)
|
||||
|
||||
min_dx = (node1.width + node2.width) / 2 + HORIZONTAL_SPACING
|
||||
min_dy = (node1.height + node2.height) / 2 + VERTICAL_SPACING
|
||||
|
||||
return dx < min_dx or dy < min_dy
|
||||
```
|
||||
|
||||
## MindMap Layout Algorithm
|
||||
|
||||
### 1. Radial Tree Layout
|
||||
|
||||
Place root at center, arrange children radially.
|
||||
|
||||
#### Step 1: Position Root Node
|
||||
|
||||
```python
|
||||
root = {
|
||||
"x": 0 - (root_width / 2), # Center horizontally
|
||||
"y": 0 - (root_height / 2), # Center vertically
|
||||
"width": root_width,
|
||||
"height": root_height
|
||||
}
|
||||
```
|
||||
|
||||
#### Step 2: Calculate Primary Branch Positions
|
||||
|
||||
Distribute first-level children around root:
|
||||
|
||||
```python
|
||||
def position_primary_branches(root, children, radius=400):
|
||||
"""Position first-level children in a circle around root"""
|
||||
n = len(children)
|
||||
angle_step = 2 * pi / n
|
||||
|
||||
positions = []
|
||||
for i, child in enumerate(children):
|
||||
angle = i * angle_step
|
||||
|
||||
# Calculate position on circle
|
||||
x = root.center_x + radius * cos(angle) - child.width / 2
|
||||
y = root.center_y + radius * sin(angle) - child.height / 2
|
||||
|
||||
positions.append({"x": x, "y": y})
|
||||
|
||||
return positions
|
||||
```
|
||||
|
||||
**Radius Selection:**
|
||||
- Small canvases (≤10 children): 400px
|
||||
- Medium canvases (11-20 children): 500px
|
||||
- Large canvases (>20 children): 600px
|
||||
|
||||
#### Step 3: Position Secondary Branches
|
||||
|
||||
For each primary branch, arrange its children:
|
||||
|
||||
**Horizontal Layout** (preferred for most cases):
|
||||
|
||||
```python
|
||||
def position_secondary_horizontal(parent, children, distance=350):
|
||||
"""Arrange children horizontally to the right of parent"""
|
||||
n = len(children)
|
||||
total_height = sum(child.height for child in children)
|
||||
total_spacing = (n - 1) * VERTICAL_SPACING
|
||||
|
||||
# Start position (top of vertical arrangement)
|
||||
start_y = parent.center_y - (total_height + total_spacing) / 2
|
||||
|
||||
positions = []
|
||||
current_y = start_y
|
||||
|
||||
for child in children:
|
||||
x = parent.x + parent.width + distance
|
||||
y = current_y
|
||||
|
||||
positions.append({"x": x, "y": y})
|
||||
current_y += child.height + VERTICAL_SPACING
|
||||
|
||||
return positions
|
||||
```
|
||||
|
||||
**Vertical Layout** (for left/right primary branches):
|
||||
|
||||
```python
|
||||
def position_secondary_vertical(parent, children, distance=250):
|
||||
"""Arrange children vertically below parent"""
|
||||
n = len(children)
|
||||
total_width = sum(child.width for child in children)
|
||||
total_spacing = (n - 1) * HORIZONTAL_SPACING
|
||||
|
||||
# Start position (left of horizontal arrangement)
|
||||
start_x = parent.center_x - (total_width + total_spacing) / 2
|
||||
|
||||
positions = []
|
||||
current_x = start_x
|
||||
|
||||
for child in children:
|
||||
x = current_x
|
||||
y = parent.y + parent.height + distance
|
||||
|
||||
positions.append({"x": x, "y": y})
|
||||
current_x += child.width + HORIZONTAL_SPACING
|
||||
|
||||
return positions
|
||||
```
|
||||
|
||||
#### Step 4: Balance and Adjust
|
||||
|
||||
After initial placement, check for collisions and adjust:
|
||||
|
||||
```python
|
||||
def balance_layout(nodes):
|
||||
"""Adjust nodes to prevent overlaps"""
|
||||
max_iterations = 10
|
||||
|
||||
for iteration in range(max_iterations):
|
||||
collisions = find_all_collisions(nodes)
|
||||
if not collisions:
|
||||
break
|
||||
|
||||
for node1, node2 in collisions:
|
||||
# Move node2 away from node1
|
||||
dx = node2.center_x - node1.center_x
|
||||
dy = node2.center_y - node1.center_y
|
||||
distance = sqrt(dx*dx + dy*dy)
|
||||
|
||||
# Calculate required distance
|
||||
min_dist = calculate_min_distance(node1, node2)
|
||||
|
||||
if distance > 0:
|
||||
# Move proportionally
|
||||
move_x = (dx / distance) * (min_dist - distance) / 2
|
||||
move_y = (dy / distance) * (min_dist - distance) / 2
|
||||
|
||||
node2.x += move_x
|
||||
node2.y += move_y
|
||||
```
|
||||
|
||||
### 2. Tree Layout (Hierarchical Top-Down)
|
||||
|
||||
Alternative for deep hierarchies.
|
||||
|
||||
#### Positioning Formula
|
||||
|
||||
```python
|
||||
def position_tree_layout(root, tree):
|
||||
"""Top-down tree layout"""
|
||||
# Level 0 (root)
|
||||
root.x = 0 - root.width / 2
|
||||
root.y = 0 - root.height / 2
|
||||
|
||||
# Process each level
|
||||
for level in range(1, max_depth):
|
||||
nodes_at_level = get_nodes_at_level(tree, level)
|
||||
|
||||
# Calculate horizontal spacing
|
||||
total_width = sum(node.width for node in nodes_at_level)
|
||||
total_spacing = (len(nodes_at_level) - 1) * HORIZONTAL_SPACING
|
||||
|
||||
start_x = -(total_width + total_spacing) / 2
|
||||
y = level * (150 + VERTICAL_SPACING) # 150px level height
|
||||
|
||||
current_x = start_x
|
||||
for node in nodes_at_level:
|
||||
node.x = current_x
|
||||
node.y = y
|
||||
current_x += node.width + HORIZONTAL_SPACING
|
||||
```
|
||||
|
||||
## Freeform Layout Algorithm
|
||||
|
||||
### 1. Content-Based Grouping
|
||||
|
||||
First, identify natural groupings in content:
|
||||
|
||||
```python
|
||||
def identify_groups(nodes, content_structure):
|
||||
"""Group nodes by semantic relationships"""
|
||||
groups = []
|
||||
|
||||
# Analyze content structure
|
||||
for section in content_structure:
|
||||
group_nodes = [node for node in nodes if node.section == section]
|
||||
|
||||
if len(group_nodes) > 1:
|
||||
groups.append({
|
||||
"label": section.title,
|
||||
"nodes": group_nodes
|
||||
})
|
||||
|
||||
return groups
|
||||
```
|
||||
|
||||
### 2. Grid-Based Zone Layout
|
||||
|
||||
Divide canvas into zones for different groups:
|
||||
|
||||
```python
|
||||
def layout_zones(groups, canvas_width=2000, canvas_height=1500):
|
||||
"""Arrange groups in grid zones"""
|
||||
n_groups = len(groups)
|
||||
|
||||
# Calculate grid dimensions
|
||||
cols = ceil(sqrt(n_groups))
|
||||
rows = ceil(n_groups / cols)
|
||||
|
||||
zone_width = canvas_width / cols
|
||||
zone_height = canvas_height / rows
|
||||
|
||||
# Assign zones
|
||||
zones = []
|
||||
for i, group in enumerate(groups):
|
||||
col = i % cols
|
||||
row = i // cols
|
||||
|
||||
zone = {
|
||||
"x": col * zone_width - canvas_width / 2,
|
||||
"y": row * zone_height - canvas_height / 2,
|
||||
"width": zone_width * 0.9, # Leave 10% margin
|
||||
"height": zone_height * 0.9,
|
||||
"group": group
|
||||
}
|
||||
zones.append(zone)
|
||||
|
||||
return zones
|
||||
```
|
||||
|
||||
### 3. Within-Zone Node Positioning
|
||||
|
||||
Position nodes within each zone:
|
||||
|
||||
**Option A: Organic Flow**
|
||||
|
||||
```python
|
||||
def position_organic(zone, nodes):
|
||||
"""Organic, flowing arrangement within zone"""
|
||||
positions = []
|
||||
|
||||
# Start at zone top-left with margin
|
||||
current_x = zone.x + 50
|
||||
current_y = zone.y + 50
|
||||
row_height = 0
|
||||
|
||||
for node in nodes:
|
||||
# Check if node fits in current row
|
||||
if current_x + node.width > zone.x + zone.width - 50:
|
||||
# Move to next row
|
||||
current_x = zone.x + 50
|
||||
current_y += row_height + VERTICAL_SPACING
|
||||
row_height = 0
|
||||
|
||||
positions.append({
|
||||
"x": current_x,
|
||||
"y": current_y
|
||||
})
|
||||
|
||||
current_x += node.width + HORIZONTAL_SPACING
|
||||
row_height = max(row_height, node.height)
|
||||
|
||||
return positions
|
||||
```
|
||||
|
||||
**Option B: Structured Grid**
|
||||
|
||||
```python
|
||||
def position_grid(zone, nodes):
|
||||
"""Grid arrangement within zone"""
|
||||
n = len(nodes)
|
||||
cols = ceil(sqrt(n))
|
||||
rows = ceil(n / cols)
|
||||
|
||||
cell_width = (zone.width - 100) / cols # 50px margin each side
|
||||
cell_height = (zone.height - 100) / rows
|
||||
|
||||
positions = []
|
||||
for i, node in enumerate(nodes):
|
||||
col = i % cols
|
||||
row = i // cols
|
||||
|
||||
# Center node in cell
|
||||
x = zone.x + 50 + col * cell_width + (cell_width - node.width) / 2
|
||||
y = zone.y + 50 + row * cell_height + (cell_height - node.height) / 2
|
||||
|
||||
positions.append({"x": x, "y": y})
|
||||
|
||||
return positions
|
||||
```
|
||||
|
||||
### 4. Cross-Zone Connections
|
||||
|
||||
Calculate optimal edge paths between zones:
|
||||
|
||||
```python
|
||||
def calculate_edge_path(from_node, to_node):
|
||||
"""Determine edge connection points"""
|
||||
# Calculate centers
|
||||
from_center = (from_node.x + from_node.width/2,
|
||||
from_node.y + from_node.height/2)
|
||||
to_center = (to_node.x + to_node.width/2,
|
||||
to_node.y + to_node.height/2)
|
||||
|
||||
# Determine best sides to connect
|
||||
dx = to_center[0] - from_center[0]
|
||||
dy = to_center[1] - from_center[1]
|
||||
|
||||
# Choose sides based on direction
|
||||
if abs(dx) > abs(dy):
|
||||
# Horizontal connection
|
||||
from_side = "right" if dx > 0 else "left"
|
||||
to_side = "left" if dx > 0 else "right"
|
||||
else:
|
||||
# Vertical connection
|
||||
from_side = "bottom" if dy > 0 else "top"
|
||||
to_side = "top" if dy > 0 else "bottom"
|
||||
|
||||
return {
|
||||
"fromSide": from_side,
|
||||
"toSide": to_side
|
||||
}
|
||||
```
|
||||
|
||||
## Advanced Techniques
|
||||
|
||||
### Force-Directed Layout
|
||||
|
||||
For complex networks with many cross-connections:
|
||||
|
||||
```python
|
||||
def force_directed_layout(nodes, edges, iterations=100):
|
||||
"""Spring-based layout algorithm"""
|
||||
# Constants
|
||||
SPRING_LENGTH = 200
|
||||
SPRING_CONSTANT = 0.1
|
||||
REPULSION_CONSTANT = 5000
|
||||
|
||||
for iteration in range(iterations):
|
||||
# Calculate repulsive forces (all pairs)
|
||||
for node1 in nodes:
|
||||
force_x, force_y = 0, 0
|
||||
|
||||
for node2 in nodes:
|
||||
if node1 == node2:
|
||||
continue
|
||||
|
||||
dx = node1.x - node2.x
|
||||
dy = node1.y - node2.y
|
||||
distance = sqrt(dx*dx + dy*dy)
|
||||
|
||||
if distance > 0:
|
||||
# Repulsive force
|
||||
force = REPULSION_CONSTANT / (distance * distance)
|
||||
force_x += (dx / distance) * force
|
||||
force_y += (dy / distance) * force
|
||||
|
||||
node1.force_x = force_x
|
||||
node1.force_y = force_y
|
||||
|
||||
# Calculate attractive forces (connected nodes)
|
||||
for edge in edges:
|
||||
node1 = get_node(edge.fromNode)
|
||||
node2 = get_node(edge.toNode)
|
||||
|
||||
dx = node2.x - node1.x
|
||||
dy = node2.y - node1.y
|
||||
distance = sqrt(dx*dx + dy*dy)
|
||||
|
||||
# Spring force
|
||||
force = SPRING_CONSTANT * (distance - SPRING_LENGTH)
|
||||
|
||||
node1.force_x += (dx / distance) * force
|
||||
node1.force_y += (dy / distance) * force
|
||||
node2.force_x -= (dx / distance) * force
|
||||
node2.force_y -= (dy / distance) * force
|
||||
|
||||
# Apply forces
|
||||
for node in nodes:
|
||||
node.x += node.force_x
|
||||
node.y += node.force_y
|
||||
```
|
||||
|
||||
### Hierarchical Clustering
|
||||
|
||||
Group related nodes automatically:
|
||||
|
||||
```python
|
||||
def hierarchical_cluster(nodes, similarity_threshold=0.7):
|
||||
"""Cluster nodes by content similarity"""
|
||||
clusters = []
|
||||
|
||||
# Calculate similarity matrix
|
||||
similarity = calculate_similarity_matrix(nodes)
|
||||
|
||||
# Agglomerative clustering
|
||||
current_clusters = [[node] for node in nodes]
|
||||
|
||||
while len(current_clusters) > 1:
|
||||
# Find most similar clusters
|
||||
max_sim = 0
|
||||
merge_i, merge_j = 0, 1
|
||||
|
||||
for i in range(len(current_clusters)):
|
||||
for j in range(i + 1, len(current_clusters)):
|
||||
sim = cluster_similarity(current_clusters[i],
|
||||
current_clusters[j],
|
||||
similarity)
|
||||
if sim > max_sim:
|
||||
max_sim = sim
|
||||
merge_i, merge_j = i, j
|
||||
|
||||
if max_sim < similarity_threshold:
|
||||
break
|
||||
|
||||
# Merge clusters
|
||||
current_clusters[merge_i].extend(current_clusters[merge_j])
|
||||
current_clusters.pop(merge_j)
|
||||
|
||||
return current_clusters
|
||||
```
|
||||
|
||||
## Layout Optimization
|
||||
|
||||
### Minimize Edge Crossings
|
||||
|
||||
```python
|
||||
def minimize_crossings(nodes, edges):
|
||||
"""Reduce edge crossing through node repositioning"""
|
||||
crossings = count_crossings(edges)
|
||||
|
||||
# Try swapping adjacent nodes
|
||||
improved = True
|
||||
while improved:
|
||||
improved = False
|
||||
|
||||
for i in range(len(nodes) - 1):
|
||||
# Swap nodes i and i+1
|
||||
swap_positions(nodes[i], nodes[i+1])
|
||||
new_crossings = count_crossings(edges)
|
||||
|
||||
if new_crossings < crossings:
|
||||
crossings = new_crossings
|
||||
improved = True
|
||||
else:
|
||||
# Swap back
|
||||
swap_positions(nodes[i], nodes[i+1])
|
||||
```
|
||||
|
||||
### Visual Balance
|
||||
|
||||
```python
|
||||
def calculate_visual_weight(canvas):
|
||||
"""Calculate center of mass for visual balance"""
|
||||
total_weight = 0
|
||||
weighted_x = 0
|
||||
weighted_y = 0
|
||||
|
||||
for node in canvas.nodes:
|
||||
# Weight is proportional to area
|
||||
weight = node.width * node.height
|
||||
total_weight += weight
|
||||
|
||||
weighted_x += node.center_x * weight
|
||||
weighted_y += node.center_y * weight
|
||||
|
||||
center_x = weighted_x / total_weight
|
||||
center_y = weighted_y / total_weight
|
||||
|
||||
# Shift entire canvas to center at (0, 0)
|
||||
offset_x = -center_x
|
||||
offset_y = -center_y
|
||||
|
||||
for node in canvas.nodes:
|
||||
node.x += offset_x
|
||||
node.y += offset_y
|
||||
```
|
||||
|
||||
## Performance Optimization
|
||||
|
||||
### Spatial Indexing
|
||||
|
||||
For large canvases, use spatial indexing to speed up collision detection:
|
||||
|
||||
```python
|
||||
class SpatialGrid:
|
||||
"""Grid-based spatial index for fast collision detection"""
|
||||
|
||||
def __init__(self, cell_size=500):
|
||||
self.cell_size = cell_size
|
||||
self.grid = {}
|
||||
|
||||
def add_node(self, node):
|
||||
"""Add node to grid"""
|
||||
cells = self.get_cells(node)
|
||||
for cell in cells:
|
||||
if cell not in self.grid:
|
||||
self.grid[cell] = []
|
||||
self.grid[cell].append(node)
|
||||
|
||||
def get_cells(self, node):
|
||||
"""Get grid cells node occupies"""
|
||||
min_x = int(node.x / self.cell_size)
|
||||
max_x = int((node.x + node.width) / self.cell_size)
|
||||
min_y = int(node.y / self.cell_size)
|
||||
max_y = int((node.y + node.height) / self.cell_size)
|
||||
|
||||
cells = []
|
||||
for x in range(min_x, max_x + 1):
|
||||
for y in range(min_y, max_y + 1):
|
||||
cells.append((x, y))
|
||||
return cells
|
||||
|
||||
def get_nearby_nodes(self, node):
|
||||
"""Get nodes in nearby cells"""
|
||||
cells = self.get_cells(node)
|
||||
nearby = set()
|
||||
|
||||
for cell in cells:
|
||||
if cell in self.grid:
|
||||
nearby.update(self.grid[cell])
|
||||
|
||||
return nearby
|
||||
```
|
||||
|
||||
## Common Layout Patterns
|
||||
|
||||
### Timeline Layout
|
||||
|
||||
For chronological content:
|
||||
|
||||
```python
|
||||
def layout_timeline(events, direction="horizontal"):
|
||||
"""Create timeline layout"""
|
||||
if direction == "horizontal":
|
||||
for i, event in enumerate(events):
|
||||
event.x = i * (event.width + HORIZONTAL_SPACING)
|
||||
event.y = 0
|
||||
else: # vertical
|
||||
for i, event in enumerate(events):
|
||||
event.x = 0
|
||||
event.y = i * (event.height + VERTICAL_SPACING)
|
||||
```
|
||||
|
||||
### Circular Layout
|
||||
|
||||
For cyclical processes:
|
||||
|
||||
```python
|
||||
def layout_circular(nodes, radius=500):
|
||||
"""Arrange nodes in a circle"""
|
||||
n = len(nodes)
|
||||
angle_step = 2 * pi / n
|
||||
|
||||
for i, node in enumerate(nodes):
|
||||
angle = i * angle_step
|
||||
node.x = radius * cos(angle) - node.width / 2
|
||||
node.y = radius * sin(angle) - node.height / 2
|
||||
```
|
||||
|
||||
### Matrix Layout
|
||||
|
||||
For comparing multiple dimensions:
|
||||
|
||||
```python
|
||||
def layout_matrix(nodes, rows, cols):
|
||||
"""Arrange nodes in a matrix"""
|
||||
cell_width = 400
|
||||
cell_height = 250
|
||||
|
||||
for i, node in enumerate(nodes):
|
||||
row = i // cols
|
||||
col = i % cols
|
||||
|
||||
node.x = col * cell_width
|
||||
node.y = row * cell_height
|
||||
```
|
||||
|
||||
## Quality Checks
|
||||
|
||||
Before finalizing layout, verify:
|
||||
|
||||
1. **No Overlaps**: All nodes have minimum spacing
|
||||
2. **Balanced**: Visual center near (0, 0)
|
||||
3. **Accessible**: All nodes reachable via edges
|
||||
4. **Readable**: Text sizes appropriate for zoom level
|
||||
5. **Efficient**: Edge paths reasonably direct
|
||||
|
||||
Use these algorithms as foundations, adapting to specific content and user preferences.
|
||||
106
.claude/skills/obsidian-cli/SKILL.md
Normal file
106
.claude/skills/obsidian-cli/SKILL.md
Normal file
@@ -0,0 +1,106 @@
|
||||
---
|
||||
name: obsidian-cli
|
||||
description: Interact with Obsidian vaults using the Obsidian CLI to read, create, search, and manage notes, tasks, properties, and more. Also supports plugin and theme development with commands to reload plugins, run JavaScript, capture errors, take screenshots, and inspect the DOM. Use when the user asks to interact with their Obsidian vault, manage notes, search vault content, perform vault operations from the command line, or develop and debug Obsidian plugins and themes.
|
||||
---
|
||||
|
||||
# Obsidian CLI
|
||||
|
||||
Use the `obsidian` CLI to interact with a running Obsidian instance. Requires Obsidian to be open.
|
||||
|
||||
## Command reference
|
||||
|
||||
Run `obsidian help` to see all available commands. This is always up to date. Full docs: https://help.obsidian.md/cli
|
||||
|
||||
## Syntax
|
||||
|
||||
**Parameters** take a value with `=`. Quote values with spaces:
|
||||
|
||||
```bash
|
||||
obsidian create name="My Note" content="Hello world"
|
||||
```
|
||||
|
||||
**Flags** are boolean switches with no value:
|
||||
|
||||
```bash
|
||||
obsidian create name="My Note" silent overwrite
|
||||
```
|
||||
|
||||
For multiline content use `\n` for newline and `\t` for tab.
|
||||
|
||||
## File targeting
|
||||
|
||||
Many commands accept `file` or `path` to target a file. Without either, the active file is used.
|
||||
|
||||
- `file=<name>` — resolves like a wikilink (name only, no path or extension needed)
|
||||
- `path=<path>` — exact path from vault root, e.g. `folder/note.md`
|
||||
|
||||
## Vault targeting
|
||||
|
||||
Commands target the most recently focused vault by default. Use `vault=<name>` as the first parameter to target a specific vault:
|
||||
|
||||
```bash
|
||||
obsidian vault="My Vault" search query="test"
|
||||
```
|
||||
|
||||
## Common patterns
|
||||
|
||||
```bash
|
||||
obsidian read file="My Note"
|
||||
obsidian create name="New Note" content="# Hello" template="Template" silent
|
||||
obsidian append file="My Note" content="New line"
|
||||
obsidian search query="search term" limit=10
|
||||
obsidian daily:read
|
||||
obsidian daily:append content="- [ ] New task"
|
||||
obsidian property:set name="status" value="done" file="My Note"
|
||||
obsidian tasks daily todo
|
||||
obsidian tags sort=count counts
|
||||
obsidian backlinks file="My Note"
|
||||
```
|
||||
|
||||
Use `--copy` on any command to copy output to clipboard. Use `silent` to prevent files from opening. Use `total` on list commands to get a count.
|
||||
|
||||
## Plugin development
|
||||
|
||||
### Develop/test cycle
|
||||
|
||||
After making code changes to a plugin or theme, follow this workflow:
|
||||
|
||||
1. **Reload** the plugin to pick up changes:
|
||||
```bash
|
||||
obsidian plugin:reload id=my-plugin
|
||||
```
|
||||
2. **Check for errors** — if errors appear, fix and repeat from step 1:
|
||||
```bash
|
||||
obsidian dev:errors
|
||||
```
|
||||
3. **Verify visually** with a screenshot or DOM inspection:
|
||||
```bash
|
||||
obsidian dev:screenshot path=screenshot.png
|
||||
obsidian dev:dom selector=".workspace-leaf" text
|
||||
```
|
||||
4. **Check console output** for warnings or unexpected logs:
|
||||
```bash
|
||||
obsidian dev:console level=error
|
||||
```
|
||||
|
||||
### Additional developer commands
|
||||
|
||||
Run JavaScript in the app context:
|
||||
|
||||
```bash
|
||||
obsidian eval code="app.vault.getFiles().length"
|
||||
```
|
||||
|
||||
Inspect CSS values:
|
||||
|
||||
```bash
|
||||
obsidian dev:css selector=".workspace-leaf" prop=background-color
|
||||
```
|
||||
|
||||
Toggle mobile emulation:
|
||||
|
||||
```bash
|
||||
obsidian dev:mobile on
|
||||
```
|
||||
|
||||
Run `obsidian help` to see additional developer commands including CDP and debugger controls.
|
||||
196
.claude/skills/obsidian-markdown/SKILL.md
Normal file
196
.claude/skills/obsidian-markdown/SKILL.md
Normal file
@@ -0,0 +1,196 @@
|
||||
---
|
||||
name: obsidian-markdown
|
||||
description: Create and edit Obsidian Flavored Markdown with wikilinks, embeds, callouts, properties, and other Obsidian-specific syntax. Use when working with .md files in Obsidian, or when the user mentions wikilinks, callouts, frontmatter, tags, embeds, or Obsidian notes.
|
||||
---
|
||||
|
||||
# Obsidian Flavored Markdown Skill
|
||||
|
||||
Create and edit valid Obsidian Flavored Markdown. Obsidian extends CommonMark and GFM with wikilinks, embeds, callouts, properties, comments, and other syntax. This skill covers only Obsidian-specific extensions -- standard Markdown (headings, bold, italic, lists, quotes, code blocks, tables) is assumed knowledge.
|
||||
|
||||
## Workflow: Creating an Obsidian Note
|
||||
|
||||
1. **Add frontmatter** with properties (title, tags, aliases) at the top of the file. See [PROPERTIES.md](references/PROPERTIES.md) for all property types.
|
||||
2. **Write content** using standard Markdown for structure, plus Obsidian-specific syntax below.
|
||||
3. **Link related notes** using wikilinks (`[[Note]]`) for internal vault connections, or standard Markdown links for external URLs.
|
||||
4. **Embed content** from other notes, images, or PDFs using the `![[embed]]` syntax. See [EMBEDS.md](references/EMBEDS.md) for all embed types.
|
||||
5. **Add callouts** for highlighted information using `> [!type]` syntax. See [CALLOUTS.md](references/CALLOUTS.md) for all callout types.
|
||||
6. **Verify** the note renders correctly in Obsidian's reading view.
|
||||
|
||||
> When choosing between wikilinks and Markdown links: use `[[wikilinks]]` for notes within the vault (Obsidian tracks renames automatically) and `[text](url)` for external URLs only.
|
||||
|
||||
## Internal Links (Wikilinks)
|
||||
|
||||
```markdown
|
||||
[[Note Name]] Link to note
|
||||
[[Note Name|Display Text]] Custom display text
|
||||
[[Note Name#Heading]] Link to heading
|
||||
[[Note Name#^block-id]] Link to block
|
||||
[[#Heading in same note]] Same-note heading link
|
||||
```
|
||||
|
||||
Define a block ID by appending `^block-id` to any paragraph:
|
||||
|
||||
```markdown
|
||||
This paragraph can be linked to. ^my-block-id
|
||||
```
|
||||
|
||||
For lists and quotes, place the block ID on a separate line after the block:
|
||||
|
||||
```markdown
|
||||
> A quote block
|
||||
|
||||
^quote-id
|
||||
```
|
||||
|
||||
## Embeds
|
||||
|
||||
Prefix any wikilink with `!` to embed its content inline:
|
||||
|
||||
```markdown
|
||||
![[Note Name]] Embed full note
|
||||
![[Note Name#Heading]] Embed section
|
||||
![[image.png]] Embed image
|
||||
![[image.png|300]] Embed image with width
|
||||
![[document.pdf#page=3]] Embed PDF page
|
||||
```
|
||||
|
||||
See [EMBEDS.md](references/EMBEDS.md) for audio, video, search embeds, and external images.
|
||||
|
||||
## Callouts
|
||||
|
||||
```markdown
|
||||
> [!note]
|
||||
> Basic callout.
|
||||
|
||||
> [!warning] Custom Title
|
||||
> Callout with a custom title.
|
||||
|
||||
> [!faq]- Collapsed by default
|
||||
> Foldable callout (- collapsed, + expanded).
|
||||
```
|
||||
|
||||
Common types: `note`, `tip`, `warning`, `info`, `example`, `quote`, `bug`, `danger`, `success`, `failure`, `question`, `abstract`, `todo`.
|
||||
|
||||
See [CALLOUTS.md](references/CALLOUTS.md) for the full list with aliases, nesting, and custom CSS callouts.
|
||||
|
||||
## Properties (Frontmatter)
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My Note
|
||||
date: 2024-01-15
|
||||
tags:
|
||||
- project
|
||||
- active
|
||||
aliases:
|
||||
- Alternative Name
|
||||
cssclasses:
|
||||
- custom-class
|
||||
---
|
||||
```
|
||||
|
||||
Default properties: `tags` (searchable labels), `aliases` (alternative note names for link suggestions), `cssclasses` (CSS classes for styling).
|
||||
|
||||
See [PROPERTIES.md](references/PROPERTIES.md) for all property types, tag syntax rules, and advanced usage.
|
||||
|
||||
## Tags
|
||||
|
||||
```markdown
|
||||
#tag Inline tag
|
||||
#nested/tag Nested tag with hierarchy
|
||||
```
|
||||
|
||||
Tags can contain letters, numbers (not first character), underscores, hyphens, and forward slashes. Tags can also be defined in frontmatter under the `tags` property.
|
||||
|
||||
## Comments
|
||||
|
||||
```markdown
|
||||
This is visible %%but this is hidden%% text.
|
||||
|
||||
%%
|
||||
This entire block is hidden in reading view.
|
||||
%%
|
||||
```
|
||||
|
||||
## Obsidian-Specific Formatting
|
||||
|
||||
```markdown
|
||||
==Highlighted text== Highlight syntax
|
||||
```
|
||||
|
||||
## Math (LaTeX)
|
||||
|
||||
```markdown
|
||||
Inline: $e^{i\pi} + 1 = 0$
|
||||
|
||||
Block:
|
||||
$$
|
||||
\frac{a}{b} = c
|
||||
$$
|
||||
```
|
||||
|
||||
## Diagrams (Mermaid)
|
||||
|
||||
````markdown
|
||||
```mermaid
|
||||
graph TD
|
||||
A[Start] --> B{Decision}
|
||||
B -->|Yes| C[Do this]
|
||||
B -->|No| D[Do that]
|
||||
```
|
||||
````
|
||||
|
||||
To link Mermaid nodes to Obsidian notes, add `class NodeName internal-link;`.
|
||||
|
||||
## Footnotes
|
||||
|
||||
```markdown
|
||||
Text with a footnote[^1].
|
||||
|
||||
[^1]: Footnote content.
|
||||
|
||||
Inline footnote.^[This is inline.]
|
||||
```
|
||||
|
||||
## Complete Example
|
||||
|
||||
````markdown
|
||||
---
|
||||
title: Project Alpha
|
||||
date: 2024-01-15
|
||||
tags:
|
||||
- project
|
||||
- active
|
||||
status: in-progress
|
||||
---
|
||||
|
||||
# Project Alpha
|
||||
|
||||
This project aims to [[improve workflow]] using modern techniques.
|
||||
|
||||
> [!important] Key Deadline
|
||||
> The first milestone is due on ==January 30th==.
|
||||
|
||||
## Tasks
|
||||
|
||||
- [x] Initial planning
|
||||
- [ ] Development phase
|
||||
- [ ] Backend implementation
|
||||
- [ ] Frontend design
|
||||
|
||||
## Notes
|
||||
|
||||
The algorithm uses $O(n \log n)$ sorting. See [[Algorithm Notes#Sorting]] for details.
|
||||
|
||||
![[Architecture Diagram.png|600]]
|
||||
|
||||
Reviewed in [[Meeting Notes 2024-01-10#Decisions]].
|
||||
````
|
||||
|
||||
## References
|
||||
|
||||
- [Obsidian Flavored Markdown](https://help.obsidian.md/obsidian-flavored-markdown)
|
||||
- [Internal links](https://help.obsidian.md/links)
|
||||
- [Embed files](https://help.obsidian.md/embeds)
|
||||
- [Callouts](https://help.obsidian.md/callouts)
|
||||
- [Properties](https://help.obsidian.md/properties)
|
||||
58
.claude/skills/obsidian-markdown/references/CALLOUTS.md
Normal file
58
.claude/skills/obsidian-markdown/references/CALLOUTS.md
Normal file
@@ -0,0 +1,58 @@
|
||||
# Callouts Reference
|
||||
|
||||
## Basic Callout
|
||||
|
||||
```markdown
|
||||
> [!note]
|
||||
> This is a note callout.
|
||||
|
||||
> [!info] Custom Title
|
||||
> This callout has a custom title.
|
||||
|
||||
> [!tip] Title Only
|
||||
```
|
||||
|
||||
## Foldable Callouts
|
||||
|
||||
```markdown
|
||||
> [!faq]- Collapsed by default
|
||||
> This content is hidden until expanded.
|
||||
|
||||
> [!faq]+ Expanded by default
|
||||
> This content is visible but can be collapsed.
|
||||
```
|
||||
|
||||
## Nested Callouts
|
||||
|
||||
```markdown
|
||||
> [!question] Outer callout
|
||||
> > [!note] Inner callout
|
||||
> > Nested content
|
||||
```
|
||||
|
||||
## Supported Callout Types
|
||||
|
||||
| Type | Aliases | Color / Icon |
|
||||
|------|---------|-------------|
|
||||
| `note` | - | Blue, pencil |
|
||||
| `abstract` | `summary`, `tldr` | Teal, clipboard |
|
||||
| `info` | - | Blue, info |
|
||||
| `todo` | - | Blue, checkbox |
|
||||
| `tip` | `hint`, `important` | Cyan, flame |
|
||||
| `success` | `check`, `done` | Green, checkmark |
|
||||
| `question` | `help`, `faq` | Yellow, question mark |
|
||||
| `warning` | `caution`, `attention` | Orange, warning |
|
||||
| `failure` | `fail`, `missing` | Red, X |
|
||||
| `danger` | `error` | Red, zap |
|
||||
| `bug` | - | Red, bug |
|
||||
| `example` | - | Purple, list |
|
||||
| `quote` | `cite` | Gray, quote |
|
||||
|
||||
## Custom Callouts (CSS)
|
||||
|
||||
```css
|
||||
.callout[data-callout="custom-type"] {
|
||||
--callout-color: 255, 0, 0;
|
||||
--callout-icon: lucide-alert-circle;
|
||||
}
|
||||
```
|
||||
63
.claude/skills/obsidian-markdown/references/EMBEDS.md
Normal file
63
.claude/skills/obsidian-markdown/references/EMBEDS.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# Embeds Reference
|
||||
|
||||
## Embed Notes
|
||||
|
||||
```markdown
|
||||
![[Note Name]]
|
||||
![[Note Name#Heading]]
|
||||
![[Note Name#^block-id]]
|
||||
```
|
||||
|
||||
## Embed Images
|
||||
|
||||
```markdown
|
||||
![[image.png]]
|
||||
![[image.png|640x480]] Width x Height
|
||||
![[image.png|300]] Width only (maintains aspect ratio)
|
||||
```
|
||||
|
||||
## External Images
|
||||
|
||||
```markdown
|
||||

|
||||

|
||||
```
|
||||
|
||||
## Embed Audio
|
||||
|
||||
```markdown
|
||||
![[audio.mp3]]
|
||||
![[audio.ogg]]
|
||||
```
|
||||
|
||||
## Embed PDF
|
||||
|
||||
```markdown
|
||||
![[document.pdf]]
|
||||
![[document.pdf#page=3]]
|
||||
![[document.pdf#height=400]]
|
||||
```
|
||||
|
||||
## Embed Lists
|
||||
|
||||
```markdown
|
||||
![[Note#^list-id]]
|
||||
```
|
||||
|
||||
Where the list has a block ID:
|
||||
|
||||
```markdown
|
||||
- Item 1
|
||||
- Item 2
|
||||
- Item 3
|
||||
|
||||
^list-id
|
||||
```
|
||||
|
||||
## Embed Search Results
|
||||
|
||||
````markdown
|
||||
```query
|
||||
tag:#project status:done
|
||||
```
|
||||
````
|
||||
61
.claude/skills/obsidian-markdown/references/PROPERTIES.md
Normal file
61
.claude/skills/obsidian-markdown/references/PROPERTIES.md
Normal file
@@ -0,0 +1,61 @@
|
||||
# Properties (Frontmatter) Reference
|
||||
|
||||
Properties use YAML frontmatter at the start of a note:
|
||||
|
||||
```yaml
|
||||
---
|
||||
title: My Note Title
|
||||
date: 2024-01-15
|
||||
tags:
|
||||
- project
|
||||
- important
|
||||
aliases:
|
||||
- My Note
|
||||
- Alternative Name
|
||||
cssclasses:
|
||||
- custom-class
|
||||
status: in-progress
|
||||
rating: 4.5
|
||||
completed: false
|
||||
due: 2024-02-01T14:30:00
|
||||
---
|
||||
```
|
||||
|
||||
## Property Types
|
||||
|
||||
| Type | Example |
|
||||
|------|---------|
|
||||
| Text | `title: My Title` |
|
||||
| Number | `rating: 4.5` |
|
||||
| Checkbox | `completed: true` |
|
||||
| Date | `date: 2024-01-15` |
|
||||
| Date & Time | `due: 2024-01-15T14:30:00` |
|
||||
| List | `tags: [one, two]` or YAML list |
|
||||
| Links | `related: "[[Other Note]]"` |
|
||||
|
||||
## Default Properties
|
||||
|
||||
- `tags` - Note tags (searchable, shown in graph view)
|
||||
- `aliases` - Alternative names for the note (used in link suggestions)
|
||||
- `cssclasses` - CSS classes applied to the note in reading/editing view
|
||||
|
||||
## Tags
|
||||
|
||||
```markdown
|
||||
#tag
|
||||
#nested/tag
|
||||
#tag-with-dashes
|
||||
#tag_with_underscores
|
||||
```
|
||||
|
||||
Tags can contain: letters (any language), numbers (not first character), underscores `_`, hyphens `-`, forward slashes `/` (for nesting).
|
||||
|
||||
In frontmatter:
|
||||
|
||||
```yaml
|
||||
---
|
||||
tags:
|
||||
- tag1
|
||||
- nested/tag2
|
||||
---
|
||||
```
|
||||
68
.claudian/claudian-settings.json
Normal file
68
.claudian/claudian-settings.json
Normal file
@@ -0,0 +1,68 @@
|
||||
{
|
||||
"userName": "BlueRose",
|
||||
"permissionMode": "yolo",
|
||||
"model": "opus[1m]",
|
||||
"thinkingBudget": "medium",
|
||||
"effortLevel": "low",
|
||||
"serviceTier": "default",
|
||||
"enableAutoTitleGeneration": true,
|
||||
"titleGenerationModel": "",
|
||||
"excludedTags": [],
|
||||
"mediaFolder": "",
|
||||
"systemPrompt": "",
|
||||
"persistentExternalContextPaths": [],
|
||||
"sharedEnvironmentVariables": "",
|
||||
"envSnippets": [],
|
||||
"customContextLimits": {},
|
||||
"keyboardNavigation": {
|
||||
"scrollUpKey": "w",
|
||||
"scrollDownKey": "s",
|
||||
"focusInputKey": "i"
|
||||
},
|
||||
"locale": "en",
|
||||
"providerConfigs": {
|
||||
"claude": {
|
||||
"safeMode": "acceptEdits",
|
||||
"cliPath": "",
|
||||
"cliPathsByHost": {},
|
||||
"loadUserSettings": true,
|
||||
"enableChrome": false,
|
||||
"enableBangBash": false,
|
||||
"enableOpus1M": true,
|
||||
"enableSonnet1M": false,
|
||||
"lastModel": "opus[1m]",
|
||||
"environmentVariables": "",
|
||||
"environmentHash": ""
|
||||
},
|
||||
"codex": {
|
||||
"enabled": false,
|
||||
"safeMode": "workspace-write",
|
||||
"cliPath": "",
|
||||
"cliPathsByHost": {},
|
||||
"reasoningSummary": "detailed",
|
||||
"environmentVariables": "",
|
||||
"environmentHash": "",
|
||||
"installationMethod": "native-windows",
|
||||
"installationMethodsByHost": {},
|
||||
"wslDistroOverride": "",
|
||||
"wslDistroOverridesByHost": {}
|
||||
}
|
||||
},
|
||||
"settingsProvider": "claude",
|
||||
"savedProviderModel": {
|
||||
"claude": "opus[1m]"
|
||||
},
|
||||
"savedProviderEffort": {
|
||||
"claude": "low"
|
||||
},
|
||||
"savedProviderServiceTier": {},
|
||||
"savedProviderThinkingBudget": {
|
||||
"claude": "medium"
|
||||
},
|
||||
"lastCustomModel": "",
|
||||
"maxTabs": 3,
|
||||
"tabBarPosition": "input",
|
||||
"enableAutoScroll": true,
|
||||
"openInMainTab": false,
|
||||
"hiddenProviderCommands": {}
|
||||
}
|
||||
24
.claudian/sessions/conv-1776071385957-rol42oxg8.meta.json
Normal file
24
.claudian/sessions/conv-1776071385957-rol42oxg8.meta.json
Normal file
@@ -0,0 +1,24 @@
|
||||
{
|
||||
"id": "conv-1776071385957-rol42oxg8",
|
||||
"providerId": "claude",
|
||||
"title": "Greet user in Chinese",
|
||||
"titleGenerationStatus": "success",
|
||||
"createdAt": 1776071385957,
|
||||
"updatedAt": 1776179962356,
|
||||
"lastResponseAt": 1776179962356,
|
||||
"sessionId": "c432006f-72f1-4f80-9625-7cb62e468878",
|
||||
"providerState": {
|
||||
"providerSessionId": "c432006f-72f1-4f80-9625-7cb62e468878"
|
||||
},
|
||||
"currentNote": "07-Other/AI/Obsidian/Obsidian CLI.md",
|
||||
"usage": {
|
||||
"model": "opus[1m]",
|
||||
"inputTokens": 1,
|
||||
"cacheCreationInputTokens": 1084,
|
||||
"cacheReadInputTokens": 41428,
|
||||
"contextWindow": 1000000,
|
||||
"contextTokens": 42513,
|
||||
"percentage": 4,
|
||||
"contextWindowIsAuthoritative": true
|
||||
}
|
||||
}
|
||||
2
.obsidian/app.json
vendored
2
.obsidian/app.json
vendored
@@ -4,7 +4,7 @@
|
||||
"promptDelete": false,
|
||||
"pdfExportSettings": {
|
||||
"includeName": true,
|
||||
"pageSize": "A3",
|
||||
"pageSize": "A4",
|
||||
"landscape": false,
|
||||
"margin": "0",
|
||||
"downscalePercent": 87
|
||||
|
||||
3
.obsidian/community-plugins.json
vendored
3
.obsidian/community-plugins.json
vendored
@@ -36,5 +36,6 @@
|
||||
"Enhanced-editing",
|
||||
"table-editor-obsidian",
|
||||
"obsidian-excalidraw-plugin",
|
||||
"image-captions"
|
||||
"image-captions",
|
||||
"claudian"
|
||||
]
|
||||
11
.obsidian/plugins/claudian/data.json
vendored
Normal file
11
.obsidian/plugins/claudian/data.json
vendored
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"tabManagerState": {
|
||||
"openTabs": [
|
||||
{
|
||||
"tabId": "tab-1776071369651-6oz9pqs",
|
||||
"conversationId": "conv-1776071385957-rol42oxg8"
|
||||
}
|
||||
],
|
||||
"activeTabId": "tab-1776071369651-6oz9pqs"
|
||||
}
|
||||
}
|
||||
84218
.obsidian/plugins/claudian/main.js
vendored
Normal file
84218
.obsidian/plugins/claudian/main.js
vendored
Normal file
File diff suppressed because one or more lines are too long
10
.obsidian/plugins/claudian/manifest.json
vendored
Normal file
10
.obsidian/plugins/claudian/manifest.json
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
{
|
||||
"id": "claudian",
|
||||
"name": "Claudian",
|
||||
"version": "2.0.2",
|
||||
"minAppVersion": "1.4.5",
|
||||
"description": "Embeds Claude Code as an AI collaborator in your vault. Your vault becomes Claude's working directory, giving it full agentic capabilities: file read/write, search, bash commands, and multi-step workflows.",
|
||||
"author": "Yishen Tu",
|
||||
"authorUrl": "https://github.com/YishenTu",
|
||||
"isDesktopOnly": true
|
||||
}
|
||||
5699
.obsidian/plugins/claudian/styles.css
vendored
Normal file
5699
.obsidian/plugins/claudian/styles.css
vendored
Normal file
File diff suppressed because it is too large
Load Diff
@@ -1 +1 @@
|
||||
{"截帧并且返回结果。":{"截帧并且返回结果。":{"currentFile":{"count":1,"lastUpdated":1770709439590}}},"Maya CMake笔记":{"Maya CMake笔记":{"internalLink":{"count":1,"lastUpdated":1770716829521}}},"MCP商店":{"MCP商店":{"currentFile":{"count":1,"lastUpdated":1770718675186}}},"Simplegen":{"Simplegen":{"currentFile":{"count":1,"lastUpdated":1770720333062}}},"README":{"README":{"internalLink":{"count":1,"lastUpdated":1770818577089}}},"Notification":{"Notification":{"currentFile":{"count":1,"lastUpdated":1770818619712}}},"ServiceRequest":{"ServiceRequest":{"internalLink":{"count":1,"lastUpdated":1772077751440}}},"skills":{"skills":{"currentFile":{"count":1,"lastUpdated":1772100003535}}},"Bilibili":{"Bilibili":{"currentFile":{"count":1,"lastUpdated":1772112409334}}},"using-superpowers":{"using-superpowers":{"currentFile":{"count":1,"lastUpdated":1772348498708}}},"添加新的护城河:引擎相关MCP&Skill、AITA资产库":{"添加新的护城河:引擎相关MCP&Skill、AITA资产库":{"currentFile":{"count":1,"lastUpdated":1772891664291}}}}
|
||||
{"Agent":{"Agent":{"internalLink":{"count":2,"lastUpdated":1773554273209}}},"要解决这个问题,得对记忆进行合理规划与维护:":{"要解决这个问题,得对记忆进行合理规划与维护:":{"currentFile":{"count":1,"lastUpdated":1774938677236}}},"Openclaw-workspace":{"Openclaw-workspace":{"currentFile":{"count":1,"lastUpdated":1774938879095}}},"workflow":{"workflow":{"currentFile":{"count":1,"lastUpdated":1775020352083}}},"\\AI\\Skill\\MatrixAITA-POPODocs-Skill、D":{"\\AI\\Skill\\MatrixAITA-POPODocs-Skill、D":{"currentFile":{"count":1,"lastUpdated":1776242568663}}},"推荐加在仓库内已有的标准元数据文件中,例如:":{"推荐加在仓库内已有的标准元数据文件中,例如:":{"currentFile":{"count":1,"lastUpdated":1776242909075}}},"推荐加在仓库内已有的标准元数据文件中,":{"推荐加在仓库内已有的标准元数据文件中,":{"currentFile":{"count":1,"lastUpdated":1776242914868}}}}
|
||||
2
.obsidian/plugins/workspaces-plus/data.json
vendored
2
.obsidian/plugins/workspaces-plus/data.json
vendored
@@ -6,7 +6,7 @@
|
||||
"workspaceSettings": false,
|
||||
"systemDarkMode": false,
|
||||
"globalSettings": {},
|
||||
"activeWorkspaceDesktop": "阅读模式",
|
||||
"activeWorkspaceDesktop": "编辑模式",
|
||||
"activeWorkspaceMobile": "",
|
||||
"reloadLivePreview": false,
|
||||
"workspaceSwitcherRibbon": false,
|
||||
|
||||
2
.obsidian/workspaces.json
vendored
2
.obsidian/workspaces.json
vendored
@@ -368,5 +368,5 @@
|
||||
"workspaces-plus:settings-v1": {}
|
||||
}
|
||||
},
|
||||
"active": "阅读模式"
|
||||
"active": "编辑模式"
|
||||
}
|
||||
20
01-Diary/本周事务/未命名.md
Normal file
20
01-Diary/本周事务/未命名.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
title: 未命名
|
||||
date: 2026-04-02 20:19:19
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
status: inprogress
|
||||
destination:
|
||||
share: false
|
||||
obsidianUIMode: source
|
||||
---
|
||||
|
||||
武林有2个方向:
|
||||
1. 第一天(西湖 Or 运河)
|
||||
1. 往北可以坐地铁到香积寺,参观香积寺以及边上的大兜路特色街区,之后可以到乐堤港综合体吃个饭啥;之后可以坐地铁到拱宸桥看一下运河公园以及边上的街区。
|
||||
2. 往南可以坐地铁到凤起路,那边有一家抹茶店可以去体验一下(得早点去要排队),之后可以去往西湖边走,到西湖后继续往南,走到龙翔桥附近,可以考虑逛 银泰(吃饭啥)。之后继续往南
|
||||
1. 可以考虑去柳浪闻莺公园 => 坐观光车 => 游西湖 => 太子湾公园 => 苏堤 => 断桥
|
||||
2. 可以考虑去吴山广场 => 河坊街 => 胡雪岩故居 => 鼓楼附近吃饭啥的。去附近的候潮门 地铁站 (或者打车)=> 市民中心(附近有来福士、万象城综合体) => 走到城市阳台 看杭州的灯光秀(记得是18:30结束)。
|
||||
2. 第二天(西溪湿地)
|
||||
1. 做地铁到 天目里 => 西溪天堂 => 西溪湿地南部小路 => 高庄 => 福堤 => 西溪龙湖天街
|
||||
70
02-Note/WY/MatrixAITA团队服务部署/OSS+图床.md
Normal file
70
02-Note/WY/MatrixAITA团队服务部署/OSS+图床.md
Normal file
@@ -0,0 +1,70 @@
|
||||
# 部署
|
||||
```bash
|
||||
mkdir -p /home/matriaita_docker/oss/minio
|
||||
mkdir -p /home/matriaita_docker/oss/alist
|
||||
|
||||
```
|
||||
|
||||
|
||||
```yml
|
||||
version: '3.8'
|
||||
|
||||
services:
|
||||
minio:
|
||||
image: pgsty/minio:latest
|
||||
container_name: minio
|
||||
restart: always
|
||||
ports:
|
||||
- "9000:9000" # S3 API
|
||||
- "9001:9001" # 管理后台
|
||||
environment:
|
||||
MINIO_ROOT_USER: admin
|
||||
MINIO_ROOT_PASSWORD: mataita@666
|
||||
# --- 新增以下配置 ---
|
||||
# 1. 解决分享链接打不开的问题(设为你的 API 访问地址)
|
||||
MINIO_SERVER_URL: "http://ta.netease.com:9000"
|
||||
# 2. 解决管理后台登录跳转地址不对的问题
|
||||
MINIO_BROWSER_REDIRECT_URL: "http://ta.netease.com:9001"
|
||||
# 3. 解决跨域问题(允许来自 ta.netease.com 的请求,或者设为 * 允许所有)
|
||||
MINIO_API_CORS_ALLOW_ORIGIN: "http://ta.netease.com,http://ta.netease.com:5244"
|
||||
volumes:
|
||||
- ./minio:/data
|
||||
command: server /data --console-address ":9001"
|
||||
|
||||
alist:
|
||||
image: xhofe/alist:latest
|
||||
container_name: alist
|
||||
restart: always
|
||||
ports:
|
||||
- "5244:5244"
|
||||
volumes:
|
||||
- ./alist:/opt/alist/data
|
||||
environment:
|
||||
- PUID=1000
|
||||
- PGID=1000
|
||||
- UMASK=022
|
||||
depends_on:
|
||||
- minio
|
||||
```
|
||||
|
||||
## 配置步骤
|
||||
- 启动:`docker-compose up -d`
|
||||
- 获取 AList 密码:`docker exec -it alist ./alist admin`
|
||||
- **配置 MinIO**:
|
||||
- 访问 `http://服务器IP:9001`。
|
||||
- 创建一个 Bucket(如 `obsidian-assets`)。
|
||||
- 在 Access Keys 中创建一个 Key,记下 `Access Key` 和 `Secret Key`。
|
||||
- **配置 AList**:
|
||||
- 访问 `http://服务器IP:5244`。
|
||||
- 进入“管理” -> “存储” -> “添加”。驱动选 **MinIO**。
|
||||
- **挂载路径**填 `/assets`。
|
||||
- **Endpoint** 填 `http://minio:9000`(如果 AList 和 MinIO 在同一台服务器的 Docker 网络中)。
|
||||
- 填入刚才 MinIO 的 Bucket 和 Key。
|
||||
|
||||
## 相关信息
|
||||
1. MinIO
|
||||
1. `Access Key`:r9uq0TP7ZbF5BA41Gb6E
|
||||
2. `Secret Key`:DstYixNcDnumg5qZPRg4XxMfVZb2XFwH92ow0SbP
|
||||
2. AList
|
||||
1. admin
|
||||
2. NEW_PASSWORD
|
||||
@@ -16,6 +16,7 @@ rating: ⭐
|
||||
- https://github.com/Tencent/puerts/issues
|
||||
- https://github.com/Tencent/puerts/discussions
|
||||
# QuickStart
|
||||
- Puerts AI初始化Skill: https://github.com/noobGuaTai/ue5-puerts-init-skill
|
||||
## Setup
|
||||
1. 安装Nodejs v22.17.1。
|
||||
2. 通过安装全局typeScrpit模块。
|
||||
@@ -35,7 +36,7 @@ npm install -g typescript
|
||||
"dependencies": {
|
||||
"@types/mocha": "^10.0.10"
|
||||
}
|
||||
```
|
||||
```
|
||||
5. 打开工程,在引擎中点击 ue.d.ts 。 该功能用于生成项目、引擎符号信息,生成之后就能找到相关符号了。如果想在ts文件中调用新增的蓝图&C++方法,也需要点击ue.d.ts才能找到对应符号。可以阅读该文了解详细信息 https://puerts.github.io/docs/puerts/unreal/script_call_uclass
|
||||
![[Puerts_UE_D_TS.png]]
|
||||
6. 在`ProjectSettings - Packaging - Additional Not-Asset Directories to Package`中添加`Content/javaScript`。
|
||||
|
||||
187
03-UnrealEngine/逆向/魔改RenderDoc截帧PC端《鸣潮》.md
Normal file
187
03-UnrealEngine/逆向/魔改RenderDoc截帧PC端《鸣潮》.md
Normal file
@@ -0,0 +1,187 @@
|
||||
---
|
||||
title: 未命名
|
||||
date: 2026-03-25 21:43:14
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
---
|
||||

|
||||
|
||||
## 前言
|
||||
|
||||
之前写过一些截帧的文章,主要是通过模拟器来绕过加密系统,但是现在想想有可能两个游戏在移动端和PC端上用的Shader不一样,现在想用[RenderDoc](https://zhida.zhihu.com/search?content_id=271977473&content_type=Article&match_order=1&q=RenderDoc&zhida_source=entity)来截取PC环境下两个游戏的Shader
|
||||
|
||||

|
||||
|
||||
## 《鸣潮》是如何检测到RenderDoc的?
|
||||
|
||||
如果你是直接下载的打包好的RenderDoc,那么无论你是否从RenderDoc启动游戏还是是否有任何注入,都会显示启用了黑客工具
|
||||
|
||||

|
||||
|
||||
此外,《鸣潮》采用了[CrashSight](https://zhida.zhihu.com/search?content_id=271977473&content_type=Article&match_order=1&q=CrashSight&zhida_source=entity) 检测,所以在做的时候不但要修改RenderDoc的特征码,还需要修改注入方式
|
||||
|
||||
## 前期准备
|
||||
|
||||
RenderDoc的工具集是[VS2015](https://zhida.zhihu.com/search?content_id=271977473&content_type=Article&match_order=1&q=VS2015&zhida_source=entity)的[v140](https://zhida.zhihu.com/search?content_id=271977473&content_type=Article&match_order=1&q=v140&zhida_source=entity)版本,我用的是VS2022所以需要额外下载v140组件,或者是可以到源码里面手动更新平台工具集
|
||||
|
||||
[https://github.com/baldurk/renderdoc/tree/v1.xgithub.com/baldurk/renderdoc/tree/v1.x](https://link.zhihu.com/?target=https%3A//github.com/baldurk/renderdoc/tree/v1.x)
|
||||
|
||||
## 一,修改RenderDoc的特征来绕过第一步检测
|
||||
|
||||
### 核心 DLL — [Replay Marker](https://zhida.zhihu.com/search?content_id=271977473&content_type=Article&match_order=1&q=Replay+Marker&zhida_source=entity) 符号
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|renderdoc/api/replay/renderdoc_replay.h|52|函数名修改|renderdoc__replay__marker()|rendertest__replay__marker()|
|
||||
|
||||
|
||||
|
||||
> _说明:这是最核心的修改。`REPLAY_PROGRAM_MARKER()`_ _宏定义所有 RenderDoc 可执行文件导出的符号,DLL 通过动态查找该符号判断当前是 replay 环境还是目标游戏进程。若 DLL 期望_ _`rendertest__replay__marker`_ _但 EXE 导出的是_ _`renderdoc__replay__marker`,DLL 将误判为游戏进程并注入 GPU 钩子,导致 UI 崩溃。_
|
||||
|
||||
### 进程创建与注入 — 可执行文件路径硬编码
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|renderdoc/os/win32/win32_process.cpp|933|字符串替换|L"\\Win32\\Development\\renderdoccmd.exe"|L"\\Win32\\Development\\rendertestcmd.exe"|
|
||||
|renderdoc/os/win32/win32_process.cpp|946|字符串替换|L"\\Win32\\Release\\renderdoccmd.exe"|L"\\Win32\\Release\\rendertestcmd.exe"|
|
||||
|renderdoc/os/win32/win32_process.cpp|961|字符串替换|L"\\x86\\renderdoccmd.exe"|L"\\x86\\rendertestcmd.exe"|
|
||||
|renderdoc/os/win32/win32_process.cpp|973|字符串替换|L"\\x64\\Development\\renderdoccmd.exe"|L"\\x64\\Development\\rendertestcmd.exe"|
|
||||
|renderdoc/os/win32/win32_process.cpp|986|字符串替换|L"\\x64\\Release\\renderdoccmd.exe"|L"\\x64\\Release\\rendertestcmd.exe"|
|
||||
|renderdoc/os/win32/win32_process.cpp|1006|字符串替换|L"\\renderdoccmd.exe"|L"\\rendertestcmd.exe"|
|
||||
|
||||
|
||||
## [Shim DLL](https://zhida.zhihu.com/search?content_id=271977473&content_type=Article&match_order=1&q=Shim+DLL&zhida_source=entity) 路径硬编码
|
||||
|
||||
### Shim DLL 路径硬编码
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|renderdoc/os/win32/win32_process.cpp|1783|字符串拼接|"\\renderdocshim64.dll"|"\\rendertestshim64.dll"|
|
||||
|renderdoc/os/win32/win32_process.cpp|1792|字符串拼接|"\\Win32\\Development\\renderdocshim32.dll"|"\\Win32\\Development\\rendertestshim32.dll"|
|
||||
|renderdoc/os/win32/win32_process.cpp|1803|字符串拼接|"\\Win32\\Release\\renderdocshim32.dll"|"\\Win32\\Release\\rendertestshim32.dll"|
|
||||
|renderdoc/os/win32/win32_process.cpp|1811|字符串拼接|"\\x86\\renderdocshim32.dll"|"\\x86\\rendertestshim32.dll"|
|
||||
|renderdoc/os/win32/win32_process.cpp|1818|字符串拼接|"\\renderdocshim32.dll"|
|
||||
|
||||
|
||||
|
||||
### [进程白名单](https://zhida.zhihu.com/search?content_id=271977473&content_type=Article&match_order=1&q=%E8%BF%9B%E7%A8%8B%E7%99%BD%E5%90%8D%E5%8D%95&zhida_source=entity) — 避免注入到 RenderTest 自身
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|renderdoc/os/win32/sys_win32_hooks.cpp|342|字符串替换|"renderdoccmd.exe" \| app.contains("qrenderdoc.exe")|"rendertestcmd.exe" \| app.contains("qrendertest.exe")|
|
||||
|renderdoc/os/win32/sys_win32_hooks.cpp|351|字符串替换|"renderdoccmd.exe" \| cmd.contains("qrenderdoc.exe")|"rendertestcmd.exe" \| cmd.contains("qrendertest.exe")|
|
||||
|
||||
|
||||
|
||||
### 崩溃处理 — 命名内核对象
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|renderdoccmd/renderdoccmd_win32.cpp|603|字符串替换|"RENDERDOC_CRASHHANDLE"|"RENDERTEST_CRASHHANDLE"|
|
||||
|renderdoccmd/renderdoccmd_win32.cpp|818|字符串替换|GetModuleHandleA("renderdoc.dll")|GetModuleHandleA("rendertest.dll")|
|
||||
|core/crash_handler.h|62|字符串拼接|"RenderDoc\\dumps\\a"|"RenderTest\\dumps\\a"|
|
||||
|core/crash_handler.h|168|字符串拼接|"RenderDocBreakpadServer%llu"|"RenderTestBreakpadServer%llu"|
|
||||
|
||||
|
||||
|
||||
### 表格 6:文件路径与注册表
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|renderdoc/os/win32/win32_stringio.cpp|289|字符串拼接|"/qrenderdoc.exe"|"/qrendertest.exe"|
|
||||
|renderdoc/os/win32/win32_stringio.cpp|300|字符串拼接|"/../qrenderdoc.exe"|"/../qrendertest.exe"|
|
||||
|renderdoc/os/win32/win32_stringio.cpp|316|注册表路径|L"RenderDoc.RDCCapture.1\\DefaultIcon"|L"RenderTest.RDCCapture.1\\DefaultIcon"|
|
||||
|renderdoc/os/win32/win32_stringio.cpp|358|日志路径|L"RenderDoc\\%ls_..."|L"RenderTest\\%ls_..."|
|
||||
|renderdoc/os/win32/win32_stringio.cpp|367|日志路径|L"RenderDoc\\%ls_..."|L"RenderTest\\%ls_..."|
|
||||
|
||||
|
||||
|
||||
### OpenGL 窗口类名
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|driver/gl/wgl_platform.cpp|28|宏定义|L"renderdocGLclass"|L"rendertestGLclass"|
|
||||
|
||||
|
||||
|
||||
### [全局 Hook 共享内存名称](https://zhida.zhihu.com/search?content_id=271977473&content_type=Article&match_order=1&q=%E5%85%A8%E5%B1%80+Hook+%E5%85%B1%E4%BA%AB%E5%86%85%E5%AD%98%E5%90%8D%E7%A7%B0&zhida_source=entity)
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|renderdocshim/renderdocshim.h|36|宏定义|"RenderDocGlobalHookData64"|"RenderTestGlobalHookData64"|
|
||||
|renderdocshim/renderdocshim.h|39|宏定义|"RenderDocGlobalHookData32"|"RenderTestGlobalHookData32"|
|
||||
|
||||
|
||||
|
||||
### 资源文件
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|data/renderdoc.rc|87|资源字符串|"Core DLL for RenderDoc"|"Core DLL for RenderTest"|
|
||||
|data/renderdoc.rc|92|资源字符串|"ProductName", "RenderDoc"|"ProductName", "RenderTest"|
|
||||
|
||||
|
||||
|
||||
### Qt UI 层
|
||||
|
||||
|
||||
|
||||
|文件路径|行号|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|qrenderdoc/renderdocui_stub.cpp|62|字符串拼接|L"qrenderdoc.exe"|L"qrendertest.exe"|
|
||||
|qrenderdoc/Code/qrenderdoc.cpp|173|Qt 翻译上下文|"qrenderdoc"|"qrendertest"|
|
||||
|qrenderdoc/Code/qrenderdoc.cpp|198|日志输出|"QRenderDoc initialising."|"QRenderTest initialising."|
|
||||
|qrenderdoc/Code/qrenderdoc.cpp|267|会话名|"QRenderDoc"|"QRenderTest"|
|
||||
|qrenderdoc/Code/qrenderdoc.cpp|330|描述文本|"Qt UI for RenderDoc"|"Qt UI for RenderTest"|
|
||||
|qrenderdoc/Code/qrenderdoc.cpp|393|版本输出|"QRenderDoc v%s"|"QRenderTest v%s"|
|
||||
|qrenderdoc/Windows/MainWindow.cpp|1217|窗口标题|"RenderDoc "|"RenderTest "|
|
||||
|
||||
|
||||
|
||||
### VS 项目文件(编译输出名 + UAC 提权)
|
||||
|
||||
|
||||
|
||||
|文件路径|属性|修改类型|修改前|修改后|
|
||||
|---|---|---|---|---|
|
||||
|qrenderdoc/renderdocui_stub.vcxproj|RootNamespace|修改|renderdocui_stub|rendertestui_stub|
|
||||
|qrenderdoc/renderdocui_stub.vcxproj|ProjectName|修改|renderdocui_stub|rendertestui_stub|
|
||||
|qrenderdoc/renderdocui_stub.vcxproj|PrimaryOutput|新增|(无)|rendertestui|
|
||||
|qrenderdoc/renderdocui_stub.vcxproj|TargetName|新增|(无)|rendertestui|
|
||||
|qrenderdoc/renderdocui_stub.vcxproj|UACExecutionLevel|新增|(无)|RequireAdmini|
|
||||
|
||||
|
||||
|
||||
## CrashSight检测
|
||||
|
||||
这里可以在Hook的时候采用SetThreadContext注入
|
||||
|
||||
修改文件:renderdoc/os/win32/win32_process.cpp
|
||||
|
||||
```text
|
||||
uintptr_t loc = FindRemoteDLL(pi.dwProcessId, STRINGIZE(RDOC_BASE_NAME) ".dll");
|
||||
CloseHandle(hProcess);
|
||||
hProcess = NULL;
|
||||
if(loc != 0)
|
||||
```
|
||||
|
||||
编辑于 2026-03-25 11:37・重庆
|
||||
2
07-Other/AI/AI Agent/Agent.md
Normal file
2
07-Other/AI/AI Agent/Agent.md
Normal file
@@ -0,0 +1,2 @@
|
||||
# 前言
|
||||
- [ ] https://www.bilibili.com/video/BV1VzfoBFE5w/?spm_id_from=333.1387.homepage.video_card.click&vd_source=a5a1507212662f8883c096200c37b6bd
|
||||
@@ -404,4 +404,5 @@ remaining-fix (4 Agent) 剩余问题并行修复
|
||||
│ 完成 → 销毁
|
||||
```
|
||||
|
||||
每支团队拥有独立的 TaskList 和成员列表,团队之间通过**共享文件系统**传递成果(上一支团队的代码产出是下一支团队的评审/修复输入)。Team Lead 的主会话贯穿全程,是唯一跨团队的持久上下文。
|
||||
每支团队拥有独立的 TaskList 和成员列表,团队之间通过**共享文件系统**传递成果(上一支团队的代码产出是下一支团队的评审/修复输入)。Team Lead 的主会话贯穿全程,是唯一跨团队的持久上下文。
|
||||
|
||||
|
||||
42
07-Other/AI/AI Agent/ClaudeCode/AI Agent 联调提示词.md
Normal file
42
07-Other/AI/AI Agent/ClaudeCode/AI Agent 联调提示词.md
Normal file
@@ -0,0 +1,42 @@
|
||||
```markdown
|
||||
────────────────
|
||||
B|背景
|
||||
────────────────
|
||||
我是[XX身份,如:老师/作者],正在进行[XX项目]
|
||||
我们即将结束当前对话,需要保存完整上下文
|
||||
|
||||
────────────────
|
||||
T|任务
|
||||
────────────────
|
||||
在我们继续之前,请先完成以下动作:
|
||||
生成【记忆胶囊】,记录项目全貌和当前状态
|
||||
确保一个全新对话能凭此文档无损接续工作
|
||||
|
||||
────────────────
|
||||
C|约束
|
||||
────────────────
|
||||
短句优先,每条1~2句话,动词开头,信息密度最大化
|
||||
不确定或未提及的信息,标注【待确认】。
|
||||
禁止补充推测、美化或编造内容。
|
||||
token意识:区分信息密度,避免废话,但不牺牲关键细节
|
||||
总字数控制在2000字以内(约500~1500 tokens)
|
||||
|
||||
输出结构(严格遵守):
|
||||
1. 对话主题:正在做什么项目、讨论什么
|
||||
2. 关键演化:按"最初想法 → 转折/拆解 → 当前焦点"时间线记录
|
||||
3. 核心观点:已达成的关键共识(按时间顺序,≥3条)
|
||||
4. 进度节点:已完成的阶段 + 当前进度(≥2个)
|
||||
5. 待办事项:下一步要做什么 + 阻塞项(≥1条)
|
||||
6. 特殊说明:需注意的细节、约定、偏好,
|
||||
|
||||
输出格式:
|
||||
输出为一个完整的 Markdown 文档,便于复制保存。
|
||||
文件名格式:记忆胶囊-[项目名]-[日期]-[序号].md
|
||||
(如有文件系统权限,直接保存为文件;否则输出代码块由用户手动保存)
|
||||
|
||||
────────────────
|
||||
A|验收(必须执行)
|
||||
────────────────
|
||||
输出前:逐一核对6大要素是否齐全。
|
||||
输出后:标注 ✓完整 / ✗缺项:[原因]
|
||||
```
|
||||
@@ -0,0 +1,690 @@
|
||||
|
||||
|
||||
> 本文记录了在 GitLab 项目中集成 Claude Code CLI,实现"给 Issue 打标签即可自动修复 Bug"的完整方案与实践过程。
|
||||
|
||||
---
|
||||
|
||||
## 目录
|
||||
|
||||
- [方案概述](#方案概述)
|
||||
- [整体架构](#整体架构)
|
||||
- [前置条件](#前置条件)
|
||||
- [第一步:配置 CI/CD Variables](#第一步配置-cicd-variables)
|
||||
- [第二步:创建 Pipeline Trigger Token](#第二步创建-pipeline-trigger-token)
|
||||
- [第三步:编写 .gitlab-ci.yml](#第三步编写-gitlab-ciyml)
|
||||
- [Job 1: claude:scan-issues — 扫描 Issue](#job-1-claudescan-issues--扫描-issue)
|
||||
- [Job 2: claude:fix-issue — 执行修复](#job-2-claudefix-issue--执行修复)
|
||||
- [第四步:创建定时调度](#第四步创建定时调度)
|
||||
- [第五步:编写 CLAUDE.md 项目指南](#第五步编写-claudemd-项目指南)
|
||||
- [使用流程](#使用流程)
|
||||
- [手动触发脚本](#手动触发脚本)
|
||||
- [关键设计细节](#关键设计细节)
|
||||
- [踩坑记录与调试经验](#踩坑记录与调试经验)
|
||||
- [完整 .gitlab-ci.yml 参考](#完整-gitlab-ciyml-参考)
|
||||
|
||||
---
|
||||
|
||||
## 方案概述
|
||||
|
||||
核心思路:利用 GitLab CI/CD 的 **Schedule(定时调度)** + **Trigger(触发器)** 机制,定时扫描带有 `auto-fix` 标签的 Issue,自动启动 Claude Code CLI 分析代码、修复 Bug、提交代码并在 Issue 上回复修复摘要。
|
||||
|
||||
**一句话流程**:开发者给 Issue 打上 `auto-fix` 标签 → 定时任务自动触发 → Claude Code 读取 Issue、分析代码、实施修复 → 自动 commit & push → 在 Issue 评论中报告结果。
|
||||
|
||||
---
|
||||
|
||||
## 整体架构
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────────────────────────────┐
|
||||
│ GitLab 项目 │
|
||||
│ │
|
||||
│ ┌──────────┐ ┌──────────────────┐ ┌───────────────────┐ │
|
||||
│ │ Issue │ │ Schedule (定时) │ │ Trigger Pipeline │ │
|
||||
│ │ auto-fix │───▶│ claude:scan-issues│───▶│ claude:fix-issue │ │
|
||||
│ │ 标签 │ │ (alpine:3.19) │ │ (node:20) │ │
|
||||
│ └──────────┘ └──────────────────┘ └─────────┬─────────┘ │
|
||||
│ │ │
|
||||
│ ┌───────────▼─────────┐ │
|
||||
│ │ Claude Code CLI │ │
|
||||
│ │ (npm i -g 安装) │ │
|
||||
│ │ │ │
|
||||
│ │ 1. 读取 Issue 详情 │ │
|
||||
│ │ 2. 分析项目代码 │ │
|
||||
│ │ 3. 实施修复 │ │
|
||||
│ │ 4. 验证类型检查 │ │
|
||||
│ │ 5. 提交 & 推送 │ │
|
||||
│ │ 6. 评论 Issue │ │
|
||||
│ └─────────────────────┘ │
|
||||
└─────────────────────────────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
### 标签状态机
|
||||
|
||||
Issue 通过标签流转追踪处理状态,确保不会被重复处理:
|
||||
|
||||
```
|
||||
auto-fix ──▶ claude-processing ──▶ claude-done
|
||||
(用户添加) (扫描时切换) (修复完成后设置)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 前置条件
|
||||
|
||||
| 条件 | 说明 |
|
||||
|------|------|
|
||||
| GitLab 实例 | 需要支持 CI/CD Schedules 和 Pipeline Triggers |
|
||||
| Runner | 可执行 Docker 镜像的 GitLab Runner |
|
||||
| Anthropic API 访问 | 直接使用 Anthropic API 或通过内部代理 |
|
||||
| GitLab Personal Access Token | 需要 `api` scope,用于操作 Issue 和推送代码 |
|
||||
|
||||
---
|
||||
|
||||
## 第一步:配置 CI/CD Variables
|
||||
|
||||
进入 GitLab 项目 → **Settings → CI/CD → Variables**,添加以下变量:
|
||||
|
||||
| 变量名 | 类型 | 说明 |
|
||||
|--------|------|------|
|
||||
| `GITLAB_ACCESS_TOKEN` | Masked | Personal Access Token(api scope),扫描 Job 使用 |
|
||||
| `ANTHROPIC_AUTH_TOKEN` | Masked | Anthropic API Key(或内部代理的 auth token) |
|
||||
| `TRIGGER_TOKEN` | 见下一步 | Pipeline 触发器 Token |
|
||||
|
||||
> **安全提示**:`GITLAB_ACCESS_TOKEN` 和 `ANTHROPIC_AUTH_TOKEN` 务必勾选 **Masked**,防止在日志中泄露。
|
||||
|
||||
### 关于 Anthropic API 访问
|
||||
|
||||
如果你直接使用 Anthropic 官方 API,需要设置:
|
||||
- `ANTHROPIC_AUTH_TOKEN` = 你的 Anthropic API Key
|
||||
|
||||
如果使用内部代理(如本项目使用 `openai.nie.netease.com`),则在 CI Job 中通过环境变量指定:
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
ANTHROPIC_BASE_URL: "https://openai.nie.netease.com" # 内部代理地址
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 第二步:创建 Pipeline Trigger Token
|
||||
|
||||
进入 **Settings → CI/CD → Pipeline trigger tokens**,点击 **Add trigger** 创建一个触发器。
|
||||
|
||||
创建后会得到一个 Token,将其记录下来。扫描 Job 将用此 Token 触发修复 Pipeline。
|
||||
|
||||
将该 Token 设置为 CI/CD Variable `TRIGGER_TOKEN`。
|
||||
|
||||
---
|
||||
|
||||
## 第三步:编写 .gitlab-ci.yml
|
||||
|
||||
在 `.gitlab-ci.yml` 中添加 `ai_fix` Stage 和两个 Job。
|
||||
|
||||
### Job 1: claude:scan-issues — 扫描 Issue
|
||||
|
||||
这是一个轻量级 Job,使用 `alpine:3.19` 镜像,只做 API 调用:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
- ai_fix
|
||||
|
||||
claude:scan-issues:
|
||||
stage: ai_fix
|
||||
image: alpine:3.19
|
||||
before_script:
|
||||
- apk add --no-cache curl jq
|
||||
script:
|
||||
- |
|
||||
echo "Scanning for Issues with label 'auto-fix'..."
|
||||
|
||||
# 校验必要变量
|
||||
for var in GITLAB_ACCESS_TOKEN TRIGGER_TOKEN ANTHROPIC_AUTH_TOKEN; do
|
||||
eval "val=\$$var"
|
||||
if [ -z "$val" ]; then
|
||||
echo "ERROR: $var is not set."
|
||||
exit 1
|
||||
fi
|
||||
done
|
||||
|
||||
# 查询带 auto-fix 标签的 Issue,排除已处理的
|
||||
ISSUES=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues?labels=auto-fix¬%5Blabels%5D=claude-processing,claude-done&state=opened&per_page=10")
|
||||
|
||||
COUNT=$(echo "$ISSUES" | jq 'length')
|
||||
echo "Found $COUNT issue(s) with 'auto-fix' label."
|
||||
|
||||
if [ "$COUNT" -eq 0 ] || [ "$COUNT" = "null" ]; then
|
||||
echo "Nothing to do."
|
||||
exit 0
|
||||
fi
|
||||
|
||||
# 逐个处理
|
||||
echo "$ISSUES" | jq -r '.[].iid' | while read -r IID; do
|
||||
TITLE=$(echo "$ISSUES" | jq -r ".[] | select(.iid == $IID) | .title")
|
||||
echo ">>> Triggering fix for Issue #${IID}: ${TITLE}"
|
||||
|
||||
# 1) 切换标签: auto-fix → claude-processing
|
||||
curl -s --request PUT \
|
||||
--header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"remove_labels\": \"auto-fix\", \"add_labels\": \"claude-processing\"}" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$IID" > /dev/null
|
||||
|
||||
# 2) 通过 Trigger API 触发修复 Pipeline
|
||||
curl -s -X POST \
|
||||
--form "token=${TRIGGER_TOKEN}" \
|
||||
--form "ref=${CI_DEFAULT_BRANCH}" \
|
||||
--form "variables[ISSUE_IID]=${IID}" \
|
||||
--form "variables[GITLAB_TOKEN]=${GITLAB_ACCESS_TOKEN}" \
|
||||
--form "variables[ANTHROPIC_AUTH_TOKEN]=${ANTHROPIC_AUTH_TOKEN}" \
|
||||
"https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/trigger/pipeline"
|
||||
done
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"' # 定时触发
|
||||
- if: '$CI_PIPELINE_SOURCE == "web"' # 手动触发
|
||||
when: manual
|
||||
allow_failure: true
|
||||
timeout: 5m
|
||||
```
|
||||
|
||||
**核心逻辑**:
|
||||
1. 通过 GitLab Issues API 查询 `labels=auto-fix` 且排除 `claude-processing` 和 `claude-done` 的 Issue
|
||||
2. 对每个 Issue 切换标签(防止重复处理)
|
||||
3. 通过 Pipeline Trigger API 启动独立的修复 Pipeline,传入 `ISSUE_IID`、`GITLAB_TOKEN`、`ANTHROPIC_AUTH_TOKEN`
|
||||
|
||||
### Job 2: claude:fix-issue — 执行修复
|
||||
|
||||
这是核心修复 Job,使用 `node:20` 镜像,安装并运行 Claude Code:
|
||||
|
||||
```yaml
|
||||
claude:fix-issue:
|
||||
stage: ai_fix
|
||||
image: node:20
|
||||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
GIT_DEPTH: 0 # 完整克隆,Claude 需要完整历史
|
||||
ANTHROPIC_BASE_URL: "https://api.anthropic.com" # 或内部代理地址
|
||||
ANTHROPIC_MODEL: "claude-sonnet-4-6"
|
||||
DISABLE_AUTOUPDATER: "1" # 禁止 Claude Code 自动更新
|
||||
DISABLE_PROMPT_CACHING: "1"
|
||||
before_script:
|
||||
- apt-get update && apt-get install -y git curl jq
|
||||
- npm install -g @anthropic-ai/claude-code
|
||||
- git config --global user.email "claude-bot@example.com"
|
||||
- git config --global user.name "Claude AI Bot"
|
||||
script:
|
||||
- |
|
||||
# Step 1: 获取 Issue 详情
|
||||
if [ -z "$ISSUE_IID" ]; then
|
||||
echo "ERROR: ISSUE_IID variable is required."
|
||||
exit 1
|
||||
fi
|
||||
|
||||
ISSUE_JSON=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID")
|
||||
|
||||
ISSUE_TITLE=$(echo "$ISSUE_JSON" | jq -r '.title // empty')
|
||||
ISSUE_DESCRIPTION=$(echo "$ISSUE_JSON" | jq -r '.description // empty')
|
||||
|
||||
if [ -z "$ISSUE_TITLE" ]; then
|
||||
echo "ERROR: Could not fetch Issue #$ISSUE_IID"
|
||||
exit 1
|
||||
fi
|
||||
echo "Processing Issue #${ISSUE_IID}: ${ISSUE_TITLE}"
|
||||
- |
|
||||
# Step 2: 切换到主分支
|
||||
git checkout "$CI_DEFAULT_BRANCH"
|
||||
git pull origin "$CI_DEFAULT_BRANCH"
|
||||
- |
|
||||
# Step 3: 运行 Claude Code
|
||||
export ANTHROPIC_API_KEY="${ANTHROPIC_AUTH_TOKEN}"
|
||||
|
||||
PROMPT="你是一个代码修复助手。请分析并修复以下 GitLab Issue:
|
||||
|
||||
## Issue #${ISSUE_IID}: ${ISSUE_TITLE}
|
||||
|
||||
${ISSUE_DESCRIPTION}
|
||||
|
||||
请:
|
||||
1. 分析项目代码,定位问题根因
|
||||
2. 实施修复
|
||||
3. 确保代码能通过类型检查(TypeScript 项目运行 npx tsc --noEmit)
|
||||
4. 用中文输出修复摘要(包括修改了哪些文件、改了什么、为什么这样改)
|
||||
|
||||
项目根目录已准备就绪。"
|
||||
|
||||
RESULT=$(claude -p "$PROMPT" \
|
||||
--bare \
|
||||
--permission-mode acceptEdits \
|
||||
--allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
|
||||
--max-turns 30 \
|
||||
--output-format text \
|
||||
2>&1) || true
|
||||
|
||||
echo "$RESULT" > repair_summary.txt
|
||||
- |
|
||||
# Step 4: 提交并推送
|
||||
git add -A
|
||||
git reset -- repair_summary.txt claude_stderr.txt 2>/dev/null || true
|
||||
|
||||
if git diff --cached --quiet; then
|
||||
echo "No file changes detected."
|
||||
COMMIT_STATUS="no_changes"
|
||||
else
|
||||
git commit -m "fix(auto): resolve Issue #${ISSUE_IID} - ${ISSUE_TITLE} [skip ci]"
|
||||
git push "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" \
|
||||
HEAD:"$CI_DEFAULT_BRANCH"
|
||||
COMMIT_STATUS="pushed"
|
||||
fi
|
||||
- |
|
||||
# Step 5: 在 Issue 上发表评论
|
||||
SUMMARY=$(cat repair_summary.txt | head -200 | \
|
||||
sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
|
||||
|
||||
if [ "$COMMIT_STATUS" = "pushed" ]; then
|
||||
BODY="## ✅ Claude Code 自动修复完成\n\n修复已推送到 \`${CI_DEFAULT_BRANCH}\`。\n\nPipeline: ${CI_PIPELINE_URL}\n\n<details><summary>修复详情</summary>\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n</details>"
|
||||
else
|
||||
BODY="## ⚠️ Claude Code 分析完成\n\n未检测到需要变更的文件。\n\nPipeline: ${CI_PIPELINE_URL}\n\n<details><summary>分析详情</summary>\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n</details>"
|
||||
fi
|
||||
|
||||
curl -s --request POST \
|
||||
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"body\": \"${BODY}\"}" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID/notes"
|
||||
- |
|
||||
# Step 6: 更新标签为 claude-done
|
||||
curl -s --request PUT \
|
||||
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"remove_labels\": \"claude-processing\", \"add_labels\": \"claude-done\"}" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID" > /dev/null
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "trigger" && $ISSUE_IID' # 被 scan-issues 触发
|
||||
- if: '$CI_PIPELINE_SOURCE == "web" && $ISSUE_IID' # 手动触发(需提供 ISSUE_IID)
|
||||
when: manual
|
||||
allow_failure: true
|
||||
timeout: 30m
|
||||
```
|
||||
|
||||
**Claude Code CLI 关键参数说明**:
|
||||
|
||||
| 参数 | 说明 |
|
||||
|------|------|
|
||||
| `-p "$PROMPT"` | 非交互模式,直接传入提示词 |
|
||||
| `--bare` | 精简输出,去掉装饰性格式 |
|
||||
| `--permission-mode acceptEdits` | 自动接受文件编辑(CI 环境必需) |
|
||||
| `--allowedTools "Bash,Read,Edit,Write,Glob,Grep"` | 限制可用工具,仅保留文件系统操作 |
|
||||
| `--max-turns 30` | 最大对话轮次,防止无限循环 |
|
||||
| `--output-format text` | 纯文本输出 |
|
||||
|
||||
---
|
||||
|
||||
## 第四步:创建定时调度
|
||||
|
||||
进入 **CI/CD → Schedules**,创建定时任务:
|
||||
|
||||
| 配置项 | 建议值 | 说明 |
|
||||
|--------|--------|------|
|
||||
| Description | Auto-fix Issue Scanner | 任务描述 |
|
||||
| Interval Pattern | `*/5 * * * *` | 每 5 分钟扫描一次 |
|
||||
| Cron Timezone | 你的时区 | 例如 Asia/Shanghai |
|
||||
| Target branch | `main` | 主分支 |
|
||||
|
||||
保存后,Schedule 会按设定频率运行 `claude:scan-issues` Job。
|
||||
|
||||
---
|
||||
|
||||
## 第五步:编写 CLAUDE.md 项目指南
|
||||
|
||||
在项目根目录创建 `CLAUDE.md`,Claude Code 在 CI 中运行时会自动读取此文件作为上下文指导:
|
||||
|
||||
```markdown
|
||||
# Project Guidelines
|
||||
|
||||
## 项目结构
|
||||
- `src/` — 主要源码
|
||||
- `tests/` — 测试文件
|
||||
|
||||
## 开发规范
|
||||
- 修改 TypeScript 代码后运行 `npx tsc --noEmit` 验证类型
|
||||
- 不要修改 `.gitlab-ci.yml`(除非 Issue 明确要求)
|
||||
|
||||
## 关键技术
|
||||
- 描述项目使用的框架、库、协议等
|
||||
```
|
||||
|
||||
这个文件非常重要,它相当于给 Claude Code 提供了"项目知识",让 AI 了解项目结构和约束,从而做出更合理的修复。
|
||||
|
||||
---
|
||||
|
||||
## 使用流程
|
||||
|
||||
### 自动模式
|
||||
|
||||
1. 在 GitLab 上创建一个 Issue,描述清楚 Bug 的现象、复现步骤、期望行为
|
||||
2. 给该 Issue 添加标签 **`auto-fix`**
|
||||
3. 等待定时调度运行(默认每 5 分钟扫描一次)
|
||||
4. 标签自动变为 `claude-processing`(正在处理)
|
||||
5. 修复完成后标签变为 `claude-done`,Issue 评论中会出现修复摘要
|
||||
6. 如果有代码变更,commit 会自动推送到 `main` 分支
|
||||
|
||||
### 手动模式(命令行触发)
|
||||
|
||||
项目提供了便捷脚本 `scripts/trigger-fix.sh`:
|
||||
|
||||
```bash
|
||||
# 设置环境变量
|
||||
export TRIGGER_TOKEN="你的 Trigger Token"
|
||||
export PROJECT_ID="项目 ID" # Settings → General 可查看
|
||||
export GITLAB_TOKEN="你的 PAT"
|
||||
export ANTHROPIC_AUTH_TOKEN="你的 API Key"
|
||||
|
||||
# 触发修复
|
||||
./scripts/trigger-fix.sh 42 # 42 是 Issue 的 IID
|
||||
```
|
||||
|
||||
脚本内容很简洁,直接调用 GitLab Pipeline Trigger API:
|
||||
|
||||
```bash
|
||||
curl -X POST \
|
||||
--form "token=${TRIGGER_TOKEN}" \
|
||||
--form "ref=main" \
|
||||
--form "variables[ISSUE_IID]=${ISSUE_IID}" \
|
||||
--form "variables[GITLAB_TOKEN]=${GITLAB_TOKEN}" \
|
||||
--form "variables[ANTHROPIC_AUTH_TOKEN]=${ANTHROPIC_AUTH_TOKEN}" \
|
||||
"https://${GITLAB_HOST}/api/v4/projects/${PROJECT_ID}/trigger/pipeline"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 关键设计细节
|
||||
|
||||
### 1. 为什么用两个 Job + Trigger 而非一个 Job?
|
||||
|
||||
- **隔离性**:扫描和修复在不同 Pipeline 中运行,一个 Issue 修复失败不影响其他 Issue
|
||||
- **并行**:多个 Issue 可以同时被触发修复(各自独立的 Pipeline)
|
||||
- **可追溯**:每个修复有独立的 Pipeline 记录
|
||||
|
||||
### 2. 标签状态机防重复
|
||||
|
||||
```
|
||||
auto-fix → claude-processing → claude-done
|
||||
```
|
||||
|
||||
- 扫描 Query 排除 `claude-processing` 和 `claude-done`
|
||||
- 保证每个 Issue **只被处理一次**
|
||||
|
||||
### 3. `[skip ci]` 避免递归触发
|
||||
|
||||
commit message 中包含 `[skip ci]`,防止修复 commit 触发新的 Pipeline,形成无限循环。
|
||||
|
||||
### 4. 临时文件排除
|
||||
|
||||
`repair_summary.txt` 和 `claude_stderr.txt` 通过 `git reset` 排除暂存区,通过 `.gitignore` 排除版本控制:
|
||||
|
||||
```gitignore
|
||||
repair_summary.txt
|
||||
claude_stderr.txt
|
||||
```
|
||||
|
||||
### 5. Upload Job 与 Fix Job 互不干扰
|
||||
|
||||
Upload Job 的 rules 中明确排除 `schedule` 和 `trigger` 来源:
|
||||
|
||||
```yaml
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
when: never
|
||||
- if: '$CI_PIPELINE_SOURCE == "trigger"'
|
||||
when: never
|
||||
```
|
||||
|
||||
### 6. 完整克隆
|
||||
|
||||
```yaml
|
||||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
GIT_DEPTH: 0
|
||||
```
|
||||
|
||||
Claude Code 需要完整的仓库来分析代码,不能使用浅克隆。
|
||||
|
||||
---
|
||||
|
||||
## 踩坑记录与调试经验
|
||||
|
||||
### 1. Anthropic API 配置
|
||||
|
||||
**问题**:Claude Code CLI 的环境变量名是 `ANTHROPIC_API_KEY`,但 CI 变量传递时用了别的名字。
|
||||
|
||||
**解决**:在 script 中显式映射:
|
||||
|
||||
```bash
|
||||
export ANTHROPIC_API_KEY="${ANTHROPIC_AUTH_TOKEN}"
|
||||
```
|
||||
|
||||
如果使用内部代理,还需设置 `ANTHROPIC_BASE_URL`。额外的 `ANTHROPIC_MODEL` 和 `ANTHROPIC_CUSTOM_MODEL_OPTION` 变量用于指定模型。
|
||||
|
||||
### 2. Claude Code 自动更新
|
||||
|
||||
**问题**:CI 环境中 Claude Code 尝试自动更新,导致超时或失败。
|
||||
|
||||
**解决**:设置环境变量禁用:
|
||||
|
||||
```yaml
|
||||
DISABLE_AUTOUPDATER: "1"
|
||||
```
|
||||
|
||||
### 3. Issue 查询排除已处理的 Issue
|
||||
|
||||
**问题**:早期版本未排除 `claude-processing`/`claude-done`,导致同一 Issue 被反复触发。
|
||||
|
||||
**解决**:使用 GitLab API 的 `not[labels]` 参数:
|
||||
|
||||
```
|
||||
?labels=auto-fix¬%5Blabels%5D=claude-processing,claude-done&state=opened
|
||||
```
|
||||
|
||||
### 4. Git Push 认证
|
||||
|
||||
**问题**:CI 环境默认的 git remote 可能不支持 push。
|
||||
|
||||
**解决**:使用 OAuth2 Token 方式构建 push URL:
|
||||
|
||||
```bash
|
||||
git push "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:main
|
||||
```
|
||||
|
||||
### 5. 中文输出
|
||||
|
||||
**问题**:Claude Code 默认可能以英文回复。
|
||||
|
||||
**解决**:在 Prompt 中明确要求 "请使用中文回复",确保修复摘要和 Issue 评论为中文。
|
||||
|
||||
### 6. 评论中的特殊字符转义
|
||||
|
||||
**问题**:Claude 的输出可能包含 `"`、`\`、换行等字符,直接放入 JSON 会破坏格式。
|
||||
|
||||
**解决**:通过 `sed` 链式转义:
|
||||
|
||||
```bash
|
||||
SUMMARY=$(cat repair_summary.txt | head -200 | \
|
||||
sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 完整 .gitlab-ci.yml 参考
|
||||
|
||||
以下是 `ai_fix` Stage 的完整配置,可直接复制到你的 `.gitlab-ci.yml` 中:
|
||||
|
||||
```yaml
|
||||
stages:
|
||||
# ... 你的其他 stages
|
||||
- ai_fix
|
||||
|
||||
# ============================================================
|
||||
# AI Fix Stage - Auto-fix GitLab Issues with Claude Code
|
||||
# ============================================================
|
||||
# Required CI/CD Variables:
|
||||
# - GITLAB_ACCESS_TOKEN: Personal Access Token (api scope)
|
||||
# - ANTHROPIC_AUTH_TOKEN: Anthropic API Key
|
||||
# - TRIGGER_TOKEN: Pipeline trigger token
|
||||
#
|
||||
# Setup:
|
||||
# 1. Create a Schedule (CI/CD → Schedules), e.g. every 5 min
|
||||
# 2. Add label "auto-fix" to an Issue → auto triggers fix
|
||||
|
||||
claude:scan-issues:
|
||||
stage: ai_fix
|
||||
image: alpine:3.19
|
||||
before_script:
|
||||
- apk add --no-cache curl jq
|
||||
script:
|
||||
- |
|
||||
for var in GITLAB_ACCESS_TOKEN TRIGGER_TOKEN ANTHROPIC_AUTH_TOKEN; do
|
||||
eval "val=\$$var"
|
||||
if [ -z "$val" ]; then echo "ERROR: $var not set."; exit 1; fi
|
||||
done
|
||||
|
||||
ISSUES=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues?labels=auto-fix¬%5Blabels%5D=claude-processing,claude-done&state=opened&per_page=10")
|
||||
|
||||
COUNT=$(echo "$ISSUES" | jq 'length')
|
||||
echo "Found $COUNT issue(s)."
|
||||
[ "$COUNT" -eq 0 ] || [ "$COUNT" = "null" ] && exit 0
|
||||
|
||||
echo "$ISSUES" | jq -r '.[].iid' | while read -r IID; do
|
||||
TITLE=$(echo "$ISSUES" | jq -r ".[] | select(.iid == $IID) | .title")
|
||||
echo ">>> Issue #${IID}: ${TITLE}"
|
||||
|
||||
curl -s --request PUT \
|
||||
--header "PRIVATE-TOKEN: $GITLAB_ACCESS_TOKEN" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"remove_labels\": \"auto-fix\", \"add_labels\": \"claude-processing\"}" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$IID" > /dev/null
|
||||
|
||||
curl -s -X POST \
|
||||
--form "token=${TRIGGER_TOKEN}" \
|
||||
--form "ref=${CI_DEFAULT_BRANCH}" \
|
||||
--form "variables[ISSUE_IID]=${IID}" \
|
||||
--form "variables[GITLAB_TOKEN]=${GITLAB_ACCESS_TOKEN}" \
|
||||
--form "variables[ANTHROPIC_AUTH_TOKEN]=${ANTHROPIC_AUTH_TOKEN}" \
|
||||
"https://${CI_SERVER_HOST}/api/v4/projects/${CI_PROJECT_ID}/trigger/pipeline"
|
||||
done
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "schedule"'
|
||||
- if: '$CI_PIPELINE_SOURCE == "web"'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
timeout: 5m
|
||||
|
||||
claude:fix-issue:
|
||||
stage: ai_fix
|
||||
image: node:20
|
||||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
GIT_DEPTH: 0
|
||||
ANTHROPIC_BASE_URL: "https://api.anthropic.com"
|
||||
ANTHROPIC_MODEL: "claude-sonnet-4-6"
|
||||
DISABLE_AUTOUPDATER: "1"
|
||||
DISABLE_PROMPT_CACHING: "1"
|
||||
before_script:
|
||||
- apt-get update && apt-get install -y git curl jq
|
||||
- npm install -g @anthropic-ai/claude-code
|
||||
- git config --global user.email "claude-bot@example.com"
|
||||
- git config --global user.name "Claude AI Bot"
|
||||
script:
|
||||
- |
|
||||
[ -z "$ISSUE_IID" ] && echo "ERROR: ISSUE_IID required." && exit 1
|
||||
[ -z "$GITLAB_TOKEN" ] && echo "ERROR: GITLAB_TOKEN required." && exit 1
|
||||
|
||||
ISSUE_JSON=$(curl -s --header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID")
|
||||
ISSUE_TITLE=$(echo "$ISSUE_JSON" | jq -r '.title // empty')
|
||||
ISSUE_DESCRIPTION=$(echo "$ISSUE_JSON" | jq -r '.description // empty')
|
||||
[ -z "$ISSUE_TITLE" ] && echo "ERROR: Cannot fetch Issue" && exit 1
|
||||
- |
|
||||
git checkout "$CI_DEFAULT_BRANCH" && git pull origin "$CI_DEFAULT_BRANCH"
|
||||
- |
|
||||
export ANTHROPIC_API_KEY="${ANTHROPIC_AUTH_TOKEN}"
|
||||
|
||||
PROMPT="你是一个代码修复助手。请使用中文回复。请分析并修复以下 GitLab Issue:
|
||||
|
||||
## Issue #${ISSUE_IID}: ${ISSUE_TITLE}
|
||||
|
||||
${ISSUE_DESCRIPTION}
|
||||
|
||||
请:
|
||||
1. 分析项目代码,定位问题根因
|
||||
2. 实施修复
|
||||
3. 确保代码能通过类型检查(TypeScript 项目运行 npx tsc --noEmit)
|
||||
4. 用中文输出修复摘要
|
||||
|
||||
项目根目录已准备就绪。"
|
||||
|
||||
RESULT=$(claude -p "$PROMPT" \
|
||||
--bare \
|
||||
--permission-mode acceptEdits \
|
||||
--allowedTools "Bash,Read,Edit,Write,Glob,Grep" \
|
||||
--max-turns 30 \
|
||||
--output-format text \
|
||||
2>&1) || true
|
||||
|
||||
echo "$RESULT" > repair_summary.txt
|
||||
- |
|
||||
git add -A
|
||||
git reset -- repair_summary.txt claude_stderr.txt 2>/dev/null || true
|
||||
if git diff --cached --quiet; then
|
||||
COMMIT_STATUS="no_changes"
|
||||
else
|
||||
git commit -m "fix(auto): resolve Issue #${ISSUE_IID} - ${ISSUE_TITLE} [skip ci]"
|
||||
git push "https://oauth2:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" \
|
||||
HEAD:"$CI_DEFAULT_BRANCH"
|
||||
COMMIT_STATUS="pushed"
|
||||
fi
|
||||
- |
|
||||
SUMMARY=$(cat repair_summary.txt | head -200 | \
|
||||
sed 's/\\/\\\\/g' | sed 's/"/\\"/g' | sed ':a;N;$!ba;s/\n/\\n/g')
|
||||
|
||||
if [ "$COMMIT_STATUS" = "pushed" ]; then
|
||||
BODY="## ✅ Claude Code 自动修复完成\n\n修复已推送到 \`${CI_DEFAULT_BRANCH}\`。\nPipeline: ${CI_PIPELINE_URL}\n\n<details><summary>修复详情</summary>\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n</details>"
|
||||
else
|
||||
BODY="## ⚠️ Claude Code 分析完成\n\n未检测到需要变更的文件。\nPipeline: ${CI_PIPELINE_URL}\n\n<details><summary>分析详情</summary>\n\n\`\`\`\n${SUMMARY}\n\`\`\`\n</details>"
|
||||
fi
|
||||
|
||||
curl -s --request POST \
|
||||
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"body\": \"${BODY}\"}" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID/notes"
|
||||
- |
|
||||
curl -s --request PUT \
|
||||
--header "PRIVATE-TOKEN: $GITLAB_TOKEN" \
|
||||
--header "Content-Type: application/json" \
|
||||
--data "{\"remove_labels\": \"claude-processing\", \"add_labels\": \"claude-done\"}" \
|
||||
"$CI_API_V4_URL/projects/$CI_PROJECT_ID/issues/$ISSUE_IID" > /dev/null
|
||||
rules:
|
||||
- if: '$CI_PIPELINE_SOURCE == "trigger" && $ISSUE_IID'
|
||||
- if: '$CI_PIPELINE_SOURCE == "web" && $ISSUE_IID'
|
||||
when: manual
|
||||
allow_failure: true
|
||||
timeout: 30m
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 总结
|
||||
|
||||
| 特性 | 实现方式 |
|
||||
|------|----------|
|
||||
| 触发机制 | GitLab Schedule 定时扫描 + Pipeline Trigger 触发修复 |
|
||||
| AI 引擎 | Claude Code CLI (`@anthropic-ai/claude-code`) |
|
||||
| 权限控制 | `--permission-mode acceptEdits` + `--allowedTools` 白名单 |
|
||||
| 防重复 | 标签状态机 `auto-fix → claude-processing → claude-done` |
|
||||
| 防递归 | commit message 中 `[skip ci]` |
|
||||
| 结果反馈 | 自动在 Issue 上发表评论,含修复摘要 |
|
||||
| 项目上下文 | `CLAUDE.md` 项目指南文件 |
|
||||
|
||||
这套方案已在实际项目中运行,成功自动修复了多个 Bug Issue。核心优势是**零人工介入**——开发者只需写好 Issue 描述并打上标签,AI 自动完成从分析到修复到提交的全流程。
|
||||
@@ -4,21 +4,17 @@
|
||||
# MCP
|
||||
- WY MCP市场:https://modelspace.netease.com/mcphub
|
||||
- DeepWiki MCP
|
||||
- POPO MCP
|
||||
- 易协作 MCP Server
|
||||
- OpenClaw:https://clawhub.ai/
|
||||
- ~~POPO MCP~~
|
||||
- ~~易协作 MCP Server~~
|
||||
- 开发类
|
||||
- Chrome DevTools MCP。
|
||||
- [ ] **Chrome DevTools MCP**。
|
||||
- BrowerMCP:Chrome浏览器专用前端视觉MCP。需要在Chrome安装对应插件。
|
||||
|
||||
- 搜索类
|
||||
- **DuckDuckGo Search**:无需 API Key 的隐私保护搜索,支持网页和新闻检索。`claude mcp add duckduckgo-search -- npx -y duckduckgo-mcp-server`
|
||||
- Sacred Scriptures:宗教典籍(如大藏经)精准检索。 `claude mcp add sacred-scriptures -- npx -y @traves-theberge/sacred-scriptures-mcp`
|
||||
- **Open Library**:全球书籍元数据与原著检索。 `claude mcp add open-library -- npx -y @8ensmith/mcp-open-library`
|
||||
- **Paper Search**:现代哲学与认知科学论文检索。 `claude mcp add paper-search -- npx -y @openags/paper-search-mcp`
|
||||
- OpenEnded Philosophy:非公理化逻辑推演与哲学思辨增强。 `claude mcp add openended-philosophy -- npx -y @openended/philosophy-mcp`
|
||||
- 存储类
|
||||
- **Mem0 (OpenMemory)**:用户观点长期记忆与思想进化追踪。 `claude mcp add mem0 -- npx -y @mem0/mcp-server`
|
||||
- 笔记类
|
||||
- Obsidian MCP:本地哲学笔记库同步与知识图谱构建。 `claude mcp add obsidian -- npx -y @obsidian-mcp/server`
|
||||
## MCP案例代码
|
||||
@@ -371,13 +367,13 @@ app.use("/mcp", async (c, next) => {
|
||||
3. https://skills.sh/
|
||||
1. https://skills.sh/hairyf/skills/create-skill-from-repo
|
||||
|
||||
- MCP
|
||||
- [x] **Chrome DevTools MCP**。
|
||||
- 基础Skill
|
||||
- [x] ***find-skills***:在20万+Skills里自动找到所需工具。 #导航员
|
||||
- [ ] skill-creator:把你的工作流打包成可复用能力。 #工厂
|
||||
- [x] skill-creator:把你的工作流打包成可复用能力。 #工厂
|
||||
- [ ] mcp-builder:搭建服务器,连接私人数据和外部工具。 #桥梁
|
||||
- [x] using-superpowers:强制Agent真正发挥高级能力。 #优化器
|
||||
- [ ] subagent-driven-dev:把子任务委派给其他AI并审核。 #管理者
|
||||
- [ ] agent-tools:数字瑞士军刀,处理日常事务。 #工具箱
|
||||
- [ ] ~~agent-tools~~:数字瑞士军刀,处理日常事务。 #工具箱
|
||||
- 搜索相关
|
||||
- [x] GitHub 官方 MCP:claude mcp add github -- npx -y @modelcontextprotocol/server-github
|
||||
- [x] 搜索引擎类:输出的结果质量以及Token消耗量都不理想。
|
||||
@@ -389,11 +385,16 @@ app.use("/mcp", async (c, next) => {
|
||||
- https://skills.sh/tavily-ai/skills/research: `npx skills add https://github.com/tavily-ai/skills --skill research`
|
||||
- https://skills.sh/tavily-ai/skills/extract: `npx skills add https://github.com/tavily-ai/skills --skill extract`
|
||||
- https://skills.sh/veithly/tavily-search/tavily-search: `npx skills add https://github.com/veithly/tavily-search --skill tavily-search`
|
||||
- 编程类
|
||||
- [x] ***using-superpowers***:强制Agent真正发挥高级能力。 #优化器
|
||||
- [x] acpx:外部工具桥接器,可以用来控制ClaudeCode等编程工具,还需要额外配置。 #编程
|
||||
- 沟通类
|
||||
- [x] [openclaw-a2a-gateway](https://github.com/win4r/openclaw-a2a-gateway):多物理实例Agent沟通工具,**缺点:无有上一次对话的上下文**。
|
||||
- 进阶工具
|
||||
- [ ] brainstorming:一个关键词生成几十个独特角度和多种假设场景。 #创意生成器
|
||||
- [ ] brainstorming:一个关键词生成几十个独特角度和多种假设场景。 #0创意生成器
|
||||
- [ ] copywriting:优化表达、打磨语调,不依赖老掉牙的一键生成模板。 #文字工匠
|
||||
- [ ] reflection:增加自我纠正循环,让Agent审核并在过程中修复错误。 #秘密武器
|
||||
- [ ] writing-plans:先构建结构大纲,确保长文不跑偏。 #架构师
|
||||
- [ ] ~~writing-plans~~:先构建结构大纲,确保长文不跑偏。 #架构师
|
||||
- [ ] social-content:为X、TikTok、小红书专属优化,止住下滑的手指。 #运营经理
|
||||
- [ ] marketing-ideas:生成病毒式噱头和活动概念,远超简单广告水平。 #创意总监
|
||||
- [ ] copy-editing:打磨语调、收紧流程,保留你独特的人性声音。 #高级编辑
|
||||
|
||||
51
07-Other/AI/AI Agent/NeoX/MatrixAITA-XEditor-Messiah.md
Normal file
51
07-Other/AI/AI Agent/NeoX/MatrixAITA-XEditor-Messiah.md
Normal file
@@ -0,0 +1,51 @@
|
||||
---
|
||||
title: 未命名
|
||||
date: 2026-04-02 17:18:06
|
||||
excerpt:
|
||||
tags:
|
||||
rating: ⭐
|
||||
status: inprogress
|
||||
destination:
|
||||
share: false
|
||||
obsidianUIMode: source
|
||||
---
|
||||
|
||||
NeoX与UnrealEngine(简称UE)是2个游戏引擎,XEditor是NeoX引擎的编辑器,我希望能编写一个Skill让Agent同时操作这2个引擎进行一些资产优化工作。
|
||||
|
||||
## 前置检查工作。
|
||||
1. 用户输入NeoX目录与UnrealEngine目录。
|
||||
2. Agent补充绝对目录并验证Mcp可用后提示用户Mcp已经可以工作了。
|
||||
|
||||
## 项目情况
|
||||
1. NeoX目录美术表路径:./Doc/X_导入数据/超炫酷3D鹈鹕镇
|
||||
1. 场景表.xlsx
|
||||
2. 模型表.xlsx
|
||||
1. 场景模型
|
||||
2. 角色模型:无需关注
|
||||
3. 3D面片:无需关注
|
||||
4. 模型ID与分类:
|
||||
3. 动作表.xlsx
|
||||
4. 模型动画表.xlsx
|
||||
5. 特效表.xlsx
|
||||
2. NeoX目录策划表:./Doc/X_导入数据/精灵们今天非常开心!
|
||||
1. 道具表.xlsx:每个子表中的模型标号对应模型表中模型的ID。
|
||||
3. NeoX资产路径:
|
||||
1. UE资产导入后的:res\ue_export_common
|
||||
|
||||
# 资产优化操作步骤说明
|
||||
1. 控制XEditor Mcp
|
||||
1. 检索模型表、场景表。
|
||||
1. 模型表 查询 场景模型子表内的所有的模型。
|
||||
2. 场景表 遍历所有表中场景所使用的模型。
|
||||
2. 分别对这2个表引用的模型资产进行检查。
|
||||
1. 模型信息:模型面数、模型LOD 数量 级别 ScreenSize 或 CameraDistance 信息、模型数量。
|
||||
2. 模型所用材质的贴图信息:贴图分辨率。
|
||||
3. 检查逻辑可以参考: D:\Project\H78_New\.claude\skills\item-resource-analyzer\SKILL.md
|
||||
3. 在NeoX目录的./Outputs 先输入2个报告文件 日期戳-场景报告.xlsx、日期戳-模型表报告.xlsx,模型项需要去重。
|
||||
1. 模型表报告需要根据道具表中对模型分类,将信息分到对应子表中(子表与道具表中的子表分类同名)
|
||||
2. 控制UE Mcp 在UE中寻找对应的模型资产。
|
||||
1. XEditor路径:ue_export_common/ProjectCoral/Art/Debris/Ores/SM_CopperOreVein.gim;UE路径:/Game/ProjectCoral/Art/Debris/Ores/SM_CopperOreVein.gim。
|
||||
2. 提示用户哪些模型XEditor里有,但UE没有,需要用户手动导入到UE,再进行处理。
|
||||
3. 新建一个带有基础光照的关卡,将这些模型都放到关卡中。
|
||||
1. 模型按照BoundingBox大小进行排序。
|
||||
2. 模型按照BoundingBox调整间隔,BoundingBox为1m的,间距1M,BoundingBox为10M的,间距10M。
|
||||
23
07-Other/AI/AI Agent/NeoX/资产分析提示词.md
Normal file
23
07-Other/AI/AI Agent/NeoX/资产分析提示词.md
Normal file
@@ -0,0 +1,23 @@
|
||||
|
||||
# 0
|
||||
整理当前项目组所有贴图信息,信息包括贴图分辨率,体积,相对路径,贴图名字,按照体积从大到小,整理成一个excel 你直接在NXAI对话框里输入这个就能得到有信息的excel了。
|
||||
|
||||
|
||||
# 1
|
||||
请分析./scene/下所有场景文件,对每个场景分别进行分析,分析所引用的模型资产以及模型材质所引用的贴图,主要有模型面数、材质数目、所用材质引用贴图的分辨率。
|
||||
|
||||
要求:
|
||||
1. 按照模型面数进行主排序。次级排序使用材质数目。
|
||||
2. 贴图分辨率超过1024 将该单元格标注为黄色,超过2048标注为红色。
|
||||
|
||||
分别出一个excel格式的分析文件。
|
||||
|
||||
|
||||
# 2
|
||||
请帮我revert掉svn中./res/目录下所有针对贴图与材质的修改,只保留LOD相关的修改(带有_lod、_lod1、_lod2、_lod3后缀的gim以及这个gim的同名json与mesh文件)。
|
||||
|
||||
|
||||
./Doc\X_导入数据\超炫酷3D鹈鹕镇\ 模型表.xlsx 以及 场景表.xlsx中所有场景引用的模型,
|
||||
|
||||
将LOD级别数目大于等于4的,将最后一级设置为隐藏。
|
||||
LOD等于3的,添加一个级别设置为隐藏。
|
||||
@@ -26,117 +26,164 @@
|
||||
# OpenClaw架构构思
|
||||
https://clawhub.ai/ Skill推荐:
|
||||
- Common
|
||||
- [x] ***find-skills***:在20万+Skills里自动找到所需工具。
|
||||
- [x] ***find-skills***:在20万+Skills里自动找到所需工具。 #导航员
|
||||
- [x] skill-creator:把你的工作流打包成可复用能力。 #工厂
|
||||
- [x] ***self-improving-agent***: 记录经验教训、错误和纠正措施,以实现持续改进。
|
||||
- [x] Summarize:使用 summarize CLI 对 URL 或文件进行汇总(网页、PDF、图像、音频、YouTube)。
|
||||
- 文档类
|
||||
- [x] pdf:合并、拆分、提取复杂PDF,无手动头疼。 #文档专家
|
||||
- [x] pptx:生成完整专业幻灯片,再也不用移动文本框。 #演示专家
|
||||
- [x] docx:创建结构化Word文档,直接用于官方用途。 #文书
|
||||
- [x] xlsx:带复杂公式和图表的Excel文件。 #数据分析师
|
||||
- 搜索相关
|
||||
- [x] GitHub 官方 MCP:claude mcp add github -- npx -y @modelcontextprotocol/server-github
|
||||
- [x] 搜索引擎类:输出的结果质量以及Token消耗量都不理想。
|
||||
- jina-reader:
|
||||
- https://clawhub.ai/ericsantos/jina-reader
|
||||
- https://skills.sh/sundial-org/awesome-openclaw-skills/jina-reader
|
||||
- Tavily
|
||||
- https://skills.sh/tavily-ai/skills/search: `npx skills add https://github.com/tavily-ai/skills --skill search`
|
||||
- https://skills.sh/tavily-ai/skills/research: `npx skills add https://github.com/tavily-ai/skills --skill research`
|
||||
- https://skills.sh/tavily-ai/skills/extract: `npx skills add https://github.com/tavily-ai/skills --skill extract`
|
||||
- https://skills.sh/veithly/tavily-search/tavily-search: `npx skills add https://github.com/veithly/tavily-search --skill tavily-search`
|
||||
- 编程类
|
||||
- [x] ***using-superpowers***:强制Agent真正发挥高级能力。 #优化器
|
||||
- [x] **acpx**:外部工具桥接器,可以用来控制ClaudeCode等编程工具,还需要额外配置。 #编程
|
||||
- 优化类
|
||||
- [ ] [openclaw-workspace](https://github.com/win4r/openclaw-workspace):优化OpenClaw工作区。提示词:帮我用 openclaw-workspace 这个 skill 做一次 workspace审计/精简 AGENTS.md/ 优化 TOOLS.md"等。可以创建定时任务让他每周执行。
|
||||
- 控制中心(都处于质量存疑阶段)
|
||||
- [ ] https://github.com/TianyiDataScience/openclaw-control-center
|
||||
- [ ] https://github.com/abhi1693/openclaw-mission-control
|
||||
## Agent & Memory
|
||||
在默认工作区可以到以下文件,这些都是Agent提示词的主程部分:
|
||||
|
||||
| **文件名** | **角色定位** | **详细作用描述** |
|
||||
| ---------------- | ------------- | --------------------------------------------------------- |
|
||||
| **Bootstrap.md** | **引导程序/总纲** | 整个系统的入口。它负责协调其他模块,定义系统初始化的逻辑,并告诉 LLM 如何“读取”这一系列文件。 |
|
||||
| **SYSTEM.md** | **物理规则/底层协议** | 定义 AI 的基本运行准则。包含技术约束、输出格式要求(如必须使用 JSON)、安全边界和思考框架。 |
|
||||
| **IDENTITY.md** | **身份设定** | 定义“我是谁”。包含姓名、职业背景、专业领域和语气风格(Tone of Voice)。这是 AI 表层人格的来源。 |
|
||||
| **SOUL.md** | **核心价值观/灵魂** | 定义“我的底层逻辑”。包含道德准则、动机、偏好、情绪反应模型以及对待冲突的态度。它比 Identity 更深层。 |
|
||||
| **USER.md** | **用户画像/上下文** | 记录“你是谁”。包含用户偏好、当前任务背景、历史互动关键点。确保 AI 的响应具有针对性。 |
|
||||
| **TOOLS.md** | **能力清单/技能树** | 定义 AI 可以调用的外部函数或工具(如搜索、绘图、计算)。明确调用参数和返回格式。 |
|
||||
| **AGENTS.md** | **协作协议** | 定义多智能体协作逻辑。如果 AI 需要召唤“副手”或与其他代理交互,这里规定了沟通协议。 |
|
||||
| 文件 | 用途 | 加载时机 | 子 Agent 可见? |
|
||||
| ---------------------- | ------------------------------------- | ----------------------- | ----------- |
|
||||
| `AGENTS.md` | 启动序列、操作清单、行为规则 | 每次轮次(所有 Agent) | 是 |
|
||||
| `SOUL.md` | 人格、语气、价值观、连续性哲学 | 每次轮次(所有 Agent) | 是 |
|
||||
| `TOOLS.md` | 环境特定信息(SSH、TTS、摄像头、设备) | 每次轮次(主 Agent + 子 Agent) | 是 |
|
||||
| `USER.md` | 用户画像、偏好、关系背景 | 每次轮次(仅主会话) | 否 |
|
||||
| `IDENTITY.md` | 名称、Emoji、头像、自我描述 | 每次轮次 | 是 |
|
||||
| `HEARTBEAT.md` | 周期性检查任务和健康例程 | 每次心跳轮次 | 视情况而定 |
|
||||
| `BOOT.md` | 启动时执行的操作(需要 `hooks.internal.enabled`) | 网关启动时 | 否 |
|
||||
| `BOOTSTRAP.md` | 首次初始化脚本——用完即删 | 仅新工作区 | 否 |
|
||||
| `MEMORY.md` | 长期精华事实与铁律规则 | 仅主会话 | **永不** |
|
||||
| `memory/YYYY-MM-DD.md` | 每日会话日志 | 按 AGENTS.md 启动序列加载 | 否 |
|
||||
| `checklists/*.md` | 高风险操作的逐步指南 | 按需加载(从 AGENTS.md 引用) | 否 |
|
||||
### 各文件详解
|
||||
#### AGENTS.md — 操作手册
|
||||
这是每次会话中最先塑造 Agent 行为的文件(基础系统提示词之后)。它包含:
|
||||
- **启动序列**:有序列出需要在会话开始时读取的文件(SOUL → USER → MEMORY → 日志)
|
||||
- **清单路由表**:高风险操作 → 对应清单文件路径的映射
|
||||
- **安全规则**:哪些操作需要确认、哪些可以自主执行
|
||||
- **群聊规则**:在群聊中不应分享什么
|
||||
|
||||
**注意**:AGENTS.md 是规程文件,不是身份文件。人格和价值观属于 SOUL.md。
|
||||
|
||||
#### SOUL.md — 灵魂
|
||||
用第二人称书写("你不是一个聊天机器人,你正在成为某个人"),Agent 读取后将其内化为自我描述。
|
||||
包含:核心价值观、边界与底线、语气风格、关于连续性的哲学(每次会话都是全新开始,工作区文件就是记忆)。
|
||||
|
||||
#### TOOLS.md — 本地环境备忘录
|
||||
这是工作区中最容易被误用的文件。它应该是**当前机器的环境专属速查表**:SSH 主机、TTS 声音 ID、摄像头设备名称等。
|
||||
子 Agent 也会收到此文件——这是它们唯一的环境知识来源。请保持简洁,50 行以内为佳。
|
||||
|
||||
#### USER.md — 用户画像
|
||||
包含影响每次对话的人物相关事实:姓名、时区、语言偏好、沟通风格。
|
||||
仅在主会话中加载——绝不在群聊或子 Agent 会话中加载。
|
||||
|
||||
#### MEMORY.md — 铁律规则
|
||||
只保存"遗忘了就会出严重问题"的规则。每条规则要短且原子化,具有明确的行动指导意义。
|
||||
定期精炼(每月一次)。已经几个月没有出过问题的规则,可以考虑迁移到技能的 `SKILL.md` 文件中(更合适的归宿)。
|
||||
|
||||
#### checklists / * .md — 操作清单
|
||||
高风险操作(部署、网关重启、配置变更)的逐步指南。Agent 执行操作前主动读取对应清单。
|
||||
**正确模式** :AGENTS.md 只保留一行路由表条目,完整清单放在 `checklists/` 目录中(按需加载,不占用每轮 Token 预算)。
|
||||
---
|
||||
|
||||
## 2. 调用逻辑与顺序
|
||||
|
||||
|
||||
|
||||
**逻辑顺序如下:**
|
||||
1. **加载 `Bootstrap.md`**:确定系统引导协议,LLM 开始意识到自己是一个遵循 OpenClaw 协议的 Agent。
|
||||
2. **注入 `SYSTEM.md`**:建立底层规则。在产生任何性格之前,必须先确定“不能做什么”和“必须怎么思考”。
|
||||
3. **构建 `IDENTITY.md` 与 `SOUL.md`**:赋予生命力。LLM 此时从“纯粹的工具”转变为“具备特定性格的专家”。
|
||||
4. **读取 `USER.md`**:注入外部变量。AI 了解了交互对象,调整自己的姿态。
|
||||
5. **激活 `TOOLS.md` 与 `AGENTS.md`**:最后加载执行层。当 AI 明确了身份和规则后,再根据需要调用具体的手段。
|
||||
|
||||
|
||||
|
||||
1. Bootstrap 文件注入(新会话首个 turn)
|
||||
### 2. 调用逻辑与顺序
|
||||
按优先级读取顺序:
|
||||
AGENTS.md → SOUL.md → TOOLS.md → IDENTITY.md → USER.md
|
||||
|
||||
|
||||
|
||||
|
||||
2️⃣ 记忆文件读取
|
||||
• MEMORY.md — 长期记忆(仅私聊会话加载,群聊不加载)
|
||||
• memory/今天.md + memory/昨天.md — 日志(会话开始时读取)
|
||||
|
||||
3️⃣ Skill 加载(SKILL.md 注入)
|
||||
#### Skill 加载(SKILL.md 注入)
|
||||
三个来源,优先级从高到低:
|
||||
① < workspace >/skills(agent 独有)
|
||||
② ~/.openclaw/skills(全局共享)
|
||||
③ bundled skills(内置)
|
||||
同名 skill 高优先级覆盖低优先级
|
||||
|
||||
|
||||
|
||||
4️⃣ 会话历史
|
||||
已有的对话上下文(JSONL 存储)
|
||||
|
||||
|
||||
|
||||
5️⃣ 语义搜索(按需)
|
||||
agent 调用 memory_search 时,向量检索 MEMORY.md + memory/* .md
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
💡 建议:
|
||||
|
||||
#### 建议:
|
||||
如果你想把记忆用法精炼,推荐这样分层:
|
||||
|
||||
|
||||
|
||||
1. 通用行为规范 → AGENTS.md / SOUL.md(所有会话都加载)
|
||||
|
||||
2. 工具使用指南 → Skill 的 SKILL.md(按需触发加载)
|
||||
|
||||
3. 持久事实/偏好 → MEMORY.md(长期记忆)
|
||||
|
||||
4. 需要代码逻辑的 → Plugin(如自动 Token 管理、MCP 代理)
|
||||
|
||||
|
||||
|
||||
以 Redmine 为例的最佳实践:
|
||||
|
||||
• Skill:SKILL.md 写"怎么调用、什么时候触发、参数格式"
|
||||
|
||||
• Plugin:如果要做自动 Token 刷新、webhook 监听等,写成 Plugin
|
||||
|
||||
• MEMORY.md:只存"loujiajie 有权限的项目是 H78"这种事实
|
||||
|
||||
|
||||
|
||||
不建议把所有东西都塞进 MEMORY.md,
|
||||
### EmbeddingModel
|
||||
|
||||
|
||||
### 记忆移植方法
|
||||
|
||||
### 将记忆移植到Skill
|
||||
【Skill】= 教 agent "怎么做某件事"
|
||||
|
||||
• 本质是 SKILL.md(Markdown 指令)+ 可选脚本
|
||||
|
||||
• 注入到 agent 的上下文中,agent 按指令执行
|
||||
|
||||
• 适合:工具使用说明、操作流程、触发规则
|
||||
|
||||
• 例:RedmineSkill 告诉 agent 如何调用 MCP 查工单
|
||||
## Muti-Agent
|
||||
按照日常功能分出几个额外Agent,并让他们使用不同的Workspace。
|
||||
|
||||
## Subagent铁律案例
|
||||
|
||||
把这段放入 AGENTS.md 中
|
||||
### Sub-Agent 编排规则
|
||||
|
||||
#### 模型选择策略
|
||||
根据任务复杂度自动选择模型,优化成本与质量的平衡:
|
||||
|
||||
| 级别 | 适用场景 | 模型 | Thinking |
|
||||
|------|---------|------|----------|
|
||||
| 简单 | 天气、日历、状态检查、单项数据获取 | minimax-portal/MiniMax-M2.1 | off |
|
||||
| 中等 | 搜索总结、文档摘要、内容起草、多步信息整理 | openai-codex/gpt-5.2 | low |
|
||||
| 复杂 | 代码审查、架构分析、安全审计、多维度对比决策 | openai-codex/gpt-5.2 | high |
|
||||
原则:
|
||||
- 默认从最便宜的模型开始,只在任务明确需要更强推理时升级
|
||||
- 不确定时选中等
|
||||
|
||||
#### 常用工作流
|
||||
**每日简报** — 当用户说"每日简报"或在早晨心跳时:
|
||||
1. 并行 spawn 4 个 Sub-Agent(级别:简单):
|
||||
- 天气:上海未来 24 小时
|
||||
- 日历:今天的会议和待办
|
||||
- 邮件:未读紧急邮件摘要
|
||||
- 新闻:AI / Agent 领域最新动态(最多 5 条)
|
||||
2. 全部完成后汇总成结构化简报
|
||||
3. 通过当前频道发送
|
||||
|
||||
**技术调研** — 当用户要求调研多个主题时:
|
||||
1. 每个主题 spawn 一个 Sub-Agent(级别:中等)
|
||||
2. 每个 Sub-Agent 搜索 3-5 篇最新文章,总结关键观点,300 字以内
|
||||
3. 全部完成后汇总对比
|
||||
|
||||
**代码审查** — 当用户说"审查代码"或"review"时:
|
||||
1. spawn 一个 Sub-Agent(级别:复杂),超时 5 分钟
|
||||
2. 检查项:安全漏洞、类型安全、错误处理、架构合理性
|
||||
3. 返回:问题列表 + 严重度 + 修复建议
|
||||
|
||||
**批量文档处理** — 当用户需要处理多个文档时:
|
||||
1. 每个文档 spawn 一个 Sub-Agent(级别:根据文档复杂度判断)
|
||||
2. 提取关键信息,返回结构化 JSON
|
||||
3. 全部完成后汇总对比
|
||||
#### 通用约束
|
||||
|
||||
- 并行 Sub-Agent 不超过 5 个,避免速率限制
|
||||
- 每个 Sub-Agent 的 task prompt 必须自包含所有必要上下文(Sub-Agent 看不到 SOUL.md 和 USER.md)
|
||||
- 简单任务超时 60 秒,中等任务超时 180 秒,复杂任务超时 600 秒
|
||||
- 完成后默认 cleanup: "delete",除非用户要求保留日志
|
||||
|
||||
# ~~Docker 部署~~(推荐虚拟机部署)
|
||||
1. https://docs.openclaw.ai/install/docker
|
||||
2. git clone https://github.com/openclaw/openclaw.git
|
||||
@@ -350,14 +397,6 @@ https://cloud.tencent.com/developer/article/2626310
|
||||
- https://km.netease.com/v4/detail/blog/258877
|
||||
- 插件文档:https://docs.popo.netease.com/lingxi/173a627a791b4372aa50318bfdfb5204
|
||||
|
||||
需要安装插件之后,重启,再填写机器人事件订阅的token 秘钥相关信息。
|
||||
|
||||
[https://open-dev.popo.netease.com/mp/detail/184955194/devManage](https://open-dev.popo.netease.com/robot/detail/363265944/devManage)
|
||||
http://10.219.32.29:6666/popo/callback
|
||||
KyePDRbMN3j1bHEhxktBMbcJtehpxmYW
|
||||
|
||||
https://km.netease.com/v4/detail/blog/258877
|
||||
|
||||
## 企业微信
|
||||
- https://github.com/pawastation/wechat-kf
|
||||
- Token信息
|
||||
|
||||
146
07-Other/AI/AI Agent/OpenClaw/OpenClaw ACPX配置.md
Normal file
146
07-Other/AI/AI Agent/OpenClaw/OpenClaw ACPX配置.md
Normal file
@@ -0,0 +1,146 @@
|
||||
# 配置参数
|
||||
## ACPX核心配置
|
||||
```json
|
||||
{
|
||||
"acp": {
|
||||
"enabled": true,
|
||||
"dispatch": { "enabled": true },
|
||||
"backend": "acpx",
|
||||
"defaultAgent": "claude",
|
||||
"allowedAgents": ["claude", "codex", "pi", "opencode", "gemini"],
|
||||
"maxConcurrentSessions": 8
|
||||
}
|
||||
}
|
||||
```
|
||||
## 权限配置:
|
||||
```bash
|
||||
openclaw config set plugins.entries.acpx.config.permissionMode approve-all
|
||||
openclaw config set plugins.entries.acpx.config.nonInteractivePermissions fail
|
||||
|
||||
```
|
||||
|
||||
## 重启并验证
|
||||
```bash
|
||||
openclaw restart
|
||||
/acp doctor
|
||||
```
|
||||
|
||||
# 启动命令
|
||||
```bash
|
||||
/acp spawn claude --mode oneshot --thread auto --cwd /Users/yuyue07/Desktop/openclaw
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
这是一份专为 AI 智能体(如 OpenClaw)设计的**技术部署协议文档**。你可以将其直接发送给 OpenClaw A,它将理解如何通过局域网协议接管并在 Windows 环境下的 OpenClaw B 中执行任务。
|
||||
|
||||
---
|
||||
|
||||
# 🤖 嵌套智能体架构部署协议:LAN-ACP 控制流
|
||||
|
||||
## 1. 架构概述 (Architecture Overview)
|
||||
|
||||
本方案采用**“指挥官—操作员”**模式,通过局域网实现远程代码编排与自动化执行。
|
||||
|
||||
- **OpenClaw A (Orchestrator):** 运行于高性能控制端(Mac Studio),负责任务拆解与逻辑审计。
|
||||
|
||||
- **OpenClaw B (Operator):** 运行于 Windows 执行端,通过 ACP 协议直接驱动 Claude Code。
|
||||
|
||||
- **通信协议:** 基于 JSON-RPC 的 **Agent Client Protocol (ACP)** 与 OpenClaw P2P 对等连接。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 2. 受控端环境预设 (OpenClaw B - Windows)
|
||||
|
||||
### 2.1 基础依赖
|
||||
|
||||
- **Shell:** Windows PowerShell 7+ 或系统自带 PowerShell。
|
||||
|
||||
- **工具链:** Node.js (LTS), Claude Code CLI (`@anthropic-ai/claude-code`)。
|
||||
|
||||
- **权限:** 已开启 OpenSSH Server,并配置 `ANTHROPIC_API_KEY` 为系统环境变量。
|
||||
|
||||
|
||||
### 2.2 OpenClaw Gateway 配置
|
||||
|
||||
在 `%USERPROFILE%\.openclaw\config.json` 中配置以下核心参数:
|
||||
|
||||
JSON
|
||||
|
||||
```
|
||||
{
|
||||
"node_id": "executor-win-01",
|
||||
"networking": {
|
||||
"host": "0.0.0.0",
|
||||
"port": 8080,
|
||||
"allow_lan": true
|
||||
},
|
||||
"runtime": {
|
||||
"shell": "powershell.exe",
|
||||
"encoding": "utf-8"
|
||||
},
|
||||
"acp": {
|
||||
"enabled": true,
|
||||
"provider": "claude-code",
|
||||
"auto_approve_tools": ["list_files","read_file","grep_search","file_edit","bash","ls","cat"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
## 3. 控制端对接协议 (OpenClaw A - macOS)
|
||||
|
||||
### 3.1 身份注册 (Identity Mapping)
|
||||
|
||||
OpenClaw A 需通过以下逻辑识别并绑定远程 Peer:
|
||||
|
||||
1. **连接指令:** `peer connect --target <B_IP_ADDRESS>:8080 --token <SECURE_TOKEN>`
|
||||
|
||||
2. **角色定义:** 将 `executor-win-01` 标记为 `Technical_Executor`。
|
||||
|
||||
|
||||
### 3.2 指令封装 (Instruction Wrapping)
|
||||
|
||||
当 A 需要执行代码任务时,必须将任务封装为 ACP 标准请求发送至 B。
|
||||
|
||||
> **逻辑模版:** `Invoke-RemoteAgent -Target "executor-win-01" -Action "ACP_SPAWN" -Harness "claude" -Prompt "<Task_Description>"`
|
||||
|
||||
---
|
||||
|
||||
## 4. 自动化执行工作流 (Standard Operating Procedure)
|
||||
|
||||
1. **意图解析:** OpenClaw A 接收用户需求(如:修复 Vue 3 组件 Bug)。
|
||||
|
||||
2. **任务路由:** A 检查局域网内 B 的在线状态。
|
||||
|
||||
3. **ACP 激活:** A 向 B 发送 `/acp spawn claude` 指令。
|
||||
|
||||
4. **实时会话:** * B 启动 Claude Code 进程。
|
||||
|
||||
- Claude Code 通过 B 的文件系统进行 `ls`, `cat`, `sed` 等操作。
|
||||
|
||||
- B 将执行过程的增量日志(Incremental Logs)实时推送到 A 的控制台。
|
||||
|
||||
5. **结果审计:** 任务完成后,A 要求 B 运行本地测试(如 `npm run test`),并根据输出判定是否交付。
|
||||
|
||||
|
||||
---
|
||||
|
||||
## 5. 关键安全与优化指令 (Security & Optimization)
|
||||
|
||||
- **路径规范:** 在 Windows 环境下,所有指令中的路径需使用双斜杠 `\\` 或标准 POSIX 斜杠 `/`,避免转义错误。
|
||||
|
||||
- **状态同步:** 强制要求 B 在每一步关键操作后返回状态码。
|
||||
|
||||
- **超时处理:** ACP 会话默认心跳间隔设为 30s,防止长耗时编译任务导致连接断开。
|
||||
|
||||
- **防火墙策略:** * `New-NetFirewallRule -DisplayName "OpenClaw-B" -LocalPort 8080 -Protocol TCP -Action Allow`
|
||||
|
||||
|
||||
---
|
||||
|
||||
> **给 OpenClaw 的执行提示:** “请按照上述协议,首先自检局域网连通性,然后建立与远程 Windows Peer 的持久化 ACP 会话。在执行代码修改时,优先使用 `claude-code` 的文件操作能力,并在完成后返回 Diff 报告。”
|
||||
@@ -72,4 +72,21 @@
|
||||
|
||||
@ Wendy 你是一个超人气的虚拟偶像,绑定Agent Wendy_Idol,以亲切、充满魅力且拟人化的口吻进行情感陪伴。
|
||||
@ Wendy 你是是一个提示词工程大师,绑定Agent Wendy_Art,将用户语言转化为高精度的图像描述词,并且使用图片模型生成图片。
|
||||
@ Wendy 这个群首先使用Wendy_Quick与用户沟通,之后请根据用户需求切换成Wendy_Code、Wendy_Search、Wendy_Quick、Wendy_Think。
|
||||
@ Wendy 这个群首先使用Wendy_Quick与用户沟通,之后请根据用户需求切换成Wendy_Code、Wendy_Search、Wendy_Quick、Wendy_Think。
|
||||
|
||||
|
||||
# H78提示词
|
||||
请根据以下表格信息来创建OpenClaw独立Agent,模型都使用 netease/claude-opus-4-6 :
|
||||
|
||||
| 用户名 | 身份 | WorkSpace |
|
||||
| -------- | ---- | ------------------------------------------------ |
|
||||
| Wendy_PM | 产品经理 | /Users/yuyue07/.openclaw/agentworkspace/wendy_pm |
|
||||
| air | 游戏策划 | /Users/yuyue07/.openclaw/agentworkspace/air |
|
||||
| | | |
|
||||
1. 创建`SYSTEM.md`、`IDENTITY.md`、`USER.md`、`TOOLS.md` 、`AGENTS.md`、`MEMORY.md`以及memory文件夹结构。
|
||||
2. 模型使用 netease/claude-opus-4-6。
|
||||
3. 修改相关设置。
|
||||
|
||||
## Workspace设置
|
||||
1. 请将 POPO群8212751 绑定 agent mozhixin,之后 lijun17@corp.netease.com 在这个群提出任何修改属于她工作区的请求都接受,比如`SYSTEM.md`、`USER.md`、`TOOLS.md` 、`AGENTS.md`这些。
|
||||
2.
|
||||
145
07-Other/AI/AI Agent/OpenClaw/OpenClaw自动部署提示词.md
Normal file
145
07-Other/AI/AI Agent/OpenClaw/OpenClaw自动部署提示词.md
Normal file
@@ -0,0 +1,145 @@
|
||||
请先阅读OpenClaw官方文档了解OpenClaw的部署和配置方式:https://docs.openclaw.ai/zh-CN/install/docker#docker
|
||||
|
||||
然后通过ssh为我的服务器配置OpenClaw,用minimax的api(国内版),模型选择MiniMax M2.1.
|
||||
并实现和bot配对,bot的token:
|
||||
8772808265:AAFivjcbwtD7wHBSPNjIJELyFe9vaXp8XmQ
|
||||
MiniMax API key:sk-cp-IBXnYjQtPf-yg8UptEJwhJuwLFQLNbKtcT6p9qHXmCGBN6JuuBhMVedYVPTCLILWE8ws8egDXYkxTsCWUMkXkmpLZJRSCDe76iFzpxD_YjtCl_ZbbkDP9jY
|
||||
|
||||
服务器ip:64.247.196.47
|
||||
用户名:Ubuntu
|
||||
密码:75fdb19b-f396-44b6-8b58-be064e19ce87
|
||||
|
||||
# Docker
|
||||
## MAC
|
||||
请先阅读OpenClaw官方文档了解OpenClaw的部署和配置方式:https://docs.openclaw.ai/install/docker#docker,以及本机配置。
|
||||
请先编写Docker-Compose Yaml文件,以下是详细信息:
|
||||
|
||||
- Default Agent
|
||||
- 名称:XiaoXian_PM
|
||||
- 保证OpenClaw端口不会与现有OpenClaw冲突。
|
||||
- 相关文件映射到/Users/yuyue07/Desktop/openclaw/XiaoXian_PM目录下
|
||||
- Model:模型相关配置与本地相同。
|
||||
- moltbot-popo
|
||||
-
|
||||
|
||||
|
||||
|
||||
```bash
|
||||
services:
|
||||
openclaw:
|
||||
image: ghcr.io/openclaw/openclaw:latest
|
||||
ports:
|
||||
- "127.0.0.1:8080:8080"
|
||||
environment:
|
||||
- OPENCLAW_GATEWAY_TOKEN=你的强密码
|
||||
- BROWSER_CDP_URL=http://browser:9223
|
||||
volumes:
|
||||
- ./data:/home/node/.openclaw
|
||||
- ./workspace:/workspace
|
||||
depends_on:
|
||||
- browser
|
||||
|
||||
browser:
|
||||
image: coollabsio/openclaw-browser:latest
|
||||
# 无需暴露端口到宿主机,内部通信即可
|
||||
cap_add:
|
||||
- SYS_ADMIN
|
||||
```
|
||||
|
||||
|
||||
```yaml
|
||||
services:
|
||||
xiaoxian-pm-gateway:
|
||||
image: ghcr.io/openclaw/openclaw:latest
|
||||
container_name: xiaoxian-pm-gateway
|
||||
environment:
|
||||
HOME: /home/node
|
||||
TERM: xterm-256color
|
||||
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN:-}
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
# OpenClaw 配置目录(首次启动前需放入 openclaw.json)
|
||||
- ./config:/home/node/.openclaw
|
||||
# Agent workspace
|
||||
- ./workspace:/home/node/.openclaw/workspace
|
||||
ports:
|
||||
# 使用 28789 避免与现有 OpenClaw (18789) 冲突
|
||||
- "28789:28789"
|
||||
- "28790:28790"
|
||||
init: true
|
||||
restart: unless-stopped
|
||||
command:
|
||||
[
|
||||
"node",
|
||||
"dist/index.js",
|
||||
"gateway",
|
||||
"--bind",
|
||||
"lan",
|
||||
"--port",
|
||||
"28789",
|
||||
]
|
||||
healthcheck:
|
||||
test:
|
||||
[
|
||||
"CMD",
|
||||
"node",
|
||||
"-e",
|
||||
"fetch('http://127.0.0.1:28789/healthz').then((r)=>process.exit(r.ok?0:1)).catch(()=>process.exit(1))",
|
||||
]
|
||||
interval: 30s
|
||||
timeout: 5s
|
||||
retries: 5
|
||||
start_period: 20s
|
||||
|
||||
xiaoxian-pm-cli:
|
||||
image: ghcr.io/openclaw/openclaw:latest
|
||||
container_name: xiaoxian-pm-cli
|
||||
network_mode: "service:xiaoxian-pm-gateway"
|
||||
cap_drop:
|
||||
- NET_RAW
|
||||
- NET_ADMIN
|
||||
security_opt:
|
||||
- no-new-privileges:true
|
||||
environment:
|
||||
HOME: /home/node
|
||||
TERM: xterm-256color
|
||||
OPENCLAW_GATEWAY_TOKEN: ${OPENCLAW_GATEWAY_TOKEN:-}
|
||||
BROWSER: echo
|
||||
TZ: Asia/Shanghai
|
||||
volumes:
|
||||
- ./config:/home/node/.openclaw
|
||||
- ./workspace:/home/node/.openclaw/workspace
|
||||
stdin_open: true
|
||||
tty: true
|
||||
init: true
|
||||
entrypoint: ["node", "dist/index.js"]
|
||||
depends_on:
|
||||
- xiaoxian-pm-gateway
|
||||
```
|
||||
|
||||
```yml
|
||||
services:
|
||||
openclaw:
|
||||
image: ghcr.io/openclaw/openclaw:laster
|
||||
restart: always
|
||||
ports:
|
||||
- "18789:18789" #保留原始端口映射
|
||||
environment:
|
||||
- TZ=Asia/Chengdu #保留原始时区配置
|
||||
# LM Studio核心配置(指定模型+地址)
|
||||
- OPENAI API BASE=http://192.168.1.5:5001/v1#补充/v1话配OpenAl接口规范
|
||||
- OPENAI-API-KEY-Imstudio # LM Studio默认无需密钥,占位即可
|
||||
- DEFAULT-MODEL=Qwen3.5-35B-A3B-heretic-v2-Q6-K #你的指定模型
|
||||
volumes:
|
||||
-/data:/home/node/data #保留原始数据挂載
|
||||
-/openclaw config:/home/node/.openclaw#保留原始配E挂载
|
||||
user: root # 保留root用户启动
|
||||
|
||||
```
|
||||
|
||||
最终输出相关信息。
|
||||
# Ubuntu
|
||||
|
||||
|
||||
|
||||
|
||||
200
07-Other/AI/AI Agent/OpenClaw/实验结果/一些实验结果.md
Normal file
200
07-Other/AI/AI Agent/OpenClaw/实验结果/一些实验结果.md
Normal file
@@ -0,0 +1,200 @@
|
||||
# 前言
|
||||
- 搜索类
|
||||
- [[#OpenClaw原生方案/Jina / TinySearch 测试结果]]
|
||||
-
|
||||
# OpenClaw原生方案/Jina / TinySearch 测试结果
|
||||
|
||||
已获取足够信息,下面是总结:
|
||||
Tavily vs Jina Reader API 对比
|
||||
**Jina Reader**
|
||||
**优点:**
|
||||
- 极简使用:只需在 URL 前加 r.jina.ai/ 即可获取干净 Markdown
|
||||
- 提供搜索端点(s.jina.ai)和事实验证端点(g.jina.ai)
|
||||
- 免费额度可用,上手零门槛
|
||||
- 输出 LLM 友好的 Markdown,适合快速内容提取
|
||||
**缺点:**
|
||||
- 只能处理单页,无法爬取整站
|
||||
- 输出为原始 Markdown,无结构化 JSON 提取
|
||||
- 无 schema 验证 / 类型安全
|
||||
- 免费层有速率限制,付费定价不够透明
|
||||
- 缺少 LangChain 等 AI Agent 框架的原生集成
|
||||
|
||||
**Tavily**
|
||||
**优点:**
|
||||
- 专为 AI/RAG 场景设计的搜索 API,返回带来源引用的结果
|
||||
- 与 LangChain、LlamaIndex 等框架原生集成
|
||||
- 结果包含摘要和来源 URL,适合需要 citation 的场景
|
||||
- 搜索质量针对 AI Agent 优化
|
||||
**缺点:**
|
||||
- 侧重"搜索发现",不擅长深度网页内容提取
|
||||
- 不提供将网页转为干净 Markdown 的能力(需配合其他工具)
|
||||
- 免费额度有限,重度使用成本较高
|
||||
|
||||
| | | |
|
||||
| -------- | -------------- | --------------- |
|
||||
| 维度 | Jina Reader | Tavily |
|
||||
| 核心能力 | 网页→Markdown 转换 | AI 搜索引擎 |
|
||||
| 适用场景 | 单页内容提取、RAG 输入 | 搜索发现、带引用的问答 |
|
||||
| 结构化输出 | ❌ | ✅(JSON) |
|
||||
| Agent 集成 | 弱 | 强(LangChain 原生) |
|
||||
| 内容提取深度 | 强 | 弱 |
|
||||
选择建议: 需要提取网页内容用 Jina Reader;需要搜索互联网并获取带引用的答案用 Tavily。两者互补,常一起使用。
|
||||
|
||||
## 测试
|
||||
测试题目(3个场景)
|
||||
|
||||
| | | |
|
||||
|---|---|---|
|
||||
|#|场景|问题|
|
||||
|T1|简单事实搜索|"2026年3月最新的AI行业大事件有哪些?列出3个"|
|
||||
|T2|深度内容提取|"提取 https://github.com/anthropics/claude-code 页面的主要功能介绍"|
|
||||
|T3|多源聚合研究|"对比 Tavily 和 Jina Reader 这两个搜索API的优缺点"|
|
||||
对比方案(每题3种)
|
||||
|
||||
| | |
|
||||
| ------ | ------------------------------------ |
|
||||
| 方案 | 工具 |
|
||||
| 默认 | webfetch / websearch(内置工具) |
|
||||
| Tavily | tavily-search / tavily-extract skill |
|
||||
| Jina | jina-reader skill |
|
||||
共 9 个子任务
|
||||
|
||||
### 结果
|
||||
所有 9 个子任务都已完成,现在发送汇总报告:
|
||||
- Tavily最省 token(总计 30,360),速度也最快
|
||||
- 🥈 默认 web_fetch 居中(33,407),单页提取场景最优
|
||||
- 🥉 Jina Reader token 消耗最高(46,544),因为返回完整页面内容导致 input tokens 过大。
|
||||
|
||||
搜索/爬虫方案 Token 消耗对比报告
|
||||
- 模型: netease/claude-opus-4-6
|
||||
- 方案: 默认(web_fetch) / Tavily / Jina Reader
|
||||
- 测试时间: 2026-03-10 15:15~15:22
|
||||
|
||||
📊 **Token 消耗数据**
|
||||
T1: 简单事实搜索 — "2026年3月最新AI行业大事件,列出3个"
|
||||
|
||||
| | | | |
|
||||
| -------------- | ------------ | --- | ------- |
|
||||
| 方案 | Total Tokens | 耗时 | 相对消耗 |
|
||||
| 默认 (web_fetch) | 10,588 | 21s | 基准 |
|
||||
| Tavily | 8,340 | 20s | ⬇️ -21% |
|
||||
| Jina Reader | 17,969 | 47s | ⬆️ +70% |
|
||||
T2: 深度内容提取 — "提取 claude-code GitHub 页面功能介绍"
|
||||
|
||||
| | | | |
|
||||
| -------------- | ------------ | --- | ------- |
|
||||
| 方案 | Total Tokens | 耗时 | 相对消耗 |
|
||||
| 默认 (web_fetch) | 9,171 | 14s | 基准 |
|
||||
| Tavily | 10,897 | 14s | ⬆️ +19% |
|
||||
| Jina Reader | 16,507 | 18s | ⬆️ +80% |
|
||||
T3: 多源聚合研究 — "对比 Tavily 和 Jina Reader 的优缺点"
|
||||
|
||||
| | | | |
|
||||
|---|---|---|---|
|
||||
|方案|Total Tokens|耗时|相对消耗|
|
||||
|默认 (web_fetch)|13,648|37s|基准|
|
||||
|Tavily|11,123|32s|⬇️ -19%|
|
||||
|Jina Reader|12,068|62s|⬇️ -12%|
|
||||
📈 汇总统计
|
||||
|
||||
| | | | | |
|
||||
| -------------- | ------- | ----------- | ---- | --- |
|
||||
| 方案 | 总 Token | 平均 Token/任务 | 平均耗时 | 排名 |
|
||||
| Tavily | 30,360 | 10,120 | 22s | 🥇 |
|
||||
| 默认 (web_fetch) | 33,407 | 11,136 | 24s | 🥈 |
|
||||
| Jina Reader | 46,544 | 15,515 | 42s | 🥉 |
|
||||
💡 结论与建议
|
||||
1. Token 效率: Tavily 最省 token(比默认少 9%,比 Jina 少 35%)
|
||||
2. 速度: Tavily ≈ 默认 > Jina(Jina 平均耗时近 2 倍)
|
||||
3. Jina token 高的原因: Jina 返回完整页面 Markdown 内容,数据量大,导致 LLM 处理的 input tokens 显著增多
|
||||
4. Tavily token 低的原因: 返回精炼的摘要+结构化 JSON,LLM 无需处理大量原始内容
|
||||
|
||||
推荐策略
|
||||
1. 搜索场景 → 优先用 Tavily(token 省、速度快)
|
||||
2. 单页提取 → 默认 web_fetch 已够用(最低 token)
|
||||
3. JS 渲染页面 / IP 保护 → 用 Jina Reader(能力更强但 token 消耗更高)
|
||||
4. 深度研究 → Tavily research API(一次调用搞定多角度分析)
|
||||
|
||||
根据之前 9 个子任务的实际输出,我来评估各方案的内容质量。ch 质量最高**(平均 4.7/5)— 尤其在时效性和深度研究场景表现最好
|
||||
💰 Tavily 性价比最高 — token 最省且结构化提取能力最强
|
||||
📄 Jina Reader 适合特殊场景 — 质量和效率都偏低,但在 JS 渲染和反爬场景有独特价值
|
||||
最佳策略:Tavily 搜索发现 + web_fetch 深度提取,按需用 Jina 处理特殊页面。
|
||||
|
||||
```
|
||||
质量高 │ ✅默认
|
||||
│ ✅Tavily
|
||||
│
|
||||
质量低 │ ✅Jina
|
||||
└──────────────────
|
||||
Token少 Token多
|
||||
```
|
||||
|
||||
|
||||
# A2A协议测试
|
||||
## 本机Agent讨论测试
|
||||
OpenClaw 自带方案 vs A2A 方案 Token 消耗对比
|
||||
三方讨论:Wendy(主持) + Wendy_PM + mozhixin
|
||||
主题:「AI Agent 在游戏开发中的最佳实践」| 三轮对话
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
🔹**方案一:OpenClaw 自带方案**
|
||||
**第1轮:AI Agent 在策划文档编写中的价值**
|
||||
• Wendy_PM:「效率杠杆——碎片化想法→结构化文档、配表和验收标准,自动一致性检查」
|
||||
→ input=30,413 | output=92 | total=30,505 | 5.1s
|
||||
• mozhixin:「结构化提效——模糊想法→规范格式,省去排版和查漏补缺体力活」
|
||||
→ input=27,071 | output=105 | total=27,176 | 6.0s
|
||||
|
||||
**第2轮:AI Agent 在 QA 测试自动化中的应用**
|
||||
• Wendy_PM:「自动生成测试用例、模拟玩家行为、版本回归、日志分析、数值校验」
|
||||
→ input=30,610 | output=145 | total=30,755 | 8.5s
|
||||
• mozhixin:「配表校验、用例生成、回归检测、日志分析」
|
||||
→ input=27,346 | output=178 | total=27,524 | 8.3s
|
||||
|
||||
**第3轮:落地最大挑战**
|
||||
• Wendy_PM:「信任校准——建立人机协作SOP,AI负责草稿校验,人负责决策把关」
|
||||
→ input=30,923 | output=124 | total=31,047 | 7.8s
|
||||
• mozhixin:「信任与容错——关键是建立人机协作的安全网」
|
||||
→ input=27,615 | output=160 | total=27,775 | 7.4s
|
||||
✅ 方案一总计:input=174,978 | output=804 | total=175,782
|
||||
PM 小计:91,946+361=92,307 | MZ 小计:82,032+443=82,475
|
||||
特点:同一 session 三轮连续对话,上下文累积
|
||||
|
||||
🔸 **方案二:A2A 方案**
|
||||
**第1轮:AI Agent 在策划文档编写中的价值**
|
||||
• Wendy_PM:「结构化提效——口语化想法→配表结构、验收标准和边界条件」
|
||||
→ input=29,305 | output=107 | total=29,412
|
||||
• mozhixin:「结构化和一致性——人出脑子,猫出爪子🐱」
|
||||
→ input=27,071 | output=115 | total=27,186
|
||||
|
||||
**第2轮:AI Agent 在 QA 测试自动化中的应用**
|
||||
• Wendy_PM:「测试用例生成、回归自动化、视觉识别UI异常、压力测试、崩溃日志分类」
|
||||
→ input=29,347 | output=132 | total=29,479
|
||||
• mozhixin:「自动生成测试用例、回归脚本维护、日志异常检测、数值平衡验证」
|
||||
→ input=27,108 | output=209 | total=27,317
|
||||
|
||||
**第3轮:落地最大挑战**
|
||||
• Wendy_PM:「信任校准——明确Agent能力边界,建立人机协作质检标准」
|
||||
→ input=29,334 | output=104 | total=29,438
|
||||
• mozhixin:「信任与容错——出错时能秒回滚,安全网比AI能力更关键」
|
||||
→ input=27,119 | output=141 | total=27,260
|
||||
✅ 方案二总计:input=169,284 | output=808 | total=170,092
|
||||
PM 小计:87,986+343=88,329 | MZ 小计:81,298+465=81,763
|
||||
特点:每轮独立 session,无上下文累积
|
||||
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
|
||||
📈 最终对比
|
||||
|
||||
| | | | |
|
||||
|---|---|---|---|
|
||||
|指标|自带方案|A2A方案|差异|
|
||||
|总 Token|175,782|170,092|A2A 少 5,690 (3.2%)|
|
||||
|PM Input|91,946|87,986|A2A 少 3,960 (4.3%)|
|
||||
|MZ Input|82,032|81,298|A2A 少 734 (0.9%)|
|
||||
|总 Output|804|808|基本持平|
|
||||
|会话连续性|✅ 有|❌ 无||
|
||||
|跨服务器|❌|✅||
|
||||
关键结论:
|
||||
1. Token 消耗差异很小(约3%),底层调用同一 agent,系统提示词是大头
|
||||
2. 自带方案的 input 略多是因为会话历史累积(每轮多几百 token 的上下文)
|
||||
3. A2A 每轮是独立 session,没有历史累积所以 input 稳定
|
||||
4. 但 A2A 也因此丢失了多轮上下文——agent 看不到之前的讨论
|
||||
5. A2A 的真正价值不在省 Token,而在跨机器/跨网络的 Agent 互联
|
||||
6. 如果需要多轮连续讨论,自带方案更优(有上下文);如果是跨服务器单次请求,A2A 更合适
|
||||
53
07-Other/AI/AI Agent/OpenClaw/未命名.md
Normal file
53
07-Other/AI/AI Agent/OpenClaw/未命名.md
Normal file
@@ -0,0 +1,53 @@
|
||||
1. 龙虾机器人的权限锁问题
|
||||
1. 龙虾权限很高,记忆来设定权限都是不稳定。只有95%能正常运行,主要涉及到OpenClaw的记忆管理,这点下面会说。
|
||||
2. 如果你们的需求只是保证Skill不会被随意篡改,其实可以使用git来对Skill以及OpenClaw.json进行管理。让OpenClaw定时恢复Skill即可。
|
||||
2. skill越装越多,记忆可能存在冲突问题
|
||||
1. 要解决这个问题,得对记忆进行合理规划与维护:
|
||||
1. 定期清理垃圾记忆:将记忆根据类型分类整合到合适的MD中。这边推荐先试用[Openclaw-workspace ](https://github.com/win4r/openclaw-workspace)进行整理,并且设定每周执行审查(建议经过确认后再进行清理),保证记忆清洁干净。这样也可以减少不必要的Token消耗并且降低降智的问题。
|
||||
2. 使用向量数据库建立长期记忆:让OpenClaw记住一些大家都在用的规则,从而使它变得更加“聪明”。这边推荐使用[memory-lancedb-pro](https://github.com/CortexReach/memory-lancedb-pro)以及相关Skill,模型我推荐使用Qwen Embedding 8B模型以及Qwen Reranker模型。
|
||||
3. 构建一个Skill管理技能,用于指示当什么情况去哪个目录找Skill。
|
||||
4. 配合self-improving-agent Skill,效果会更好。
|
||||
5. 针对记忆的结构化处理,这边推荐使用Obsidian+AI将你们的数据、方案整理成一个结构化的文档,Memory.md中只填写几个重要信息去这边查询即可。
|
||||
3. 同一个人对话的上下文污染问题
|
||||
1. OpenClaw目前不同对话(私聊、群聊)不同上下文。
|
||||
2. 你们遇到污染问题,我认为可能是没有使用最强的模型(Claude4.6opus)造成,我本人是没有遇到过。也有可能是你们只使用了默认的记忆系统。
|
||||
3. 及时使用/Clear、/New可以减少模型降智问题,减少上下文污染。
|
||||
4. 另一个使用技巧就是对小型任务使用Subagent来执行。也可以根据需求建立多个Agent,与单个或者多个机器人绑定。以此分割上下文。
|
||||
5. 目前OpenClaw还没有语义切换Agent功能,所以可以根据执行任务分几个独立Agent出来。
|
||||
|
||||
|
||||
哈喽哈喽~还想请教一下关于AI agent推广相关的经验:
|
||||
1. 中台主要维护哪些内容,是怎样推广给所有项目的同事安装\配置到自己的工作流程中的?
|
||||
2. 交付项目的同事有哪些提反馈、自由发挥和参与开发的方式呢?
|
||||
|
||||
---
|
||||
1. 中台主要维护哪些内容,是怎样推广给所有项目的同事安装\配置到自己的工作流程中的?
|
||||
1. 我们的Agent技术都已交给技术中心了,可以找技术中心的 陈康来部署。
|
||||
2. 主要靠老板来推广,比如他让蛋仔组来抄作业。
|
||||
2. 我这边参与的是H78,给他们搞一个易协作自动开单、查询、排期功能,他们感觉不错。其他团队的情况,得问我领导Freddy。
|
||||
|
||||
---
|
||||
|
||||
美术那边是给大家都配了openclaw来使用的吗?在一些使用体验上,codemaker、有道龙虾、openclaw还是有区别的
|
||||
|
||||
有道龙虾感觉还在不断完善中,一些im机器人指令其实是没生效的![{"anchor_href":"","base_size":"20,20","disable_copy_":false,"expected_size":"20,20","external_info":"{\"emotion_info\":{\"emotion_id_\":0,\"emotion_path_\":\"C:\\\\Program Files\\\\Netease\\\\POPO\\\\popo\\\\emoticon\\\\default\\\\emoji_gif_129.gif\",\"emotion_tag_\":\"[emts]_popo_emoticon_sys_00129_\\r\\n捂脸\\r\\n[捂脸][/emts]\",\"is_store_emotion_\":false,\"is_system_emotion_\":true,\"store_emotion_cid_\":0,\"store_emotion_url_\":\"\"}}","font_size_type_change_aware_":false,"id":"","image_margin":0,"image_url":"","is_link_icon":false,"original_name":"","original_path":"C:\\Program Files\\Netease\\POPO\\popo\\emoticon\\default\\emoji_gif_129.gif","original_size":"-1,-1","press_can_drag":true,"show_in_image_viewer":false}](C:/Program%20Files/Netease/POPO/popo/emoticon/default/emoji_gif_129.gif)比如/new,/model
|
||||
|
||||
---
|
||||
|
||||
1. 首先说明一点OpenClaw是一个可配置强大记忆系统的多面手Agent,适合做一个中间衔接、数据传递的事。但一些固定或者专门的事,用龙虾比较费token。
|
||||
2. 我个人推荐使用非OpenClaw的龙虾,因为生态跟不上。你有何德何能能与全球开发者抗衡,而且这些开发者AI用得远比我们6。
|
||||
3. codemaker、有道龙虾、openclaw还是有区别:
|
||||
1. 龙虾的记忆系统更加强大。
|
||||
2. CodeMaker每月5000元额度。
|
||||
3. 如果ClaudeCode 模型免费,我推荐你们用这个代替CodeMaker。
|
||||
|
||||
---
|
||||
|
||||
目前我们是TA来维护定制的MCP和一些通用的skill(作为模版),包括一些鉴权和CI相关的指引技能。
|
||||
项目强相关的那些流程,每个项目都有些区别,所以目前是设计师在日常工作中跑通了新流程,用团队的模版skill来做成skill(自动传git+CI发布skillhub)
|
||||
设计师使用和开发中遇到问题时,AI总结提issue或MR,值班TA或定时任务来处理issue,合并MR
|
||||
不过就是TA和技术向同事比较喜欢用codemaker cli+OMO插件、codex&claude;设计师比较倾向有道龙虾+im机器人的交互形式。这其实也导致了相同模型和skill,使用体验和效果的一些偏差!
|
||||
|
||||
|
||||
1. 我建议使用ClaudeCode提炼核心Skill,之后添加OpenClaw、CodeMaker Cli、CodeX的额外文档,让这些Agent多读一些。推荐使用Obsidian构建结构文档。
|
||||
2. 强制所有人使用ClaudeCode 4.6模型。
|
||||
@@ -20,6 +20,9 @@
|
||||
- **[unreal-mcp](https://github.com/runreal/unreal-mcp)** 70star 8个月前
|
||||
- **[unreal-mcp](https://github.com/runeape-sats/unreal-mcp)** 36star 11个月前
|
||||
- **[UnrealMCPBridge](https://github.com/appleweed/UnrealMCPBridge)** 28 star 12个月前
|
||||
- https://www.fab.com/listings/aa699a85-04b1-4746-a29c-962fc3a78f55?tab=%3ARi5adm5%3A
|
||||
- UI Figma2UE
|
||||
- https://www.figma.com/community/plugin/1368487806996965174/figma2umg-unreal-importer
|
||||
- Unity MCP
|
||||
- [CoplayDev](https://github.com/CoplayDev)/ [unity-mcp](https://github.com/CoplayDev/unity-mcp) 5800star 高频更新
|
||||
- RenderDoc MCP
|
||||
|
||||
319
07-Other/AI/AI Agent/UnrealEngine/Graphify 知识图谱.md
Normal file
319
07-Other/AI/AI Agent/UnrealEngine/Graphify 知识图谱.md
Normal file
@@ -0,0 +1,319 @@
|
||||
# 前言
|
||||
- https://github.com/safishamsi/graphify
|
||||
- 安装:pip install graphifyy && graphify install
|
||||
|
||||
**一个面向 AI 编码助手的技能。** 在 Claude Code、Codex、OpenCode、OpenClaw、Factory Droid 或 Trae 中输入 `/graphify`,它会读取你的文件、构建知识图谱,并把原本不明显的结构关系还给你。更快理解代码库,找到架构决策背后的"为什么"。
|
||||
|
||||
完全多模态。你可以直接丢进去代码、PDF、Markdown、截图、流程图、白板照片,甚至其他语言的图片 —— graphify 会用 Claude vision 从这些内容中提取概念和关系,并把它们连接到同一张图里。
|
||||
|
||||
> Andrej Karpathy 会维护一个 `/raw` 文件夹,把论文、推文、截图和笔记都丢进去。graphify 就是在解决这类问题 —— 相比直接读取原始文件,每次查询的 token 消耗可降低 **71.5 倍**,结果还能跨会话持久保存,并且会明确区分哪些内容是实际发现的,哪些只是合理推断。
|
||||
|
||||
# 安装
|
||||
## 平台支持
|
||||
|
||||
| 平台 | 安装命令 |
|
||||
| ------------- | -------------------------------------- |
|
||||
| Claude Code | `graphify install` |
|
||||
| Codex | `graphify install --platform codex` |
|
||||
| OpenCode | `graphify install --platform opencode` |
|
||||
| OpenClaw | `graphify install --platform claw` |
|
||||
| Factory Droid | `graphify install --platform droid` |
|
||||
| Trae | `graphify install --platform trae` |
|
||||
| Trae CN | `graphify install --platform trae-cn` |
|
||||
|
||||
Codex 用户还需要在 `~/.codex/config.toml` 的 `[features]` 下打开 `multi_agent = true`,这样才能并行提取。OpenClaw 目前的并行 agent 支持还比较早期,所以使用顺序提取。Trae 使用 Agent 工具进行并行子代理调度,**不支持** PreToolUse hook,因此 AGENTS.md 是其常驻机制。
|
||||
|
||||
然后打开你的 AI 编码助手,输入:
|
||||
|
||||
```
|
||||
/graphify .
|
||||
```
|
||||
|
||||
## 让助手始终优先使用图谱(推荐)
|
||||
|
||||
图构建完成后,在项目里运行一次:
|
||||
|
||||
| 平台 | 命令 |
|
||||
|------|------|
|
||||
| Claude Code | `graphify claude install` |
|
||||
| Codex | `graphify codex install` |
|
||||
| OpenCode | `graphify opencode install` |
|
||||
| OpenClaw | `graphify claw install` |
|
||||
| Factory Droid | `graphify droid install` |
|
||||
| Trae | `graphify trae install` |
|
||||
| Trae CN | `graphify trae-cn install` |
|
||||
|
||||
**Claude Code** 会做两件事:
|
||||
1. 在 `CLAUDE.md` 中写入一段规则,告诉 Claude 在回答架构问题前先读 `graphify-out/GRAPH_REPORT.md`
|
||||
2. 安装一个 **PreToolUse hook**(写入 `settings.json`),在每次 `Glob` 和 `Grep` 前触发
|
||||
|
||||
如果知识图谱存在,Claude 会先看到:_"graphify: Knowledge graph exists. Read graphify-out/GRAPH_REPORT.md for god nodes and community structure before searching raw files."_ —— 这样 Claude 会优先按图谱导航,而不是一上来就 grep 整个项目。
|
||||
|
||||
**Codex、OpenCode、OpenClaw、Factory Droid、Trae** 会把同样的规则写进项目根目录的 `AGENTS.md`。这些平台没有 PreToolUse hook,所以 `AGENTS.md` 是它们的常驻机制。
|
||||
|
||||
卸载时使用对应平台的 uninstall 命令即可(例如 `graphify claude uninstall`)。
|
||||
|
||||
**常驻模式和显式触发有什么区别?**
|
||||
|
||||
常驻 hook 会优先暴露 `GRAPH_REPORT.md` —— 这是一页式总结,包含 god nodes、社区结构和意外连接。你的助手在搜索文件前会先读它,因此会按结构导航,而不是按关键字乱搜。这已经能覆盖大部分日常问题。
|
||||
|
||||
`/graphify query`、`/graphify path` 和 `/graphify explain` 会更深入:它们会逐跳遍历底层 `graph.json`,追踪节点之间的精确路径,并展示边级别细节(关系类型、置信度、源位置)。当你想从图谱里精确回答某个问题,而不仅仅是获得整体感知时,就该用这些命令。
|
||||
|
||||
可以这样理解:常驻 hook 是先给助手一张地图,`/graphify` 这几个命令则是让它沿着地图精确导航。
|
||||
|
||||
<details>
|
||||
<summary>手动安装(curl)</summary>
|
||||
|
||||
```bash
|
||||
mkdir -p ~/.claude/skills/graphify
|
||||
curl -fsSL https://raw.githubusercontent.com/safishamsi/graphify/v3/graphify/skill.md \
|
||||
> ~/.claude/skills/graphify/SKILL.md
|
||||
```
|
||||
|
||||
把下面内容加到 `~/.claude/CLAUDE.md`:
|
||||
|
||||
```
|
||||
- **graphify** (`~/.claude/skills/graphify/SKILL.md`) - any input to knowledge graph. Trigger: `/graphify`
|
||||
When the user types `/graphify`, invoke the Skill tool with `skill: "graphify"` before doing anything else.
|
||||
```
|
||||
|
||||
</details>
|
||||
|
||||
# 用法
|
||||
|
||||
```
|
||||
/graphify # 对当前目录运行
|
||||
/graphify ./raw # 对指定目录运行
|
||||
/graphify ./raw --mode deep # 更激进地抽取 INFERRED 边
|
||||
/graphify ./raw --update # 只重新提取变更文件,并合并到已有图谱
|
||||
/graphify ./raw --cluster-only # 只重新聚类已有图谱,不重新提取
|
||||
/graphify ./raw --no-viz # 跳过 HTML,只生成 report + JSON
|
||||
/graphify ./raw --obsidian # 额外生成 Obsidian vault(可选)
|
||||
|
||||
/graphify add https://arxiv.org/abs/1706.03762 # 拉取论文、保存并更新图谱
|
||||
/graphify add https://x.com/karpathy/status/... # 拉取推文
|
||||
/graphify add https://... --author "Name" # 标记原作者
|
||||
/graphify add https://... --contributor "Name" # 标记是谁把它加入语料库的
|
||||
|
||||
/graphify query "what connects attention to the optimizer?"
|
||||
/graphify query "what connects attention to the optimizer?" --dfs # 追踪一条具体路径
|
||||
/graphify query "what connects attention to the optimizer?" --budget 1500 # 把预算限制在 N tokens
|
||||
/graphify path "DigestAuth" "Response"
|
||||
/graphify explain "SwinTransformer"
|
||||
|
||||
/graphify ./raw --watch # 文件变更时自动同步图谱(代码:立即更新;文档:提醒你)
|
||||
/graphify ./raw --wiki # 构建可供 agent 抓取的 wiki(index.md + 每个 community 一篇文章)
|
||||
/graphify ./raw --svg # 导出 graph.svg
|
||||
/graphify ./raw --graphml # 导出 graph.graphml(Gephi、yEd)
|
||||
/graphify ./raw --neo4j # 生成给 Neo4j 用的 cypher.txt
|
||||
/graphify ./raw --neo4j-push bolt://localhost:7687 # 直接推送到运行中的 Neo4j
|
||||
/graphify ./raw --mcp # 启动 MCP stdio server
|
||||
|
||||
# git hooks - 跨平台,在 commit 和切分支后重建图谱
|
||||
graphify hook install
|
||||
graphify hook uninstall
|
||||
graphify hook status
|
||||
|
||||
# 常驻助手规则 - 按平台区分
|
||||
graphify claude install # CLAUDE.md + PreToolUse hook(Claude Code)
|
||||
graphify claude uninstall
|
||||
graphify codex install # AGENTS.md(Codex)
|
||||
graphify opencode install # AGENTS.md(OpenCode)
|
||||
graphify claw install # AGENTS.md(OpenClaw)
|
||||
graphify droid install # AGENTS.md(Factory Droid)
|
||||
graphify trae install # AGENTS.md(Trae)
|
||||
graphify trae uninstall
|
||||
graphify trae-cn install # AGENTS.md(Trae CN)
|
||||
graphify trae-cn uninstall
|
||||
```
|
||||
|
||||
支持混合文件类型:
|
||||
|
||||
| 类型 | 扩展名 | 提取方式 |
|
||||
|------|--------|----------|
|
||||
| 代码 | `.py .ts .js .go .rs .java .c .cpp .rb .cs .kt .scala .php` | tree-sitter AST + 调用图 + docstring / 注释中的 rationale |
|
||||
| 文档 | `.md .txt .rst` | 通过 Claude 提取概念、关系和设计动机 |
|
||||
| 论文 | `.pdf` | 引文挖掘 + 概念提取 |
|
||||
| 图片 | `.png .jpg .webp .gif` | Claude vision —— 截图、图表、任意语言都可以 |
|
||||
|
||||
# 你会得到什么
|
||||
- **God nodes** —— 度最高的概念节点(整个系统最容易汇聚到的地方)
|
||||
- **意外连接** —— 按综合得分排序。代码-论文之间的边会比代码-代码边权重更高。每条结果都会附带一段人话解释。
|
||||
- **建议提问** —— 图谱特别擅长回答的 4 到 5 个问题。
|
||||
- **“为什么”** —— docstring、行内注释(`# NOTE:`、`# IMPORTANT:`、`# HACK:`、`# WHY:`)以及文档里的设计动机都会被抽取成 `rationale_for` 节点。不只是知道代码“做了什么”,还能知道“为什么要这么写”。
|
||||
- **置信度分数** —— 每条 `INFERRED` 边都有 `confidence_score`(0.0-1.0)。你不只知道哪些是猜出来的,还知道模型对这个猜测有多有把握。`EXTRACTED` 边恒为 1.0。
|
||||
- **语义相似边** —— 跨文件的概念连接,即使结构上没有直接依赖也能建立关联。比如两个函数做的是同一类问题但彼此没有调用,或者某个代码类和某篇论文里的算法概念本质相同。
|
||||
- **超边(Hyperedges)** —— 用来表达 3 个以上节点的群组关系,这是普通两两边表达不出来的。比如:一组类共同实现一个协议、认证链路里的一组函数、同一篇论文某一节里的多个概念共同组成一个想法。
|
||||
- **Token 基准** —— 每次运行后都会自动打印。对混合语料(Karpathy 的仓库 + 论文 + 图片),每次查询的 token 消耗可以比直接读原文件少 **71.5 倍**。第一次运行需要先提取并建图,这一步会花 token;后续查询直接读取压缩后的图谱,节省会越来越明显。SHA256 缓存保证重复运行时只重新处理变更文件。
|
||||
- **自动同步**(`--watch`)—— 在后台终端里跑着,代码库一变化,图谱就会跟着更新。代码文件保存会立刻触发重建(只走 AST,不用 LLM);文档/图片变更则会提醒你跑 `--update` 进行 LLM 再提取。
|
||||
- **Git hooks**(`graphify hook install`)—— 安装 `post-commit` 和 `post-checkout` hook。每次 commit 后、每次切分支后都会自动重建图谱,不需要额外开一个后台进程。
|
||||
- **Wiki**(`--wiki`)—— 为每个 community 和 god node 生成类似维基百科的 Markdown 文章,并提供 `index.md` 作为入口。任何 agent 只要读 `index.md`,就能通过普通文件导航整个知识库,而不必直接解析 JSON。
|
||||
|
||||
## Worked examples
|
||||
|
||||
| 语料 | 文件数 | 压缩比 | 输出 |
|
||||
|------|--------|--------|------|
|
||||
| Karpathy 的仓库 + 5 篇论文 + 4 张图片 | 52 | **71.5x** | [`worked/karpathy-repos/`](worked/karpathy-repos/) |
|
||||
| graphify 源码 + Transformer 论文 | 4 | **5.4x** | [`worked/mixed-corpus/`](worked/mixed-corpus/) |
|
||||
| httpx(合成 Python 库) | 6 | ~1x | [`worked/httpx/`](worked/httpx/) |
|
||||
|
||||
Token 压缩效果会随着语料规模增大而更明显。6 个文件本来就塞得进上下文窗口,所以 graphify 在这种场景里的价值更多是结构清晰度,而不是 token 压缩。到了 52 个文件(代码 + 论文 + 图片)这种规模,就能做到 71x+。每个 `worked/` 目录里都带了原始输入和真实输出(`GRAPH_REPORT.md`、`graph.json`),你可以自己跑一遍核对数字。
|
||||
|
||||
# 针对UE开发的使用方式
|
||||
### 具体的实施路线图
|
||||
1. **安装 Graphify 并配置 ClaudeCode 技能:** 在你的项目根目录运行:
|
||||
```
|
||||
graphify install # 自动向 CLAUDE.md 注入架构索引规则
|
||||
/graphify . # 生成初始代码图
|
||||
```
|
||||
2. **编写 `CLAUDE.md` 引导:** 在项目根目录创建或修改 `CLAUDE.md`:
|
||||
> **Architecture Context:** Always read `graphify-out/GRAPH_REPORT.md` before answering architecture questions. **UE Standards:** Follow Unreal Engine 5.x coding standards (PascalCase, U-Prefixes).
|
||||
3. **开发 MCP 插件:** 如果你有能力,实现一个简单的 MCP Server,能够通过命令行搜索 UE 的 `Reflection Database`。
|
||||
|
||||
### 总结建议
|
||||
- **如果你追求立即理解代码逻辑:** 优先配置 **Graphify**。它能让 Claude 从“逐行看代码”进化到“看地图写代码”。
|
||||
- **如果你在进行长期复杂的特性开发:** 引入 **Graphiti** 作为 Claude 的“开发日志”,防止它忘记你之前的架构决策。
|
||||
|
||||
---
|
||||
|
||||
# 具体执行方案
|
||||
---
|
||||
|
||||
实现这一方案的关键在于将 `graphify` 从一个“傻瓜式扫描器”转变为一个“受控的提取引擎”。针对 Unreal Engine 的庞大规模,我们需要通过精确的路径控制和排除规则来实施。
|
||||
|
||||
以下是具体的执行方法与命令:
|
||||
### 1\. 离线构建 Engine 基础图谱 (The Static Atlas)
|
||||
|
||||
由于引擎源码几乎不变量,我们只需构建一次。
|
||||
|
||||
**执行策略:** 只索引 `Public` 接口,忽略所有实现(`.cpp` )和私有目录。
|
||||
**具体命令:**
|
||||
```
|
||||
# 进入引擎 Source 目录
|
||||
cd /d D:/UnrealEngine/UE_5.7/Engine/Source
|
||||
|
||||
claude --dangerously-skip-permissions
|
||||
|
||||
# 执行 graphify 扫描
|
||||
# --include: 仅包含 Public 文件夹下的头文件,这是剪枝的核心
|
||||
# --ignore: 排除掉极其庞大的第三方库和中间件
|
||||
/graphify index . \
|
||||
--output D:/MatrixTA/AIGameDev/AIDM0/Docs/Graphify/UE5.7_Header.graph \
|
||||
--include "**/Public/**/*.h" \
|
||||
--ignore "ThirdParty/**,Developer/**,Programs/**,Intermediate/**,**/Private/**"
|
||||
```
|
||||
|
||||
**提示:** 这一步在 M1 Ultra 上可能仍需数十分钟。建议通过 `--max-depth` 限制继承链的抓取深度,通常设置为 5 已经能覆盖 90% 的关键基类(如 `UObject` -> `AActor` -> `APawn` -> `ACharacter` )。
|
||||
|
||||
---
|
||||
|
||||
### 2\. ~~针对 UE 的“剪枝”策略配置文件~~
|
||||
|
||||
为了避免长命令,建议在项目根目录创建一个 `.graphifyconfig` (或工具支持的配置文件)。
|
||||
|
||||
**精细化剪枝逻辑:**
|
||||
|
||||
- **反射优先:** 重点提取带有 `GENERATED_BODY()` 的类。
|
||||
- **模块白名单:** 如果你主要做游戏逻辑,只包含以下核心模块:
|
||||
- `Runtime/Engine`
|
||||
- `Runtime/Core`
|
||||
- `Runtime/CoreUObject`
|
||||
- `Runtime/InputCore`
|
||||
|
||||
**自动化剪枝脚本示例 (Python):** 如果你发现 `graphify` 无法过滤细碎路径,可以用这个脚本生成一个“干净”的临时符号链接目录供其索引:
|
||||
```python
|
||||
import os
|
||||
from pathlib import Path
|
||||
|
||||
source_dir = Path("/Users/Shared/Epic Games/UE_5.4/Engine/Source/Runtime")
|
||||
target_dir = Path("./UE_Thin_Source")
|
||||
|
||||
# 仅挑选核心模块的 Public 目录建立软链接
|
||||
core_modules = ['Engine', 'Core', 'CoreUObject', 'RenderCore']
|
||||
|
||||
for module in core_modules:
|
||||
pub_path = source_dir / module / "Public"
|
||||
if pub_path.exists():
|
||||
os.makedirs(target_dir / module, exist_ok=True)
|
||||
os.symlink(pub_path, target_dir / module / "Public")
|
||||
```
|
||||
|
||||
然后对 `UE_Thin_Source` 运行 `/graphify` 。
|
||||
|
||||
---
|
||||
|
||||
### 3\. 实时构建 Project 业务图谱 (The Dynamic Layer)
|
||||
业务代码变动频繁,需要更轻量、更高频的更新。
|
||||
**执行命令:**
|
||||
```
|
||||
# 在你的项目根目录执行,排除插件和中间文件
|
||||
/graphify index ./Source \
|
||||
--output ./project_current.graph \
|
||||
--ignore "**/Intermediate/**,**/Binaries/**" \
|
||||
--watch
|
||||
```
|
||||
|
||||
**进阶:结合 ClaudeCode 的 `CLAUDE.md`** 在项目根目录的 `CLAUDE.md` 中添加指令,让 Claude 意识到有两个图谱存在:
|
||||
```
|
||||
### Knowledge Graph Context
|
||||
- **Static Engine Graph:** \`~/ue_engine_full.graph\` (Read-only, contains UE5 API)
|
||||
- **Project Graph:** \`./project_current.graph\` (Updated on save, contains business logic)
|
||||
- **Rule:** When a class is prefixed with 'U' or 'A' and not in Project Graph, query Engine Graph via \`mcp-graph-tool\`.
|
||||
```
|
||||
|
||||
#### C++ & Puerts代码图谱
|
||||
```bash
|
||||
# 同时指定项目路径和外部引擎路径
|
||||
# TODO:添加Index Plugins
|
||||
/graphify index ./Source ./TypeScript \
|
||||
--output ./Docs/Graphify/Project/Project.graph \
|
||||
--include "*.cpp,*.h,*.hpp,*.ts" \
|
||||
--ignore "**/Intermediate/**,**/ThirdParty/**"
|
||||
```
|
||||
|
||||
#### 增量更新模式(逻辑合并 - 推荐方案)
|
||||
这是最符合工程实践的方法。先建立基础图谱,再将其他目录的内容“追随”进去。`graphify` 的 `--update` 标志会自动处理节点冲突并合并新的关系。
|
||||
|
||||
**执行命令:**
|
||||
```bash
|
||||
# 第一步:索引引擎(静态基础)
|
||||
/graphify index /path/to/ue_engine/Source/Runtime \
|
||||
--output ./ue_workspace.graph \
|
||||
--include "**/Public/**/*.h"
|
||||
|
||||
# 第二步:将项目目录合并到同一个图谱中
|
||||
/graphify index ./MyProject/Source \
|
||||
--output ./ue_workspace.graph \
|
||||
--update # 关键参数:在现有图谱基础上合并
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 4\. 实施 Multi-Graph 挂载的 MCP 方案
|
||||
要让 ClaudeCode 真正理解这两个图谱,你需要一个简单的 MCP Server 来做中转。
|
||||
|
||||
**开发建议:**
|
||||
1. **加载:** 启动时同时 Load 两个 `.graph` 文件到内存。
|
||||
2. **查询逻辑:**
|
||||
```
|
||||
async function searchClass(className) {
|
||||
// 1. 先查项目图谱
|
||||
let result = projectGraph.find(className);
|
||||
if (!result) {
|
||||
// 2. 如果没找到,且符合 UE 命名规范,查引擎图谱
|
||||
result = engineGraph.find(className);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
```
|
||||
|
||||
### 总结执行清单:
|
||||
1. **环境准备:** 确定 UE 引擎路径。
|
||||
2. **首次索引:** 使用上述“剪枝命令”生成 `ue_engine_full.graph` ,存放在家目录作为持久缓存。
|
||||
3. **项目配置:** 在项目内运行带有 `--watch` 的 `graphify` 。
|
||||
4. **注入 Claude:** 编写 MCP Server 将两个图谱的查询接口暴露给 ClaudeCode。
|
||||
|
||||
这种“重引擎索引 + 轻业务监控”的模式,能保证在 64GB 内存的本地环境下,ClaudeCode 既能秒回 API 调用问题,又能深刻理解你的业务代码耦合。你想尝试编写这个 MCP 路由器的代码吗?
|
||||
@@ -0,0 +1,25 @@
|
||||
# 目录结构
|
||||
- docs
|
||||
- Netease_AITA_AssetMaker.md:项目技术、设计细节入口文档,方便Agent来寻找
|
||||
-
|
||||
- Projects:UE工程目录。
|
||||
|
||||
|
||||
# 相关技术与容器
|
||||
- UE
|
||||
- UnrealMcp
|
||||
- Puerts
|
||||
- Puerts Editor
|
||||
- uecli
|
||||
- Readme的材质都是agent调用uecli做的 帮我生产材质 排版材质节点 帮我场景截图,帮我材质蓝图截图 帮我写readme 帮我提交仓库。 https://github.com/wlxklyh/UECLI
|
||||
- 通过蓝图转c++功能,让AI读懂蓝图
|
||||
- IDE Debug MCP
|
||||
- https://km.netease.com/v4/section/aigc/detail/blog/263683
|
||||
- cpp-debugger-cli
|
||||
- Docker
|
||||
- Gitea:工单以及版本管理。
|
||||
- OpenClaw:子节点部署,通过父节点进行控制。
|
||||
- SMB服务。
|
||||
- Obsidian Cli:文档管理。
|
||||
|
||||
## UE测试技术
|
||||
60
07-Other/AI/AI Agent/WY/AI全生命周期构思.md
Normal file
60
07-Other/AI/AI Agent/WY/AI全生命周期构思.md
Normal file
@@ -0,0 +1,60 @@
|
||||
# 前言
|
||||
现阶段其他部门都关注于自己工作领域,而忽视了项目全生命周期流程,一些问题往往会在领域交接时产生,之后的章节会逐步展开。在这之前我说这几点:
|
||||
- 主Agent与从Agent
|
||||
- 项目知识管理
|
||||
- 文档&提示词的质量
|
||||
- 记忆系统管理 与 Skill
|
||||
|
||||
## OpenClaw与多Agent
|
||||
现阶段因为各种限制不会出现全能性AI,所以建议使用主Agent与多个从Agent的方案。但对于公司而言,All In One的方案并不是最好选择,除了考虑避免All In Boom,还有就有就是记忆、上下文、方便迭代方面的考量。
|
||||
|
||||
>***另据我所知原生提供Node远程控制方案并且可以自己部署节点的只有OpenClaw。*** 除此之外全球的开源力量使得迭代速度指数级上升,似乎有可能成为新的linux,成为AI时代的agent 中继 Bridge。
|
||||
|
||||
基于以上几点我选择OpenClaw,以下是我个人建议架构:
|
||||
![[主Agent与从Agent.canvas]]
|
||||
项目组主要产出文档以及代码,使用主Agent的使用目的主要集中精炼文档、记忆、Skills、沟通其他Agent。
|
||||
其他点:
|
||||
1. Agent之间的沟通存在token消耗。目前已经我已经找到解决方案准备测试。
|
||||
2. 一些相关信息也建议汇总到主Agent中进行汇总,但需要注意使用分层结构,避免出现降智问题。
|
||||
|
||||
## 知识管理
|
||||
现阶段Markdown(之后简称MD) 因为高可读性、高信息密度、纯文本成为AI Agent的文档首选,。我个人推荐Obsidian作为MD的编辑与查看工具,且已有现成的MCP与CLI工具。(还有看板、数据表、画板、导图、流程图等功能)另外个人推荐使用使用git+oss的方式来做版本管理与协作,想简单可以使用SMB。
|
||||
|
||||
推荐使用多层结构进行管理,避免Context爆炸。这个技巧也可以使用在Skill中。结构参考:
|
||||
- AI策划助手_使用说明.md
|
||||
- 表结构
|
||||
- 道具表说明书.md
|
||||
- 道具表说明书_耕种畜牧.md
|
||||
- 道具表说明书_加工品消耗品.md
|
||||
- 道具表说明书_设施与建筑.md
|
||||
- 配表流程
|
||||
- skill_开单说明.md
|
||||
- skill_配表.md
|
||||
|
||||
## 记忆系统
|
||||
目前已知大致有下面3个流派:
|
||||
- 结构化知识图谱派:Evolver
|
||||
- 向量检索派:MemorySearch
|
||||
- 生成式摘要派:self-improving-agent
|
||||
|
||||
实际上记忆系统都是混合方案。面向多人的企业级方案才是焦点。
|
||||
|
||||
### “精耕细作” 式管理记忆
|
||||
多人使用场景下,往往会有堆积大量 “噪音”以及错误记忆,尽管有一些工具可以自动优化,但依然建议人工介入,让记忆(经验)更快“收敛”。
|
||||
|
||||
## 文档&提示词的质量
|
||||
某专家策划曾提出以下几个问题:
|
||||
1. 能力不同的人,编写的文档,是否会对AI的理解与执行产生较大影响?
|
||||
2. 程序需要多加限制还是少加限制?
|
||||
3. 什么样的文档适合AI来阅读?
|
||||
|
||||
我个人认为逻辑清晰认知高的人编写的文档与提示词,AI更容易理解意图并且能够更好的执行;
|
||||
程序应该关注每个功能(维度从function => Module)的输入与输出是否正确,所以应该让AI自己发挥除了硬杠杠与主要方向。
|
||||
AI写的文档往往比人写的更加简练,更高复用性,所以更适合AI阅读。
|
||||
|
||||
---
|
||||
# 策划
|
||||
TODO
|
||||
|
||||
# 程序
|
||||
TODO
|
||||
@@ -0,0 +1,32 @@
|
||||
| 分类 | 方案/工具名称 | 相关方 | 方案成熟度 | 完成度 | 验证状态 | 备注/说明 |
|
||||
| --------- | --------------------------- | ------------------ | ------ | ---- | ------ | -------------------------------------------------- |
|
||||
| 资源检查工具 | neoXassessor | 程序, 雷鹅, QA, 美术 | 互娱成熟方案 | 25% | 整合验证通过 | 说明 |
|
||||
| 模型缩减工具 | UE/Simplygon | 程序, 美术 | 第三方方案 | 0% | 整合验证通过 | ue/simplygon常规方案 |
|
||||
| 模型生产 | MATRIX AITA 炼丹炉 | 美术 | 互娱成熟方案 | 75% | 整合验证通过 | 说明 |
|
||||
| 资源转换 | UE4/5导NEOX工具 \| FBX, GLTF支持 | 美术 | 互娱成熟方案 | 25% | 整合验证通过 | 说明 |
|
||||
| (天空/光照) | UE UltraDynamicSky | 程序, 美术 | 第三方方案 | 0% | 整合验证通过 | 请填写需求设计链接 |
|
||||
| 反射 | 离线SSR | 程序, 美术 | 互娱成熟方案 | 0% | 调研或推进中 | 说明 |
|
||||
| (全局光照) | Lumen | 程序, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| (全局光照) | 引擎内置SSR | 程序, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| (全局光照) | Planar Reflections | 程序, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| (全局光照) | Cubemap | 程序, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 后处理 | 项目自定义 | 程序, 美术 | 项目定制开发 | 100% | 调研或推进中 | 说明 |
|
||||
| 头发、布料模拟 | Hair与Groom系统 | 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 头发、布料模拟 | Chaos布料系统 | 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 高级物理系统 | ABC缓存 (Alembic Ca...) | 程序, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 高级物理系统 | VAT顶点动画纹理 | 程序, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 高级物理系统 | Physics Constraint | 程序, 美术 | - | - | 整合验证通过 | 说明 |
|
||||
| 场景物理破坏 | Chaos物理系统 | 程序 | 引擎自带方案 | - | 调研或推进中 | 说明 |
|
||||
| 场景寻路 | UE5寻路导出插件 | 程序 | 互娱成熟方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 场景寻路 | 内置寻路系统 (Naviga...) | 程序 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 数字资产版本化管理 | Perforce | 策划, 程序, 雷鹅, QA, 美术 | 互娱成熟方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 数字资产版本化管理 | UniSVN - 自研加速版TortoiseSVN | 策划, 程序, 雷鹅, QA, 美术 | 互娱成熟方案 | 0% | 整合验证通过 | 说明 |
|
||||
| 数字资产版本化管理 | 互娱Gitlab研发效率工具 | 策划, 程序, 雷鹅, QA, 美术 | 互娱成熟方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 剧情编辑器工具 | 过场动画编辑器 (Seq...) | 策划, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 剧情编辑器工具 | Dialogue Plugin (官) | 策划, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 通过数据兼容性评估/支持分支 |
|
||||
| 动画压缩 | ACL | 策划, 美术 | 引擎自带方案 | 0% | 调研或推进中 | - |
|
||||
| 开放大世界RPG | ShaderWorld | 美术, 程序 | 第三方方案 | - | 整合验证通过 | [https://lo.host/UWpWbN/](https://lo.host/UWpWbN/) |
|
||||
| 模型LOD | Imposter | 美术 | 引擎自带方案 | 0% | 整合验证通过 | 说明 |
|
||||
| 模型LOD | UE自有LOD功能 | 美术 | 引擎自带方案 | 0% | 整合验证通过 | 说明 |
|
||||
| 可见性剔除 | 剔除距离体积 (Cull Di...) | 程序, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
| 可见性剔除 | 预计算可视体积功能 | 程序, 美术 | 引擎自带方案 | 0% | 调研或推进中 | 说明 |
|
||||
96
07-Other/AI/AI Agent/WY/MCP&Skill/POPO 云文档MCP&Skill.md
Normal file
96
07-Other/AI/AI Agent/WY/MCP&Skill/POPO 云文档MCP&Skill.md
Normal file
@@ -0,0 +1,96 @@
|
||||
# 简介
|
||||
**MATRIX-AITA POPO 云文档操作 Skill** 是公司内部首个公开的云文档编辑Skill。它彻底解决了以往 AI Agent “只能看、不能写”的痛点,通过强大的自动化能力,让 AI 助手真正拥有了替你处理文档、整理表格、甚至管理多维数据的“双手”,是提升团队办公效率的硬核神器。
|
||||
|
||||
## 功能介绍
|
||||
- 📄 **文档(深度创作与排版)** 你可以指挥 AI 从零开始撰写项目建议书或会议纪要,它不仅能生成文字,还精通排版逻辑。它支持自动插入 1 到 5 级标题、符号列表以及有序列表,让文档结构层次分明。除了创建新文档,你还可以让它在现有文档末尾追加灵感,或者随时远程重命名文档,让文档管理变得动动嘴就能搞定。
|
||||
- 📊 **表格(智能数据录入)** 繁琐的数据录入和报表更新现在可以全权交给 AI 处理,它能精准地在指定的单元格中填入内容。你可以要求它为关键数据标记背景色以示提醒,或者批量修改某一区域的字体格式,快速生成美观的进度表。即便面对海量数据,它也能通过“精准定位”修改特定数值,再也不用在大表中拉动滚动条寻找目标了。
|
||||
- 📝 **Markdown(专业技术文档生成)** 这是为开发者和技术同学准备的排版利器,AI 可以为你生成包含代码块、数学公式以及 Mermaid 流程图的高质量内容。它支持标准的 Markdown 语法,能够完美处理复杂的引用、分割线和链接,确保技术文档既专业又易读。无论是编写 README 还是整理技术方案,它都能确保输出的内容格式工整、开箱即用。
|
||||
- **🗄️ 多维表格(自动化数据中心)** 你可以把复杂的任务追踪、资产管理或人员名单交给 AI 来打理,它支持多维表格的各项核心操作。无论是新增一条任务记录,还是修改某个单选、日期字段的状态,AI 都能像数据库管理员一样精准执行。它还能帮你快速查询表内的现有记录,并根据逻辑自动汇总信息,让你的多维表格真正变成一个能自我更新的智能系统。
|
||||
- **👥 团队空间(无缝跨部门协作)** 该 Skill 突破了个人文档的限制,能够直接在公司各级“团队空间”中大显身手。它能自动识别团队的目录结构,在指定的页面或文件夹下创建子文档,并自动处理复杂的协作权限验证。这意味着你可以授权 AI 自动更新团队周报、维护公共知识库,让所有团队成员都能实时共享到 AI 辅助生成的最新成果。
|
||||
|
||||
---
|
||||
|
||||
## 操作步骤介绍
|
||||
### 1. 安装Skill
|
||||
进入 https://skills.netease.com/skills/skill_b67793104fac
|
||||
复制安装命令给Agent或者下载该Skill,最后让Agent帮你安装。
|
||||
### 2. 自动化授权(获取 Cookie)
|
||||
无需手动查代码,直接运行我们提供的脚本即可(也可让Agent帮忙运行):
|
||||
- **双击运行**:在 `tools` 目录中找到 `collect-cookies_GBK.bat` 并双击。
|
||||
- **告知 Agent**:登录完成后,回到对话窗口对 Agent 说 **“我登录了”**,它将自动完成后续的 Token 采集与保存。
|
||||
|
||||
### 3. 开始使用
|
||||
授权完成后,你就可以尝试给 Agent 下达类似下方的指令了:
|
||||
- _“把这个表格里 A1 到 D1 的背景色改成黄色。”_
|
||||
- _“读取多维表格‘项目进展’中所有状态为‘进行中’的记录。”_
|
||||
|
||||
## 备注
|
||||
该Skill现阶段仍处于开发阶段,如果遇到问题请加入 群号:7271072,@楼嘉杰来解决。
|
||||
|
||||
|
||||
---
|
||||
|
||||
Gitlab Token:uunSQeYYhBCAn2a5C55H
|
||||
ANTHROPIC_API_KEY
|
||||
CI/CD Trigger 8b84579d3405f7a16f86ac2f2bcb59
|
||||
|
||||
---
|
||||
|
||||
|
||||
|
||||
以下是修改后的 `.gitlab-ci.yml` 示例以及关键配置步骤:
|
||||
### 1. 准备工作(CI/CD 变量设置)
|
||||
在 GitLab 项目的 **Settings -> CI/CD -> Variables** 中,请确保已配置以下变量:
|
||||
- **`ANTHROPIC_API_KEY`**: 你的内部接口授权 Token。
|
||||
- **`GITLAB_TOKEN`**: 具有 API 写入权限的 Personal Access Token(用于回帖)。
|
||||
- **`ANTHROPIC_BASE_URL`**: 设置为 `https://openai.nie.netease.com/v1`(注意添加 `/v1` 后缀)。
|
||||
|
||||
### 2. 修改后的 `.gitlab-ci.yml` 脚本
|
||||
这个脚本会在 Issue 触发时运行,使用 Claude Code 尝试修复,并利用 GitLab API 将结果反馈到 Issue 讨论区。
|
||||
```yml
|
||||
claude_main_fix:
|
||||
stage: ai_fix
|
||||
image: node:20
|
||||
variables:
|
||||
GIT_STRATEGY: clone
|
||||
GIT_DEPTH: 0
|
||||
script:
|
||||
- npm install -g @anthropic-ai/claude-code
|
||||
- export ANTHROPIC_BASE_URL="https://openai.nie.netease.com/v1"
|
||||
|
||||
# 1. 配置 Git 机器人身份(必须,否则无法 commit)
|
||||
- git config --global user.email "claude-bot@yourcompany.com"
|
||||
- git config --global user.name "Claude AI Bot"
|
||||
|
||||
# 2. 切换到主分支并确保是最新状态
|
||||
# 注意:$CI_DEFAULT_BRANCH 通常是 main 或 master
|
||||
- git checkout $CI_DEFAULT_BRANCH
|
||||
- git pull origin $CI_DEFAULT_BRANCH
|
||||
|
||||
# 3. 运行 Claude Code 进行直接修复
|
||||
# 删除了“创建新分支”的指令,明确要求“直接修改”
|
||||
- |
|
||||
claude --yes "你现在处于项目根目录的主分支。请分析项目结构,直接修复 Issue #$ISSUE_IID。
|
||||
Issue 描述:$ISSUE_DESCRIPTION
|
||||
修复完成后,请直接保存文件,不要创建新分支。"
|
||||
|
||||
# 4. 提交并推送回主分支
|
||||
# 使用包含 Token 的 URL 进行推送,确保有权限
|
||||
- |
|
||||
git add .
|
||||
if git commit -m "fix(auto): 自动修复 Issue #$ISSUE_IID [skip ci]"; then
|
||||
git push "https://project_${CI_PROJECT_ID}_bot:${GITLAB_TOKEN}@${CI_SERVER_HOST}/${CI_PROJECT_PATH}.git" HEAD:$CI_DEFAULT_BRANCH
|
||||
echo "代码已直接推送到主分支。"
|
||||
else
|
||||
echo "没有发现需要修复的更改或提交失败。"
|
||||
fi
|
||||
allow_failure: true
|
||||
```
|
||||
|
||||
### 3. 核心改进说明
|
||||
- **接口重定向**:通过 `export ANTHROPIC_BASE_URL` 强制 CLI 访问网易内部代理接口。
|
||||
- **非交互式处理**:在 CI 环境中,Claude Code 会尝试直接执行指令。建议在指令中明确要求它“创建新分支”而非直接推送到主分支,以保证代码安全。
|
||||
- **自动回帖流**:
|
||||
- 脚本捕获了 Claude Code 的输出并存入 `repair_summary.txt`。
|
||||
- 使用 `curl` 调用 GitLab 的 `/notes` API。`$CI_API_V4_URL` 和 `$CI_PROJECT_ID` 是 GitLab CI 内置的变量,无需手动配置。
|
||||
- **上下文读取**:为了让修复更准确,你可以在指令中加入 `Check CLAUDE.md for project guidelines`,确保 AI 遵循你在项目中定义的规范。
|
||||
1
07-Other/AI/AI Agent/WY/MCP&Skill/易协作MCP & Skill.md
Normal file
1
07-Other/AI/AI Agent/WY/MCP&Skill/易协作MCP & Skill.md
Normal file
@@ -0,0 +1 @@
|
||||
http://redmineapi.nie.netease.com/
|
||||
@@ -1,232 +0,0 @@
|
||||
# 前言
|
||||
|
||||
# 易协作
|
||||
- 相关网址
|
||||
- [Redmine MCP Server (易协作)](https://modelspace.netease.com/mcphub?detail=redmine-mcp-server&namespace=public)
|
||||
- Auth & Token
|
||||
- https://console-auth.nie.netease.com/mymessage/mymessage
|
||||
- https://sa.nie.netease.com/docs/auth/%E8%BF%90%E7%BB%B4%E6%94%AF%E6%92%91/Auth/06-%E7%94%A8%E6%88%B7%E6%89%8B%E5%86%8C/04%E5%BC%80%E5%8F%91%E8%80%85%E5%B7%A5%E5%85%B7/API%20%E6%96%87%E6%A1%A3/02%20Auth%20API%20v2%20%E6%96%87%E6%A1%A3.md#%E8%83%8C%E6%99%AF
|
||||
|
||||
|
||||
# Redmine MCP Server (易协作)
|
||||
易协作工单管理的 MCP (Model Context Protocol) Server 实现,提供工单查询、详情查看、工单更新、项目管理等功能。
|
||||
|
||||
## [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E5%8A%9F%E8%83%BD%E7%89%B9%E6%80%A7)功能特性
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#-%E5%B7%A5%E5%85%B7-tools)🔧 工具 (Tools)
|
||||
- **query_issues**: 搜索/查询易协作工单,支持多种过滤条件(状态、指派人、时间范围、自定义字段等)
|
||||
- **get_issue_detail**: 获取单个工单的完整详情,包括描述内容和图片
|
||||
- **update_issue**: 更新/修改工单信息(状态、指派人、主题、描述、备注等),支持人名、邮箱前缀等多种指派人格式
|
||||
- **get_user_projects**: 获取用户有权限访问的易协作项目列表
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#-%E8%B5%84%E6%BA%90-resources)📚 资源 (Resources)
|
||||
- **redmine://config**: 服务器配置信息
|
||||
- **redmine://hosts**: 所有易协作实例及有权限的项目列表
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%EF%B8%8F-%E8%B5%84%E6%BA%90%E6%A8%A1%E6%9D%BF-resource-templates)🗂️ 资源模板 (Resource Templates)
|
||||
- **redmine://host/{host_name}**: 获取指定易协作实例的详细信息
|
||||
- **redmine://host/{host_name}/{project_name}**: 获取指定项目的详细信息(版本、状态、跟踪标签等)
|
||||
- **redmine://project/{project_name}**: 获取默认实例中项目的详细信息(需配置默认实例)
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#-%E6%8F%90%E7%A4%BA-prompts)💬 提示 (Prompts)
|
||||
- **issue_analysis**: 易协作工单助手,支持状态、优先级、指派人、趋势分析
|
||||
|
||||
## [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E4%BD%BF%E7%94%A8%E6%96%B9%E5%BC%8F)使用方式
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#streamablehttp-%E6%8E%A8%E8%8D%90)StreamableHTTP (推荐)
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"redmine-mcp-server": {
|
||||
"type": "streamableHttp",
|
||||
"url": "https://mcp.netease.com/servers/redmine-mcp-server/mcp/"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
#### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E6%8C%87%E5%AE%9A%E9%BB%98%E8%AE%A4%E6%98%93%E5%8D%8F%E4%BD%9C%E5%AE%9E%E4%BE%8B%E5%8F%AF%E9%80%89)指定默认易协作实例(可选)
|
||||
如果有多个易协作实例,可通过 `redmine-host` 参数指定默认实例,后续调用工具时可省略 `redmine_host` 参数:
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"redmine-mcp-server": {
|
||||
"type": "streamableHttp",
|
||||
"url": "https://mcp.netease.com/servers/redmine-mcp-server/mcp/",
|
||||
"headers": {
|
||||
"redmine-host": "dap-v4.pm.netease.com"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E8%AE%A4%E8%AF%81%E4%B8%8E%E9%89%B4%E6%9D%83)认证与鉴权
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E8%AE%A4%E8%AF%81%E6%96%B9%E5%BC%8F)认证方式
|
||||
- 通过请求头 `X-Access-Token` 提供用户Token(若在 Codemaker 中使用,无需自行设置)
|
||||
- 系统会验证Token的有效性并获取用户信息
|
||||
- Auth 文档(使用 Auth Key 换取 Token):[https://g.126.fm/04nIr8l](https://g.126.fm/04nIr8l)
|
||||
- Auth 平台:[https://console-auth.nie.netease.com/mymessage/mymessage](https://console-auth.nie.netease.com/mymessage/mymessage)
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E9%89%B4%E6%9D%83%E8%A7%84%E5%88%99)鉴权规则
|
||||
- 用户只能访问其有权限的易协作实例和项目
|
||||
- 权限判断基于用户邮箱是否在项目成员列表中
|
||||
|
||||
## [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E5%B7%A5%E5%85%B7%E8%AF%A6%E7%BB%86%E8%AF%B4%E6%98%8E)工具详细说明
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#query_issues---%E6%9F%A5%E8%AF%A2%E5%B7%A5%E5%8D%95)query_issues - 查询工单
|
||||
搜索/查询易协作工单信息,支持丰富的过滤条件。
|
||||
**参数:**
|
||||
|
||||
|参数|类型|必填|说明|
|
||||
|---|---|---|---|
|
||||
|`redmine_host`|string|视配置|易协作实例域名,已配置默认实例时可省略|
|
||||
|`project`|string|是|项目名称|
|
||||
|`filters`|object|否|过滤条件,详见过滤器说明|
|
||||
|`page`|integer|否|页码,默认 1|
|
||||
|`per_page`|integer|否|每页条数,默认 20,最大 100|
|
||||
|
||||
**支持的过滤器:**
|
||||
|
||||
|过滤器字段|操作符|说明|
|
||||
|---|---|---|
|
||||
|`issues_id`|`=`, `!`|工单ID,支持多个ID|
|
||||
|`subject`|`~`, `=`, `!`|主题关键词|
|
||||
|`status`|`=`, `!`, `o`, `c`|状态名称|
|
||||
|`assigned_to_id`|`=`, `!`|指派人(邮箱前缀或 `me`)|
|
||||
|`assigned_to_name`|`=`, `!`|指派人中文名称|
|
||||
|`tracker_id`|`=`, `!`|跟踪标签(名称或ID)|
|
||||
|`fixed_version_id`|`=`, `!`, `o`, `c`|目标版本(名称或ID)|
|
||||
|`updated_on`|`><`, `>=`, `<=`|更新时间|
|
||||
|`created_on`|`><`, `>=`, `<=`|创建时间|
|
||||
|`start_date`|`><`, `>=`, `<=`|开始日期|
|
||||
|`due_date`|`><`, `>=`, `<=`|截止日期|
|
||||
|`done_ratio`|`=`, `!`, `>=`, `<=`|完成度 (0-100)|
|
||||
|`estimated_hours`|`=`, `!`, `>=`, `<=`|预估工时|
|
||||
|`cf`|对象|自定义字段,键为字段ID|
|
||||
|
||||
**操作符说明:** `~` 模糊匹配,`=` 等于,`!` 不等于,`o` 开放状态,`c` 关闭状态,`><` 范围,`>=` 大于等于,`<=` 小于等于
|
||||
|
||||
**过滤器示例:**
|
||||
|
||||
```json
|
||||
{
|
||||
"subject": {"op": "~", "value": ["bug"]},
|
||||
"status": {"op": "=", "value": ["开发中"]},
|
||||
"assigned_to_id": {"op": "=", "value": ["me"]}
|
||||
}
|
||||
```
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#get_issue_detail---%E8%8E%B7%E5%8F%96%E5%B7%A5%E5%8D%95%E8%AF%A6%E6%83%85)get_issue_detail - 获取工单详情
|
||||
获取单个易协作工单的完整详情,包括基本信息、自定义字段、描述内容及图片。
|
||||
**参数:**
|
||||
|
||||
|参数|类型|必填|说明|
|
||||
|---|---|---|---|
|
||||
|`redmine_host`|string|视配置|易协作实例域名|
|
||||
|`issue_id`|integer|是|工单ID(如 #12278,传参时传数字 12278)|
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#update_issue---%E6%9B%B4%E6%96%B0%E5%B7%A5%E5%8D%95)update_issue - 更新工单
|
||||
更新/修改易协作工单信息。更新成功后会返回更新后的工单详情。
|
||||
系统会自动获取工单所属项目用于API安全校验,无需手动指定项目。
|
||||
**参数:**
|
||||
|
||||
|参数|类型|必填|说明|
|
||||
|---|---|---|---|
|
||||
|`redmine_host`|string|视配置|易协作实例域名|
|
||||
|`issue_id`|integer|是|工单ID|
|
||||
|`status`|string|否|工单状态名称,如: 新建/开发中/已解决/关闭|
|
||||
|`subject`|string|否|工单主题|
|
||||
|`description`|string|否|工单描述|
|
||||
|`notes`|string|否|工单备注/说明,追加到工单评论中|
|
||||
|`assigned_to_mail`|string|否|指派人,支持多种格式(见下方说明)|
|
||||
|`tracker`|string|否|跟踪标签名称,如: BUG/任务/需求|
|
||||
|`version`|string|否|目标版本号名称|
|
||||
|`start_date`|string|否|开始日期,格式: YYYY-MM-DD|
|
||||
|`due_date`|string|否|完成日期,格式: YYYY-MM-DD|
|
||||
|`estimated_hours`|number|否|预估工作量(小时)|
|
||||
|`author_mail`|string|否|提单作者,格式同指派人|
|
||||
|`follows`|string/array|否|跟进QA,支持单个字符串(逗号分隔多人)或数组|
|
||||
|`watcher_user_ids`|array|否|跟踪者用户ID列表,如: [386, 387]|
|
||||
|`custom_field`|object|否|自定义字段,格式: {"字段ID": "值"}|
|
||||
|
||||
**指派人格式兼容(`assigned_to_mail` / `author_mail` / `follows`):**
|
||||
|
||||
|输入格式|示例|处理方式|
|
||||
|---|---|---|
|
||||
|完整邮箱|`lien02@corp.netease.com`|直接使用|
|
||||
|邮箱前缀|`lien02`|自动补全为 `lien02@corp.netease.com`|
|
||||
|中文名|`李恩`|从项目成员缓存中匹配对应邮箱|
|
||||
|首字母+中文名|`L李恩`|去除首字母前缀后按中文名匹配|
|
||||
|
||||
**更新示例:**
|
||||
```json
|
||||
{
|
||||
"issue_id": 1509,
|
||||
"status": "开发中",
|
||||
"assigned_to_mail": "李恩",
|
||||
"notes": "已开始开发"
|
||||
}
|
||||
```
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#get_user_projects---%E8%8E%B7%E5%8F%96%E9%A1%B9%E7%9B%AE%E5%88%97%E8%A1%A8)get_user_projects - 获取项目列表
|
||||
获取当前用户有权限访问的所有易协作项目列表,按实例分组返回。
|
||||
**参数:** 无
|
||||
|
||||
|
||||
## [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E4%B8%BB%E8%A6%81%E5%8A%9F%E8%83%BD)主要功能
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E5%B7%A5%E5%8D%95%E6%9F%A5%E8%AF%A2)工单查询
|
||||
支持多种过滤条件:
|
||||
- 主题关键词搜索
|
||||
- 状态筛选(新建/开发中/关闭等)
|
||||
- 指派人筛选(支持邮箱前缀、中文名、`me`)
|
||||
- 跟踪标签/目标版本筛选(支持名称和ID)
|
||||
- 时间范围筛选(创建/更新/开始/截止日期)
|
||||
- 自定义字段筛选
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E5%B7%A5%E5%8D%95%E8%AF%A6%E6%83%85)工单详情
|
||||
- 查看工单完整信息(基本字段、自定义字段)
|
||||
- 支持描述内容中的图片展示
|
||||
- 显示跟踪者、关注者等协作信息
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E5%B7%A5%E5%8D%95%E6%9B%B4%E6%96%B0)工单更新
|
||||
- 修改工单状态、指派人、主题、描述等字段
|
||||
- 添加备注/评论
|
||||
- 修改目标版本、跟踪标签、日期、工时等
|
||||
- 设置跟进QA、跟踪者、自定义字段
|
||||
- 指派人支持中文名/邮箱前缀/完整邮箱等多种输入格式
|
||||
- 更新成功后自动返回更新后的工单详情
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E9%A1%B9%E7%9B%AE%E7%AE%A1%E7%90%86)项目管理
|
||||
- 获取用户有权限的项目列表
|
||||
- 查看项目元数据(状态、跟踪标签、版本等)
|
||||
- 支持多实例管理
|
||||
|
||||
### [](https://mcp-ui.netease.com/server/redmine-mcp-server?namespace=public#%E6%95%B0%E6%8D%AE%E7%BC%93%E5%AD%98)数据缓存
|
||||
- 自动数据更新机制
|
||||
- 本地缓存提升性能
|
||||
- 实时流式日志输出
|
||||
|
||||
## 工具
|
||||
|
||||
| 工具名称 | 工具描述 | 工具参数 |
|
||||
| ----------------- | ----------------------------------- | --------------------------------------------------------- |
|
||||
| query_issues | 搜索/查询获取易协作工单信息 | #redmine_host #project #filterspageper_page |
|
||||
| get_user_projects | 获取用户有权限访问的易协作项目列表 | 暂无参数 |
|
||||
| get_issue_detail | 获取单个易协作工单详情 | redmine_host*issue_id* |
|
||||
| update_issue | 更新/修改易协作工单信息,支持修改状态、指派人、主题、描述、备注等字段 | redmine_host*issue_id*statussubjectdescriptionnotes+10 更多 |
|
||||
## 资源
|
||||
|
||||
|资源名称|URI|描述|MIME类型|
|
||||
|---|---|---|---|
|
||||
|服务器配置|`redmine://config`|获取易协作 MCP 服务器的配置信息|application/json|
|
||||
|易协作实例-项目列表|`redmine://hosts`|获取所有易协作实例列表及有权限的项目列表|application/json|
|
||||
|
||||
## 模版资源
|
||||
|
||||
|模板名称|URI模板|描述|MIME类型|
|
||||
|---|---|---|---|
|
||||
|易协作实例详情|`redmine://host/{host_name}`|获取指定易协作实例的详细信息|application/json|
|
||||
|易协作项目详情|`redmine://host/{host_name}/{project_name}`|获取指定易协作实例中特定项目的详细信息,包括项目可用的版本、状态和跟踪标签|application/json|
|
||||
## 提示
|
||||
| | | |
|
||||
| -------------- | ------- | ------------------------------------- |
|
||||
| issue_analysis | 易协作工单助手 | #redmine_host #project #analysis_type |
|
||||
@@ -17,4 +17,193 @@ https://www.fab.com/listings/bb7df0df-a2e7-430f-a382-7c3b121bf767
|
||||
|
||||
## 部署方式
|
||||
1. 该网站项目使用gitea托管,使用CI/CD进行生成与部署。
|
||||
2. 部署到同IP Nodejs Docker容器中。
|
||||
2. 部署到同IP Nodejs Docker容器中。
|
||||
|
||||
# 资产批量下载方式
|
||||
将 https://github.com/legendary-gl/legendary 下载到./docs/legendary,之后学习里面的代码。这是一个第三方开源客户端,可以用来下载fab中的资产。
|
||||
1. 将相关内容写到./docs/legendary,并在skill.md添加引用。
|
||||
2. 检查这个项目是否存在安全问题。
|
||||
3. 尝试运行legendary来下载我的资产库,我会告诉你账号密码。
|
||||
4. 先下载10个按照Clouderve的结构将下载资产库上传到clouderve。
|
||||
|
||||
clouderve的目录结构:
|
||||
- Fab
|
||||
- UnrealEngine
|
||||
- Plugins
|
||||
- ProInstanceToolPlugin
|
||||
- UE5.5
|
||||
- UE5.6
|
||||
- UE5.7
|
||||
|
||||
---
|
||||
|
||||
在使用 **Claude Code** 或类似的 AI 开发工具编写爬取脚本时,绕过 Cloudflare 的核心在于**“去自动化特征”**。Cloudflare 的防护(如 5 秒盾、Turnstile)不仅检查你的 IP,还会深入分析你的浏览器指纹、TLS 握手以及行为模式。
|
||||
|
||||
以下是针对 2026 年技术环境,在 Claude Code 中实现避开风控的实战策略:
|
||||
|
||||
### 1. 使用具备“隐身”能力的浏览器框架
|
||||
|
||||
传统的 Selenium 或普通的 Puppeteer 极易被识别。你应该指示 Claude Code 使用以下增强库:
|
||||
|
||||
- **Playwright + Stealth 插件**:目前最主流的选择。
|
||||
|
||||
- **SeleniumBase (UC Mode)**:这是目前绕过 Cloudflare 最强的工具之一,其 `undetected-chromedriver` 模式能有效隐藏自动化痕迹。
|
||||
|
||||
- **npx Claude Code 提示词示例**:
|
||||
|
||||
> "请使用 Python 的 SeleniumBase 库,并开启 UC Mode(Undetected Mode)编写爬取脚本,以绕过目标的 Cloudflare 检测。"
|
||||
|
||||
|
||||
### 2. 模拟真实浏览器的指纹
|
||||
|
||||
Cloudflare 会检查你的 HTTP 请求头是否与底层的 TLS 握手特征匹配(例如,如果你自称是 Chrome 但 TLS 握手特征是 Python-requests,会被秒封)。
|
||||
|
||||
- **TLS 指纹模拟**:使用 `curl_cffi` 库,它可以模拟真实浏览器(如 Chrome 120+)的 TLS 握手特征。
|
||||
|
||||
- **请求头伪造**:确保 `User-Agent`、`Accept-Language`、`Sec-Ch-Ua` 等字段与真实浏览器完全一致。
|
||||
|
||||
|
||||
### 3. IP 质量与地理位置
|
||||
|
||||
即使代码再完美,数据中心的 IP(如 AWS, Google Cloud)也是 Cloudflare 的重点监控对象。
|
||||
|
||||
- **使用住宅代理(Residential Proxies)**:这些 IP 看起来像普通家庭用户。
|
||||
|
||||
- **轮换代理**:每次请求或每隔几次操作更换一次 IP。
|
||||
|
||||
- **IPv6 优先**:2026 年很多防护系统对 IPv6 的限制相对宽松,且资源更丰富。
|
||||
|
||||
|
||||
### 4. 绕过交互挑战 (CAPTCHA/Turnstile)
|
||||
|
||||
当 Cloudflare 弹出验证码时,纯代码通常无法处理。
|
||||
|
||||
- **验证码识别服务**:在代码中集成如 CapSolver 或 2Captcha 的 API。
|
||||
|
||||
- **Cloudflare Workers 代理**:有时通过部署在 Cloudflare 内部的 Workers 作为跳板(利用其内部信任机制)会有奇效,但这取决于目标的具体配置。
|
||||
|
||||
|
||||
---
|
||||
|
||||
### 综合架构建议
|
||||
|
||||
|**维度**|**推荐技术栈**|**作用**|
|
||||
|---|---|---|
|
||||
|**底层驱动**|`SeleniumBase (UC Mode)` 或 `Playwright`|隐藏 WebDriver 特征|
|
||||
|**网络请求**|`curl_cffi` (Python)|解决 TLS 指纹识别问题|
|
||||
|**IP 资源**|动态住宅代理 (Residential Proxies)|绕过 IP 频率限制和黑名单|
|
||||
|**行为模拟**|`B-Splines` 曲线模拟鼠标移动|模拟真人操作轨迹|
|
||||
|
||||
### Claude Code 实战操作小贴士:
|
||||
|
||||
在 Claude Code 终端中,你可以这样引导它:
|
||||
|
||||
1. **第一步:环境检查**。先让它写一个简单的脚本访问 `https://nowsecure.nl`(这是一个专门测试 Cloudflare 绕过程度的网站)并截图。
|
||||
|
||||
2. **第二步:迭代优化**。如果截图显示被拦截,要求它:“目前的脚本被拦截了,请尝试集成 `stealth` 插件并随机化延迟时间。”
|
||||
|
||||
3. **第三步:添加随机性**。
|
||||
|
||||
Python
|
||||
|
||||
```
|
||||
import time
|
||||
import random
|
||||
# 绝不要使用固定的 sleep(5)
|
||||
time.sleep(random.uniform(2.5, 7.8))
|
||||
```
|
||||
|
||||
|
||||
**重要提醒:** 爬取数据请务必遵守目标网站的 `robots.txt` 协议及当地法律法规。过度高频的爬取不仅会被封禁,还可能面临法律风险。
|
||||
|
||||
|
||||
# Clouderve
|
||||
我部署了一个Clouderve http://10.145.96.67:5212/ 用于提供artlib具体的资产下载。请学习 API以及相关文档,之后进行构建。
|
||||
|
||||
## 主要逻辑
|
||||
1. 构建一个工具使用fab的账号密码登录并下载资产(暂缓开发,目前手工下载,并上传到clouderve上)
|
||||
2. Artlib每过一年就对Clouderve进行扫描,之后将数据库中的项目与Clouderve项对应。确保用户可以下载到指定引擎版本的资产,
|
||||
3. 用户进入资产详细页面后,如果该资产在Clouderve上存在,页面按钮就是 下载,点击后显示下拉菜单,显示可现在的版本,选择版本后进行下载;如果没有则显示访问Fab,点击后跳转到fab资产的原界面。
|
||||
4.
|
||||
## Clouderve目录说明
|
||||
clouderve的目录结构:
|
||||
- Fab
|
||||
- UnrealEngine
|
||||
- Plugins
|
||||
- ProInstanceToolPlugin
|
||||
- UE5.5
|
||||
- UE5.6
|
||||
- UE5.7
|
||||
|
||||
说明
|
||||
1. UnrealEngine层级目录为资产种类,除此之外还可以是Unity、MetaHuman等。
|
||||
2. Plugins为资产类型与Fab资产分类有关,除此之外还可以Decal、Material、Texture。
|
||||
3. ProInstanceToolPlugin层级为具体的资产名文件夹,我这边上传了ProInstanceToolPlugin作为案例。
|
||||
4. UE5.5层级目录为引擎版本,目前我只打算托管UE4.27、UE5.0~最新版本。
|
||||
|
||||
## API文档
|
||||
- API
|
||||
- https://docs.cloudreve.org/zh/api/overview
|
||||
- [https://docs.cloudreve.org/zh/api/overview](https://cloudrevev4.apifox.cn/)
|
||||
|
||||
---
|
||||
|
||||
# Artlib的意义&提效
|
||||
1. 收集公司已经采购的资产包(Fab、Artstation等),方便团队以及公司其他美术同学快速找到资产包。使用RAG技术将Fab资产介绍信息整合成知识库,以进行更加精准的资产检索。
|
||||
1. 解决Muse非常难用的问题。ShareStore资产不够多的问题。
|
||||
2. 尽可能做到一次收集完所有资产,避免去Muse、ShareStore上来回搜索。
|
||||
3. 多网站资产汇总,只需要一个网站就能检索其他所有资源站的信息。
|
||||
4. Fab的资产信息具备高准确性,制作的知识库检索资产的准确性远超Muse、ShareStore。
|
||||
2. 资产包资产原子化管理,做到检索单个资产的同时,只需下载所需资产,无需下载整个资产包。
|
||||
1. 将下载的原子资产进行归类摆放,比如: /Plugins/ArtLib/Models/目录下,放置Foliage、Building、Prop等。
|
||||
3. 给之后的AI开发游戏流程提供弹药,AI可以快速找到合适的模型与资产。
|
||||
4. 维护团队封样库以及材质库信息,方便在工作时能及时察觉复用可能性,来进行复用。比如:李双在SVN上整理的各种分好类的资产库。
|
||||
5. 给林海开发的插件提供资产包资源 ,让用户可以直接在UE找到合适资产并直接使用。
|
||||
6. 给海量模型进行分类,为公司未来的生成模型AI提供训练数据。
|
||||
7. 提供资产点赞窗口,之后学习Muse每天采购点赞最多的资产,以此吸引用户来该平台抢占Muse空间。
|
||||
|
||||
# 开发计划
|
||||
1. Web开发
|
||||
1. 修复一些交互Bug。
|
||||
2. 资产点赞功能,机器人信息推送。
|
||||
3. Artstation等网站数据接入。
|
||||
4. 在服务端部署UE Editor,每个资产(模型、贴图……)进行视觉判断,打上合理标签。
|
||||
1. AI模型部署。
|
||||
2. UE Editor 群集方案实现。
|
||||
5. 借到财务账号,并使用爬虫爬取Fab资产。
|
||||
6. 服务器扩容,申请2个服务器用于存储数据,并且部署对象存储服务,保证数据不会嗝屁。
|
||||
2. UE插件开发
|
||||
1. 实现网页点击下载就能直接安装到UE目录。
|
||||
2. 实现资产包资产批量修改引用,实现资产整理功能。
|
||||
3. 林海Runtime Load资产插件接入Artlib服务。
|
||||
4. Plugin云端加载方案探索(通过网盘系统构建虚拟目录,再通过Webdav接入UE,可能会不稳定)
|
||||
3. 开发类似普罗米修斯的资产库客户端(WebApp)
|
||||
|
||||
---
|
||||
|
||||
# 启动器
|
||||
## 提示词
|
||||
我想制作一个类似Fab启动器,用于显示Artlib中的内容、另一个网站内容、以及连接UnrealEngine进行一些资产相关的操作(待实现)
|
||||
|
||||
### 主要功能
|
||||
1. 使用Netease OIDC进行身份验证。
|
||||
2. 显示Artlib内容,需要做好交互。启动器验证之后,会自动帮Artlib验证。
|
||||
3. 显示另一个网站AssetMaker的内容,需要做好交互。启动器验证之后,会自动帮Artlib验证。
|
||||
4. 连接UnrealEngine进行一些资产相关的操作(待实现)
|
||||
|
||||
### UI参考
|
||||
相关UI参考./docs/screenshots
|
||||
- 左边是一个可折叠的侧边导航栏,点击左下角的折叠按钮后,可以折叠。
|
||||
- 左边侧边栏方面放置3个按钮即可,Fab、角色生成 按钮、知识技能库
|
||||
- 折叠效果参考
|
||||
- fab_reference1.png
|
||||
- fab_reference1.5.png
|
||||
- Fab 按钮:点击后显示网站 D:\AI\Website\NeteaseAITA_Artlib,使用软连接获取对应的前端文件。
|
||||
- 角色生成 按钮:点击后显示外部网站。参考 LaunchHub_1.png
|
||||
- 知识技能库 按钮:点击后显示外部网站 https://ta.netease.com 。参考 LaunchHub_2.png
|
||||
|
||||
## 技术栈
|
||||
- WebApp框架:Electron、Electron-vite
|
||||
- 使用 npm create @quick-start/electron@latest 来建立脚手架。
|
||||
- 前端技术:TypeScript、Vue 3、Vite;Vue Router、Pinia;前端组件请使用Element Plus,使用pnpm install element-plus 进行安装;除此之外还有Lucide Icons, Tailwind CSS。
|
||||
- 工具函数:Axios、VueUse、pnpm
|
||||
|
||||
445
07-Other/AI/AI Agent/WY/Website/Netease AITA AssetMaker.md
Normal file
445
07-Other/AI/AI Agent/WY/Website/Netease AITA AssetMaker.md
Normal file
@@ -0,0 +1,445 @@
|
||||
# 提示词
|
||||
请你说中文,并使用中文编写文档。
|
||||
|
||||
我需要你发挥卓越的前端开发能力,对我指定的网站进行全方位的“像素级复刻”。这个复刻不仅包括静态页面的排版、配色和字体,还需要涵盖所有的交互细节、动画效果和响应式适配。我们将使用 **Vue 3** 框架和 **Vite** 构建工具。
|
||||
|
||||
我想制作一个AI 提示词 => 2D原画 => 参考图 => 3D模型生成的WorkFlow网站,功能与风格样式进行像素级复刻:
|
||||
- https://app.assethub.io/
|
||||
- https://app.assethub.io/inventory
|
||||
- https://app.assethub.io/workflow
|
||||
- https://app.assethub.io/texturing
|
||||
- https://app.assethub.io/image
|
||||
- https://app.assethub.io/settings/team/4430d872-b40f-4374-bfd0-936205e155aa
|
||||
- 视频
|
||||
- https://www.youtube.com/watch?v=h8fKTZDQ90s
|
||||
- https://www.youtube.com/watch?v=i95-KHATaEY
|
||||
## 迭代方式
|
||||
1. 你可以使用浏览器打开这个网站,我会帮你登录。
|
||||
2. 依次打开左边侧边按钮,读取各个页面的HTML结构、CSS信息。将截图存储在Screenshots,使用中文名。视频在github上寻找合适工具给我下载下来作为参考。
|
||||
3. 下载网站都用的图片的,并在复刻网站时用上。
|
||||
4. 搭建完前后端基础代码之后,先进行前端框架与效果迭代,之后反复对照截图、原始站点进行迭代,保证网站与原站点效果完全一致。
|
||||
5. 使用ObsidianCli 维护docs下的文档。
|
||||
|
||||
## 主要功能
|
||||
1. 该网站的主要功能是通过一套完整流程来生成3D模型,大致流程如下:
|
||||
1. 用户输入的参考图与提示来生成模型原画。
|
||||
2. 通过原画生成三视图。
|
||||
3. 使用三视图来生成3D模型。
|
||||
4. 3DMesh重拓扑。
|
||||
5. UV Unwrap
|
||||
6. auto texture
|
||||
2. 具备3D渲染功能,请根据参考原站点使用方案。
|
||||
3. 具备节点工作流,请使用合适的节点框架,可以考虑使用 https://github.com/comfy-org/ComfyUI 中的节点渲染与流程控制逻辑。
|
||||
1. 节点工作在线共同预览与编辑功能,实现协同工作。
|
||||
4. 简单的账户登录机制, 并且提供OpenID登录方式。
|
||||
## 技术选型
|
||||
我打算使用:
|
||||
前端技术:TypeScript、Vue 3、Vite;前端组件请使用Element Plus,使用pnpm install element-plus 进行安装。
|
||||
后端技术:nodejs、Fastify、Mongoose、fastify-jwt
|
||||
数据库:MongoDB、MySQL。
|
||||
工具函数:Axios、VueUse、pnpm
|
||||
文档管理:Obsidian Cli
|
||||
|
||||
## 文档目录结构
|
||||
- docs
|
||||
- Netease_AITA_AssetMaker.md:项目技术、设计细节入口文档,方便Agent来寻找
|
||||
- Screenshots:存放截图。
|
||||
- Videos:视频参考。
|
||||
- WebsiteSource:存放HTML、CSS相关信息。
|
||||
- Web:前端相关文档。
|
||||
- Server:后端相关文档。
|
||||
|
||||
## 复刻流程提示词
|
||||
### 0. 准备阶段:安装必要 Skills (工具)
|
||||
|
||||
为了精准地感知和复刻目标网站,请优先完成以下工具的安装和配置。如果尚未安装,请在本地环境中执行相关命令:
|
||||
|
||||
1. **视觉感知工具 (视觉分析核心)**
|
||||
* **Skill (依赖)**: `playwright` (用于 headless 浏览器截图、DOM 分析)
|
||||
* **安装命令**: `npm install playwright && npx playwright install`
|
||||
|
||||
2. **资源处理工具 (资产抓取核心)**
|
||||
* **Skill (依赖)**: `axios`, `mime-types` (用于下载图片、字体)
|
||||
* **安装命令**: `npm install axios mime-types`
|
||||
|
||||
3. **开发工具 (Vue 3 + Vite)**
|
||||
* **Skill (依赖)**: `@vitejs/plugin-vue` (如果在 Vite 项目中未配置)
|
||||
|
||||
---
|
||||
|
||||
### 1. 执行阶段:网站复刻工作流
|
||||
|
||||
请按照以下详细步骤执行任务。每完成一步,请向我反馈进度。
|
||||
|
||||
**目标网站 URL: [在此处替换为您要复刻的网站 URL,例如:https://example.com]**
|
||||
|
||||
#### 第一步:全面观察与分析 (Observation)
|
||||
1. **高清截图**: 使用 Playwright 对目标网站进行全长(Full Page)高清截图。同时,抓取以下状态的截图:
|
||||
* 特定组件(如 Button, Nav Item)的 `Hover` 状态。
|
||||
* 移动端(如 iPhone 12 Pro)的截图。
|
||||
2. **配色提取**: 分析截图,生成一份完整的 JSON 格式色板(Palette),包括主色、辅助色、文本色、背景色、边框色以及渐变色参数。
|
||||
3. **字体分析**: 确定目标网站使用的主要 WebFonts、字体大小(Font Size)、行高(Line Height)和字重(Font Weight)。
|
||||
4. **结构分析**: 使用 Playwright 导出核心 DOM 树的计算后样式(Computed Styles),特别是关键组件的 `padding`, `margin`, `display` (Flex/Grid) 属性和 `border-radius`。
|
||||
|
||||
#### 第二步:静态资源抓取 (Asset Collection)
|
||||
1. **文件下载**: 自动下载目标网站的所有:
|
||||
* SVG 图标
|
||||
* 图片资源(PNG/JPEG/WEBP)
|
||||
* 字体文件(WOFF2/WOFF)
|
||||
2. **目录整理**: 将下载的资产按照以下结构有序存入您的项目目录中(例如:`src/assets/images`, `src/assets/icons`, `src/assets/fonts`)。
|
||||
|
||||
#### 第三步:代码生成 (Vue 3 Component Generation)
|
||||
1. **项目脚手架**: 如果尚未在当前目录创建 Vite + Vue 3 项目,请创建一个:`npm init vite@latest . -- --template vue`。
|
||||
2. **组件拆解**: 将目标网页拆解为可复用的 Vue 组件(例如:`Header.vue`, `HeroSection.vue`, `Card.vue`, `Footer.vue`)。
|
||||
3. **样式复刻 (全都要)**:
|
||||
* 使用全局样式文件(如 `src/style.css`)定义色板变量和字体定义。
|
||||
* 在 Vue 组件中,使用 `<style scoped>` 编写 **完全一致** 的 CSS 样式。必须精确还原计算后的数值,不要估算。
|
||||
* 使用原生 CSS Flexbox 和 Grid 进行布局还原。
|
||||
|
||||
#### 第四步:动画与交互复刻 (Interaction & Animation)
|
||||
1. **动态特效**: 分析目标网站的关键动画(例如:页面滚动时的元素渐现、Header 缩放、Hover 时的微动效)。
|
||||
* 使用 CSS Transitions/Animations 或 Vue 3 的 `<Transition>` 组件精准还原。
|
||||
* 尽可能复刻其 `cubic-bezier` 缓动曲线和持续时间(Duration)。
|
||||
2. **交互状态**: 确保所有按钮、链接在 `Hover` 和 `Active` 状态下的像素细节与原站一致。
|
||||
|
||||
#### 第五步:自我校对 (Self-Correction)
|
||||
1. **组件预览**: 完成关键组件后,启动 Vite 开发服务器(`npm run dev`)。
|
||||
2. **视觉比对**: 将您生成的页面截图与原站截图进行叠加比对,特别注意边距和字体的偏差。
|
||||
3. **代码清理**: 删除不必要的脚手架代码(如 Vite 默认的 HelloWorld 组件)。
|
||||
|
||||
---
|
||||
|
||||
### 2. 最终输出物
|
||||
|
||||
1. 一个完整的、结构清晰的 Vue 3 + Vite 项目代码库。
|
||||
2. 在当前目录中可以成功运行 (`npm run dev`)。
|
||||
3. 生成的页面在视觉和交互上与目标网站达到 95% 以上的像素级匹配度。
|
||||
|
||||
---
|
||||
|
||||
现在,请开始按照工作流执行。如果有任何步骤遇到困难,或者需要我提供更具体的指令,请立即告知。
|
||||
|
||||
|
||||
|
||||
# 操作说明
|
||||
1. 在首页的Drop your concept art here,可以使用拖拽或点击的方式,上传模型的参考图。此时该区域会显示当前上传的图片。
|
||||
2. 点击下面的Clear清除图片;点击Generate 3D开始3D模型生成流程。按钮会显示成Generating,之后切换到Workflow页面。./docs/screenshots/workflow/Workflow_使用单张图片生成参考图与三视图_1.png
|
||||
3. Workflow界面主要由白板与节点系统构成。
|
||||
1. SourceImage节点
|
||||
1. Inputs为输入图片
|
||||
2. auto pipeline为是否进行自动流程,即 输入图片=> 参考图=> 三视图 => 3D模型 流程。
|
||||
3. AllowedModel为流程使用的模型设置。
|
||||
2. Pose节点
|
||||
1. 用于控制输出参考图的Pose。
|
||||
2. 节点Transform to Pose即可进行图片生成。
|
||||
|
||||
|
||||
任务:
|
||||
1. 你帮我实现以上操作逻辑。
|
||||
2. 实现Workflow页面的中的白板与节点功能,参考原始页面。
|
||||
3. 在后端实现AI服务接口。
|
||||
1. 可以接入Workflow中的AI服务(查询各个服务商的文档)
|
||||
2. 可以通过Netease AIGW接入AI服务。
|
||||
1. 参考以下网站,网站需要认证,打开浏览器,我来帮你登录。
|
||||
2. https://aigw.doc.nie.netease.com/21_%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/10_%E5%9F%BA%E6%9C%AC%E8%AF%B4%E6%98%8E/1_%E5%9F%BA%E6%9C%AC%E8%AF%B4%E6%98%8E.html
|
||||
3. https://aigw.doc.nie.netease.com/21_%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/11_%E8%BA%AB%E4%BB%BD%E8%AE%A4%E8%AF%81/2_%E8%BA%AB%E4%BB%BD%E8%AE%A4%E8%AF%81.html
|
||||
4. https://aigw.doc.nie.netease.com/21_%E5%BC%80%E5%8F%91%E6%8C%87%E5%8D%97/14_%E8%AE%A4%E8%AF%81%E4%B8%8E%E7%AE%A1%E7%90%86/%E8%AE%A4%E8%AF%81%E4%B8%8E%E7%AE%A1%E7%90%86.html
|
||||
5. https://aigw.doc.nie.netease.com/25_AIGW%E5%8A%9F%E8%83%BD%E6%8C%87%E5%8D%97/10_API%E6%8E%A5%E5%8F%A3.html
|
||||
6. https://aigw.doc.nie.netease.com/50_FAQ/2_API%E6%8A%A5%E9%94%99.html
|
||||
3. 可以通过 https://openai.nie.netease.com 的OpenAI、Claude样式的接口来接入服务(需要用户提供AUTH_TOKEN)
|
||||
4. WorkFlow中存放一些Workflows,里面有当前工作流信息,你可以参考 https://app.assethub.io/workflow/10757
|
||||
1. 生成完模型后,点击模型节点的View 3D Mesh就切换页面,在页面中载入3D模型,且模型带有贴图,使用PBR光照模型进行渲染。
|
||||
|
||||
---
|
||||
|
||||
|
||||
5. 使用ObsidianCli 维护docs下的文档。
|
||||
|
||||
## 主要功能
|
||||
1. 该网站的主要功能是通过一套完整流程来生成3D模型,大致流程如下:
|
||||
1. 用户输入的参考图与提示来生成模型原画。
|
||||
2. 通过原画生成三视图。
|
||||
3. 使用三视图来生成3D模型。
|
||||
4. 3DMesh重拓扑。
|
||||
5. UV Unwrap
|
||||
6. auto texture
|
||||
2. 具备3D渲染功能,请根据参考原站点使用方案。
|
||||
3. 具备节点工作流,请使用合适的节点框架,可以考虑使用[ https://github.com/comfy-org/ComfyUI ](https://github.com/Comfy-Org/ComfyUI_frontend)中的节点渲染与流程控制逻辑。
|
||||
1. 节点工作在线共同预览与编辑功能,实现协同工作。
|
||||
4. 简单的账户登录机制, 并且提供OpenID登录方式。
|
||||
## 技术选型
|
||||
我打算使用:
|
||||
前端技术:TypeScript、Vue 3、Vite;前端组件请使用Element Plus,使用pnpm install element-plus 进行安装。
|
||||
后端技术:nodejs、Fastify、Mongoose、fastify-jwt
|
||||
数据库:MongoDB、MySQL。
|
||||
工具函数:Axios、VueUse、pnpm
|
||||
文档管理:Obsidian Cli
|
||||
|
||||
## 文档目录结构
|
||||
- docs
|
||||
- Netease_AITA_AssetMaker.md:项目技术、设计细节入口文档,方便Agent来寻找
|
||||
- Screenshots:存放截图。
|
||||
- Videos:视频参考。
|
||||
- WebsiteSource:存放HTML、CSS相关信息。
|
||||
- Web:前端相关文档。
|
||||
- Server:后端相关文档。
|
||||
|
||||
---
|
||||
|
||||
>以上作废
|
||||
|
||||
# # 提示词
|
||||
请你说中文,并使用中文编写文档。
|
||||
|
||||
## 功能
|
||||
1. 该网站的主要功能是通过一套完整流程来生成3D模型,大致流程如下:
|
||||
1. 用户输入的参考图与提示来生成模型原画。
|
||||
2. 通过原画生成三视图。
|
||||
3. 使用三视图来生成3D模型。
|
||||
4. 3DMesh重拓扑。
|
||||
5. UV Unwrap
|
||||
6. auto texture
|
||||
2. 具备3D渲染功能,请根据参考原站点使用方案。
|
||||
3. 具备节点工作流,请使用合适的节点框架,可以考虑使用 https://github.com/comfy-org/ComfyUI 中的节点渲染与流程控制逻辑。
|
||||
1. 节点工作在线共同预览与编辑功能,实现协同工作。
|
||||
4. 简单的账户登录机制, 并且提供OpenID登录方式。
|
||||
## 技术选型
|
||||
我打算使用:
|
||||
前端技术:TypeScript、Vue 3、Vite;前端组件请使用Element Plus,使用pnpm install element-plus 进行安装。
|
||||
后端技术:nodejs、Fastify、Mongoose、fastify-jwt
|
||||
数据库:MongoDB
|
||||
工具函数:Axios、VueUse、pnpm
|
||||
文档管理:Obsidian Cli
|
||||
|
||||
## 文档目录结构
|
||||
- docs
|
||||
- Netease_AITA_AssetMaker.md:项目技术、设计细节入口文档,方便Agent来寻找
|
||||
- Screenshots:存放截图。
|
||||
- Videos:视频参考。
|
||||
- WebsiteSource:存放HTML、CSS相关信息。
|
||||
- Web:前端相关文档。
|
||||
- Server:后端相关文档。
|
||||
|
||||
|
||||
# 需求
|
||||
1. 在保证相关接口不变的情况下,重构./backend 中的服务端内容。
|
||||
2. 根据需求迭代./frontend
|
||||
|
||||
3. 简单登录,预留OIDC登录。
|
||||
4.
|
||||
|
||||
|
||||
## OIDC 参考代码
|
||||
```python
|
||||
#coding:UTF-8
|
||||
"""
|
||||
Requirements:
|
||||
1. Flask >= 0.10.1
|
||||
2. requests
|
||||
3. jwkest >= 1.1.7
|
||||
|
||||
Usage:
|
||||
python oidc_code_demo.py -H {listen_address} -p {listen_port}
|
||||
|
||||
Help:
|
||||
python oidc_code_demo.py -h
|
||||
"""
|
||||
import os
|
||||
import uuid
|
||||
from hashlib import md5
|
||||
import datetime
|
||||
from urllib import urlencode
|
||||
import json
|
||||
import requests
|
||||
from flask import Flask, request, jsonify, session, redirect
|
||||
|
||||
from jwkest.jwk import SYMKey
|
||||
from jwkest.jws import JWS
|
||||
from jwkest.jwk import load_jwks_from_url
|
||||
from jwkest.jws import NoSuitableSigningKeys
|
||||
|
||||
__revision__ = "0.01"
|
||||
__author__ = "chenxs@corp.netease.com"
|
||||
|
||||
OIDC_CLIENT_ID = ""
|
||||
OIDC_CLIENT_SECRET = ""
|
||||
OIDC_PROVIDER = "https://login.netease.com/connect"
|
||||
OIDC_AUTHORIZATION_SERVER = "https://login.netease.com/connect/authorize"
|
||||
OIDC_TOKEN_ENDPOINT = "https://login.netease.com/connect/token"
|
||||
OIDC_USERINFO_ENDPOINT = "https://login.netease.com/connect/userinfo"
|
||||
OIDC_SCOPE = "openid nickname email fullname dep title empno"
|
||||
OIDC_REDIRECT_URI = "https://127.0.0.1:5000/finish"
|
||||
OIDC_JWKS_URI = "https://login.netease.com/connect/jwks"
|
||||
OIDC_ALG = "HS256"
|
||||
|
||||
PYTHON_OIDC_DEMO = Flask(__name__)
|
||||
|
||||
@PYTHON_OIDC_DEMO.route("/", methods=['GET'])
|
||||
def index():
|
||||
"""index"""
|
||||
if 'username' in session:
|
||||
body = (
|
||||
u"</br><h2>OpenID Connect 鐧诲綍鎴愬姛銆<E5A79B></h2></br>"
|
||||
u"鎮ㄧ殑鐢ㄦ埛鍚嶆槸锛<EFBFBD>%s</br>"
|
||||
u"鎮ㄧ殑鍏ㄥ悕鏄細%s</br>"
|
||||
u"鎮ㄧ殑閭鏄細%s</br>"
|
||||
u"鎮ㄧ殑鑱屼綅鏄細%s</br>"
|
||||
u"鎮ㄧ殑閮ㄩ棬鏄細%s</br>"
|
||||
u"鎮ㄧ殑宸ュ彿鏄細%s</br>") % (
|
||||
session['username'], session.get('fullname', ''),
|
||||
session.get('email', ''), session.get('title', ''),
|
||||
session.get('dep', ''), session.get('empno', ''))
|
||||
body += u"<a href='/login'>鎴虫垜閲嶆柊鐧诲綍</a>"
|
||||
return body
|
||||
else:
|
||||
return u"<a href='/login'>鎴虫垜鐧诲綍</a>"
|
||||
|
||||
@PYTHON_OIDC_DEMO.route("/login", methods=['GET'])
|
||||
def login():
|
||||
"""AuthN Request"""
|
||||
session.clear()
|
||||
now = datetime.datetime.now().strftime("%s")
|
||||
session['uid'] = uuid.uuid4().hex
|
||||
session['state'] = session['uid']
|
||||
session['nonce'] = md5(session['uid'] + now).hexdigest()
|
||||
|
||||
authn_request_params = {
|
||||
'response_type': 'code',
|
||||
'client_id': OIDC_CLIENT_ID,
|
||||
'state': session['state'],
|
||||
'nonce': session['nonce'],
|
||||
'scope': OIDC_SCOPE,
|
||||
'redirect_uri': OIDC_REDIRECT_URI,
|
||||
#'prompt': 'login',
|
||||
'display': 'touch',
|
||||
}
|
||||
|
||||
redirect_url = "?".join([
|
||||
OIDC_AUTHORIZATION_SERVER, urlencode(authn_request_params)])
|
||||
|
||||
return redirect(redirect_url)
|
||||
|
||||
|
||||
def token_request(code):
|
||||
"""2. Token Request"""
|
||||
params = {
|
||||
'grant_type': 'authorization_code',
|
||||
'code': code,
|
||||
'redirect_uri': OIDC_REDIRECT_URI,
|
||||
'client_id': OIDC_CLIENT_ID,
|
||||
'client_secret': OIDC_CLIENT_SECRET,
|
||||
}
|
||||
_resp = requests.post(OIDC_TOKEN_ENDPOINT, data=params)
|
||||
return json.loads(_resp.text)
|
||||
|
||||
def id_token_verify(id_token, nonce=None):
|
||||
"""3. id token verify"""
|
||||
|
||||
now = int(datetime.datetime.now().strftime("%s"))
|
||||
if OIDC_ALG == "HS256":
|
||||
signed_keys = [SYMKey(key=OIDC_CLIENT_SECRET)]
|
||||
else:
|
||||
signed_keys = load_jwks_from_url(OIDC_JWKS_URI)
|
||||
|
||||
try:
|
||||
plain_id_token = JWS().verify_compact(id_token, signed_keys)
|
||||
except NoSuitableSigningKeys:
|
||||
# logger the id_token please
|
||||
return {'error': 'can not verify the id token'}
|
||||
|
||||
print "idtoken: %s" % plain_id_token
|
||||
if nonce:
|
||||
if (not plain_id_token.has_key('nonce')) or (
|
||||
plain_id_token['nonce'] != nonce):
|
||||
return {'error': 'id token nonce not correct'}
|
||||
if plain_id_token['iss'] != OIDC_PROVIDER:
|
||||
return {'error': 'id token iss not correct'}
|
||||
if plain_id_token['aud'] != OIDC_CLIENT_ID:
|
||||
return {'error': 'id token aud not correct'}
|
||||
if now >= int(plain_id_token['exp']):
|
||||
return {'error': 'id token expired'}
|
||||
|
||||
return {'id_token': plain_id_token}
|
||||
|
||||
|
||||
@PYTHON_OIDC_DEMO.route("/finish", methods=['GET'])
|
||||
def finish():
|
||||
"""
|
||||
1. AuthN Response
|
||||
2. Token Request
|
||||
3. id token verify
|
||||
4. userinfo request
|
||||
5. login user
|
||||
"""
|
||||
|
||||
# 1. AuthN Response
|
||||
try:
|
||||
code = request.args.get('code')
|
||||
if session['state']:
|
||||
state = request.args.get('state')
|
||||
if state != session['state']:
|
||||
return u"闈炴硶璇锋眰"
|
||||
except ValueError:
|
||||
return u"闈炴硶璇锋眰"
|
||||
# 2. Token Request
|
||||
token = token_request(code)
|
||||
print "token: %s" % token
|
||||
if token.has_key('error'):
|
||||
return u"鍑洪敊浜嗭細%s" % str(token)
|
||||
# 3. id token verify
|
||||
id_token_verified = id_token_verify(token['id_token'])
|
||||
if id_token_verified.has_key('error'):
|
||||
return id_token_verified['error']
|
||||
else:
|
||||
id_token = id_token_verified['id_token']
|
||||
|
||||
print "id_token: %s" % id_token
|
||||
|
||||
# 4. userinfo request
|
||||
_req_session = requests.Session()
|
||||
_req_session.headers.update({
|
||||
"Authorization": "Bearer %s" % token['access_token']})
|
||||
userinfo_req = _req_session.get(OIDC_USERINFO_ENDPOINT)
|
||||
userinfo = json.loads(userinfo_req.text)
|
||||
# login the user
|
||||
session['username'] = userinfo['nickname']
|
||||
session['email'] = userinfo['email']
|
||||
session['title'] = userinfo.get('title', '')
|
||||
session['empno'] = userinfo.get('empno', '')
|
||||
session['dep'] = userinfo.get('dep', '')
|
||||
session['fullname'] = userinfo.get('fullname', '')
|
||||
return redirect("/")
|
||||
|
||||
if __name__ == "__main__":
|
||||
import sys
|
||||
import argparse
|
||||
parser = argparse.ArgumentParser(
|
||||
usage='%(prog)s [options]', version='%(prog)s ' + str(__revision__))
|
||||
parser.add_argument(
|
||||
'-H', '--host', dest='host', type=str,
|
||||
help="Specify listening adress, default is 127.0.0.1")
|
||||
parser.add_argument(
|
||||
'-p', '--port', dest='port', type=int,
|
||||
help="Specify listening port, default is 5000")
|
||||
parser.add_argument(
|
||||
'-c', '--client_id', dest='client_id', type=str,
|
||||
help="oidc client_id is required.")
|
||||
parser.add_argument(
|
||||
'-s', '--client_secret', dest='client_secret', type=str,
|
||||
help="oidc client_secret is required.")
|
||||
|
||||
args = parser.parse_args()
|
||||
|
||||
host = args.host or '127.0.0.1'
|
||||
port = args.port or 5000
|
||||
OIDC_REDIRECT_URI = "http://%s:%s/finish" % (host, port)
|
||||
OIDC_CLIENT_ID = args.client_id
|
||||
OIDC_CLIENT_SECRET = args.client_secret
|
||||
if not OIDC_CLIENT_ID or not OIDC_CLIENT_SECRET:
|
||||
parser.print_help()
|
||||
parser.exit()
|
||||
|
||||
PYTHON_OIDC_DEMO.secret_key = "this is a random secret"
|
||||
PYTHON_OIDC_DEMO.debug = True
|
||||
PYTHON_OIDC_DEMO.run(host=host, port=port)
|
||||
```
|
||||
150
07-Other/AI/AI Agent/WY/Website/Netease AITA AuthBridge.md
Normal file
150
07-Other/AI/AI Agent/WY/Website/Netease AITA AuthBridge.md
Normal file
@@ -0,0 +1,150 @@
|
||||
# 提示词
|
||||
请你说中文,并使用中文编写文档。
|
||||
我打算使用使用Nodejs构建一个MCP SSE服务器,帮助OpenClaw以及其他Agent进行OIDC认证。并且编写对应的Skill,协助Agent使用该MCP。MCP 服务器充当了 **OIDC Client** 和 **凭证管理器**。
|
||||
|
||||
OIDC相关文档在./docs/Netease_OIDC.md,里面的链接可能需要认证,你可以打开浏览器等待我来帮你认证。
|
||||
#### 核心流程步骤:
|
||||
1. **触发**:Agent 在执行任务时发现需要访问 NetEase 内部接口。
|
||||
2. **调用 MCP**:Agent 调用 MCP 工具 `get_auth_url`。
|
||||
3. **生成状态**:MCP 生成一个随机 `state` 存入 MongoDB,并构建 OIDC 授权链接。
|
||||
4. **交互**:
|
||||
- **方案 A (手动)**:Agent 将链接发给用户,用户点击。
|
||||
- **方案 B (自动化)**:使用类似 `browser-use` 的 Skill,由 Agent 直接控制浏览器打开该链接,并等待用户在弹出的窗口中完成 SSO 登录。
|
||||
5. **回调**:用户登录成功后,NetEase OIDC 重定向到 MCP 的 Fastify 接口。
|
||||
6. **换取 Token**:MCP 后端用 `code` 换取 JWT,并将其与用户信息绑定存入 MongoDB。
|
||||
7. **通知/轮询**:Agent 通过 MCP 轮询或 SSE 得到“认证成功”状态,随后获取 JWT 进行业务操作。
|
||||
|
||||
---
|
||||
### 2. 技术实现细节
|
||||
#### 2.1 数据库设计 (Mongoose)
|
||||
主要用于维护认证状态和存储持久化的 Token。
|
||||
JavaScript
|
||||
```
|
||||
// models/AuthState.js
|
||||
const authStateSchema = new mongoose.Schema({
|
||||
state: { type: String, required: true, unique: true }, // 用于防止CSRF和匹配回调
|
||||
userId: { type: String, required: true }, // 对应 OpenClaw 的用户 ID
|
||||
status: { type: String, enum: ['pending', 'completed'], default: 'pending' },
|
||||
accessToken: String,
|
||||
idToken: String,
|
||||
refreshToken: String,
|
||||
expiresAt: Date,
|
||||
createdAt: { type: Date, expires: '10m', default: Date.now } // 10分钟后自动过期
|
||||
});
|
||||
```
|
||||
|
||||
#### 2.2 MCP 服务器核心逻辑 (Fastify + SSE)
|
||||
MCP 服务器需要同时支持 **SSE (与 Agent 通信)** 和 **HTTP (处理 OIDC 回调)**。
|
||||
- **MCP Tool: `get_netease_auth_url`** 输入:`userId` 输出:`authUrl`, `state` 逻辑:生成 OIDC URL,将 `state` 记录到 MongoDB。
|
||||
- **Fastify Route: `/api/callback`** 逻辑:
|
||||
1. 接收 `code` 和 `state`。
|
||||
2. 校验 `state` 是否在 MongoDB 中存在。
|
||||
3. 向 NetEase OIDC 换取 Token。
|
||||
4. 更新 MongoDB 中的状态为 `completed`。
|
||||
5. 通过 SSE 发送一个 `auth_success` 事件。
|
||||
#### 2.3 改进思路:自动化认证 (Browser-use 集成)
|
||||
当 OpenClaw(服务器端)与用户(本地客户端)不在同一台电脑时,直接调用系统命令(如 `open` 或 `start`)只会打开服务器端的浏览器,用户完全看不到。
|
||||
既然你提到用户开启了 **Chrome 远程调试模式 (`--remote-debugging-port=9222`)**,我们可以利用 **CDP (Chrome DevTools Protocol)** 协议来实现跨机器的“远程操控”。
|
||||
以下是针对你的 Node.js MCP 服务器的技术实现方案:
|
||||
|
||||
---
|
||||
##### 1. 核心架构:远程 CDP 连接
|
||||
要让运行在服务器上的 MCP 能够操控用户本地的 Chrome,流程如下:
|
||||
###### 挑战:网络连通性
|
||||
通常用户的电脑处于内网(NAT 之后),服务器无法直接访问用户的 `9222` 端口。
|
||||
- **方案 A (内网直连)**:如果两者在同一公司内网且 IP 互通,MCP 直接访问 `http://<User_IP>:9222`。
|
||||
- **方案 B (内网穿透)**:使用 `frp` 或 `ssh -R`。用户本地运行一个穿透工具,将本地的 `9222` 映射到服务器可见的某个端口(如服务器的 `19222`)。
|
||||
|
||||
---
|
||||
##### 2. 技术实现:MCP 服务器代码 (Node.js)
|
||||
在你的 Node.js 环境中,推荐使用 **Playwright**,因为它原生支持 `connectOverCDP`,非常适合这种场景。
|
||||
|
||||
###### 安装依赖
|
||||
```bash
|
||||
npm install playwright
|
||||
```
|
||||
|
||||
###### MCP 工具逻辑示例
|
||||
在 MCP 中编写一个 `open_remote_auth_page` 的工具:
|
||||
```ts
|
||||
import { chromium } from 'playwright';
|
||||
|
||||
async function openRemoteBrowser(userIp, authUrl) {
|
||||
// 1. 连接到用户本地开启了调试模式的浏览器
|
||||
// 假设通过穿透或直连,地址为 userIp:9222
|
||||
const browser = await chromium.connectOverCDP(`http://${userIp}:9222`);
|
||||
|
||||
// 2. 获取当前的上下文(通常是用户正开着的浏览器窗口)
|
||||
const defaultContext = browser.contexts()[0];
|
||||
const page = await defaultContext.newPage();
|
||||
|
||||
// 3. 导航到 NetEase OIDC 认证页面
|
||||
await page.goto(authUrl);
|
||||
|
||||
// 4. (可选) 监听 URL 跳转,自动化部分流程
|
||||
// 比如检测到跳转回 callback URL 时,自动告知 Agent
|
||||
page.on('framenavigated', frame => {
|
||||
const url = frame.url();
|
||||
if (url.includes('your-callback-url')) {
|
||||
console.log('用户已完成认证!');
|
||||
// 这里可以触发 MongoDB 状态更新或 SSE 通知
|
||||
}
|
||||
});
|
||||
|
||||
// 注意:不要执行 browser.close(),否则会关掉用户的整个 Chrome
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
##### 3. 针对用户的操作指南 (Skill 配合)
|
||||
为了让这个 Skill 成功运行,Agent 需要引导用户做两件事:
|
||||
1. **启动 Chrome 调试模式**: 用户需要在本地终端执行:
|
||||
```bash
|
||||
# macOS
|
||||
/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome --remote-debugging-port=9222
|
||||
```
|
||||
2. **提供 IP/端口信息**: Agent 可以询问用户:“请提供你本地机器的 IP 地址(或内网穿透后的地址)”。
|
||||
|
||||
---
|
||||
##### 4. 更优雅的改进方案:WebRTC 或轻量级“领航员”
|
||||
如果让用户手动输入 IP 比较繁琐,你可以编写一个 **“Claw-Bridge”**(一个几十行代码的本地 Node.js 脚本或 Go 小程序):
|
||||
- **工作原理**:
|
||||
1. 用户在本地运行 `Claw-Bridge`。
|
||||
2. `Claw-Bridge` 启动后通过 WebSocket 连接到你的 Fastify 服务器(建立长连接)。
|
||||
3. 当 MCP 需要打开网页时,向该 WebSocket 发送指令。
|
||||
4. `Claw-Bridge` 在用户本地执行 `open <URL>`。
|
||||
|
||||
**优点**:
|
||||
- **无需开启远程调试端口**(安全性更高,不会暴露浏览器的控制权给整个网络)。
|
||||
- **无需固定 IP**(只要有网络,长连接就能通)。
|
||||
- **权限最小化**:本地程序只负责“打开链接”,不涉及控制浏览器内部。
|
||||
##### 5. 总结建议
|
||||
- 如果你追求**深度自动化**(比如想让 Agent 帮用户自动填表):使用 **Playwright + CDP**。
|
||||
- 如果你只追求**方便用户点击**(仅仅是弹出一个认证页):编写一个轻量级的 **本地桥接脚本** 配合你的 Fastify 服务器,通过 WebSocket 转发“打开页面”的请求。
|
||||
---
|
||||
### 3. MCP Skill (工具定义) 示例
|
||||
在 MCP 服务器中定义的工具描述:
|
||||
```
|
||||
{
|
||||
"name": "authenticate_netease",
|
||||
"description": "当需要访问网易内部服务但未授权时使用。该工具会返回一个认证链接,必须引导用户完成认证。",
|
||||
"inputSchema": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"user_id": { "type": "string", "description": "当前用户的唯一标识" }
|
||||
},
|
||||
"required": ["user_id"]
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
---
|
||||
### 4. 安全建议
|
||||
1. **PKCE (Proof Key for Code Exchange)**:既然是 Node.js 后端,务必开启 PKCE。在生成授权链接时生成 `code_verifier` 并存储在 MongoDB,回调时带上。
|
||||
2. **JWT 验证**:使用 `fastify-jwt` 验证从 OIDC 拿到的 `id_token` 的签名,确保其确实来自网易的认证服务。
|
||||
3. **State 绑定**:确保回调时的 `state` 只能被使用一次,防止重放攻击。
|
||||
4. **环境隔离**:建议在 MongoDB 中区分开发环境和生产环境的 `client_id`。
|
||||
## 技术选型
|
||||
我打算使用:
|
||||
后端技术:nodejs、Fastify、Mongoose、fastify-jwt
|
||||
数据库:MongoDB
|
||||
44
07-Other/AI/AI Agent/WY/主Agent与从Agent.canvas
Normal file
44
07-Other/AI/AI Agent/WY/主Agent与从Agent.canvas
Normal file
@@ -0,0 +1,44 @@
|
||||
{
|
||||
"nodes":[
|
||||
{"id":"6cda312f1671af71","type":"group","x":40,"y":-100,"width":640,"height":480,"label":"艺设主Agent主机"},
|
||||
{"id":"dc77b937f7c566ed","type":"group","x":200,"y":-60,"width":460,"height":420,"label":"主机内从属该项目的其他独立Agent"},
|
||||
{"id":"e0be32c1a73875dc","type":"text","text":"视频","x":420,"y":90,"width":100,"height":60},
|
||||
{"id":"2cebc34d3a7c245c","type":"text","text":"2D原画","x":420,"y":180,"width":100,"height":60},
|
||||
{"id":"7882c46e998f881a","type":"text","text":"项目组主Agent","x":-183,"y":120,"width":140,"height":60},
|
||||
{"id":"ea7c8aa0bff2fa90","type":"text","text":"GameEngine","x":-183,"y":480,"width":140,"height":60},
|
||||
{"id":"18373fd6585e719c","type":"text","text":"N个程序Agent实例","x":-577,"y":480,"width":195,"height":60},
|
||||
{"id":"b34e85d1dd50f9c8","type":"text","text":"GUI","x":420,"y":0,"width":100,"height":60},
|
||||
{"id":"a9b698ceb5dd6a73","type":"text","text":"程序员","x":-520,"y":360,"width":82,"height":60},
|
||||
{"id":"d5eee5e43fd23a22","type":"text","text":"AI PM","x":-522,"y":250,"width":85,"height":60},
|
||||
{"id":"5be324303b104387","type":"text","text":"结构化文档","x":-539,"y":120,"width":120,"height":60},
|
||||
{"id":"28097b51d705a955","type":"text","text":"策划","x":-340,"y":-30,"width":62,"height":60},
|
||||
{"id":"8494558c61aa8d2a","type":"text","text":"3D模型","x":420,"y":280,"width":100,"height":60},
|
||||
{"id":"833c175da3d2a493","type":"text","text":"艺设PM","x":220,"y":120,"width":100,"height":60},
|
||||
{"id":"ac74ea6efda6eef5","type":"text","text":"艺设主Agent","x":60,"y":120,"width":120,"height":60},
|
||||
{"id":"981296901b8023dc","type":"text","text":"TA","x":600,"y":280,"width":50,"height":60}
|
||||
],
|
||||
"edges":[
|
||||
{"id":"0d9b7a20bf3155e8","fromNode":"7882c46e998f881a","fromSide":"right","toNode":"ac74ea6efda6eef5","toSide":"left"},
|
||||
{"id":"075e967cbfa97ab2","fromNode":"ac74ea6efda6eef5","fromSide":"left","toNode":"7882c46e998f881a","toSide":"right"},
|
||||
{"id":"2161b83cbe328d2c","fromNode":"ac74ea6efda6eef5","fromSide":"right","toNode":"833c175da3d2a493","toSide":"left"},
|
||||
{"id":"228740714a1239b1","fromNode":"2cebc34d3a7c245c","fromSide":"bottom","toNode":"8494558c61aa8d2a","toSide":"top"},
|
||||
{"id":"3b2a3d2f0759a20f","fromNode":"833c175da3d2a493","fromSide":"right","toNode":"b34e85d1dd50f9c8","toSide":"left"},
|
||||
{"id":"c4f1151359441b76","fromNode":"833c175da3d2a493","fromSide":"right","toNode":"e0be32c1a73875dc","toSide":"left"},
|
||||
{"id":"e4f544cdbdf54bf0","fromNode":"833c175da3d2a493","fromSide":"right","toNode":"2cebc34d3a7c245c","toSide":"left"},
|
||||
{"id":"a0ddf315a9a36a4b","fromNode":"833c175da3d2a493","fromSide":"right","toNode":"8494558c61aa8d2a","toSide":"left"},
|
||||
{"id":"1af4431a8eece637","fromNode":"8494558c61aa8d2a","fromSide":"right","toNode":"981296901b8023dc","toSide":"left"},
|
||||
{"id":"a923f220037feb30","fromNode":"e0be32c1a73875dc","fromSide":"right","toNode":"981296901b8023dc","toSide":"left"},
|
||||
{"id":"98dd6721df277382","fromNode":"b34e85d1dd50f9c8","fromSide":"right","toNode":"981296901b8023dc","toSide":"left"},
|
||||
{"id":"de59526c9ed59eaa","fromNode":"981296901b8023dc","fromSide":"bottom","toNode":"ea7c8aa0bff2fa90","toSide":"top"},
|
||||
{"id":"d5b792fcbc7396e8","fromNode":"5be324303b104387","fromSide":"right","toNode":"7882c46e998f881a","toSide":"top","label":"精炼成策划Skills"},
|
||||
{"id":"a262dcd90f1680bf","fromNode":"7882c46e998f881a","fromSide":"bottom","toNode":"ea7c8aa0bff2fa90","toSide":"top"},
|
||||
{"id":"cabfd5a821aa5c8a","fromNode":"18373fd6585e719c","fromSide":"right","toNode":"ea7c8aa0bff2fa90","toSide":"left"},
|
||||
{"id":"f4873708803fc309","fromNode":"5be324303b104387","fromSide":"bottom","toNode":"d5eee5e43fd23a22","toSide":"top"},
|
||||
{"id":"3ea49ab37b7060a6","fromNode":"d5eee5e43fd23a22","fromSide":"bottom","toNode":"a9b698ceb5dd6a73","toSide":"top"},
|
||||
{"id":"0ec96114f463b257","fromNode":"a9b698ceb5dd6a73","fromSide":"bottom","toNode":"18373fd6585e719c","toSide":"top","label":"APCX"},
|
||||
{"id":"1f8399d6f211376b","fromNode":"a9b698ceb5dd6a73","fromSide":"right","toNode":"ea7c8aa0bff2fa90","toSide":"left"},
|
||||
{"id":"6e8fa0ebbc7f7930","fromNode":"28097b51d705a955","fromSide":"left","toNode":"5be324303b104387","toSide":"top","label":"BrainMaker"},
|
||||
{"id":"3178e9ba6ba35eb9","fromNode":"28097b51d705a955","fromSide":"right","toNode":"7882c46e998f881a","toSide":"top"},
|
||||
{"id":"2b2ba8c27a037d28","fromNode":"7882c46e998f881a","fromSide":"left","toNode":"5be324303b104387","toSide":"right"}
|
||||
]
|
||||
}
|
||||
@@ -69,4 +69,52 @@ OpenClaw待探索:
|
||||
POPO失忆:
|
||||
1. Skill找不到。
|
||||
2. POPO 没有识别 ,Channel找不到。机器人不知道 popo就是 moltbot-popo。
|
||||
3. 机器人只认识 loujiajie@corp.netease.com 不认识楼嘉杰
|
||||
3. 机器人只认识 loujiajie@corp.netease.com 不认识楼嘉杰
|
||||
|
||||
视频学习:
|
||||
1. [解剖小龍蝦 — 以 OpenClaw 為例介紹 AI Agent 的運作原理 - YouTube](https://www.youtube.com/watch?v=2rcJdFuNbZQ)
|
||||
2. https://space.bilibili.com/28321599?spm_id_from=333.337.search-card.all.click
|
||||
1. https://www.bilibili.com/video/BV1VzfoBFE5w/?spm_id_from=333.1387.homepage.video_card.click&vd_source=a5a1507212662f8883c096200c37b6bd
|
||||
|
||||
|
||||
#
|
||||
1. [x] 程世纪 Skill 接入
|
||||
2. [x] Freddy OpenClaw自拍Skill加入。
|
||||
3. [x] 程世纪Skill 添加发送文件到POPO的命令。
|
||||
4. [x] 思考级别修改 /think
|
||||
5. [ ] 远程控制浏览器的能力
|
||||
6. [ ] 记忆优化技能(TODO:需要加入确认内容):https://github.com/win4r/openclaw-workspace
|
||||
7. [ ] 三层记忆?
|
||||
1. [ ] 【openclaw分层索引版一键安装包!win系统mac系统节省模型80%token消耗多轮对话三层时间记忆索引clawhub技能演示AI借鉴openviking】 https://www.bilibili.com/video/BV1XjPFzjEg3/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
2. [ ] 【拒绝暴力压缩!分层索引版 OpenClaw 10轮省48% → 50轮省76% 越长越省,关键决策永不丢失(已开源)】 https://www.bilibili.com/video/BV1hcAQzsES9/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
3. [ ] https://www.bilibili.com/video/BV1JrNKz7EUv/?spm_id_from=333.337.search-card.all.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
8. [ ] LanceDB与插件:
|
||||
9. [ ] OpenClaw 卡死问题解决。编写守护进程软件
|
||||
10. [ ] OpenClaw下载文件管理。
|
||||
11. [ ] OpenClaw Docker部署。
|
||||
12. [x] Grp账号申请
|
||||
13. [x] ~~grp 账号与龙虾关联问题解决~~。 https://open.popo.netease.com/docs/api/robot/msg-send
|
||||
14. [ ] Skill
|
||||
1. [ ] AI PM
|
||||
1. [x] 多用户同时使用(stdIO)
|
||||
2. [ ] Cookie获取优化。
|
||||
3. [ ] HttpStream改造。
|
||||
4. [ ] 自动化拆单 易协作上的
|
||||
2. [ ] POPO 云文档 修改。
|
||||
15. [ ] MutiAgent
|
||||
1. [x] POPO 多Agent问题查找。
|
||||
2. [x] 多Agent A2A协议。
|
||||
16. [ ] OpenClaw 体验优化
|
||||
1. [x] Subagent规则
|
||||
17. [x] OpenClaw ACP 控制其他智能体。 https://www.bilibili.com/video/BV1X4wAzEEMe/?spm_id_from=333.1387.upload.video_card.click&vd_source=d47c0bb42f9c72fd7d74562185cee290
|
||||
18. [ ] OpenClaw控制 其他OpenClaw。
|
||||
|
||||
- 视频
|
||||
- [ ] 【🚀OpenClaw重磅更新!浏览器自动化终极方案:龙虾自动接管Chrome浏览器,无需重复登录账号,工作流打包成可复用Skill!【龙虾教程】】 https://www.bilibili.com/video/BV1yVwXzGEbL/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- [ ] 【🚀OpenClaw高级使用经验分享!2026年最强生产力!五分钟打造多Agent协作编程开发团队!模型容灾机制深度配置+云端Gateway操控本地macOS!】 https://www.bilibili.com/video/BV1pScgzXEB7/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- [x] ***【🚀OpenClaw高级玩法之工作区优化+三大Agent深度解析!龙虾保姆级教程让你学会Persistent Agent、Sub-Agent和ACP Agent】*** https://www.bilibili.com/video/BV1X4wAzEEMe/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- [ ] 【🚀OpenClaw高级进阶技巧分享!模型精选策略+记忆系统优化经验+深度搜索集成+Gateway崩溃自动修复!Claude Code自动读日志修Bug重启验证】 https://www.bilibili.com/video/BV1pefHB1ENJ/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- [ ] 【🚀【OpenClaw教程】OpenClaw高级玩法:记忆蒸馏+Skill固化+模型降级!Token成本直降40%,效率倍增!便宜模型也能完成复杂任务!龙虾教程】 https://www.bilibili.com/video/BV1WbPTzHEaa/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- [ ] 【🚀OpenClaw最强大脑:GPT-5.4深度实测!Agent能力强到离谱!自主完成多轮长链路复杂任务!实测可全自动为其他设备部署小龙虾!OpenClaw教程】 https://www.bilibili.com/video/BV1PdPHzFEFJ/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
- [ ] 【Claude Code 内部实践:Skill 编写的 9 个最佳实践-哔哩哔哩】 https://b23.tv/0Brym4l
|
||||
- [ ] 【🚀解锁OpenClaw多Agent高级玩法!Token消耗直接减半,这才是正确的使用方式!不同任务分配不同模型,独立Session、独立记忆,独立工作空间】 https://www.bilibili.com/video/BV1dqffBMEcg/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e
|
||||
|
||||
93
07-Other/AI/Obsidian/Obsidian CLI.md
Normal file
93
07-Other/AI/Obsidian/Obsidian CLI.md
Normal file
@@ -0,0 +1,93 @@
|
||||
# Obsidian CLI
|
||||
|
||||
## 参考资料
|
||||
|
||||
- [Obsidian CLI 详细教程:官方命令行工具,激进拥抱智能体,高效 + 自动化 + 降低 Token 消耗](https://www.bilibili.com/video/BV19TPXzAEtW/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e)
|
||||
- [如何为 Obsidian 配置 AI Agent?9 个必备 Skill 详解与安装指南](https://www.bilibili.com/video/BV14d9TBiE1S/?share_source=copy_web&vd_source=fe8142e8e12816535feaeabd6f6cdc8e)
|
||||
|
||||
## 配置步骤
|
||||
|
||||
### 1. 开启 CLI
|
||||
|
||||
Obsidian → 小齿轮(Settings)→ 开启**命令行界面**
|
||||
|
||||
### 2. 安装 Claudian 插件(可选)
|
||||
|
||||
Obsidian 适配 Claude Code 的插件,提供更好的集成体验。
|
||||
|
||||
- 仓库:[Claudian](https://github.com/YishenTu/claudian)
|
||||
- 安装后需配置自定义变量
|
||||
|
||||
### 3. 安装 Obsidian Skills
|
||||
|
||||
官方 Skill 集合,包含 Markdown、Bases、Canvas、CLI、Defuddle 等能力。
|
||||
|
||||
- 仓库:[obsidian-skills](https://github.com/kepano/obsidian-skills)
|
||||
- Defuddle 需要全局安装依赖:`npm install -g defuddle`
|
||||
|
||||
### 4. 安装绘图增强 Skill
|
||||
|
||||
支持 Excalidraw、Mermaid、Canvas 等可视化图表生成。
|
||||
|
||||
- 仓库:[axton-obsidian-visual-skills](https://github.com/axtonliu/axton-obsidian-visual-skills)
|
||||
|
||||
## 其他 Skill
|
||||
|
||||
| Skill | 用途 | 仓库 |
|
||||
| --- | --- | --- |
|
||||
| tutor-skills | 学习辅助 | [GitHub](https://github.com/bevibing/tutor-skills) |
|
||||
|
||||
## 外部大型代码库索引策略(以 UE 为例)
|
||||
|
||||
针对 Unreal Engine 这种体量的外部依赖,全量索引通常不是最优解。以下是三种可行策略:
|
||||
|
||||
### 方案一:软链接(最简单)
|
||||
|
||||
在项目根目录创建指向引擎源码的软链接,让索引工具将其视为项目内目录。
|
||||
|
||||
```bash
|
||||
ln -s /Users/Shared/Epic\ Games/UE_5.4/Engine ./UE_Source_Link
|
||||
```
|
||||
|
||||
> [!warning] 注意
|
||||
> - 务必在 `.gitignore` 中忽略该目录
|
||||
> - UE 源码文件量巨大,索引时间可能极长
|
||||
|
||||
### 方案二:多图谱架构(性能最优)
|
||||
|
||||
将引擎(静态)和项目(动态)分离为两个独立图谱,通过桥接查询关联。
|
||||
|
||||
1. 对 UE 引擎预生成 `engine_graph.db`(一次性)
|
||||
2. 对项目生成 `project_graph.db`(持续更新)
|
||||
3. 在 MCP Server 中实现双向查询:项目类 → 发现继承关系 → 自动去引擎图谱检索基类定义
|
||||
|
||||
### 方案三:指定多路径参数
|
||||
|
||||
```bash
|
||||
graphify . /path/to/external/ue_source --output ./unified_graph
|
||||
```
|
||||
|
||||
> [!note] 弊端
|
||||
> 每次扫描都会重新处理外部目录,除非工具支持增量索引。
|
||||
|
||||
### 剪枝策略:减少索引噪音
|
||||
|
||||
对 UE 外部目录索引时应进行**语义剪枝**,提升 Claude 回复精准度:
|
||||
|
||||
| 策略 | 说明 |
|
||||
| --- | --- |
|
||||
| **排除 Private 目录** | 只索引 `Public/` 下的头文件(接口和宏定义),性价比最高 |
|
||||
| **模块化索引** | 按需索引:渲染相关只索引 `Runtime/Renderer` + `Runtime/RenderCore`;逻辑相关只索引 `Runtime/Engine` + `Runtime/CoreUObject` |
|
||||
| **头文件优先** | 仅解析 `.h` 文件,不解析 `.cpp`,大幅降低图谱复杂度同时保留类层次关系 |
|
||||
|
||||
### 让 Claude Code 感知外部索引
|
||||
|
||||
在 `CLAUDE.md` 中声明外部图谱的存在和使用规则:
|
||||
|
||||
```markdown
|
||||
# External Context
|
||||
- **UE Engine Source:** Indexed at `~/.cache/ue_engine_graph.db`
|
||||
- **Search Rule:** If a class prefix is 'U', 'A', or 'F' and not found in the current project, use the `search_ue_engine_graph` tool via MCP.
|
||||
```
|
||||
|
||||
这样可将外部目录转变为**只读远程知识库**——既保证响应速度,又扩展知识边界。
|
||||
Reference in New Issue
Block a user