Skill: relunctance/hawk-context (version 1.1.0)

--- name: context-compressor description: | Context Compressor — Real-time conversation context compression engine. Activates when user says "compress context", "压缩上下文", "上下文爆了", "context full", "上下文瘦身", "reduce context", or when context exceeds 70% threshold. This skill compresses the CURRENT conversation history into a clean, short prompt. NOT a memory manager — pure context compression tool. Core capabilities: - Compress current chat history (180k → 30k instantly) - Auto-trigger at 70% context threshold - Structured JSON output with compression metrics - Preserve system prompt, keep recent N messages intact - Summarize older messages with configurable intensity - No database, no dependencies, pure Python --- # Context Compressor 🦅 > **纯上下文压缩引擎** — 不是记忆库，是救命工具。 > 当上下文爆炸前，压缩当前对话，继续轻松对话。 --- ## 核心定位 | 工具 | 做什么 | 何时用 | |------|--------|--------| | **Context Compressor** | 压缩当前对话上下文 | 现在、立刻、当上下文快满 | | memory-lancedb-pro | 跨会话持久记忆 | 对话之间存取知识 | | context-hawk | 长期记忆管理 | 日常沉淀 | **Context Compressor = 急救工具。memory-lancedb-pro/context-hawk = 日常工具。** --- ## 触发方式 **自动触发**（推荐）： - 上下文超过 70% 时自动提示压缩 - 每 10 轮对话自动检查水位 **手动触发**： - "压缩上下文" / "compress context" - "上下文爆了" / "context full" - "瘦身" / "reduce context" - `/compress` --- ## Quick Start ```bash # Install skill openclaw skills install ./context-compressor.skill # Auto-link command to ~/bin (one-time) bash ~/.openclaw/workspace/skills/context-compressor/scripts/install.sh source ~/.bashrc # Compress current conversation hawk-compress --level normal --keep 5 # Dry run (preview) hawk-compress --dry-run --level light --keep 3 # Python API python3 -c "from hawk_compress import ContextCompressor; \ c = ContextCompressor(keep_recent=5); \ r = c.compress(your_chat_history); \ print(r['stats']['ratio'])" ``` ## 压缩原理 ### 输入（压缩前） ``` [完整对话历史 — 180k tokens — 爆炸边缘] System: 你是一个助手... User: 第一个问题... Assistant: 第一个回答... User: 第二个问题... Assistant: 第二个回答... ... (越来越长) ``` ### 输出（压缩后） ```json { "compressed_prompt": "...[结构化压缩后的对话]...", "original_tokens": 180000, "compressed_tokens": 32000, "ratio": "5.6x", "kept_messages": 5, "summarized_count": 87, "compression_level": "normal", "timestamp": "2026-03-29T00:39:00+08:00" } ``` --- ## 压缩层级 | 层级 | 触发 | 效果 | 适用 | |------|------|------|------| | `light` | 60-70% | 摘要 > 30天的消息 | 日常维护 | | `normal` | 70-85% | 摘要 + 保留最近10轮 | 推荐默认 | | `heavy` | 85-95% | 只保留最近5轮 + 核心摘要 | 紧急急救 | | `emergency` | > 95% | 只保留系统提示 + 最近3轮 | 立即执行 | --- ## 系统指令永久保留 以下内容**永远不参与压缩**（完整保留）： - System Prompt / SOUL.md / AGENTS.md - 用户设定的角色定义 - 核心规则和约束 - 当前的任务描述 --- ## 重要度过滤 压缩时自动判断每条消息的重要度： | 重要度 | 消息类型 | 处理方式 | |--------|---------|---------| | 🔴 极高 | 决策/规则/任务 | 保留原文 | | 🟡 高 | 技术方案/代码片段 | 保留摘要 | | 🟢 中 | 一般讨论 | 摘要或合并 | | ⚪ 低 | 闲聊/确认/废话 | **直接丢弃** | --- ## 压缩策略 ### 1. 消息摘要 把每条老消息压缩为一句话： ``` User: 讨论了Laravel的四层架构，讨论了Controller层的作用... → [摘要] User就Laravel四层架构提出问题 ``` ### 2. 合并重复 重复的说明/确认/指令合并为一条： ``` User: 好的 User: 好的 User: 明白了 → [合并] User确认理解 ``` ### 3. 代码折叠 长代码片段只保留文件路径和关键行号： ``` [代码: app/Logic/OrderLogic.php — 45行] → [代码折叠] ``` ### 4. 时间戳裁剪 同一时间段内的密集对话压缩为一条： ``` [上午10:00-10:30 共12轮对话] → [摘要] ``` --- ## 压缩后的上下文格式 ``` ## 对话摘要 [最近5轮完整对话] User: 最新问题... Assistant: 最新回答... [历史摘要] - 2026-03-28: 讨论了Skill架构，决定不拆分 - 2026-03-28: 补充了DAO查询模式 - 2026-03-28: 完成qujin-laravel-team Skill v2 ## 任务状态 - 当前任务：压缩上下文 - 进度：进行中 ## 核心规则（永久保留） [系统提示内容] ## 用户偏好（永久保留） [关键偏好] ``` --- ## 参考文档 | 文档 | 用途 | |------|------| | [references/compression-logic.md](references/compression-logic.md) | 压缩算法详解 | | [references/auto-trigger.md](references/auto-trigger.md) | 自动触发机制 | | [references/structured-output.md](references/structured-output.md) | JSON输出格式 | | [references/cli.md](references/cli.md) | CLI工具 |

path: README.md
content: # 🦅 Context Compressor

> **Pure Context Compression Engine** — not a memory manager, a lifesaver.

When your AI conversation context is about to explode, Context Compressor compresses it instantly. 180k → 35k. Continue chatting like nothing happened.

[![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](LICENSE)
[![OpenClaw Compatible](https://img.shields.io/badge/OpenClaw-2026.3%2B-brightgreen)](https://github.com/openclaw/openclaw)

---

## What is this vs. context-hawk?

| Tool | What it does | When to use |
|------|--------------|-------------|
| **Context Compressor** | Compresses **current** conversation | Right now, when context is full |
| **context-hawk** | Manages **persistent** memory across sessions | Daily, between conversations |

**Context Compressor = Emergency rescue. context-hawk = Daily maintenance.**

---

## How it works

### Before compression (180k tokens — exploding)

```
[Full conversation history — 180k tokens — at 88%]
System: You are a helpful assistant...
User: First question...
Assistant: First answer...
User: Second question...
... (越来越长，越来越贵，越来越慢)
```

### After compression (35k tokens — clean)

```json
{
  "compressed_prompt": [
    {"role": "system", "content": "[永久保留的系统提示]", "status": "preserved"},
    {"role": "user", "content": "[最新问题完整原文]", "status": "preserved"},
    {"role": "assistant", "content": "[最新回答完整原文]", "status": "preserved"},
    {"role": "summary", "content": "[早期45条消息摘要]", "status": "summarized"}
  ],
  "stats": {
    "original_tokens": 180000,
    "compressed_tokens": 35000,
    "ratio": "5.1x",
    "kept_messages": 5,
    "summarized_count": 87,
    "level": "normal"
  }
}
```

---

## ✨ Core Features

| Feature | Description |
|---------|-------------|
| **Auto-trigger** | Compresses automatically at 70% context threshold |
| **4 compression levels** | light / normal / heavy / emergency |
| **Structured JSON output** | Full stats: tokens, ratio, counts |
| **System prompt preserved** | Role definitions never compressed |
| **Importance filtering** | Discards noise, keeps decisions/rules/code |
| **Message deduplication** | Merges repeated confirmations |
| **Code collapsing** | Long code blocks folded to meta |
| **Pure Python** | No database, no dependencies |
| **Writes to memory** | Compression history saved to `memory/today.md` |

---

## 🚀 Quick Start

```bash
# Install skill
openclaw skills install ./context-compressor.skill

# Auto-link hawk-compress command to ~/bin (one-time)
bash ~/.openclaw/workspace/skills/context-compressor/scripts/install.sh
source ~/.bashrc

# Compress current conversation (auto-detect level)
hawk-compress

# Compress with specific level
hawk-compress --level heavy

# Preview without writing
hawk-compress --dry-run
```

---

## Compression Levels

| Level | When | Effect |
|--------|-------|--------|
| `light` | 60-70% | Summarize messages > 30 days old |
| `normal` | 70-85% | Summarize + keep recent 10 ← **default** |
| `heavy` | 85-95% | Keep recent 5 only |
| `emergency` | > 95% | Keep recent 3 only |

---

## Auto-Trigger

When context reaches 70%, every answer includes:

```
[🦅 Context: 72%] Compress recommended: /hawk-compress
  148k → ~35k | Save 113k tokens
```

At 85%+, forces confirmation before continuing.

---

## File Structure

```
context-compressor/
├── SKILL.md
├── README.md
├── LICENSE
├── scripts/
│   └── hawk-compress       # Python CLI tool
└── references/
    ├── compression-logic.md    # Compression algorithm
    ├── auto-trigger.md        # Auto-trigger system
    ├── structured-output.md    # JSON output format
    └── cli.md                # CLI reference
```

---

## Requirements

- Python 3.8+
- No external dependencies
- No database required

---

## License

MIT — free to use, modify, and distribute.


path: references/auto-trigger.md
content: # 自动触发机制 · Auto-Trigger

---

## 触发条件

| 条件 | 阈值 | 动作 |
|------|------|------|
| 上下文超过 70% | 143.5k/205k | 提示压缩（不强制） |
| 上下文超过 80% | 164k/205k | 强烈建议压缩 |
| 上下文超过 90% | 184.5k/205k | 强制压缩提示 |
| 上下文超过 95% | 194.75k/205k | 阻止继续写入 |

---

## 自动触发流程

```
每次回答前检查：
    │
    ├─ context < 60% → 不提示，继续
    │
    ├─ 60% ≤ context < 70% → 静默，继续
    │
    ├─ 70% ≤ context < 80% → 提示：
    │   "[🦅 Context: 72%] 建议压缩，输入 /compress"
    │
    ├─ 80% ≤ context < 90% → 强烈建议：
    │   "[🦅⚠️ Context: 84%] 上下文较满，请压缩 /compress"
    │
    └─ context ≥ 90% → 强制提示，阻止非必要写入：
        "[🦅🚨 Context: 93%] 必须压缩后才能继续"
```

---

## 每 N 轮自动检查

每完成 10 轮对话，自动检查上下文水位：

```
Round 10: 检查 → 68% → 正常
Round 20: 检查 → 73% → 提示
Round 30: 检查 → 81% → 强烈建议
Round 40: 检查 → 89% → 必须压缩
```

---

## 用户触发词（自然语言）

以下任何一句自动触发压缩：

```
压缩上下文
上下文太长了
压缩对话
reduce context
context too big
上下文爆了
瘦身
compress
context满
token太多了
上下文太多了
```

---

## 压缩确认流程

触发后，必须确认才执行：

```
[🦅 Context Compressor]

检测到上下文 72% (148k tokens)
建议压缩策略：normal（摘要+保留最近10轮）

效果预估：
  原始：148,000 tokens
  压缩后：约 35,000 tokens
  压缩比：约 4.2x
  保留消息：最近 10 轮完整 + 历史摘要

选项：
  [1] 执行 normal 压缩
  [2] 轻度压缩（保留更多）
  [3] 重度压缩（最小化）
  [4] 取消
```

---

## 压缩后自动写入

压缩完成后：

1. 将压缩结果写入 `memory/today.md`（历史记录）
2. 将压缩后的干净上下文替换当前对话
3. 向用户报告压缩结果：

```
✅ 压缩完成

  原始：148,000 tokens (72%)
  压缩后：35,000 tokens (17%)
  压缩比：4.2x
  节省：113,000 tokens

  可继续对话约 48 小时
```

---

## 配置项

```json
{
  "auto_trigger_enabled": true,
  "auto_trigger_threshold": 70,
  "check_interval_rounds": 10,
  "default_strategy": "normal",
  "keep_recent_normal": 10,
  "keep_recent_heavy": 5,
  "keep_recent_emergency": 3,
  "preserve_system_prompt": true
}
```


path: references/cli.md
content: # CLI 工具 · context-compressor

---

## 压缩核心脚本

```python
#!/usr/bin/env python3
"""
Context Compressor CLI
Usage: hawk-compress [options]
"""

import argparse
import json
import sys
from datetime import datetime

def compress_context(history, level='normal', keep_recent=10):
    """压缩对话上下文"""
    # 分离系统提示
    system = None
    dialog = []
    for msg in history:
        if msg.get('role') == 'system':
            system = msg['content']
        else:
            dialog.append(msg)

    # 按压缩层级处理
    if level == 'light':
        recent = dialog[-keep_recent:]
        older = [summarize(m) for m in dialog[:-keep_recent]]
    elif level == 'normal':
        recent = dialog[-keep_recent:]
        older = [summarize(m) for m in dialog[:-keep_recent]]
    elif level == 'heavy':
        recent = dialog[-5:]
        older = [summarize_all(dialog[:-5])]
    else:  # emergency
        recent = dialog[-3:]
        older = []

    # 重组
    result = []
    if system:
        result.append({'role': 'system', 'content': system})
    result.extend(older)
    result.extend(recent)

    return result

def main():
    parser = argparse.ArgumentParser(description='Context Compressor')
    parser.add_argument('--level', '-l', choices=['light', 'normal', 'heavy', 'emergency'],
                        default='normal', help='Compression level')
    parser.add_argument('--keep', '-k', type=int, default=10,
                        help='Number of recent messages to keep intact')
    parser.add_argument('--input', '-i', help='Input JSON file (chat history)')
    parser.add_argument('--output', '-o', help='Output JSON file')
    parser.add_argument('--dry-run', '-n', action='store_true',
                        help='Preview without writing')
    args = parser.parse_args()

    # 读取输入
    if args.input:
        with open(args.input) as f:
            history = json.load(f)
    else:
        # 从 stdin 读取
        history = json.load(sys.stdin)

    # 压缩
    compressed = compress_context(history, args.level, args.keep)

    # 计算统计
    original_tokens = sum(estimate_tokens(m['content']) for m in history)
    compressed_tokens = sum(estimate_tokens(m['content']) for m in compressed)

    output = {
        'status': 'success',
        'compressed_prompt': compressed,
        'stats': {
            'original_tokens': original_tokens,
            'compressed_tokens': compressed_tokens,
            'ratio': f"{original_tokens / max(compressed_tokens, 1):.1f}x",
            'kept_messages': len([m for m in compressed if m.get('status') == 'preserved']),
            'summarized_count': len([m for m in compressed if m.get('status') == 'summarized']),
            'dropped_count': len(history) - len(compressed),
            'level': args.level
        },
        'timestamp': datetime.now().isoformat()
    }

    if args.dry_run:
        print(json.dumps(output, indent=2, ensure_ascii=False))
    else:
        if args.output:
            with open(args.output, 'w') as f:
                json.dump(output, f, indent=2, ensure_ascii=False)
            print(f"✅ 压缩完成: {original_tokens} → {compressed_tokens} tokens ({output['stats']['ratio']})")
        else:
            print(json.dumps(output, indent=2, ensure_ascii=False))

if __name__ == '__main__':
    main()
```

---

## 使用示例

```bash
# 基本压缩（normal级别）
hawk-compress --level normal --keep 10

# 预览（不写入）
hawk-compress --level heavy --dry-run

# 指定输入输出文件
hawk-compress --input history.json --output compressed.json

# 从 stdin 传入
cat history.json | hawk-compress --level normal
```

---

## 命令速查

| 命令 | 说明 |
|------|------|
| `hawk-compress` | 标准压缩（normal） |
| `hawk-compress -l light` | 轻度压缩 |
| `hawk-compress -l heavy` | 重度压缩 |
| `hawk-compress -l emergency` | 紧急压缩 |
| `hawk-compress -n` | 预览压缩效果 |
| `hawk-compress -k 15` | 保留最近15轮 |


path: references/compression-logic.md
content: # 压缩算法详解 · Compression Logic

---

## 压缩流程

```
1. 解析对话历史
       ↓
2. 分离系统提示（永久保留）
       ↓
3. 分类消息（按重要度）
       ↓
4. 应用压缩策略
       ↓
5. 重组上下文
       ↓
6. 输出结构化JSON
```

---

## 第一步：解析对话历史

从当前上下文提取所有消息：

```python
def parse_history(messages):
    system_prompt = None
    dialog = []
    
    for msg in messages:
        if msg['role'] == 'system':
            system_prompt = msg['content']
        elif msg['role'] in ('user', 'assistant'):
            dialog.append({
                'role': msg['role'],
                'content': msg['content'],
                'tokens': estimate_tokens(msg['content'])
            })
    
    return system_prompt, dialog
```

---

## 第二步：重要度评分

```python
def score_importance(msg):
    content = msg['content'].lower()
    
    # 🔴 极高 — 保留原文
    if any(k in content for k in [
        '决定:', '规则:', '架构:', '任务:',
        '规范:', '禁止:', '必须:', '重要:'
    ]):
        return 1.0
    
    # 🟡 高 — 保留摘要
    if any(k in content for k in [
        '代码:', '方案:', '设计:', '实现:',
        '修改:', '新增:', '完成:'
    ]):
        return 0.7
    
    # 🟢 中 — 摘要或合并
    if any(k in content for k in [
        '讨论:', '对话:', '说了', '觉得'
    ]):
        return 0.4
    
    # ⚪ 低 — 直接丢弃
    if any(k in content for k in [
        '好的', '收到', 'ok', '嗯', '好',
        '请问', '谢谢', '抱歉', '对不起'
    ]):
        return 0.1
    
    return 0.5  # 默认中
```

---

## 第三步：应用压缩策略

### Light（轻度）

```python
def compress_light(dialog, keep_recent=10):
    # 保留最近10轮完整
    recent = dialog[-keep_recent:]
    # 其余全部摘要
    summarized = [summarize(msg) for msg in dialog[:-keep_recent]]
    return recent + summarized
```

### Normal（标准，默认）

```python
def compress_normal(dialog, keep_recent=10):
    recent = dialog[-keep_recent:]
    older = dialog[:-keep_recent]
    
    # 分组：按时间段/主题聚合
    groups = group_by_topic(older)
    
    summarized = []
    for group in groups:
        if len(group) > 3:
            summarized.append(summarize_group(group))
        else:
            summarized.extend([summarize(m) for m in group])
    
    return recent + summarized
```

### Heavy（重度）

```python
def compress_heavy(dialog, keep_recent=5):
    recent = dialog[-keep_recent:]
    older = summarize_all(dialog[:-keep_recent])
    return recent + [older]
```

### Emergency（紧急）

```python
def compress_emergency(dialog, keep_recent=3):
    return dialog[-keep_recent:]
```

---

## 第四步：摘要生成

```python
def summarize(msg):
    content = msg['content']
    tokens = msg['tokens']
    
    # 原文 < 200 tokens → 直接保留
    if tokens < 200:
        return msg
    
    # 代码片段 → 折叠
    if '```' in content:
        return {
            'role': msg['role'],
            'content': f"[代码折叠: {count_lines(content)}行]",
            'tokens': 15
        }
    
    # 长文本 → 提取核心句子
    sentences = content.split('。')
    core = sentences[:2] if len(sentences) > 2 else sentences
    
    return {
        'role': msg['role'],
        'content': '。'.join(core) + '。',
        'tokens': estimate_tokens('。'.join(core))
    }
```

---

## 第五步：输出结构化 JSON

```json
{
  "compressed_prompt": "...",
  "original_tokens": 180000,
  "compressed_tokens": 32000,
  "ratio": "5.6x",
  "kept_messages": 5,
  "summarized_count": 87,
  "dropped_count": 12,
  "compression_level": "normal",
  "system_prompt_preserved": true,
  "timestamp": "2026-03-29T00:39:00+08:00",
  "messages": [
    {"role": "system", "content": "...", "tokens": 500},
    {"role": "user", "content": "最新问题...", "tokens": 80},
    {"role": "assistant", "content": "最新回答...", "tokens": 200}
  ]
}
```

---

## Token 估算

```python
def estimate_tokens(text):
    # 粗略估算：中文 ~1.5字/token，英文 ~4字/token
    chinese = len([c for c in text if '\u4e00' <= c <= '\u9fff'])
    english = len([c for c in text if c.isascii()])
    return int(chinese / 1.5 + english / 4)
```

---

## 合并重复消息

```python
def merge_duplicates(dialog):
    merged = []
    for msg in dialog:
        if merged and msg['role'] == merged[-1]['role']:
            # 连续同角色消息
            if is_confirmation(msg['content']):
                continue  # 跳过确认
            if similarity(msg['content'], merged[-1]['content']) > 0.8:
                # 高度相似 → 合并
                merged[-1]['content'] += f"\n[另]: {msg['content']}"
                continue
        merged.append(msg)
    return merged
```


path: references/structured-output.md
content: # 结构化输出 · Structured Output

---

## JSON 输出格式

每次压缩后输出结构化 JSON：

```json
{
  "status": "success",
  "compressed_prompt": "...",
  "stats": {
    "original_tokens": 148000,
    "compressed_tokens": 35000,
    "ratio": "4.2x",
    "kept_messages": 10,
    "summarized_count": 87,
    "dropped_count": 12,
    "system_prompt_tokens": 500
  },
  "compression": {
    "level": "normal",
    "strategy": "summarize + merge + collapse",
    "keep_recent": 10
  },
  "timestamp": "2026-03-29T00:39:00+08:00",
  "messages": [
    {
      "role": "system",
      "content": "[永久保留的系统提示]",
      "status": "preserved"
    },
    {
      "role": "user",
      "content": "[最新问题完整原文]",
      "status": "preserved"
    },
    {
      "role": "assistant",
      "content": "[最新回答完整原文]",
      "status": "preserved"
    },
    {
      "role": "user",
      "content": "[历史消息摘要]",
      "original_count": 45,
      "status": "summarized"
    },
    {
      "role": "user",
      "content": "[代码折叠]",
      "original_lines": 120,
      "status": "collapsed"
    }
  ],
  "warnings": [],
  "next_check": "约48小时后或每10轮检查"
}
```

---

## 错误输出

```json
{
  "status": "error",
  "error": "context_too_small",
  "message": "上下文未达到压缩阈值（当前 45%，建议 >60%）",
  "current_tokens": 92250,
  "threshold": 123000,
  "suggestion": "当前上下文充足，无需压缩"
}
```

---

## 状态报告（每次回答后）

当 `hawk_alert` 开启时，每次回答附带：

```
[🦅 Context: 72%] 压缩建议: /compress
  原始: 148k | 压缩后: ~35k | 节省: 113k
```

---

## 压缩历史记录

每次压缩写入 `memory/today.md`：

```markdown
## 2026-03-29 压缩记录

### 00:39 压缩（normal）
- 原始: 148,000 tokens (72%)
- 压缩后: 35,000 tokens (17%)
- 压缩比: 4.2x
- 保留: 最近10轮 + 历史摘要
- 节省: 113,000 tokens
```

---

## 与 Context Hawk Memory 的关系

```
Context Compressor（压缩当前对话）
    ↓ 写入
memory/today.md（历史压缩记录）
    ↓
Context Hawk Memory（持久记忆管理）
    ↓
memory-lancedb-pro（向量检索）
```

两者协同工作：
- **Context Compressor** = 现在时（压缩当前）
- **Context Hawk Memory** = 过去时（管理历史）


path: scripts/install.sh
content: #!/usr/bin/env bash
# install.sh — Auto-link hawk-compress command to ~/bin
# Run after: openclaw skills install ./context-compressor.skill

set -e

echo "[install] Context-Compressor — creating symlinks..."

mkdir -p "$HOME/bin"
if ! echo "$PATH" | grep -q "$HOME/bin"; then
    echo "[install] Adding ~/bin to PATH in ~/.bashrc..."
    echo 'export PATH="$HOME/bin:$PATH"' >> "$HOME/.bashrc"
    export PATH="$HOME/bin:$PATH"
fi

SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
ln -sf "$SCRIPT_DIR/hawk-compress" "$HOME/bin/hawk-compress"

echo "[install] Symlink created: ~/bin/hawk-compress"
echo "[install] Run: source ~/.bashrc && hawk-compress --help"


Security scan verdicts — ClawScan: suspicious | static analysis: clean | VirusTotal: clean | SkillSpector: suspicious (CRITICAL)