Skip to content
Merged
4 changes: 2 additions & 2 deletions .claude-plugin/marketplace.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,14 @@
},
"metadata": {
"description": "AI-native Vibe Coding — From idea to Deploy, Zero friction. Unified plugin with 11 agents, 5 commands, 7 skills, team-based parallel build, 20 design styles, screen-spec generator.",
"version": "0.1.16"
"version": "0.1.17"
},
"plugins": [
{
"name": "wigtn-plugins",
"description": "Unified AI-native Vibe Coding plugin: PRD generation, digging analysis, screen-spec (IA / User Flow / Wireframe HTML / Dev Handoff), team-based parallel implementation, quality-gated auto-commit, 20 design styles, frontend (React/Next.js), backend (NestJS/Prisma), mobile (React Native/Expo), AI integration (STT/LLM), DevOps (Docker/CI-CD). 11 agents, 5 commands, 7 skills.",
"source": "./plugins/wigtn-plugins",
"version": "0.1.16",
"version": "0.1.17",
"author": {
"name": "WIGTN Crew",
"email": "contact@wigtn.com"
Expand Down
2 changes: 1 addition & 1 deletion .claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wigtn-plugins",
"version": "0.1.16",
"version": "0.1.17",
"description": "AI-native Vibe Coding — From idea to Deploy, Zero friction. Unified plugin with 11 agents, 5 commands, 7 skills, team-based parallel build, 20 design styles.",
"author": {
"name": "WIGTN Crew",
Expand Down
170 changes: 170 additions & 0 deletions .github/scripts/test_context_scripts.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,170 @@
#!/usr/bin/env bash
# 번들 컨텍스트 스크립트 회귀 테스트 (repo-context / repo-state / pr-context).
#
# 이 세 스크립트는 /implement·/auto-commit·/review-pr 의 첫 단계라, 조용히 틀리면
# 모델이 빈 트리나 없는 PR 을 그대로 믿고 진행한다. 실제로 리뷰에서 잡힌 결함이
# 전부 "실패했는데 성공처럼 보이는" 종류였다 - tracked-only 수집, 잘린 개수를
# 전체로 보고, 서브디렉터리 경로 어긋남, gh 실패의 빈 PR 성공 처리.
#
# 임시 저장소를 만들어 실제 실행하고 JSON 필드만 본다. 모델 호출 없음.
#
# 실행: bash .github/scripts/test_context_scripts.sh

REPO_ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)
S="$REPO_ROOT/plugins/wigtn-plugins/scripts"
PASS=0; FAIL=0
TMP=$(mktemp -d); trap 'rm -rf "$TMP"' EXIT

git_init() { git init -q "$1" && git -C "$1" config user.email t@t && git -C "$1" config user.name t; }

# $1 라벨 $2 기대 $3 실제
eq() {
if [ "$2" = "$3" ]; then echo " ok $1"; PASS=$((PASS+1))
else echo " FAIL $1 - 기대 [$2] 실제 [$3]"; FAIL=$((FAIL+1)); fi
}
# $1 라벨 $2 JSON $3 python 표현식(d 로 접근)
field() { python3 -c "
import json,sys
d=json.loads(sys.stdin.read())
print($3)" <<< "$2"; }

echo "repo-context.sh"

# 커밋하지 않은 파일도 소스 트리에 들어간다 (git ls-files 는 tracked 만 낸다)
D="$TMP/untracked"; mkdir -p "$D/src"; git_init "$D"
echo '{"scripts":{"test":"vitest"}}' > "$D/package.json"; echo x > "$D/src/app.ts"
J=$(cd "$D" && bash "$S/repo-context.sh")
eq "커밋 전 파일이 트리에 잡힌다" "2" "$(field _ "$J" "d['file_count']")"
eq " 경로가 root 기준이다" "src/app.ts" "$(field _ "$J" "[f for f in d['files'] if f.endswith('app.ts')][0]")"

# tracked 가 이미 있는 레포에서도 untracked 가 합쳐져야 한다.
# (커밋이 없으면 find 폴백이 결함을 가리므로 이 케이스가 진짜 회귀 검사다)
D="$TMP/mixed"; mkdir -p "$D/src"; git_init "$D"
echo a > "$D/src/committed.ts"; git -C "$D" add -A; git -C "$D" commit -q -m init
echo b > "$D/src/new.ts"
J=$(cd "$D" && bash "$S/repo-context.sh")
eq "tracked+untracked 가 합쳐진다" "2" "$(field _ "$J" "d['file_count']")"
eq " untracked 가 포함된다" "True" "$(field _ "$J" "'src/new.ts' in d['files']")"

# git 저장소가 아니면 find 로 떨어진다
D="$TMP/nogit"; mkdir -p "$D/src"; echo x > "$D/src/a.ts"; echo y > "$D/README.md"
J=$(cd "$D" && bash "$S/repo-context.sh")
eq "git 아닌 디렉터리도 수집한다" "2" "$(field _ "$J" "d['file_count']")"

# .gitignore 는 존중한다
D="$TMP/ignore"; mkdir -p "$D/node_modules/p"; git_init "$D"
echo "node_modules/" > "$D/.gitignore"; echo x > "$D/app.ts"; echo n > "$D/node_modules/p/f.js"
J=$(cd "$D" && bash "$S/repo-context.sh")
eq "node_modules 는 빠진다" "0" "$(field _ "$J" "len([f for f in d['files'] if 'node_modules' in f])")"

# 자를 때 file_count 는 자르기 전 전체 수여야 한다
D="$TMP/trunc"; mkdir -p "$D"; git_init "$D"
for i in $(seq 1 250); do echo x > "$D/f$i.txt"; done
J=$(cd "$D" && bash "$S/repo-context.sh")
eq "file_count 는 자르기 전 개수" "250" "$(field _ "$J" "d['file_count']")"
eq " 목록은 상한까지만" "200" "$(field _ "$J" "len(d['files'])")"
eq " 잘림을 표기한다" "True" "$(field _ "$J" "d['files_truncated']")"

# 검증 명령 감지 - npm 외 생태계도 잡아야 한다 (빈 목록이면 모델이 검증을 건너뛴다)
detect() { # $1 디렉터리명 $2.. 생성할 파일
local d="$TMP/eco_$1"; shift; mkdir -p "$d"; git_init "$d"
for f in "$@"; do printf '%s' "$3" > "$d/$f"; done
(cd "$d" && bash "$S/repo-context.sh")
}
D="$TMP/py"; mkdir -p "$D"; git_init "$D"
printf '[tool.pytest.ini_options]\nx=1\n[tool.ruff]\ny=2\n' > "$D/pyproject.toml"
J=$(cd "$D" && bash "$S/repo-context.sh")
eq "pyproject 에서 pytest 감지" "pytest" "$(field _ "$J" "d['verify_commands'].get('test')")"

D="$TMP/go"; mkdir -p "$D"; git_init "$D"; printf 'module x\ngo 1.22\n' > "$D/go.mod"
eq "go.mod 에서 go test 감지" "go test ./..." "$(field _ "$(cd "$D" && bash "$S/repo-context.sh")" "d['verify_commands'].get('test')")"

D="$TMP/rust"; mkdir -p "$D"; git_init "$D"; printf '[package]\nname="x"\n' > "$D/Cargo.toml"
eq "Cargo.toml 에서 cargo test 감지" "cargo test" "$(field _ "$(cd "$D" && bash "$S/repo-context.sh")" "d['verify_commands'].get('test')")"

D="$TMP/mk"; mkdir -p "$D"; git_init "$D"; printf 'test:\n\techo t\n' > "$D/Makefile"
eq "Makefile 타깃 감지" "make test" "$(field _ "$(cd "$D" && bash "$S/repo-context.sh")" "d['verify_commands'].get('test')")"

# bun·pnpm 의 test 서브커맨드는 package.json 스크립트를 돌리지 않는다
for lk in bun.lock bun.lockb; do
D="$TMP/bun_$lk"; mkdir -p "$D"; git_init "$D"
echo '{"scripts":{"test":"vitest"}}' > "$D/package.json"; touch "$D/$lk"
eq "$lk -> bun run test" "bun run test" "$(field _ "$(cd "$D" && bash "$S/repo-context.sh")" "d['verify_commands'].get('test')")"
done
D="$TMP/pnpm"; mkdir -p "$D"; git_init "$D"
echo '{"scripts":{"test":"vitest"}}' > "$D/package.json"; touch "$D/pnpm-lock.yaml"
eq "pnpm -> pnpm run test" "pnpm run test" "$(field _ "$(cd "$D" && bash "$S/repo-context.sh")" "d['verify_commands'].get('test')")"

D="$TMP/plain"; mkdir -p "$D"; git_init "$D"; echo hi > "$D/README.md"
eq "감지 실패는 verify_detected=false" "False" "$(field _ "$(cd "$D" && bash "$S/repo-context.sh")" "d['verify_detected']")"

echo "repo-state.sh"

# staged·unstaged·untracked 가 모두 changed_files 에 들어간다
D="$TMP/state"; mkdir -p "$D/sub" "$D/sub2"; git_init "$D"
git -C "$D" commit -q --allow-empty -m init
echo r > "$D/root_untracked.txt"; echo s > "$D/sub/sub_untracked.txt"
echo x > "$D/sub2/x.txt"; git -C "$D" add sub2/x.txt
J=$(cd "$D" && bash "$S/repo-state.sh")
eq "세 상태가 모두 잡힌다" "3" "$(field _ "$J" "d['file_count']")"

# 서브디렉터리에서 실행해도 결과가 같아야 한다
# (git ls-files --others 는 cwd 하위만 cwd 기준 경로로 낸다)
J2=$(cd "$D/sub" && bash "$S/repo-state.sh")
eq "서브디렉터리에서도 동일" "3" "$(field _ "$J2" "d['file_count']")"
eq " root 파일이 안 빠진다" "True" "$(field _ "$J2" "'root_untracked.txt' in d['changed_files']")"
eq " 경로가 root 기준이다" "True" "$(field _ "$J2" "'sub/sub_untracked.txt' in d['changed_files']")"

echo "pr-context.sh"

# 셸 인젝션 - 인자가 명령으로 해석되면 안 된다
MARK="$TMP/pwned"
bash "$S/pr-context.sh" "44; touch $MARK; #" >/dev/null 2>&1
eq "인젝션 차단" "no" "$([ -e "$MARK" ] && echo yes || echo no)"

for bad in "" "abc" "44abc" '$(id)'; do
out=$(bash "$S/pr-context.sh" "$bad" 2>/dev/null)
eq "잘못된 입력 거부 [${bad:-빈값}]" "True" "$(field _ "$out" "'error' in d")"
done

# gh 조회 실패를 빈 PR 로 성공 처리하지 않는다 (없는 코드에 대한 리뷰 방지).
# 가짜 gh 를 PATH 앞에 두어 인증 상태와 무관하게 각 실패 지점을 재현한다.
FAKE="$TMP/bin"; mkdir -p "$FAKE"
make_gh() { # $1 view 결과(ok|fail) $2 diff 결과(ok|fail)
cat > "$FAKE/gh" <<GHEOF
#!/bin/bash
if [ "\$1" = "pr" ] && [ "\$2" = "diff" ]; then
[ "$2" = "ok" ] || { echo "error: auth required" >&2; exit 1; }
echo "diff --git a/a.ts b/a.ts"; exit 0
fi
if [ "\$1" = "pr" ] && [ "\$2" = "view" ]; then
[ "$1" = "ok" ] || { echo "error: not found" >&2; exit 1; }
echo '{"title":"x","state":"OPEN","files":[{"path":"a.ts"}],"additions":1,"deletions":0,"changedFiles":1,"author":{"login":"u"},"baseRefName":"main","headRefName":"f","body":"","reviewDecision":null,"reviews":[],"comments":[]}'
exit 0
fi
exit 1
GHEOF
chmod +x "$FAKE/gh"
}

make_gh fail fail
out=$(PATH="$FAKE:$PATH" bash "$S/pr-context.sh" 44 2>/dev/null); rc=$?
eq "메타 조회 실패는 error" "True" "$(field _ "$out" "'error' in d")"
eq " exit code 가 0 이 아니다" "1" "$rc"

# 메타는 성공하고 diff 만 실패하는 경우 - 가장 위험한 경로
# (코드 없이 리뷰가 만들어진다)
make_gh ok fail
out=$(PATH="$FAKE:$PATH" bash "$S/pr-context.sh" 44 2>/dev/null); rc=$?
eq "diff 조회 실패도 error" "True" "$(field _ "$out" "'error' in d")"
eq " exit code 가 0 이 아니다" "1" "$rc"

make_gh ok ok
out=$(PATH="$FAKE:$PATH" bash "$S/pr-context.sh" 44 2>/dev/null); rc=$?
eq "둘 다 성공하면 error 없음" "False" "$(field _ "$out" "'error' in d")"
eq " exit code 0" "0" "$rc"
eq " diff 가 담긴다" "True" "$(field _ "$out" "len(d['diff'])>0")"

echo
echo "통과 $PASS · 실패 $FAIL"
[ "$FAIL" -eq 0 ] || exit 1
3 changes: 3 additions & 0 deletions .github/workflows/validate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,9 @@ jobs:
- name: Dangerous-command hook tests (real JSON -> shell -> grep chain)
run: bash .github/scripts/test_danger_hook.sh

- name: Context script tests (real temp repos, no model calls)
run: bash .github/scripts/test_context_scripts.sh

- name: Prompt surface measurement (reported, non-blocking)
run: |
bash .github/scripts/measure.sh HEAD >> "$GITHUB_STEP_SUMMARY" || true
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

A unified Claude Code plugin enabling AI-powered Vibe Coding: idea to production with minimal friction.

**Version**: 0.1.16
**Version**: 0.1.17
**License**: Apache-2.0
**Repository**: https://github.com/wigtn/wigtn-plugins

Expand All @@ -19,6 +19,7 @@ wigtn-plugins/
│ ├── agents/ # 11 agent definitions
│ ├── commands/ # 5 commands (/prd, /screen-spec, /implement, /auto-commit, /review-pr)
│ ├── skills/ # 7 skills (code-review-levels, design-system-reference, handdrawn-diagram, screen-spec, team-memory-protocol, wigtn-ppt)
│ ├── scripts/ # Bundled shell scripts commands call directly
│ └── hooks/ # Hooks configuration
├── CLAUDE.md # This file
├── README.md # English docs
Expand All @@ -33,6 +34,7 @@ plugins/wigtn-plugins/
├── agents/ # Agent definitions (.md)
├── commands/ # User-invocable commands (.md)
├── skills/ # Skills with SKILL.md + reference files
├── scripts/ # Bundled scripts invoked via ${CLAUDE_PLUGIN_ROOT}
└── hooks/hooks.json # Hooks configuration
```

Expand Down
8 changes: 8 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ plugins/wigtn-plugins/
├── agents/ *.md # one file per agent
├── commands/ *.md # one file per slash command
├── skills/ <name>/SKILL.md # one folder per skill
├── scripts/ *.sh # bundled scripts commands call directly
└── hooks/hooks.json
```

Expand Down Expand Up @@ -42,6 +43,13 @@ plugins/wigtn-plugins/
`allowed-tools`. See existing skills for the pattern.
- **Mermaid in generated docs**: quote every label (`["..."]`), use only valid
shapes, no `mindmap` (use `flowchart LR`).
- **Bundled scripts**: a command that needs several read-only lookups (`git`,
`gh`, file probes) calls one script under `scripts/` via
`bash "${CLAUDE_PLUGIN_ROOT}/scripts/<name>.sh"` instead of listing the commands
for the model to run one by one. Return a single JSON object, cap unbounded
fields, and never let a missing tool abort the script. Keep the decision in the
command (the model), not in the script. Add a line telling the model not to run
the individual commands anyway — without it the model re-runs them to confirm.

## Versioning & releases

Expand Down
2 changes: 1 addition & 1 deletion README.cn.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**一个插件。11 个智能体。从创意到通过验证的提交。**

![Version](https://img.shields.io/badge/v0.1.16-Unified_Plugin-FF6B6B?style=for-the-badge)
![Version](https://img.shields.io/badge/v0.1.17-Unified_Plugin-FF6B6B?style=for-the-badge)
![Agents](https://img.shields.io/badge/11-Agents-5A67D8?style=for-the-badge)
![Commands](https://img.shields.io/badge/5-Commands-38B2AC?style=for-the-badge)
![Skills](https://img.shields.io/badge/6-Skills-00D4AA?style=for-the-badge)
Expand Down
2 changes: 1 addition & 1 deletion README.ko.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**하나의 플러그인. 11개 에이전트. 아이디어에서 검증된 커밋까지.**

![Version](https://img.shields.io/badge/v0.1.16-Unified_Plugin-FF6B6B?style=for-the-badge)
![Version](https://img.shields.io/badge/v0.1.17-Unified_Plugin-FF6B6B?style=for-the-badge)
![Agents](https://img.shields.io/badge/11-Agents-5A67D8?style=for-the-badge)
![Commands](https://img.shields.io/badge/5-Commands-38B2AC?style=for-the-badge)
![Skills](https://img.shields.io/badge/6-Skills-00D4AA?style=for-the-badge)
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@

**One plugin. 11 agents. From idea to a verified commit.**

![Version](https://img.shields.io/badge/v0.1.16-Unified_Plugin-FF6B6B?style=for-the-badge)
![Version](https://img.shields.io/badge/v0.1.17-Unified_Plugin-FF6B6B?style=for-the-badge)
![Agents](https://img.shields.io/badge/11-Agents-5A67D8?style=for-the-badge)
![Commands](https://img.shields.io/badge/5-Commands-38B2AC?style=for-the-badge)
![Skills](https://img.shields.io/badge/6-Skills-00D4AA?style=for-the-badge)
Expand Down
2 changes: 1 addition & 1 deletion plugins/wigtn-plugins/.claude-plugin/plugin.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "wigtn-plugins",
"version": "0.1.16",
"version": "0.1.17",
"description": "AI-native Vibe Coding — From idea to Deploy, Zero friction. 11 agents, 5 commands, 7 skills, team-based parallel build, 20 design styles, screen-spec generator (IA / User Flow / Wireframe HTML), WIGTN-brand PPT generator.",
"agents": [
"./agents/ai-agent.md",
Expand Down
37 changes: 20 additions & 17 deletions plugins/wigtn-plugins/commands/auto-commit.md
Original file line number Diff line number Diff line change
Expand Up @@ -157,26 +157,29 @@ fi

### Step 1: 상태 확인 및 브랜치 판단

```bash
git fetch origin # 최신 변경사항 가져오기
current_branch=$(git branch --show-current)
git status
git diff --stat
git diff --stat HEAD # 스테이징 안된 파일 포함
git remote -v
상태 조회는 스크립트가 한 번에 처리한다. **개별 `git`/`gh` 명령을 따로 실행하지 말 것.**

# ⚠️ Stale 검사 — 현재 브랜치에 머지/닫힌 PR이 있는가? (Stale Branch Handling 참조)
gh pr list --head "$current_branch" --state all --json number,state,title --limit 1

# 다른 브랜치 재사용 판단용 open PR 목록
gh pr list --state open --json number,title,headRefName

# PRD/PLAN 파일 확인 (feature name 추출)
# 정본 경로는 `/prd`가 쓰는 docs/todo_plan/ 이다. 루트 fallback은 구버전 산출물용.
ROOT=$(git rev-parse --show-toplevel 2>/dev/null || echo .)
ls "$ROOT"/docs/todo_plan/PLAN_*.md "$ROOT"/PLAN_*.md "$ROOT"/PRD.md 2>/dev/null
```bash
bash "${CLAUDE_PLUGIN_ROOT}/scripts/repo-state.sh"
```

반환 JSON 필드:

| 필드 | 쓰임 |
|---|---|
| `branch` · `on_main` | 브랜치 판단 |
| `staged_stat` · `unstaged_stat` | 변경 규모 |
| `file_count` | 전체 변경 파일 수 (staged + unstaged + untracked) |
| `changed_files` · `files_truncated` | 변경 파일 목록. 50개를 넘으면 잘리고 `files_truncated`가 true |
| `staged_files` · `unstaged_files` · `untracked_files` | 스테이징 상태별 분류 |
| `remotes` | remote 여러 개인지 |
| `head_pr` | 현재 브랜치의 PR (Stale 검사) |
| `stale_branch` | **머지/닫힌 PR이면 `true`** — 새 브랜치 분기 필요 |
| `open_prs` | 다른 브랜치 재사용 판단용 |
| `plan_files` | PRD/PLAN 경로 (feature name 추출) |

`git fetch origin`은 스크립트가 이미 실행한다. 파일 내용이 더 필요하면 그때 Read 한다.

**브랜치 판단:**
1. `--direct` → Direct 모드, 브랜치 판단 스킵
2. 피처 브랜치: Stale 검사 먼저 (머지/닫힌 PR이면 새 브랜치 분기) → Open PR이면 재사용 → PR 없으면 그대로 사용 + 새 PR
Expand Down
14 changes: 13 additions & 1 deletion plugins/wigtn-plugins/commands/implement.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,19 @@ BLOCKED이면 Critical 이슈(번호·위치·영향)를 나열하고 ① PRD

### Step 3~4: 프로젝트 상태 분석

기존 구현 여부, 관련 파일 위치, 사용 중인 패턴·컨벤션을 파악해 **이미 된 부분은 다시 만들지 않는다**. 새 코드는 발견한 컨벤션을 따른다.
레포 상태 조회는 스크립트가 한 번에 처리한다. **`ls`/`find`/`cat package.json`/`git log`/`node -v` 같은 조회 명령을 따로 실행하지 말 것.**

```bash
bash "${CLAUDE_PLUGIN_ROOT}/scripts/repo-context.sh"
```

반환 JSON에 파일 목록·디렉터리 구조·`package.json` 스크립트·**검증 명령(`verify_commands`, 감지 여부는 `verify_detected`)**·PRD/PLAN 경로·런타임 버전·git 상태가 들어 있다. 파일 목록은 tracked·untracked 를 모두 담되 200개에서 자르고, 잘리면 `files_truncated`가 true다(`file_count`는 자르기 전 전체 수).

**`verify_detected`가 true면 검증은 `verify_commands`에 적힌 명령만 쓴다.** `npm test`가 실패한다고 다른 테스트 러너 호출 방식(node --test 변형 등)을 시도하지 말 것 — 명령이 틀린 게 아니라 코드가 틀린 것이다.

**`verify_detected`가 false면 검증을 건너뛰지 말고 직접 찾는다.** 스크립트가 감지하는 것은 npm/pnpm/yarn/bun 스크립트, `pyproject.toml`(pytest·ruff·mypy), `go.mod`, `Cargo.toml`, Makefile 타깃까지다. 그 밖의 빌드 체계는 레포에서 검증 방법을 찾아 실행하고, 정말 없으면 없다는 사실을 보고한다.

이 JSON으로 기존 구현 여부, 관련 파일 위치, 사용 중인 패턴·컨벤션을 파악해 **이미 된 부분은 다시 만들지 않는다**. 파일 내용이 더 필요하면 그때 Read 한다. 새 코드는 발견한 컨벤션을 따른다.

**화면정의서가 있으면 읽는다.** `docs/prd/screens/{feature}/` 가 존재하면
`03-SCREEN-SPEC.md`(화면별 상태·컴포넌트)와 `05-DEV-HANDOFF.md`(FR ↔ 화면 ↔ 컴포넌트 매핑)를
Expand Down
Loading
Loading