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
12 changes: 12 additions & 0 deletions .github/workflows/commits.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,18 @@ concurrency:
cancel-in-progress: true

jobs:
title:
name: pull request title
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6

- name: Validate Conventional Commit PR title
env:
PR_TITLE: ${{ github.event.pull_request.title }}
run: make check-pr-title

messages:
name: commit messages
runs-on: ubuntu-latest
Expand Down
6 changes: 5 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
.PHONY: help install install-deps lint docs-check check-commits check-assets check-deprecated-names check-cjk check-datetime openapi check-openapi format test integration package cov ci clean
.PHONY: help install install-deps lint docs-check check-commits check-pr-title check-assets check-deprecated-names check-cjk check-datetime openapi check-openapi format test integration package cov ci clean

help:
@echo "Targets:"
Expand All @@ -7,6 +7,7 @@ help:
@echo " lint ruff + import-linter + repo hygiene + datetime discipline + openapi drift"
@echo " docs-check Validate Markdown links, use-case banners, and issue template YAML"
@echo " check-commits Validate Conventional Commit subjects for a git range"
@echo " check-pr-title Validate PR title uses Conventional Commit format"
@echo " check-assets Block committed images, videos, and asset/media directories"
@echo " check-deprecated-names Block deprecated product names"
@echo " check-cjk Scan for CJK outside the language-policy allowlist (advisory)"
Expand Down Expand Up @@ -48,6 +49,9 @@ docs-check:
check-commits:
python3 scripts/check_commit_messages.py $(RANGE)

check-pr-title:
python3 scripts/check_pr_title.py

# Repository media hygiene gate. Images/videos belong in external hosting,
# release artifacts, or other approved storage, then linked from docs.
check-assets:
Expand Down
53 changes: 18 additions & 35 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center" id="readme-top">

![EverOS banner](https://github.com/EverMind-AI/EverOS/releases/download/v1.0.0/everos-readme-banner.jpg)
![EverOS banner](https://github.com/EverMind-AI/EverOS/releases/download/v1.0.0/everos-readme-banner-optimized.jpg)

<p align="center">
<a href="https://x.com/evermind"><img src="https://img.shields.io/badge/EverMind-000000?labelColor=gray&style=for-the-badge&logo=x&logoColor=white" alt="X"></a>
Expand Down Expand Up @@ -54,53 +54,36 @@
<table>
<tr>
<td width="33%" valign="top">
<strong>Markdown-First Memory</strong><br>
Memory is persisted as plain Markdown: visible, auditable, hand-editable,
Git-friendly, and owned by the user.
<strong>Markdown As Source Of Truth</strong><br>
All memory is persisted as <code>.md</code> files: readable, editable,
grep-able, Git-versioned, and openable directly in Obsidian.
</td>
<td width="33%" valign="top">
<strong>Lightweight Local Stack</strong><br>
Install with Python. SQLite tracks runtime state; LanceDB powers vector,
BM25, and scalar-filter retrieval locally.
<strong>Local Three-Part Stack</strong><br>
Markdown + SQLite + LanceDB keep vectors, BM25, and scalar filters
local. No MongoDB, Elasticsearch, or Redis required.
</td>
<td width="33%" valign="top">
<strong>Layered Memory Model</strong><br>
User memory and agent memory are first-class today. Wiki-style knowledge
is the next layer in the roadmap.
<strong>Dual-Track Memory</strong><br>
Agent memory (<code>cases</code> / <code>skills</code>) and user memory
(<code>episodes</code> / <code>profile</code>) are extracted independently.
</td>
</tr>
<tr>
<td width="33%" valign="top">
<strong>Self-Evolving Agents</strong><br>
Agent memory can extract reusable cases and skills from repeated
experience, so workflows become smarter over time.
</td>
<td width="33%" valign="top">
<strong>Multimodal Ingestion</strong><br>
Text, image, audio, documents, PDF, HTML, and email can be parsed into
memory through the optional multimodal pipeline.
</td>
<td width="33%" valign="top">
<strong>Online And Offline Strategy Control</strong><br>
Online extraction and offline evolution stay separate, with configurable
prompts and models at each step. Dreaming is coming next.
</td>
</tr>
<tr>
<td width="33%" valign="top">
<strong>Orthogonal Memory Scope</strong><br>
Owner, memory type, and scope are independent: search by user, agent,
app, project, session, and structured filters.
Text, images, audio, documents, PDFs, HTML, and email are unified into
searchable memory.
</td>
<td width="33%" valign="top">
<strong>Progressive Disclosure</strong><br>
Readable memory surfaces stay simple while deeper facts, cases, and
skills remain available.
<strong>Self-Evolution</strong><br>
Common skills are extracted from real usage; repeated patterns become
reusable workflows, no retraining required.
</td>
<td width="33%" valign="top">
<strong>Modular By Design</strong><br>
EverAlgo owns algorithms; EverOS owns runtime, persistence, online flows,
and offline evolution.
<strong>Orthogonal Retrieval</strong><br>
Search independently by <code>user_id</code>, <code>agent_id</code>,
<code>app_id</code>, <code>project_id</code>, and <code>session_id</code>.
</td>
</tr>
</table>
Expand Down
38 changes: 12 additions & 26 deletions README.zh-CN.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<div align="center" id="readme-top">

![EverOS banner](https://github.com/EverMind-AI/EverOS/releases/download/v1.0.0/everos-readme-banner.jpg)
![EverOS banner](https://github.com/EverMind-AI/EverOS/releases/download/v1.0.0/everos-readme-banner-optimized.jpg)

<p align="center">
<a href="https://x.com/evermind"><img src="https://img.shields.io/badge/EverMind-000000?labelColor=gray&style=for-the-badge&logo=x&logoColor=white" alt="X"></a>
Expand Down Expand Up @@ -54,44 +54,30 @@
<table>
<tr>
<td width="33%" valign="top">
<strong>Markdown-First Memory</strong><br>
记忆以普通 Markdown 持久化:可见、可审计、可手动编辑、Git 友好,并由用户自己拥有
<strong>Markdown As Source Of Truth</strong><br>
所有记忆持久化为 <code>.md</code> 文件:可读、可改、可 grep、可 Git 版本化,也可直接用 Obsidian 打开
</td>
<td width="33%" valign="top">
<strong>Lightweight Local Stack</strong><br>
用 Python 即可安装。SQLite 负责运行时状态;LanceDB 在本地提供向量、BM25 和结构化过滤检索
<strong>Local Three-Part Stack</strong><br>
Markdown + SQLite + LanceDB 在本地完成向量、BM25 和标量过滤检索,无需 MongoDB、Elasticsearch 或 Redis
</td>
<td width="33%" valign="top">
<strong>Layered Memory Model</strong><br>
用户记忆和 Agent 记忆现在是一等公民。Wiki 式知识层是路线图中的下一层
<strong>Dual-Track Memory</strong><br>
Agent 记忆(<code>cases</code> / <code>skills</code>)与用户记忆(<code>episodes</code> / <code>profile</code>)独立提取,互不污染
</td>
</tr>
<tr>
<td width="33%" valign="top">
<strong>Self-Evolving Agents</strong><br>
Agent 记忆可以从重复经验中提取可复用的 cases 和 skills,让工作流随着时间变得更聪明。
</td>
<td width="33%" valign="top">
<strong>Multimodal Ingestion</strong><br>
文本、图片、音频、文档、PDF、HTML 和邮件都可以通过可选的多模态管线解析进记忆。
</td>
<td width="33%" valign="top">
<strong>Online And Offline Strategy Control</strong><br>
在线提取和离线进化保持分离,并且每一步都可以配置 prompts 和 models。Dreaming 即将到来。
</td>
</tr>
<tr>
<td width="33%" valign="top">
<strong>Orthogonal Memory Scope</strong><br>
Owner、memory type 和 scope 相互独立:可以按 user、agent、app、project、session 和结构化 filters 搜索。
文本、图像、音频、文档、PDF、HTML 和邮件统一抽取为可检索的记忆形态。
</td>
<td width="33%" valign="top">
<strong>Progressive Disclosure</strong><br>
可读记忆界面保持简单,同时更深层的 facts、cases 和 skills 仍然可以被系统使用
<strong>Self-Evolution</strong><br>
从真实使用经验中自动抽取共性 skills,重复模式沉淀为可复用流程,无需重训
</td>
<td width="33%" valign="top">
<strong>Modular By Design</strong><br>
EverAlgo 负责算法;EverOS 负责运行时、持久化、在线流程和离线进化
<strong>Orthogonal Retrieval</strong><br>
按 <code>user_id</code>、<code>agent_id</code>、<code>app_id</code>、<code>project_id</code> 和 <code>session_id</code> 五维独立检索
</td>
</tr>
</table>
Expand Down
70 changes: 70 additions & 0 deletions scripts/check_pr_title.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
"""Validate pull request titles against the EverOS Conventional Commits policy."""

from __future__ import annotations

import importlib.util
import os
import sys
from pathlib import Path
from types import ModuleType

_SCRIPT_DIR = Path(__file__).resolve().parent


def _load_commit_policy() -> ModuleType:
policy_path = _SCRIPT_DIR / "check_commit_messages.py"
spec = importlib.util.spec_from_file_location("_commit_message_policy", policy_path)
if spec is None or spec.loader is None:
raise RuntimeError(f"Unable to load commit policy from {policy_path}")

module = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = module
spec.loader.exec_module(module)
return module


_POLICY = _load_commit_policy()
ALLOWED_TYPES = _POLICY.ALLOWED_TYPES
MAX_TITLE_LENGTH = _POLICY.MAX_TITLE_LENGTH
TITLE_RE = _POLICY.TITLE_RE


def validate_title(title: str) -> list[str]:
title = title.strip()
if not title:
return ["missing PR title"]

if len(title) > MAX_TITLE_LENGTH:
return [f"PR title is {len(title)} chars; max is {MAX_TITLE_LENGTH}: {title}"]

if not TITLE_RE.match(title):
allowed = ", ".join(ALLOWED_TYPES)
return [
f"invalid PR title: {title}\n"
" expected: <type>[(scope)][!]: <description>\n"
f" allowed types: {allowed}"
]

return []


def _title_from_args_or_env(argv: list[str]) -> str:
if argv:
return " ".join(argv)
return os.getenv("PR_TITLE", "")


def main(argv: list[str] | None = None) -> int:
title = _title_from_args_or_env(sys.argv[1:] if argv is None else argv)
failures = validate_title(title)
if failures:
print("Pull request title check failed:")
print("\n".join(failures))
return 1

print("Pull request title follows Conventional Commits.")
return 0


if __name__ == "__main__":
raise SystemExit(main())
56 changes: 56 additions & 0 deletions tests/unit/test_scripts/test_check_pr_title.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
"""Self-tests for ``scripts/check_pr_title.py``."""

from __future__ import annotations

import importlib.util
import sys
from pathlib import Path

_REPO_ROOT = Path(__file__).resolve().parents[3]
_CHECKER_PATH = _REPO_ROOT / "scripts" / "check_pr_title.py"


def _load_checker():
assert _CHECKER_PATH.exists(), "PR title checker should exist"
spec = importlib.util.spec_from_file_location("_pr_title_checker", _CHECKER_PATH)
assert spec and spec.loader
mod = importlib.util.module_from_spec(spec)
sys.modules[spec.name] = mod
spec.loader.exec_module(mod)
return mod


def test_conventional_pr_title_is_allowed() -> None:
checker = _load_checker()

assert checker.validate_title("docs(readme): polish launch highlights") == []


def test_bracketed_codex_pr_title_is_blocked() -> None:
checker = _load_checker()

failures = checker.validate_title("[codex] simplify README launch highlights")

assert len(failures) == 1
assert "invalid PR title" in failures[0]
assert "expected: <type>[(scope)][!]: <description>" in failures[0]


def test_long_pr_title_is_blocked() -> None:
checker = _load_checker()
title = (
"docs(readme): polish launch highlights and banner with a title that is "
"too long"
)

failures = checker.validate_title(title)

assert len(failures) == 1
assert "max is 72" in failures[0]


def test_main_reads_pr_title_environment(monkeypatch) -> None:
checker = _load_checker()
monkeypatch.setenv("PR_TITLE", "docs(readme): polish launch highlights")

assert checker.main([]) == 0
Loading