ํ๋ก์ ํธ ๋ฌด๊ด(project-agnostic) AI ์ฝ๋ฉ ํ๋ค์ค โ ์ด๋ repo ์๋ ๋๋กญ์ธ. ์คํยทํ์ผยทํ๋กฌํํธ ๋จ๊ณ์ ๋ผ์ด๋ค์ด ๊ท์น์ ๊ฐ์ ํ๊ณ , ๋ชจ๋ ๊ฒฐ๊ณผ๋ฅผ append-only JSONL ๋ก ๋จ๊ธด๋ค.
๐ง ํ๋ค์ค = "AI ์ฝ๋ฉ ๋ณด์๊ฒ์๋" โ AI ์์ด์ ํธ(Claude Code / Codex ๋ฑ)๊ฐ ๋ช ๋ น์ ์คํํ๊ฑฐ๋ ํ์ผ์ ๊ณ ์น๊ธฐ ์ง์ ยท์งํ์ ๊ฒ์ดํธ๋ฅผ ํต๊ณผ์์ผ, ์ํํ ๋์์ ๋ง๊ณ (block) ์ ๊ธ ํ์ผ ์์ ์ ๊ฒฝ๊ณ (warn)ํ๋ฉฐ ๊ฒ์ฆยท์ธ๊ณ๋ฅผ ์๋ํํ๋ค. ESLint ๊ฐ "์ฝ๋ ๋ฌธ๋ฒ"๋ง ๋ณธ๋ค๋ฉด, ํ๋ค์ค๋ ๋ช ๋ น ์คํยทํ์ผ ์ ๊ธยท๊ฒ์ฆยท์ธ์ ์ธ๊ณ๊น์ง ์์ ํ๋ฆ ์ ์ฒด๋ฅผ ๋จ์ํ๋ค.
์ด ์ ์ฅ์๋ dancinlab ์ ๋ชจ๋ repo(edge ยท anima ยท ...)๊ฐ ๊ณต์ ํ๋ ์์ง์ด๋ค. ํ๋ก์ ํธ๋ง๋ค ๋ฌ๋ผ์ง๋ ๊ฒ์ harness.config.json + .harness/*.json(๊ท์น ๋ฐ์ดํฐ)๋ฟ์ด๊ณ , .ts ์์ง ์ฝ๋๋ ์ ๋ถ ๊ณต์ ํ๋ค.
๐ ์ธ์ด ๋ฌด๊ด: ์น๋ฟ ์๋๋ผ Python ยท Rust ยท C/C++ ยท Go ยท Swift ยท hexa ๋ก์ปฌ/๋ชจ๋ฐ์ผ ์ฑ์๋ ์ด๋ค.
init์ด ์คํ์ ๊ฐ์งํด ๊ฒ์ฆ ๋ช ๋ น(cargo/pytest/swift build/...)๊ณผ ๋ค๊ตญ์ด ์ฐํํจํด(# type: ignoreยท#[allow]ยทswiftlint:disable...)์ ์๋ ์ ์ฉ. ์์ง ์คํ์ ๊ฐ๋ฐ๋จธ์ Node 1๊ฐ๋ง ํ์(ํ๊น ๋น๋์ ๋ฌด๊ด). โ docs/languages.md
[ edge repo ]โโโ
[ anima repo ]โโผโโโถ ๊ฐ์ .ts ์์ง (์ด repo)
[ ๋ค๋ฅธ repo ]โโโ โฒใใใใ
โ ๊ฐ์ harness.config.json + .harness/*.json ์ผ๋ก
โโ ๊ท์น๋ง ๋ค๋ฅด๊ฒ ์ฃผ์
| # | ์์น | ์๋ฏธ |
|---|---|---|
| H1 | ์ฑ๊ณต์ ์กฐ์ฉ, ์คํจ๋ ์๋๋ฝ๊ฒ | ํต๊ณผ ์ stdout ์นจ๋ฌต, ์คํจ๋ง stderr (JSON) |
| H2 | ์๋ ์์ ์ ํจ | ์ ์ยท์ฐจ๋จยท๊ฒฝ๊ณ ๋ง. ๊ณ ์น๋ ๊ฑด ์ฌ๋/์์ด์ ํธ |
| H3 | bitter-gate | ์ ๊ท์น ์ถ๊ฐ ์ , ์ ์ฐ๋(dormant) ๊ท์น ๋จผ์ ํ๊ธฐ |
| H4 | config ์ฃผ๋ | ๋ชจ๋ ํ๋ก์ ํธ ์์ฑ๋ ๋ฐ์ดํฐ(JSON)๋ก. ์์ง ์ฝ๋๋ ๋ถ๋ณ |
| H5 | AI-native | ๋ชจ๋ ์ฐ์ถ๋ฌผ JSONL append-only (.harness/logs/*.jsonl) |
harness/
โโโ bin/harness ์คํ ์
๊ตฌ (bash โ tsx ๋ฐํ์ ์๋ํ์)
โโโ cli/index.ts ๋์คํจ์ฒ (harness lint โ lint.ts)
โโโ lib/ ๊ณต์ฉ ๋ถํ
โ โโโ paths.ts repo-root ์๋ํ์ (harness.config.json / .git ์ํฅ ํ์)
โ โโโ config.ts harness.config.json ๋ก๋ + ๊ธฐ๋ณธ๊ฐ ๋จธ์ง
โ โโโ lockdown.ts L0(์ ๊ธ) ํ์ผ ๋ชฉ๋ก (config + ๐ด ๋งํฌ๋ค์ด ๋ธ๋ก ํ์ฑ)
โ โโโ log.ts json.ts exec.ts
โโโ modules/ ๊ธฐ๋ฅ 12์ข
(์๋ ํ)
โโโ config/ ๋ฒ๋ค ๊ธฐ๋ณธ ๊ท์น (๋๋ฉ์ธ-๋ฌด๊ด)
โ โโโ enforcement.json ์คํ/์ฐ๊ธฐ ์ฐจ๋จ ๊ท์น + ํ๋กฌํํธ ํํธ
โ โโโ keywords.json ํ๋กฌํํธ ํค์๋ ํธ๋ฆฌ๊ฑฐ
โ โโโ severity-map.json ์ค๋ฅ severity ๋ถ๋ฅ
โโโ harness.config.example.json
| ๋ช ๋ น | ์ญํ | hook ๋จ๊ณ |
|---|---|---|
pre bash / pre write |
์ฝ๋๋ ๋ฒจ ๊ฐ๋(force-push{blind --force/-f/+refspec ์ฐจ๋จ ยท --force-with-lease๋ ํ์ฉ ยท # force-ok ์์ธ} ยท cloud-raw c11 ยท poll c19 ยท danger{--no-verifyยทreset --hardยทcurl|sh ๋ ์์ always-on ยท rm -rf ๋ฃจํธ(/ยท/*ยท~ยท$HOMEยท*)๋ config dangerGuard.rmRfRoot ํ ๊ธ ยท ๊ธฐ๋ณธ OFF(opt-out)} ยท secret-literal c1 ยท handoff-scatter) โ ๊ทธ๋ค์ config enforcement ์ ๊ท์. ์ฝ๋ ๊ฐ๋๋ config๋ณด๋ค ๋จผ์ ์คํ(profile ํธ์ง ๋ฌด๋ ฅํ ๋ฐฉ์ง) ยท ์ธ๋ผ์ธ # ...-ok/// @secret-ok ๋ง์ปค + dangerGuard.rmRfRoot ํ ๊ธ๋ง ์์ธ |
PreToolUse |
post bash <exit> / post edit <file> |
๊ฒฐ๊ณผ ๊ธฐ๋ก, 0โ exit ๋ผ์ฐํ , L0 ํธ์ง ๊ฒฝ๊ณ | PostToolUse |
prompt <text> |
ํค์๋ ํธ๋ฆฌ๊ฑฐ + ํ๋กฌํํธ ํํธ ์ฃผ์ | UserPromptSubmit |
architecture {inject|show} |
repo-root ARCHITECTURE.json(์ฐ์ )/.md ๋ฅผ ์ปจํ
์คํธ๋ก ์ฃผ์
โ CLAUDE.md ์ฒ๋ผ ์ค๊ณ SSOT ์์ฃผ (80KB ์ด๊ณผ ์ ์ ๋จ, ๋ถ์ฌ ์ ๋ฌด์) |
SessionStart + ๋งค UserPromptSubmit |
claudemd {inject|show} |
repo-root CLAUDE.md(ํ๋ก์ ํธ ๊ท์น)๋ฅผ ๋งค ํด ์ฌ์ฃผ์
โ commons ์ฒ๋ผ salience ์ ์งํด ๊ท์น์ด ๋ฌปํ์ง ์๊ฒ (์ ํ์ <!-- enforce:start/end --> ๋ธ๋ก๋ง, 80KB ์ ๋จ, ๋ถ์ฌ ์ ๋ฌด์) |
UserPromptSubmit |
lint [all|fast] |
staged-L0 + ์ ์ ๋ + CHANGELOG ๋๋ฝ + ์๋ ด ๋๋ฝ ์ฒดํฌ | commit ์ (git pre-commit hook) |
ci [all|fast|list] |
config ์ ๊ฒ์ฆ ๋ช
๋ น ๋ณ๋ ฌ ์คํ (์คํจ 1๊ฐ๋ผ๋ โ exit 1; ์ ์ด๋ฆ verify ๋ณ์นญ ์ ์ง, config ํค๋ verify.checks) |
commit/push ์ |
ci-track <pr|branch> [--watch] [--merge-on-green] [-R owner/repo] |
์๊ฒฉ PR/CI ์ฒดํฌ ์ถ์ โ gh pr checks --json โ pass/fail/pending ์ง๊ณ + ๐ขGREEN/๐ดRED/๐กPENDING/โชNONE verdict(exit 0/2/1/0). --watch = CLI-๋ด๋ถ ํด๋ง์ผ๋ก terminal ๊น์ง ๋๊ธฐ(์์ ์ง gh pr checks|grep + /tmp monitor sleep ๋ฃจํ ๋์ฒด ยท c19), --merge-on-green = ๊ทธ๋ฆฐ์ด๋ฉด ์๋ squash-merge |
merge-on-green ยท CI ๋๊ธฐ ์ |
worktree {scan|gc|guard <cmd>} |
no-pileup ๊ฐ์ โ scan=stranded(๋ฏธ์ปค๋ฐ/๋ฏธํธ์) ์ํฌํธ๋ฆฌ ์ ๋ฐ(exit 1 ๊ฒ์ดํธ) ยท gc=agent ์ํฌํธ๋ฆฌ ์๋ ์๊ฑฐ: [gone] ๋จธ์ง๋ถ + age ๋ฐฑ์คํฑ(HEAD>worktree.maxAgeDays ๊ธฐ๋ณธ 3์ผ โ ๋ฏธํธ์ ํ์ refs/reaped/<br> ๋ณด์กด ํ reap). dirty/locked/recent(<1h)๋ ์ ๋ ์ ๊ฑด๋๋ฆผ. squash-mergeยทno-push ๋ก [gone] ์ ๋จ๋ fleet ์ํฌํธ๋ฆฌ ๋์ ์ ๋ง์ |
SessionStart |
errors {route|list|drain_check|mark_fixed} |
์ค๋ฅ severity ๋ถ๋ฅ + ํ | ์์ |
ledger {register|complete|list|gc|dup_check} |
๋ฐฑ๊ทธ๋ผ์ด๋ ์์ด์ ํธ ์์ ๋ฑ๋ก(์ค๋ณต ๋ฐฉ์ง) | Agent ์ /ํ |
bitter-gate audit [window] |
๊ท์น ํํธ ๋น๋ โ dormant ๊ท์น ํ๊ธฐ ๊ฒํ | ๊ท์น ์ถ๊ฐ ์ |
audit [full|summary|json] |
6์ถ ์๊ฐ ์ค์ฝ์ด์นด๋ (/60) | ์ฃผ๊ธฐ์ |
gc [scan|drift] |
๊ฐ์ด๋ ๋งํฌ๋ค์ด์ ๊นจ์ง ๋งํฌ ํ์ง | ์ฃผ๊ธฐ์ |
folders [scan|scaffold <dir>] |
์๋ธํด๋๋ณ CLAUDE.md ๋๋ฝ ํ์ง + ํ ํ๋ฆฟ ์์ฑ (ํธ์ง ์ ์๋ ๋์ง) | ์ฃผ๊ธฐ์ /์์ ์ค |
convergence {status|recompute|by-category|scan|due-check} |
incident ์๋ ด ์ถ์ + scan = ์ธ๋ผ์ธ @convergence ์ฌ๋ฐ๋ฐฉ์ง ๋ง์ปค(c1) ๊ฒ์ฆ(ํ์ํค stateยทid + state enum) โ harness lint ๊ฐ ํธ์ถํด ๋ถ๋์ CONVERGENCE-MALFORMED(block)๋ก ์ปค๋ฐ ์ฐจ๋จ. ์ฌ๋ฐ ์ ํธ(์ฌ๋ฐยทstaleยทregressionยทagain...)๋ keyword ํธ๋ฆฌ๊ฑฐ๊ฐ โฆCONVERGENCE-DUE id=...โง ์บก์ฒ ํ ํฐ+๋ถ์ฑ ์ถ๋ ฅ โ well-formed ๋ง์ปค ์์ฑ ์ post-edit ์๋ ํด์, ๋ฏธํด์๋ฉด due-check ๊ฐ Stop ์์ 1ํ ํ๊ธฐ(warn-only) |
๋ฒ๊ทธ ์์ ํ ยท commit |
sync {run|diff} |
(์ ํ) repo ์์ฒด ๊ณต์ ํ์ผ sync ์คํฌ๋ฆฝํธ ์คํ | ๊ณต์ ํ์ผ ๋ณ๊ฒฝ ํ |
pool {list|add|rm|on <h> <cmd>|status|specs [h]} |
ํธ์คํธ ๋ก์คํฐ + ์๊ฒฉ ์คํ. shared:false ํธ์คํธ๋ ์ ํ ํธ์คํธ โ allow ํ๋ก์ ํธ ์ปจํ
์คํธ ๋ฐ์์ on ์ฐจ๋จ(๊ณต์ฉ ์ปดํจํธ๋ก ๋ชป ์). on ์ ssh ๋ฅผ ์ง์ spawn(argv)ํด cmd ์ $/$(...) ๊ฐ ๋ก์ปฌ์ด ์๋๋ผ ์๊ฒฉ ์
ธ์์ ์ ๊ฐ๋จ. specs ๋ ํธ์คํธ๋ณ ์ฝ์ด/๋ฉ๋ชจ๋ฆฌ/GPU ๋ฅผ ssh ํ๋ก๋ธํด ๋ก์คํฐ์ ์บ์(listยทstatus ์ ใ12c ยท 30G ยท GPU:...ใ ์ธ๋ผ์ธ ํ๊ธฐ) โ ์ ํ ํธ์คํธ๋ ํ๋ก๋ธํ์ง ์์ |
์๊ฒฉ ์คํ ยท ์์ ํ์ธ ์ |
mem-guard {status|check|install|uninstall} |
OOM(๋ฉ๋ชจ๋ฆฌ๋ถ์กฑ ๊ฐ์ ์ข
๋ฃ) ์๋ฐฉ. PreToolUse ํ๋ฆฌํ๋ผ์ดํธ(์์): background-spawn(... &/nohup/disown/setsid) ์ง์ ์์คํ
free RAM ์ vm_stat ๋ก ์ฝ์ด warnPct(๊ธฐ๋ณธ 15%) ์ดํ warn ยท blockPct(๊ธฐ๋ณธ 0=off) ์ดํ spawn block โ ๋ณ๋ ฌ fan-out ๋์ ์ด macOS jetsam ์ ํธ๋ฆฌ๊ฑฐํ๋ ๊ทผ๋ณธ์์ธ ์ฐจ๋จ. launchd ์์น๋
(install ๋ก opt-in): watchdogIntervalSec(45s)๋ง๋ค ๋ฉ๋ชจ๋ฆฌ ํด๋ง โ ๋ฎ์ผ๋ฉด macOS ์๋ฆผ(5๋ถ throttle). ์๋ฆผ ์ ์ฉ ยท ํ๋ก์ธ์ค kill ์์ ยท ๋ค์ค Claude ์ธ์
๋์ ์ ๋ณด๋ ์ ์ผํ ์ธต |
๋งฅ์ด ์๊พธ ์ฃฝ์ ๋ ยท OOM ์๋ฐฉ |
pod / dojo [<slug>] |
GPU ํด๋ผ์ฐ๋ ๋ฐ๋ถ + dojo ํ์ต์ก ์ค์บํด๋. dojo ๊ธฐ๋ณธ ์คํ์ config.dojo(์์ง ๋ฌดํ๋์ฝ๋ฉ)๊ฐ ์ด๋ฐ โ ์ค์ ์ hexa dojo <delegate> ์์. ๋ค์ค๋ ๋ฐฐ์น๋ hexa cloud fire-shards (์์ launcher.sh ๋ CLOUD-HANDROLLED-FANOUT warn ์ผ๋ก ๋ฆฌ๋ค์ด๋ ํธ) |
GPU ๋์คํจ์น ยท ํ์ต ์ค์บํด๋ |
imagine <prompt-file> <out.{png|mp4}> [-i img] |
AI ์ด๋ฏธ์ง+์์ ์์ฑ โ ์ถ๋ ฅ ํ์ฅ์๋ก ๋ถ๊ธฐ: ์ด๋ฏธ์ง(.png)=fal openai/gpt-image-2(image2 ํ) ยท ์์(.mp4/.mov)=fal Seedance 2.0 ํ โ -i ์์ผ๋ฉด .../text-to-video, -i <์ด๋ฏธ์ง> ๋ฉด .../image-to-video(์ด๋ฏธ์ง ์ ๋๋ฉ์ดํธ). ํค๋ secret get ๊ฒฝ์ ยท ํ๋กฌํํธ๋ FILE ๋ก ยท -m ๋ก override |
ํ์งยทfigureยท์์ ์์ฑ ์ |
paper {new|build|cover|list} |
demiurge ํ์ฐ์ค ๋
ผ๋ฌธ ๋๊ตฌ โ new ์ค์บํด๋(์ด๋ชจ์ง ์ ๋ชฉ ยท g5 tier-badge ๋์คํฌ ยท TikZ+pgfplots ยท fal.ai ํ์ง include) โ imagine ํ์ง โ build(xelatex+bibteรใฐใค3 ยท g51 โฅ10p ๊ฒ์ดํธ). ์ ์กฐ๋ฆฝ ๊ท์จ์ ๋๊ตฌ๋ก ๋ฐ์ |
๋ ผ๋ฌธ ์์ฑยท์ปดํ์ผ ์ |
| heartbeat (c22) | live ์ฅ๊ธฐ-์งํ๊ฑด(podยท๋ฐฑ๊ทธ๋ผ์ด๋ ์์ด์ ํธ)์ poll.maxSilenceSec(๊ธฐ๋ณธ 10๋ถ) ๋๊ฒ ์ ๋ณด๋ฉด post bash/ing inject ์์ ๋ฐฉ์น ๊ฒฝ๊ณ . c19(๊ณผ๋คํด๋ง ์ฐจ๋จ)์ ๋ฐ๋ โ ๋ฏธํด๋ง/idle-burn ๋ฐฉ์ง |
PostToolUse ยท SessionStart |
commands/*.md โ ์ ์ฒด ์ฌ์ฉ์-๋๋ฉด ๋ช
๋ น์ด bare /cmd ์ฌ๋์ ๋ช
๋ น์ผ๋ก ๋
ธ์ถ๋๋ค(/paperยท/imagineยท
/pr-cycleยท/sbsยท/fleetยท/fleet-labยท/ingยท/ciยท/kickยท/poll ...). ๊ฐ .md ๋ ํ๋ฐํธ๋งคํฐ(description +
Triggers ์์ฐ์ด๊ตฌ + argument-hint + allowed-tools: Bash)์ !harness $ARGUMENTS`` ๋ณธ๋ฌธ์ ์์
์์์ โ Claude Code ๊ฐ description/Triggers ๋ก ์ธ์งํ๋ค(ํ๊ตญ์ดยท์์ด ํธ๋ฆฌ๊ฑฐ ์์ชฝ).
๋
ธ์ถ ๊ฒฝ๋ก = harness shadow (bare ยท user-scope): Claude Code ๋ ํ๋ฌ๊ทธ์ธ ๋ช
๋ น์ ๋ฌด์กฐ๊ฑด /plugin:cmd
๋ค์์คํ์ด์ค๋ก ๋์ฐ๋ฏ๋ก, ๊ทธ๋๋ก ๋๋ฉด harness shadow ์ bare /fleet ์ ํ๋ฌ๊ทธ์ธ /harness:fleet ๊ฐ picker ์
2์ค๋ก ์ค๋ณต๋๋ค. ๊ทธ๋์ .claude-plugin/plugin.json ์ด commands: [] (๊ธฐ๋ณธ commands/ ์ค์บ์ ๋น ๋ชฉ๋ก์ผ๋ก
๋์ฒด)๋ก ํ๋ฌ๊ทธ์ธ ๋ช
๋ น ๋ฑ๋ก์ ๋๊ณ , ์ฌ๋์ ๋
ธ์ถ์ harness shadow ๊ฐ commands/*.md ๋ฅผ ~/.claude/commands/ ์
bare /cmd ์์์๋ก ๋ฏธ๋ฌํ๋ ๋จ์ผ ๊ฒฝ๋ก๋ง ์ด๋ค(๋ง์ปค ์ถ์ ยท ์์ ์์ฑํ ๋๋ช
ํ์ผ์ ๋ณด์กด ยท shadow remove ๋ก ์ ๋ฆฌ)
โ picker ์ bare 1์ค. SHADOW_MARKER ์ถ์ ์ฃผ์์ frontmatter ๋ซ๋ --- ๋ค์ ์ฝ์
ํ๋ค โ Claude Code ๋ --- ๊ฐ
1ํ์ผ ๋๋ง description: ์ ์ฝ์ผ๋ฏ๋ก, ์์ ๋ถ์ด๋ฉด picker ๊ฐ ๋ง์ปค ์ฃผ์์ ์ค๋ช
์ผ๋ก ํ์ํ๋ค.
์๊ธฐ์๊ฒฐ(self-contained) ํ๋ฌ๊ทธ์ธ ยท ํ๋ก์ ํธ ๋ฌด๊ด: marketplace source: "." ๋ผ repo ๋ฃจํธ๊ฐ ๊ณง ํ๋ฌ๊ทธ์ธ โ
ํ
๋ฟ ์๋๋ผ harness CLI ๋ณธ์ฒด(bin/ยทcli/ยทlib/ยทmodules/ยทconfig/ยทcommands/)๊น์ง ํ ๋ฉ์ด๋ฆฌ๋ก ์ค๋ฆฐ๋ค
(commands/ ๋ shadow ๊ฐ ๋ฏธ๋ฌํ๋ SOURCE ๋ก ์ค๋ฆด ๋ฟ, ํ๋ฌ๊ทธ์ธ ๋ช
๋ น์ผ๋ก ๋ก๋๋์ง ์๋๋ค). ํ
์
${CLAUDE_PLUGIN_ROOT}/bin/harness(ํ๋ฌ๊ทธ์ธ ์๊ธฐ ๋ฒ๋ค)๋ฅผ ์คํํ๋ฏ๋ก, /plugin update + ๋ฆฌ๋ก๋ ํ ๋ฒ์
CLIยทhooks ๊ฐ ์ต์ ํ๋๋ค โ ํ๋ก์ ํธ๋ง๋ค ๋ณต์ฌยท๊ฐฑ์ ๋, ๋ณ๋ harness self-update ๋ ๋ถํ์(์ฌ๋์๋ ๊ฐฑ์ ํ
harness shadow ์ฌ์คํ์ผ๋ก ๋ฐ์). (์ ์ญ harness on PATH ๋ ํด๋ฐฑ.) ์ฌ์์ฑ๊ธฐ = _tools/gen_commands.py.
hook-๋ด๋ถ ์ ์ฉ(pre/post/prompt)์ ์ฌ๋์๋ก ๋
ธ์ถํ์ง ์๋๋ค.
ing add์์ ํ ์คํธ์ ์ ธ ํน์๋ฌธ์(๊ดํธยท๋ฐ์ดํยท$ยทโ)๊ฐ ์์ผ๋ฉด ์ฌ๋์$ARGUMENTS๋ฌด์ธ์ฉ ํ์ฅ์ด ๊นจ์ง๋ค โprintf '%s' "<text>" \| harness ing add --stdin(STDIN ๊ฒฝ๋ก)๋ก ์์ ํ๊ฒ ๋ฑ๋กํ๋ค(--to <repo>์๋ ํธํ).
๋จธ์ ์ ํ๋ค์ค๋ฅผ ๊ณต์ฉ ๋ช ๋ น์ผ๋ก ๊น๊ณ ์ ์ญ ํ ๊น์ง ํ ๋ฐฉ์ ๋ฐฐ์ ํ๋ค(ํน์ repo ๋จ๋ ์ธํ ์๋). ๋ถํธ์คํธ๋ฉ one-liner:
curl -fsSL https://raw.githubusercontent.com/dancinlab/harness/main/scripts/install.sh | bashํ๋ ์ผ(๋ฉฑ๋ฑ โ ์ฌ์คํ = ์ต์ ์ผ๋ก ๊ฐฑ์ ):
โฌ clone dancinlab/harness โ ~/.harness/cli
๐ link harness ๋ํผ โ ~/.local/bin/harness (PATH ์๋ด)
๐ช hooks harness install-hooks --global (๋ชจ๋ Claude Code ์ธ์
์ ๊ฐ๋/์ฃผ์
)
์ดํ ๊ฐฑ์ ์ harness self-update, ์ด๋ฏธ ํ๋ค์ค๊ฐ ๊น๋ ค ์์ผ๋ฉด harness install ๋ก๋ ๋์ผ ๋์. ํ
์์ด ๊น๋ ค๋ฉด --no-hooks, ๋ฏธ๋ฆฌ๋ณด๊ธฐ๋ --dry-run. ๊ทธ๋ค์ ํน์ repo ์ ์ ์ฉํ๋ ค๋ฉด ์๋ 1ยท2 ๋จ๊ณ(๋๋ harness init).
cd your-repo git submodule add https://github.com/dancinlab/harness .harness-engine # ๋๋ ๊ทธ๋ฅ clone / vendor ํด๋ ๋จ
bash .harness-engine/bin/harness init --hooks
์ด ํ ์ค์ด ๋ง๋ ๋ค (๊ธฐ์กด ํ์ผ์ ๋ณด์กด, --force ๋ง ์์ธ ยท --dry-run ์ผ๋ก ๋ฏธ๋ฆฌ๋ณด๊ธฐ):
โ harness.config.json ํ๋ก์ ํธ๋ช
์๋๊ฐ์ง
โ .harness/enforcement.json ๋ฒ๋ค ๊ธฐ๋ณธ ๊ท์น ๋ณต์ฌ (repo ๊ฐ ์์ )
โ .harness/keywords.json
โ .harness/severity-map.json
โ .gitignore ๋ก๊ทธ ๋ฌด์ ์ถ๊ฐ
โ scripts/harness ์์ ๋ํผ
โ .claude/settings.json hook ๋ฐฐ์ (--hooks ์ผ ๋)
์์ฑ ํ harness.config.json ์ verify.checks ยท lockdown.files ๋ง repo ์ ๋ง๊ฒ ์ฑ์ฐ๋ฉด ๋๋ค.
์๋ ์ค์ ๋ ๊ฐ๋ฅ:
.harness/*.json์ ๋์ง ์์ผ๋ฉด ๋ฒ๋ค ๊ธฐ๋ณธ ๊ท์น(config/*.json)์ด ์๋ ์ ์ฉ๋๋ค.์ ๊ฑฐ:
harness uninstall(์ฃผ์ ๋ฌผ๋ง ์ ๊ฑฐ, ์ฌ์ฉ์ ์ฝํ ์ธ ๋ณด์กด ยท--dry-run๋ฏธ๋ฆฌ๋ณด๊ธฐ). ์์ธ docs/install.md.
bash .harness-engine/bin/harness audit bash .harness-engine/bin/harness ci list
.claude/settings.json:
{
"hooks": {
"PreToolUse": [
{ "matcher": "Bash", "hooks": [{ "type": "command", "command": "CLAUDE_TOOL_INPUT=\"$CLAUDE_TOOL_INPUT\" bash .harness-engine/bin/harness pre bash" }] },
{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "CLAUDE_TOOL_INPUT=\"$CLAUDE_TOOL_INPUT\" bash .harness-engine/bin/harness pre write" }] }
],
"PostToolUse": [
{ "matcher": "Write|Edit", "hooks": [{ "type": "command", "command": "bash .harness-engine/bin/harness post edit \"$CLAUDE_FILE_PATH\"" }] }
],
"UserPromptSubmit": [
{ "hooks": [{ "type": "command", "command": "bash .harness-engine/bin/harness prompt \"$CLAUDE_USER_PROMPT\"" }] }
]
}
}ํ๊ฒฝ๋ณ์ ์ด๋ฆ(
CLAUDE_TOOL_INPUT๋ฑ)์ ๋ฐํ์ ๋ฒ์ ์ ๋ฐ๋ผ ๋ค๋ฅผ ์ ์๋ค. ํ๋ค์ค๋CLAUDE_TOOL_INPUT์CODEX_TOOL_INPUT๋ ๋ค ์ฝ๋๋ค. JSON ํ์:{"command":"...","file_path":"...","content":"..."}.
๐ก
harness install-hooks [--global|--repo]๋ hook ๋ฐฐ์ ๊ณผ ํจ๊ปsettings.json์env์CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1๋ ์ธํ ํ๋ค โ ๋ฐฑ๊ทธ๋ผ์ด๋ subagent ์SendMessage(agent-teams)๋ฅผ ๊ธฐ๋ณธ ํ์ฑํ. ์ด๋ฏธ ๊ทธ ํค๊ฐ ์์ผ๋ฉด ์ฌ์ฉ์ ๊ฐ์ ๋ณด์กดํ๋ค(๋ฎ์ด์ฐ์ง ์์). ๋๋ ค๋ฉด ๊ทธ ํค๋ฅผ"0"์ผ๋ก ๋๋ฉด ๋๋ค.
์ฌ์ฉ์ ํ๋กฌํํธ โโถ [prompt] ํค์๋ ํธ๋ฆฌ๊ฑฐ + ํํธ ์ฃผ์
์์ด์ ํธ Bash โโถ [pre bash] โ ๋งค์นญ? โโถ block(stdout JSON) / warn(stderr) / ํต๊ณผ(์นจ๋ฌต)
โ
โผ (์คํ ํ)
[post bash <exit>] โ 0โ exit โโถ errors ํ ๋ผ์ฐํ
์์ด์ ํธ Edit โโถ [pre write] โ ๊ฒฝ๋ก/๋ด์ฉ ๊ท์น โโถ block/warn
[post edit <file>] โ L0? โโถ ๊ฒฝ๊ณ
์ปค๋ฐ ์ โโถ [lint] + [verify]
์ธ์
์ข
๋ฃ โโถ [ing]
๋ชจ๋ ๋จ๊ณ๋ .harness/logs/*.jsonl ์ ํ ์ค์ฉ ์์ธ๋ค โ audit ์ด ์ด๋ฅผ ์ฝ์ด ๊ฑด๊ฐ๋๋ฅผ ์ ์ํํ๋ค.
- docs/languages.md โ ์ธ์ด/ํ๋ซํผ ๋ฒ์ฉ์ฑ (PythonยทRustยทCยทGoยทSwiftยทhexa ํ๋ฆฌ์ + Node ๋ฐํ์ ์๊ตฌ)
- ARCHITECTURE.json โ ํ๋ค์ค ์ํคํ
์ฒ ํธ๋ฆฌ SSOT (์ปฌ๋ผํ ๋
ธ๋: ์ด๋ฆยท์ญํ ยท๊ตฌ๋ถยท์์ธ). ์ฌ๋์ฉ ๋ทฐ์ด๋ ARCHITECTURE.html โ ๋ก์ปฌ์
python3 serve.py(์๋ฒ + ๋ธ๋ผ์ฐ์ ์๋ ์คํ), ์๊ฒฉ์ raw.githack.com / GitHub Pages - docs/install.md โ repo ํตํฉ ์์ธ (submodule / vendor / ๋ฉํฐ repo)
- docs/extending.md โ ๊ท์น ์ถ๊ฐ, ๋๋ฉ์ธ ๋ชจ๋ ํ์ฅ ํจํด
์ด repo ์์ฒด๊ฐ ํ๋ค์ค๋ฅผ ์ด๋ค(dogfooding) โ harness.config.json + .claude/settings.json self hooks + pre-commit bin/harness lint. ์ฝ์ด(.ts) ๋ณ๊ฒฝ ์ CHANGELOG ๋์ ๊ฐฑ์ ์ด ๊ฐ์ ๋๊ณ , ๋ฒ๋ค enforcement(root-causeยทsecretยทforce-push)๊ฐ ์๊ธฐ ์ฝ๋์๋ ์ ์ฉ๋๋ค. ๋จ protectedBranches ๋ฏธ์ค์ ์ผ๋ก ์๊ธฐ ๊ฐ๋ฐ ํ๋ฆ(main ์ง์ push)์ ๋ง์ง ์๋๋ค.
๋งค ์ฌ์ดํด(harness pr-cycle)์ doc-gate ๋ ์๋ฏธ์๋ ๋ณ๊ฒฝ์ ๋ํด CHANGELOG.md(append) + (์กด์ฌ ์) ARCHITECTURE.mdยทREADME.md ํํํ๋ฅผ ์๊ตฌํ๋ค โ ์
์ค ๋ฏธ๊ฐฑ์ ์ด ์์ผ๋ฉด ๋จธ์ง๋ฅผ ๊ฑฐ๋ถํ๋ค(--no-doc ๋ ์ง์ง ๋ฌธ์ ๋ถํ์ํ ๋๋ง). ์ด README ๋ ๊ทธ ๋์์ด๋ฏ๋ก ๋งค ์ฌ์ดํด ์ต์ ์ํ๋ก ์ ์ง๋๋ค. (commons c12)
harness pr-cycle ์ ๊ฒ์ฆ๋ ๋จธ์ง ์งํ ๋ก์ปฌ base(main) ๋ฅผ origin/base ๋ก ff-sync ํ๋ค(feature ๋ธ๋์น์์ git fetch origin <base>:<base> โ checkout ์ ํ ์์ด ๋ก์ปฌ main ๋ค์ฒ์ง ๋ฐฉ์ง, non-ff ๊ฑฐ๋ถ=์์ ). origin ๋ง ๊ฐฑ์ ํ๊ณ ๋ก์ปฌ main ์ ๋ฐฉ์นํ์ง ์์ผ๋ฏ๋ก, ๋ค์ ์์
๋ธ๋์น๋ ํญ์ ์ต์ base ์์ ๋ถ๊ธฐ๋๋ค. (commons c12)
๊ฐ์ doc-gate ๊ฐ pre-commit harness lint ์์๋ ๋ฐํํ๋ค โ pr-cycle ์ ๊ฑฐ์น์ง ์๋ ์์
์ด๋ผ๋, ์๋ฏธ์๋ ์ฝ๋ ๋ณ๊ฒฝ์ด staged ์ธ๋ฐ CHANGELOG / (์กด์ฌ ์) ARCHITECTUREยทREADME ๊ฐ ๊ฐ์ด staged ์ ๋์ผ๋ฉด commit ์ ์ฐจ๋จํ๋ค(CHANGELOG-MISSINGยทARCHITECTURE-MISSINGยทREADME-MISSING, ๋ชจ๋ block). ์ฆ "๋ชจ๋ ์์
์ดํ" ๋ฌธ์ ํํํ๊ฐ ๊ฐ์ ๋๋ค. ์ง์ง ๋ฌธ์ ๋ถํ์ํ ๋ณ๊ฒฝ๋ง git commit --no-verify.
MIT