์ด ๋ฌธ์„œ๋Š” 2ํŽธ Hook System์˜ ํ•˜์œ„ ๊ฐœ๋…์œผ๋กœ, 18๊ฐœ Hook ์Šคํฌ๋ฆฝํŠธ ์ค‘ skill-injector.mjs๋ฅผ ๋‹ค๋ฃฌ๋‹ค.


  • skill-injector.mjs๋Š” UserPromptSubmit Hook์—์„œ ์‚ฌ์šฉ์ž ํ”„๋กฌํ”„ํŠธ๋ฅผ ํ•™์Šต๋œ Skill ํŒŒ์ผ์˜ trigger์™€ ๋งค์นญํ•˜์—ฌ, ๊ด€๋ จ Skill ๋‚ด์šฉ์„ Claude context์— ์ฃผ์ž…ํ•˜๋Š” ์Šคํฌ๋ฆฝํŠธ
  • keyword-detector.mjs๊ฐ€ 14๊ฐœ ๋งค์ง ํ‚ค์›Œ๋“œ๋ฅผ ํ•˜๋“œ์ฝ”๋”ฉ์œผ๋กœ ๊ฐ์ง€ํ•˜๋Š” ๋ฐ˜๋ฉด, skill-injector.mjs๋Š” ์‚ฌ์šฉ์ž๊ฐ€ ํ•™์Šต์‹œํ‚จ Skill ํŒŒ์ผ์˜ trigger๋ฅผ ๋™์ ์œผ๋กœ ๋งค์นญ
  • 3-tier ๋””๋ ‰ํ† ๋ฆฌ์—์„œ Skill์„ ํƒ์ƒ‰ํ•˜๊ณ , ์„ธ์…˜ ์บ์‹ฑ์œผ๋กœ ์ค‘๋ณต ์ฃผ์ž…์„ ๋ฐฉ์ง€ํ•˜๋ฉฐ, ์ตœ๋Œ€ 5๊ฐœ๊นŒ์ง€ ๋งค์นญ

์ž‘์„ฑ์ผ: 2026-03-15 ์†Œ์Šค: skill-injector.mjs

ํ•ด๋‹น ๊ฐœ๋…์ด ํ•„์š”ํ•œ ์ด์œ 

  • keyword-detector.mjs๋Š” 14๊ฐœ ํ‚ค์›Œ๋“œ๋งŒ ๊ฐ์ง€ ๊ฐ€๋Šฅํ•˜๋‹ค. ์‚ฌ์šฉ์ž๊ฐ€ ์ž์ฒด Skill์„ ๋งŒ๋“ค์–ด๋„ ํ‚ค์›Œ๋“œ ๊ฐ์ง€์—๋Š” ๋ฐ˜์˜๋˜์ง€ ์•Š๋Š”๋‹ค
  • skill-injector.mjs๋Š” .md ํŒŒ์ผ์˜ YAML frontmatter์— triggers๋ฅผ ์ •์˜ํ•˜๋ฉด, ํ•ด๋‹น trigger๊ฐ€ ํ”„๋กฌํ”„ํŠธ์— ํฌํ•จ๋  ๋•Œ ์ž๋™์œผ๋กœ Skill ๋‚ด์šฉ์„ ์ฃผ์ž…ํ•œ๋‹ค
  • ์ด ์Šคํฌ๋ฆฝํŠธ๋Š” hooks.json์—์„œ UserPromptSubmit ์ด๋ฒคํŠธ์˜ ๋‘ ๋ฒˆ์งธ ์Šคํฌ๋ฆฝํŠธ๋กœ ๋“ฑ๋ก๋˜์–ด ์žˆ์–ด, keyword-detector.mjs ์ดํ›„์— ์‹คํ–‰๋œ๋‹ค

AS-IS

sequenceDiagram
    autonumber
    participant U as User
    participant CC as Claude Code

    U->>CC: "PR ์ค€๋น„ํ•ด์ค˜"
    CC->>CC: ํ•™์Šต๋œ Skill ์กด์žฌํ•˜์ง€๋งŒ ๋งค์นญ ์ˆ˜๋‹จ ์—†์Œ
    CC-->>U: ์ผ๋ฐ˜ ์‘๋‹ต (Skill ๋ฌด์‹œ)

TO-BE

sequenceDiagram
    autonumber
    participant U as User
    participant CC as Claude Code
    box OMC
    participant SI as skill-injector.mjs
    end

    U->>CC: "PR ์ค€๋น„ํ•ด์ค˜"
    CC->>SI: stdin: { prompt, cwd, session_id }
    SI->>SI: 3-tier ๋””๋ ‰ํ† ๋ฆฌ์—์„œ .md Skill ํƒ์ƒ‰
    SI->>SI: trigger "PR ์ค€๋น„" ๋งค์นญ (score: 10)
    SI-->>CC: { additionalContext: "<mnemosyne>Skill ๋‚ด์šฉ</mnemosyne>" }
    CC->>CC: ์ฃผ์ž…๋œ Skill ์ง€์‹์œผ๋กœ PR ์ค€๋น„ ์ˆ˜ํ–‰

keyword-detector.mjs์™€์˜ ์—ญํ•  ๋ถ„๋‹ด

sequenceDiagram
    autonumber
    participant U as User
    participant CC as Claude Code
    box OMC
    participant KD as keyword-detector.mjs
    participant SI as skill-injector.mjs
    end

    U->>CC: ํ”„๋กฌํ”„ํŠธ ์ž…๋ ฅ
    CC->>KD: UserPromptSubmit (1st, timeout 5s)
    KD->>KD: 14๊ฐœ ๋งค์ง ํ‚ค์›Œ๋“œ ๋งค์นญ
    KD-->>CC: Skill ํ˜ธ์ถœ ์ง€์‹œ or ๋ฉ”์‹œ์ง€ ์ฃผ์ž…
    CC->>SI: UserPromptSubmit (2nd, timeout 3s)
    SI->>SI: ํ•™์Šต๋œ Skill trigger ๋งค์นญ
    SI-->>CC: ๋งค์นญ๋œ Skill ๋‚ด์šฉ context ์ฃผ์ž…
๊ตฌ๋ถ„keyword-detector.mjsskill-injector.mjs
๋งค์นญ ๋Œ€์ƒ14๊ฐœ ํ•˜๋“œ์ฝ”๋”ฉ ํ‚ค์›Œ๋“œํ•™์Šต๋œ Skill ํŒŒ์ผ์˜ trigger
๋งค์นญ ๋ฐฉ์‹์ •๊ทœ์‹ ํŒจํ„ดtrigger ํ‚ค์›Œ๋“œ ํฌํ•จ ์—ฌ๋ถ€ (์ ์ˆ˜์ œ)
์ถœ๋ ฅ ๋ฐฉ์‹Skill tool ํ˜ธ์ถœ ์ง€์‹œ or ๋ฉ”์‹œ์ง€ ์ง์ ‘ ์ฃผ์ž…<mnemosyne> ํƒœ๊ทธ๋กœ Skill ๋‚ด์šฉ ์ฃผ์ž…
์‹คํ–‰ ์ˆœ์„œ1st (5s)2nd (3s)

3-tier Skill ๋””๋ ‰ํ† ๋ฆฌ

graph TB
    subgraph Tier1["Tier 1: ํ”„๋กœ์ ํŠธ ๋ ˆ๋ฒจ"]
        P[".omc/skills/*.md"]
    end

    subgraph Tier2["Tier 2: ๊ธ€๋กœ๋ฒŒ ๋ ˆ๋ฒจ"]
        G["~/.omc/skills/*.md"]
    end

    subgraph Tier3["Tier 3: ๋ ˆ๊ฑฐ์‹œ"]
        L["~/.claude/skills/omc-learned/*.md"]
    end

    SI["skill-injector.mjs"] --> P
    SI --> G
    SI --> L

    P -->|"์šฐ์„ "| Result["๋งค์นญ ํ›„๋ณด"]
    G --> Result
    L --> Result
  • ํ”„๋กœ์ ํŠธ ๋ ˆ๋ฒจ์ด ์šฐ์„  โ€” ๊ฐ™์€ Skill์ด ํ”„๋กœ์ ํŠธ์™€ ๊ธ€๋กœ๋ฒŒ์— ๋ชจ๋‘ ์žˆ์œผ๋ฉด ํ”„๋กœ์ ํŠธ ๋ฒ„์ „์„ ์‚ฌ์šฉ
  • realpathSync()๋กœ ์‹ฌ๋ณผ๋ฆญ ๋งํฌ๋ฅผ ํ•ด๊ฒฐํ•˜์—ฌ ์ค‘๋ณต ์ œ๊ฑฐ

Skill ํŒŒ์ผ ๊ตฌ์กฐ์™€ ๋งค์นญ ๋กœ์ง

YAML frontmatter์˜ triggers

---
name: pr-creator
triggers:
  - PR ์ค€๋น„
  - PR ready
  - ์ปค๋ฐ‹ํ•˜๊ณ  ํ‘ธ์‹œ
---
 
Skill ๋ณธ๋ฌธ ๋‚ด์šฉ...

parseSkillFrontmatterFallback()๊ฐ€ --- ๊ตฌ๋ถ„์ž ์‚ฌ์ด์—์„œ name๊ณผ triggers ๋ฐฐ์—ด์„ ์ถ”์ถœํ•œ๋‹ค.

์Šค์ฝ”์–ด๋ง ๊ธฐ๋ฐ˜ ๋งค์นญ

graph LR
    Prompt["์‚ฌ์šฉ์ž ํ”„๋กฌํ”„ํŠธ"] --> Lower["์†Œ๋ฌธ์ž ๋ณ€ํ™˜"]
    Lower --> Check{"๊ฐ Skill์˜ trigger ์ˆœํšŒ"}
    Check -->|"trigger ํฌํ•จ"| Score["+10์ "]
    Check -->|"๋ฏธํฌํ•จ"| Skip["์Šคํ‚ต"]
    Score --> Sort["์ ์ˆ˜ ๋‚ด๋ฆผ์ฐจ์ˆœ ์ •๋ ฌ"]
    Sort --> Limit["์ƒ์œ„ 5๊ฐœ ๋ฐ˜ํ™˜"]
  • ํ”„๋กฌํ”„ํŠธ๋ฅผ ์†Œ๋ฌธ์ž๋กœ ๋ณ€ํ™˜ ํ›„, ๊ฐ Skill ํŒŒ์ผ์˜ trigger๋ฅผ ์ˆœํšŒํ•˜๋ฉฐ ํฌํ•จ ์—ฌ๋ถ€๋ฅผ ๊ฒ€์‚ฌ
  • trigger 1๊ฐœ ๋งค์นญ๋‹น 10์  ๊ฐ€์‚ฐ
  • ์ ์ˆ˜ ๋‚ด๋ฆผ์ฐจ์ˆœ์œผ๋กœ ์ •๋ ฌํ•˜์—ฌ ์ตœ๋Œ€ 5๊ฐœ(MAX_SKILLS_PER_SESSION) ๋ฐ˜ํ™˜

์„ธ์…˜ ์บ์‹ฑ โ€” ์ค‘๋ณต ์ฃผ์ž… ๋ฐฉ์ง€

const injectedCacheFallback = new Map();  // sessionId โ†’ Set<skillPath>
  • ํ•œ ์„ธ์…˜์—์„œ ์ด๋ฏธ ์ฃผ์ž…๋œ Skill์€ ๋‹ค์‹œ ์ฃผ์ž…ํ•˜์ง€ ์•Š๋Š”๋‹ค
  • session_id๋ณ„๋กœ ์ฃผ์ž…๋œ Skill ๊ฒฝ๋กœ์˜ Set์„ ์œ ์ง€
  • ๊ฐ™์€ ์„ธ์…˜์—์„œ ๊ฐ™์€ trigger๋ฅผ ๋ฐ˜๋ณต ์ž…๋ ฅํ•ด๋„ context๊ฐ€ ๋ถˆํ•„์š”ํ•˜๊ฒŒ ์ปค์ง€์ง€ ์•Š์Œ

Bridge vs Fallback ์•„ํ‚คํ…์ฒ˜

graph TB
    SI["skill-injector.mjs"] --> Try{"dist/hooks/skill-bridge.cjs ๋กœ๋“œ ์‹œ๋„"}
    Try -->|"์„ฑ๊ณต"| Bridge["Bridge ๋ชจ๋“œ"]
    Try -->|"์‹คํŒจ"| Fallback["Fallback ๋ชจ๋“œ"]

    Bridge --> R1["RECURSIVE ๋””๋ ‰ํ† ๋ฆฌ ํƒ์ƒ‰"]
    Bridge --> R2["์˜์† ์บ์‹ฑ"]

    Fallback --> F1["NON-RECURSIVE ํƒ์ƒ‰"]
    Fallback --> F2["์ธ๋ฉ”๋ชจ๋ฆฌ ์บ์‹ฑ (์„ธ์…˜ ํ•œ์ •)"]
  • Bridge ๋ชจ๋“œ: ์ปดํŒŒ์ผ๋œ skill-bridge.cjs ๋ฒˆ๋“ค์„ ์‚ฌ์šฉ. ์žฌ๊ท€์  ๋””๋ ‰ํ† ๋ฆฌ ํƒ์ƒ‰, ์˜์† ์บ์‹ฑ ์ง€์›
  • Fallback ๋ชจ๋“œ: Bridge ๋กœ๋“œ ์‹คํŒจ ์‹œ ์ธ๋ผ์ธ ๊ตฌํ˜„ ์‚ฌ์šฉ. ๋น„์žฌ๊ท€ ํƒ์ƒ‰, ์ธ๋ฉ”๋ชจ๋ฆฌ ์บ์‹ฑ๋งŒ ์ง€์›
  • Fallback์€ ํ•˜์œ„ ํ˜ธํ™˜์„ฑ์„ ์œ„ํ•œ ๊ฒƒ์œผ๋กœ, ๊ธฐ๋Šฅ์ ์œผ๋กœ๋Š” ๋™์ผํ•œ ๊ฒฐ๊ณผ๋ฅผ ๋ฐ˜ํ™˜

์ถœ๋ ฅ ํ˜•์‹ โ€” <mnemosyne> ํƒœ๊ทธ

formatSkillsMessage()๊ฐ€ ๋งค์นญ๋œ Skill๋“ค์„ ์•„๋ž˜ ํ˜•์‹์œผ๋กœ ๋ž˜ํ•‘ํ•œ๋‹ค:

<mnemosyne>
<skill-metadata>
{
  "name": "pr-creator",
  "path": ".omc/skills/pr-creator.md",
  "triggers": ["PR ์ค€๋น„", "PR ready"],
  "score": 20,
  "scope": "project"
}
</skill-metadata>
 
Skill ๋ณธ๋ฌธ ๋‚ด์šฉ...
 
---
</mnemosyne>

<mnemosyne>๋Š” ๊ทธ๋ฆฌ์Šค ์‹ ํ™”์˜ ๊ธฐ์–ต์˜ ์—ฌ์‹  ๋ฏ€๋„ค๋ชจ์‹œ๋„ค์—์„œ ๋”ฐ์˜จ ์ด๋ฆ„์ด๋‹ค. OMC์—์„œ๋Š” โ€œClaude๊ฐ€ ๊ธฐ์–ตํ•ด์•ผ ํ•  ํ•™์Šต๋œ ์ง€์‹โ€์„ ๊ฐ์‹ธ๋Š” OMC ์ž์ฒด ์ปจ๋ฒค์…˜์œผ๋กœ ์‚ฌ์šฉํ•œ๋‹ค. Claude Code๊ฐ€ ๊ณต์‹์œผ๋กœ ์ œ๊ณตํ•˜๋Š” ๊ฒƒ์€ additionalContext ๋ฌธ์ž์—ด ํ•„๋“œ๋ฟ์ด๋ฉฐ, ๊ทธ ์•ˆ์— ์–ด๋–ค ํ˜•์‹์„ ์‚ฌ์šฉํ• ์ง€๋Š” ํ”Œ๋Ÿฌ๊ทธ์ธ์ด ์ž์œ ๋กญ๊ฒŒ ๊ฒฐ์ •ํ•œ๋‹ค. OMC๋Š” XML ํƒœ๊ทธ ํ˜•์‹์„ ์„ ํƒํ•˜์—ฌ Claude๊ฐ€ ๊ฒฝ๊ณ„๋ฅผ ์ธ์‹ํ•˜๊ธฐ ์‰ฝ๊ฒŒ ํ–ˆ๋‹ค.

  • <skill-metadata>์— ๋งค์นญ ์ •๋ณด๋ฅผ JSON์œผ๋กœ ํฌํ•จํ•˜์—ฌ ๋””๋ฒ„๊น… ๊ฐ€๋Šฅ
  • ๋ณต์ˆ˜ Skill ๋งค์นญ ์‹œ --- ๊ตฌ๋ถ„์ž๋กœ ๋ถ„๋ฆฌ

keyword-detector.mjs์™€์˜ ์ถœ๋ ฅ ํ˜•์‹ ์ฐจ์ด:

keyword-detector.mjsskill-injector.mjs
๋ž˜ํ•‘์—†์Œ (ํ‰๋ฌธ ์ง€์‹œ)<mnemosyne> ํƒœ๊ทธ
๋ชฉ์ โ€์ด Skill์„ ์‹คํ–‰ํ•˜๋ผโ€ ๋ช…๋ นโ€์ด ์ง€์‹์„ ์ฐธ๊ณ ํ•˜๋ผโ€ ์ปจํ…์ŠคํŠธ
์˜ˆ์‹œ[MAGIC KEYWORD: ULTRAWORK]<mnemosyne><skill-metadata>...</mnemosyne>

์—๋Ÿฌ ์ฒ˜๋ฆฌ

๋ชจ๋“  ์—๋Ÿฌ๋Š” gracefulํ•˜๊ฒŒ ์ฒ˜๋ฆฌ๋œ๋‹ค:

// ์–ด๋–ค ์—๋Ÿฌ๊ฐ€ ๋ฐœ์ƒํ•ด๋„ Claude Code ์ง„ํ–‰์„ ์ฐจ๋‹จํ•˜์ง€ ์•Š์Œ
console.log(JSON.stringify({ continue: true, suppressOutput: true }));
  • Skill ํŒŒ์ผ ์ฝ๊ธฐ ์‹คํŒจ, JSON ํŒŒ์‹ฑ ์—๋Ÿฌ ๋“ฑ ๋ชจ๋“  ์˜ˆ์™ธ์—์„œ { continue: true } ๋ฐ˜ํ™˜
  • suppressOutput: true๋กœ verbose ๋ชจ๋“œ์—์„œ๋„ ์—๋Ÿฌ ๋…ธ์ด์ฆˆ๋ฅผ ์ˆจ๊น€
  • Hook ์Šคํฌ๋ฆฝํŠธ๋Š” ๋ณด์กฐ ๊ธฐ๋Šฅ์ด๋ฏ€๋กœ, ์‹คํŒจํ•ด๋„ ๋ณธ ์ž‘์—…์ด ์ค‘๋‹จ๋˜๋ฉด ์•ˆ ๋œ๋‹ค

์ฐธ๊ณ  ๋ฌธ์„œ