chore(docs): remove non-e2e documentation

This commit is contained in:
Jiayuan Zhang 2026-02-17 00:46:36 +08:00
parent 292e2b9454
commit ecb0cd392e
47 changed files with 0 additions and 11844 deletions

View file

@ -1,197 +0,0 @@
# Agent Profile System
The Agent Profile system allows you to define and manage agent personalities, capabilities, and configurations. Each profile is a collection of markdown files and a JSON configuration file stored in a directory.
## Directory Structure
```
~/.super-multica/agent-profiles/
└── <profile-id>/
├── soul.md # Personality constraints and behavior style
├── identity.md # Agent's name and self-awareness
├── tools.md # Custom tool usage instructions
├── memory.md # Persistent knowledge base
├── bootstrap.md # Guidance for each conversation start
└── config.json # Profile configuration (tools, provider, model)
```
## Profile Files
### soul.md
Defines the agent's personality constraints and behavior boundaries.
```markdown
# Soul
You are a helpful AI assistant. Follow these guidelines:
- Be concise and direct in your responses
- Ask clarifying questions when requirements are ambiguous
- Admit when you don't know something
```
### identity.md
Contains the agent's identity information.
```markdown
# Identity
- Name: CodeBot
- Role: Software development assistant
```
### tools.md
Custom instructions for tool usage (appended to the system prompt).
### memory.md
Persistent knowledge base that survives across conversations.
### bootstrap.md
Guidance information provided at the start of each conversation.
### config.json
JSON configuration for the profile:
```json
{
"tools": {
"profile": "coding",
"allow": ["web_fetch"],
"deny": ["exec"]
},
"provider": "anthropic",
"model": "claude-sonnet-4-20250514",
"thinkingLevel": "medium"
}
```
## Configuration Options
### tools
Tool policy configuration. See [Tools README](../tools/README.md) for details.
| Field | Type | Description |
|-------|------|-------------|
| `profile` | string | Base profile: `minimal`, `coding`, `web`, `full` |
| `allow` | string[] | Additional tools to allow (supports `group:*` syntax) |
| `deny` | string[] | Tools to block (takes precedence over allow) |
| `byProvider` | object | Provider-specific tool rules |
Example configurations:
```json
// Minimal - only file operations
{
"tools": {
"profile": "minimal",
"allow": ["group:fs"]
}
}
// Coding without web access
{
"tools": {
"profile": "coding",
"deny": ["group:web"]
}
}
// Full access except shell execution
{
"tools": {
"deny": ["exec", "process"]
}
}
```
### provider
Default LLM provider for this profile.
### model
Default model ID for this profile.
### thinkingLevel
Default thinking level: `none`, `low`, `medium`, `high`.
## Usage
### CLI
```bash
# Use a specific profile
pnpm agent:cli --profile my-agent "Hello"
# Profile with custom base directory
pnpm agent:cli --profile my-agent --profile-dir /path/to/profiles "Hello"
```
### Programmatic
```typescript
import { ProfileManager } from "./profile/index.js";
// Load existing profile
const manager = new ProfileManager({
profileId: "my-agent",
baseDir: "/custom/path", // optional
});
// Get profile (returns undefined if not exists)
const profile = manager.getProfile();
// Get or create with defaults
const profile = manager.getOrCreateProfile(true); // useTemplates
// Build system prompt from profile
const systemPrompt = manager.buildSystemPrompt();
// Get tools configuration
const toolsConfig = manager.getToolsConfig();
// Get full profile config
const config = manager.getProfileConfig();
```
## Config Priority
When using a profile, configurations are merged with CLI options:
1. **Profile config.json** - Base configuration
2. **CLI options** - Override profile settings
```bash
# Profile has tools.profile = "coding"
# CLI adds --tools-deny exec
# Result: coding profile without exec tool
pnpm agent:cli --profile my-agent --tools-deny exec "list files"
```
The merge behavior:
- `profile`: CLI wins if specified
- `allow`: Union of both lists
- `deny`: Union of both lists
- `byProvider`: Deep merge with CLI taking precedence
## Creating a Profile
### Manual Creation
1. Create directory: `mkdir -p ~/.super-multica/agent-profiles/my-agent`
2. Create markdown files (soul.md, identity.md, etc.)
3. Create config.json with your settings
### Programmatic Creation
```typescript
import { createAgentProfile } from "./profile/index.js";
// Create with default templates
const profile = createAgentProfile("my-agent", {
useTemplates: true, // Fill with default content
});
// Create empty profile
const profile = createAgentProfile("minimal-agent", {
useTemplates: false,
});
```

View file

@ -1,438 +0,0 @@
# Skills System
[English](./README.md) | [中文](./README.zh-CN.md)
Skills extend agent capabilities through `SKILL.md` definition files.
## Table of Contents
- [SKILL.md Specification](#skillmd-specification)
- [Skill Invocation](#skill-invocation)
- [Loading & Precedence](#loading--precedence)
- [CLI Commands](#cli-commands)
---
## SKILL.md Specification
Each skill is a directory containing a `SKILL.md` file with YAML frontmatter + Markdown content.
### Basic Structure
```markdown
---
name: My Skill
version: 1.0.0
description: What this skill does
metadata:
emoji: "🔧"
requires:
bins: [git]
---
# Instructions
Detailed instructions injected into the agent's system prompt...
```
### Frontmatter Fields
| Field | Type | Required | Description |
|-------|------|----------|-------------|
| `name` | string | Yes | Display name |
| `version` | string | No | Version number |
| `description` | string | No | Short description |
| `homepage` | string | No | Homepage URL |
| `metadata` | object | No | See below |
| `config` | object | No | See below |
| `install` | array | No | See below |
### metadata.requires
Defines eligibility requirements:
```yaml
metadata:
emoji: "📝"
requires:
bins: [git, node] # All must exist
anyBins: [npm, pnpm] # At least one must exist
env: [API_KEY] # All must be set
platforms: [darwin, linux] # Current OS must match
```
| Field | Description |
|-------|-------------|
| `bins` | Required binaries (all must exist in PATH) |
| `anyBins` | Alternative binaries (at least one must exist) |
| `env` | Required environment variables |
| `platforms` | Supported platforms: `darwin`, `linux`, `win32` |
### config
Runtime configuration options:
```yaml
config:
enabled: true
requiresConfig: ["skills.myskill.apiKey"]
options:
timeout: 30000
```
### install
Dependency installation specifications:
```yaml
install:
- kind: brew
package: jq
- kind: npm
package: typescript
global: true
- kind: uv
package: requests
- kind: go
package: github.com/example/tool@latest
- kind: download
url: https://example.com/tool.tar.gz
archiveType: tar.gz
stripComponents: 1
```
**Supported install kinds:**
| Kind | Description | Key Fields |
|------|-------------|------------|
| `brew` | Homebrew | `package`, `cask` |
| `npm` | npm/pnpm/yarn | `package`, `global` |
| `uv` | Python uv | `package` |
| `go` | Go install | `package` |
| `download` | Download & extract | `url`, `archiveType` |
**Common fields:** `id`, `label`, `platforms`, `when`
---
## Skill Invocation
Skills can be invoked by users via slash commands (`/skill-name`) or automatically by the AI model.
### User Invocation
In the interactive CLI, type `/` followed by a skill name to invoke it:
```
You: /pdf analyze report.pdf
```
**Tab completion**: Type `/p` then press Tab to see matching skills like `/pdf`.
**List available skills**: Type `/help` to see all available skill commands.
### Invocation Control
Control how skills can be invoked using frontmatter fields:
```yaml
---
name: My Skill
user-invocable: true # Can be invoked via /command (default: true)
disable-model-invocation: false # Include in AI prompt (default: false)
---
```
| Field | Default | Description |
|-------|---------|-------------|
| `user-invocable` | `true` | Enable `/command` invocation in CLI |
| `disable-model-invocation` | `false` | If `true`, skill is hidden from AI's system prompt |
**Use cases:**
- **User-only skill** (`disable-model-invocation: true`): User can invoke via `/command`, but AI won't use it automatically
- **AI-only skill** (`user-invocable: false`): AI can use it, but no `/command` available
- **Disabled skill** (both `false`): Hidden from both user and AI
### Command Dispatch
For advanced integrations, skills can dispatch directly to tools:
```yaml
---
name: PDF Tool
command-dispatch: tool
command-tool: pdf-processor
command-arg-mode: raw
---
```
| Field | Description |
|-------|-------------|
| `command-dispatch` | Set to `tool` to enable tool dispatch |
| `command-tool` | Name of the tool to invoke |
| `command-arg-mode` | How arguments are passed (`raw` = as-is) |
### Command Name Normalization
Skill names are normalized for command use:
- Converted to lowercase
- Special characters replaced with underscores
- Truncated to 32 characters max
- Duplicate names get numeric suffixes (e.g., `pdf_2`)
---
## Loading & Precedence
Skills load from two sources with precedence (lowest to highest):
| Priority | Source | Path | Description |
|----------|--------|------|-------------|
| 1 | managed | `~/.super-multica/skills/` | Global skills (CLI-installed + bundled) |
| 2 | profile | `~/.super-multica/agent-profiles/<id>/skills/` | Profile-specific skills |
Higher priority sources override skills with the same ID.
### Initialization
On first run, bundled skills are automatically copied to the managed directory (`~/.super-multica/skills/`). This makes them editable and allows users to customize or remove them.
### Adding Profile-Specific Skills
You can install skills directly to a profile using the `--profile` option:
```bash
# Install skill to a specific profile
multica skills add owner/repo --profile my-agent
# Install with force overwrite
multica skills add owner/repo/skill-name --profile my-agent --force
```
Alternatively, create them manually:
```bash
# Create profile skills directory
mkdir -p ~/.super-multica/agent-profiles/<profile-id>/skills/<skill-name>
# Create the SKILL.md file
cat > ~/.super-multica/agent-profiles/<profile-id>/skills/<skill-name>/SKILL.md << 'EOF'
---
name: My Profile Skill
version: 1.0.0
description: A skill specific to this profile
---
# Instructions
Your skill instructions here...
EOF
```
Profile skills automatically override managed skills with the same ID, allowing per-profile customization.
### Eligibility Filtering
After loading, skills are filtered by:
1. Platform check (`platforms`)
2. Binary check (`bins`, `anyBins`)
3. Environment check (`env`)
4. Config check (`requiresConfig`)
5. Enabled check (`config.enabled`)
Only skills passing all checks are marked as eligible.
---
## CLI Commands
All commands use the unified `multica` CLI (or `pnpm multica` during development).
### List Skills
```bash
multica skills list # List all skills
multica skills list -v # Verbose mode
multica skills status # Summary status
multica skills status <id> # Specific skill status
```
### Install from GitHub
**Example: Installing from [anthropics/skills](https://github.com/anthropics/skills)**
The repository structure:
```
anthropics/skills/
├── skills/
│ ├── algorithmic-art/
│ │ └── SKILL.md
│ ├── brand-guidelines/
│ │ └── SKILL.md
│ ├── pdf/
│ │ └── SKILL.md
│ └── ... (16 skills total)
```
Install the entire repository (all 16 skills):
```bash
multica skills add anthropics/skills
# Installs to: ~/.super-multica/skills/skills/
# All skills available: algorithmic-art, brand-guidelines, pdf, etc.
```
Install a single skill only:
```bash
multica skills add anthropics/skills/skills/pdf
# Installs to: ~/.super-multica/skills/pdf/
# Only the pdf skill is installed
```
Install from a specific branch or tag:
```bash
multica skills add anthropics/skills@main
```
Using full URL:
```bash
multica skills add https://github.com/anthropics/skills
multica skills add https://github.com/anthropics/skills/tree/main/skills/pdf
```
Force overwrite existing:
```bash
multica skills add anthropics/skills --force
```
**Supported formats:**
| Format | Example | Description |
|--------|---------|-------------|
| `owner/repo` | `anthropics/skills` | Clone entire repository |
| `owner/repo/path` | `anthropics/skills/skills/pdf` | Single directory (sparse checkout) |
| `owner/repo@ref` | `anthropics/skills@v1.0.0` | Specific branch or tag |
| Full URL | `https://github.com/anthropics/skills` | GitHub URL |
| Full URL + path | `https://github.com/.../tree/main/skills/pdf` | URL with specific path |
### Remove Skills
```bash
multica skills remove <name> # Remove installed skill
multica skills remove # List installed skills
```
### Install Dependencies
```bash
multica skills install <id> # Install skill dependencies
multica skills install <id> <install-id> # Specific install option
```
---
## Status Diagnostics
The `status` command provides detailed diagnostics for understanding why skills are or aren't eligible.
### Summary Status
```bash
multica skills status # Show summary with grouping by issue type
multica skills status -v # Verbose mode with hints
```
Output shows:
- Total/eligible/ineligible counts
- Ineligible skills grouped by issue type (binary, env, platform, etc.)
### Detailed Skill Status
```bash
multica skills status <skill-id>
```
Output includes:
- Basic skill info (name, version, source, path)
- **Eligibility status** with detailed diagnostics
- **Requirements checklist** showing which binaries/env vars are present
- **Install options** with availability status
- **Quick actions** with actionable hints to resolve issues
### Diagnostic Types
| Type | Description | Example Hint |
|------|-------------|--------------|
| `disabled` | Skill disabled in config | Enable via `skills.<id>.enabled: true` |
| `not_in_allowlist` | Bundled skill not allowed | Add to `config.allowBundled` array |
| `platform` | Platform mismatch | "Only works on: darwin, linux" |
| `binary` | Missing required binary | "brew install git" |
| `any_binary` | No alternative binary found | "Install any of: npm, pnpm, yarn" |
| `env` | Missing environment variable | "export OPENAI_API_KEY=..." |
| `config` | Missing config value | "Set config path: browser.enabled" |
---
## Async Serialization
The skills system uses async serialization to prevent concurrent operations from corrupting files or causing race conditions.
### How It Works
Operations with the same key are executed sequentially:
```typescript
import { serialize, SerializeKeys } from "./skills/index.js";
// These will execute sequentially, not in parallel
const p1 = serialize(SerializeKeys.skillAdd("my-skill"), () => addSkill(...));
const p2 = serialize(SerializeKeys.skillAdd("my-skill"), () => addSkill(...));
// This runs in parallel (different key)
const p3 = serialize(SerializeKeys.skillAdd("other-skill"), () => addSkill(...));
```
### Built-in Serialization
The following operations are automatically serialized:
- `addSkill()` - by skill name
- `removeSkill()` - by skill name
- `installSkill()` - by skill ID
### Utility Functions
```typescript
import {
isProcessing, // Check if key is being processed
getQueueLength, // Get pending operations count
getActiveKeys, // Get all active operation keys
waitForKey, // Wait for key operations to complete
waitForAll, // Wait for all operations
} from "./skills/index.js";
```
---
## Troubleshooting
**Skill not showing as eligible?**
Run `pnpm skills:cli status <skill-id>` to see detailed diagnostics with actionable hints.
**Override a bundled skill?**
Create a skill with the same ID in `~/.super-multica/skills/` or profile skills directory.
**Hot reload not working?**
Ensure `chokidar` is installed: `pnpm add chokidar`
**Concurrent operations causing issues?**
All add/remove/install operations are automatically serialized. If you're building custom integrations, use the `serialize()` function with appropriate keys.

View file

@ -1,438 +0,0 @@
# Skills 系统
[English](./README.md) | [中文](./README.zh-CN.md)
Skills 通过 `SKILL.md` 定义文件扩展 Agent 的能力。
## 目录
- [SKILL.md 规范](#skillmd-规范)
- [Skill 调用](#skill-调用)
- [加载与优先级](#加载与优先级)
- [CLI 命令](#cli-命令)
---
## SKILL.md 规范
每个 skill 是一个包含 `SKILL.md` 文件的目录,文件包含 YAML frontmatter 和 Markdown 内容。
### 基本结构
```markdown
---
name: My Skill
version: 1.0.0
description: 这个 skill 的功能描述
metadata:
emoji: "🔧"
requires:
bins: [git]
---
# 说明
注入到 agent 系统提示词中的详细说明...
```
### Frontmatter 字段
| 字段 | 类型 | 必需 | 描述 |
|------|------|------|------|
| `name` | string | 是 | 显示名称 |
| `version` | string | 否 | 版本号 |
| `description` | string | 否 | 简短描述 |
| `homepage` | string | 否 | 主页 URL |
| `metadata` | object | 否 | 见下文 |
| `config` | object | 否 | 见下文 |
| `install` | array | 否 | 见下文 |
### metadata.requires
定义资格要求:
```yaml
metadata:
emoji: "📝"
requires:
bins: [git, node] # 全部必须存在
anyBins: [npm, pnpm] # 至少一个必须存在
env: [API_KEY] # 全部必须设置
platforms: [darwin, linux] # 当前操作系统必须匹配
```
| 字段 | 描述 |
|------|------|
| `bins` | 必需的二进制文件(全部必须存在于 PATH 中) |
| `anyBins` | 备选二进制文件(至少一个必须存在) |
| `env` | 必需的环境变量 |
| `platforms` | 支持的平台:`darwin``linux``win32` |
### config
运行时配置选项:
```yaml
config:
enabled: true
requiresConfig: ["skills.myskill.apiKey"]
options:
timeout: 30000
```
### install
依赖安装规范:
```yaml
install:
- kind: brew
package: jq
- kind: npm
package: typescript
global: true
- kind: uv
package: requests
- kind: go
package: github.com/example/tool@latest
- kind: download
url: https://example.com/tool.tar.gz
archiveType: tar.gz
stripComponents: 1
```
**支持的安装类型:**
| 类型 | 描述 | 关键字段 |
|------|------|----------|
| `brew` | Homebrew | `package``cask` |
| `npm` | npm/pnpm/yarn | `package``global` |
| `uv` | Python uv | `package` |
| `go` | Go install | `package` |
| `download` | 下载并解压 | `url``archiveType` |
**通用字段:** `id``label``platforms``when`
---
## Skill 调用
用户可以通过斜杠命令(`/skill-name`)调用 skillsAI 模型也可以自动调用。
### 用户调用
在交互式 CLI 中,输入 `/` 加上 skill 名称来调用:
```
You: /pdf analyze report.pdf
```
**Tab 补全**:输入 `/p` 然后按 Tab 键查看匹配的 skills`/pdf`
**列出可用 skills**:输入 `/help` 查看所有可用的 skill 命令。
### 调用控制
使用 frontmatter 字段控制 skill 的调用方式:
```yaml
---
name: My Skill
user-invocable: true # 可通过 /command 调用默认true
disable-model-invocation: false # 包含在 AI 提示词中默认false
---
```
| 字段 | 默认值 | 描述 |
|------|--------|------|
| `user-invocable` | `true` | 在 CLI 中启用 `/command` 调用 |
| `disable-model-invocation` | `false` | 如果为 `true`skill 对 AI 的系统提示词隐藏 |
**使用场景:**
- **仅用户 skill**`disable-model-invocation: true`):用户可通过 `/command` 调用,但 AI 不会自动使用
- **仅 AI skill**`user-invocable: false`AI 可使用,但没有 `/command` 可用
- **禁用 skill**(两者都为 `false`):对用户和 AI 都隐藏
### 命令分发
对于高级集成skills 可以直接分发到工具:
```yaml
---
name: PDF Tool
command-dispatch: tool
command-tool: pdf-processor
command-arg-mode: raw
---
```
| 字段 | 描述 |
|------|------|
| `command-dispatch` | 设置为 `tool` 启用工具分发 |
| `command-tool` | 要调用的工具名称 |
| `command-arg-mode` | 参数传递方式(`raw` = 原样传递) |
### 命令名称规范化
Skill 名称会被规范化以用作命令:
- 转换为小写
- 特殊字符替换为下划线
- 截断至最多 32 个字符
- 重复名称添加数字后缀(如 `pdf_2`
---
## 加载与优先级
Skills 从两个来源加载,优先级从低到高:
| 优先级 | 来源 | 路径 | 描述 |
|--------|------|------|------|
| 1 | managed | `~/.super-multica/skills/` | 全局 skillsCLI 安装 + 内置) |
| 2 | profile | `~/.super-multica/agent-profiles/<id>/skills/` | Profile 专属 skills |
高优先级来源会覆盖具有相同 ID 的 skills。
### 初始化
首次运行时,内置 skills 会自动复制到 managed 目录(`~/.super-multica/skills/`)。这使得用户可以编辑或删除它们。
### 添加 Profile 专属 Skills
可以使用 `--profile` 选项直接安装 skills 到特定 profile
```bash
# 安装 skill 到特定 profile
multica skills add owner/repo --profile my-agent
# 强制覆盖安装
multica skills add owner/repo/skill-name --profile my-agent --force
```
也可以手动创建:
```bash
# 创建 profile skills 目录
mkdir -p ~/.super-multica/agent-profiles/<profile-id>/skills/<skill-name>
# 创建 SKILL.md 文件
cat > ~/.super-multica/agent-profiles/<profile-id>/skills/<skill-name>/SKILL.md << 'EOF'
---
name: My Profile Skill
version: 1.0.0
description: 此 profile 专属的 skill
---
# 说明
你的 skill 说明内容...
EOF
```
Profile skills 会自动覆盖同 ID 的 managed skills允许按 profile 自定义。
### 资格过滤
加载后skills 会按以下条件过滤:
1. 平台检查(`platforms`
2. 二进制文件检查(`bins``anyBins`
3. 环境变量检查(`env`
4. 配置检查(`requiresConfig`
5. 启用检查(`config.enabled`
只有通过所有检查的 skills 才会被标记为符合条件。
---
## CLI 命令
所有命令使用统一的 `multica` CLI开发时使用 `pnpm multica`)。
### 列出 Skills
```bash
multica skills list # 列出所有 skills
multica skills list -v # 详细模式
multica skills status # 汇总状态
multica skills status <id> # 特定 skill 状态
```
### 从 GitHub 安装
**示例:从 [anthropics/skills](https://github.com/anthropics/skills) 安装**
仓库结构:
```
anthropics/skills/
├── skills/
│ ├── algorithmic-art/
│ │ └── SKILL.md
│ ├── brand-guidelines/
│ │ └── SKILL.md
│ ├── pdf/
│ │ └── SKILL.md
│ └── ... (共 16 个 skills)
```
安装整个仓库(所有 16 个 skills
```bash
multica skills add anthropics/skills
# 安装到:~/.super-multica/skills/skills/
# 所有 skills 可用algorithmic-art、brand-guidelines、pdf 等
```
只安装单个 skill
```bash
multica skills add anthropics/skills/skills/pdf
# 安装到:~/.super-multica/skills/pdf/
# 只安装 pdf skill
```
从特定分支或标签安装:
```bash
multica skills add anthropics/skills@main
```
使用完整 URL
```bash
multica skills add https://github.com/anthropics/skills
multica skills add https://github.com/anthropics/skills/tree/main/skills/pdf
```
强制覆盖现有:
```bash
multica skills add anthropics/skills --force
```
**支持的格式:**
| 格式 | 示例 | 描述 |
|------|------|------|
| `owner/repo` | `anthropics/skills` | 克隆整个仓库 |
| `owner/repo/path` | `anthropics/skills/skills/pdf` | 单个目录(稀疏检出) |
| `owner/repo@ref` | `anthropics/skills@v1.0.0` | 特定分支或标签 |
| 完整 URL | `https://github.com/anthropics/skills` | GitHub URL |
| 完整 URL + 路径 | `https://github.com/.../tree/main/skills/pdf` | 带特定路径的 URL |
### 移除 Skills
```bash
multica skills remove <name> # 移除已安装的 skill
multica skills remove # 列出已安装的 skills
```
### 安装依赖
```bash
multica skills install <id> # 安装 skill 依赖
multica skills install <id> <install-id> # 特定安装选项
```
---
## 状态诊断
`status` 命令提供详细的诊断信息,帮助了解 skills 为何符合或不符合条件。
### 汇总状态
```bash
multica skills status # 显示按问题类型分组的汇总
multica skills status -v # 详细模式带提示
```
输出显示:
- 总计/符合条件/不符合条件计数
- 按问题类型分组的不符合条件 skillsbinary、env、platform 等)
### 详细 Skill 状态
```bash
multica skills status <skill-id>
```
输出包括:
- 基本 skill 信息(名称、版本、来源、路径)
- **资格状态**及详细诊断
- **要求检查表**显示哪些二进制文件/环境变量存在
- **安装选项**及可用性状态
- **快速操作**及可操作的提示
### 诊断类型
| 类型 | 描述 | 示例提示 |
|------|------|----------|
| `disabled` | Skill 在配置中禁用 | 通过 `skills.<id>.enabled: true` 启用 |
| `not_in_allowlist` | 内置 skill 不在允许列表中 | 添加到 `config.allowBundled` 数组 |
| `platform` | 平台不匹配 | "仅支持darwin、linux" |
| `binary` | 缺少必需的二进制文件 | "brew install git" |
| `any_binary` | 未找到备选二进制文件 | "安装任一npm、pnpm、yarn" |
| `env` | 缺少环境变量 | "export OPENAI_API_KEY=..." |
| `config` | 缺少配置值 | "设置配置路径browser.enabled" |
---
## 异步序列化
Skills 系统使用异步序列化来防止并发操作损坏文件或导致竞态条件。
### 工作原理
具有相同键的操作按顺序执行:
```typescript
import { serialize, SerializeKeys } from "./skills/index.js";
// 这些将按顺序执行,而非并行
const p1 = serialize(SerializeKeys.skillAdd("my-skill"), () => addSkill(...));
const p2 = serialize(SerializeKeys.skillAdd("my-skill"), () => addSkill(...));
// 这个并行运行(不同的键)
const p3 = serialize(SerializeKeys.skillAdd("other-skill"), () => addSkill(...));
```
### 内置序列化
以下操作自动序列化:
- `addSkill()` - 按 skill 名称
- `removeSkill()` - 按 skill 名称
- `installSkill()` - 按 skill ID
### 工具函数
```typescript
import {
isProcessing, // 检查键是否正在处理
getQueueLength, // 获取待处理操作数量
getActiveKeys, // 获取所有活动操作键
waitForKey, // 等待键操作完成
waitForAll, // 等待所有操作
} from "./skills/index.js";
```
---
## 故障排除
**Skill 未显示为符合条件?**
运行 `multica skills status <skill-id>` 查看详细诊断及可操作的提示。
**覆盖内置 skill**
`~/.super-multica/skills/` 或配置文件 skills 目录中创建具有相同 ID 的 skill。
**热重载不工作?**
确保安装了 `chokidar``pnpm add chokidar`
**并发操作导致问题?**
所有 add/remove/install 操作都会自动序列化。如果你在构建自定义集成,请使用 `serialize()` 函数并使用适当的键。

View file

@ -1,172 +0,0 @@
# Subagent System
The subagent system allows a parent agent to spawn isolated child agents that run tasks in parallel and report results back automatically.
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────────┐
│ Parent Agent (runner.ts) │
│ │
│ tools: sessions_spawn, sessions_list │
│ state: resolvedProvider, toolsOptions │
└──────────┬──────────────────────────────────────────────────────────┘
│ sessions_spawn(task, label, timeoutSeconds)
┌─────────────────────────────────────────────────────────────────────┐
│ Spawn Flow (sessions-spawn.ts) │
│ │
│ 1. Build subagent system prompt (announce.ts) │
│ 2. hub.createSubagent(childSessionId, { provider, model }) │
│ 3. registerSubagentRun({ start: () => childAgent.write(task) }) │
│ 4. Return { status: "accepted", runId, childSessionId } │
└──────────┬──────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Concurrency Queue (command-queue.ts) │
│ │
│ Lane: "subagent" — max 10 concurrent (configurable) │
│ Queued runs wait for a slot before start() is called │
└──────────┬──────────────────────────────────────────────────────────┘
│ slot acquired
┌─────────────────────────────────────────────────────────────────────┐
│ Child Agent Execution │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ AsyncAgent (async-agent.ts) │ │
│ │ - Isolated session with restricted tools (isSubagent=true) │ │
│ │ - Inherits parent's LLM provider │ │
│ │ - System prompt: task focus + error reporting rules │ │
│ │ - Tracks lastRunError for error propagation │ │
│ └───────────────────────────────────────────────────────────────┘ │
│ │
│ ┌───────────────────────────────────────────────────────────────┐ │
│ │ watchChildAgent (registry.ts) │ │
│ │ - Sets startedAt, starts timeout timer │ │
│ │ - waitForIdle() — waits for child's task queue to drain │ │
│ │ - onClose() — handles explicit close (timeout kill, etc.) │ │
│ └───────────────────────────────────────────────────────────────┘ │
└──────────┬──────────────────────────────────────────────────────────┘
│ child completes / errors / times out
┌─────────────────────────────────────────────────────────────────────┐
│ Completion Handling (registry.ts) │
│ │
│ handleRunCompletion(record) │
│ │ │
│ ├─ Phase 1: captureFindings() │
│ │ - Read last assistant reply from child session JSONL │
│ │ - Falls back to last toolResult if no assistant text │
│ │ - Persists findings to record before session deletion │
│ │ │
│ ├─ Session Cleanup │
│ │ - cleanup="delete": rm child session dir + hub.closeAgent() │
│ │ - cleanup="keep": preserve for audit │
│ │ │
│ └─ Phase 2: checkAndAnnounce(requesterSessionId) │
│ - Finds all unannounced, completed runs with findings │
│ - Calls runCoalescedAnnounceFlow() │
│ - Marks records: announced=true, archiveAtMs=now+60min │
└──────────┬──────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Announcement Delivery (announce.ts) │
│ │
│ runCoalescedAnnounceFlow(requesterSessionId, records) │
│ │ │
│ ├─ Format message: formatCoalescedAnnouncementMessage() │
│ │ - Single record: task name, status, findings, stats │
│ │ - Multiple records: combined report with all findings │
│ │ │
│ ├─ Two-tier delivery: │
│ │ │
│ │ Tier 1: BUSY (parent running or has pending writes) │
│ │ └─ enqueueAnnounce() → announce-queue.ts │
│ │ - Debounce 1s to batch nearby completions │
│ │ - Drain via writeInternal() when parent finishes │
│ │ │
│ │ Tier 2: IDLE (parent not running) │
│ │ └─ sendAnnounceDirect() │
│ │ - writeInternal(msg, { forwardAssistant, persistResponse })│
│ │ │
│ └─ All delivery uses writeInternal() (marks as internal: true) │
│ → Prevents announcement from showing as user bubble in UI │
│ → LLM processes findings and responds naturally to user │
└──────────┬──────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────────┐
│ Record Lifecycle (registry.ts) │
│ │
│ created → startedAt → endedAt → findingsCaptured → announced │
│ │
│ After announcement: │
│ - Record kept with archiveAtMs = now + 60 min │
│ - sessions_list can still query records during this window │
│ - Sweeper runs every 60s, removes expired records │
│ - When all records removed, sweeper stops │
└─────────────────────────────────────────────────────────────────────┘
```
## Key Files
| File | Purpose |
|------|---------|
| `sessions-spawn.ts` | Tool: spawns a child agent with task, label, timeout, provider |
| `sessions-list.ts` | Tool: lists subagent runs and their status |
| `registry.ts` | Lifecycle management: register, watch, capture, announce, archive |
| `announce.ts` | System prompt builder, findings reader, message formatter, delivery |
| `announce-queue.ts` | Debounced queue for batching announcements when parent is busy |
| `command-queue.ts` | Concurrency limiter for subagent lane slots |
| `lanes.ts` | Lane config: max concurrency (10), default timeout (1800s) |
| `types.ts` | Shared types: SubagentRunRecord, SubagentRunOutcome, etc. |
| `registry-store.ts` | Persistence: save/load runs to disk for crash recovery |
## Provider Inheritance
Subagents inherit the parent's resolved LLM provider:
```
runner.ts (resolvedProvider)
→ toolsOptions.provider
→ tools.ts (CreateToolsOptions.provider)
→ sessions-spawn.ts (options.provider)
→ hub.createSubagent({ provider })
```
When the user switches providers via UI (`setProvider()`), `toolsOptions.provider` is updated in sync so future spawns use the new provider.
## Error Propagation
```
Child tool error (e.g., API 401)
→ Subagent LLM sees error, includes in final message (system prompt rule)
→ captureFindings() reads final message
→ Announcement includes error in findings
→ Parent LLM sees error and can inform user
Child run error (e.g., missing API key for provider)
→ AsyncAgent._lastRunError set
→ registry.ts checks childAgent.lastRunError after waitForIdle()
→ outcome = { status: "error", error: "No API key configured..." }
→ Announcement: "task failed: No API key configured..."
```
## Timeout Behavior
Default: 1800s (30 min). System prompt guides the parent LLM:
- Simple tasks: 1800s (default)
- Moderate tasks: 1800-2400s (30-40 min)
- Complex tasks: 2400-3600s (40-60 min)
On timeout:
1. Timeout timer fires in `watchChildAgent()`
2. `cleanup({ status: "timeout" })` is called
3. Child agent is closed via `hub.closeAgent()`
4. Findings are captured from whatever the child wrote so far
5. Announcement reports "timed out" with partial findings

View file

@ -1,266 +0,0 @@
# Tools System
[中文文档](./README.zh-CN.md)
The tools system provides LLM agents with capabilities to interact with the external world. Tools are the "hands and feet" of an agent - without tools, an LLM can only generate text responses.
## Architecture Overview
```
┌─────────────────────────────────────────────────────────────────┐
│ Tool Definition │
│ (AgentTool from @mariozechner/pi-agent-core) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ name │ │ description │ │ parameters │ │
│ │ label │ │ execute │ │ (TypeBox) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 3-Layer Policy Filter │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Layer 1: Global Allow/Deny │ │
│ │ User customization via CLI or config │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Layer 2: Provider-Specific │ │
│ │ Different rules for different LLM providers │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ Layer 3: Subagent Restrictions │ │
│ │ Limited tools for spawned child agents │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ Filtered Tools │
│ (passed to pi-agent-core) │
└─────────────────────────────────────────────────────────────────┘
```
## Available Tools
| Tool | Name | Description |
| -------------- | ---------------- | ----------------------------------- |
| Read | `read` | Read file contents |
| Write | `write` | Write content to files |
| Edit | `edit` | Edit existing files |
| Glob | `glob` | Find files by pattern |
| Exec | `exec` | Execute shell commands |
| Process | `process` | Manage long-running processes |
| Web Fetch | `web_fetch` | Fetch and extract content from URLs |
| Web Search | `web_search` | Search the web via Devv Search |
| Sessions Spawn | `sessions_spawn` | Spawn a sub-agent session |
> **Note**: Agents use file-based memory (`memory.md`, `memory/*.md`) via `read` and `edit` tools instead of dedicated memory tools.
## Tool Groups
Groups provide shortcuts for allowing/denying multiple tools at once:
| Group | Tools |
| ---------------- | ------------------------------------ |
| `group:fs` | read, write, edit, glob |
| `group:runtime` | exec, process |
| `group:web` | web_search, web_fetch |
| `group:subagent` | sessions_spawn |
| `group:core` | All fs, runtime, and web tools |
## Usage
### CLI Usage
All commands use the unified `multica` CLI (or `pnpm multica` during development).
```bash
# Allow only specific tools
multica run --tools-allow group:fs,group:runtime "list files"
# Deny specific tools
multica run --tools-deny exec,process "read file.txt"
# Use tool groups
multica run --tools-allow group:fs "read config.json"
```
### Programmatic Usage
```typescript
import { Agent } from './runner.js';
const agent = new Agent({
tools: {
// Layer 1: Global allow/deny
allow: ['group:fs', 'group:runtime', 'web_fetch'],
deny: ['exec'],
// Layer 2: Provider-specific rules
byProvider: {
google: {
deny: ['exec', 'process'], // Google models can't use runtime tools
},
},
},
// Layer 3: Subagent mode
isSubagent: false,
});
```
### Inspecting Tool Configuration
Use the tools CLI to inspect and test configurations:
```bash
# List all available tools
multica tools list
# List tools with allow rules
multica tools list --allow group:fs,group:runtime
# List tools with deny rules
multica tools list --deny exec
# Show all tool groups
multica tools groups
```
## Policy System Details
### Layer 1: Global Allow/Deny
User-specified allow/deny lists:
- `allow`: Only these tools are available (supports group:\* syntax)
- `deny`: These tools are blocked (takes precedence over allow)
If no `allow` list is specified, all tools are available by default.
### Layer 2: Provider-Specific
Different LLM providers may have different capabilities or restrictions:
```typescript
{
byProvider: {
google: { deny: ["exec"] }, // Gemini can't execute commands
anthropic: { allow: ["*"] }, // Claude has full access
}
}
```
### Layer 3: Subagent Restrictions
When `isSubagent: true`, additional restrictions are applied to prevent spawned agents from accessing sensitive tools like session management.
## Adding New Tools
1. Create a new file in `src/agent/tools/` (e.g., `my-tool.ts`)
2. Define the tool using TypeBox for the schema:
```typescript
import { Type } from '@sinclair/typebox';
import type { AgentTool } from '@mariozechner/pi-agent-core';
const MyToolSchema = Type.Object({
param1: Type.String({ description: 'Parameter description' }),
param2: Type.Optional(Type.Number()),
});
export function createMyTool(): AgentTool<typeof MyToolSchema> {
return {
name: 'my_tool',
label: 'My Tool',
description: 'What this tool does',
parameters: MyToolSchema,
execute: async (toolCallId, args) => {
// Implementation
return { result: 'success' };
},
};
}
```
3. Register the tool in `src/agent/tools.ts`:
```typescript
import { createMyTool } from './tools/my-tool.js';
export function createAllTools(cwd: string): AgentTool<any>[] {
// ... existing tools
const myTool = createMyTool();
return [
...baseTools,
myTool as AgentTool<any>,
// ...
];
}
```
4. Add the tool to appropriate groups in `groups.ts`:
```typescript
export const TOOL_GROUPS: Record<string, string[]> = {
'group:my_category': ['my_tool', 'other_tool'],
// ...
};
```
## Testing
Run the policy system tests:
```bash
pnpm test src/agent/tools/policy.test.ts
```
## Agent Profile Integration
Tools configuration can be defined in Agent Profile's `config.json`, allowing different agents to have different tool capabilities:
```
┌─────────────────────────────────────────────────────────────────┐
│ Super Multica Hub │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Agent A │ │ Agent B │ │ Agent C │ │
│ │ Profile: │ │ Profile: │ │ Profile: │ │
│ │ coder │ │ reviewer │ │ devops │ │
│ │ │ │ │ │ │ │
│ │ tools: │ │ tools: │ │ tools: │ │
│ │ allow:fs │ │ deny:* │ │ allow:* │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
└─────────┼────────────────┼────────────────┼─────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Client │ │ Client │
└──────────┘ └──────────┘ └──────────┘
```
Each Agent's Profile can define its own tools configuration in `config.json`:
```json
{
"tools": {
"allow": ["group:fs", "group:runtime"],
"deny": ["exec"]
},
"provider": "anthropic",
"model": "claude-sonnet-4-20250514"
}
```
See [Profile README](../profile/README.md) for full documentation.

View file

@ -1,268 +0,0 @@
# 工具系统
[English](./README.md)
工具系统为 LLM Agent 提供与外部世界交互的能力。工具是 Agent 的"手和脚"——没有工具LLM 只能生成文本响应。
## 架构概览
```
┌─────────────────────────────────────────────────────────────────┐
│ 工具定义 │
│ (AgentTool from @mariozechner/pi-agent-core) │
│ │
│ ┌─────────────┐ ┌─────────────┐ ┌─────────────┐ │
│ │ name │ │ description │ │ parameters │ │
│ │ label │ │ execute │ │ (TypeBox) │ │
│ └─────────────┘ └─────────────┘ └─────────────┘ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 3 层策略过滤器 │
│ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 第 1 层: 全局 Allow/Deny │ │
│ │ 通过 CLI 或配置文件进行用户自定义 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 第 2 层: Provider 特定规则 │ │
│ │ 不同 LLM Provider 有不同的规则 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │ │
│ ▼ │
│ ┌──────────────────────────────────────────────────────────┐ │
│ │ 第 3 层: Subagent 限制 │ │
│ │ 子 Agent 的工具访问受限 │ │
│ └──────────────────────────────────────────────────────────┘ │
│ │
└─────────────────────────────────────────────────────────────────┘
┌─────────────────────────────────────────────────────────────────┐
│ 过滤后的工具 │
│ (传递给 pi-agent-core) │
└─────────────────────────────────────────────────────────────────┘
```
## 可用工具
| 工具 | 名称 | 描述 |
| -------------- | ---------------- | ------------------------------ |
| Read | `read` | 读取文件内容 |
| Write | `write` | 写入文件内容 |
| Edit | `edit` | 编辑现有文件 |
| Glob | `glob` | 按模式查找文件 |
| Exec | `exec` | 执行 Shell 命令 |
| Process | `process` | 管理长时间运行的进程 |
| Web Fetch | `web_fetch` | 从 URL 获取并提取内容 |
| Web Search | `web_search` | 搜索网络(需要 API Key |
| Memory Search | `memory_search` | 搜索 memory 文件(需要 Profile|
| Sessions Spawn | `sessions_spawn` | 创建子 Agent 会话 |
> **注意**: `memory_search` 工具通过关键词搜索 `memory.md``memory/*.md` 文件。Agent 通过 `read``edit` 工具操作 memory 文件内容。
## 工具组
工具组提供了一次性允许/禁止多个工具的快捷方式:
| 组 | 工具 |
| ---------------- | ------------------------------ |
| `group:fs` | read, write, edit, glob |
| `group:runtime` | exec, process |
| `group:web` | web_search, web_fetch |
| `group:memory` | memory_search |
| `group:subagent` | sessions_spawn |
| `group:core` | 所有 fs、runtime 和 web 工具 |
## 使用方法
### CLI 使用
所有命令使用统一的 `multica` CLI开发时使用 `pnpm multica`)。
```bash
# 只允许特定工具
multica run --tools-allow group:fs,group:runtime "list files"
# 禁止特定工具
multica run --tools-deny exec,process "read file.txt"
# 使用工具组
multica run --tools-allow group:fs "read config.json"
```
### 编程使用
```typescript
import { Agent } from './runner.js';
const agent = new Agent({
tools: {
// 第 1 层: 全局 allow/deny
allow: ['group:fs', 'group:runtime', 'web_fetch'],
deny: ['exec'],
// 第 2 层: Provider 特定规则
byProvider: {
google: {
deny: ['exec', 'process'], // Google 模型不能使用运行时工具
},
},
},
// 第 3 层: Subagent 模式
isSubagent: false,
});
```
### 检查工具配置
使用 tools CLI 检查和测试配置:
```bash
# 列出所有可用工具
multica tools list
# 列出带有允许规则的工具
multica tools list --allow group:fs,group:runtime
# 列出带有禁止规则的工具
multica tools list --deny exec
# 显示所有工具组
multica tools groups
```
## 策略系统详情
### 第 1 层: 全局 Allow/Deny
用户指定的 allow/deny 列表:
- `allow`: 只有这些工具可用(支持 group:\* 语法)
- `deny`: 这些工具被阻止(优先于 allow
如果未指定 `allow` 列表,默认所有工具都可用。
### 第 2 层: Provider 特定规则
不同的 LLM Provider 可能有不同的能力或限制:
```typescript
{
byProvider: {
google: { deny: ["exec"] }, // Gemini 不能执行命令
anthropic: { allow: ["*"] }, // Claude 有完全访问权限
}
}
```
### 第 3 层: Subagent 限制
`isSubagent: true` 时,会应用额外的限制,防止子 Agent 访问敏感工具(如会话管理)。
## 添加新工具
1. 在 `src/agent/tools/` 中创建新文件(例如 `my-tool.ts`
2. 使用 TypeBox 定义工具的 Schema
```typescript
import { Type } from '@sinclair/typebox';
import type { AgentTool } from '@mariozechner/pi-agent-core';
const MyToolSchema = Type.Object({
param1: Type.String({ description: '参数描述' }),
param2: Type.Optional(Type.Number()),
});
export function createMyTool(): AgentTool<typeof MyToolSchema> {
return {
name: 'my_tool',
label: 'My Tool',
description: '这个工具做什么',
parameters: MyToolSchema,
execute: async (toolCallId, args) => {
// 实现
return { result: 'success' };
},
};
}
```
3. 在 `src/agent/tools.ts` 中注册工具:
```typescript
import { createMyTool } from './tools/my-tool.js';
export function createAllTools(cwd: string): AgentTool<any>[] {
// ... 现有工具
const myTool = createMyTool();
return [
...baseTools,
myTool as AgentTool<any>,
// ...
];
}
```
4. 在 `groups.ts` 中将工具添加到适当的组:
```typescript
export const TOOL_GROUPS: Record<string, string[]> = {
'group:my_category': ['my_tool', 'other_tool'],
// ...
};
```
## 测试
运行策略系统测试:
```bash
pnpm test src/agent/tools/policy.test.ts
```
## Agent Profile 集成
工具配置可以在 Agent Profile 的 `config.json` 中定义,允许不同的 Agent 拥有不同的工具能力:
```
┌─────────────────────────────────────────────────────────────────┐
│ Super Multica Hub │
│ │
│ ┌───────────┐ ┌───────────┐ ┌───────────┐ │
│ │ Agent A │ │ Agent B │ │ Agent C │ │
│ │ Profile: │ │ Profile: │ │ Profile: │ │
│ │ coder │ │ reviewer │ │ devops │ │
│ │ │ │ │ │ │ │
│ │ tools: │ │ tools: │ │ tools: │ │
│ │ allow:fs │ │ deny:* │ │ allow:* │ │
│ └─────┬─────┘ └─────┬─────┘ └─────┬─────┘ │
│ │ │ │ │
└─────────┼────────────────┼────────────────┼─────────────────────┘
│ │ │
▼ ▼ ▼
┌──────────┐ ┌──────────┐ ┌──────────┐
│ Client │ │ Client │ │ Client │
└──────────┘ └──────────┘ └──────────┘
```
每个 Agent 的 Profile 可以在 `config.json` 中定义自己的工具配置:
```json
{
"tools": {
"allow": ["group:fs", "group:runtime"],
"deny": ["exec"]
},
"provider": "anthropic",
"model": "claude-sonnet-4-20250514"
}
```
详见 [Profile README](../profile/README.md)。

View file

@ -1,17 +0,0 @@
# @multica/store
Zustand state management for Multica apps.
## Usage
```tsx
// From barrel
import { useHubStore, useMessagesStore, useGatewayStore } from '@multica/store'
// Per-file subpath import
import { useGatewayStore } from '@multica/store/gateway'
import { useHubStore } from '@multica/store/hub'
import { useMessagesStore } from '@multica/store/messages'
import { useHubInit } from '@multica/store/hub-init'
import { useDeviceId } from '@multica/store/device-id'
```

View file

@ -1,32 +0,0 @@
# @multica/ui
Shared UI component library. Shadcn + Tailwind CSS v4.
## Usage
```tsx
// UI components — subpath imports, no barrel
import { Button } from '@multica/ui/components/ui/button'
import { Card, CardContent } from '@multica/ui/components/ui/card'
// Feature components
import { ThemeProvider } from '@multica/ui/components/theme-provider'
import { Chat } from '@multica/ui/components/chat'
import { Markdown } from '@multica/ui/components/markdown'
// Hooks
import { useIsMobile } from '@multica/ui/hooks/use-mobile'
import { useAutoScroll } from '@multica/ui/hooks/use-auto-scroll'
// Utilities
import { cn } from '@multica/ui/lib/utils'
// Styles (app entry point)
import '@multica/ui/globals.css'
```
## Adding Components
```bash
pnpm --filter @multica/ui dlx shadcn@latest add <component>
```