Metadata-Version: 2.4
Name: pyminicode
Version: 0.3.0
Summary: Terminal-native AI coding assistant (Python) with ReAct loop, memory system, and extensible tool/skill/hook architecture
Author: minicode authors
License: MIT
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: pydantic>=2.6
Requires-Dist: httpx>=0.27
Requires-Dist: PyYAML>=6.0
Provides-Extra: dev
Requires-Dist: pytest>=8.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.23; extra == "dev"
Requires-Dist: ruff>=0.4; extra == "dev"

# minicode

Terminal-native AI coding assistant, implemented in Python.

> **当前状态**：已完成 **v0**（工具层）、**v1**（模型层）、**v2**（ReAct 循环 + 记忆系统 + 权限 + Hook + Goal + Chat Bridge + 斜杠命令补全 + 自定义命令 + 代码简化）。
> 下一版（v3）会接 Google provider、plugin system、远程 skill/agent 拉取。

## Quick start

```bash
# 1. install
pip install -e .

# 2. 跑起来（首次会自动建 .minicode/）
minicode

# 一次性命令
minicode --init              # 初始化项目.minicode
minicode --version
minicode --paths
minicode --print-tools       # 列工具
minicode --check-config      # 校验配置
```

## Layout

```
minicode/
├── pyproject.toml
├── README.md
├── .minicode/                       # per-project config dir
│   ├── skills/                      # skill files
│   │   ├── code-review/SKILL.md
│   │   ├── refactor/SKILL.md
│   │   └── test-gen/SKILL.md
│   ├── agents/                      # subagent definitions
│   │   ├── code-reviewer.md
│   │   └── explorer.md
│   ├── commands/                    # custom slash commands
│   │   ├── review.md
│   │   └── fix.md
│   ├── hooks/                       # python/shell hooks
│   ├── mcp.json                     # MCP server config (stdio / http)
│   └── config.yaml                  # LLM provider 配置
├── minicode/                        # source
│   ├── __init__.py
│   ├── __main__.py
│   ├── _ansi.py                     # ANSI 颜色常量 + TTY 探测（公共模块）
│   ├── paths.py                     # .minicode 路径解析
│   ├── config.py                    # config.yaml 加载
│   ├── command.py                   # custom command loader
│   ├── registry.py                  # 全局 registry (tools/models/skills/agents/hooks)
│   ├── tool/                        # 工具层
│   │   ├── base.py                  # Tool 抽象基类 + ToolContext + ToolResult
│   │   ├── registry.py              # ToolRegistry
│   │   ├── skill.py                 # SkillLoader
│   │   ├── mcp.py                   # McpClient + McpToolAdapter
│   │   └── builtin/                 # 8 个内置工具
│   │       ├── bash.py
│   │       ├── edit.py
│   │       ├── glob_tool.py
│   │       ├── grep_tool.py
│   │       ├── read.py
│   │       ├── write.py
│   │       ├── skill.py
│   │       └── subagent.py          # delegate_to_subagent
│   ├── model/                       # 模型层
│   │   ├── base.py                  # Model 抽象基类 + ModelResponse/Event
│   │   ├── message.py               # Message / Part / ToolSchema
│   │   ├── openai_compat.py         # OpenAI Chat Completions
│   │   ├── anthropic.py             # Anthropic Messages API
│   │   ├── demo.py                  # 假 provider（echo）
│   │   └── registry.py              # ModelRegistry
│   ├── agent/                       # ReAct 循环 + subagent
│   │   ├── loader.py                # SubagentLoader
│   │   └── runtime.py               # run_agent / run_subagent + 预算管理
│   ├── memory/                      # 记忆系统
│   │   ├── budget.py                # ContextBudget + token 估算
│   │   ├── truncation.py            # 分级裁剪（soft/hard trim + 滑动窗口）
│   │   ├── compact.py               # /compact 手动压缩
│   │   ├── context.py               # AGENTS.md + rules → system prompt
│   │   ├── context_view.py          # /context 可视化
│   │   ├── history.py               # 会话历史持久化
│   │   ├── loaders.py               # AGENTS.md / rules 加载
│   │   └── status.py                # 输入框前 ctx 状态栏
│   ├── permission/                  # 工具调用权限
│   │   ├── service.py               # PermissionService
│   │   └── types.py                 # PermissionRequest/Result
│   ├── hooks/                       # Hook 系统
│   │   ├── dispatcher.py            # HookDispatcher
│   │   ├── types.py                 # EventName / Action / HookResponse
│   │   ├── python.py                # PythonHook
│   │   └── shell.py                 # ShellHook
│   ├── goal/                        # 停止条件 + judge
│   │   ├── service.py               # GoalService
│   │   ├── types.py                 # Goal / Verdict
│   │   └── judge.py                 # judge 独立 LLM 调用
│   ├── display/                     # 结构化渲染
│   │   └── formatter.py             # thinking / tool-call / code-change
│   ├── chatbridge/                  # 多端聊天桥接
│   │   ├── adapter.py               # ChatAdapter
│   │   ├── adapters/                # stdio / webhook
│   │   └── manager.py               # ChatBridgeManager
│   └── cli/
│       ├── app.py                   # REPL + 命令处理
│       └── input.py                 # 斜杠命令补全
└── tests/                           # 单测 + 端到端冒烟
    ├── test_base.py
    ├── test_skill_loader.py
    ├── test_registry.py
    ├── test_config.py
    ├── test_model_registry.py
    ├── test_openai_compat.py
    ├── test_anthropic.py
    ├── test_demo.py
    ├── test_agent_runtime.py
    ├── test_memory_budget.py
    ├── test_memory_truncation.py
    ├── test_memory_compact.py
    ├── test_permission.py
    ├── test_hooks.py
    ├── test_goal.py
    ├── test_command.py
    ├── demo_mcp_server.py
    └── test_e2e_mcp.py
```

## config.yaml 格式

`.minicode/config.yaml`：

```yaml
# LLM provider：当前支持 openai / anthropic / demo
provider: openai

# API Key。推荐用环境变量：${OPENAI_API_KEY}
api_key: ${OPENAI_API_KEY}

# API base URL
base_url: https://api.openai.com/v1

# 模型名称
model: gpt-4o

# 上下文窗口大小（可选）。支持 128K / 1M / 128000 等写法
context_window: 128K

# 透传给 provider 的额外参数（可选）
extra:
  temperature: 0.7
  max_tokens: 8K
```

`provider` 可选值：
- `openai` — 任何实现 OpenAI Chat Completions 的服务（OpenAI/DeepSeek/Moonshot/ollama/vllm/...）
- `anthropic` — Anthropic Messages API
- `demo` — 进程内回显，无需网络

`extra` 字段透传给 provider 实现（OpenAI：`temperature`/`top_p`/`presence_penalty`；Anthropic：`max_tokens` 必填/`temperature`/`top_k`）。

## CLI 命令

| 命令 | 作用 |
| --- | --- |
| `/tools` | 列出所有工具（builtin + skill + mcp） |
| `/skills` | 列出 skill |
| `/agents` | 列出 subagent |
| `/hooks` | 列出已加载的 hook |
| `/mcp` | 列出 MCP 服务和状态 |
| `/model` | 显示当前 model 详情 |
| `/model test` | 流式发 "ping" 测试当前 model 的连通性 |
| `/memory` | 显示 AGENTS.md + rules |
| `/context` | 显示上下文窗口占用（进度条 + 分项明细） |
| `/history` | 列出历史会话 |
| `/compact` | 手动压缩历史对话（调 LLM 摘要） |
| `/goal` | 设置/查看停止条件 |
| `/goal judge` | 调 judge 评估是否完成 |
| `/chat` | chat bridge 管理 |
| `/permission` | 权限管理（always allow/deny/clear） |
| `/display` | 渲染 demo（thinking / tool-call / code-change） |
| `/paths` | 打印路径解析结果 |
| `/call <id> [json]` | 手动调用一个工具 |
| `/reload` | 重新 build registry |
| `/commands` | 列出自定义命令 |
| `/help` | 打印帮助 |
| `/exit` | 退出 |

## 工具协议

所有工具实现同一接口（[minicode/tool/base.py](minicode/tool/base.py)）：

```python
class Tool(ABC):
    kind: ToolKind = ToolKind.BUILTIN

    @property
    def id(self) -> str: ...

    @property
    def description(self) -> str: ...

    @property
    def parameters(self) -> type[BaseModel]: ...

    async def execute(self, args: Parameters, ctx: ToolContext) -> ToolResult: ...
```

`ToolContext` 携带：
- `session_id` — 当前会话
- `cwd` / `project_root` — 路径
- `tool_registry` — 工具注册表（工具间互相调用）
- `permission_service` — 权限检查
- `hook_dispatcher` — hook 触发
- `bus` — 事件总线
- `model_registry` — 模型切换
- `config` — 当前配置
- `history` — 对话历史
- `context_budget` — token 预算

`ToolResult`：
- `title` — 短标题
- `output` — 内容（str | list[Part]）
- `metadata` — dict（error / file_path / tool_name 等）

## 记忆系统

### Token 预算（memory/budget.py）

不引入 tiktoken，用 `chars / 3` 粗估。`ContextBudget` 跟踪 system + history 的 token，按压力等级分级：

| 等级 | 阈值 | 动作 |
|------|------|------|
| 0 | < 50% | 无 |
| 1 | 50-70% | 软裁剪旧 tool result（head+tail） |
| 2 | 70-85% | 硬裁剪旧 tool result（清空内容） |
| 3 | ≥ 85% | 自动 compact（调 LLM 压缩历史） |

### 分级裁剪（memory/truncation.py）

- **soft_trim_tool_results**：旧 tool result → head+tail（保留结构，压缩体积）
- **hard_trim_tool_results**：旧 tool result → 清空标记（保留 tool_call 配对）
- **truncate_messages**：丢弃整轮旧消息（最后的兜底）

保护最近 N 轮不动（避免裁到当前上下文）。

### /compact 手动压缩（memory/compact.py）

把旧消息喂给 LLM 生成摘要，替换成一条 assistant summary message。

### 上下文可视化（memory/context_view.py）

`/context` 命令展示：

```
┌──────────────────────────────────────────┐
│ context window  6500/8000  81.3%          │
│ [████████░░]                              │
│                                          │
│ breakdown                                │
│   system prompt   1200 (15.0%)           │
│   tools schema     800 (10.0%)           │
│   history         4500 (56.3%)           │
│     user text     1200                   │
│     assistant     1500                   │
│     tool calls     800                   │
│     tool results  1000                   │
│                                          │
│   output reserve  4000 (50.0%)           │
│   remaining       1500 (18.8%)           │
│                                          │
│   pressure: high (level 3)               │
└──────────────────────────────────────────┘
```

### 状态栏（memory/status.py）

输入框前显示：`minicode> [ctx 6500/8000 ████████░░] $_`

颜色：绿 (< 60%) / 黄 (60-85%) / 红 (> 85%)。

### 历史持久化（memory/history.py）

会话退出时保存到 `.minicode/history/{session_id}.json`，支持 `/history` 列出和恢复。

## 权限系统（permission/）

工具调用前询问用户：

```
[permission] tool 'bash' wants to run
            args: {"command": "ls -la"}
            [1] Yes
            [2] Yes, and always (allow this tool for the rest of the session)
            [3] No  [default: 1]
```

- `always_allow` / `always_deny`：per-session 状态
- `/permission` 管理：`allow <tool>` / `deny <tool>` / `clear`

## Hook 系统（hooks/）

支持 Python 和 Shell 两种 hook：

```python
# .minicode/hooks/my_hook.py
def run(event, context):
    if event.event == "tool_call_before":
        if event.data["tool"] == "bash":
            return HookResponse.deny("no bash allowed")
    return HookResponse.allow()
```

```bash
# .minicode/hooks/my_hook.sh
#!/bin/bash
EVENT=$(cat)
if echo "$EVENT" | jq -e '.event == "tool_call_before" and .data.tool == "bash"' > /dev/null; then
  echo '{"action":"deny","reason":"no bash"}'
  exit 0
fi
echo '{"action":"allow"}'
```

事件：`session_start`, `session_end`, `user_prompt_submit`, `assistant_message`, `tool_call_before`, `tool_call_after`, `error`, `stop`, `compact`

聚合规则：并行执行，任一 deny → 整体 deny，多个 modify → 串联合并。

## Goal + Judge（goal/）

设置停止条件，让 judge 独立 LLM 调用评估是否完成：

```
> /goal tests pass
Goal set: "tests pass"

> /goal judge
[judge] evaluating...
[goal not yet] test suite has 3 failures
```

Verdict：`ok`（满足）、`impossible`（不可达）、`error`（judge 失败，fail-open）。

## Subagent（agent/）

`.minicode/agents/<name>.md` 定义独立 LLM 上下文：

```markdown
---
name: code-reviewer
description: 严格审查代码改动并给出改进建议
---

你是 code reviewer...
```

`delegate_to_subagent` 工具让父 LLM 把任务委派给 subagent，防递归（subagent 看不到此工具）。

## Chat Bridge（chatbridge/）

多端聊天桥接，支持 stdio / webhook adapter：

```
> /chat
> /chat register webhook https://example.com/webhook
> /chat status
```

## 自定义命令（command.py）

`.minicode/commands/<name>.md`：

```markdown
---
description: 代码审查命令
---

请审查以下代码改动，给出改进建议。
用户输入：$ARGUMENTS
```

运行时 `$ARGUMENTS` 替换为用户输入。

## 斜杠命令补全（cli/input.py）

TTY 环境下输入 `/` 时显示命令列表，继续输入实时过滤，Tab 补全到最长公共前缀。非 TTY 降级为 `input()`。

## 架构图

```
┌──────────────────────────────────────────────────────────────────────┐
│                           CLI (cli/app.py)                           │
│  REPL / 命令处理 / 斜杠补全 / 状态栏 / 渲染                           │
└──────────────────────────┬───────────────────────────────────────────┘
                           │
          ┌────────────────┼────────────────┐
          │                │                │
          ▼                ▼                ▼
   ┌────────────┐  ┌────────────┐  ┌────────────────┐
   │  ToolRegistry│  │ModelRegistry│  │ CommandLoader  │
   │  (all tools) │  │ (providers) │  │ (custom cmds)  │
   └──────┬─────┘  └──────┬─────┘  └────────────────┘
          │                │
          │                │
          ▼                ▼
   ┌──────────────────────────────────────────┐
   │              ReAct 循环 (agent/runtime.py) │
   │  user → LLM → tool_call → execute → result │
   │       ↑                          │        │
   │       └──────────────────────────┘        │
   │                                          │
   │  预算管理：soft/hard trim → compact       │
   │  doom loop 检测                           │
   │  tool 输出截断（错误感知 head+tail）       │
   └──────────────────────────────────────────┘
          │
          ├──────────────┬──────────────┬──────────────┐
          ▼              ▼              ▼              ▼
   ┌──────────┐  ┌──────────┐  ┌──────────┐  ┌──────────┐
   │  builtin │  │  skill   │  │   mcp    │  │subagent  │
   │  tools   │  │  tools   │  │  tools   │  │ (nested) │
   └──────────┘  └──────────┘  └──────────┘  └──────────┘

   ┌──────────────────────────────────────────────────┐
   │              记忆系统 (memory/)                    │
   │  budget → truncation → compact → context_view     │
   │  history (持久化) / status (输入框前)             │
   └──────────────────────────────────────────────────┘

   ┌──────────────┐  ┌──────────────┐  ┌──────────────┐
   │  permission  │  │    hooks     │  │    goal      │
   │  (per-session)│  │ (python/shell)│  │  + judge    │
   └──────────────┘  └──────────────┘  └──────────────┘

   ┌──────────────────────────────────────────────────┐
   │              chatbridge (多端桥接)                  │
   │  stdio / webhook adapter → bus → model → history  │
   └──────────────────────────────────────────────────┘
```
