feat: 重组为标准 skill 包结构 + 修复 verify.sh bash 3.2 兼容
- 新增 .claude/skills/new-project/SKILL.md(标准 skill 格式) - install-skill.sh 安装时组装 SKILL.md + verify.sh,单一源无重复 - verify.sh 改用索引数组替代 declare -A,兼容 macOS bash 3.2 - log_pass/log_skip 显式 return 0,避免 set -e 下非 verbose 误退出 - README 改为 skill 优先;补全 copier.yml/scripts/template 入库
This commit is contained in:
@@ -0,0 +1,39 @@
|
||||
# Agent 配置模板 —— 复制为 .env 后按需修改
|
||||
# .env 含真实配置,不提交 git;本文件是提交到 git 的模板
|
||||
|
||||
# 运行环境:dev | prod
|
||||
ENV=dev
|
||||
|
||||
# 服务端口
|
||||
PORT={{ agent_port }}
|
||||
|
||||
# 工作目录
|
||||
WORK_DIR=~/{{ project_slug }}
|
||||
|
||||
# 日志级别:DEBUG | INFO | WARNING | ERROR
|
||||
LOG_LEVEL=DEBUG
|
||||
|
||||
{%- if llm_provider == 'gemini-cli' %}
|
||||
|
||||
# LLM CLI 命令
|
||||
LLM_CMD={{ llm_cmd }}
|
||||
|
||||
# 并发控制
|
||||
MAX_CONCURRENT_SESSIONS=4
|
||||
PROCESS_POOL_WORKERS=4
|
||||
PTY_READ_BUFFER_SIZE=4096
|
||||
{%- elif llm_provider == 'openai-api' %}
|
||||
|
||||
# OpenAI API
|
||||
OPENAI_API_KEY=sk-...
|
||||
OPENAI_MODEL=gpt-4o
|
||||
{%- elif llm_provider == 'anthropic-api' %}
|
||||
|
||||
# Anthropic API
|
||||
ANTHROPIC_API_KEY=sk-ant-...
|
||||
ANTHROPIC_MODEL=claude-sonnet-4-6
|
||||
{%- else %}
|
||||
|
||||
# 自定义 LLM 配置
|
||||
LLM_CMD={{ llm_cmd }}
|
||||
{%- endif %}
|
||||
@@ -0,0 +1,22 @@
|
||||
FROM python:{{ python_version }}-slim
|
||||
|
||||
WORKDIR /app
|
||||
|
||||
COPY --from=ghcr.io/astral-sh/uv:latest /uv /usr/local/bin/uv
|
||||
|
||||
RUN apt-get update && apt-get install -y --no-install-recommends \
|
||||
libglib2.0-0 libnss3 libatk1.0-0 libatk-bridge2.0-0 \
|
||||
libcups2 libdrm2 libxkbcommon0 libxcomposite1 libxdamage1 \
|
||||
libxfixes3 libxrandr2 libgbm1 libasound2 \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
|
||||
COPY pyproject.toml uv.lock ./
|
||||
RUN uv sync --frozen --no-dev
|
||||
{%- if llm_provider == 'gemini-cli' %}
|
||||
RUN uv run playwright install chromium
|
||||
{%- endif %}
|
||||
|
||||
COPY src ./src
|
||||
|
||||
EXPOSE {{ agent_port }}
|
||||
CMD ["uv", "run", "python", "-m", "src.main"]
|
||||
@@ -0,0 +1,53 @@
|
||||
[project]
|
||||
name = "{{ project_slug }}-agent"
|
||||
version = "0.1.0"
|
||||
description = "{{ description }} — agent service"
|
||||
requires-python = ">={{ python_version }}"
|
||||
dependencies = [
|
||||
"fastapi",
|
||||
"uvicorn[standard]",
|
||||
"loguru",
|
||||
"pydantic-settings",
|
||||
{%- if llm_provider == 'gemini-cli' %}
|
||||
"playwright",
|
||||
"python-pptx",
|
||||
{%- elif llm_provider == 'openai-api' %}
|
||||
"openai",
|
||||
{%- elif llm_provider == 'anthropic-api' %}
|
||||
"anthropic",
|
||||
{%- endif %}
|
||||
]
|
||||
|
||||
[dependency-groups]
|
||||
dev = [
|
||||
"pytest",
|
||||
"pytest-asyncio",
|
||||
"pytest-cov",
|
||||
"mypy",
|
||||
"ruff",
|
||||
"pip-audit",
|
||||
]
|
||||
|
||||
[tool.ruff.lint]
|
||||
select = ["E", "F", "N", "T20", "I", "C901", "ANN", "S", "PLR"]
|
||||
ignore = ["ANN101", "ANN102"]
|
||||
|
||||
[tool.ruff.lint.per-file-ignores]
|
||||
"tests/**" = ["ANN"]
|
||||
|
||||
[tool.mypy]
|
||||
strict = true
|
||||
ignore_missing_imports = true
|
||||
|
||||
[tool.pytest.ini_options]
|
||||
addopts = "--cov=src --cov-report=term-missing --cov-fail-under=80"
|
||||
asyncio_mode = "auto"
|
||||
|
||||
[tool.importlinter]
|
||||
root_packages = ["src"]
|
||||
|
||||
[[tool.importlinter.contracts]]
|
||||
name = "Domain zero IO"
|
||||
type = "forbidden"
|
||||
source_modules = ["src.domain"]
|
||||
forbidden_modules = ["fastapi", "httpx", "subprocess", "playwright"]
|
||||
@@ -0,0 +1,32 @@
|
||||
from fastapi import FastAPI
|
||||
from loguru import logger
|
||||
|
||||
from .core.config import get_settings
|
||||
from .core.logging import configure_logging
|
||||
|
||||
settings = get_settings()
|
||||
configure_logging(settings.log_level)
|
||||
|
||||
app = FastAPI(
|
||||
title="{{ project_name }} Agent",
|
||||
docs_url="/docs" if settings.enable_api_docs else None,
|
||||
redoc_url="/redoc" if settings.enable_api_docs else None,
|
||||
openapi_url="/openapi.json" if settings.enable_api_docs else None,
|
||||
)
|
||||
|
||||
|
||||
@app.get("/health")
|
||||
async def health() -> dict[str, str]:
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
@app.get("/ready")
|
||||
async def ready() -> dict[str, str]:
|
||||
return {"status": "ok"}
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
import uvicorn
|
||||
|
||||
logger.info("Starting agent | port={}", settings.port)
|
||||
uvicorn.run("src.main:app", host="0.0.0.0", port=settings.port, reload=True)
|
||||
Reference in New Issue
Block a user