diff --git a/README.en.md b/README.en.md new file mode 100644 index 0000000..c26e4e9 --- /dev/null +++ b/README.en.md @@ -0,0 +1,144 @@ +# Ossmate + +[πŸ‡°πŸ‡· ν•œκ΅­μ–΄](README.md) Β· **πŸ‡¬πŸ‡§ English** + +[![CI](https://img.shields.io/github/actions/workflow/status/sunjin12/ossmate/ci.yml?branch=main&style=for-the-badge)](https://github.com/sunjin12/ossmate/actions) +[![PyPI](https://img.shields.io/pypi/v/ossmate?style=for-the-badge)](https://pypi.org/project/ossmate/) +[![Claude Code Plugin](https://img.shields.io/badge/Claude%20Code-Plugin-7C3AED?style=for-the-badge)](https://github.com/sunjin12/ossmate) +[![Python](https://img.shields.io/badge/python-3.11+-blue?style=for-the-badge)](https://www.python.org) +[![License: MIT](https://img.shields.io/badge/license-MIT-green?style=for-the-badge)](LICENSE) + +> Your Claude-powered co-maintainer for open source. + +Ossmate automates the repetitive parts of OSS maintenance β€” PR triage, issue classification, release notes, dependency audits, stale-issue sweeps, and contributor onboarding β€” through a Claude Code plugin and a standalone CLI. + +It exists for two reasons: + +1. **A practical tool** for solo maintainers drowning in their issue queue. +2. **A reference implementation** showing how to combine *every* Claude Code extension surface β€” Skills, Subagents, Hooks, MCP, Plugins, Agent SDK, Cron, Status line, Output styles, Memory, Settings, Keybindings β€” into a single coherent product. + +--- + +## Built surfaces + +| Surface | Where | Status | +|---|---|---| +| Skills (slash commands) | [.claude/commands/](.claude/commands/) | `[x]` Phase 5 (8/8) | +| Subagents | [.claude/agents/](.claude/agents/) | `[x]` Phase 5 (6 β€” haiku/sonnet/opus matched) | +| Hooks | [.claude/hooks/](.claude/hooks/) | `[x]` Phase 3 (5 events, 21 tests) | +| MCP server | [mcp/ossmate_mcp/](mcp/ossmate_mcp/) | `[x]` Phase 4 (11 tools, 3 templates) | +| Plugin packaging | [.claude-plugin/](.claude-plugin/) | `[x]` Phase 6 (manifest + self-marketplace) | +| Claude Agent SDK CLI | [cli/ossmate/](cli/ossmate/) | `[x]` Phase 7 (Typer + 8 subcommands, dry-run mode) | +| Status line | [.claude/statusline.sh](.claude/statusline.sh) | `[x]` Phase 1 | +| Output styles | [.claude/output-styles/](.claude/output-styles/) | `[x]` Phase 1 | +| Scheduled triggers | [scheduled/](scheduled/) | `[x]` Phase 8 (3 cron jobs, off-minute spread) | +| Memory templates | [.claude/CLAUDE.md](.claude/CLAUDE.md) | `[x]` Phase 0 | +| Settings & permissions | [.claude/settings.json](.claude/settings.json) | `[x]` Phase 0 | +| Keybindings | [.claude/keybindings.json.example](.claude/keybindings.json.example) | `[x]` Phase 1 | +| CI / Release | [.github/workflows/](.github/workflows/) | `[x]` Phase 9 (3-OS Γ— 3-Python matrix, OIDC PyPI publish) | + +--- + +## Quickstart + +> Three ways to use Ossmate. Pick the one that matches your environment. + +### A. Claude Code Plugin (recommended) + +```bash +claude plugin marketplace add https://raw.githubusercontent.com/sunjin12/ossmate/main/.claude-plugin/marketplace.json +claude plugin install ossmate@ossmate +``` + +Then inside any repo: + +``` +/triage-pr 1234 +/release-notes v1.4.0 +/stale-sweep --days 60 +``` + +> Plugins are namespaced under their plugin name, so `/ossmate:triage-pr 1234` works too β€” use the namespaced form when another installed plugin defines a same-named command. + +### B. Standalone CLI + +```bash +pipx install ossmate +ossmate triage-pr 1234 +ossmate release-notes v1.4.0 +ossmate triage-pr 1234 --dry-run # print the rendered prompt + ClaudeAgentOptions +``` + +The CLI loads the same `.claude/commands/*.md` skill bodies the plugin uses β€” write a skill once, get a slash command and a CLI subcommand for free. + +### C. From source (development) + +```bash +git clone https://github.com/sunjin12/ossmate.git +cd ossmate +bash scripts/dev_link.sh # editable install of mcp + cli, runs --check +# Windows: +# powershell -ExecutionPolicy Bypass -File scripts/dev_link.ps1 +pytest -q # 160 hermetic tests, ~5s +``` + +--- + +## Architecture + +```mermaid +flowchart LR + User([Maintainer]) + User -->|slash commands| CC[Claude Code] + User -->|terminal / CI| CLI[ossmate CLI] + CC --> Skills[Skills] + CC --> Hooks[Hooks] + Skills --> Subagents + Subagents --> MCP[ossmate MCP server] + CLI --> SDK[Claude Agent SDK] + SDK --> MCP + MCP --> GH[(GitHub API)] + MCP --> Repo[(Local repo)] + MCP --> Adv[(OSV advisories)] +``` + +The same MCP server backs both the plugin and the standalone CLI β€” write tools once, use them everywhere. + +--- + +## Why each surface? + +Ossmate uses every harness extension because the OSS-maintainer domain genuinely needs each one. This isn't a contrived demo β€” read [docs/architecture.md](docs/architecture.md) for the per-surface justification. + +--- + +## Self-dogfooding + +The CHANGELOG of this repo is generated by Ossmate itself, scheduled triggers run nightly digests on this very repo, and the PreToolUse hook protects `main` from accidental force-pushes. See [CHANGELOG.md](CHANGELOG.md). + +--- + +## Releasing + +Two PyPI projects (`ossmate-mcp` and `ossmate`) ship together β€” version, tag, push, GitHub Actions does the rest: + +```bash +python scripts/bump_version.py 0.2.0 # bumps both pyprojects + plugin.json + marketplace.json +python scripts/bump_version.py --check # invariant: all 5 markers agree +git commit -am "chore(release): v0.2.0" +git tag v0.2.0 && git push --tags # blocked by PreToolUse hook unless explicitly approved +``` + +The release workflow refuses to publish if the tag's version disagrees with `pyproject.toml`. PyPI uploads use OIDC trusted publishing β€” no API tokens stored in repo secrets. + +--- + +## Development status + +Currently building in phases β€” see [memory/project_phases.md](https://github.com/sunjin12/ossmate/blob/main/memory/project_phases.md) for the plan. Each phase is tagged (`phase-0`, `phase-1`, …) so you can browse the project's evolution. + +--- + +## License + +MIT β€” see [LICENSE](LICENSE). diff --git a/README.md b/README.md index e829b1f..6cf379c 100644 --- a/README.md +++ b/README.md @@ -1,54 +1,56 @@ # Ossmate +**πŸ‡°πŸ‡· ν•œκ΅­μ–΄** Β· [πŸ‡¬πŸ‡§ English](README.en.md) + [![CI](https://img.shields.io/github/actions/workflow/status/sunjin12/ossmate/ci.yml?branch=main&style=for-the-badge)](https://github.com/sunjin12/ossmate/actions) [![PyPI](https://img.shields.io/pypi/v/ossmate?style=for-the-badge)](https://pypi.org/project/ossmate/) [![Claude Code Plugin](https://img.shields.io/badge/Claude%20Code-Plugin-7C3AED?style=for-the-badge)](https://github.com/sunjin12/ossmate) [![Python](https://img.shields.io/badge/python-3.11+-blue?style=for-the-badge)](https://www.python.org) [![License: MIT](https://img.shields.io/badge/license-MIT-green?style=for-the-badge)](LICENSE) -> Your Claude-powered co-maintainer for open source. +> Claude 기반 μ˜€ν”ˆμ†ŒμŠ€ λ©”μΈν…Œμ΄λ„ˆ 보쑰 도ꡬ. -Ossmate automates the repetitive parts of OSS maintenance β€” PR triage, issue classification, release notes, dependency audits, stale-issue sweeps, and contributor onboarding β€” through a Claude Code plugin and a standalone CLI. +OssmateλŠ” OSS λ©”μΈν…Œμ΄λ„ˆμ˜ 반볡 업무 β€” PR νŠΈλ¦¬μ•„μ§€, 이슈 λΆ„λ₯˜, 릴리슀 λ…ΈνŠΈ μž‘μ„±, μ˜μ‘΄μ„± 감사, stale 이슈 정리, μ»¨νŠΈλ¦¬λ·°ν„° μ˜¨λ³΄λ”© β€” 을 Claude Code ν”ŒλŸ¬κ·ΈμΈκ³Ό 독립 CLI둜 μžλ™ν™”ν•©λ‹ˆλ‹€. -It exists for two reasons: +두 κ°€μ§€ λͺ©μ μœΌλ‘œ λ§Œλ“€μ–΄μ‘ŒμŠ΅λ‹ˆλ‹€: -1. **A practical tool** for solo maintainers drowning in their issue queue. -2. **A reference implementation** showing how to combine *every* Claude Code extension surface β€” Skills, Subagents, Hooks, MCP, Plugins, Agent SDK, Cron, Status line, Output styles, Memory, Settings, Keybindings β€” into a single coherent product. +1. **μ‹€μš© 도ꡬ** β€” 이슈 큐에 λΉ μ Έ μ£½λŠ” μ†”λ‘œ λ©”μΈν…Œμ΄λ„ˆλ₯Ό μœ„ν•œ μ§„μ§œ 도ꡬ. +2. **레퍼런슀 κ΅¬ν˜„μ²΄** β€” Claude Code의 *λͺ¨λ“ * ν™•μž₯ ν‘œλ©΄(Skills, Subagents, Hooks, MCP, Plugins, Agent SDK, Cron, Status line, Output styles, Memory, Settings, Keybindings)을 ν•˜λ‚˜μ˜ μΌκ΄€λœ μ œν’ˆμœΌλ‘œ κ²°ν•©ν•œ 사둀. --- -## Built surfaces +## κ΅¬μΆ•λœ ν‘œλ©΄ -| Surface | Where | Status | +| ν‘œλ©΄ | μœ„μΉ˜ | μƒνƒœ | |---|---|---| -| Skills (slash commands) | [.claude/commands/](.claude/commands/) | `[x]` Phase 5 (8/8) | -| Subagents | [.claude/agents/](.claude/agents/) | `[x]` Phase 5 (6 β€” haiku/sonnet/opus matched) | -| Hooks | [.claude/hooks/](.claude/hooks/) | `[x]` Phase 3 (5 events, 21 tests) | -| MCP server | [mcp/ossmate_mcp/](mcp/ossmate_mcp/) | `[x]` Phase 4 (11 tools, 3 templates) | -| Plugin packaging | [.claude-plugin/](.claude-plugin/) | `[x]` Phase 6 (manifest + self-marketplace) | -| Claude Agent SDK CLI | [cli/ossmate/](cli/ossmate/) | `[x]` Phase 7 (Typer + 8 subcommands, dry-run mode) | +| Skills (μŠ¬λž˜μ‹œ λͺ…λ Ή) | [.claude/commands/](.claude/commands/) | `[x]` Phase 5 (8/8) | +| Subagents | [.claude/agents/](.claude/agents/) | `[x]` Phase 5 (6개 β€” haiku/sonnet/opus λ§€μΉ­) | +| Hooks | [.claude/hooks/](.claude/hooks/) | `[x]` Phase 3 (5개 이벀트, 21개 ν…ŒμŠ€νŠΈ) | +| MCP μ„œλ²„ | [mcp/ossmate_mcp/](mcp/ossmate_mcp/) | `[x]` Phase 4 (11개 도ꡬ, 3개 ν…œν”Œλ¦Ώ) | +| ν”ŒλŸ¬κ·ΈμΈ νŒ¨ν‚€μ§• | [.claude-plugin/](.claude-plugin/) | `[x]` Phase 6 (manifest + 자체 λ§ˆμΌ“ν”Œλ ˆμ΄μŠ€) | +| Claude Agent SDK CLI | [cli/ossmate/](cli/ossmate/) | `[x]` Phase 7 (Typer + 8개 μ„œλΈŒμ»€λ§¨λ“œ, dry-run λͺ¨λ“œ) | | Status line | [.claude/statusline.sh](.claude/statusline.sh) | `[x]` Phase 1 | | Output styles | [.claude/output-styles/](.claude/output-styles/) | `[x]` Phase 1 | -| Scheduled triggers | [scheduled/](scheduled/) | `[x]` Phase 8 (3 cron jobs, off-minute spread) | +| Scheduled triggers | [scheduled/](scheduled/) | `[x]` Phase 8 (3개 cron 작, off-minute λΆ„μ‚°) | | Memory templates | [.claude/CLAUDE.md](.claude/CLAUDE.md) | `[x]` Phase 0 | | Settings & permissions | [.claude/settings.json](.claude/settings.json) | `[x]` Phase 0 | | Keybindings | [.claude/keybindings.json.example](.claude/keybindings.json.example) | `[x]` Phase 1 | -| CI / Release | [.github/workflows/](.github/workflows/) | `[x]` Phase 9 (3-OS Γ— 3-Python matrix, OIDC PyPI publish) | +| CI / Release | [.github/workflows/](.github/workflows/) | `[x]` Phase 9 (3-OS Γ— 3-Python 맀트릭슀, OIDC PyPI λ°œν–‰) | --- -## Quickstart +## λΉ λ₯Έ μ‹œμž‘ -> Three ways to use Ossmate. Pick the one that matches your environment. +> μ„Έ κ°€μ§€ μ‚¬μš© 방법이 μžˆμŠ΅λ‹ˆλ‹€. 본인 ν™˜κ²½μ— λ§žλŠ” 것을 κ³ λ₯΄μ„Έμš”. -### A. Claude Code Plugin (recommended) +### A. Claude Code ν”ŒλŸ¬κ·ΈμΈ (ꢌμž₯) ```bash claude plugin marketplace add https://raw.githubusercontent.com/sunjin12/ossmate/main/.claude-plugin/marketplace.json claude plugin install ossmate@ossmate ``` -Then inside any repo: +μ„€μΉ˜ ν›„ μ–΄λŠ repoμ—μ„œλ‚˜: ``` /triage-pr 1234 @@ -56,33 +58,33 @@ Then inside any repo: /stale-sweep --days 60 ``` -> Plugins are namespaced under their plugin name, so `/ossmate:triage-pr 1234` works too β€” use the namespaced form when another installed plugin defines a same-named command. +> ν”ŒλŸ¬κ·ΈμΈμ€ 자체 λ„€μž„μŠ€νŽ˜μ΄μŠ€λ₯Ό κ°€μ§€λ―€λ‘œ `/ossmate:triage-pr 1234` ν˜•μ‹λ„ μž‘λ™ν•©λ‹ˆλ‹€ β€” λ‹€λ₯Έ ν”ŒλŸ¬κ·ΈμΈκ³Ό λͺ…λ Ή 이름이 μΆ©λŒν•  λ•Œ λ„€μž„μŠ€νŽ˜μ΄μŠ€ ν˜•μ‹μ„ μ“°μ„Έμš”. -### B. Standalone CLI +### B. 독립 CLI ```bash pipx install ossmate ossmate triage-pr 1234 ossmate release-notes v1.4.0 -ossmate triage-pr 1234 --dry-run # print the rendered prompt + ClaudeAgentOptions +ossmate triage-pr 1234 --dry-run # λ Œλ”λ§λœ prompt + ClaudeAgentOptions 좜λ ₯ ``` -The CLI loads the same `.claude/commands/*.md` skill bodies the plugin uses β€” write a skill once, get a slash command and a CLI subcommand for free. +CLIλŠ” ν”ŒλŸ¬κ·ΈμΈμ΄ μ‚¬μš©ν•˜λŠ” λ™μΌν•œ `.claude/commands/*.md` μŠ€ν‚¬ 본문을 λ‘œλ“œν•©λ‹ˆλ‹€ β€” μŠ€ν‚¬μ„ ν•œ 번 μž‘μ„±ν•˜λ©΄ μŠ¬λž˜μ‹œ λͺ…λ Ήκ³Ό CLI μ„œλΈŒμ»€λ§¨λ“œκ°€ μžλ™μœΌλ‘œ ν•¨κ»˜ μƒκΉλ‹ˆλ‹€. -### C. From source (development) +### C. μ†ŒμŠ€μ—μ„œ (개발용) ```bash git clone https://github.com/sunjin12/ossmate.git cd ossmate -bash scripts/dev_link.sh # editable install of mcp + cli, runs --check +bash scripts/dev_link.sh # mcp + cli editable μ„€μΉ˜, --check μžλ™ μ‹€ν–‰ # Windows: # powershell -ExecutionPolicy Bypass -File scripts/dev_link.ps1 -pytest -q # 160 hermetic tests, ~5s +pytest -q # 161개 hermetic ν…ŒμŠ€νŠΈ, ~5초 ``` --- -## Architecture +## μ•„ν‚€ν…μ²˜ ```mermaid flowchart LR @@ -100,43 +102,45 @@ flowchart LR MCP --> Adv[(OSV advisories)] ``` -The same MCP server backs both the plugin and the standalone CLI β€” write tools once, use them everywhere. +같은 MCP μ„œλ²„κ°€ ν”ŒλŸ¬κ·ΈμΈκ³Ό 독립 CLI μ–‘μͺ½μ„ λͺ¨λ‘ μ§€μ›ν•©λ‹ˆλ‹€ β€” 도ꡬλ₯Ό ν•œ 번 μž‘μ„±ν•˜λ©΄ μ–΄λ””μ„œλ‚˜ μ‚¬μš© κ°€λŠ₯. --- -## Why each surface? +## μ™œ λͺ¨λ“  ν‘œλ©΄μ„ μ‚¬μš©ν–ˆλ‚˜? -Ossmate uses every harness extension because the OSS-maintainer domain genuinely needs each one. This isn't a contrived demo β€” read [docs/architecture.md](docs/architecture.md) for the per-surface justification. +Ossmateκ°€ λͺ¨λ“  ν•˜λ„€μŠ€ ν™•μž₯을 μ‚¬μš©ν•˜λŠ” μ΄μœ λŠ” OSS λ©”μΈν…Œμ΄λ„ˆ 도메인이 각각을 *μ§„μ§œλ‘œ* ν•„μš”λ‘œ ν•˜κΈ° λ•Œλ¬Έμž…λ‹ˆλ‹€. λ‹¨μˆœ 데λͺ¨κ°€ μ•„λ‹™λ‹ˆλ‹€ β€” ν‘œλ©΄λ³„ μ •λ‹Ήν™”λŠ” [docs/architecture.md](docs/architecture.md)λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”. --- ## Self-dogfooding -The CHANGELOG of this repo is generated by Ossmate itself, scheduled triggers run nightly digests on this very repo, and the PreToolUse hook protects `main` from accidental force-pushes. See [CHANGELOG.md](CHANGELOG.md). +이 μ €μž₯μ†Œμ˜ CHANGELOGλŠ” Ossmate μžμ‹ μ΄ μƒμ„±ν•˜κ³ , μŠ€μΌ€μ€„λœ νŠΈλ¦¬κ±°κ°€ 맀일 이 repo에 λ‹€μ΄μ œμŠ€νŠΈλ₯Ό 돌리며, PreToolUse 훅이 μ‹€μˆ˜λ‘œ `main`에 force-push ν•˜λŠ” 것을 λ§‰μŠ΅λ‹ˆλ‹€. [CHANGELOG.md](CHANGELOG.md)λ₯Ό μ°Έμ‘°ν•˜μ„Έμš”. --- -## Releasing +## 릴리슀 방법 -Two PyPI projects (`ossmate-mcp` and `ossmate`) ship together β€” version, tag, push, GitHub Actions does the rest: +두 PyPI ν”„λ‘œμ νŠΈ(`ossmate-mcp`, `ossmate`)λŠ” ν•¨κ»˜ μΆœμ‹œλ©λ‹ˆλ‹€ β€” 버전 올리고, νƒœκ·Έ 달고, push만 ν•˜λ©΄ GitHub Actionsκ°€ λ‚˜λ¨Έμ§€λ₯Ό μ²˜λ¦¬ν•©λ‹ˆλ‹€: ```bash -python scripts/bump_version.py 0.2.0 # bumps both pyprojects + plugin.json + marketplace.json -python scripts/bump_version.py --check # invariant: all 5 markers agree +python scripts/bump_version.py 0.2.0 # 두 pyproject + plugin.json + marketplace.json λ™μ‹œ κ°±μ‹  +python scripts/bump_version.py --check # invariant: 5κ³³ λͺ¨λ‘ 일치 확인 git commit -am "chore(release): v0.2.0" -git tag v0.2.0 && git push --tags # blocked by PreToolUse hook unless explicitly approved +git tag v0.2.0 && git push --tags # PreToolUse 훅이 λͺ…μ‹œμ  승인 μ—†μ΄λŠ” 차단 ``` -The release workflow refuses to publish if the tag's version disagrees with `pyproject.toml`. PyPI uploads use OIDC trusted publishing β€” no API tokens stored in repo secrets. +릴리슀 μ›Œν¬ν”Œλ‘œμš°λŠ” νƒœκ·Έ 버전과 `pyproject.toml` 버전이 λ‹€λ₯΄λ©΄ λ°œν–‰μ„ κ±°λΆ€ν•©λ‹ˆλ‹€. PyPI μ—…λ‘œλ“œλŠ” OIDC μ‹ λ’° λ°œν–‰ 방식 β€” μ €μž₯μ†Œ μ‹œν¬λ¦Ώμ— API 토큰이 μ—†μŠ΅λ‹ˆλ‹€. --- -## Development status +## 개발 ν˜„ν™© + +λ‹¨κ³„λ³„λ‘œ λΉŒλ“œλ˜μ—ˆμŠ΅λ‹ˆλ‹€ β€” κ³„νšμ€ [memory/project_phases.md](https://github.com/sunjin12/ossmate/blob/main/memory/project_phases.md)에 μžˆμŠ΅λ‹ˆλ‹€. 각 λ‹¨κ³„λŠ” νƒœκ·Έ(`phase-0`, `phase-1`, …)둜 λ§ˆν‚Ήλ˜μ–΄ μžˆμ–΄ ν”„λ‘œμ νŠΈμ˜ μ§„ν™” 과정을 따라가볼 수 μžˆμŠ΅λ‹ˆλ‹€. -Currently building in phases β€” see [memory/project_phases.md](https://github.com/sunjin12/ossmate/blob/main/memory/project_phases.md) for the plan. Each phase is tagged (`phase-0`, `phase-1`, …) so you can browse the project's evolution. +**v0.1.0 (2026-04-19)** β€” 첫 곡개 릴리슀. λͺ¨λ“  12개 ν‘œλ©΄ μž‘λ™, PyPI λ°œν–‰ μ™„λ£Œ. --- -## License +## λΌμ΄μ„ μŠ€ -MIT β€” see [LICENSE](LICENSE). +MIT β€” [LICENSE](LICENSE) μ°Έμ‘°.