μ리μ¦: oh-my-claudecode λ΄λΆ ꡬ쑰λΆν° νλ¬κ·ΈμΈ κ°λ°κΉμ§
μ΄ μ리μ¦λ Claude Code νλ¬κ·ΈμΈ μμ€ν μ λ΄λΆ λμμ λ¨κ³λ³λ‘ ν΄λΆνκ³ , μ΅μ’ μ μΌλ‘ λλ§μ νλ¬κ·ΈμΈμ μ§μ κ°λ°νλ κ²κΉμ§ λλ¬νλ κ³Όμ μ΄λ€.
| νΈ | λ΄μ© | ν΅μ¬ |
|---|---|---|
| 1νΈ | Plugin Anatomy | plugin.json νλλ‘ μμνλ νλ¬κ·ΈμΈ λΌλ |
| 2νΈ | Hook System | μ΄λ²€νΈ β stdin JSON β μ€ν¬λ¦½νΈ β stdout JSON |
| 3νΈ | Skill System | SKILL.mdλ‘ μν¬νλ‘μ°λ₯Ό μ μνλ λ°©λ² |
| 4νΈ | Agent System | Task toolλ‘ μμλλ μ€μ μμ μνμ |
| 5νΈ | MCP Tools Bridge | LSP, AST λ± μ»€μ€ν λꡬ μλ² |
| 6νΈ (λ³Έλ¬Έ) | State & Lifecycle | .omc/ μν κ΄λ¦¬μ λͺ¨λ μ§μ |
| 7νΈ | Parallel Orchestration | Ultrawork, Team, Autopilot, Ralph ν¨ν΄ |
- OMCμ State & Lifecycleμ λͺ¨λ μν, μΈμ
λ©λͺ¨λ¦¬, νλ‘μ νΈ μ§μμ
.omc/λλ ν 리μμ κ΄λ¦¬νλ μν μμν μμ€ν - keyword-detectorκ° λͺ¨λλ₯Ό νμ±ννλ©΄
.omc/state/μ μν νμΌμ κΈ°λ‘νκ³ , Stop hook(persistent-mode)μ΄ μνλ₯Ό μ½μ΄ μμ μλ£κΉμ§ μ’ λ£λ₯Ό μ°¨λ¨νλ ꡬ쑰 - 컨ν μ€νΈ μμΆ(Compact) μ ν΅μ¬ μ λ³΄κ° μ μ€λλ λ¬Έμ λ₯Ό Notepad(3μΉμ λ©λͺ¨λ¦¬)μ Project Memory(νλ‘μ νΈ μ§μ JSON)λ‘ ν΄κ²°νλ μμΆ λ΄μ± μν€ν μ²
ν΄λΉ κ°λ μ΄ νμν μ΄μ
- 5νΈ(MCP Tools Bridge)μμ state_read, state_write λ± MCP λκ΅¬λ‘ μνλ₯Ό μ½κ³ μΈ μ μκ² λμμ
- νμ§λ§ **βμΈμ μνλ₯Ό κΈ°λ‘νκ³ , μΈμ μ½κ³ , λͺ¨λ μ νμ μ΄λ»κ² λλ©°, 컨ν μ€νΈ μμΆ μ μ 보λ₯Ό μ΄λ»κ² 보쑴νλμ§β**κ° νμ
- μ΄ ννΈλ OMCμ μν κ΄λ¦¬ μ λ΅ β λͺ¨λ λ μ§μ€νΈλ¦¬, μ’ λ£ μ°¨λ¨, μμΆ λμ λ©λͺ¨λ¦¬μ μ 체 μλͺ μ£ΌκΈ°λ₯Ό λ€λ£Έ
AS-IS
sequenceDiagram autonumber participant U as μ¬μ©μ participant CC as Claude Code U->>CC: "ralphλ‘ λ¦¬ν©ν°λ§ ν΄μ€" CC->>CC: μμ μμ CC->>CC: 1λ¨κ³ μλ£ CC-->>U: μλ΅ μ’ λ£ (μλ) Note over U: "μμ§ μ λλ¬λλ°..." U->>CC: "κ³μν΄μ€" CC->>CC: μ΄μ 컨ν μ€νΈ μ μ€ Note over CC: λ νκ³ μμλμ§<br/>κΈ°μ΅ λͺ» ν¨
TO-BE
sequenceDiagram autonumber participant KD as keyword-detector participant ST as .omc/state/ participant PM as persistent-mode<br/>(Stop hook) participant CC as Claude Code KD->>ST: ralph-state.json μμ±<br/>{ active: true, started_at } CC->>CC: μμ μ§ν CC->>PM: Stop μ΄λ²€νΈ (μλ΅ μ’ λ£ μλ) PM->>ST: readModeState("ralph") ST-->>PM: { active: true } PM-->>CC: { continue: false } β μ’ λ£ μ°¨λ¨ CC->>CC: μμ κ³μ μ§ν CC->>ST: state_write({ active: false }) CC->>PM: Stop μ΄λ²€νΈ PM-->>CC: { continue: true } β μ’ λ£ νμ©
.omc/ λλ ν 리 ꡬ쑰
.omc/
βββ state/ β λͺ¨λ μν νμΌ
β βββ ultrawork-state.json
β βββ ralph-state.json
β βββ team-state.json
β βββ autopilot-state.json
β βββ ultraqa-state.json
β βββ skill-active-state.json
β βββ last-tool-error.json β μΌμμ (60μ΄ TTL)
β βββ subagent-tracking.json β μΌμμ
β βββ mission-state.json
β βββ checkpoints/ β PreCompact 체ν¬ν¬μΈνΈ
β βββ shared-memory/ β μμ΄μ νΈ κ° κ³΅μ μ μ₯μ
β β βββ {namespace}/{key}.json
β βββ sessions/ β μΈμ
λ³ κ²©λ¦¬
β βββ {sessionId}/
β βββ {mode}-state.json
β βββ cancel-signal.json
β βββ {mode}-stop-breaker.json
βββ sessions/ β μΈμ
μ’
λ£ μ μμ½ κΈ°λ‘
β βββ {sessionId}.json
βββ notepad.md β μμΆ λ΄μ± λ©λͺ¨λ¦¬ (3μΉμ
)
βββ project-memory.json β νλ‘μ νΈ μ§μ
βββ notepads/ β νλλ³ μμ¦λ€ λ
ΈνΈν¨λ
β βββ {planName}/
β βββ learnings.md
β βββ decisions.md
β βββ issues.md
β βββ problems.md
βββ plans/ β κ³ν λ¬Έμ
βββ research/ β μ‘°μ¬ κ²°κ³Ό
βββ drafts/ β λλννΈ νμΌ
βββ logs/ β λ‘κ·Έ
βββ autopilot/ β Autopilot μ€ν/νλ
βββ skills/ β μ€ν¬ μ μ₯μ
ensureAllOmcDirs()κ° state, plans, research, logs, notepads, draftsλ₯Ό μλ μμ±νλ€. setup-init.mjsλ μΆκ°λ‘ state/checkpointsλ 보μ₯.
Mode Registry β λͺ¨λ μν λ¨Έμ
5κ°μ§ μ€ν λͺ¨λ (ExecutionMode)
src/hooks/mode-registry/types.tsμ μ μλ 곡μ μ€ν λͺ¨λ:
type ExecutionMode = 'autopilot' | 'team' | 'ralph' | 'ultrawork' | 'ultraqa';stateDiagram-v2 [*] --> inactive: κΈ°λ³Έ μν inactive --> active: ν€μλ κ°μ§ or Skill νμ±ν active --> active: μμ μ§ν μ€ (Stop hookμ΄ μ’ λ£ μ°¨λ¨) active --> inactive: μμ μλ£ or /cancel active --> stale: 24μκ° μ΄μ λΉνμ± state active { [*] --> running running --> verifying: κ²μ¦ λ¨κ³ (ralph) verifying --> running: κ²μ¦ μ€ν¨ β μ¬μμ verifying --> done: κ²μ¦ ν΅κ³Ό }
| λͺ¨λ | μν νμΌ | λ°°νμ ? | μ€λͺ |
|---|---|---|---|
autopilot | autopilot-state.json | Yes (μ μΌ) | μ 체 μλν |
team | team-state.json | No | N worker λ³λ ¬ |
ralph | ralph-state.json | No | κ²μ¦ 루ν |
ultrawork | ultrawork-state.json | No | λ³λ ¬ μ€ν |
ultraqa | ultraqa-state.json | No | QA μ¬μ΄ν΄ |
- λ°°νμ λͺ¨λ:
EXCLUSIVE_MODESλ°°μ΄μ autopilotλ§ ν¬ν¨.canStartMode()κ°isModeActiveInAnySession()μΌλ‘ μ μΈμ μ κ²μ¬νμ¬ λμ μ€ν μ°¨λ¨ - ν©μ± κ°λ₯ λͺ¨λ: team, ralph, ultrawork, ultraqaλ λμ νμ±ν κ°λ₯
μ€ν λͺ¨λκ° μλ μν λꡬ λͺ¨λ
state-tools.tsμ STATE_TOOL_MODESμλ μ€ν λͺ¨λ 5κ° μΈμ μΆκ° λͺ¨λκ° μλ€:
| λͺ¨λ | μ±κ²© | λΉκ³ |
|---|---|---|
ralplan | μ€ν μ κ³ν λͺ¨λ | Stop hookμμ λ³λ checkRalplan()μΌλ‘ μ²λ¦¬ (μ΅λ 30ν κ°ν, 45λΆ TTL) |
omc-teams | team λͺ¨λμ μ€ν¬ μν | skill-stateμμ protection 'none'μΌλ‘ λ±λ‘ |
deep-interview | μΈν°λ·° λͺ¨λ | skill-stateμμ protection 'heavy' (10ν κ°ν, 30λΆ TTL) |
μ΄λ€μ ExecutionMode νμ
μ ν¬ν¨λμ§ μμΌλ©°, Mode Registryκ° μλ κ°μμ λ©μ»€λμ¦μΌλ‘ μνλ₯Ό κ΄λ¦¬νλ€.
λͺ¨λ νμ±ν κ²½λ‘
graph TD UP["UserPromptSubmit"] --> KD["keyword-detector.mjs"] KD -->|"ralph, autopilot,<br/>ultrawork, ultraqa"| ACT["λͺ¨λ νμ±ν"] KD -->|"ralplan gate μ μ©"| RG{"μ ν¨ν ν둬ννΈ?<br/>(15λ¨μ΄ μ΄μ,<br/>νμΌκ²½λ‘Β·ν¨μλͺ λ±)"} RG -->|"No"| RP["ralplanμΌλ‘ λ체"] RG -->|"Yes"| ACT SK["Skill μ§μ νΈμΆ"] -->|"/team, /ralph λ±"| ACT Note1["team ν€μλ κ°μ§λ<br/>λͺ μμ μΌλ‘ λΉνμ±νλ¨<br/>(never-match regex)"] style Note1 fill:#ffd,stroke:#aa0
keyword-detectorκ° μ€ν ν€μλλ₯Ό κ°μ§νλ, applyRalplanGate()λ‘ ν둬ννΈ νμ§μ κ²μ¦νλ€. 15λ¨μ΄ λ―Έλ§μ΄κ³ ꡬ체μ μ νΈ(νμΌ κ²½λ‘, ν¨μλͺ
λ±)κ° μμΌλ©΄ μ€ν λͺ¨λ λμ ralplan(κ³ν λͺ¨λ)μΌλ‘ λ체λλ€.
team λͺ¨λμ ν€μλ κ°μ§λ (?!x)x(μ λ λ§€μΉ λΆκ° μ κ·μ)λ‘ μ€μ λμ΄ μ€ν¬ μ§μ νΈμΆ(/team)μΌλ‘λ§ νμ±ν κ°λ₯νλ€.
Stale State λ°©μ§
clearStaleSessionDirs()κ° μΈμ
λλ ν 리λ₯Ό μ 리νλ€:
function clearStaleSessionDirs(
cwd: string,
maxAgeMs: number = 24 * 60 * 60 * 1000 // κΈ°λ³Έ 24μκ°
): string[]λλ ν 리 λ΄ κ°μ₯ μ΅μ νμΌμ mtimeMsλ₯Ό κΈ°μ€μΌλ‘, 24μκ° μ΄μ κ²½κ³Όν μΈμ
λλ ν 리λ₯Ό μ κ±°νλ€. λΉ λλ ν 리λ μ¦μ μ 리.
Persistent Mode β Stop Hookμ μ’ λ£ μ°¨λ¨
μν€ν μ²
hooks.jsonμ Stop μ΄λ²€νΈμ λ±λ‘λ 3κ° ν
:
context-guard-stop.mjs(5μ΄) β 컨ν μ€νΈ κ°λpersistent-mode.cjs(10μ΄) β λ©μΈ μ’ λ£ μ°¨λ¨κΈ°code-simplifier.mjs(5μ΄) β μ½λ λ¨μν
μ°μ μμλ³ μ²΄ν¬ μμ
checkPersistentModes()κ° λ€μ μμλ‘ κ²μ¬νλ€:
graph TD START["Stop μ΄λ²€νΈ μμ "] --> CRIT{"Critical Context?<br/>(95%+ λλ 429/401/403<br/>λλ /cancel)"} CRIT -->|"Yes"| ALLOW["μ’ λ£ νμ©<br/>{ continue: true }"] CRIT -->|"No"| CANCEL{"cancel-signal.json<br/>μ‘΄μ¬ + μ ν¨?"} CANCEL -->|"Yes"| ALLOW CANCEL -->|"No"| P1["P1: Ralph 체ν¬"] P1 -->|"νμ±"| BLOCK["μ’ λ£ μ°¨λ¨<br/>{ continue: false,<br/>message: '...' }"] P1 -->|"λΉνμ±"| P15["P1.5: Autopilot 체ν¬"] P15 -->|"νμ±"| BLOCK P15 -->|"λΉνμ±"| P17["P1.7: Team Pipeline<br/>(max 20ν, TTL 5λΆ)"] P17 -->|"νμ±"| BLOCK P17 -->|"λΉνμ±"| P18["P1.8: Ralplan<br/>(max 30ν, TTL 45λΆ)"] P18 -->|"νμ±"| BLOCK P18 -->|"λΉνμ±"| P2["P2: Ultrawork 체ν¬"] P2 -->|"νμ±"| BLOCK P2 -->|"λΉνμ±"| P3["P3: Skill Active State"] P3 -->|"νμ±"| BLOCK P3 -->|"λΉνμ±"| ALLOW
μλ΅ ν¬λ§·
function createHookOutput(result: PersistentModeResult): {
continue: boolean;
message?: string;
} {
return {
continue: !result.shouldBlock,
message: result.message || undefined
};
}| μν© | μλ΅ |
|---|---|
| μ’ λ£ μ°¨λ¨ | { "continue": false, "message": "<κ³μ μμ
ν둬ννΈ>" } |
| μ’ λ£ νμ© | { "continue": true } |
Circuit Breaker (Stop-Breaker)
κ° λͺ¨λλ 무ν μ°¨λ¨μ λ°©μ§νκΈ° μν νλ‘ μ°¨λ¨κΈ°λ₯Ό κ°μ§λ€:
| λͺ¨λ | μ΅λ κ°ν νμ | TTL |
|---|---|---|
| Team Pipeline | 20ν | 5λΆ |
| Ralplan | 30ν | 45λΆ |
| Deep-interview | 10ν | 30λΆ |
Ralphμ νΉμ λμ
Ralph λͺ¨λμμ state.iteration >= state.max_iterationsμ λλ¬νλ©΄ μ’
λ£νμ§ μκ³ νλλ₯Ό 10 μΆκ° νμ₯νλ€:
state.max_iterations += 10;
writeRalphState(workingDir, state, sessionId);Ralphμ μ μΌν μ’
λ£ λ°©λ²μ λͺ
μμ /oh-my-claudecode:cancel λͺ
λ Ήμ΄λ€.
Notepad β 컨ν μ€νΈ μμΆ λμ λ©λͺ¨λ¦¬
Claude Codeλ 컨ν μ€νΈ μλμ°κ° κ°λ μ°¨λ©΄ Compact(μμΆ)μ μννλ€. μ΄μ λνκ° μμ½λλ©΄μ μΈλΆ μ λ³΄κ° μ μ€λλ€.
3μΉμ ꡬ쑰
.omc/notepad.mdμ κ³ μ ꡬ쑰:
# Notepad
<!-- Auto-managed by OMC. Manual edits preserved in MANUAL section. -->
## Priority Context
<!-- ALWAYS loaded. Keep under 500 chars. Critical discoveries only. -->
## Working Memory
<!-- Session notes. Auto-pruned after 7 days. -->
## MANUAL
<!-- User content. Never auto-pruned. -->| μΉμ | μ°κΈ° λ°©μ | μ΅λ ν¬κΈ° | μλͺ μ£ΌκΈ° |
|---|---|---|---|
| Priority Context | REPLACE (κ΅μ²΄) | 500μ κΆμ₯ (hard limit 2000) | μꡬ μ μ§, SessionStart μ νμ μ£Όμ |
| Working Memory | APPEND (μΆκ°) | 4000μ/νλͺ© | 7μΌ ν μλ μ 리 |
| Manual | APPEND (μΆκ°) | 4000μ/νλͺ© | μλ μ 리 μμ |
μ 체 νμΌ ν¬κΈ° μ ν: 8KB (maxTotalSize: 8192)
Working Memory νλͺ©μ ### YYYY-MM-DD HH:MM ν€λλ‘ νμμ€ν¬νκ° λΆμ¬λλ€.
μμΆ λμ νλ¦
sequenceDiagram autonumber participant CC as Claude Code participant PC as pre-compact.mjs participant NP as .omc/notepad.md participant WD as .omc/notepads/{plan}/ participant PM as project-memory-precompact.mjs participant CC2 as Claude Code (μμΆ ν) CC->>CC: 컨ν μ€νΈ μκ³μΉ λλ¬ CC->>PC: PreCompact μ΄λ²€νΈ PC->>PC: 체ν¬ν¬μΈνΈ JSON μμ±<br/>(.omc/state/checkpoints/) PC->>WD: μμ¦λ€ λ ΈνΈ λ΄λ³΄λ΄κΈ°<br/>(learnings, decisions, issues) PC-->>CC: systemMessage: νμ± λͺ¨λ + TODO μμ½ CC->>PM: PreCompact μ΄λ²€νΈ PM-->>CC: systemMessage: project-memory.json μ¬μ£Όμ CC->>CC: Compact μ€ν (μ΄μ λν μμ½) CC2->>NP: notepad_read("priority") NP-->>CC2: Priority Context 볡μ Note over CC2: μμΆ μ ν΅μ¬ μ 보 볡μ μ±κ³΅
Priority Contextλ SessionStart μ μλ μ£Όμ
λμ΄ μΈμ
μ΄ μμλ λλ§λ€ ν΅μ¬ μ λ³΄κ° μ»¨ν
μ€νΈμ λ‘λλλ€. PreCompact hookμ 체ν¬ν¬μΈνΈλ₯Ό μμ±νκ³ νλλ³ μμ¦λ€ λ
ΈνΈ(notepads/ νμ)λ₯Ό λ΄λ³΄λ΄λ μν μ΄λ€.
Project Memory β νλ‘μ νΈ μ§μ
λ°μ΄ν° ꡬ쑰
.omc/project-memory.jsonμ μ€μ μ€ν€λ§ (src/hooks/project-memory/types.ts):
{
"version": "1.0.0",
"lastScanned": 1710849600000,
"projectRoot": "/path/to/project",
"techStack": {
"languages": ["typescript"],
"frameworks": ["express"],
"buildTools": ["esbuild"]
},
"build": {
"buildCommand": "npm run build",
"testCommand": "npm test",
"lintCommand": "npm run lint",
"devCommand": "npm run dev",
"scripts": {}
},
"conventions": {
"namingStyle": "camelCase",
"importStyle": "esm",
"testPattern": "*.test.ts",
"fileOrganization": "feature-based"
},
"structure": {},
"customNotes": [],
"directoryMap": {},
"hotPaths": [],
"userDirectives": []
}νλλͺ
μ£Όμ: build (buildInfo), conventions (codeConventions)
νμ΅ κ²½λ‘ 3κ°μ§
graph TD SS["SessionStart<br/>session-start.mjs"] -->|"detectProjectEnvironment()"| PM[".omc/project-memory.json"] PT["PostToolUse<br/>project-memory-posttool.mjs"] -->|"learnFromToolOutput()"| PM PC["PreCompact<br/>project-memory-precompact.mjs"] -->|"formatContextSummary()"| SYS["systemMessageλ‘ μ¬μ£Όμ "] subgraph "PostToolUse νμ΅ λμ" R["Read/Edit/Write β νμΌ ν«ν¨μ€"] G["Glob/Grep β λλ ν 리 ν«ν¨μ€"] B["Bash β λΉλ/ν μ€νΈ λͺ λ Ή, νκ²½ ννΈ"] U["User λ©μμ§ β μ§μμ¬ν κ°μ§"] end PT --> R PT --> G PT --> B PT --> U
- SessionStart:
shouldRescan()μ΄ true(24μκ° κ²½κ³Ό λλ νμΌ λ―Έμ‘΄μ¬)μ΄λ©΄detectProjectEnvironment()λ‘ νλ‘μ νΈ μλ κ°μ§ - PostToolUse:
learnFromToolOutput()μ΄ λꡬ μΆλ ₯μμ ν«ν¨μ€, λΉλ λͺ λ Ή, νκ²½ ννΈλ₯Ό μΆμΆ. customNotesλ μ΅λ 20κ° (μ΄κ³Ό μ κ°μ₯ μ€λλ νλͺ© μ κ±°) - PreCompact:
formatContextSummary(memory)λ₯Ό systemMessageλ‘ λ°ννμ¬ μμΆ νμλ νλ‘μ νΈ μ§μμ΄ μ μ§λ¨. userDirectivesμλ βμμΆ νμλ λ°λμ λ°λΌμΌ ν¨β μ§μ ν¬ν¨
Session Lifecycle μ 체 νλ¦
graph TD A["SessionStart"] -->|"session-start.mjs (5s)<br/>project-memory-session.mjs (5s)<br/>setup-init.mjs (30s, matcher: init)<br/>setup-maintenance.mjs (60s, matcher: maintenance)"| B["μ΄κΈ° μ€μ μλ£"] B --> C["μ¬μ©μ μ λ ₯"] C -->|"UserPromptSubmit"| D["keyword-detector.mjs (5s)<br/>skill-injector.mjs (3s)"] D --> E["μμ μ§ν"] E -->|"PreToolUse"| F["pre-tool-enforcer.mjs (3s)"] F --> G["λꡬ μ€ν"] G -->|"PostToolUse"| H["post-tool-verifier.mjs (3s)<br/>project-memory-posttool.mjs (3s)"] H --> E G -->|"PostToolUseFailure"| HF["post-tool-use-failure.mjs (3s)"] HF --> E E -->|"SubagentStart"| SA["subagent-tracker.mjs start (3s)"] SA -->|"SubagentStop"| SB["subagent-tracker.mjs stop (5s)<br/>verify-deliverables.mjs (5s)"] E -->|"PreCompact"| I["pre-compact.mjs (10s)<br/>project-memory-precompact.mjs (5s)"] I -->|"Compact μ€ν"| E E -->|"Stop"| J{"persistent-mode.cjs<br/>(10s)"} J -->|"continue: false"| K["μ’ λ£ μ°¨λ¨ β κ³μ"] K --> E J -->|"continue: true"| L["μ’ λ£ νμ©"] L -->|"SessionEnd"| M["session-end.mjs (10s)<br/>λ©νΈλ¦ κΈ°λ‘ + μν μ 리"]
λλ§μ νλ¬κ·ΈμΈμ λ§λ λ€λ©΄
μ΅μ μν κ΄λ¦¬λ‘ βμλ£κΉμ§ λ©μΆμ§ μλβ λͺ¨λλ₯Ό ꡬννλ ν¨ν΄:
// μν κΈ°λ‘ (keyword-detectorμμ)
const state = {
active: true,
started_at: new Date().toISOString(),
original_prompt: prompt
};
writeFileSync('.omc/state/my-mode-state.json', JSON.stringify(state));// μν μ½κΈ° (Stop hookμμ)
const state = JSON.parse(readFileSync('.omc/state/my-mode-state.json'));
if (state.active) {
// μ’
λ£ μ°¨λ¨: continue: falseκ° ν΅μ¬
console.log(JSON.stringify({ continue: false, message: "μμ
κ³μ μ§ν" }));
} else {
console.log(JSON.stringify({ continue: true }));
}μ°Έκ³ λ¬Έμ
- μμ€:
~/Documents/study/oh-my-claudecode/src/hooks/mode-registry/ - μμ€:
~/Documents/study/oh-my-claudecode/src/hooks/persistent-mode/ - μμ€:
~/Documents/study/oh-my-claudecode/src/hooks/notepad/ - μμ€:
~/Documents/study/oh-my-claudecode/src/hooks/project-memory/ - μμ€:
~/Documents/study/oh-my-claudecode/src/hooks/pre-compact/