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
44 changes: 44 additions & 0 deletions .github/ISSUE_TEMPLATE/bug_report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: 🐞 Bug report
description: 플러그인이 기대대로 동작하지 않을 때 / Report a bug in the plugin
labels: ["bug"]
body:
- type: markdown
attributes:
value: |
버그를 알려주셔서 감사합니다. 재현 단계가 구체적일수록 빨리 고칠 수 있습니다.
- type: input
id: component
attributes:
label: 영향 받는 부분 / Affected part
description: 어떤 command / agent / skill 인가요?
placeholder: "/screen-spec, code-reviewer, handdrawn-diagram, …"
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: 무슨 일이 있었나요? / What happened?
description: 기대한 동작 vs 실제 동작
placeholder: |
기대: …
실제: …
validations:
required: true
- type: textarea
id: repro
attributes:
label: 재현 단계 / Steps to reproduce
placeholder: |
1. `/prd …`
2. `/screen-spec …`
3. …
validations:
required: true
- type: textarea
id: env
attributes:
label: 환경 / Environment
description: Claude Code 버전, OS, 플러그인 버전
placeholder: "Claude Code x.y, macOS 15, wigtn-coding 2.1.0"
validations:
required: false
2 changes: 2 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Discussions are not enabled on this repo; general questions go through blank issues.
blank_issues_enabled: true
36 changes: 36 additions & 0 deletions .github/ISSUE_TEMPLATE/feature_request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
name: 💡 Feature request
description: 새 agent / command / skill 또는 개선 제안 / Propose a new capability
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: 어떤 문제를 풀고 싶나요? / What problem does this solve?
description: 해결하려는 워크플로우/불편을 먼저 적어주세요 (해결책보다 문제 먼저).
validations:
required: true
- type: dropdown
id: kind
attributes:
label: 종류 / Kind
options:
- New skill
- New command
- New agent
- Improve existing
- Other
validations:
required: true
- type: textarea
id: proposal
attributes:
label: 제안 / Proposed solution
description: 어떻게 동작하면 좋을지. 트리거 예시(`/...`)가 있으면 도움이 됩니다.
validations:
required: false
- type: textarea
id: alternatives
attributes:
label: 대안 / Alternatives considered
validations:
required: false
28 changes: 28 additions & 0 deletions .github/PULL_REQUEST_TEMPLATE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
<!-- 한 PR에 하나의 관심사. 제목은 Conventional Commits 형식 (feat/fix/docs/refactor/chore). -->

## Summary

<!-- 무엇을, 왜 바꿨는지 1~3줄 -->

## Changes

-

## Type

- [ ] `feat` — new agent / command / skill / capability
- [ ] `fix` — bug fix
- [ ] `docs` — documentation only
- [ ] `refactor` / `chore`

## Checklist

- [ ] 새 agent/command/skill을 `plugin.json`에 등록했다 (해당 시)
- [ ] 카운트를 모두 동기화했다 — `plugin.json`, `marketplace.json`, `README.{md,ko,cn}` 배지/표, `CLAUDE.md` (해당 시)
- [ ] `python3 .github/scripts/validate_plugin.py` 통과
- [ ] 버전 변경 시 `marketplace.json` ↔ `plugin.json` ↔ README/CLAUDE.md 일치
- [ ] 사용자 노출 콘텐츠는 한국어, 코드/커밋은 영어

## Related issue

<!-- Closes #NN -->
166 changes: 166 additions & 0 deletions .github/scripts/validate_plugin.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,166 @@
#!/usr/bin/env python3
"""Validate WIGTN plugin manifests against the filesystem and each other.

Guards the count/version drift class of bugs: stated counts in descriptions,
plugin.json arrays, and the actual files on disk must all agree, and the
version must be consistent across manifests. Exits non-zero on any mismatch.

Run locally: python3 .github/scripts/validate_plugin.py
"""

from __future__ import annotations

import json
import re
import sys
from pathlib import Path
from typing import List, Tuple

ROOT = Path(__file__).resolve().parents[2]
MARKETPLACE = ROOT / ".claude-plugin" / "marketplace.json"

errors: List[str] = []


def load_json(path: Path) -> dict:
"""Load JSON, recording a fatal error if it is missing or malformed."""
try:
return json.loads(path.read_text(encoding="utf-8"))
except FileNotFoundError:
errors.append(f"{path.relative_to(ROOT)}: file not found")
except json.JSONDecodeError as exc:
errors.append(f"{path.relative_to(ROOT)}: invalid JSON — {exc}")
return {}


def count_dir(path: Path, pattern: str) -> int:
"""Count entries matching a glob; 0 if the directory is absent."""
return len(list(path.glob(pattern))) if path.is_dir() else 0


def stated_counts(text: str) -> dict:
"""Extract 'N agents | commands | skills' counts from a description string."""
found = {}
for kind in ("agents", "commands", "skills"):
match = re.search(rf"(\d+)\s+{kind}", text)
if match:
found[kind] = int(match.group(1))
return found


def check_plugin(source: Path, mkt_version: str) -> None:
"""Validate one plugin's manifest, arrays, file counts, and version."""
rel = source.relative_to(ROOT)
manifest = source / ".claude-plugin" / "plugin.json"
data = load_json(manifest)
if not data:
return

actual: dict = {
"agents": count_dir(source / "agents", "*.md"),
"commands": count_dir(source / "commands", "*.md"),
"skills": len([p for p in (source / "skills").glob("*") if p.is_dir()])
if (source / "skills").is_dir()
else 0,
}

# plugin.json arrays must match the files on disk.
for kind in ("agents", "commands", "skills"):
listed = len(data.get(kind, []))
if listed != actual[kind]:
errors.append(
f"{rel}: plugin.json lists {listed} {kind} "
f"but {actual[kind]} exist on disk"
)

# Stated counts in plugin.json description must match reality.
for kind, n in stated_counts(data.get("description", "")).items():
if n != actual[kind]:
errors.append(
f"{rel}: description says {n} {kind} but {actual[kind]} exist"
)

# Version must agree with the marketplace entry (when present).
plugin_version = data.get("version")
if plugin_version and mkt_version and plugin_version != mkt_version:
errors.append(
f"{rel}: plugin.json version {plugin_version} != "
f"marketplace version {mkt_version}"
)

return actual


def main() -> int:
mkt = load_json(MARKETPLACE)
if not mkt:
print_report()
return 1

mkt_meta_version = mkt.get("metadata", {}).get("version", "")
last_actual: dict = {}

for entry in mkt.get("plugins", []):
source = (ROOT / entry["source"]).resolve()
entry_version = entry.get("version", "")
if entry_version and mkt_meta_version and entry_version != mkt_meta_version:
errors.append(
f"marketplace.json: plugin '{entry.get('name')}' version "
f"{entry_version} != metadata version {mkt_meta_version}"
)
actual = check_plugin(source, entry_version or mkt_meta_version)

# Stated counts in the marketplace description must match reality too.
if actual:
last_actual[entry.get("name")] = actual
for kind, n in stated_counts(entry.get("description", "")).items():
if n != actual[kind]:
errors.append(
f"marketplace.json: plugin '{entry.get('name')}' "
f"description says {n} {kind} but {actual[kind]} exist"
)
for kind, n in stated_counts(
mkt.get("metadata", {}).get("description", "")
).items():
if n != actual[kind]:
errors.append(
f"marketplace.json: metadata description says {n} {kind} "
f"but {actual[kind]} exist"
)

# Root-level .claude-plugin/plugin.json (top manifest, no arrays) must also
# agree on version and stated counts. It has no source, so validate it
# against the single plugin's actuals when there is exactly one plugin.
root_plugin = ROOT / ".claude-plugin" / "plugin.json"
if root_plugin.exists() and len(last_actual) == 1:
rp = load_json(root_plugin)
actual = next(iter(last_actual.values()))
rp_version = rp.get("version")
if rp_version and mkt_meta_version and rp_version != mkt_meta_version:
errors.append(
f".claude-plugin/plugin.json: version {rp_version} != "
f"marketplace version {mkt_meta_version}"
)
for kind, n in stated_counts(rp.get("description", "")).items():
if n != actual[kind]:
errors.append(
f".claude-plugin/plugin.json: description says {n} {kind} "
f"but {actual[kind]} exist"
)

print_report()
return 1 if errors else 0


def print_report() -> None:
if errors:
print("Plugin validation FAILED:\n")
for err in errors:
print(f" ✗ {err}")
print(f"\n{len(errors)} problem(s). See .github/scripts/validate_plugin.py")
else:
print("Plugin validation passed — manifests, counts, and version agree.")


if __name__ == "__main__":
sys.exit(main())
25 changes: 25 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
name: validate

on:
pull_request:
paths:
- ".claude-plugin/**"
- "plugins/**"
- "README*.md"
- "CLAUDE.md"
- ".github/scripts/validate_plugin.py"
push:
branches: [main]

permissions:
contents: read

jobs:
manifests:
name: Validate plugin manifests & counts
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4

- name: Validate JSON, arrays, counts, and version consistency
run: python3 .github/scripts/validate_plugin.py
64 changes: 64 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
# Contributing to WIGTN Coding

Thanks for your interest! This repo is a Claude Code **plugin marketplace** — one
unified plugin (`wigtn-coding`) made of agents, commands, and skills. The notes
below keep contributions consistent and mergeable.

한국어 사용자를 위한 콘텐츠는 한국어로 작성하되, 코드·커밋·이 문서는 영어를 유지합니다.

## Repo layout

```
.claude-plugin/marketplace.json # marketplace entry (name, version, plugin list)
plugins/wigtn-coding/
├── .claude-plugin/plugin.json # plugin manifest (agents/commands/skills arrays)
├── agents/ *.md # one file per agent
├── commands/ *.md # one file per slash command
├── skills/ <name>/SKILL.md # one folder per skill
└── hooks/hooks.json
```

## Adding an agent / command / skill

1. Create the file/folder under the matching directory.
2. **Register it** in `plugins/wigtn-coding/.claude-plugin/plugin.json` (add to the
`agents` / `commands` / `skills` array). A skill that is not in the array is not
exposed by the plugin.
3. **Update the counts everywhere** — this is enforced by CI (see below):
- `plugins/wigtn-coding/.claude-plugin/plugin.json` description (`N agents, …`)
- `.claude-plugin/plugin.json` (root manifest description)
- `.claude-plugin/marketplace.json` (both descriptions)
- `README.md`, `README.ko.md`, `README.cn.md` (the `N-Skills` / `N-Agents`
badges **and** the skill/agent tables)
- `CLAUDE.md` architecture block
4. User-facing content (skills, commands) is written in Korean; keep technical
terms in English.

## Conventions

- **Commits**: Conventional Commits — `feat(skill):`, `fix:`, `docs:`, `chore:`, …
- **Branches**: `feat/<name>`, `fix/<desc>`, `refactor/<desc>`.
- **Skill frontmatter**: `name`, `description` (with trigger keywords), optional
`allowed-tools`. See existing skills for the pattern.
- **Mermaid in generated docs**: quote every label (`["..."]`), use only valid
shapes, no `mindmap` (use `flowchart LR`).

## Versioning & releases

Single source of truth: the `version` in `marketplace.json` and `plugin.json` must
match, and `README` / `CLAUDE.md` must state the same number. Bump with
[SemVer](https://semver.org/): `fix` → patch, `feat` → minor.

## Before you open a PR

Run the same check CI runs:

```bash
python3 .github/scripts/validate_plugin.py
```

It verifies that JSON is valid and that the stated counts, the `plugin.json`
arrays, and the files on disk all agree — and that the version is consistent. PRs
that drift will fail this check.

Fill in the PR template, link any related issue, and keep one concern per PR.
4 changes: 2 additions & 2 deletions README.cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -186,7 +186,7 @@ WIGTN Coding 是一个 Claude Code 插件。你描述想要构建的东西,13
| 命令 | 功能 |
|------|------|
| `/prd <功能>` | 根据功能创意生成 PRD + 分阶段任务计划(针对 UI 功能新增 User Roles、Page State Matrix、User Flow 章节) |
| `/screen-spec <功能>` | 可选 UI 阶段:IA + 用户流程 + 屏幕规格 + 可点击 HTML 线框图 + Dev Handoff,与 design-discovery 20 种风格联动 |
| `/screen-spec <功能>` | 可选 UI 阶段:IA + 用户流程 + 屏幕规格 + 可点击 HTML 线框图 + Dev Handoff。灰度 + 语义色 lo-fi 线框图(风格决策在 `/implement` 阶段) |
| `/implement <功能>` | 自动并行模式检测,进行设计 + 构建(如存在 screen-spec 产物则作为输入使用) |
| `/auto-commit` | 3 智能体并行审查 → 质量门禁 → 提交 + PR |
| `/review-pr <PR>` | 在终端审查 GitHub PR:diff 分析、质量评分、行内评论 |
Expand Down Expand Up @@ -233,7 +233,7 @@ WIGTN Coding 是一个 Claude Code 插件。你描述想要构建的东西,13
|------|---------|
| `code-review-levels` | 深度审查(Level 3:调用链、边界情况、并发)和架构审查(Level 4:SOLID、层违规、可扩展性) |
| `design-system-reference` | 20 个风格指南 — 排版、色彩、组件、动效、反模式。与 design-discovery 协同进行上下文感知推荐 |
| `screen-spec` | 从 PRD 生成 5 种 UI 产物 — IA、用户流程、屏幕规格、可点击 Wireframe HTML、Dev Handoff。与 20 种设计风格联动。由 `/screen-spec` 调用 |
| `screen-spec` | 从 PRD 生成 5 种 UI 产物 — IA、用户流程、屏幕规格、可点击 Wireframe HTML、Dev Handoff。灰度 + 语义色 lo-fi 线框图(风格在 `/implement` 决定)。由 `/screen-spec` 调用 |
| `team-memory-protocol` | 并行构建中跨智能体共享上下文(SHARED_CONTEXT)管理 |

</details>
Expand Down
Loading
Loading