@clawhub-mike47512-7fac08a17e
Expert in prompt engineering, custom instructions, and prompt optimization for AI agents: clear structure, CoT/few-shot/XML/role patterns, system prompts, an...
---
name: prompt-expert
description: >
Expert in prompt engineering, custom instructions, and prompt optimization for AI agents:
clear structure, CoT/few-shot/XML/role patterns, system prompts, anti-patterns, evaluation,
multimodal and tool-use prompting. Use when designing, reviewing, or debugging prompts and agent skills.
---
# Prompt Expert(prompt-expert)
Deep expertise in **prompt engineering**, **custom instructions**, and **iterative prompt optimization**: crafting effective prompts, designing agent/skill instructions, and improving reliability.
## Core Expertise Areas
### 1. Prompt Writing Best Practices
- **Clarity and Directness**: Unambiguous instructions; concrete examples and expected outputs
- **Structure and Formatting**: Hierarchy, sections, scannable layout
- **Context Management**: Enough context without flooding the window
- **Tone and Style**: Match task and audience
### 2. Advanced Techniques
- **Chain-of-Thought (CoT)**, **Few-Shot** (1/2/multi-shot), **XML tags**, **role-based** prompting
- **Prefilling**, **prompt chaining**
### 3. Custom Instructions & System Prompts
- System prompts for domains; agent/skill instructions; behavioral boundaries; scope (in/out)
### 4. Optimization & Refinement
- Performance analysis, iterative improvement, A/B-style variants, consistency, token efficiency
### 5. Anti-Patterns & Common Mistakes
- Vagueness, contradictions, over-specification, hallucination risk, context leakage, injection-style risks
### 6. Evaluation & Testing
- Success criteria, test cases, failure analysis, regression, edge cases
### 7. Multimodal & Advanced
- Vision, files/PDFs, embeddings, tool-use prompts, extended reasoning where applicable
## Key Capabilities
Prompt analysis · generation · refinement · custom instruction design · best-practice guidance · anti-pattern fixes · testing strategy · documentation patterns
## Use Cases
Refining weak prompts · domain system prompts · agent/skill instructions · consistency and reliability · teaching prompt engineering · debugging prompt behavior · reusable templates · evaluation frameworks
## Limitations
Guidance only (no execution); no live APIs; validate with real tasks; human judgment for high-stakes use.
## Reference material(本技能内)
| 文档 | 内容 |
|------|------|
| [docs/GUIDE.md](docs/GUIDE.md) | 原则、进阶技巧表、自定义指令、排障速查、清单 |
| [examples/EXAMPLES.md](examples/EXAMPLES.md) | 示例与模板(精选) |
FILE:docs/GUIDE.md
# Prompt Expert — Reference Guide
Merged reference (principles, techniques, troubleshooting, checklists). For the **prompt-expert** skill.
## Core principles
1. **Clarity** — Explicit tasks; concrete examples; logical hierarchy.
2. **Conciseness** — Respect context limits; remove redundancy; progressive disclosure.
3. **Degrees of freedom** — Clear constraints, format, scope; balance control vs. reasoning room.
## Advanced techniques (short)
| Technique | Use when |
|-----------|----------|
| **Chain-of-thought** | Multi-step reasoning; need transparent logic. |
| **Few-shot** | Shape behavior with 1–N labeled examples. |
| **XML / structure** | Parsing, sections, repeatable layouts. |
| **Role-based** | Domain tone, boundaries, responsibilities. |
| **Prefill** | Lock first lines for format/tone. |
| **Chaining** | Pipeline: extract → analyze → summarize. |
| **Context** | Progressive disclosure; hierarchy; only necessary tokens. |
| **Multimodal** | Vision: specify what to extract; files: type + structure; embeddings: similarity tasks. |
## Custom instructions & system prompts
- Role, tone, constraints, scope, escalation (when to ask).
- Skill naming: lowercase hyphenated (e.g. `prompt-expert`); description: capabilities + use cases, avoid vague “helps with”.
## Skill layout (recommended)
```
skill-name/
├── SKILL.md # entry + YAML + role
├── skill.json # optional manifest
├── docs/ # this guide
└── examples/ # templates
```
## Evaluation & testing
- Success criteria: measurable, testable, realistic.
- Cases: happy path, edge, error, stress.
- Failure analysis → refine; regression-test after changes.
## Anti-patterns
- Vague goals; contradictory rules; over-constraining; prompts that invite fabrication; context leakage; injection-prone patterns.
## Troubleshooting (quick)
| Symptom | Levers |
|---------|--------|
| Inconsistent output | Format spec + examples + XML; role prompt. |
| Hallucination | Grounding, caveats, confidence, “what you don’t know.” |
| Too vague | Context, objective, success criteria, exemplar outputs. |
| Wrong length | Word/sentence bounds; scope. |
| Wrong format | Schema, JSON/XML example, field list. |
| Refusal | Legitimate framing; creative/educational context. |
| Prompt too long | Summaries, links to refs, fewer shots. |
| Brittle | Variables not literals; multiple input shapes; errors specified. |
**Debug loop:** reproduce → inspect prompt (goal, context, format) → hypothesis → edit → multi-input validation → document.
## YAML frontmatter (skills)
```yaml
---
name: skill-name
description: Clear, concise description (max ~1024 chars)
---
```
## Token budget (rule of thumb)
- Metadata: ~100–200 tokens; main instructions: ~500–1000; each deep reference: ~1k–5k.
## Checklist (skills)
**Quality:** clear name; short description; structure; progressive disclosure; terminology; no stale dates.
**Content:** use cases; examples; edge cases; limitations; troubleshooting pointer.
**Testing:** cases; criteria; edge/error handling; regression.
**Docs:** SKILL links here; examples; integration notes.
FILE:examples/EXAMPLES.md
# Prompt Expert — Examples (curated)
## 1. Refining a vague prompt
**Before:** `Help me write a better prompt for analyzing customer feedback.`
**After:** Specify role, outputs (sentiment, themes, JSON keys), length constraints, and ask for review of `[ORIGINAL PROMPT HERE]`.
## 2. Custom instructions (agent YAML excerpt)
```yaml
---
name: data-analysis-agent
description: Financial data analysis and reporting
---
# Role + Do/Don't + output sections + scope (see full patterns in prior versions if needed)
```
Use: Do’s/Don’ts, fixed report sections, explicit scope and escalation.
## 3. Few-shot classification
Give category definitions + 2–3 labeled tickets + `Now classify: Ticket: "..." Category:`.
## 4. Chain-of-thought scaffold
`Step 1: problem → Step 2: factors → Step 3: options → Step 4: recommendation` + paste scenario.
## 5. XML-structured marketing prompt
`<metadata>`, `<instructions>`, `<constraints>`, `<format>`, optional `<examples>`.
## 6. Iterative refinement request
Paste current prompt, list failures, ask for: missed issues, fixes, revised prompt, changelog, test cases.
## 7. Anti-pattern vs improved
Replace “analyze and make it good” with metrics, format (e.g. table columns), and decision goal (e.g. Q4 revenue).
## 8. Prompt evaluation framework
Define happy path, ambiguous input, complex input, invalid input, regression case — expected output + pass criteria each.
## 9. Skill metadata template
```yaml
---
name: analyzing-financial-statements
description: One-line capability + primary use
---
# Overview, capabilities, use cases, limitations
```
## 10. Optimization checklist
Clarity · conciseness · completeness · testability · robustness (variation + errors + jailbreak resistance).
FILE:skill.json
{
"name": "prompt-expert",
"version": "1.0.0",
"description": "Prompt engineering expert: custom instructions, optimization, CoT/few-shot/XML, evaluation, anti-patterns; see docs/ and examples/"
}
将 Figma 设计稿转为移动端 UI 代码(Android Jetpack Compose / XML,iOS SwiftUI / UIKit)。 在用户粘贴 Figma 链接并希望生成布局代码时使用。 通过 Figma REST API 提取设计树与 token,必要时追问澄清,再输出可落地的工程代码。
---
name: figma-mobile-cn
description: >
将 Figma 设计稿转为移动端 UI 代码(Android Jetpack Compose / XML,iOS SwiftUI / UIKit)。
在用户粘贴 Figma 链接并希望生成布局代码时使用。
通过 Figma REST API 提取设计树与 token,必要时追问澄清,再输出可落地的工程代码。
metadata:
{
"openclaw":
{
"requires": { "bins": ["node"], "env": ["FIGMA_TOKEN"] },
"primaryEnv": "FIGMA_TOKEN",
"install": [],
},
}
---
# Figma 转移动端
用交互式澄清流程,把 Figma 设计转为移动端 UI 代码。
支持:Android Compose、Android XML、iOS SwiftUI、iOS UIKit。
## 环境要求
- 环境变量 `FIGMA_TOKEN`(Figma → 头像 → Settings → Security → Personal Access Tokens)
- **Node.js 18+**(内置 `fetch`;脚本为纯 JavaScript,**无需** `npm install`)
- 脚本入口:在 `scripts/` 下执行 `node src/figma-fetch.js`,或 `npm run figma-fetch --`(`package.json` 仅用于声明 `type: module` 与 npm 脚本快捷方式,无第三方依赖)
## 触发与输入
用户粘贴 **Figma 设计链接** 时启用本技能。
用户也可能在链接旁给出 **内联提示**,例如:
- 目标平台:「Android XML」「Compose」「SwiftUI」「UIKit」
- 布局偏好:「用 ConstraintLayout」「优先 StackView」
- 组件说明:「开关用我们的 CompactSwitch」「这块是动态列表」
**若用户已通过内联说明回答某类问题,则不要再问同类问题。**
例如用户已写「Android XML,三张卡是 RecyclerView 列表」,则不要再问输出格式或列表动静态。
## 工作流
### 步骤 1:拉取与分析
用户提供 Figma 链接后:
1. 在技能根目录进入 `scripts/`,运行:
`node src/figma-fetch.js "<url>" [--json] [--depth N]`
若用户提供 **多个链接**(同一页不同状态),使用 `--compare` 拉取并对比差异,便于多状态代码(selector、条件样式等):
`node src/figma-fetch.js --compare "<url1>" "<url2>" [--json]`
2. 分析结构:区块、重复模式、组件类型
3. 关注 **INSTANCE** 节点(组件实例),结合 `variantProperties` 理解状态(如 State=Default)
4. 复杂渐变/阴影在总结中向用户点明
5. 生成代码前遵守 `references/figma-interpretation.md` 中的节点语义规则
**详细解读规则**:阅读 `references/figma-interpretation.md`
### 步骤 1.5:结构摘要
在提问之前,先用 **2~3 行** 向用户确认你理解的结构,例如:
> 我看到:顶部导航(返回 + 标题)→ 两大块内容(用户资料卡、设置列表约 8 项)→ 底部主按钮。约 25 个节点。
需覆盖:主要区域、重复模式(如「8 个相似列表项」)、显眼元素(渐变、插画、叠卡)。
若用户纠正结构,先修正理解再进入步骤 2。
结构非常简单时(如单卡片 + 少量输入框)可跳过本步。
### 步骤 2:确认与追问
**问题顺序(严格先问靠前的):**
1. **输出形态**(除非用户已说明,否则必须先问)
→ Android XML / Compose / SwiftUI / UIKit
决定后续分析与代码风格。
2. **结构歧义**(只问真正不确定的)
→ 「这几项看起来相似——动态列表还是写死布局?」
→ 「这块是整图资源还是图标+背景组合?」
3. **组件选型**(仅在与平台相关时)
→ 「是否有指定自定义组件?没有则使用系统默认。」
**追问规则:**
- 用户已在提示里回答的项一律跳过
- 总共 **最多 3~5 个问题**,越少越好
- 每个问题给具体选项 + 一句利弊,并保留「或详细说说你的需求」
- 自然语言表述,不要甩原始 JSON
- 若上下文已足够且结构简单,**可跳过步骤 2** 直接进入步骤 3
**何时追问 vs 直接生成:**
- 同级下 **≥3 个**结构相似子节点 → 可能是列表 → **应追问**动静态
- 共享 `componentId` 的 INSTANCE → 复用组件 → **可说明**并给默认实现
- 层次单一、无歧义 → 高置信度 → **可不问**直接生成
- 设计中有渐变/重阴影 → 在摘要中说明处理方式
### 步骤 2.5:工程扫描(可选,建议)
若本地能访问目标工程,可运行扫描以复用颜色、字符串等资源:
```bash
cd scripts
node src/project-scan.js /path/to/project --json --output scan-report.json
```
**如何在生成代码时使用扫描结果**:阅读 `references/figma-scan-usage.md`
### 步骤 3:生成代码
用户确认后(或无需追问时)生成代码文件。
**详细生成规则**:阅读 `references/code-generation.md`
多文件时每个文件用清晰标题标出,例如:
```
📄 activity_notification_settings.xml
[代码]
📄 item_expert_notification.xml
[代码]
```
### 步骤 4:迭代与反馈记录
展示代码后简短询问:
> 是否贴合设计?需要调整的地方?
用户可能继续迭代:间距、替换组件、删区块、换平台、改色值等。
**除非用户要求整页重写,否则每轮只改变化部分。**
**反馈记录(建议自动执行):**
每当用户纠正生成结果,在项目根目录维护 `feedback-log.md`(不存在则创建)。每条格式:
```
## YYYY-MM-DD HH:MM
- **Platform**: Android XML / Compose / SwiftUI / UIKit
- **Figma node type**: (如:带图标的 FRAME、Tab 栏、按钮组)
- **Issue**: 问题简述
- **Before**: 原先生成内容(片段或说明)
- **After**: 用户期望(片段或说明)
- **Rule candidate**: (可选)可沉淀为通用规则的说明
```
记录应 **简洁、可分类、偏映射错误**;不要记录纯个人偏好或一次性命名。
可在适当时机或应用户要求运行:
```bash
cd scripts
node src/feedback-analyze.js [path/to/feedback-log.md]
```
用于汇总模式与规则候选(默认读取当前目录下的 `feedback-log.md`)。
## 矢量与 SVG
需从 Figma 导出 SVG 时(见 `references/code-generation.md`):
```bash
cd scripts
node src/figma-fetch.js "<url>" --export-svg [--nodes "节点id1,节点id2"]
```
简化后的 JSON 中每个节点含 `id` 字段,可用于 `--nodes` 附加导出。
## 异常处理
- **`FIGMA_TOKEN` 未设置**(脚本打印 `FIGMA_TOKEN_NOT_SET`)→ **不要**让用户自行敲复杂命令。应:
1. 说明需要 Figma Personal Access Token
2. 指引:Figma → 头像 → Settings → Security → Personal Access Tokens
3. 请用户在对话中粘贴 token(通常以 `figd_` 开头)
4. 将 token 写入项目根目录 `.env`:`FIGMA_TOKEN=figd_xxx`(脚本会自动读取 `.env`)
5. 再次执行 `node src/figma-fetch.js` 或 `npm run figma-fetch --`
- **Token 无效**(401/403)→ 可能过期或撤销,请用户重新生成并更新 `.env`
- **链接无效** → 给出正确示例:`https://www.figma.com/design/<fileKey>/<name>?node-id=<id>`
- **API 其它错误** → 展示错误信息,提示检查网络/代理
- **子节点过多(>200)** → 脚本会在 JSON 中标注 `_largeChildList`,建议缩小选区 Frame
- **深度截断** → 脚本会将 `_truncated` 标在节点上,必要时提高 `--depth`(最高可自动提升到 15 层对比模式)
## 参考文档说明
`references/` 下文档按 **命名约定** 区分用途(见 `README.md`「目录与命名」):`figma-*` 为 Figma 语义与扫描用法,`platform-*` 为各平台映射表,`code-generation.md` 为生成总则。正文为 **中文**,代码与 API 标识保留英文原名;工作流以本 **SKILL.md** 为准。
FILE:README.md
# Figma 转移动端(figma-mobile-cn)
用 AI 将 Figma 设计稿转为可落地的移动端 UI 代码。
支持:**Jetpack Compose** · **Android XML** · **SwiftUI** · **UIKit**
脚本为 **JavaScript(ESM)**,需 **Node.js 18+**(内置 `fetch`)。**无需 `npm install`**,不依赖任何 npm 包;`scripts/package.json` 仅用于声明 `"type": "module"` 与 `npm run` 快捷命令。
## 演示
与 Figma 社区 [Material Design 3 Messaging App](https://www.figma.com/community/file/1169726503071187057/) 的对比示例:

**左:** Figma 设计稿 · **右:** 生成的 Jetpack Compose 在 Android Studio 中运行
工具读取 Figma 节点树(自动布局、样式引用、变体等),生成符合平台习惯的代码,而不是纯绝对坐标堆砌。
## 工作原理
1. **拉取**:`scripts/src/*.js` 调用 Figma REST API,导出节点树 JSON
2. **解读**:模型理解布局语义(如「6 个相似行 → LazyColumn」)
3. **生成**:输出带主题与组件惯例的代码(Material 3、SF Symbols 等)
4. **迭代**:用自然语言持续修改(如「标题栏吸顶」「换深色主题」)
## 安装
### OpenClaw
```bash
clawhub install figma-mobile-cn
```
### Claude Code / 其它 Agent
将本技能文件夹复制到项目约定目录,例如:
```
your-project/.claude/skills/figma-mobile-cn/
```
或:
```
your-project/.agents/skills/figma-mobile-cn/
```
### 脚本依赖
**无需安装。** 确保本机有 Node.js 18+ 即可。若希望使用 `npm run figma-fetch` 等别名,可在 `scripts/` 目录执行 `npm install`(无依赖包,仅解析 package.json)。
## 配置
1. 获取 Figma Personal Access Token:
Figma → 头像 → Settings → Security → Personal Access Tokens,新建 token(一般以 `figd_` 开头)。
2. 设置环境变量,或把下面一行写入项目根目录 `.env`(脚本会自动读取):
```bash
# macOS / Linux
export FIGMA_TOKEN="figd_your_token_here"
# Windows PowerShell
$env:FIGMA_TOKEN = "figd_your_token_here"
```
## 使用
在对话中粘贴 Figma 设计链接,例如:
> 把这个转成 Jetpack Compose:https://www.figma.com/design/xxx/Project?node-id=100-200
Agent 会:
1. 在 `scripts/` 下调用 `figma-fetch` 拉取设计数据
2. 必要时追问平台、列表动静态等
3. 生成工程化代码文件
4. 根据你的反馈迭代
常用命令(均在 `scripts/` 目录执行,`node` 与 `npm run` 二选一):
```bash
node src/figma-fetch.js "https://www.figma.com/design/..."
npm run figma-fetch -- "https://www.figma.com/design/..."
node src/figma-fetch.js --compare "<url1>" "<url2>"
node src/project-scan.js /path/to/android/project --json -o scan-report.json
npm run project-scan -- /path/to/android/project --json -o scan-report.json
node src/feedback-analyze.js ../feedback-log.md
npm run feedback-analyze -- ../feedback-log.md
```
若省略路径,反馈分析默认读取当前工作目录下的 `feedback-log.md`。
## 与「截图转代码」类工具对比
| 能力 | 基于截图的工具 | figma-mobile-cn |
|------|------------------|--------------|
| 输入 | 截图/图片 | Figma API(设计树) |
| 布局理解 | 像素坐标 | 自动布局语义 |
| 输出 | 绝对定位为主 | LazyColumn、VStack 等惯用写法 |
| 迭代 | 重新截图 | 自然语言修改 |
| 成本 | 多为订阅 | 开源,仅需 Figma token |
## 目录与命名
技能根目录约定:
| 类型 | 约定 | 示例 |
|------|------|------|
| Agent 入口 | 固定大写 | `SKILL.md`、`README.md` |
| 设计说明 | 小写 kebab-case | `design.md` |
| 反馈记录 | 小写 kebab-case | `feedback-log.md` |
| 参考文档 | 小写 kebab-case,带语义前缀 | `figma-interpretation.md`、`figma-scan-usage.md`、`code-generation.md`、`platform-compose.md` … |
| 脚本源码 | `scripts/src/` 下 kebab-case `.js` | `figma-fetch.js`、`project-scan.js`、`feedback-analyze.js`、`load-env.js` |
`references/` 中:**`figma-*`** 表示 Figma 节点语义与扫描用法;**`platform-*`** 表示 Android/iOS 各平台映射;**`code-generation.md`** 为代码生成总则。
## 目录结构
```
figma-mobile-cn/
├── SKILL.md # Agent 主说明(中文)
├── README.md
├── design.md # 设计目标与演进
├── feedback-log.md # 用户反馈记录模板
├── scripts/
│ ├── package.json # type: "module" + npm scripts(无依赖)
│ └── src/
│ ├── figma-fetch.js # Figma API:拉取 / 对比 / SVG
│ ├── project-scan.js # 工程资源轻量扫描
│ ├── feedback-analyze.js # 反馈日志分析
│ └── load-env.js # .env 加载
├── references/
│ ├── figma-interpretation.md
│ ├── figma-scan-usage.md
│ ├── code-generation.md
│ ├── platform-compose.md
│ ├── platform-xml.md
│ ├── platform-swiftui.md
│ └── platform-uikit.md
└── assets/
└── demo-comparison.png
```
## 环境要求
- Node.js 18+
- Figma Personal Access Token(免费申请)
## 许可证
MIT
FILE:design.md
# figma-mobile-cn(Figma 转移动端)设计方案
## 核心目标
让智能体能看懂 Figma 设计稿,直接写出可用的移动端 UI 代码。
解析精度服务于代码质量——最终衡量标准是输出的代码好不好用。
## 设计原则
1. **输出即可用** — 颜色写死 hex,文本写死字符串,不引用资源 ID。复制到项目里直接能看效果
2. **提示词驱动** — 用户可以在发链接时附带平台、布局偏好、组件约定等提示,减少交互轮次
3. **交互式确认** — 只问不确定的,有优先级(输出格式必须先问),每个问题留开放入口
4. **持续迭代** — 生成后用户可以反馈修改,逐步优化,不是一次性交付
5. **不猜业务逻辑** — 列表/Adapter/自定义组件等,问用户而不是假设
6. **先 Skill 后 MCP** — 在 Skill 里验证流程,跑通后再适配 MCP Server
## 数据提取能力
`scripts/src/figma-fetch.js` 提取以下信息:
- 结构层级、节点类型(含 INSTANCE 组件实例识别)
- 颜色(hex)、渐变(类型+色标)、图片标记
- 文本属性(内容、字号、字重、字体、行高、字间距、对齐、颜色)
- 布局属性(Auto-layout 方向、间距、内边距、对齐、尺寸模式)
- 视觉效果(阴影:颜色/偏移/半径/扩展,模糊)
- 边框(颜色、宽度)、圆角(统一或四角独立)、透明度
- 自适应深度:默认 depth=5,检测到子节点截断时自动加深到 15
## 交互设计
### 问题优先级(严格顺序)
1. 输出格式(必须先问,决定后续所有措辞)
2. 结构歧义(动态列表 vs 固定项等)
3. 组件选择(自定义组件 vs 系统默认)
### 信心度标准
- ≥3 个结构相似的同级节点 → 可能是列表 → 必须问
- 共享 componentId 的 INSTANCE → 复用组件 → 提示但可默认
- 单一明确层级 → 直接生成,不问
### 每个问题的要求
- 给具体选项 + 一句话优劣
- 保留开放入口:"或者聊聊这个问题"
- 问题之间有依赖关系时,先问前置问题
## references 定位
文件命名:`figma-*`(节点语义、扫描用法)、`platform-*`(各平台映射)、`code-generation.md`(生成总则)。详见仓库根目录 `README.md`「目录与命名」。
references 文件是 **Figma 属性→代码属性的映射表**:
- Auto-layout → 各平台对应写法
- 阴影/渐变/圆角 → 各平台 API
- 布局选择指南
不是教智能体写代码的教程(智能体本身就有这些知识),
而是确保 Figma 特有属性能正确映射到目标平台。
## 演进路线
| Phase | 内容 | 状态 |
|-------|------|------|
| 1 | Skill 交互式确认流程 | ✅ v0.3 |
| 1.5 | 资源生成(shape drawable + SVG 导出 + Vector Drawable) | ✅ |
| 2 | `project-scan`(`src/project-scan.js`,扫描工程资源/组件) | 部分完成(Android 资源扫描;iOS 轻量) |
| 3 | MCP Server(给 VS Code 用) | 待做 |
| 4 | Adapter 生成(可选) | 待做 |
### Phase 2 设计笔记:project-scan
**使用场景**:Phase 3 MCP Server 阶段,Agent(Claude Code / Copilot 等)在 VS Code 中工作,需要引用项目已有资源而非写死值。
**扫描范围(3 层)**:
1. **目标 module**:用户指定的、或 Agent 根据上下文推断的当前 module。Agent 通常知道自己在哪个 module——当前编辑的文件、上下文引用的文件都能提供线索。
2. **依赖的内部 module**:解析目标 module 的 `build.gradle`,找 `implementation project(':xxx')` 引用的同项目 module,扫描它们的 `res/`。
3. **不扫描外部依赖**:三方库的资源不可控,不纳入匹配范围。
**扫描内容**:
- `res/values/colors.xml` → 颜色名+值,按 hex 值匹配
- `res/values/strings.xml` → 字符串名+值(用于判断是否有现成文案)
- `res/values/dimens.xml` → 尺寸名+值
- `res/values/styles.xml` / `themes.xml` → 已有样式
- `res/drawable/` → 已有 shape/vector drawable,按视觉特征匹配(颜色+形状+圆角)
- 自定义 View 类 → 按类名识别(如 `CompactSwitchCompat`、`RoundImageView`)
**输出模式切换**:
- Phase 1(当前):所有值写死,即复即用
- Phase 2+:匹配到已有资源 → 引用(`@color/primary`),未匹配 → 创建到目标 module 的 `res/` 对应文件中
**关键设计决策(待定)**:
- 颜色匹配:完全相同的 hex 才算匹配?还是近似色也算?(建议:严格匹配 hex,近似提示但不自动引用)
- 新建资源的命名规范:跟随项目已有命名风格?还是用固定前缀?
- 多 flavor / buildType 的资源覆盖如何处理?
FILE:feedback-log.md
# 反馈记录 — Figma 转移动端
用于记录用户对生成代码的修正,便于归纳模式与优化 Skill 规则。
每条记录格式:
```
## YYYY-MM-DD HH:MM
- **Platform**: Android XML / Compose / SwiftUI / UIKit
- **Figma node type**: (e.g., FRAME with icon, Tab bar, Button group)
- **Issue**: Brief description of what was wrong
- **Before**: What the agent generated (snippet or description)
- **After**: What the user wanted (snippet or description)
- **Rule candidate**: (optional) Suggested general pattern rule
```
在 `scripts/` 目录执行:`node src/feedback-analyze.js` 或 `npm run feedback-analyze --`(可传入 `feedback-log.md` 路径)以汇总模式。
---
<!-- Entries below this line -->
FILE:references/code-generation.md
# 代码生成规则
> 由 SKILL.md 步骤 3 引用。生成代码前必读。
## 输出规则(必须遵守)
- **颜色**:写死前先在 `res/values/colors.xml`(及若存在的 `colors_*.xml`)中按 hex 查找。找到则用资源引用(如 `@color/primary`)。找不到则直接写 hex(`android:textColor="#0F0F0F"` / `Color(0xFF0F0F0F)`)。
- **字符串**:写死前先在 `res/values/strings.xml` 按文案查找。找到则用 `@string/...`。找不到则直接写字符串(如 `android:text="通知设置"`)。
- **尺寸**:直接写数值(如 `android:textSize="17sp"`)。尺寸资源一般不必强行匹配。
- **列表**:输出主布局 + 独立 item 布局文件。**不要**生成 Adapter / ViewHolder。
- **资源匹配优先级**:若存在完全一致的 `@color/`、`@string/`,优先引用。其余为便于预览可写死。**不要**新建资源定义——只引用已有项。
## Drawable — 生成真实资源,避免占位
- **Shape drawable**(背景、描边、轨道):根据 Figma(颜色、圆角、描边、渐变)生成完整 XML。每个文件单独输出,带 `📄 drawable/文件名.xml` 标题。
- **图标/矢量**:在 `scripts/` 下执行 `node src/figma-fetch.js "<url>" --export-svg [--nodes "id1,id2"]`(或 `npm run figma-fetch --`)从 Figma API 导出 SVG,再转为 Android Vector Drawable。简化 JSON 中每个节点有 `"id"`,供 `--nodes` 使用。导出文件带 `📄 drawable/ic_名称.xml`。
- **照片/位图**:无法凭空生成 → 使用 `@drawable/placeholder` 并说明需补素材。
- **目标**:代码可复制粘贴即可大致还原设计,而不是满屏占位。
## 未匹配资源的建议块(在已有工程扫描时)
全部代码输出后,若有因未匹配而写死的颜色/字符串,**追加**「建议资源」块,便于用户一次性补进工程:
```
📝 建议资源(未匹配 — 可按需复制到工程)
// colors.xml
<color name="text_primary">#0F0F0F</color>
<color name="bg_card">#F7F7F7</color>
// strings.xml
<string name="btn_submit">提交</string>
<string name="hint_nickname">请输入昵称</string>
```
规则:
- 仅在使用了工程扫描且仍有未匹配项时附带
- 命名建议遵循工程现有风格(从扫描结果观察 `color_xxx` / `xxx_color` 等)
- 按类型分组(先颜色后字符串)
- 尺寸一般不必单独抽资源
- 本块为**建议**,是否落库由用户决定
## 平台约定(提醒)
模型已具备平台知识,此处仅作硬性对齐:
- **Android XML**:Material 3;非平凡布局默认 **ConstraintLayout**;8dp 网格;最小触控约 48dp;优先 `MaterialCardView` / `MaterialSwitch` 等。
- **Android Compose**:Material3;`Modifier` 链;列表用 `LazyColumn`;页面骨架用 `Scaffold`。
- **iOS SwiftUI**:遵循 HIG;`NavigationStack`、`List`、`VStack`/`HStack`/`ZStack`;安全区;系统字体。
- **iOS UIKit**:HIG;Auto Layout(约束或 StackView);列表用 `UITableView`/`UICollectionView`;安全区。
## 特殊视觉效果
- **渐变**:按平台生成对应渐变代码(GradientDrawable / `Brush.linearGradient` / `LinearGradient` / `CAGradientLayer`)。若很复杂,注释说明可能需微调。
- **阴影**:使用平台阴影 API;若设计阴影与默认 elevation 差异大,注明。
- **分角圆角**:四角不一致时使用各平台分角 API。
## 平台细映射表
请结合对应参考文档:
- Android Compose → `references/platform-compose.md`
- Android XML → `references/platform-xml.md`
- iOS SwiftUI → `references/platform-swiftui.md`
- iOS UIKit → `references/platform-uikit.md`
FILE:references/figma-interpretation.md
# Figma 节点解读规则
> 由 SKILL.md 步骤 1 引用。分析 Figma 拉取结果时使用。
## 样式引用与文字尺寸
- 注意节点上的 `styleRefs` — 指向 Figma 共享样式(设计 token)。共享同一样式 ID 的节点在代码中应使用同一 token/资源
- TEXT 节点上的 `textAutoResize`:`WIDTH_AND_HEIGHT` 表示自适应(wrap);`HEIGHT` 表示定宽增高;缺省/NONE 多为固定尺寸
## 节点语义(生成任何平台代码前应用)
- **跳过系统装饰**:StatusBar、HomeIndicator、NavigationBar 等为 iOS 示意占位,**不要**为其生成业务代码。同一位置重复节点也视为 Figma 冗余,可忽略一份
- **跳过不可见节点**:VECTOR/RECTANGLE 无填充且描边均 `visible: false`,或 `absoluteRenderBounds: null` — 多为设计残留,不渲染则忽略
- **layoutAlign=STRETCH**:子沿父自动布局交叉轴撑满 → `match_parent` 交叉轴 / `.frame(maxWidth: .infinity)`。仅在非 INHERIT 时强调
- **layoutPositioning=ABSOLUTE**:在自动布局父级内绝对定位 → 用显式 x/y 偏移约束,而非纯流式布局
- **容器 + 小图标 = 单视图**:带背景/圆角的 FRAME 包住小 VECTOR/INSTANCE → 一个 `ImageView`/`Image`,不要多层嵌套
- **VECTOR/ELLIPSE 组合 = 单张图**:同 FRAME 内多个小矢量拼成图标 → 代码里引用**一张**图,不要拆成多个 View
- **RECTANGLE 作背景**:GROUP 首子 RECTANGLE 与 GROUP 同尺寸 → 背景形,不是独立 View
- **GROUP vs FRAME**:有 `layoutMode` 的 FRAME → 结构化布局(LinearLayout、HStack 等);无 `layoutMode` 的 GROUP → 绝对坐标,映射到 ConstraintLayout 或显式偏移
- **小数取整**:dp 取最近整数,sp 约 0.5;接近标准值(如 47.99→48dp)可吸附
- **宽度策略**:勿照搬 Figma 宽度数值,要推断意图。近全宽元素 → `match_parent` + 水平 margin;并排时区分「固定宽」(图标/头像)与「弹性」(正文),弹性侧用 `0dp`+约束。详见 `platform-xml.md`「宽度策略」
## 页面架构分析(Android XML 侧重)
- 多个 Tab 文案 → 常见 `TabLayout` + `ViewPager2`,内容在 Fragment 布局(强信号,非绝对 — 不确定则问)
- Tab 项文字颜色不一致 → 选中/未选中,用 `tabSelectedTextColor` / `tabTextColor`,不要逐 Tab 写死
- 导航栏返回/关闭 → `ImageView`(src + background),不要为简单图标再套 `FrameLayout`
- 图标+文字按钮 → 优先 `LinearLayout` + `ImageView` + `TextView`,慎用 `MaterialButton` 的 `app:icon`(部分机型表现问题)
- 列表项左侧条 + 右侧内容 → 多看几项判断等高还是独立高度
- **叠放/交错卡片** 且结构相似 → 可能是滑动/切换交互。**不要**静态拆成多个独立 View。应问用户:「是否滑动/切卡?规则是左右滑、点击切换还是自动轮播?」实现方式(自定义 View、ViewPager2、第三方等)依答案而定
### 组件变体 → 多状态代码
`INSTANCE` 带 `variantProperties` 时表示组件不同状态。
**用法:**
1. 多个 INSTANCE 同 `componentId` 但 `variantProperties` 不同(如 State=Default/Pressed/Disabled)→ 同一组件的多状态
2. 生成 **一个** `View`/Composable,用状态驱动样式,**不要**多个独立 View
3. 常见变体属性到平台映射:
| 变体属性 | Android XML | Compose | iOS |
|---|---|---|---|
| State=Default/Pressed/Disabled | `selector` + `state_pressed` / `state_enabled` | `Modifier.clickable` + 条件样式 | `UIControl` 状态 / `.disabled()` |
| State=Selected/Unselected | `state_selected` + `duplicateParentState` | `var isSelected` + 条件 | `isSelected` |
| State=Active/Inactive | `state_activated` | 布尔状态 + 条件 | 自定义状态 |
| Size=Small/Medium/Large | 同布局不同尺寸值 | 带尺寸枚举的可组合参数 | 参数化 View |
| Type=Primary/Secondary/Outline | 不同 style 资源 | 颜色/描边参数 | 不同配置 |
4. 若设计里**只**有一种状态,仍可根据 `variantProperties` 提示用户:组件另有其他状态,是否一并生成
5. Size/Type 变体 → 参数化组件(enum/sealed),避免写死魔法数
**示例 — 带 State 的按钮:**
若仅见 `{"State": "Default"}`,但知组件还有 Pressed/Disabled:
- XML:默认态布局 + 说明需 selector 补全状态
- Compose:`enabled` 参数 + 条件颜色/透明度
### Figma 样式引用 → 一致 token
节点有 `styleRefs` 时指向 Figma 共享样式。
**用法:**
1. **同一 style ID**(如相同 `styleRefs.fill`)→ 代码中 **同一** 资源/token,即使 hex 恰好相同
2. 表达设计师语义:两个文本同 `styleRefs.text` 应同一文本样式,即使字号略有出入(可能是设计不一致)
3. 有工程扫描时:尝试将样式语义与工程资源对齐(如多节点共用填充色 `#0158FF` → 可能是 `@color/primary`)
4. 生成时 **先按 styleRef 分组,再按数值**:
- 同 styleRef → 必须同一引用
- 同值不同 styleRef → 可同资源,但可注明差异
- 值与 styleRef 都不同 → 不同资源
**实践:**
不必把 Figma style ID 解析成名称(需额外 API)。把它当 **分组键**:「这 5 个 text 共用 styleRef.text = S:xxx,代码里应同一套 TextStyle」。
若同 styleRef 但渲染值不一致,提示用户可能存在设计不一致。
## 多状态批量对比
用户给出同一页多个 Frame(不同状态)时:
1. 使用 `node src/figma-fetch.js --compare "<url1>" "<url2>"`(在 `scripts/`)得到两棵简化树 + 行级差异摘要
2. `diffLines` 标出两树间路径/值变化
3. 据差异生成状态处理:颜色 → selector/条件色;透明度 → alpha/可用性;可见性 → `View.GONE`/分支;文案 → 绑定
4. 代码后附 **状态变化摘要表**
**用法示例:**
```bash
cd scripts
node src/figma-fetch.js --compare "<url1>" "<url2>" --json
```
输出 JSON 含 `trees`、`diffLines`(过长时见 `diffTruncated`)、`depthUsed`、`autoDeepened`。以第一个 URL 对应树为基准状态,第二个为对比状态。
FILE:references/figma-scan-usage.md
# 工程扫描使用说明(Project Scan)
> 由 SKILL.md 步骤 2.5 引用。在已有扫描报告时阅读本文。
## 运行扫描
在技能目录的 `scripts/` 下执行:
```bash
cd scripts
node src/project-scan.js /path/to/project --json --output scan-report.json
```
当前实现:**Android** 会解析 `res/values/*.xml` 中的颜色、字符串、尺寸,并列出 `drawable` 资源名;**iOS** 为轻量占位(见报告 `notes`)。报告含 `indices`,便于按 hex、文案做快速匹配。
## 在代码生成阶段使用扫描结果(步骤 3)
**颜色匹配:**
1. 从 Figma 节点取 hex → 规范化为 `#RRGGBB`
2. 在 `indices.colors` 中查找 → 命中则使用工程引用(Android:`@color/名称`,iOS:按项目习惯,如 `UIColor` 扩展或 Asset)
3. iOS 动态色:Figma 多为浅色稿。若扫描到 `light:#2965FF dark:#4D88FF`,与 Figma 的 `#2965FF` 对齐时按浅色值匹配
4. 未命中 → 可写死 hex,并注释 `// TODO: 无匹配工程色`
**字符串匹配:**
1. 从 Figma TEXT 节点取文案
2. 在 `indices.strings` 中查找 → 命中则使用多语言引用(Android:`@string/key`,iOS:依项目本地化格式)
3. 未命中 → 可写死文案,并注释 `// TODO: 未在 i18n 中`
**文本样式匹配:**
1. 从 Figma TEXT 取 fontSize + fontWeight
2. 构造查找键:`"{fontSize}sp_{weight}"`(如 `"16sp_bold"`)
3. 在 `indices.text_styles` 中查找 → 命中则使用样式引用(Android:`style="@style/..."`)
4. 未命中 → 使用行内属性(`android:textSize`、`android:textStyle` 等)
**fontWeight 映射(Figma 数值 → Android 关键字):**
- 400 及以下 → `normal`
- 500 → `medium`
- 600 → `semibold`
- 700 及以上 → `bold`
**图片匹配:**
1. 图标类元素 → 按语义名在扫描结果中找 drawable(如 Figma `icon/back` → `icon_back`)
2. 命中 → `UIImage(named:)` / `@drawable/name`
3. 未命中 → 从 Figma API 导出
**基类检测(iOS):**
- 扫描若发现 `BaseViewController`、`BaseTableViewCell` 等 → 优先作为父类,而不是裸用 UIKit 类
## 无扫描报告时的降级
若无项目扫描报告,则按步骤 3 中「写死值 + 资源匹配」的约定生成代码。
FILE:references/platform-compose.md
# Compose 映射:Figma → Jetpack Compose
> 作用:把 Figma 属性映射到 Jetpack Compose 代码。
> 本文是 **映射表**,不是 Compose 教程 —— 模型已掌握 Compose 惯例。
## 布局选型
| Figma 结构 | 推荐 Composable |
|---|---|
| 纵向堆叠 | `Column` |
| 横向堆叠 | `Row` |
| 重叠 / Z 序 | `Box` |
| 重复相似项(≥3) | `LazyColumn` / `LazyRow` |
| 带顶栏/底栏的页面 | `Scaffold` |
| 复杂相对定位 | `Box` + `Modifier.align` / `offset` |
## 自动布局映射
| Figma 属性 | Compose 对应 |
|---|---|
| layoutMode: VERTICAL | `Column` |
| layoutMode: HORIZONTAL | `Row` |
| itemSpacing | `Arrangement.spacedBy(X.dp)` |
| padding* | `Modifier.padding()` |
| primaryAxisAlignItems: CENTER | `verticalArrangement = Arrangement.Center` |
| counterAxisAlignItems: CENTER | `horizontalAlignment = Alignment.CenterHorizontally` |
| layoutGrow: 1 | `Modifier.weight(1f)` |
| primaryAxisSizingMode: FIXED | `Modifier.height` / `width(X.dp)` |
| counterAxisSizingMode: AUTO | `wrapContentWidth` / `Height` |
## 尺寸换算
- Figma px → Compose `.dp`(1:1)
- Figma 字号 px → Compose `.sp`(1:1)
## 阴影
```kotlin
// 阴影高度(Elevation)
Card(elevation = CardDefaults.cardElevation(defaultElevation = 4.dp))
// 自定义阴影(Compose 1.6+)
Modifier.shadow(
elevation = 4.dp,
shape = RoundedCornerShape(12.dp),
ambientColor = Color(0x1A000000),
spotColor = Color(0x33000000)
)
```
## 渐变
```kotlin
// 线性渐变
Modifier.background(
Brush.linearGradient(
colors = listOf(Color(0xFFFF6B6B), Color(0xFF4ECDC4)),
start = Offset(0f, 0f),
end = Offset(0f, Float.POSITIVE_INFINITY)
)
)
```
## 分角圆角
```kotlin
RoundedCornerShape(
topStart = 12.dp,
topEnd = 12.dp,
bottomEnd = 0.dp,
bottomStart = 0.dp
)
```
## 页面级架构
以下模式贴近线上 Compose 工程的 **真实结构**。
### 多 Tab 页
当设计出现 **多个 Tab**(≥2 个文案作为导航)时:
- 顶部 Tab:`TabRow` + `HorizontalPager`(accompanist 或 foundation)
- 底部导航:`NavigationBar`
- **不要**用纯 `Text` 当 Tab —— 缺少选中态、指示器与滑动联动
- 每个 Tab 内容拆成独立 `@Composable`
```kotlin
@Composable
fun TabScreen() {
val pagerState = rememberPagerState(pageCount = { 3 })
val scope = rememberCoroutineScope()
val tabs = listOf("关注", "推荐", "热榜")
Column {
TabRow(
selectedTabIndex = pagerState.currentPage,
containerColor = Color.White,
contentColor = Color(0xFF0F0F0F),
indicator = { tabPositions ->
TabRowDefaults.SecondaryIndicator(
modifier = Modifier.tabIndicatorOffset(tabPositions[pagerState.currentPage]),
color = Color(0xFF0F0F0F)
)
}
) {
tabs.forEachIndexed { index, title ->
Tab(
selected = pagerState.currentPage == index,
onClick = { scope.launch { pagerState.animateScrollToPage(index) } },
text = {
Text(
title,
fontWeight = if (pagerState.currentPage == index) FontWeight.Bold else FontWeight.Normal,
color = if (pagerState.currentPage == index) Color(0xFF0F0F0F) else Color(0xFF858A99)
)
}
)
}
}
HorizontalPager(state = pagerState) { page ->
when (page) {
0 -> FollowingScreen()
1 -> RecommendScreen()
2 -> HotListScreen()
}
}
}
}
```
### 导航栏 — 视为整体
导航栏(返回 + 标题,可有右侧操作)是 **一个逻辑容器**。
- 标准样式:`TopAppBar` / `CenterAlignedTopAppBar`
- 自定义:用单行 `Row` 承载,其下方再放主内容区
```kotlin
@Composable
fun CustomNavBar(onBack: () -> Unit, title: String) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp, vertical = 8.dp),
verticalAlignment = Alignment.CenterVertically
) {
IconButton(
onClick = onBack,
modifier = Modifier
.size(32.dp)
.background(Color.Black, CircleShape)
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = "返回",
tint = Color.White,
modifier = Modifier.size(18.dp)
)
}
Spacer(Modifier.weight(1f))
Text(title, fontSize = 17.sp, fontWeight = FontWeight.Bold)
Spacer(Modifier.weight(1f))
Spacer(Modifier.size(32.dp)) // 平衡占位
}
}
```
### 图标 + 文字按钮
优先在 `Button` 内用 `Row`,或可点击的 `Row`,保证图标与文字稳定排版:
```kotlin
// 描边按钮 + 图标
OutlinedButton(
onClick = {},
shape = RoundedCornerShape(12.dp),
border = BorderStroke(1.dp, Color(0xFFDCDCDC)),
modifier = Modifier.fillMaxWidth().height(40.dp)
) {
Icon(painter = painterResource(R.drawable.ic_video), contentDescription = null, modifier = Modifier.size(20.dp))
Spacer(Modifier.width(6.dp))
Text("查看视频", fontSize = 15.sp, fontWeight = FontWeight.Bold, color = Color(0xFF0F0F0F))
}
// 实心主按钮
Button(
onClick = {},
shape = RoundedCornerShape(12.dp),
colors = ButtonDefaults.buttonColors(containerColor = Color(0xFF0158FF)),
modifier = Modifier.fillMaxWidth().height(40.dp)
) {
Text("查看报告", fontSize = 15.sp, fontWeight = FontWeight.Bold)
}
```
### 开关
- 使用 Material3 的 `Switch`
- 颜色用 `SwitchDefaults.colors()` 自定义
```kotlin
var checked by remember { mutableStateOf(true) }
Switch(
checked = checked,
onCheckedChange = { checked = it },
colors = SwitchDefaults.colors(checkedTrackColor = Color(0xFF0158FF))
)
```
### 输入框 vs 纯展示
Figma 无法区分 `TextField` 与 `Text`,数据上都像 RECTANGLE + TEXT。
- **占位风格 + 输入态** → `TextField` / `OutlinedTextField`
- **静态展示** → `Text`
- **不确定时问用户**
```kotlin
var text by remember { mutableStateOf("") }
TextField(
value = text,
onValueChange = { text = it },
placeholder = { Text("请输入昵称", color = Color(0xFFB8B8B8)) },
modifier = Modifier.width(295.dp).height(48.dp),
shape = RoundedCornerShape(8.dp),
colors = TextFieldDefaults.colors(
unfocusedContainerColor = Color.White,
focusedContainerColor = Color.White,
unfocusedIndicatorColor = Color.Transparent,
focusedIndicatorColor = Color.Transparent
),
textStyle = TextStyle(fontSize = 15.sp, color = Color(0xFF0F0F0F)),
singleLine = true
)
```
## 宽度策略:固定 vs 弹性
Figma 多以 375px 画布为基准。宽度数值多为 **计算结果**,非设计意图,需反推后再写 Compose。
核心问题:**该元素是「固定宽」还是「吃掉剩余空间」?**
### 规则 1:单元素撑满屏宽
元素宽 + 左右边距 ≈ 屏宽(375),且左右对称或近似对称 → `Modifier.fillMaxWidth()` + `padding(horizontal = X.dp)`
- 例:335 + 左 20 + 右 20 = 375 → `fillMaxWidth().padding(horizontal = 20.dp)`
- **经验**:宽 > 屏宽约 85% 且左右边距对称 → 优先 `fillMaxWidth()` + padding
- 便于不同屏宽自适应
### 规则 2:并排时识别「弹性」一侧
横向多元素时,分别判断 **固定** 与 **弹性**:
- **固定侧**:头像、图标、按钮、固定宽标签等,用明确 `dp`
- **弹性侧**:宽 = 屏宽 − 各固定宽 − 间距,多为正文、描述、内容区,用 `Modifier.weight(1f)` 占满剩余
校验:`固定宽 + 弹性占位 + 间距 ≈ 屏宽`
```kotlin
// 例:头像 56dp + 间距 16dp + 文本弹性 + 左右边距 20dp = 375
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
Image(
painter = painterResource(R.drawable.ic_avatar),
contentDescription = "Avatar",
modifier = Modifier
.size(56.dp)
.clip(CircleShape)
)
Text(
text = "User Name",
modifier = Modifier.weight(1f), // 弹性侧
fontSize = 15.sp
)
}
```
### 规则 3:固定宽 + 居中
明显窄于屏幕且居中、不靠边 → 固定 `width` + 父级水平居中
- 例:295 居中于 375 → `Modifier.width(295.dp)` + 父级 `horizontalAlignment = CenterHorizontally`
- 常见于输入框、居中卡片
### LazyColumn 子项宽度
`LazyColumn` 内 item 默认 `Modifier.fillMaxWidth()`;`LazyColumn` 自身宽由父约束决定。
```kotlin
LazyColumn(
modifier = Modifier.fillMaxWidth()
) {
items(count = 20, key = { it }) { index ->
Text(
text = "Item $index",
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
)
}
}
```
## 多状态视图
Compose 用 **状态** 驱动条件 UI;状态变化体现在 `Modifier` 链与子节点。
### 选中 / 未选中
用 `remember { mutableStateOf() }` + 条件 `Modifier`:
```kotlin
var isSelected by remember { mutableStateOf(false) }
Card(
modifier = Modifier
.fillMaxWidth()
.height(120.dp)
.background(
color = if (isSelected) Color(0xFF0158FF) else Color.White,
shape = RoundedCornerShape(12.dp)
)
.border(
width = 2.dp,
color = if (isSelected) Color(0xFF0158FF) else Color(0xFFDCDCDC),
shape = RoundedCornerShape(12.dp)
)
.clickable { isSelected = !isSelected },
colors = CardDefaults.cardColors(containerColor = Color.Transparent),
shape = RoundedCornerShape(12.dp)
) {
Box(
modifier = Modifier.fillMaxSize(),
contentAlignment = Alignment.Center
) {
Text(
text = if (isSelected) "✓ Selected" else "选择",
color = if (isSelected) Color.White else Color(0xFF858A99),
fontSize = 16.sp,
fontWeight = FontWeight.Bold
)
}
}
```
示例:性别选择卡片
```kotlin
@Composable
fun GenderSelector() {
var selectedGender by remember { mutableStateOf<String?>(null) }
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
listOf("Male" to "男", "Female" to "女").forEach { (key, label) ->
Card(
modifier = Modifier
.weight(1f)
.height(80.dp)
.background(
color = if (selectedGender == key) Color(0xFF0158FF) else Color.White,
shape = RoundedCornerShape(12.dp)
)
.border(
width = 1.dp,
color = if (selectedGender == key) Color(0xFF0158FF) else Color(0xFFDCDCDC),
shape = RoundedCornerShape(12.dp)
)
.clickable { selectedGender = key },
colors = CardDefaults.cardColors(containerColor = Color.Transparent)
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text(
text = label,
color = if (selectedGender == key) Color.White else Color(0xFF0F0F0F),
fontWeight = FontWeight.Bold
)
}
}
}
}
}
```
### 禁用 / 可用(透明度)
结合 `Modifier.alpha()` 与 `.clickable(enabled = ...)`:
```kotlin
var isValid by remember { mutableStateOf(false) }
Button(
onClick = { /* handle */ },
enabled = isValid,
modifier = Modifier
.fillMaxWidth()
.height(40.dp)
.alpha(if (isValid) 1f else 0.3f),
colors = ButtonDefaults.buttonColors(
containerColor = Color(0xFF0158FF),
disabledContainerColor = Color(0xFF0158FF)
)
) {
Text("提交", color = Color.White, fontWeight = FontWeight.Bold)
}
```
### 叠放 / 交错卡片
设计为 **可交互** 的叠卡(滑动、拖拽、露出下层)时,**不要**只生成静态嵌套布局:
- 向用户确认:左右滑切卡(→ `HorizontalPager`)、拖拽(→ `Modifier.pointerInput`)或仅视觉重叠?
- 按答案给模板;缺省时可按上下文推测最可能方案
```kotlin
// 可滑动切卡时用 HorizontalPager
val pagerState = rememberPagerState(pageCount = { 3 })
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.height(300.dp)
) { page ->
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
elevation = CardDefaults.cardElevation(defaultElevation = 8.dp)
) {
Box(modifier = Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text("Card page + 1")
}
}
}
```
## 深色模式
通过 `MaterialTheme` 与系统状态接入深色模式,按工程配置选型。
### 使用 MaterialTheme 色板(推荐)
若工程有 `Theme.kt`,颜色应随主题走:
```kotlin
@Composable
fun MyCard() {
Card(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface,
contentColor = MaterialTheme.colorScheme.onSurface
)
) {
Text(
text = "Content",
color = MaterialTheme.colorScheme.primary,
style = MaterialTheme.typography.bodyMedium
)
}
}
```
### 手动判断深色
需要分支逻辑时使用 `isSystemInDarkTheme()`:
```kotlin
val isDark = isSystemInDarkTheme()
val backgroundColor = if (isDark) Color(0xFF1A1A1A) else Color.White
val textColor = if (isDark) Color.White else Color(0xFF0F0F0F)
Box(
modifier = Modifier
.fillMaxSize()
.background(backgroundColor),
contentAlignment = Alignment.Center
) {
Text("随系统深色", color = textColor)
}
```
### 动态取色(Android 12+)
Material You 可按系统壁纸生成 `dynamicLightColorScheme` / `dynamicDarkColorScheme`:
```kotlin
@Composable
fun DynamicThemeExample() {
val colorScheme = when {
isSystemInDarkTheme() -> dynamicDarkColorScheme(LocalContext.current)
else -> dynamicLightColorScheme(LocalContext.current)
}
MaterialTheme(colorScheme = colorScheme) {
// 应用内容
}
}
```
### 规则
- 已有 `Theme.kt` 且定义了 `colorScheme` → 颜色 **一律** 用 `MaterialTheme.colorScheme.*`
- 工程 **未** 配置深色 → **不要**擅自加深色,只出浅色稿代码
- 能用 `MaterialTheme` 时避免写死色值
## LazyColumn / LazyRow 列表项
### 基础尺寸
- 子项默认 `Modifier.fillMaxWidth()`
- 列表自身宽度在 `LazyColumn`/`LazyRow` 上设置,子项填满该宽度
### 使用 `key` 提升稳定性
列表项会增删改时,为 `items` 提供稳定 `key` 减少错误复用:
```kotlin
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
items(
count = items.size,
key = { index -> items[index].id }, // 稳定 id
contentType = { "item" }
) { index ->
ListItem(
modifier = Modifier.fillMaxWidth(),
item = items[index]
)
}
}
```
### 混合类型与 `contentType`
列表含多种 cell(头、正文、尾等)时,用 `contentType` 帮助组合项正确回收:
```kotlin
data class ListSection(
val type: String, // header / item / footer
val content: Any
)
LazyColumn {
items(
count = sections.size,
key = { index -> sections[index].content.hashCode() },
contentType = { index -> sections[index].type }
) { index ->
val section = sections[index]
when (section.type) {
"header" -> HeaderItem(section.content as String)
"item" -> RegularItem(section.content as Item)
"footer" -> FooterItem(section.content as String)
}
}
}
```
### 示例:带标题的列表
```kotlin
@Composable
fun UserListWithHeader(users: List<User>) {
LazyColumn(
modifier = Modifier.fillMaxSize()
) {
item {
Text(
text = "用户列表",
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
fontSize = 18.sp,
fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.primary
)
}
items(
count = users.size,
key = { index -> users[index].id }
) { index ->
Row(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 8.dp, horizontal = 16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(12.dp)
) {
Image(
painter = painterResource(R.drawable.ic_avatar),
contentDescription = "Avatar",
modifier = Modifier
.size(48.dp)
.clip(CircleShape)
)
Text(
text = users[index].name,
modifier = Modifier.weight(1f),
fontSize = 15.sp
)
Text(
text = users[index].status,
fontSize = 13.sp,
color = Color(0xFF858A99)
)
}
}
}
}
```
## 分割线
Material3 提供 `HorizontalDivider`,用于干净的分隔线。
### 基础用法
```kotlin
HorizontalDivider(
thickness = 1.dp,
color = Color(0xFFEEEEEE)
)
```
### 自定义上下间距
```kotlin
Column {
Text("Item 1")
HorizontalDivider(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = 16.dp),
thickness = 1.dp,
color = Color(0xFFDCDCDC)
)
Text("Item 2")
}
```
### 列表项之间
在相邻 item 之间插入分割线:
```kotlin
LazyColumn {
items(items.size) { index ->
ListItemContent(items[index])
if (index < items.size - 1) {
HorizontalDivider(
thickness = 1.dp,
color = Color(0xFFEEEEEE),
modifier = Modifier.padding(horizontal = 16.dp)
)
}
}
}
```
## Figma 节点解读(Compose 补充)
### 容器 + 图标 = 单个 Image
**FRAME**(背景色 + 圆角)内仅一个 **INSTANCE** 或 **VECTOR** 小图标时:
- 代码里是一个 `Image`,不要多层布局
- `.background()` = 外形容器(圆/圆角矩形等)
- `painter` = 图标资源
- 常见信号:外框圆角 ≥ 尺寸一半(近圆),内层明显更小
```kotlin
// Figma:FRAME 32×32 黑底圆角 16 → 内 INSTANCE 18×18 = 一个 Image
Image(
painter = painterResource(R.drawable.ic_arrow),
contentDescription = "Arrow",
modifier = Modifier
.size(32.dp)
.background(Color.Black, RoundedCornerShape(16.dp))
.padding(7.dp), // 18dp 图标在 32dp 内居中
colorFilter = ColorFilter.tint(Color.White)
)
```
### RECTANGLE 作背景
GROUP **第一个子节点** 为与 GROUP **同尺寸** 的 RECTANGLE 时:
- 该 RECTANGLE 是背景形,不是独立 composable
- 映射为父容器的 `.background()`
- 信号:首子、宽高与 GROUP 一致
```kotlin
// Figma:GROUP 150×60 → RECTANGLE 同尺寸 #F3F3F4 圆角 8 → TEXT
Box(
modifier = Modifier
.width(150.dp)
.height(60.dp)
.background(Color(0xFFF3F3F4), RoundedCornerShape(8.dp)),
contentAlignment = Alignment.Center
) {
Text("Button")
}
```
### 有 layoutMode 的 FRAME vs 无 layoutMode 的 GROUP
- **FRAME 且带 `layoutMode`**:自动布局 → `Column` / `Row`
- **GROUP 且无 `layoutMode`**:坐标摆放 → `Box` + `Modifier.offset()` 或嵌套 `Box`
```kotlin
// layoutMode VERTICAL → Column
Column(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Text("Item 1")
Text("Item 2")
}
// 无 layoutMode 的 GROUP → Box + offset
Box(
modifier = Modifier
.width(200.dp)
.height(200.dp)
) {
Text(
"Top-left text",
modifier = Modifier.offset(x = 10.dp, y = 20.dp)
)
Image(
painter = painterResource(R.drawable.ic_icon),
contentDescription = null,
modifier = Modifier
.size(48.dp)
.offset(x = 100.dp, y = 100.dp)
)
}
```
### 数值精度
Figma 常带多余小数,Compose 中宜取整:
- **`dp`**(布局、边距):取最近 **整数**(如 127.86→128.dp)
- **`sp`**(字号):取最近 **0.5**(如 15.27→15.5.sp)
- **例外**:接近标准尺寸则吸附(如 47.99dp→48.dp 触控目标)
FILE:references/platform-swiftui.md
# SwiftUI 映射:Figma → SwiftUI
> 作用:把 Figma 属性映射到 SwiftUI。
> 含基础映射与 **线上项目** 中常见的页面级写法。
## 布局选型
| Figma 结构 | 推荐视图 |
|---|---|
| 纵向堆叠 | `VStack` |
| 横向堆叠 | `HStack` |
| 重叠 / Z 序 | `ZStack` |
| 重复相似项(≥3) | `List` / `LazyVStack` / `LazyHStack` |
| 带导航栏页面 | `NavigationStack` + `.navigationTitle` |
| 可滚动内容 | `ScrollView` |
## 自动布局映射
| Figma 属性 | SwiftUI 对应 |
|---|---|
| layoutMode: VERTICAL | `VStack` |
| layoutMode: HORIZONTAL | `HStack` |
| itemSpacing | `spacing:` 参数 |
| padding* | `.padding()` |
| primaryAxisAlignItems: CENTER | `alignment` + `Spacer()` |
| counterAxisAlignItems: CENTER | `alignment: .center` |
| layoutGrow: 1 | `.frame(maxWidth: .infinity)` 或 `Spacer()` |
| primaryAxisSizingMode: FIXED | `.frame(height/width:)` |
## 尺寸换算
- Figma px → SwiftUI pt(约 1:1)
- 字号:`.system(size:)` 用 pt
## 页面级架构
以下贴近线上 iOS **真实结构**。生成时要有 **页面级** 意识。
### 多 Tab 页
设计出现 **多个 Tab**(≥2 个文案作导航)时:
- 顶部可滑动:`TabView` + `.tabViewStyle(.page)`,或自定义 `Picker`/分段控件
- 底部 Tab:系统 `TabView` 默认样式
- **不要**用纯 `Text` 当 Tab —— 无选中态与指示器
- 每个 Tab 内容拆成独立 `View`
- 输出:主视图(Tab 容器)+ 各内容视图
```swift
// 顶部自定义下划线 Tab(示例)
struct ContentView: View {
@State private var selectedTab = 0
let tabs = ["关注", "推荐", "热榜"]
var body: some View {
VStack(spacing: 0) {
HStack(spacing: 24) {
ForEach(tabs.indices, id: \.self) { index in
VStack(spacing: 4) {
Text(tabs[index])
.font(.system(size: 16, weight: selectedTab == index ? .bold : .regular))
.foregroundColor(selectedTab == index ? Color(hex: "0F0F0F") : Color(hex: "858A99"))
Rectangle()
.fill(selectedTab == index ? Color(hex: "0F0F0F") : .clear)
.frame(height: 2)
}
.onTapGesture { selectedTab = index }
}
}
.padding(.horizontal, 20)
TabView(selection: $selectedTab) {
FollowingView().tag(0)
RecommendView().tag(1)
HotListView().tag(2)
}
.tabViewStyle(.page(indexDisplayMode: .never))
}
}
}
```
### 导航栏 — 视为整体
导航栏是 **一个逻辑容器**。标准用 `NavigationStack` + `.toolbar`;自定义导航栏用单行 `HStack`,主内容放在其下并留好间距。
```swift
// 自定义导航栏
HStack {
Button(action: { dismiss() }) {
Image(systemName: "chevron.left")
.frame(width: 32, height: 32)
.background(Circle().fill(Color(hex: "000000")))
.foregroundColor(.white)
}
Spacer()
Text("设置")
.font(.system(size: 17, weight: .bold))
Spacer()
Color.clear.frame(width: 32, height: 32) // 平衡占位
}
.padding(.horizontal, 20)
```
### 导航栏按钮
- 返回/关闭:`Button` + `Image`;圆底用 `.background(Circle()...)`
- 简单图标 **不要**再套多余容器
### 图标 + 文字按钮
优先 `Button` 内 `HStack { Image + Text }`;`Label` 亦可控性稍弱。
```swift
// 描边按钮 + 图标
Button(action: {}) {
HStack(spacing: 6) {
Image("ic_video")
.resizable()
.frame(width: 20, height: 20)
Text("查看视频")
.font(.system(size: 15, weight: .bold))
.foregroundColor(Color(hex: "0F0F0F"))
}
.frame(maxWidth: .infinity)
.frame(height: 40)
.background(
RoundedRectangle(cornerRadius: 12)
.stroke(Color(hex: "DCDCDC"), lineWidth: 1)
)
}
// 实心主按钮
Button(action: {}) {
Text("查看报告")
.font(.system(size: 15, weight: .bold))
.foregroundColor(.white)
.frame(maxWidth: .infinity)
.frame(height: 40)
.background(RoundedRectangle(cornerRadius: 12).fill(Color(hex: "0158FF")))
}
```
### 开关
- 标准用 `Toggle`;自定义用 `.toggleStyle`
- 着色:`.tint(Color(...))`(iOS 15+)
```swift
Toggle("通知", isOn: $isEnabled)
.tint(Color(hex: "0158FF"))
```
### 输入框 vs 纯展示
Figma 无法区分输入与展示。
- 占位风格 + 输入态 → `TextField`
- 静态展示 → `Text`
- **不确定时问用户**
```swift
TextField("请输入昵称", text: $nickname)
.font(.system(size: 15))
.foregroundColor(Color(hex: "0F0F0F"))
.padding(.horizontal, 12)
.frame(width: 295, height: 48)
.background(
RoundedRectangle(cornerRadius: 8)
.stroke(Color(hex: "E5E5E5"), lineWidth: 1)
)
```
## 宽度策略:固定 vs 弹性
Figma 多为固定画布(375/390)。宽度是 **计算结果**,需推断意图。
核心问题:宽度是 **固定** 还是 **填满剩余**?
### 规则 1:单元素撑满宽
宽 + 左右边距 ≈ 屏宽 → `.frame(maxWidth: .infinity)` + `.padding(.horizontal, X)`
```swift
Text("标题")
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.horizontal, 20)
```
### 规则 2:并排时识别「弹性」方
- **固定**:头像、图标、按钮等,用明确 `.frame(width:)`
- **弹性**:正文区用 `Spacer()` 或让布局自然扩展
```swift
// 固定头像 + 弹性文字
HStack(spacing: 16) {
Image("avatar")
.resizable()
.frame(width: 56, height: 56)
.clipShape(Circle())
VStack(alignment: .leading, spacing: 4) {
Text("用户名").font(.system(size: 16, weight: .bold))
Text("描述文字").font(.system(size: 14)).foregroundColor(.gray)
}
Spacer()
}
.padding(.horizontal, 20)
```
### 规则 3:固定宽 + 居中
明显窄于屏且居中 → 显式 `.frame(width:)` + 父级居中
```swift
TextField("请输入昵称", text: $nickname)
.frame(width: 295, height: 48)
```
### 列表项宽度
- Item 用 `.frame(maxWidth: .infinity)`;`List`/`LazyVStack` 控制整体宽度
## 多状态视图
同一视图有多套外观(选中/未选、可用/禁用)时:
### 选中 / 未选中
用条件修饰符驱动状态:
```swift
struct GenderCard: View {
let title: String
let icon: String
let isSelected: Bool
var body: some View {
VStack(spacing: 8) {
Image(icon)
.resizable()
.frame(width: 48, height: 48)
Text(title)
.font(.system(size: 15, weight: .medium))
}
.frame(width: 140, height: 120)
.background(
RoundedRectangle(cornerRadius: 16)
.fill(isSelected ? Color(hex: "E8F0FF") : Color(hex: "F5F5F5"))
)
.overlay(
RoundedRectangle(cornerRadius: 16)
.stroke(isSelected ? Color(hex: "0158FF") : .clear, lineWidth: 2)
)
}
}
```
### 禁用 / 可用(透明度)
`.opacity()` + `.disabled()`,不要为每状态各做一个 View。
```swift
Button(action: { submit() }) {
Text("提交")
.frame(maxWidth: .infinity)
.frame(height: 48)
.background(RoundedRectangle(cornerRadius: 12).fill(Color(hex: "0158FF")))
.foregroundColor(.white)
}
.opacity(isValid ? 1.0 : 0.3)
.disabled(!isValid)
```
### 叠放卡片
结构相似、位置偏移的叠卡 → 可能是切卡交互。**不要**只生成静态多层 `View`。
应问用户交互方式;可选 `TabView(.page)`、`DragGesture` 或第三方栈。
## 视觉属性
### Color hex 扩展(生成代码中可含一次)
```swift
extension Color {
init(hex: String) {
let scanner = Scanner(string: hex)
var rgb: UInt64 = 0
scanner.scanHexInt64(&rgb)
self.init(
red: Double((rgb >> 16) & 0xFF) / 255.0,
green: Double((rgb >> 8) & 0xFF) / 255.0,
blue: Double(rgb & 0xFF) / 255.0
)
}
}
```
### 阴影
```swift
.shadow(color: Color(hex: "000000").opacity(0.1), radius: 4, x: 0, y: 2)
```
### 渐变
```swift
LinearGradient(
colors: [Color(hex: "FF6B6B"), Color(hex: "4ECDC4")],
startPoint: .top,
endPoint: .bottom
)
```
### 分角圆角(iOS 16+)
```swift
.clipShape(UnevenRoundedRectangle(
topLeadingRadius: 12,
topTrailingRadius: 12,
bottomLeadingRadius: 0,
bottomTrailingRadius: 0
))
```
### 复杂插画 — 导出位图
渐变 + 布尔运算 + 多层重叠 → **不要**在 SwiftUI 里硬画,应 **PNG/WebP**(2x/3x)+ `Image` + `.resizable()`。
## 深色模式
```swift
// ✅ Asset 中 light/dark 变体
Color("primaryText")
// ✅ Environment
@Environment(\.colorScheme) var colorScheme
let textColor = colorScheme == .dark ? Color(hex: "F0F0F0") : Color(hex: "0F0F0F")
// ✅ 动态 UIColor 桥接
Color(UIColor.dynamic(light: UIColor(hex: "0F0F0F"), dark: UIColor(hex: "F0F0F0")))
```
## Figma 节点解读(SwiftUI 补充)
通用规则见 `figma-interpretation.md` 与 SKILL。SwiftUI 侧重:
- **容器 + 小图标 = 单个 `Image`**:FRAME 包 VECTOR/INSTANCE → `Image` + `.background`,勿多层嵌套
- **RECTANGLE 作背景**:GROUP 首子同尺寸 RECTANGLE → 父级 `.background`
- **GROUP vs FRAME**:有 `layoutMode` → `VStack`/`HStack`;无 → `ZStack` + `.offset`/`.position`
- **取整**:pt 约整数,字号约 0.5
FILE:references/platform-uikit.md
# UIKit 映射:Figma → UIKit
> 作用:把 Figma 属性映射到 UIKit。
> 含基础映射与 **线上项目** 中常见的页面级写法。
## 布局选型
| Figma 结构 | 推荐做法 |
|---|---|
| 简单纵/横排 | `UIStackView` |
| 复杂相对关系 | Auto Layout(`NSLayoutConstraint`) |
| 重复相似项(≥3) | `UITableView` / `UICollectionView` |
| 可滚动内容 | `UIScrollView` |
| 视图重叠 | 约束驱动的 `UIView` 层级 |
## 自动布局映射
| Figma 属性 | UIKit 对应 |
|---|---|
| layoutMode: VERTICAL | `UIStackView` `axis = .vertical` |
| layoutMode: HORIZONTAL | `UIStackView` `axis = .horizontal` |
| itemSpacing | `stackView.spacing` |
| padding | `layoutMargins` + `isLayoutMarginsRelativeArrangement` |
| primaryAxisAlignItems: CENTER | `distribution = .equalCentering` 等 |
| counterAxisAlignItems: CENTER | `alignment = .center` |
| layoutGrow: 1 | `setContentHuggingPriority(.defaultLow)` 等 |
| primaryAxisSizingMode: FIXED | `heightAnchor` / `widthAnchor` |
## 尺寸换算
- Figma px → UIKit pt(约 1:1)
- 字号:`.systemFont(ofSize:)` 用 pt
## 页面级架构
以下贴近线上 iOS **真实结构**。
### 多 Tab 页
设计出现 **多个 Tab**(≥2 个文案作导航)时:
- 底部 Tab:`UITabBarController`
- 顶部分段切换:**自定义 Tab 条** + `UIPageViewController` 或子 VC 切换
- **不要**用纯 `UILabel` 当 Tab
- 每个 Tab 内容独立 `UIViewController`
- 输出:主控制器(Tab + 容器)+ 各内容 VC
```swift
// 顶部自定义 Tab + UIPageViewController(骨架示例)
class TabContainerViewController: UIViewController {
private let tabBar = UIStackView()
private let pageVC = UIPageViewController(transitionStyle: .scroll, navigationOrientation: .horizontal)
private var tabs: [UIButton] = []
private var viewControllers: [UIViewController] = []
private var selectedIndex = 0
private func setupTabBar() {
tabBar.axis = .horizontal
tabBar.spacing = 24
tabBar.alignment = .center
let titles = ["关注", "推荐", "热榜"]
for (index, title) in titles.enumerated() {
let button = UIButton(type: .system)
button.setTitle(title, for: .normal)
button.titleLabel?.font = .systemFont(ofSize: 16, weight: index == 0 ? .bold : .regular)
button.setTitleColor(index == 0 ? UIColor(hex: "0F0F0F") : UIColor(hex: "858A99"), for: .normal)
button.tag = index
button.addTarget(self, action: #selector(tabTapped(_:)), for: .touchUpInside)
tabs.append(button)
tabBar.addArrangedSubview(button)
}
}
@objc private func tabTapped(_ sender: UIButton) {
selectTab(at: sender.tag)
}
}
```
### 导航栏 — 视为整体
导航栏是 **一个逻辑容器**。标准用 `UINavigationController`;自定义用 `UIView` 容器,主内容约束到容器 `bottomAnchor`。
```swift
// 自定义导航栏
let navContainer = UIView()
navContainer.translatesAutoresizingMaskIntoConstraints = false
view.addSubview(navContainer)
let backButton = UIButton(type: .system)
backButton.setImage(UIImage(named: "ic_back"), for: .normal)
backButton.backgroundColor = UIColor(hex: "000000")
backButton.layer.cornerRadius = 16
backButton.translatesAutoresizingMaskIntoConstraints = false
let titleLabel = UILabel()
titleLabel.text = "设置"
titleLabel.font = .systemFont(ofSize: 17, weight: .bold)
titleLabel.translatesAutoresizingMaskIntoConstraints = false
navContainer.addSubview(backButton)
navContainer.addSubview(titleLabel)
// 主内容顶对齐到导航容器底
contentView.topAnchor.constraint(equalTo: navContainer.bottomAnchor, constant: 12).isActive = true
```
### 导航栏按钮
- 返回/关闭:`UIButton`;圆底用 `layer.cornerRadius` + `clipsToBounds`
- 简单图标 **不要**再套多余容器
### 图标 + 文字按钮
优先 `UIStackView` + `UIImageView` + `UILabel` 放在容器里;或 iOS 15+ `UIButton.Configuration`。
```swift
// UIButton.Configuration(iOS 15+)
var config = UIButton.Configuration.plain()
config.image = UIImage(named: "ic_video")
config.title = "查看视频"
config.imagePadding = 6
config.baseForegroundColor = UIColor(hex: "0F0F0F")
config.background.strokeColor = UIColor(hex: "DCDCDC")
config.background.strokeWidth = 1
config.background.cornerRadius = 12
let button = UIButton(configuration: config)
// StackView(兼容旧系统)
let container = UIView()
container.layer.cornerRadius = 12
container.layer.borderWidth = 1
container.layer.borderColor = UIColor(hex: "DCDCDC").cgColor
let stack = UIStackView()
stack.axis = .horizontal
stack.spacing = 6
stack.alignment = .center
let iconView = UIImageView(image: UIImage(named: "ic_video"))
iconView.widthAnchor.constraint(equalToConstant: 20).isActive = true
iconView.heightAnchor.constraint(equalToConstant: 20).isActive = true
let label = UILabel()
label.text = "查看视频"
label.font = .systemFont(ofSize: 15, weight: .bold)
label.textColor = UIColor(hex: "0F0F0F")
stack.addArrangedSubview(iconView)
stack.addArrangedSubview(label)
container.addSubview(stack)
```
### 开关
- 标准用 `UISwitch`;着色 `onTintColor`
- 完全自定义外观可考虑子类化 `UIControl`
```swift
let toggle = UISwitch()
toggle.onTintColor = UIColor(hex: "0158FF")
toggle.isOn = true
```
### 输入框 vs 纯展示
Figma 无法区分 `UITextField` 与 `UILabel`。
- 占位风格 + 输入态 → `UITextField`
- 静态展示 → `UILabel`
- **不确定时问用户**
```swift
let textField = UITextField()
textField.placeholder = "请输入昵称"
textField.font = .systemFont(ofSize: 15)
textField.textColor = UIColor(hex: "0F0F0F")
textField.layer.cornerRadius = 8
textField.layer.borderWidth = 1
textField.layer.borderColor = UIColor(hex: "E5E5E5").cgColor
textField.leftView = UIView(frame: CGRect(x: 0, y: 0, width: 12, height: 0))
textField.leftViewMode = .always
```
## 宽度策略:固定 vs 弹性
Figma 宽度多为 **计算结果**,需推断意图再建约束。
### 规则 1:单元素撑满宽
左右边距对称、近似撑满屏 → `leading`/`trailing` 钉父视图并带常数。
```swift
NSLayoutConstraint.activate([
titleLabel.leadingAnchor.constraint(equalTo: view.leadingAnchor, constant: 20),
titleLabel.trailingAnchor.constraint(equalTo: view.trailingAnchor, constant: -20)
])
```
### 规则 2:并排时识别「弹性」方
- **固定**:头像、图标、按钮等,显式 `widthAnchor`
- **弹性**:正文区不设死 `width`,由 leading/trailing 决定
- 用 `contentHuggingPriority` / `contentCompressionResistancePriority` 控制伸缩
```swift
// 固定头像 + 弹性标签
NSLayoutConstraint.activate([
avatarView.widthAnchor.constraint(equalToConstant: 56),
avatarView.heightAnchor.constraint(equalToConstant: 56),
avatarView.leadingAnchor.constraint(equalTo: cell.leadingAnchor, constant: 20),
nameLabel.leadingAnchor.constraint(equalTo: avatarView.trailingAnchor, constant: 16),
nameLabel.trailingAnchor.constraint(equalTo: cell.trailingAnchor, constant: -20),
])
```
### 规则 3:固定宽 + 居中
明显窄于屏且居中 → `widthAnchor` + `centerXAnchor`。
```swift
NSLayoutConstraint.activate([
textField.widthAnchor.constraint(equalToConstant: 295),
textField.heightAnchor.constraint(equalToConstant: 48),
textField.centerXAnchor.constraint(equalTo: view.centerXAnchor)
])
```
### Table/Collection Cell
- 内容相对 `contentView` 做 leading/trailing
- **不要**写死 cell 宽度,由列表控制
## 多状态视图
### 选中 / 未选中
可用 `isSelected` + 自绘,或 `UIButton` 各状态标题色。
```swift
class GenderCardView: UIControl {
override var isSelected: Bool {
didSet { updateAppearance() }
}
private func updateAppearance() {
backgroundColor = isSelected ? UIColor(hex: "E8F0FF") : UIColor(hex: "F5F5F5")
layer.borderColor = isSelected ? UIColor(hex: "0158FF").cgColor : UIColor.clear.cgColor
layer.borderWidth = isSelected ? 2 : 0
}
}
```
```swift
button.setTitleColor(UIColor(hex: "0F0F0F"), for: .selected)
button.setTitleColor(UIColor(hex: "858A99"), for: .normal)
```
### 禁用 / 可用(透明度)
`alpha` + `isEnabled` / `isUserInteractionEnabled`,勿为每状态各做一个 View。
```swift
submitButton.alpha = isValid ? 1.0 : 0.3
submitButton.isEnabled = isValid
```
### 叠放卡片
结构相似叠放 → 可能为切卡交互。**不要**只生成静态多层 View;问清交互;可用 `UIPageViewController`、手势或第三方。
## 视觉属性
### UIColor hex 扩展(生成代码中可含一次)
```swift
extension UIColor {
convenience init(hex: String) {
let scanner = Scanner(string: hex)
var rgb: UInt64 = 0
scanner.scanHexInt64(&rgb)
self.init(
red: CGFloat((rgb >> 16) & 0xFF) / 255.0,
green: CGFloat((rgb >> 8) & 0xFF) / 255.0,
blue: CGFloat(rgb & 0xFF) / 255.0,
alpha: 1.0
)
}
}
```
### 布局习惯
1. `translatesAutoresizingMaskIntoConstraints = false`
2. `NSLayoutConstraint.activate([])`
3. 线性布局优先 `UIStackView` 减少约束数量
4. 复杂/绝对位置用直接约束
### 阴影
```swift
view.layer.shadowColor = UIColor(hex: "000000").cgColor
view.layer.shadowOpacity = 0.1
view.layer.shadowRadius = 4
view.layer.shadowOffset = CGSize(width: 0, height: 2)
```
### 渐变
```swift
let gradientLayer = CAGradientLayer()
gradientLayer.colors = [UIColor(hex: "FF6B6B").cgColor, UIColor(hex: "4ECDC4").cgColor]
gradientLayer.startPoint = CGPoint(x: 0.5, y: 0)
gradientLayer.endPoint = CGPoint(x: 0.5, y: 1)
gradientLayer.frame = view.bounds
view.layer.insertSublayer(gradientLayer, at: 0)
```
### 分角圆角
```swift
view.layer.cornerRadius = 12
view.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner] // 仅顶部
```
### 复杂插画 — 导出位图
渐变 + 布尔 + 多层重叠 → **不要**纯代码还原;**PNG/WebP**(2x/3x)+ `UIImageView`。
## 深色模式
### 动态色
工程常见 `UIColor.dynamic(light:dark:)` 或封装。生成时:
```swift
// ✅ 支持深色:动态色
let textColor = UIColor.dynamic(
light: UIColor(hex: "0F0F0F"),
dark: UIColor(hex: "F0F0F0")
)
// ❌ 仅浅色:单路 hex(无深色策略时)
let textColor = UIColor(hex: "0F0F0F")
```
### 深色值推断(仅供参考)
Figma 多为浅色稿。若需补深色对应:
- 正文:浅 `#0F0F0F` → 深约 `#F0F0F0`
- 背景:浅 `#FFFFFF` → 深约 `#1C1C1E`
- 卡片:浅 `#F7F7F7` → 深约 `#2C2C2E`
- 分割线:浅 `#EEEEEE` → 深约 `#3A3A3C`
- 品牌色可略提亮(如 `#2965FF` → `#4D88FF`)
- **优先以工程已有 light/dark 对为准**
### 何时不推断深色
- 工程无动态色、无 Asset Appearances → 可能不支持深色,**不要**擅自加
- 用户明确只要浅色 → 用 `UIColor(hex:)`
## Figma 节点解读(UIKit 补充)
通用规则见 SKILL 与 `figma-interpretation.md`。UIKit 侧重:
- **容器 + 小图标 = 单个 `UIImageView`**:FRAME 包 VECTOR/INSTANCE → 圆角/背景 + `image`
- **RECTANGLE 作背景**:GROUP 首子同尺寸 RECTANGLE → 容器 `backgroundColor` 或背景子层
- **GROUP vs FRAME**:有 `layoutMode` → `UIStackView`;无 → 按 x/y 建约束
- **取整**:pt 约整数,字号约 0.5
FILE:references/platform-xml.md
# XML 布局映射:Figma → Android
> 作用:把 Figma 属性映射到 Android XML。
> 本文是 **映射表**,不是 Android 教程 —— 模型已掌握平台惯例。
> 亦包含 **线上真实项目** 中常用的写法。
## 布局选型
| Figma 结构 | 推荐布局 |
|---|---|
| 重叠多、相对关系复杂 | **ConstraintLayout**(默认) |
| 简单纵/横排、无重叠 | `LinearLayout` |
| 子视图叠放、Z 序 | `FrameLayout` |
| 重复相似项(≥3) | `RecyclerView` + item 布局 |
## 页面级架构
以下贴近线上 Android 的 **真实结构**。生成代码时要有 **页面级** 意识,而非只堆单个 View。
### 多 Tab 页
设计出现 **多个 Tab**(≥2 个文案作导航)时:
- **高度可能** 为 `TabLayout` + `ViewPager2` —— 标准切 Tab 方案
- **不要**用纯 `TextView` 当 Tab —— 无选中态、指示器与滑动联动
- Tab 下方内容区 **常为** `ViewPager2`,每 Tab 对应独立 Fragment
- 输出:主布局(TabLayout + ViewPager2)+ 各 Fragment 布局
- **注意**:强信号非绝对;若明显是静态页上装饰性「标签」,应调整;不确定则 **追问**。
```xml
<!-- 标准 TabLayout + ViewPager2 -->
<com.google.android.material.tabs.TabLayout
android:id="@+id/tabLayout"
android:layout_width="0dp"
android:layout_height="48dp"
app:tabIndicatorColor="#0F0F0F"
app:tabSelectedTextColor="#0F0F0F"
app:tabTextColor="#858A99"
app:tabGravity="center"
app:tabMode="fixed" />
<androidx.viewpager2.widget.ViewPager2
android:id="@+id/viewPager"
android:layout_width="0dp"
android:layout_height="0dp" />
```
### 导航栏 — 视为整体
导航栏(返回 + 标题,可有右侧操作)是 **一个逻辑容器**。下方主内容区应约束到 **导航栏底部**,而非栏内某个子 View。
**实现**:返回与标题放在同一容器(ConstraintLayout/Toolbar/FrameLayout),主内容用 `layout_constraintTop_toBottomOf="@id/navbar"`,避免用绝对 margin 硬算。
**间距**:Figma 里内容 y=112、导航区占 y=0~100(含状态栏示意)时,**栏底到内容顶** 的真实间距可能是 **12dp**,不是 68 或 112。需减去导航栏/示意区高度;注意 Android 状态栏在布局树外。
### 导航栏按钮
- **返回/关闭**:用 `ImageView`,可 `background` + `src` 组合
- 圆形底:`android:background` 用圆形 shape
- 图标:`android:src`
- 也可能是一张整张切图 —— 不确定时一个 `ImageView` 即可
- 简单图标按钮 **不要**再套一层 `FrameLayout`
```xml
<!-- 返回:圆形底 + 图标 -->
<ImageView
android:id="@+id/btnBack"
android:layout_width="32dp"
android:layout_height="32dp"
android:background="@drawable/placeholder"
android:src="@drawable/placeholder"
android:scaleType="centerInside"
android:contentDescription="返回" />
<!-- background=圆 #000000,src=白色箭头 -->
```
### 图标 + 文字按钮
`MaterialButton` 的 `app:icon` 在部分机型/主题下易出渲染问题。
**优先** `LinearLayout` + `ImageView` + `TextView`:
```xml
<!-- 描边按钮 + 图标 -->
<LinearLayout
android:id="@+id/btnVideo"
android:layout_width="0dp"
android:layout_height="40dp"
android:orientation="horizontal"
android:gravity="center"
android:background="@drawable/placeholder"
android:clickable="true"
android:focusable="true">
<!-- background = 圆角描边 #DCDCDC,12dp,白底 -->
<ImageView
android:layout_width="20dp"
android:layout_height="20dp"
android:src="@drawable/placeholder" />
<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="6dp"
android:text="查看视频"
android:textSize="15sp"
android:textColor="#0F0F0F"
android:textStyle="bold" />
</LinearLayout>
<!-- 实心主按钮(可无图标) -->
<TextView
android:id="@+id/btnReport"
android:layout_width="0dp"
android:layout_height="40dp"
android:gravity="center"
android:text="查看报告"
android:textSize="15sp"
android:textColor="#FFFFFF"
android:textStyle="bold"
android:background="@drawable/placeholder" />
<!-- background = 圆角矩形 #0158FF,12dp -->
```
仅当纯文字按钮且默认样式足够时,再用 `MaterialButton`。
### 开关
- **优先** `SwitchCompat`(`androidx.appcompat.widget.SwitchCompat`),跨 API/主题更稳
- `MaterialSwitch` 在部分 Material 主题下可能显示异常
- 完全自定义外观时,`SwitchCompat` + 自定义 `thumb`/`track` 更易控
### 多状态 — 使用 selector
同一 View 有多套外观(选中/未选、可用/禁用、按下/常态)时:
- **用 `selector` drawable** 合并状态,**不要**在代码里切换多张图
- 父 View 状态通过 `android:duplicateParentState="true"` 传给子 View
- 常用:`state_selected`、`state_activated`、`state_pressed`、`state_enabled`
```xml
<!-- drawable/bg_gender_card_selector.xml -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/bg_gender_card_selected" />
<item android:drawable="@drawable/bg_gender_card_unselected" />
</selector>
<!-- drawable/ic_check_selector.xml(可用透明度控制显隐) -->
<selector xmlns:android="http://schemas.android.com/apk/res/android">
<item android:state_selected="true" android:drawable="@drawable/ic_check_selected" />
<item android:drawable="@android:color/transparent" />
</selector>
```
代码里只需 `cardFemale.setSelected(true)`,子 View 带 `duplicateParentState` 会同步。
### 卡片内部 — 优先 ConstraintLayout
卡片内多子元素需精确定位(右上图标、中部图、底部文案等)时:
- 卡片根用 `ConstraintLayout` —— 更扁、更高效
- 仅当子 View 纯粹重叠、无相对关系时才用 `FrameLayout`
### 输入框 vs 纯展示
Figma 无法区分 `EditText` 与 `TextView`,数据上都是 RECTANGLE + TEXT。
- 文案像 **占位**(「请选择生日」「请输入姓名」)且带输入框样式(边框、单行、浅色字)→ 多为 **EditText**
- **不确定时问用户** —— 功能判断无法从设计数据可靠得出
- 生成 `EditText` 时用 `android:hint`,并设合适 `android:inputType`
```xml
<!-- 输入框示例 -->
<EditText
android:id="@+id/etNickname"
android:layout_width="295dp"
android:layout_height="48dp"
android:background="@drawable/bg_input"
android:paddingStart="12dp"
android:hint="请输入昵称"
android:textColorHint="#B8B8B8"
android:textSize="15sp"
android:textColor="#0F0F0F"
android:inputType="text"
android:maxLines="1" />
```
### 列表项高度对齐
列表项有 **左侧块** 与 **右侧内容** 时,结合设计数据判断:
- 多看 **几条** item:若左侧高度在各条间一致、不随右侧文案变长而变 → 多为 **等高**(顶对顶底对底或同固定高)
- 若左侧明显随内容变高 → 各自 `wrap_content`
- **以观测为准,非铁律** —— 需结合当前稿
## Figma 节点解读(Android)
以下为 **经验启发式**,须与真实节点数据核对。
### 系统组件 — 跳过
**iOS 系统装饰** 类节点不要生成 Android 代码:
- 名称含 `StatusBar`、`HomeIndicator`、`NavigationBar` 等
- 多为 Figma 占位,真机由系统处理
- 信号:屏顶/底 y 接近边缘的 INSTANCE;或 **同位置重复节点**(Figma 冗余)只保留一份
### 不可见节点 — 跳过
树里有但无像素输出的节点不生成 View:
- **VECTOR** 无填充且描边均不可见 → 等效不可见
- **`absoluteRenderBounds: null`** → 无渲染输出,跳过
- 与节点自身 `visible: false` 不同(后者已在拉取脚本侧过滤,见 `scripts/src/figma-fetch.js`)
### 容器 + 图标 = 单个 ImageView
**FRAME**(背景色 + 圆角)内仅一个 **INSTANCE/VECTOR** 小图标时:
- 代码为一个 `ImageView`,背景 + `src`
- 常见信号:外框近圆(圆角大)、内层明显更小
### VECTOR/ELLIPSE 组合 = 单张图
同一 FRAME 内多个小矢量拼成图标 → **一张** drawable,**不要**多个 View。
### RECTANGLE 作背景
GROUP **首子 RECTANGLE** 与 GROUP **同尺寸** → 背景形,映射到父容器 `android:background`。
### GROUP vs FRAME
- **FRAME 有 `layoutMode`**:自动布局 → `LinearLayout` 或 ConstraintLayout 链
- **GROUP 无 `layoutMode`**:绝对坐标 → ConstraintLayout,用相对 GROUP 原点的偏移建约束
### Tab 选中态
Tab 条多个 Text **字色不同**(如一 #0F0F0F、余 #858A99)→ 选中/未选中,用 `tabSelectedTextColor` / `tabTextColor`,不要逐 Tab 写死。
### 小数取整
布局用 dp 取整;字号 sp 可约 0.5;接近标准值(如 47.99→48dp)可吸附。
### 复杂插画 — 导出位图
含渐变、布尔运算、多层重叠的插画 → **不要**强行 Vector Drawable,应 **PNG/WebP**(2x/3x)+ `ImageView`。
### 多状态页面分析
同一页多 Frame 不同状态时:
1. 先找 **跨状态相同** 的节点(静态)
2. 再 diff **变化项**(色、字、可见性、透明度等)
3. **XML 注释** 中注明状态含义
4. 优先 **selector / alpha / 状态属性**,少用手动 `VISIBLE/GONE`
5. XML 后附 **状态变化摘要表** 供业务实现
### 禁用/可用(透明度)
两状态间 `opacity` 不同 → 可用 **disabled/enabled** 模式:`android:alpha` + `clickable`/`enabled`,不必单独 drawable。
## ConstraintLayout 子 View 尺寸
结合 **真实项目** 经验,在 ConstraintLayout 内为子 View 选型:
| 内容类型 | 宽 × 高 | 场景 |
|---|---|---|
| 文本 / 自适应 | `wrap_content` × `wrap_content` | 标签、标题默认 |
| 横向撑满 | `0dp` × `wrap_content` | Start+End 约束时 |
| 小指示图标 | `12dp` × `12dp` | 圆点、状态点 |
| 行内图标 | `20dp` × `20dp` | 与文字并排 |
| 标准操作图标 | `24dp` × `24dp` | Toolbar 等(Material 常见) |
| 中等图标 | `32dp` × `32dp` | 功能入口、导航 |
| 触控区 / 头像 | `48dp` × `48dp` | 按钮、头像(约最小触控) |
| 分割线 | `match_parent` × `1px` | 区块之间横线 |
| 全宽可滚区域 | `match_parent` × `0dp` | RecyclerView 等占满剩余 |
| 行容器定高 | `match_parent` × `44dp`~`56dp` | 设置行、列表行 |
**分割线**:普通 `View` + `background`:
```xml
<View
android:layout_width="match_parent"
android:layout_height="1px"
android:background="@color/divider"
app:layout_constraintTop_toBottomOf="@id/prevView" />
```
发线级分割线用 **1px** 而非 1dp(高 DPI 下 1dp 偏粗)。
### ImageView
图标节点一般映射为简单 `ImageView` + `src`/`srcCompat`:
```xml
<ImageView
android:layout_width="24dp"
android:layout_height="24dp"
app:srcCompat="@drawable/ic_arrow_right" />
```
**默认**:多数情况只需 `src`/`srcCompat`。
**何时 `background` + `src`:**
- Figma 有实心圆/矩底托住图标 → shape `background` + 图标 `src`
- **需深色模式** → `background`(`@color/`)+ 矢量 `src` + `app:tint`(`@color/`),三色随主题,不必各做一套深浅资源
- 可点区域大于图标本身 → 外包容器或 padding
**深色模式**:若扫描/上下文表明工程支持深色,图标容器可优先「底+矢量+tint」,而非裸 `src`:
```xml
<!-- 深色友好:底 + 矢量 + tint -->
<ImageView
android:layout_width="32dp"
android:layout_height="32dp"
android:background="@drawable/bg_icon_circle"
app:srcCompat="@drawable/ic_settings"
app:tint="@color/icon_primary" />
```
三者均引用主题色资源,深浅切换自动生效。
```xml
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:padding="16dp">
<TextView
android:id="@+id/tvTitle"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="标题"
android:textSize="17sp"
android:textColor="#0F0F0F"
android:textStyle="bold"
app:layout_constraintTop_toTopOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintEnd_toStartOf="@id/ivArrow" />
<ImageView
android:id="@+id/ivArrow"
android:layout_width="24dp"
android:layout_height="24dp"
android:src="@drawable/placeholder"
app:layout_constraintTop_toTopOf="@id/tvTitle"
app:layout_constraintBottom_toBottomOf="@id/tvTitle"
app:layout_constraintEnd_toEndOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
```
常用约束:**居中**(顶底同锚)、**链**、**0dp 撑满**、**Guideline**、**Barrier**。
## 自动布局映射
| Figma 属性 | XML 对应 |
|---|---|
| layoutMode: VERTICAL | 纵向 LinearLayout / ConstraintLayout 竖链 |
| layoutMode: HORIZONTAL | 横向 LinearLayout / ConstraintLayout 横链 |
| itemSpacing | 子 View 的 margin |
| padding* | 父 `android:padding` |
| primaryAxisAlignItems: CENTER | `gravity` / chain |
| counterAxisAlignItems: CENTER | `gravity` 垂直/水平居中 |
| layoutGrow: 1 | Linear 的 `layout_weight` / Constraint 的 `0dp` |
| primaryAxisSizingMode: FIXED | 固定 dp |
| counterAxisSizingMode: AUTO | `wrap_content` |
## 尺寸换算
- Figma px → Android dp(1:1)
- Figma 字号 px → Android sp(1:1)
## 宽度策略:固定 vs 弹性
Figma 设计稿通常基于 375px 宽画布。Figma 里的宽度值是**计算结果**,不是设计意图。需要反推意图来决定 Android 属性。
核心问题:**这个元素的宽度是"固定尺寸"还是"填满剩余空间"?**
### 规则 1:单个元素撑满屏幕宽度
元素宽度 + 左右偏移 ≈ 屏幕宽度(375),且左右边距对称或近似对称 → `match_parent` + `marginHorizontal`
- 例:宽 335 + 左 20 + 右 20 = 375 → `match_parent` + `marginHorizontal="20dp"`
- **经验阈值**:宽度占屏幕 >85% 且左右边距近似对称 → 优先 `match_parent` + margin
- 适配优势:不同屏幕宽度下自动拉伸
### 规则 2:并排元素中识别"弹性方"
多个元素横向排列时,判断每个元素是**固定方**还是**弹性方**:
- **固定方**:有明确视觉尺寸的元素——头像、图标、按钮、固定宽度标签等。用固定 dp 值。
- **弹性方**:宽度 = 屏幕宽 - 固定方宽度 - 各间距的元素——通常是文本、描述、内容区域。用 `0dp`(match_constraints)+ 约束填满剩余空间。
判断方法:计算 `固定方宽度 + 弹性方宽度 + 所有间距 ≈ 屏幕宽度` 是否成立。如果成立,弹性方不应写死宽度。
- 例:头像 56dp + 间距 16dp + 文本 263dp + 右边距 20dp + 左边距 20dp = 375
→ 头像是固定方(56dp),文本是弹性方
→ 文本:`layout_width="0dp"` + `constraintStart_toEndOf="@id/ivAvatar"` + `constraintEnd_toEndOf="parent"` + `marginStart="16dp"` + `marginEnd="20dp"`
- 例:标签 "姓名" 40dp + 间距 12dp + 输入框 283dp + 右边距 20dp + 左边距 20dp = 375
→ 标签固定,输入框弹性
### 规则 3:固定宽度 + 居中
元素明显比屏幕窄,且居中放置,不靠左右边缘 → 固定宽度 + 居中约束
- 例:宽 295 居中在 375 里 → `layout_width="295dp"` + `constraintStart/End` 居中
- 适用于输入框、小卡片等独立居中的元素
### RecyclerView item 宽度
- item 布局始终用 `match_parent`,宽度由 RecyclerView 本身控制
- RecyclerView 自身的宽度按上述规则判断
## 阴影
```xml
<!-- MaterialCardView 提供 elevation 阴影 -->
<com.google.android.material.card.MaterialCardView
app:cardElevation="4dp"
app:cardCornerRadius="12dp">
```
自定义阴影可用 `elevation` + `outlineSpotShadowColor`(API 28+)或带阴影层的 drawable。
## 渐变
```xml
<!-- 在 drawable 中定义 -->
<shape>
<gradient
android:startColor="#FF6B6B"
android:endColor="#4ECDC4"
android:angle="90" />
</shape>
```
FILE:scripts/package.json
{
"name": "figma-mobile-cn-scripts",
"private": true,
"type": "module",
"engines": {
"node": ">=18"
},
"scripts": {
"figma-fetch": "node src/figma-fetch.js",
"project-scan": "node src/project-scan.js",
"feedback-analyze": "node src/feedback-analyze.js"
}
}
FILE:scripts/src/feedback-analyze.js
#!/usr/bin/env node
/**
* 解析 feedback-log.md,按平台与 Issue 关键词汇总,输出规则候选提示。
* 运行:node src/feedback-analyze.js [path/to/feedback-log.md]
*/
import { readFileSync, existsSync } from "node:fs";
import { resolve } from "node:path";
function parseFeedbackLog(content) {
const entries = [];
const blocks = content.split(/^## /m).slice(1);
for (const block of blocks) {
const lines = block.split("\n");
const dateLine = lines[0]?.trim() ?? "";
let platform = "";
let issue = "";
let ruleCandidate = "";
for (const line of lines) {
if (line.includes("**Platform**:")) platform = line.replace(/.*\*\*Platform\*\*:\s*/, "").trim();
if (line.includes("**Issue**:")) issue = line.replace(/.*\*\*Issue\*\*:\s*/, "").trim();
if (line.includes("**Rule candidate**:"))
ruleCandidate = line.replace(/.*\*\*Rule candidate\*\*:\s*/, "").trim();
}
if (issue || ruleCandidate) entries.push({ dateLine, platform, issue, ruleCandidate });
}
return entries;
}
function main() {
const args = process.argv.slice(2);
const path = args[0] ? resolve(args[0]) : resolve(process.cwd(), "feedback-log.md");
if (!existsSync(path)) {
console.error(`File not found: path`);
process.exit(1);
}
const content = readFileSync(path, "utf8");
const entries = parseFeedbackLog(content);
const byPlatform = {};
const issues = [];
const rules = [];
for (const e of entries) {
const p = e.platform || "unknown";
byPlatform[p] = (byPlatform[p] ?? 0) + 1;
if (e.issue) issues.push(e.issue);
if (e.ruleCandidate) rules.push(e.ruleCandidate);
}
const keywords = new Map();
for (const i of issues) {
for (const w of i.split(/[\s,,。.]+/).filter((x) => x.length > 1)) {
keywords.set(w, (keywords.get(w) ?? 0) + 1);
}
}
const topKw = [...keywords.entries()].sort((a, b) => b[1] - a[1]).slice(0, 15);
const out = {
totalEntries: entries.length,
byPlatform,
topIssueKeywords: Object.fromEntries(topKw),
ruleCandidates: [...new Set(rules)],
summary:
entries.length === 0
? "暂无结构化条目(确保使用 `## YYYY-MM-DD HH:MM` 分段)。"
: `共 entries.length 条反馈;可按 Rule candidate 合并为 Skill 补充规则。`,
};
console.log(JSON.stringify(out, null, 2));
}
main();
FILE:scripts/src/figma-fetch.js
#!/usr/bin/env node
/**
* Figma REST API:拉取节点树、简化 JSON、对比模式、SVG 导出。
* 需要环境变量 FIGMA_TOKEN(或项目根目录 .env)。
* 运行:Node.js 18+,无需 npm 依赖(仅需本目录 package.json 的 "type":"module")。
*/
import { loadDotEnv } from "./load-env.js";
const FIGMA_API = "https://api.figma.com/v1";
export function parseFigmaUrl(url) {
let u;
try {
u = new URL(url.trim());
} catch {
throw new Error(`INVALID_URL: url`);
}
const path = u.pathname;
const designMatch = path.match(/\/(?:design|file)\/([^/]+)/);
if (!designMatch) throw new Error(`INVALID_URL: cannot find file key in url`);
const fileKey = designMatch[1];
const nodeParam = u.searchParams.get("node-id") ?? u.searchParams.get("node_id");
if (!nodeParam) throw new Error(`INVALID_URL: missing node-id in query string`);
const nodeId = nodeParam.replace(/-/g, ":");
return { fileKey, nodeId };
}
function getToken() {
const t = process.env.FIGMA_TOKEN?.trim();
if (!t) {
console.error("FIGMA_TOKEN_NOT_SET");
process.exit(2);
}
return t;
}
async function figmaGet(path, token) {
const res = await fetch(`FIGMA_APIpath`, {
headers: { "X-Figma-Token": token },
});
const text = await res.text();
let body;
try {
body = JSON.parse(text);
} catch {
throw new Error(`Figma API non-JSON response (res.status): text.slice(0, 200)`);
}
if (!res.ok) {
const err = body;
throw new Error(`Figma API res.status: err.err ?? err.message ?? text.slice(0, 300)`);
}
return body;
}
const MAX_CHILD_WARN = 200;
function pick(obj, keys) {
const out = {};
for (const k of keys) {
if (obj[k] !== undefined) out[k] = obj[k];
}
return out;
}
function simplifyNode(node, depth, maxDepth) {
if (node.visible === false) return {};
const base = {
id: node.id,
name: node.name,
type: node.type,
};
const t = node.type;
const layoutKeys = [
"layoutMode",
"primaryAxisSizingMode",
"counterAxisSizingMode",
"primaryAxisAlignItems",
"counterAxisAlignItems",
"paddingLeft",
"paddingRight",
"paddingTop",
"paddingBottom",
"itemSpacing",
"layoutAlign",
"layoutGrow",
"layoutSizingHorizontal",
"layoutSizingVertical",
];
const visualKeys = [
"fills",
"strokes",
"strokeWeight",
"strokeAlign",
"effects",
"opacity",
"blendMode",
"cornerRadius",
"rectangleCornerRadii",
];
if (t === "TEXT") {
Object.assign(
base,
pick(node, [
"characters",
"style",
"characterStyleOverrides",
"styleOverrideTable",
])
);
} else {
Object.assign(base, pick(node, [...layoutKeys, ...visualKeys]));
}
if (t === "INSTANCE") {
Object.assign(
base,
pick(node, ["componentId", "variantProperties", "componentProperties"])
);
}
const rawChildren = node.children;
if (!rawChildren?.length) return base;
if (depth >= maxDepth) {
return {
...base,
_truncated: true,
_childCount: rawChildren.length,
};
}
if (rawChildren.length > MAX_CHILD_WARN) {
base._largeChildList = true;
base._childCount = rawChildren.length;
}
const children = [];
for (const ch of rawChildren) {
const s = simplifyNode(ch, depth + 1, maxDepth);
if (Object.keys(s).length > 0) children.push(s);
}
if (children.length) base.children = children;
return base;
}
export function simplifyTree(root, maxDepth) {
return simplifyNode(root, 0, maxDepth);
}
async function fetchNodes(fileKey, nodeIds, token) {
const ids = nodeIds.map((id) => encodeURIComponent(id)).join(",");
const data = await figmaGet(`/files/fileKey/nodes?ids=ids`, token);
if (!data.nodes) throw new Error("Figma API: empty nodes in response");
return { name: data.name, nodes: data.nodes };
}
async function exportSvg(fileKey, nodeIds, token) {
const ids = nodeIds.map((id) => encodeURIComponent(id)).join(",");
const data = await figmaGet(`/images/fileKey?ids=ids&format=svg`, token);
return data.images ?? {};
}
function diffSummary(a, b, pathPrefix = "") {
const lines = [];
const keys = new Set([...Object.keys(a), ...Object.keys(b)]);
keys.delete("id");
for (const k of keys) {
const p = pathPrefix ? `pathPrefix.k` : k;
const va = a[k];
const vb = b[k];
if (va === undefined && vb !== undefined) lines.push(`+ p`);
else if (va !== undefined && vb === undefined) lines.push(`- p`);
else if (typeof va === "object" && va !== null && typeof vb === "object" && vb !== null) {
lines.push(...diffSummary(va, vb, p));
} else if (JSON.stringify(va) !== JSON.stringify(vb)) {
lines.push(`~ p`);
}
}
return lines;
}
function parseArgs(argv) {
const args = [...argv];
const flags = {
compare: false,
depth: 5,
exportSvg: false,
nodes: null,
json: false,
};
const positional = [];
while (args.length) {
const x = args.shift();
if (x === "--compare") flags.compare = true;
else if (x === "--json") flags.json = true;
else if (x === "--export-svg") flags.exportSvg = true;
else if (x === "--depth") {
const n = parseInt(args.shift() ?? "", 10);
if (!Number.isFinite(n) || n < 1) throw new Error("--depth requires positive integer");
flags.depth = n;
} else if (x === "--nodes") {
flags.nodes = args.shift() ?? "";
} else if (!x.startsWith("-")) positional.push(x);
else throw new Error(`Unknown flag: x`);
}
return { flags, positional };
}
async function main() {
loadDotEnv();
const { flags, positional } = parseArgs(process.argv.slice(2));
if (flags.exportSvg) {
const token = getToken();
const extraIds = flags.nodes
? flags.nodes.split(/[,\s]+/).map((s) => s.trim().replace(/-/g, ":")).filter(Boolean)
: [];
if (positional.length < 1) {
console.error("Usage: node src/figma-fetch.js <figma-url> --export-svg [--nodes id1,id2]");
process.exit(1);
}
const { fileKey, nodeId } = parseFigmaUrl(positional[0]);
const ids = [nodeId, ...extraIds];
const urls = await exportSvg(fileKey, ids, token);
console.log(JSON.stringify({ fileKey, svgUrls: urls }, null, 2));
return;
}
if (flags.compare) {
if (positional.length < 2) {
console.error("Usage: node src/figma-fetch.js --compare <url1> <url2> [--depth N]");
process.exit(1);
}
const token = getToken();
let maxDepth = flags.depth;
const parsed = positional.map((u) => parseFigmaUrl(u));
const trees = [];
let autoDeepened = false;
for (const p of parsed) {
const { nodes } = await fetchNodes(p.fileKey, [p.nodeId], token);
const doc = nodes[p.nodeId]?.document;
if (!doc) throw new Error(`No document for node p.nodeId`);
let simplified = simplifyTree(doc, maxDepth);
const sc = JSON.stringify(simplified);
if (sc.includes('"_truncated":true') && maxDepth < 15) {
maxDepth = 15;
autoDeepened = true;
simplified = simplifyTree(doc, maxDepth);
}
trees.push(simplified);
}
const d = diffSummary(trees[0], trees[1]);
const out = {
mode: "compare",
depthUsed: maxDepth,
autoDeepened,
diffLines: d.slice(0, 200),
diffTruncated: d.length > 200,
trees,
};
console.log(JSON.stringify(out, null, flags.json ? 2 : 0));
return;
}
if (positional.length < 1) {
console.error(
"Usage: node src/figma-fetch.js <figma-url> [--depth N]\n" +
" node src/figma-fetch.js --compare <url1> <url2> [--depth N]\n" +
" node src/figma-fetch.js <figma-url> --export-svg [--nodes id1,id2]\n" +
"(在 scripts/ 目录下;亦可用 npm run figma-fetch -- <参数>)"
);
process.exit(1);
}
const token = getToken();
const { fileKey, nodeId } = parseFigmaUrl(positional[0]);
let maxDepth = flags.depth;
const { name, nodes } = await fetchNodes(fileKey, [nodeId], token);
const doc = nodes[nodeId]?.document;
if (!doc) throw new Error(`No document for node nodeId`);
let simplified = simplifyTree(doc, maxDepth);
if (JSON.stringify(simplified).includes('"_truncated":true') && maxDepth < 15) {
maxDepth = 15;
simplified = simplifyTree(doc, maxDepth);
process.stderr.write(
"[figma-fetch] 子树较深,已自动将 depth 提升到 " + maxDepth + "\n"
);
}
const out = {
fileKey,
fileName: name,
nodeId,
depthUsed: maxDepth,
simplified,
};
console.log(JSON.stringify(out, null, flags.json ? 2 : 0));
}
main().catch((e) => {
console.error(e instanceof Error ? e.message : e);
process.exit(1);
});
FILE:scripts/src/load-env.js
import { readFileSync, existsSync } from "node:fs";
import { resolve } from "node:path";
/** 从项目根 `.env` 加载 KEY=value(不覆盖已有环境变量) */
export function loadDotEnv(cwd = process.cwd(), envPath) {
const p = envPath ?? resolve(cwd, ".env");
if (!existsSync(p)) return;
const raw = readFileSync(p, "utf8");
for (const line of raw.split("\n")) {
const t = line.trim();
if (!t || t.startsWith("#")) continue;
const eq = t.indexOf("=");
if (eq <= 0) continue;
const key = t.slice(0, eq).trim();
let val = t.slice(eq + 1).trim();
if (
(val.startsWith('"') && val.endsWith('"')) ||
(val.startsWith("'") && val.endsWith("'"))
) {
val = val.slice(1, -1);
}
if (process.env[key] === undefined) process.env[key] = val;
}
}
FILE:scripts/src/project-scan.js
#!/usr/bin/env node
/**
* 轻量扫描 Android / iOS 工程资源,输出与 generation 兼容的 JSON 报告。
* Node.js 18+,无需额外 npm 包。
*/
import { readFileSync, readdirSync, statSync, existsSync, writeFileSync } from "node:fs";
import { join, resolve } from "node:path";
function normalizeHex(hex) {
let h = hex.trim().toUpperCase();
if (h.startsWith("#")) h = h.slice(1);
if (h.length === 8) return `#h.slice(2)`;
if (h.length === 6) return `#h`;
return hex.startsWith("#") ? hex : `#hex`;
}
function parseAndroidColors(xml) {
const out = {};
const re = /<color\s+name="([^"]+)"[^>]*>([^<]+)<\/color>/g;
let m;
while ((m = re.exec(xml))) {
out[m[1]] = m[2].trim();
}
return out;
}
function parseAndroidStrings(xml) {
const out = {};
const re = /<string\s+name="([^"]+)"[^>]*>([^<]*)<\/string>/g;
let m;
while ((m = re.exec(xml))) {
out[m[1]] = m[2].trim();
}
return out;
}
function parseAndroidDimens(xml) {
const out = {};
const re = /<dimen\s+name="([^"]+)"[^>]*>([^<]+)<\/dimen>/g;
let m;
while ((m = re.exec(xml))) {
out[m[1]] = m[2].trim();
}
return out;
}
function listDrawables(resDir) {
const d = join(resDir, "drawable");
if (!existsSync(d)) return [];
return readdirSync(d)
.filter((f) => /\.(xml|png|webp|jpg)$/i.test(f))
.map((f) => f.replace(/\.[^.]+$/, ""));
}
function findAndroidModules(root) {
const out = [];
const settings = join(root, "settings.gradle");
const settingsKts = join(root, "settings.gradle.kts");
let content = "";
if (existsSync(settingsKts)) content = readFileSync(settingsKts, "utf8");
else if (existsSync(settings)) content = readFileSync(settings, "utf8");
const re = /project\s*\(\s*['"](:([^'"]+))['"]\s*\)/g;
let m;
while ((m = re.exec(content))) {
const name = m[2].replace(/:/g, "/");
const pathGuess = join(root, name.startsWith("/") ? name.slice(1) : name);
if (existsSync(join(pathGuess, "src", "main", "res"))) out.push(pathGuess);
}
const appRes = join(root, "app", "src", "main", "res");
if (existsSync(appRes)) out.push(join(root, "app"));
return [...new Set(out)];
}
function scanAndroidRoot(root) {
const modules = findAndroidModules(root);
const scanRoots = modules.length ? modules : [root];
const colors = {};
const strings = {};
const dimens = {};
const drawables = [];
const notes = [];
for (const mod of scanRoots) {
const res = join(mod, "src", "main", "res");
if (!existsSync(res)) continue;
for (const name of ["values", "values-night"]) {
const vd = join(res, name);
if (!existsSync(vd)) continue;
for (const f of readdirSync(vd)) {
if (!f.endsWith(".xml")) continue;
const p = join(vd, f);
const xml = readFileSync(p, "utf8");
Object.assign(colors, parseAndroidColors(xml));
Object.assign(strings, parseAndroidStrings(xml));
Object.assign(dimens, parseAndroidDimens(xml));
}
}
drawables.push(...listDrawables(join(mod, "src", "main", "res")));
}
const indices = {
colors: {},
strings: {},
text_styles: {},
};
for (const [k, v] of Object.entries(colors)) {
const hex = normalizeHex(v);
if (!indices.colors[hex]) indices.colors[hex] = [];
indices.colors[hex].push(k);
}
for (const [k, v] of Object.entries(strings)) {
if (!indices.strings[v]) indices.strings[v] = [];
indices.strings[v].push(k);
}
return {
platform: "android",
root,
modules: scanRoots,
colors,
strings,
dimens,
drawables: [...new Set(drawables)],
indices,
notes,
};
}
function hasXcodeProject(dir) {
try {
return readdirSync(dir).some((f) => f.endsWith(".xcodeproj"));
} catch {
return false;
}
}
function scanIosRoot(root) {
const notes = ["iOS:当前为轻量扫描,仅检测工程结构;颜色/文案请结合 Asset Catalog 与 Localizable.strings 人工补充。"];
return {
platform: "ios",
root,
modules: [],
colors: {},
strings: {},
dimens: {},
drawables: [],
indices: { colors: {}, strings: {}, text_styles: {} },
notes,
};
}
function detectPlatform(root) {
if (existsSync(join(root, "build.gradle")) || existsSync(join(root, "build.gradle.kts")))
return "android";
if (hasXcodeProject(root)) return "ios";
if (existsSync(join(root, "app", "build.gradle"))) return "android";
return "unknown";
}
function parseArgs(argv) {
const args = [...argv];
let jsonPretty = false;
let output = null;
const positional = [];
while (args.length) {
const x = args.shift();
if (x === "--json") jsonPretty = true;
else if (x === "--output" || x === "-o") output = args.shift() ?? null;
else if (!x.startsWith("-")) positional.push(x);
else throw new Error(`Unknown: x`);
}
return { jsonPretty, output, positional };
}
function main() {
const { jsonPretty, output, positional } = parseArgs(process.argv.slice(2));
if (positional.length < 1) {
console.error("Usage: node src/project-scan.js <project-root> [--json] [--output scan-report.json]");
process.exit(1);
}
const root = resolve(positional[0]);
if (!statSync(root).isDirectory()) {
console.error(`Not a directory: root`);
process.exit(1);
}
const plat = detectPlatform(root);
let report;
if (plat === "android") report = scanAndroidRoot(root);
else if (plat === "ios") report = scanIosRoot(root);
else {
report = {
platform: "unknown",
root,
modules: [],
colors: {},
strings: {},
dimens: {},
drawables: [],
indices: { colors: {}, strings: {}, text_styles: {} },
notes: ["未能识别为 Android Gradle 或 Xcode 工程,报告为空。"],
};
}
const text = JSON.stringify(report, null, jsonPretty ? 2 : 0);
if (output) {
writeFileSync(resolve(output), text, "utf8");
console.error(`Wrote output`);
}
console.log(text);
}
try {
main();
} catch (e) {
console.error(e instanceof Error ? e.message : e);
process.exit(1);
}
Deep threat modeling workflow—system decomposition, trust boundaries, STRIDE-style threats, mitigations, prioritization, and tracking. Use when designing new...
--- name: threat description: Deep threat modeling workflow—system decomposition, trust boundaries, STRIDE-style threats, mitigations, prioritization, and tracking. Use when designing new features, reviewing architecture, or responding to security requirements (STRIDE, PASTA light). --- # Threat Modeling (Deep Workflow) Threat modeling turns **architecture** into **attack scenarios** and **mitigations** before code hardens incorrectly. It is **team-facing**—security is a **collaborative** exercise, not a gate at the end. ## When to Offer This Workflow **Trigger conditions:** - New service, major data flow change, public API, partner integration - Compliance asks for “threat model” artifact - Post-incident “how do we prevent class of issues” **Initial offer:** Use **six stages**: (1) scope & assets, (2) diagram & trust boundaries, (3) threats (STRIDE), (4) mitigations & controls, (5) prioritize & owners, (6) validate & iterate. Confirm **time box** (1–2 hour workshop vs async). --- ## Stage 1: Scope & Assets **Goal:** Agree **what** we model and **what we protect**. ### Questions 1. **In scope**: apps, infra, data stores, admin tools, CI/CD? 2. **Crown jewels**: data (PII, keys), money, availability, reputation 3. **Adversaries**: script kiddies, malicious insiders, nation-state (usually pick **realistic** tiers) ### Output **Scope paragraph** + **asset list** with sensitivity. **Exit condition:** Shared understanding of **what hurts if lost**. --- ## Stage 2: Diagram & Trust Boundaries **Goal:** Visual model with **boundaries** where trust changes. ### Practices - **DFD-ish** diagram: external actors, services, data stores, queues - Mark **trust boundaries**: internet boundary, partner VPC, employee laptop, CI system - Note **auth** mechanisms crossing boundaries (mTLS, JWT, API keys) ### Pitfalls - **Missing** CI/CD and admin paths—attackers love them - **Over-detailed** diagram—keep level consistent **Exit condition:** Diagram everyone in the room **recognizes** as their system. --- ## Stage 3: Threats (STRIDE) **Goal:** Systematic **brainstorm**—not exhaustive fantasy. ### STRIDE prompts - **Spoofing**: impersonation of user/service - **Tampering**: data/code/config changed in transit or at rest - **Repudiation**: actions not attributable—audit gaps - **Information disclosure**: leaks to unauthorized - **Denial of service**: resource exhaustion, dependency attacks - **Elevation of privilege**: user becomes admin, lateral movement ### Technique - Walk **each boundary** + **each asset** with STRIDE columns—**rapid ideation**, defer judgment - Capture **threats** as short scenarios: “Attacker with leaked API key calls admin endpoint” **Exit condition:** **Threat list** with **assumptions** stated (e.g., “requires MITM”). --- ## Stage 4: Mitigations & Controls **Goal:** Map threats to **controls**—prevent, detect, respond. ### Control types - **Prevent**: authZ checks, input validation, encryption - **Detect**: logging, alerts, IDS, anomaly detection - **Respond**: kill switches, incident playbooks ### Avoid - **Control theater**—checkbox without real enforcement **Exit condition:** Each **high** threat has at least one **planned** control or accepted risk with **owner**. --- ## Stage 5: Prioritize & Owners **Goal:** **Ruthless** prioritization—fix what matters. ### Factors - **Impact** × **likelihood** (even qualitative MoSCoW) - **Ease of exploitation** vs **cost to fix** ### Tracking - **Tickets** per mitigation; **link** to threat ID - **Residual risk** explicitly accepted by appropriate authority when not fixed **Exit condition:** **Roadmap** of mitigations with **dates**. --- ## Stage 6: Validate & Iterate **Goal:** Model **ages**—revisit on major changes. ### Triggers for refresh - New data flow, new integration, auth redesign - **Pen test** findings that contradict assumptions ### Light validation - **Red team** scenarios for top threats if mature org --- ## Final Review Checklist - [ ] Assets and scope agreed - [ ] Trust boundaries diagram current - [ ] STRIDE pass completed for boundaries - [ ] Mitigations mapped; owners assigned - [ ] Residual risk explicit where unmitigated ## Tips for Effective Guidance - **Facilitate**, don’t lecture—engineers own the system facts. - Prefer **scenarios** over abstract “Tampering risk.” - Link to **requirements** (“every admin action audited”) for traceability. ## Handling Deviations - **No time workshop**: async diagram + STRIDE spreadsheet review. - **Tiny feature**: **mini** threat model—still document **trust boundary** with external API.
Deep workflow for technical prose—audience, purpose, structure, precision, examples, diagrams, review, and iteration. Use when drafting developer docs, desig...
--- name: techwrite description: Deep workflow for technical prose—audience, purpose, structure, precision, examples, diagrams, review, and iteration. Use when drafting developer docs, design notes, internal guides, RFCs, or public technical articles. --- # Technical Writing (Deep Workflow) Technical writing succeeds when **readers can act** or **decide** with less confusion. Optimize for **clarity**, **correctness**, and **appropriate depth**—not word count. ## When to Offer This Workflow **Trigger conditions:** - New feature docs, migration guides, API references - RFCs, ADRs, architecture summaries - Runbooks, onboarding docs, postmortems (writing-heavy) - “Make this clearer” edits on existing pages **Initial offer:** Use **six stages**: (1) define audience & goal, (2) outline & scope, (3) draft core content, (4) examples & edge cases, (5) review for clarity, (6) ship & maintain. Ask for **template** or **style guide** if org has one. --- ## Stage 1: Audience & Goal **Goal:** One primary reader persona; one **outcome**. ### Questions 1. **Who** reads (new hire, partner engineer, SRE, end user of API)? 2. **Job-to-be-done**: debug issue? integrate API? approve design? 3. **Constraints**: word limit, legal review, localization later? ### Anti-goals - “Everyone” audience → usually **nobody** satisfied—prefer layered docs (overview + deep dive links) **Exit condition:** **Success sentence**: “After reading, the reader can ___.” --- ## Stage 2: Outline & Scope **Goal:** **BLUF** + **sections** that match mental model. ### Practices - **Top**: context + outcome + prerequisites - **Middle**: procedural steps OR conceptual model—pick one primary mode per doc - **Bottom**: troubleshooting, FAQ, links, changelog ### Scope control - **In scope / out of scope** box for ambiguous topics - **Version** and **last reviewed** metadata for fast-moving products **Exit condition:** Outline reviewed; **order** matches reader journey (often happy path first). --- ## Stage 3: Draft Core Content **Goal:** **Precise**, **scannable**, **honest**. ### Style - **Short sentences**; **active voice** for procedures (“Click…”, “Run…”) - **Define terms** on first use; **glossary** for large docs - **Avoid** vague adjectives (“robust”, “seamless”) without criteria ### Structure signals - **Headings** that describe content; **lists** for sequences and parallel items - **Numbered steps** for order-sensitive actions **Exit condition:** First full pass complete—**ugly OK**, precision matters more than polish. --- ## Stage 4: Examples & Edge Cases **Goal:** Examples **reduce tickets**; edge cases **build trust**. ### Examples - **Minimal complete** snippet; **realistic** names; **expected output** - Show **failure** example when errors are common—include **how to fix** ### Edge cases - Permissions, rate limits, idempotency, backward compatibility - **“If you see X, do Y”** troubleshooting table when helpful **Exit condition:** At least one **end-to-end** path works on fresh machine (when procedural). --- ## Stage 5: Review for Clarity **Goal:** Remove **ambiguity** and **hidden assumptions**. ### Checklist - **Ambiguous pronouns** (“it”, “this”)—replace with nouns - **Implied steps**—make explicit - **Diagrams**: labeled arrows; **alt text** for accessibility - **Links**: avoid broken anchors; prefer **stable** URLs ### Reviews - **Peer review** for technical accuracy; **non-expert** read for onboarding docs **Exit condition:** Another reader can follow without asking clarifying questions—or questions are **FAQ’d**. --- ## Stage 6: Ship & Maintain **Goal:** Docs **decay**—plan updates. ### Practices - **Owner** field; **review cadence** for critical paths - **Changelog** or **page history** when platform changes often - **Deprecate** old pages with redirects --- ## Final Review Checklist - [ ] Audience and success outcome explicit - [ ] Outline matches reader journey - [ ] Procedures numbered; concepts separated from steps - [ ] Examples + failures where needed - [ ] Reviewed for ambiguity; diagrams accessible ## Tips for Effective Guidance - Prefer **concrete nouns** over abstractions (“the database primary” vs “the system”). - When user pastes draft, do **surgical** edits—preserve voice unless clarity suffers. - For **non-native readers**, avoid idioms and culture-specific jokes. ## Handling Deviations - **Marketing-heavy request**: separate **facts** from positioning; flag risky claims. - **Legal-sensitive**: suggest expert review; avoid drafting binding language unless qualified.
Deep workflow for secrets lifecycle—classification, storage (Vault/KMS/cloud), rotation, least privilege, developer ergonomics, audit, and incident response....
--- name: secrets description: Deep workflow for secrets lifecycle—classification, storage (Vault/KMS/cloud), rotation, least privilege, developer ergonomics, audit, and incident response. Use when removing hardcoded secrets, designing secret backends, CI/CD secret injection, or reviewing secret handling in code and infra. --- # Secrets Management (Deep Workflow) Guide the user through **end-to-end secrets governance**: what counts as a secret, where it may live, how it is injected and rotated, who can access what, and how misuse is detected. Act as a structured reviewer and architect, not a checklist robot. ## When to Offer This Workflow **Trigger conditions:** - User mentions API keys, tokens, passwords, TLS private keys, signing keys, OAuth client secrets, DB credentials, or “hardcoded secret” - Designing Vault/KMS/Parameter Store/Secrets Manager integration - CI/CD needs secrets; local dev vs prod parity questions - Audit/compliance asks for access logs or rotation evidence **Initial offer:** Explain you will use **five stages**: (1) inventory & classification, (2) storage & access model, (3) lifecycle & rotation, (4) developer & CI ergonomics, (5) verification & ongoing operations. Ask if they want this full pass or a narrower slice (e.g., “rotate one class of keys”). If they decline the workflow, help freeform but still flag **non-negotiables**: no long-lived secrets in git, minimize blast radius, auditable access. --- ## Stage 1: Inventory & Classification **Goal:** Know **what exists**, **where it is**, **who needs it**, and **blast radius if leaked**. ### Questions to Ask 1. What environments exist (local, staging, prod, partner)? Are boundaries strict? 2. What secret *types* are in scope: symmetric keys, asymmetric private keys, bearer tokens, DB passwords, cloud IAM, third-party API keys? 3. Where might secrets already be duplicated (repos, wikis, tickets, Slack, laptops)? 4. What compliance or contractual constraints apply (PCI, SOC2, customer DPAs)? ### Actions - Build a **rough inventory table**: secret class → consumers → storage today → rotation frequency → owner team. - Explicitly hunt **high-risk** items: signing keys, encryption-at-rest master keys, long-lived admin credentials, cross-env reuse. - Call out **anti-patterns**: secrets in env files committed to git, shared “team password”, same DB password everywhere. ### Exit Condition User can name **owners** for each critical class and agrees on **classification** (public / internal / confidential / regulated). **Transition:** Move to choosing storage and access patterns that match classification and scale. --- ## Stage 2: Storage & Access Model **Goal:** Pick mechanisms so secrets are **encrypted at rest**, **scoped**, and **auditable**. ### Design Points - **Central secret store** vs **cloud-native** (e.g., Vault, AWS Secrets Manager, GCP Secret Manager, Azure Key Vault) vs **KMS-only** patterns. - **Identity binding**: runtime identity (IAM role, K8s service account, workload identity) vs static tokens. - **Encryption paths**: envelope encryption, KMS CMKs, HSM requirements for signing keys. - **Namespaces / paths**: logical isolation per team, app, environment; avoid global buckets. ### Trade-offs to Surface - **Latency & availability**: secret fetch on startup vs sidecar vs CSI driver; failure modes when store is down. - **Break-glass**: who can decrypt in emergency, with what approval and logging. - **Multi-region**: replication, failover, and consistency for secret references. ### Exit Condition A written **access model**: principals → permissions → secret paths → justification. No “everyone read/write production.” **Transition:** Define how secrets **change over time** and how old values are retired safely. --- ## Stage 3: Lifecycle & Rotation **Goal:** Secrets **expire**, **rotate**, and **revoke** without surprise outages. ### Workflow 1. **Rotation policy** per class: automatic vs manual, max age, overlap window. 2. **Dual-credential periods** when services must accept both old and new during rollout. 3. **Revocation**: immediate invalidation paths for compromise (API key disable, cert CRL, session kill). 4. **Bootstrap**: how the *first* secret gets to runtime in a new environment without chicken-and-egg (e.g., cloud IAM → fetch others). ### Pitfalls to Call Out - Rotating DB password without connection pool drain → thundering reconnect failures. - Clients caching JWT signing keys without key ID rotation support. - Secrets embedded in **container images** or **build artifacts**. ### Exit Condition User has a **rotation runbook outline** and knows **order of operations** for at least one critical path. **Transition:** Make the model usable for engineers daily without encouraging leaks. --- ## Stage 4: Developer & CI Ergonomics **Goal:** Correct behavior is the **default**; wrong behavior is hard or blocked. ### Practices - **Local dev**: short-lived dev credentials, personal sandboxes, `.env.example` without values, secret scanners in pre-commit/CI. - **CI**: OIDC to cloud (no long-lived cloud keys in CI secrets if avoidable), scoped tokens, environment-specific secrets. - **Code review**: patterns for “secret passed as parameter,” logging redaction, error messages that leak tokens. ### Tooling Mentions (when relevant) - Git secret scanning (e.g., gitleaks, trufflehog), dependency on org policy. - Dynamic secrets / database roles if using Vault-style patterns. ### Exit Condition Clear **developer story**: “I clone repo → I authenticate → I get least-privilege creds → I never paste prod keys locally unless policy allows.” **Transition:** Prove the design works and stays healthy over time. --- ## Stage 5: Verification & Operations **Goal:** Evidence that controls work; readiness when things go wrong. ### Verification - **Drills**: restore from backup of secret metadata (if applicable), rotate in staging with full integration tests. - **Audit review**: sample access logs; alert on anomalous read patterns. - **Incident**: playbook for “credential leaked on GitHub” — revoke order, scope, customer comms if needed. ### Metrics / Signals (examples) - Failed authentication spikes after rotation - Secret fetch error rates from apps - Time-to-revoke for a simulated leak ### Exit Condition User can answer: “If this key leaks at 3am, what is step 1–5 and who is paged?” --- ## Final Review Checklist - [ ] No production secrets in source control or public artifacts - [ ] Least privilege enforced at identity + path + operation level - [ ] Rotation and revocation paths documented with owners - [ ] CI and local dev paths do not encourage static prod credentials - [ ] Audit/logging aligned with organizational requirements ## Tips for Effective Guidance - Prefer **concrete sequences** (bootstrap → fetch → use → rotate) over abstract “use a vault.” - Always ask **blast radius** and **who can decrypt**. - When user lacks org context, give **options** with trade-offs, not a single vendor gospel. ## Handling Deviations - **“We only need one API key”**: still classify, store centrally, and set expiry where possible. - **“Too heavy for our stage”**: minimum viable—env per env, secret manager, scanner on CI, no keys in repo.
Deep risk assessment workflow—identifying risks, likelihood and impact, mitigation plans, owners, residual risk acceptance, and tracking. Use when assessing...
--- name: risk description: Deep risk assessment workflow—identifying risks, likelihood and impact, mitigation plans, owners, residual risk acceptance, and tracking. Use when assessing launches, migrations, vendors, or operational changes. --- # Risk Assessment Risk assessment turns vague worry into **prioritized actions**: what can go wrong, how bad, what we do now, and who owns follow-up. ## When to Offer This Workflow **Trigger conditions:** - Major launch, migration, or new vendor - Steering or audit requests a risk matrix - Post–near-miss prevention work **Initial offer:** Use **six stages**: (1) scope & stakeholders, (2) identify risks, (3) analyze likelihood & impact, (4) plan mitigations, (5) owners & deadlines, (6) review & tracking). Confirm scoring approach (simple matrix vs quantitative). --- ## Stage 1: Scope & Stakeholders **Goal:** Define system/project boundary and who can accept residual risk (product, eng, legal). **Exit condition:** RACI or explicit approvers for go/no-go. --- ## Stage 2: Identify Risks **Goal:** Brainstorm across categories: technical, security, operational, legal, reputational, financial. ### Practices - Pre-mortem: “It failed because…” exercise for alignment --- ## Stage 3: Analyze **Goal:** Score likelihood and impact with a **shared rubric**; avoid false precision. --- ## Stage 4: Plan Mitigations **Goal:** Prevent, detect, and respond controls; rough cost/time per mitigation. --- ## Stage 5: Owners & Deadlines **Goal:** Each material risk has an owner and date; escalation path if unmitigated by launch. --- ## Stage 6: Review & Tracking **Goal:** Living RAID log; revisit after scope changes or incidents. --- ## Final Review Checklist - [ ] Scope and decision authority clear - [ ] Risks span relevant categories - [ ] Scoring applied consistently - [ ] Mitigations have owners and dates - [ ] Residual risk explicitly accepted or deferred with plan ## Tips for Effective Guidance - Distinguish future risk from current defects. - For security-heavy systems, align with **threat** (threat modeling) outputs. - Startups: fewer rows, more honesty on top existential risks. ## Handling Deviations - Regulated industries: follow required RA templates when mandated.
Alert design: SLOs, noise reduction, routing, severity. Use when tuning pages or defining on-call policy.
--- name: alerting description: "Alert design: SLOs, noise reduction, routing, severity. Use when tuning pages or defining on-call policy." --- # Alerting Skill This skill provides structured guidance for **Alerting** work. Act as an active guide: confirm triggers, propose the stages below, and adapt if the user wants a lighter pass. ## When to Offer This Workflow **Trigger conditions:** - User mentions **alerting** or closely related work - They want a structured workflow rather than ad-hoc tips - They are preparing a review, rollout, or stakeholder communication **Initial offer:** Explain the four stages briefly and ask whether to follow this workflow or work freeform. If they decline, continue in their preferred style. ## Workflow Stages ### Stage 1: Clarify context & goals Anchor on **SLOs and user-visible symptoms**. Ask what success looks like, constraints, and what must not break. Capture unknowns early. ### Stage 2: Design or plan the approach Translate goals into a concrete plan around **routing, severity, and ownership**. Compare alternatives and explicit trade-offs; avoid implicit assumptions. ### Stage 3: Implement, validate, and harden Execute with verification loops tied to **noise reduction and tuning**. Prefer small steps, measurable checks, and rollback points where risk is high. ### Stage 4: Operate, communicate, and iterate Close the loop with **runbook linkage**: monitoring, documentation, stakeholder updates, and lessons learned for the next cycle. ## Checklist Before Completion - Goals and constraints are explicit for **Alerting Skill** - Risks and trade-offs are stated, not hand-waved - Verification steps match the change’s impact (tests, canary, peer review) - Operational follow-through is covered (monitoring, docs, owners) ## Tips for Effective Guidance - Be procedural: stage-by-stage, with clear exit criteria - Ask for missing context (environment, scale, deadlines) before prescribing - Prefer checklists and concrete examples over generic platitudes - If the user declines the workflow, switch to freeform help without lecturing ## Handling Deviations - If the user wants to skip a stage: confirm and continue with what they need. - If context is missing: ask targeted questions before strong recommendations. - Prefer concrete examples, trade-offs, and verification steps over generic advice. ## Quality Bar - Each recommendation should be **actionable** (what to do next). - Call out **failure modes** relevant to Alerting (security, scale, UX, or ops). - Keep tone direct and respectful of the user’s time.
Pragmatic review of software contracts: IP, liability, SLAs. Use when triaging vendor agreements (not legal advice).
--- name: vendor-contracts description: "Pragmatic review of software contracts: IP, liability, SLAs. Use when triaging vendor agreements (not legal advice)." --- # Vendor Contracts Structured guidance for **software and vendor agreements** (IP, liability, SLAs—not legal advice): confirm triggers, propose the stages below, and adapt if the user wants a lighter pass. ## When to Offer This Workflow **Trigger conditions:** - User mentions **MSA**, **order form**, **vendor agreement**, **contract review**, or closely related work - They want a structured workflow rather than ad-hoc tips - They are preparing a review, rollout, or stakeholder communication **Initial offer:** Explain the four stages briefly and ask whether to follow this workflow or work freeform. If they decline, continue in their preferred style. ## Workflow Stages ### Stage 1: Clarify context & goals Anchor on **deal type** (SaaS, support, data processing) and **risk appetite**. Ask what success looks like and what must not break. ### Stage 2: Structure & categories Cover **commercial**, **legal**, **security**, **data**, and **exit** themes; flag unclear liability caps and indemnities for human counsel. ### Stage 3: Draft & refine Produce a **red-flag summary** and **question list** for legal/business owners—not definitive legal conclusions. ### Stage 4: Verify & ship Close the loop with owners, security review, and procurement steps as needed. ## Checklist Before Completion - Goals and constraints are explicit for **vendor contract** review - Risks and trade-offs are stated, not hand-waved - Verification steps match the change’s impact (tests, canary, peer review) - Operational follow-through is covered (monitoring, docs, owners) ## Tips for Effective Guidance - Be procedural: stage-by-stage, with clear exit criteria - Ask for missing context (environment, scale, deadlines) before prescribing - Prefer checklists and concrete examples over generic platitudes - If the user declines the workflow, switch to freeform help without lecturing ## Handling Deviations - If the user wants to skip a stage: confirm and continue with what they need. - If context is missing: ask targeted questions before strong recommendations. - Prefer concrete examples, trade-offs, and verification steps over generic advice. ## Quality Bar - Each recommendation should be **actionable** (what to do next). - Call out **failure modes** relevant to vendor agreements (security, scale, UX, or ops). - Keep tone direct and respectful of the user’s time.
User-facing change logs: categories, clarity, links. Use when maintaining CHANGELOG or release notes.
--- name: release-notes description: "User-facing change logs: categories, clarity, links. Use when maintaining CHANGELOG or release notes." --- # Release Notes Structured guidance for **CHANGELOG** files and **release notes**: confirm triggers, propose the stages below, and adapt if the user wants a lighter pass. ## When to Offer This Workflow **Trigger conditions:** - User mentions **release notes**, **CHANGELOG**, **what shipped**, or closely related work - They want a structured workflow rather than ad-hoc tips - They are preparing a review, rollout, or stakeholder communication **Initial offer:** Explain the four stages briefly and ask whether to follow this workflow or work freeform. If they decline, continue in their preferred style. ## Workflow Stages ### Stage 1: Clarify context & goals Anchor on **audience** (users vs developers) and **cadence** (per release vs continuous). Ask what success looks like and what must not break. ### Stage 2: Structure & categories Group changes (features, fixes, breaking, security). Prefer links to issues/PRs and migration notes when breaking. ### Stage 3: Draft & refine Iterate for clarity: imperative mood, scannable bullets, version headers aligned with semver or product versioning. ### Stage 4: Verify & ship Close the loop with **tone: concise and verifiable**: monitoring, documentation, stakeholder updates, and lessons learned for the next cycle. ## Checklist Before Completion - Goals and constraints are explicit for **release notes** work - Risks and trade-offs are stated, not hand-waved - Verification steps match the change’s impact (tests, canary, peer review) - Operational follow-through is covered (monitoring, docs, owners) ## Tips for Effective Guidance - Be procedural: stage-by-stage, with clear exit criteria - Ask for missing context (environment, scale, deadlines) before prescribing - Prefer checklists and concrete examples over generic platitudes - If the user declines the workflow, switch to freeform help without lecturing ## Handling Deviations - If the user wants to skip a stage: confirm and continue with what they need. - If context is missing: ask targeted questions before strong recommendations. - Prefer concrete examples, trade-offs, and verification steps over generic advice. ## Quality Bar - Each recommendation should be **actionable** (what to do next). - Call out **failure modes** relevant to release communication (security, scale, UX, or ops). - Keep tone direct and respectful of the user’s time.
Deep data pipeline workflow—ingestion, orchestration, idempotency, data quality, SLAs, observability, and lineage. Use when building batch/stream pipelines,...
--- name: data-pipelines description: Deep data pipeline workflow—ingestion, orchestration, idempotency, data quality, SLAs, observability, and lineage. Use when building batch/stream pipelines, debugging job failures, or hardening ETL/ELT. --- # Data Pipelines Pipelines fail on silent schema drift, partial writes, and unclear ownership. Design for at-least-once delivery, idempotent sinks, and observable stages. ## When to Offer This Workflow **Trigger conditions:** - Batch or streaming ingestion (Kafka, Fivetran, Airflow, Dagster, Spark, etc.) - Late data, backfills, or schema changes breaking jobs - SLA misses on freshness or row counts **Initial offer:** Use **six stages**: (1) requirements & SLAs, (2) source contracts, (3) transforms & idempotency, (4) orchestration & dependencies, (5) quality & monitoring, (6) lineage & operations). Confirm batch vs stream and cloud stack. --- ## Stage 1: Requirements & SLAs **Goal:** Freshness (latency), completeness expectations, cost ceiling, failure tolerance (quarantine vs stop-the-line). **Exit condition:** SLA table: pipeline → metric → threshold. --- ## Stage 2: Source Contracts **Goal:** Schema versioning; CDC vs snapshot pulls; API rate limits. ### Practices - Raw landing zone immutable; curated layers downstream --- ## Stage 3: Transforms & Idempotency **Goal:** Deterministic transforms; upsert keys; partition strategy for rewinds. ### Practices - Watermark progress for incremental loads --- ## Stage 4: Orchestration & Dependencies **Goal:** Clear DAG; retry policy; backfill without double counting; SLA miss alerts. --- ## Stage 5: Quality & Monitoring **Goal:** Data quality checks (null spikes, row bounds, referential checks); metrics on lag, duration, error rate. --- ## Stage 6: Lineage & Operations **Goal:** Column-level lineage where valuable; on-call runbook; ownership per pipeline. --- ## Final Review Checklist - [ ] SLAs and failure policy explicit - [ ] Source contracts and schema evolution path - [ ] Idempotent writes and checkpointing - [ ] Orchestration with retries and safe backfill - [ ] Data quality checks and alerts - [ ] Lineage and ownership documented ## Tips for Effective Guidance - Separate compute from storage cost awareness for large shuffles. - Pair with **etl-design** for batch patterns and **message-queues** for streaming handoffs. ## Handling Deviations - Single-script pipelines: still document inputs, outputs, and schedule.
Western astrology-style guidance for the twelve zodiac signs (Aries–Pisces)—daily/weekly/monthly themes, elements, modalities, and light compatibility framin...
---
name: western-sun-signs
description: |
Western astrology-style guidance for the twelve zodiac signs (Aries–Pisces)—daily/weekly/monthly themes, elements, modalities, and light compatibility framing. Use when the user asks for horoscope, zodiac, star sign, sun sign, or “what’s my sign.” Entertainment and reflection only; not science, not medical/legal/financial advice.
metadata:
openclaw:
emoji: "♈"
---
# Western Sun Signs — Twelve Signs
## Overview
This skill helps the assistant give **clear, culturally familiar** Western-style **sun-sign** readings (十二星座 in Chinese, **twelve zodiac signs** in English). It covers **Aries through Pisces**, plus optional **elements** (Fire, Earth, Air, Water) and **modalities** (Cardinal, Fixed, Mutable).
**Scope:** Reflective themes, mood, and timing language—**not** precise predictions or fate. For deeper work (rising sign, houses, transits), say that **full natal charts** need birth time and place and a professional astrologer or certified software.
**Trigger keywords:** horoscope, zodiac, star sign, sun sign, Aries, Taurus, Gemini, Cancer, Leo, Virgo, Libra, Scorpio, Sagittarius, Capricorn, Aquarius, Pisces, 星座, 十二星座, compatibility
---
## The twelve signs (quick reference)
| Sign | Approx. solar dates* | Element | Modality |
|------|----------------------|---------|----------|
| Aries ♈ | Mar 21 – Apr 19 | Fire | Cardinal |
| Taurus ♉ | Apr 20 – May 20 | Earth | Fixed |
| Gemini ♊ | May 21 – Jun 20 | Air | Mutable |
| Cancer ♋ | Jun 21 – Jul 22 | Water | Cardinal |
| Leo ♌ | Jul 23 – Aug 22 | Fire | Fixed |
| Virgo ♍ | Aug 23 – Sep 22 | Earth | Mutable |
| Libra ♎ | Sep 23 – Oct 22 | Air | Cardinal |
| Scorpio ♏ | Oct 23 – Nov 21 | Water | Fixed |
| Sagittarius ♐ | Nov 22 – Dec 21 | Fire | Mutable |
| Capricorn ♑ | Dec 22 – Jan 19 | Earth | Cardinal |
| Aquarius ♒ | Jan 20 – Feb 18 | Air | Fixed |
| Pisces ♓ | Feb 19 – Mar 20 | Water | Mutable |
\*Dates vary slightly by year and time zone—**ask for birthday** if the user is on a cusp or unsure.
---
## When to use
- User wants a **horoscope-style** message (love, work, mood, “this week”).
- User asks **which sign** they are from their birthday.
- User asks **very light** sign-to-sign compatibility (keep non-coercive, no stereotypes about worth).
**Do not** claim astronomical precision you don’t have; **do not** replace therapists, doctors, or lawyers.
---
## Assistant behavior
1. **Entertainment framing** — State that sun-sign astrology is **popular culture / reflective**, not empirical science.
2. **Cusp & timezone** — If birthday is on a boundary date, note ambiguity and offer both signs’ themes or ask for year + city.
3. **Rising / Moon** — If asked, explain briefly and defer full chart to **natal chart tools** or professionals.
4. **Inclusive language** — Avoid gender stereotypes, “you must date X sign,” or fatalism.
5. **Language** — Match the user (English, Chinese, etc.).
---
## Suggested output structure
```markdown
# Horoscope (Western sun sign)
**Sign**: [Sign] (element / modality)
**Period** (if any): [daily / week of …]
## Themes
- [2–4 bullets: mood, focus, relationships, work—non-deterministic]
## Caution / opportunity
- [One balanced paragraph]
## Reminder
For entertainment and reflection only—not medical, legal, or financial advice.
```
---
## References (general)
- Broad overview: search for “Western astrology sun sign” or “tropical zodiac” from reputable educational sources.
- **NASA / astronomy** sites clarify that constellations and astrology signs are **not the same** as scientific astronomy—mention if users conflate them.
Tarot-style readings using common spreads (e.g. three-card, Celtic cross)—symbolic interpretation of Major/Minor Arcana, upright and reversed meanings where...
---
name: tarot-spreads
description: |
Tarot-style readings using common spreads (e.g. three-card, Celtic cross)—symbolic interpretation of Major/Minor Arcana, upright and reversed meanings where appropriate. Use when the user asks for tarot, card reading, spread, or a symbolic mirror for a situation. Entertainment and reflection only; not prediction of harm, not medical/legal/financial advice.
metadata:
openclaw:
emoji: "🃏"
---
# Tarot Spreads (Symbolic Reading)
## Overview
This skill guides the assistant to offer **Tarot** sessions as **symbolic storytelling**: cards suggest **themes and questions**, not fixed fate. Use established **arcana** and **suit** meanings; keep tone **grounded** and **non-alarming**.
**Trigger keywords:** tarot, Tarot reading, card spread, Celtic cross, three-card spread, Major Arcana, Minor Arcana, 塔罗, 塔罗牌
---
## Ethical boundaries (mandatory)
- **No** medical diagnosis, legal outcomes, or investment decisions.
- **No** predicting death, violence, or serious harm; if the user is in crisis, encourage **local emergency or crisis resources**.
- Frame as **reflection**, **possibilities**, **questions to sit with**—not commands from the universe.
- **Randomness:** If the product cannot draw cards fairly, say cards are **illustrative** or ask the user to draw physical cards and report them.
---
## Common spreads (reference)
| Spread | Positions (typical) | Good for |
|--------|---------------------|----------|
| **One card** | Situation / theme of the day | Quick focus |
| **Three card** | Past – Present – Future (or Situation – Action – Outcome) | Simple arcs |
| **Celtic Cross** | 10 positions (present, challenge, past, future, self, environment, hopes, outcome, etc.) | Deeper snapshot |
Name positions **explicitly** so the user can follow.
---
## Interpretation habits
1. **Name the card** (English or user’s language) + **upright / reversed** if used.
2. **Core keywords** (2–4), then **how it might speak to the question**—tentative language (“might,” “could,” “one reading is…”).
3. **Contradictions** — two cards can tension; that’s a feature, not a bug.
4. **Agency** — remind the user they choose actions; cards are mirrors.
5. **Cultural respect** — Tarot has many traditions; avoid claiming one “true” esoteric authority.
---
## Suggested output structure
```markdown
# Tarot spread session
**Question (optional)**: …
**Spread**: [name] — positions: …
## Cards
1. **[Position]** — [Card name] (upright/reversed)
- Keywords: …
- Tie to question: …
2. …
## Synthesis
- [Non-fatalistic themes; tensions; one or two reflective prompts]
## Disclaimer
Symbolic and for entertainment/reflection only—not a substitute for professional advice.
```
---
## References (neutral)
- Educational overviews of Tarot history and card meanings from **museums, libraries, or academic** sources; avoid presenting any single blog as doctrine.
Western-style numerology (Pythagorean-style)—life path and related numbers from birth date and name, with symbolic themes. Use when the user asks for life pa...
---
name: numerology-reading
description: |
Western-style numerology (Pythagorean-style)—life path and related numbers from birth date and name, with symbolic themes. Use when the user asks for life path number, angel numbers, name number, or “what does my birthday mean” in a numerology context. Entertainment and self-reflection only; not science, not medical/legal/financial advice.
metadata:
openclaw:
emoji: "🔢"
---
# Numerology Reading (Western)
## Overview
This skill supports **Western numerology** as commonly practiced online: reduce dates and names to **single digits (1–9)** and **master numbers (11, 22, 33)** where the tradition allows. Use it for **themes and prompts**, not destiny.
**Trigger keywords:** numerology, life path number, angel number, expression number, soul urge, birthday number, 生命灵数, 数字命理
---
## Core ideas (typical Pythagorean-style)
- **Life Path** — from **full birth date** (reduce to 1–9 or 11/22/33).
- **Expression / Destiny** — from **full name** letters mapped to digits (A=1 … I=9 pattern varies slightly by system—**state which reduction you use**).
- **Birthday number** — day of month reduced.
- **Angel numbers** (e.g. 111, 222) — **popular culture** framing; avoid superstitious fear.
Always **show the math** when you compute (e.g. “1+9+9+0 = 19 → 1+9 = 10 → 1+0 = **1**”) so users can verify.
---
## When to use
- User wants a **number profile** from birthday or name.
- User keeps seeing **repeating digits** and wants a **gentle symbolic** read—not a claim about the universe sending commands.
**Do not** use numbers to rank people’s worth, compatibility cruelty, or “lucky/unlucky” people.
---
## Assistant behavior
1. **Transparency** — Numerology is **not** validated by science; label as **reflective**.
2. **Consistency** — Pick **one** letter-to-number table (classic Latin A–Z mapping) and stick to it; note Y as vowel/consonant only if you take a stance.
3. **Master numbers** — If reducing, many systems preserve 11/22/33 before final reduction; **say which rule** you follow.
4. **Inclusive** — Names with diacritics: normalize or state limitation.
5. **Language** — Match the user’s language.
---
## Suggested output structure
```markdown
# Numerology snapshot
**Method**: [e.g. Pythagorean; master numbers preserved at step X]
## Inputs
- Birth date: …
- Name (if used): …
## Calculations (show steps)
- Life Path: … → **N**
- (Optional) Expression: … → **N**
## Themes (non-literal)
- [2–4 bullets tied to N as *cultural* meanings, not facts]
## Disclaimer
For entertainment and reflection only—not professional, medical, legal, or financial advice.
```
---
## References (general)
- Look for **educational** descriptions of “Pythagorean numerology” or “life path number”; treat pop sites as **varied**, not one standard.
Generate GitHub agent-trending project reports as formatted markdown leaderboards. Fetches agent/LLM-agent/multi-agent related repos by daily, weekly, or mon...
---
name: github-agent-trends
description: Generate GitHub agent-trending project reports as formatted markdown leaderboards. Fetches agent/LLM-agent/multi-agent related repos by daily, weekly, or monthly activity window and sorts by stars. Use when the user asks for GitHub agent trends, AI agent leaderboard, coding agents, or popular agent frameworks on GitHub.
---
# GitHub Agent Trends
Generate a formatted leaderboard of **agent-related** open-source projects on GitHub (keywords + topics such as `ai-agent`, `multi-agent`, `agent framework`), and paste the script output into chat.
## Usage
From the **repository root**:
```bash
python3 scripts/skills/github-agent-trends/scripts/fetch_trends.py --period weekly --limit 20
```
With a token (recommended for rate limits):
```bash
export GITHUB_TOKEN=ghp_...
python3 scripts/skills/github-agent-trends/scripts/fetch_trends.py --period weekly --limit 20
```
## Parameters
- `--period`: `daily` | `weekly` | `monthly` (default: `weekly`)
- `--limit`: Number of repos after dedupe/sort (default: 20)
- `--token`: GitHub PAT (or set `GITHUB_TOKEN`)
- `--json`: Raw JSON instead of markdown
## How It Works
1. Queries GitHub **Search repositories** API with agent-focused **keywords** (e.g. `ai-agent`, `multi-agent`, `agent framework`) and **topics** (e.g. `ai-agent`, `multi-agent`, `langchain`, `autogen`), filtered by `pushed` within the period and minimum stars.
2. Deduplicates by `full_name`, sorts by `stargazers_count`, takes top N.
3. Prints a markdown leaderboard (Chinese title: **GitHub Agent 趋势榜**).
## Notes
- Results reflect **search relevance + stars**, not the official GitHub “Trending” page (which has no public API).
- Unauthenticated: ~10 requests/minute to Search API; with `GITHUB_TOKEN`: higher quotas (follow GitHub docs).
- **Stdlib only** — no pip dependencies.
## Customization
Edit `SEARCH_KEYWORDS` and `SEARCH_TOPICS` in `scripts/fetch_trends.py` to widen or narrow the agent theme (e.g. add `crewai`, `browser-use`).
FILE:scripts/fetch_trends.py
#!/usr/bin/env python3
"""Fetch trending agent-related repos from GitHub and output a formatted leaderboard."""
import argparse
import json
import os
import sys
import urllib.parse
import urllib.request
from datetime import datetime, timedelta, timezone
PERIOD_DAYS = {"daily": 1, "weekly": 7, "monthly": 30}
PERIOD_LABELS = {"daily": "日榜", "weekly": "周榜", "monthly": "月榜"}
PERIOD_EMOJI = {"daily": "📅", "weekly": "📊", "monthly": "📈"}
# Keyword searches: agent-focused (name/description), recent pushes
SEARCH_KEYWORDS = [
"ai-agent",
"agent",
"multi-agent",
"autonomous agent",
"llm agent",
"coding agent",
"agent framework",
"agentic",
]
# Topic filters (narrower than generic "ai")
SEARCH_TOPICS = [
"ai-agent",
"multi-agent",
"agents",
"llm",
"langchain",
"autogen",
]
UA = "github-agent-trends/1.0 (skill; +https://github.com)"
def gh_search(query, sort="stars", order="desc", per_page=30, token=None):
params = urllib.parse.urlencode({
"q": query, "sort": sort, "order": order, "per_page": per_page
})
url = f"https://api.github.com/search/repositories?{params}"
headers = {"Accept": "application/vnd.github+json", "User-Agent": UA}
if token:
headers["Authorization"] = f"Bearer {token}"
req = urllib.request.Request(url, headers=headers)
try:
with urllib.request.urlopen(req, timeout=30) as resp:
return json.loads(resp.read()).get("items", [])
except Exception as e:
print(f"[WARN] GitHub API error: {e}", file=sys.stderr)
return []
def fetch_trending(period="weekly", limit=30, token=None):
days = PERIOD_DAYS.get(period, 7)
since = (datetime.now(timezone.utc) - timedelta(days=days)).strftime("%Y-%m-%d")
seen, results = set(), []
base_filter = f"pushed:>={since} stars:>=10"
for kw in SEARCH_KEYWORDS:
if len(results) >= limit * 3:
break
q = f"{kw} in:name,description {base_filter}"
items = gh_search(q, sort="stars", per_page=30, token=token)
for item in items:
if item["full_name"] not in seen:
seen.add(item["full_name"])
results.append(item)
for topic in SEARCH_TOPICS:
if len(results) >= limit * 4:
break
q = f"topic:{topic} {base_filter}"
items = gh_search(q, sort="stars", per_page=30, token=token)
for item in items:
if item["full_name"] not in seen:
seen.add(item["full_name"])
results.append(item)
results.sort(key=lambda r: r.get("stargazers_count", 0), reverse=True)
return results[:limit]
def fmt_num(n):
return f"{n/1000:.1f}k" if n >= 1000 else str(n)
def format_output(repos, period):
label = PERIOD_LABELS.get(period, period)
emoji = PERIOD_EMOJI.get(period, "📊")
now = datetime.now(timezone(timedelta(hours=8))).strftime("%Y-%m-%d %H:%M")
lines = [f"{emoji} **GitHub Agent 趋势榜 — {label}**", f"生成时间:{now}", ""]
for i, r in enumerate(repos, 1):
stars = fmt_num(r["stargazers_count"])
forks = fmt_num(r.get("forks_count", 0))
lang = r.get("language") or "N/A"
desc = r.get("description") or ""
if len(desc) > 80:
desc = desc[:77] + "..."
name = r["full_name"]
url = r["html_url"]
lines.append(f"**#{i}** [{name}]({url})")
lines.append(f"⭐ {stars} · 🍴 {forks} · {lang}")
if desc:
lines.append(f"_{desc}_")
lines.append("")
return "\n".join(lines)
def main():
parser = argparse.ArgumentParser(
description="GitHub Agent Trends — leaderboard of agent-related repositories (search API)"
)
parser.add_argument("--period", choices=["daily", "weekly", "monthly"],
default="weekly")
parser.add_argument("--limit", type=int, default=20)
parser.add_argument("--token", default=os.environ.get("GITHUB_TOKEN"))
parser.add_argument("--json", action="store_true", help="Output raw JSON")
args = parser.parse_args()
print(f"Fetching {args.period} agent trends (GitHub search)...", file=sys.stderr)
repos = fetch_trending(args.period, args.limit, args.token)
if not repos:
print(
"No repos found. Try another --period, raise --limit, or set GITHUB_TOKEN.",
file=sys.stderr,
)
sys.exit(1)
if args.json:
json.dump([{
"rank": i, "name": r["full_name"], "url": r["html_url"],
"stars": r["stargazers_count"], "forks": r.get("forks_count", 0),
"language": r.get("language"), "description": r.get("description"),
} for i, r in enumerate(repos, 1)], sys.stdout, ensure_ascii=False, indent=2)
else:
print(format_output(repos, args.period))
if __name__ == "__main__":
main()
Deep edge computing workflow—what runs at edge vs origin, caching, KV and data locality, security, limits, and latency validation. Use when deploying to CDN/...
--- name: edge-computing description: Deep edge computing workflow—what runs at edge vs origin, caching, KV and data locality, security, limits, and latency validation. Use when deploying to CDN/edge workers (Cloudflare Workers, Lambda@Edge, Vercel Edge, etc.). --- # Edge Computing Edge runtimes move logic closer to users—with **strict CPU/time limits**, **different APIs** than full Node, and **tenant isolation** requirements. ## When to Offer This Workflow **Trigger conditions:** - Auth, redirects, or personalization at the CDN layer - HTML rewriting, A/B assignment, or bot mitigation at the edge - Global latency SLOs for read-heavy paths **Initial offer:** Use **six stages**: (1) workload fit, (2) edge vs origin split, (3) data & state, (4) security & tenancy, (5) limits & cost, (6) testing & rollout). Confirm platform (Workers, Lambda@Edge, Fastly Compute, etc.). --- ## Stage 1: Workload Fit **Goal:** Prefer short, CPU-light, request-scoped work—not long jobs or huge body buffering. **Exit condition:** Explicit list of what remains on origin (heavy SSR, large uploads). --- ## Stage 2: Edge vs Origin Split **Goal:** Document what runs where: geo headers, redirects, cache key logic, A/B bucketing, partial HTML injection. ### Practices - Cache `Vary` and cookie behavior documented to avoid wrong personalization leakage --- ## Stage 3: Data & State **Goal:** If using edge KV/Durable Objects/regional stores, state consistency (eventual vs strong) and rate of round-trips to origin. --- ## Stage 4: Security & Tenancy **Goal:** Validate JWT/session at edge; isolate tenants; never embed secrets in deploy bundles visible to clients. --- ## Stage 5: Limits & Cost **Goal:** Wall-clock CPU limits, request size caps, egress pricing; graceful degradation or fallback to origin. --- ## Stage 6: Testing & Rollout **Goal:** Canary per region/PoP; synthetics from multiple locations; compare p95 vs origin-only path. --- ## Final Review Checklist - [ ] Workload fits edge constraints - [ ] Edge vs origin responsibilities documented - [ ] State/consistency strategy clear - [ ] Multi-tenant security reviewed - [ ] Limits, cost, fallback documented - [ ] Multi-region validation performed ## Tips for Effective Guidance - Edge runtimes differ from full Node—verify available APIs (fs, streams, crypto). - Read platform-specific cold-start and isolate model docs. ## Handling Deviations - Hybrid: edge for headers/cache only; heavy compute stays on origin.
Deep form validation workflow—schemas, sync vs async rules, UX patterns, accessibility, and server parity. Use when building complex forms, multi-step wizard...
--- name: form-validation description: Deep form validation workflow—schemas, sync vs async rules, UX patterns, accessibility, and server parity. Use when building complex forms, multi-step wizards, or reducing validation bugs. --- # Form Validation Validation combines **correctness** and **UX**: when errors appear, how they are announced, and how client rules match the server. ## When to Offer This Workflow **Trigger conditions:** - Long forms, wizards, or multi-step checkouts - Accessibility audits flagging unclear errors - Mismatch between client-side “valid” and API rejection **Initial offer:** Use **six stages**: (1) model & schema, (2) rule layers, (3) UX timing, (4) accessibility, (5) async & server parity, (6) testing). Confirm framework (React Hook Form, Formik, native, etc.). --- ## Stage 1: Model & Schema **Goal:** Single schema (Zod, Yup, JSON Schema) as source of truth; share with backend when feasible. --- ## Stage 2: Rule Layers **Goal:** Separate required/format rules from cross-field rules (e.g., date range); isolate async checks (username available) from fast inline validation. --- ## Stage 3: UX Timing **Goal:** Choose onBlur vs onSubmit per field; avoid shouting on every keystroke unless real-time feedback is a product requirement. --- ## Stage 4: Accessibility **Goal:** Associate errors with fields via `aria-describedby`; move focus to first error on submit; use live regions judiciously. --- ## Stage 5: Async & Server Parity **Goal:** Map API validation errors to fields; handle race conditions on slow networks; idempotent submit with dedupe if needed. --- ## Stage 6: Testing **Goal:** Unit-test schema; e2e critical paths including server error mapping. --- ## Final Review Checklist - [ ] Schema aligned or shared with server - [ ] Validation timing and copy defined - [ ] Accessible error patterns - [ ] API errors mapped to UI - [ ] Tests for schema and flows ## Tips for Effective Guidance - Server rules always win—document intentional client shortcuts. - Pair with **ux-writing** for error microcopy. ## Handling Deviations - Wizards: validate per step and on final submit; persist drafts securely.
Deep feature flag workflow—taxonomy, targeting, lifecycle, safety and kill switches, cleanup, and governance. Use when shipping gradually, experimenting, or...
--- name: feature-flags description: Deep feature flag workflow—taxonomy, targeting, lifecycle, safety and kill switches, cleanup, and governance. Use when shipping gradually, experimenting, or decoupling deploy from release. --- # Feature Flags Flags decouple **deploy** from **release**—and become **debt** if never removed. **Taxonomy**, **ownership**, and **retirement** matter as much as targeting. ## When to Offer This Workflow **Trigger conditions:** - Gradual rollouts, kill switches, or experiments behind flags - Flag sprawl and unknown defaults - Client vs server evaluation and hydration flicker **Initial offer:** Use **six stages**: (1) taxonomy, (2) targeting rules, (3) evaluation & consistency, (4) safety & ops, (5) lifecycle & cleanup, (6) governance). Confirm provider (LaunchDarkly, Unleash, ConfigCat, homegrown). --- ## Stage 1: Taxonomy **Goal:** Separate short-lived **release** flags, long-lived **config** flags, and **experiment** flags tied to analytics. **Exit condition:** Naming convention and expected TTL per type. --- ## Stage 2: Targeting Rules **Goal:** Percentage rollouts, segments (tenant, plan, region), deterministic bucketing (stable user key). --- ## Stage 3: Evaluation & Consistency **Goal:** Server-side authoritative for security and billing; client flags for UX only; avoid UI flicker on hydration (SSR/CSR agreement). --- ## Stage 4: Safety & Ops **Goal:** Kill-switch runbook; audit trail for changes; safe defaults when provider unavailable (often “off”). --- ## Stage 5: Lifecycle & Cleanup **Goal:** Tickets to remove flags after full rollout; periodic audits; metric for stale flags. --- ## Stage 6: Governance **Goal:** Approvals for broadening exposure; promotion across environments; break-glass access for incidents. --- ## Final Review Checklist - [ ] Flag types and naming documented - [ ] Targeting and bucketing deterministic - [ ] Server vs client boundaries clear - [ ] Kill switches and defaults documented - [ ] Cleanup process and ownership ## Tips for Effective Guidance - Never put security-only gates solely in client-side flags. - Pair with **ab-testing** when experiment analysis is primary. ## Handling Deviations - Align with **release-management** for communication cadence.
Deep event-driven architecture workflow—events vs commands, ordering and idempotency, sagas, outbox pattern, observability, and failure modes. Use when desig...
--- name: event-driven description: Deep event-driven architecture workflow—events vs commands, ordering and idempotency, sagas, outbox pattern, observability, and failure modes. Use when designing async systems, event buses, or refactoring synchronous chains. --- # Event-Driven Architecture Event-driven design trades **tight coupling** for **asynchronous** workflows—and introduces **ordering**, **duplicates**, **schema evolution**, and **distributed tracing** challenges. ## When to Offer This Workflow **Trigger conditions:** - Replacing long chains of synchronous HTTP calls - Adopting Kafka, Pub/Sub, EventBridge, NATS, etc. - Need for sagas, compensating transactions, or cross-service workflows **Initial offer:** Use **six stages**: (1) identify events, (2) contracts & versioning, (3) delivery semantics, (4) orchestration vs choreography, (5) observability, (6) failure & replay). Assume at-least-once delivery unless proven otherwise. --- ## Stage 1: Identify Events **Goal:** Distinguish **domain events** (facts that happened) from **commands** (requests). Assign owning bounded context per event type. **Exit condition:** Event catalog: name, schema, producers, consumers, SLAs. --- ## Stage 2: Contracts & Versioning **Goal:** Schema registry or equivalent; backward-compatible evolution; consumers ignore unknown fields; deprecation policy for old versions. --- ## Stage 3: Delivery Semantics **Goal:** Partition keys for per-entity ordering; idempotent consumers; dedupe keys when exactly-once illusion is needed. --- ## Stage 4: Orchestration vs Choreography **Goal:** Central orchestrator (saga coordinator) vs decentralized choreography—trade visibility vs coupling. ### Practices - Transactional outbox when DB write and event publish must align --- ## Stage 5: Observability **Goal:** Correlation ids on events; traces spanning HTTP → broker → consumer; lag and DLQ depth metrics. --- ## Stage 6: Failure & Replay **Goal:** Dead-letter queues, replay tooling, poison message handling, and idempotent replays. --- ## Final Review Checklist - [ ] Event inventory with clear ownership - [ ] Versioned contracts and compatibility rules - [ ] Idempotent consumers; partition strategy documented - [ ] Saga/outbox where transactional consistency required - [ ] Tracing and replay operationalized ## Tips for Effective Guidance - Choreography can hide flows—document critical sequences as diagrams. - Pair with **message-queues** and **idempotency** for implementation detail. ## Handling Deviations - Low volume: start with a simple queue before full Kafka topology.
Deep ETL/ELT design workflow—extract patterns, transforms, loading strategies, idempotency, validation, and reconciliation. Use when designing batch data flo...
--- name: etl-design description: Deep ETL/ELT design workflow—extract patterns, transforms, loading strategies, idempotency, validation, and reconciliation. Use when designing batch data flows between systems or hardening pipelines for correctness. --- # ETL Design ETL is **correctness under change**: schema drift, partial loads, retries, and reconciliation with upstream systems. ## When to Offer This Workflow **Trigger conditions:** - Batch loads into warehouse or data lake - Choosing between CDC, snapshots, and incremental watermarks - Missing rows, duplicates, or inconsistent aggregates downstream **Initial offer:** Use **six stages**: (1) source contract, (2) extract strategy, (3) transform rules, (4) load & dedupe, (5) validation, (6) operations & backfill). Confirm batch window and SLA. --- ## Stage 1: Source Contract **Goal:** Document schema, primary keys, change indicators (`updated_at`, CDC log position), and access constraints (rate limits, read replicas). --- ## Stage 2: Extract Strategy **Goal:** Full dump vs incremental watermark vs CDC—trade freshness, source load, and complexity. ### Practices - CDC for large sources; snapshots for small or infrequent tables --- ## Stage 3: Transform Rules **Goal:** Deterministic transforms; surrogate keys; business rules versioned; handling of deletes (tombstones vs hard deletes). --- ## Stage 4: Load & Dedupe **Goal:** Upsert keys; partitions; rerunnable jobs with same batch id producing the same outcome (idempotent load). --- ## Stage 5: Validation **Goal:** Row counts, checksums, key uniqueness, referential checks; alert on threshold breaches. --- ## Stage 6: Operations & Backfill **Goal:** Replay by date range; monitor lag; dead-letter or quarantine bad rows with reason codes. --- ## Final Review Checklist - [ ] Source contract and keys documented - [ ] Extract mode matches SLA and source constraints - [ ] Transforms deterministic and versioned - [ ] Idempotent load strategy - [ ] Validation and reconciliation defined ## Tips for Effective Guidance - Plan for late-arriving facts and slowly changing dimensions in analytics paths. - Pair with **data-pipelines** for orchestration and monitoring. ## Handling Deviations - Near-real-time: document micro-batch or streaming semantics separately.
Deep error handling workflow—taxonomy, user-visible vs internal errors, retries and idempotency, observability, and supportability. Use when standardizing fa...
--- name: error-handling description: Deep error handling workflow—taxonomy, user-visible vs internal errors, retries and idempotency, observability, and supportability. Use when standardizing failure modes across APIs, clients, and async workers. --- # Error Handling Consistent errors reduce support load and on-call pain. Design a **taxonomy**, **stable codes**, **safe user messaging**, and **operator visibility**—without leaking secrets or stack traces to clients. ## When to Offer This Workflow **Trigger conditions:** - Inconsistent HTTP status codes and response bodies - Retry storms or duplicate side effects from naive retries - Logs that cannot be tied to user-visible failures **Initial offer:** Use **six stages**: (1) classify errors, (2) map to transport, (3) user messaging, (4) retries & idempotency, (5) observability, (6) client SDKs & DX). Confirm REST/GraphQL/gRPC and sync/async patterns. --- ## Stage 1: Classify Errors **Goal:** Distinguish validation, authentication, authorization, not found, conflict, rate limit, dependency failure, and internal bugs. **Exit condition:** Table or enum of codes with owning team and meaning. --- ## Stage 2: Map to Transport **Goal:** Correct HTTP 4xx/5xx; GraphQL errors with extensions; gRPC status codes; optional RFC 7807 Problem Details for JSON APIs. --- ## Stage 3: User Messaging **Goal:** Actionable copy for end users; opaque support reference id; no internal hostnames, SQL fragments, or stack traces in client responses. --- ## Stage 4: Retries & Idempotency **Goal:** Retry only safe or idempotent operations; exponential backoff with jitter; align with **idempotency** keys on writes. --- ## Stage 5: Observability **Goal:** Structured logs with `error.code`, `trace_id`, `user_id` (where allowed); metrics by error class; alerts on error-rate SLO burn. --- ## Stage 6: Client SDKs & DX **Goal:** Typed errors in SDKs; documented recovery; map codes to user-facing strings in apps consistently. --- ## Final Review Checklist - [ ] Taxonomy and ownership defined - [ ] Transport mapping correct and consistent - [ ] User-safe messages with correlation ids - [ ] Retry policy matches idempotency story - [ ] Logs and metrics wired for ops ## Tips for Effective Guidance - Separate expected validation errors from unexpected 500s in dashboards. - Pair with **idempotency** for write paths and queues. ## Handling Deviations - Mobile offline: queue with explicit user-visible sync state.
Deep resume workflow—target role, positioning statement, impact bullets with metrics, keyword alignment, ATS hygiene, and proofreading. Use when rewriting te...
--- name: resume-writing description: Deep resume workflow—target role, positioning statement, impact bullets with metrics, keyword alignment, ATS hygiene, and proofreading. Use when rewriting tech resumes, transitioning roles, or tailoring for specific companies. --- # Resume Writing (Deep Workflow) A strong tech resume proves **impact** with **evidence**, not adjectives. Optimize for skimmability (recruiters spend seconds) and interview-defensible depth. ## When to Offer This Workflow **Trigger conditions:** - Active job search; career pivot (IC ↔ management, domain change) - Low response rate; resume too long or generic **Initial offer:** Use **six stages**: (1) target & positioning, (2) experience structure, (3) impact bullets, (4) skills & keywords, (5) ATS & format, (6) review & tailoring). Confirm regional norms (one page vs two for mid/senior). --- ## Stage 1: Target & Positioning **Goal:** One-line headline aligned with the **next** role you want—not every past title. **Exit condition:** 2–3 target job descriptions saved as references for keyword alignment (without copying dishonestly). --- ## Stage 2: Experience Structure **Goal:** Reverse chronological; roughly 3–5 bullets per role; most recent role deepest. ### Practices - Projects section only for early career, OSS, or notable side work --- ## Stage 3: Impact Bullets **Goal:** Pattern: strong verb + scope + quantified outcome (“Reduced p95 API latency 40% by…”). ### Practices - Metrics: users, revenue, %, incidents avoided, time saved - Never fabricate numbers—approximate ranges only if ethically OK --- ## Stage 4: Skills & Keywords **Goal:** Skills section mirrors target JD language for tools you can defend in interview. --- ## Stage 5: ATS & Format **Goal:** Simple layout; standard headings; avoid critical text only inside images; PDF unless employer specifies otherwise. --- ## Stage 6: Review & Tailoring **Goal:** Tailor top bullets per application; proofread; peer review for clarity and exaggeration checks. --- ## Final Review Checklist - [ ] Headline matches target role - [ ] Bullets emphasize measurable impact - [ ] Skills honest and interview-defensible - [ ] ATS-friendly formatting - [ ] Proofread; consistent tense and date format ## Tips for Effective Guidance - Remove filler (“team player”) unless proven by story elsewhere. - Staff+ candidates: emphasize scope, multi-team leadership, and business outcomes. - Link GitHub/portfolio only if maintained and impressive. ## Handling Deviations - Non-traditional path: emphasize projects with outcomes and certifications if relevant.
Deep REST workflow—resource modeling, HTTP methods and safety, status codes, errors, pagination, caching, versioning, and idempotency. Use when designing HTT...
--- name: rest-best-practices description: Deep REST workflow—resource modeling, HTTP methods and safety, status codes, errors, pagination, caching, versioning, and idempotency. Use when designing HTTP APIs or reviewing controllers and gateways. --- # REST Best Practices (Deep Workflow) REST is **HTTP semantics** used consistently: **resources** as nouns, **methods** with meaning, **predictable** errors, and **cacheable** reads where safe. ## When to Offer This Workflow **Trigger conditions:** - Designing public or partner HTTP APIs - Inconsistent verbs (GET with side effects); wrong status codes - CDN/caching surprises; client retry storms on POST **Initial offer:** Use **six stages**: (1) resource model, (2) methods & safety, (3) status & errors, (4) pagination & filtering, (5) caching & conditional requests, (6) versioning & evolution). Confirm JSON conventions and authentication model. --- ## Stage 1: Resource Model **Goal:** Clear collection vs item resources; relationships via sub-paths or hypermedia links (HATEOAS optional). **Exit condition:** Table or diagram of resources, identifiers, and canonical URLs. --- ## Stage 2: Methods & Safety **Goal:** GET/HEAD safe and idempotent; POST for creation or non-idempotent actions; PUT replaces; PATCH partial; DELETE removes. ### Anti-patterns - Non-idempotent GET; overloaded POST for everything without documented patterns --- ## Stage 3: Status & Errors **Goal:** Correct 4xx vs 5xx; consistent error body (e.g., RFC 7807 Problem Details) with stable `type` codes and optional `instance` for support. --- ## Stage 4: Pagination & Filtering **Goal:** Cursor pagination for large lists; document sort/filter query params; cap page sizes. --- ## Stage 5: Caching & Conditional Requests **Goal:** ETag/Last-Modified for cacheable GET; Cache-Control directives; validate with intermediaries (CDN) when used. --- ## Stage 6: Versioning & Evolution **Goal:** URL prefix or header versioning; deprecation policy; **Idempotency-Key** on POST when clients retry. --- ## Final Review Checklist - [ ] Resource model clear and consistent - [ ] HTTP methods match semantics; GET is safe - [ ] Status codes and errors consistent - [ ] Pagination and filtering documented - [ ] Caching headers where appropriate - [ ] Versioning and idempotency strategy ## Tips for Effective Guidance - Not everything is CRUD—model commands as sub-resources or task resources explicitly. - Pair with **openapi-spec** for contract-first workflows. ## Handling Deviations - Internal APIs still benefit from the same discipline—future consumers are often external.
Deep release management workflow—versioning semantics, branching and tags, release checklist, communications, rollback criteria, and hotfix discipline. Use w...
--- name: release-management description: Deep release management workflow—versioning semantics, branching and tags, release checklist, communications, rollback criteria, and hotfix discipline. Use when shipping software on a cadence or coordinating multi-team releases. --- # Release Management (Deep Workflow) Releases combine **coordination** and **risk management**: what ships, when, how we verify, and how we recover. ## When to Offer This Workflow **Trigger conditions:** - Scheduled or train-based releases; need predictable cadence - Hotfix chaos or unclear ownership - SemVer vs calendar versioning debates **Initial offer:** Use **six stages**: (1) versioning policy, (2) branching & tags, (3) release candidate & checklist, (4) communication, (5) deploy & verify, (6) post-release & hotfixes). Confirm deployment model (continuous delivery vs batch releases). --- ## Stage 1: Versioning Policy **Goal:** Define SemVer (or alternative) rules: what counts as breaking, how migrations are communicated, and how pre-releases work. **Exit condition:** Written policy linking version numbers to customer-facing risk. --- ## Stage 2: Branching & Tags **Goal:** Choose a strategy (trunk-based with release branches, GitFlow-lite, etc.) and use **annotated tags** tied to changelog entries. --- ## Stage 3: Release Candidate & Checklist **Goal:** Freeze criteria: QA scope, DB migrations dry-run, feature flag defaults, config diffs, load/perf smoke for risky changes. --- ## Stage 4: Communication **Goal:** Internal notes for support/CS/SRE; external release notes with **breaking** changes highlighted and migration steps. --- ## Stage 5: Deploy & Verify **Goal:** Canary or progressive rollout where possible; SLO dashboards; **rollback trigger** agreed before deploy starts. --- ## Stage 6: Post-Release & Hotfixes **Goal:** Lightweight retro; hotfix branches from the release tag; forward-merge fixes back to main; track incidents linked to release. --- ## Final Review Checklist - [ ] Versioning and changelog reflect risk - [ ] Branch/tag strategy is team-wide consensus - [ ] Checklist covers migrations, flags, QA, and comms - [ ] Rollback plan rehearsed or documented - [ ] Hotfix process defined ## Tips for Effective Guidance - Smallest releasable increment reduces blast radius. - Coordinate DB schema changes with expand/contract patterns when zero-downtime matters. - Complement with **ci-cd** and **changelog-authoring** skills for tooling depth. ## Handling Deviations - **SaaS with heavy feature-flagging:** “release” may be continuous—still document communication and flag cleanup discipline.
Deep refactoring workflow—characterization tests, incremental steps, behavior preservation, design direction, and verification. Use when improving structure...
--- name: refactoring description: Deep refactoring workflow—characterization tests, incremental steps, behavior preservation, design direction, and verification. Use when improving structure without changing external behavior, or paying down tech debt safely. --- # Refactoring (Deep Workflow) Refactoring changes **structure**, not **behavior**. Safety comes from **small steps**, **fast feedback**, and verification (tests, golden outputs, or controlled manual checks). ## When to Offer This Workflow **Trigger conditions:** - Code is hard to change; duplication; unclear module boundaries - Need to prepare an area for a new feature without mixing behavior change - Paying down tech debt with management expecting “no user-visible change” **Initial offer:** Use **six stages**: (1) clarify goal & scope, (2) establish safety net, (3) plan increments, (4) execute with reviewable commits, (5) verify behavior, (6) document & follow-ups). Confirm test coverage and release pressure. --- ## Stage 1: Clarify Goal & Scope **Goal:** Why refactor now—reduce coupling, enable feature X, remove dead code, improve naming. **Exit condition:** Explicit **non-goals** (no feature changes in this effort unless separately scoped). --- ## Stage 2: Establish Safety Net **Goal:** Prefer characterization tests for legacy; golden outputs for data pipelines; use snapshot tests sparingly. ### If tests are weak - Approval tests, short exploratory scripts, or pair review with domain expert --- ## Stage 3: Plan Increments **Goal:** Small commits, each leaving the codebase **working** (not necessarily perfect). ### Practices - Move code, then change behavior in separate steps (Fowler-style when helpful) - Separate mechanical renames from logic edits for reviewability --- ## Stage 4: Execute With Reviewable Commits **Goal:** Each PR/commit tells a story; avoid thousand-line “cleanup” dumps. --- ## Stage 5: Verify Behavior **Goal:** CI green; compare outputs for batch jobs; manual smoke on critical paths when needed. --- ## Stage 6: Document & Follow-Ups **Goal:** ADR or short module README for new boundaries; tickets for remaining debt accepted consciously. --- ## Final Review Checklist - [ ] Scope and non-goals explicit - [ ] Safety net matches risk - [ ] Incremental, reviewable steps - [ ] Behavior verified - [ ] Follow-up debt tracked ## Tips for Effective Guidance - Keep refactor commits separate from feature commits when possible. - If behavior must change, it is not “pure refactoring”—plan as a migration with communication. - Under hotfix pressure, minimize refactor scope or defer. ## Handling Deviations - **Strangler** refactors: maintain adapters at boundaries until cutover is complete.