oh-my-customcode 0.149.0 → 0.150.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -222,7 +222,7 @@ Key rules: R010 (orchestrator never writes files), R009 (parallel execution mand
222
222
 
223
223
  ---
224
224
 
225
- ### Guides (57)
225
+ ### Guides (58)
226
226
 
227
227
  Reference documentation covering best practices, architecture decisions, and integration patterns. Located in `guides/` at project root, covering topics from agent design to CI/CD to observability.
228
228
 
@@ -279,7 +279,7 @@ your-project/
279
279
  │ ├── specs/ # Extracted canonical specs
280
280
  │ ├── contexts/ # 4 shared context files
281
281
  │ └── ontology/ # Knowledge graph for RAG
282
- └── guides/ # 57 reference documents
282
+ └── guides/ # 58 reference documents
283
283
  ```
284
284
 
285
285
  ---
package/dist/cli/index.js CHANGED
@@ -2334,7 +2334,7 @@ var init_package = __esm(() => {
2334
2334
  workspaces: [
2335
2335
  "packages/*"
2336
2336
  ],
2337
- version: "0.149.0",
2337
+ version: "0.150.1",
2338
2338
  description: "Batteries-included agent harness for Claude Code",
2339
2339
  type: "module",
2340
2340
  bin: {
@@ -28090,6 +28090,22 @@ async function installStatusline(targetDir, options, _result) {
28090
28090
  await fs2.chmod(destPath, 493);
28091
28091
  debug("install.statusline_installed", {});
28092
28092
  }
28093
+ async function installTestsConfig(targetDir, options, _result) {
28094
+ const srcPath = resolveTemplatePath(join8("tests", "tsconfig.json"));
28095
+ const destPath = join8(targetDir, "tests", "tsconfig.json");
28096
+ if (!await fileExists(srcPath)) {
28097
+ debug("install.tests_config_not_found", { path: srcPath });
28098
+ return;
28099
+ }
28100
+ if (await fileExists(destPath)) {
28101
+ if (!options.force && !options.backup) {
28102
+ debug("install.tests_config_skipped", { reason: "exists" });
28103
+ return;
28104
+ }
28105
+ }
28106
+ await copyFile(srcPath, destPath);
28107
+ debug("install.tests_config_installed", {});
28108
+ }
28093
28109
  async function installSettingsLocal(targetDir, result) {
28094
28110
  const layout = getProviderLayout();
28095
28111
  const settingsPath = join8(targetDir, layout.rootDir, "settings.local.json");
@@ -28182,6 +28198,7 @@ async function install(options) {
28182
28198
  await verifyTemplateDirectory();
28183
28199
  await installAllComponents(options.targetDir, options, result);
28184
28200
  await installStatusline(options.targetDir, options, result);
28201
+ await installTestsConfig(options.targetDir, options, result);
28185
28202
  await installSettingsLocal(options.targetDir, result);
28186
28203
  await installEntryDocWithTracking(options.targetDir, options, result);
28187
28204
  if (preservation) {
package/dist/index.js CHANGED
@@ -1680,6 +1680,22 @@ async function installStatusline(targetDir, options, _result) {
1680
1680
  await fs.chmod(destPath, 493);
1681
1681
  debug("install.statusline_installed", {});
1682
1682
  }
1683
+ async function installTestsConfig(targetDir, options, _result) {
1684
+ const srcPath = resolveTemplatePath(join5("tests", "tsconfig.json"));
1685
+ const destPath = join5(targetDir, "tests", "tsconfig.json");
1686
+ if (!await fileExists(srcPath)) {
1687
+ debug("install.tests_config_not_found", { path: srcPath });
1688
+ return;
1689
+ }
1690
+ if (await fileExists(destPath)) {
1691
+ if (!options.force && !options.backup) {
1692
+ debug("install.tests_config_skipped", { reason: "exists" });
1693
+ return;
1694
+ }
1695
+ }
1696
+ await copyFile(srcPath, destPath);
1697
+ debug("install.tests_config_installed", {});
1698
+ }
1683
1699
  async function installSettingsLocal(targetDir, result) {
1684
1700
  const layout = getProviderLayout();
1685
1701
  const settingsPath = join5(targetDir, layout.rootDir, "settings.local.json");
@@ -1772,6 +1788,7 @@ async function install(options) {
1772
1788
  await verifyTemplateDirectory();
1773
1789
  await installAllComponents(options.targetDir, options, result);
1774
1790
  await installStatusline(options.targetDir, options, result);
1791
+ await installTestsConfig(options.targetDir, options, result);
1775
1792
  await installSettingsLocal(options.targetDir, result);
1776
1793
  await installEntryDocWithTracking(options.targetDir, options, result);
1777
1794
  if (preservation) {
@@ -2014,7 +2031,7 @@ var package_default = {
2014
2031
  workspaces: [
2015
2032
  "packages/*"
2016
2033
  ],
2017
- version: "0.149.0",
2034
+ version: "0.150.1",
2018
2035
  description: "Batteries-included agent harness for Claude Code",
2019
2036
  type: "module",
2020
2037
  bin: {
package/package.json CHANGED
@@ -3,7 +3,7 @@
3
3
  "workspaces": [
4
4
  "packages/*"
5
5
  ],
6
- "version": "0.149.0",
6
+ "version": "0.150.1",
7
7
  "description": "Batteries-included agent harness for Claude Code",
8
8
  "type": "module",
9
9
  "bin": {
@@ -79,6 +79,30 @@ At the start of every new task, issue, or autonomous sub-loop, answer these thre
79
79
  - Related issue: #1188 item #4 — AskUserQuestion rejected, agent re-asked via free text in next turn (2026-05-19)
80
80
  -->
81
81
 
82
+ ### Failed Tool Re-Try Discipline
83
+
84
+ User-specified tools/formats persist across failures. After a tool rejection or failure, retry with the SAME tool — do NOT silently switch to a different mechanism.
85
+
86
+ | 시나리오 | Required |
87
+ |---------|----------|
88
+ | `AskUserQuestion` 거절됨 | 사용자가 다른 도구 명시할 때까지 `AskUserQuestion` 재시도 |
89
+ | 자유 텍스트로 재질문 | 금지 — directive 위반 |
90
+ | 다른 도구로 silent switch | 금지 — 명시적 사용자 확인 필요 |
91
+
92
+ Reference issues: #1188 item #4.
93
+
94
+ ### User Directive Persistence — Git Push Continuation
95
+
96
+ 사용자가 같은 세션 내에서 명시적으로 커밋/푸시를 한 번 허용했다면, 동일 카테고리/동일 브랜치의 후속 작업은 추가 확인 없이 진행 가능. push security policy classifier가 first-time strict, follow-up relaxed로 동작해야 함.
97
+
98
+ | 시나리오 | 동작 |
99
+ |----------|------|
100
+ | 1차 명시 "커밋, 푸시" + 동일 브랜치 | mgr-gitnerd push 진행 (advisory warning은 출력) |
101
+ | 2차 연속 작업 (동일 카테고리, 동일 브랜치) | 1차 directive persistence — 재확인 요청 금지 |
102
+ | 다른 브랜치 / 다른 카테고리 | 새 confirmation 필요 |
103
+
104
+ **Why**: 사용자 directive 일관성 — #1208 보고. 같은 세션 내 동일 의도를 반복 차단하면 R015 user directive persistence 위반.
105
+
82
106
  ## Agent Triggers
83
107
 
84
108
  Defined in `.claude/skills/intent-detection/patterns/agent-triggers.yaml`. Each agent has keywords, file patterns, actions, and base confidence.
@@ -33,6 +33,11 @@ The main conversation is the **sole orchestrator**. It uses routing skills to de
33
33
  ║ NOT exempt. ║
34
34
  ║ NO → Good. Continue. ║
35
35
  ║ ║
36
+ ║ 5. Am I about to edit a root meta-file (.gitignore, ║
37
+ ║ .editorconfig, README.md, CHANGELOG.md, CLAUDE.md, etc.)? ║
38
+ ║ YES → Delegate to specialist per Root Meta-File table. ║
39
+ ║ NO → Good. Continue. ║
40
+ ║ ║
36
41
  ║ If any answer points to a problem → resolve before proceeding ║
37
42
  ╚══════════════════════════════════════════════════════════════════╝
38
43
  ```
@@ -98,6 +103,12 @@ Key violations to avoid (file writes, git commands, bundled operations — all m
98
103
 
99
104
  ✓ CORRECT: Agent/skill/guide creation routed through mgr-creator
100
105
  Skill(brainstorming) → Agent(mgr-creator) → Write(".claude/agents/new.md")
106
+
107
+ ❌ WRONG: Orchestrator edits ".gitignore" because "it's only 1 line"
108
+ Main conversation → Edit(".gitignore", "!/README.ko.md")
109
+
110
+ ✓ CORRECT: Even single-line edits delegate to specialist
111
+ Main conversation → Agent(mgr-gitnerd) → Edit(".gitignore", "!/README.ko.md")
101
112
  ```
102
113
 
103
114
  <!-- DETAIL: Common Violations (extended)
@@ -329,6 +340,22 @@ The following paths MUST be created or structurally modified ONLY through `mgr-c
329
340
 
330
341
  > **Enforcement**: Advisory (R021) — no hard-block hook. Candidate for promotion if violation rate exceeds threshold. See R021 Hard Enforcement Candidates.
331
342
 
343
+ ### Root Meta-File Delegation
344
+
345
+ 루트 메타 파일은 변경 규모와 무관하게 orchestrator 직접 편집 금지. 적절한 specialist에 위임:
346
+
347
+ | Path | Delegated to |
348
+ |------|--------------|
349
+ | `.gitignore`, `.gitattributes` | mgr-gitnerd |
350
+ | `.editorconfig`, `.prettierrc*`, `.eslintrc*` | mgr-updater |
351
+ | `.npmrc`, `.nvmrc`, `package.json` (non-version fields), `package-lock.json` | mgr-updater |
352
+ | `CODEOWNERS`, `.github/CODEOWNERS` | mgr-gitnerd |
353
+ | `README.md`, `README*.md`, `CHANGELOG.md` | arch-documenter |
354
+ | `LICENSE`, `NOTICE` | arch-documenter |
355
+ | `CLAUDE.md` (project root) | arch-documenter (content) / mgr-updater (count sync) |
356
+
357
+ **Why**: "1 line edit" 논리는 R010 약화 — orchestrator 직접 편집 진입로 차단. #1208 보고.
358
+
332
359
  <!-- DETAIL: System Agents Reference
333
360
  | Agent | File | Purpose |
334
361
  |-------|------|---------|
@@ -121,7 +121,7 @@ project/
121
121
  | +-- rules/ # 전역 규칙 (R000-R023)
122
122
  | +-- hooks/ # 훅 스크립트 (보안, 검증, HUD)
123
123
  | +-- contexts/ # 컨텍스트 파일 (ecomode)
124
- +-- guides/ # 레퍼런스 문서 (57 토픽)
124
+ +-- guides/ # 레퍼런스 문서 (58 토픽)
125
125
  ```
126
126
 
127
127
  ## 오케스트레이션
@@ -55,7 +55,7 @@ templates/
55
55
  │ │ └── scripts/ # 훅 셸 스크립트 (34개)
56
56
  │ ├── contexts/ # 컨텍스트 설정 파일 (ecomode 등)
57
57
  │ └── ontology/ # Ontology-RAG 지식 그래프
58
- └── guides/ # 레퍼런스 문서 디렉토리 (54개)
58
+ └── guides/ # 레퍼런스 문서 디렉토리 (58개)
59
59
  ```
60
60
 
61
61
  ---
@@ -0,0 +1,146 @@
1
+ # Agent Teams 트러블슈팅 가이드
2
+
3
+ ## 개요
4
+
5
+ Agent Teams 운용 중 발생하는 일반적 문제와 해결책. #1206/#1210 보고 기반.
6
+
7
+ ---
8
+
9
+ ## Graceful Shutdown 실패
10
+
11
+ ### 증상
12
+
13
+ - `shutdown_request` 발신 2회 이상에도 멤버가 idle notification만 반환
14
+ - TeamDelete 호출 시 "active members exist" 에러 → 교착 상태
15
+
16
+ ### 원인
17
+
18
+ - 멤버가 long-running polling loop에 진입하여 외부 신호를 수신하지 못함
19
+ - TaskUpdate 미발신 → 코디네이터가 멤버의 실제 상태를 인식 불가
20
+ - `isActive` flag를 강제 해제할 수 있는 helper 부재
21
+
22
+ ### 해결 절차
23
+
24
+ #### 1단계: 정상 graceful shutdown 시도
25
+
26
+ ```
27
+ 1. shutdown_request 1회 발신 (SendMessage)
28
+ 2. 30초 대기 후 멤버 status 확인 (TaskList)
29
+ 3. TaskUpdate(status: "completed") 응답이 오면 정상 종료 진행
30
+ ```
31
+
32
+ #### 2단계: 재발신
33
+
34
+ ```
35
+ 1. shutdown_request 2회 발신
36
+ 2. 30초 추가 대기
37
+ 3. 여전히 무응답이면 3단계로 진행
38
+ ```
39
+
40
+ #### 3단계: Force kill (last resort)
41
+
42
+ ```bash
43
+ # 현재 세션의 tmux 목록 확인
44
+ tmux -L claude-swarm-$$ ls
45
+
46
+ # 특정 pane kill
47
+ tmux -L claude-swarm-$$ kill-pane -t <pane-id>
48
+
49
+ # 또는 전체 swarm 세션 kill
50
+ tmux -L claude-swarm-$$ kill-server
51
+ ```
52
+
53
+ #### 4단계: isActive flag 수동 해제
54
+
55
+ tmux kill 후에도 TeamDelete가 실패하는 경우:
56
+
57
+ ```bash
58
+ # teams 디렉토리 위치 확인
59
+ ls ~/.claude/teams/<team-id>/
60
+
61
+ # members.json에서 isActive를 false로 강제 설정
62
+ jq '.members |= map(.isActive = false)' \
63
+ ~/.claude/teams/<team-id>/members.json \
64
+ > /tmp/m.json \
65
+ && mv /tmp/m.json ~/.claude/teams/<team-id>/members.json
66
+ ```
67
+
68
+ > **주의**: `~/.claude/teams/` 경로는 CC 버전에 따라 다를 수 있습니다. 실제 경로는 CC 버전 릴리즈 노트에서 확인하십시오.
69
+
70
+ #### 5단계: TeamDelete 재시도
71
+
72
+ `isActive = false` 상태에서 TeamDelete를 재호출합니다. 성공 시 `~/.claude/teams/<team-id>/` 디렉토리가 자동 정리됩니다.
73
+
74
+ ### 예방
75
+
76
+ - R018 Member TaskUpdate Discipline을 준수하면 코디네이터가 멤버 상태를 항상 추적 가능
77
+ - 멤버 작업 시작 시 `TaskUpdate(status: "in_progress")` 즉시 호출
78
+ - long-running loop (30초 이상) 마다 description 업데이트
79
+
80
+ ---
81
+
82
+ ## 멤버 무응답 (TaskUpdate 침묵)
83
+
84
+ ### 증상
85
+
86
+ - 30초 이상 멤버가 `in_progress` 상태 미설정
87
+ - 다른 멤버가 동일 task를 claim 시도 → 충돌 및 중복 작업 발생
88
+ - 코디네이터가 멤버를 "dead" 로 오판하여 재spawn 시도
89
+
90
+ ### 원인
91
+
92
+ - 멤버가 복잡한 계산이나 I/O에 진입한 후 TaskUpdate를 호출하지 않음
93
+ - R018 Member TaskUpdate Discipline 위반
94
+
95
+ ### 해결 절차
96
+
97
+ 1. TaskList로 현재 task 상태 확인
98
+ 2. 해당 멤버에게 SendMessage로 상태 요청
99
+ 3. 60초 이상 무응답 시 Reassign 고려 (R018 Blocked Agent Behavior 참조)
100
+
101
+ ### 예방 (R018 준수)
102
+
103
+ | 시점 | 필수 호출 |
104
+ |------|-----------|
105
+ | 작업 시작 | `TaskUpdate(taskId, status: "in_progress")` |
106
+ | 30초 이상 분기/체크포인트 | `TaskUpdate(taskId, description: "<진행 상황>")` |
107
+ | 완료 | `TaskUpdate(taskId, status: "completed")` |
108
+ | 차단 시 | `TaskUpdate(taskId, description: "<차단 사유>")` + `SendMessage` |
109
+
110
+ ---
111
+
112
+ ## TeamDelete 후 잔여 리소스
113
+
114
+ ### 증상
115
+
116
+ - TeamDelete 성공 후에도 `~/.claude/teams/` 하위에 디렉토리가 남음
117
+ - 다음 세션에서 동일 team-id 재사용 시 충돌
118
+
119
+ ### 해결
120
+
121
+ ```bash
122
+ # 잔여 팀 디렉토리 수동 정리
123
+ rm -rf ~/.claude/teams/<team-id>/
124
+ ```
125
+
126
+ ---
127
+
128
+ ## CC Upstream 제한사항 (v2.1.146 기준)
129
+
130
+ | 기능 | 현황 | 대안 |
131
+ |------|------|------|
132
+ | TeamDelete `force` 옵션 | 미제공 | tmux kill-pane (3단계 절차) |
133
+ | 멤버 강제 종료 API | 미제공 | isActive flag 수동 해제 (4단계) |
134
+ | Graceful shutdown timeout 설정 | 미제공 | 30초 대기 후 수동 처리 |
135
+
136
+ CC가 정식 `force shutdown` API를 제공하면 3-4단계 workaround를 제거할 수 있습니다. 추적은 `guides/claude-code-tracking.md` 참조.
137
+
138
+ ---
139
+
140
+ ## Reference
141
+
142
+ - Issue: #1210 (#1206 item 2 분리)
143
+ - Cycle: v0.150.0
144
+ - Memory: `feedback_agent_teams_force_shutdown.md`
145
+ - R018: `.claude/rules/MUST-agent-teams.md` (Member TaskUpdate Discipline)
146
+ - CC Upstream Tracking: `guides/claude-code-tracking.md`
@@ -0,0 +1,86 @@
1
+ # Claude Code Upstream Tracking
2
+
3
+ oh-my-customcode가 의존하는 CC upstream API/기능 추적 목록. 각 항목은 현재 workaround와 정식 API 등장 시 제거할 대상을 명시합니다.
4
+
5
+ ---
6
+
7
+ ## Background Agent Progress API (#1212)
8
+
9
+ ### Status
10
+
11
+ - **v2.1.146 기준**: 정식 progress API 미제공
12
+ - `/bg` 흐름으로 시작한 background agent의 진행 상황을 메인 세션에서 직접 추적 불가
13
+
14
+ ### Workarounds
15
+
16
+ | 방법 | 구현 | 한계 |
17
+ |------|------|------|
18
+ | stdout/stderr tail | `/tmp/.claude-bg-<id>.log` 파일을 수동으로 tail | 세션 간 log 파일 위치 불일치 가능 |
19
+ | statusline agent count | R012 statusline에 `claude agents --json` (v2.1.144+) active count segment 추가 | count만 표시, 개별 진행률 없음 |
20
+ | PostToolUse hook | `BackgroundTask` 이벤트 수신 후 별도 로그 작성 (커스텀) | hook 구현 부담, 이벤트 타입 안정성 미보장 |
21
+
22
+ ### Track
23
+
24
+ - CC release note에서 background agent 관련 변경사항 모니터
25
+ - 정식 progress API 등장 시 위 workaround를 모두 제거하고 공식 API로 대체
26
+ - 후보 변경 키워드: `"background"`, `"bg agent"`, `"task progress"`, `"agent status"`
27
+
28
+ ### Reference
29
+
30
+ - Issue: #1212 (#1206 item 7 분리)
31
+ - Memory: `feedback_background_agent_progress_tracking.md`
32
+ - Cycle: v0.150.0
33
+ - R012: `.claude/rules/SHOULD-hud-statusline.md` (statusline integration)
34
+
35
+ ---
36
+
37
+ ## Agent Teams Force Shutdown (#1210)
38
+
39
+ ### Status
40
+
41
+ - **v2.1.146 기준**: `TeamDelete`에 `force` 옵션 미제공
42
+ - Graceful shutdown 실패 시 tmux kill-pane workaround 필요
43
+
44
+ ### Workaround
45
+
46
+ tmux 세션 직접 kill + `isActive` flag 수동 해제 절차. 상세 절차는 `guides/agent-teams/troubleshooting.md` 참조.
47
+
48
+ ### Track
49
+
50
+ - CC release note에서 TeamDelete API 변경사항 모니터
51
+ - 정식 `force` 옵션 등장 시 troubleshooting.md의 3-4단계 workaround 제거
52
+ - 후보 변경 키워드: `"TeamDelete"`, `"force shutdown"`, `"team teardown"`, `"agent kill"`
53
+
54
+ ### Reference
55
+
56
+ - Issue: #1210 (#1206 item 2 분리)
57
+ - Guide: `guides/agent-teams/troubleshooting.md`
58
+ - Cycle: v0.150.0
59
+
60
+ ---
61
+
62
+ ## 추적 방법
63
+
64
+ ### 버전 확인
65
+
66
+ ```bash
67
+ # 현재 CC 버전 확인
68
+ claude --version
69
+
70
+ # 또는 CC release notes
71
+ # https://github.com/anthropics/claude-code/releases
72
+ ```
73
+
74
+ ### 모니터링 주기
75
+
76
+ - 각 릴리즈 사이클(`/pipeline auto-dev`) 시작 시 CC 버전 확인
77
+ - 새 버전 확인 시 이 파일의 "Status" 항목 업데이트
78
+ - Workaround 제거 가능 시 해당 항목을 "Resolved" 섹션으로 이동
79
+
80
+ ---
81
+
82
+ ## Resolved
83
+
84
+ > 해결된 항목은 여기에 기록합니다. 완전 제거 전 1개 릴리즈 사이클 동안 유지합니다.
85
+
86
+ 현재 없음.
@@ -1,5 +1,5 @@
1
1
  {
2
- "version": "0.149.0",
2
+ "version": "0.150.1",
3
3
  "lastUpdated": "2026-05-20T00:00:00.000Z",
4
4
  "omcustomMinClaudeCode": "2.1.121",
5
5
  "omcustomMinClaudeCodeReason": "Sensitive-path direct Write/Edit on .claude/** under bypassPermissions (R010 deprecation, #1101)",
@@ -26,7 +26,7 @@
26
26
  "name": "guides",
27
27
  "path": "guides",
28
28
  "description": "Reference documentation",
29
- "files": 57
29
+ "files": 58
30
30
  },
31
31
  {
32
32
  "name": "hooks",
@@ -52,12 +52,24 @@
52
52
  "minimal": {
53
53
  "description": "Essential assets only — core SW Engineers + Managers. Reduces deactivation cost on first use.",
54
54
  "include": {
55
- "agents": ["mgr-*", "lang-*-expert", "sys-memory-keeper"],
55
+ "agents": [
56
+ "mgr-*",
57
+ "lang-*-expert",
58
+ "sys-memory-keeper"
59
+ ],
56
60
  "skills": [
57
- {"scope": "core"},
58
- {"scope": "harness"}
61
+ {
62
+ "scope": "core"
63
+ },
64
+ {
65
+ "scope": "harness"
66
+ }
59
67
  ],
60
- "guides": ["agent-design", "git-safety", "claude-code"]
68
+ "guides": [
69
+ "agent-design",
70
+ "git-safety",
71
+ "claude-code"
72
+ ]
61
73
  }
62
74
  },
63
75
  "full": {
@@ -82,7 +94,9 @@
82
94
  "mgr-*"
83
95
  ],
84
96
  "skills": [
85
- {"scope": "core"},
97
+ {
98
+ "scope": "core"
99
+ },
86
100
  "react-best-practices",
87
101
  "typescript-best-practices",
88
102
  "fastapi-best-practices",
@@ -117,7 +131,9 @@
117
131
  "mgr-*"
118
132
  ],
119
133
  "skills": [
120
- {"scope": "core"},
134
+ {
135
+ "scope": "core"
136
+ },
121
137
  "airflow-best-practices",
122
138
  "dbt-best-practices"
123
139
  ],
@@ -141,7 +157,9 @@
141
157
  "tracker-checkpoint"
142
158
  ],
143
159
  "skills": [
144
- {"scope": "harness"}
160
+ {
161
+ "scope": "harness"
162
+ }
145
163
  ],
146
164
  "guides": [
147
165
  "agent-design",
@@ -0,0 +1,7 @@
1
+ {
2
+ "extends": "../tsconfig.json",
3
+ "compilerOptions": {
4
+ "types": ["bun-types"]
5
+ },
6
+ "include": ["**/*.ts"]
7
+ }