Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions .github/release.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Configuration for auto-generated release notes.
# Docs: https://docs.github.com/en/repositories/releasing-projects-on-github/automatically-generated-release-notes
#
# Categorization runs on merged PR labels. Commits landing outside PRs (rare
# now that main is protected) fall into "Other Changes".

changelog:
exclude:
labels:
- ignore-for-release
authors:
- dependabot
- github-actions
categories:
- title: "New Features"
labels:
- "type: feat"
- feature
- title: "Bug Fixes"
labels:
- "type: fix"
- bug
- title: "Tests & E2E"
labels:
- "type: test"
- test
- title: "CI / Build"
labels:
- "type: ci"
- ci
- build
- title: "Docs"
labels:
- "type: docs"
- documentation
- title: "Chore / Refactor"
labels:
- "type: chore"
- chore
- refactor
- title: "Other Changes"
labels:
- "*"
30 changes: 30 additions & 0 deletions .github/workflows/autofix.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
name: autofix.ci

on:
pull_request:

permissions:
contents: read

jobs:
autofix:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- name: Install ruff
run: pip install "ruff>=0.4.0"

- name: Ruff auto-fix (lint)
run: ruff check --fix tools/ skills/ tests/ hermes/
continue-on-error: true

- name: Ruff format
run: ruff format tools/ skills/ tests/ hermes/

- uses: autofix-ci/action@c5b2d67aa2274e7b5a18224e8171550871fc7e4a
76 changes: 76 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,82 @@ jobs:
- name: Test OpenClaw plugin registration
run: npx tsx tests/test_openclaw_integration.ts

- name: E2E — host↔python round-trip (pytest)
run: pytest tests/e2e/test_host_to_python.py -v

- name: E2E — Pi host real subprocess
run: npx tsx tests/e2e/test_pi_e2e.ts

- name: E2E — OpenClaw host real subprocess
run: npx tsx tests/e2e/test_openclaw_e2e.ts

e2e-network:
# Real akshare/yfinance calls. Marked opt-in; run in a dedicated job so
# provider outages fail loud without blocking the main test job.
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- name: Install dependencies
run: |
pip install -r tools/requirements.txt
pip install -r requirements-dev.txt

- name: E2E — real provider network
run: pytest tests/e2e/test_provider_network.py -m integration_network -v

e2e-llm:
# Real DeepSeek call. Only runs when DEEPSEEK_API_KEY secret is present
# (skipped on forks to prevent secret exposure).
runs-on: ubuntu-latest
if: ${{ github.event_name == 'push' || github.event.pull_request.head.repo.full_name == github.repository }}
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.11"
cache: pip

- uses: actions/setup-node@v4
with:
node-version: "20"

- name: Install Python dependencies
run: |
pip install -r tools/requirements.txt
pip install -r requirements-dev.txt

- name: Install Node dependencies
run: npm install

- name: E2E — Hermes LLM flow
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
DEEPSEEK_MODEL: ${{ vars.DEEPSEEK_MODEL || 'deepseek-v4-flash' }}
run: |
if [ -z "$DEEPSEEK_API_KEY" ]; then
echo "DEEPSEEK_API_KEY not configured — skipping LLM e2e"
exit 0
fi
pytest tests/e2e/test_hermes_llm.py -m integration_llm -v

- name: E2E — Pi LLM flow
env:
DEEPSEEK_API_KEY: ${{ secrets.DEEPSEEK_API_KEY }}
DEEPSEEK_MODEL: ${{ vars.DEEPSEEK_MODEL || 'deepseek-v4-flash' }}
run: |
if [ -z "$DEEPSEEK_API_KEY" ]; then
echo "DEEPSEEK_API_KEY not configured — skipping LLM e2e"
exit 0
fi
npx tsx tests/e2e/test_pi_llm.ts

build:
runs-on: ubuntu-latest
needs: [lint, test, integration]
Expand Down
42 changes: 26 additions & 16 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -169,24 +169,34 @@ jobs:
with:
fetch-depth: 0

- name: Generate release notes
- name: Compose install footer
run: |
PREV_TAG=$(git tag --sort=-v:refname | sed -n '2p')
if [ -z "$PREV_TAG" ]; then
LOG=$(git log --pretty=format:"- %s" HEAD)
else
LOG=$(git log --pretty=format:"- %s" ${PREV_TAG}..HEAD)
fi
echo "## What's Changed" > release_notes.md
echo "" >> release_notes.md
echo "$LOG" >> release_notes.md
echo "" >> release_notes.md
echo "---" >> release_notes.md
echo "**npm**: \`npm install @weaxs/stock-analysis-plugin@${GITHUB_REF_NAME#v}\`" >> release_notes.md
echo "**pip**: \`pip install stock-analysis-plugin==${GITHUB_REF_NAME#v}\`" >> release_notes.md
echo "**OpenClaw**: \`openclaw plugins install clawhub:@weaxs/openclaw-stock-analysis\`" >> release_notes.md
VERSION="${GITHUB_REF_NAME#v}"
cat > install_footer.md <<EOF

---
### Install

- **npm**: \`npm install @weaxs/stock-analysis-plugin@${VERSION}\`
- **pip**: \`pip install stock-analysis-plugin==${VERSION}\`
- **OpenClaw**: \`openclaw plugins install clawhub:@weaxs/openclaw-stock-analysis\`
EOF

- name: Create GitHub Release
run: gh release create "${{ github.ref_name }}" --title "${{ github.ref_name }}" --notes-file release_notes.md
# --generate-notes uses .github/release.yml for PR categorization and
# automatically appends author @mentions, PR numbers, and a Full
# Changelog compare link. We append our install footer via --notes-file
# after the fact, since --generate-notes and --notes-file are exclusive.
run: |
gh release create "${GITHUB_REF_NAME}" \
--title "${GITHUB_REF_NAME}" \
--generate-notes
# Fetch the generated body, append install footer, update the release
GENERATED=$(gh release view "${GITHUB_REF_NAME}" --json body --jq .body)
{
echo "$GENERATED"
cat install_footer.md
} > final_notes.md
gh release edit "${GITHUB_REF_NAME}" --notes-file final_notes.md
env:
GH_TOKEN: ${{ github.token }}
8 changes: 8 additions & 0 deletions hermes/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,14 @@
"get_market_review": tools.get_market_review,
"run_watchlist_analysis": tools.run_watchlist_analysis,
"detect_anomaly": tools.detect_anomaly,
"diagnose_data_sources": tools.diagnose_data_sources,
"get_market_capabilities": tools.get_market_capabilities,
"render_stock_report": tools.render_stock_report,
"render_market_report": tools.render_market_report,
"build_watchlist_context": tools.build_watchlist_context,
"analyze_position_context": tools.analyze_position_context,
"check_alert_rules": tools.check_alert_rules,
"parse_stock_list": tools.parse_stock_list,
}


Expand Down
8 changes: 8 additions & 0 deletions hermes/plugin.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,14 @@ provides_tools:
- get_market_review
- run_watchlist_analysis
- detect_anomaly
- diagnose_data_sources
- get_market_capabilities
- render_stock_report
- render_market_report
- build_watchlist_context
- analyze_position_context
- check_alert_rules
- parse_stock_list
requires_env:
- name: TAVILY_API_KEY
description: "Tavily search API key (optional, one search engine is enough)"
Expand Down
110 changes: 110 additions & 0 deletions hermes/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -570,4 +570,114 @@
"required": ["symbol"],
},
},
{
"name": "diagnose_data_sources",
"description": "数据源诊断 — 检查当前环境可用的数据 provider(akshare/tushare/yfinance/finnhub/longbridge/alphavantage),输出每个市场的可用链路、缺失 env、warnings。用于让 agent 自解释为何拿不到数据",
"parameters": {
"type": "object",
"properties": {
"market": {
"type": "string",
"enum": ["A", "HK", "US", "all"],
"description": "市场,默认 all",
},
},
},
},
{
"name": "get_market_capabilities",
"description": "市场能力边界 — 返回指定市场支持/不支持的工具列表,避免 agent 对港股调 get_chip_distribution 或对美股调 get_capital_flow 后编造数据",
"parameters": {
"type": "object",
"properties": {
"market": {"type": "string", "enum": ["A", "HK", "US"], "description": "市场代码"},
"symbol": {"type": "string", "description": "股票代码(自动识别市场,与 market 二选一)"},
},
},
},
{
"name": "render_stock_report",
"description": "股票分析报告渲染 — 将结构化 JSON(符合 schemas/report_schema.json)通过 j2 模板渲染为 Markdown。template: brief|full。仅渲染,不保存不推送",
"parameters": {
"type": "object",
"properties": {
"report": {"type": "object", "description": "结构化股票报告,字段参考 schemas/report_schema.json"},
"template": {"type": "string", "enum": ["brief", "full"], "description": "模板类型,默认 full"},
},
"required": ["report"],
},
},
{
"name": "render_market_report",
"description": "大盘复盘报告渲染 — 将结构化 JSON(符合 schemas/market_review_schema.json)通过 j2 模板渲染为 Markdown",
"parameters": {
"type": "object",
"properties": {
"report": {"type": "object", "description": "结构化市场复盘"},
"template": {"type": "string", "enum": ["full"], "description": "模板类型,默认 full"},
},
"required": ["report"],
},
},
{
"name": "build_watchlist_context",
"description": "自选股上下文包 — 对多只股票输出评分/趋势/异常/风险/建议 next_tools 的 agent 友好摘要。宿主 agent 决定如何写日报或深入分析",
"parameters": {
"type": "object",
"properties": {
"symbols": {"type": "string", "description": "逗号分隔的股票代码列表"},
"include_market_review": {"type": "boolean", "description": "是否附带各市场复盘,默认 false"},
"workers": {"type": "number", "description": "并发数,默认 3"},
},
"required": ["symbols"],
},
},
{
"name": "analyze_position_context",
"description": "持仓上下文分析 — 输入成本/仓位/止损止盈,结合现价和技术位输出浮盈亏、离止损距离、风险级别、操作建议。无状态、不存账户",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "股票代码"},
"cost": {"type": "number", "description": "成本价"},
"quantity": {"type": "number", "description": "持仓数量"},
"stop_loss": {"type": "number", "description": "止损价(可选)"},
"take_profit": {"type": "number", "description": "止盈价(可选)"},
},
"required": ["symbol", "cost", "quantity"],
},
},
{
"name": "check_alert_rules",
"description": "无状态告警规则检查 — 传入规则数组,返回当前是否触发。规则类型:price_below/price_above/change_pct_above/change_pct_below/volume_ratio_above/anomaly/risk_veto/risk_level_at_least。不做调度不存历史",
"parameters": {
"type": "object",
"properties": {
"symbol": {"type": "string", "description": "股票代码"},
"rules": {
"type": "array",
"description": "规则列表,每项 { type, value }",
"items": {
"type": "object",
"properties": {
"type": {"type": "string"},
"value": {},
},
},
},
},
"required": ["symbol", "rules"],
},
},
{
"name": "parse_stock_list",
"description": "自选股/文本导入解析 — 从自然语言、CSV、Markdown 表格提取股票,自动识别 A 股 6 位代码、港股 xxxxx.HK、美股 ticker,并调用 name_resolver 处理中文股票名",
"parameters": {
"type": "object",
"properties": {
"text": {"type": "string", "description": "待解析的文本"},
},
"required": ["text"],
},
},
]
Loading
Loading