@simplysm/sd-claude 13.0.69 → 13.0.71

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.
Files changed (78) hide show
  1. package/README.md +12 -601
  2. package/claude/agents/sd-api-reviewer.md +0 -1
  3. package/claude/agents/sd-code-reviewer.md +0 -1
  4. package/claude/agents/sd-code-simplifier.md +1 -1
  5. package/claude/agents/sd-security-reviewer.md +0 -1
  6. package/claude/refs/sd-angular.md +26 -26
  7. package/claude/refs/sd-orm-v12.md +17 -17
  8. package/claude/rules/sd-refs-linker.md +14 -14
  9. package/claude/sd-statusline.js +21 -21
  10. package/claude/skills/sd-api-name-review/SKILL.md +1 -2
  11. package/claude/skills/sd-brainstorm/SKILL.md +3 -4
  12. package/claude/skills/sd-check/SKILL.md +1 -2
  13. package/claude/skills/sd-commit/SKILL.md +2 -3
  14. package/claude/skills/sd-debug/SKILL.md +1 -2
  15. package/claude/skills/sd-discuss/SKILL.md +1 -3
  16. package/claude/skills/sd-document/SKILL.md +99 -0
  17. package/claude/skills/sd-document/extract_docx.py +92 -0
  18. package/claude/skills/sd-document/extract_pdf.py +102 -0
  19. package/claude/skills/sd-document/extract_pptx.py +77 -0
  20. package/claude/skills/sd-document/extract_xlsx.py +83 -0
  21. package/claude/skills/sd-email-analyze/SKILL.md +6 -6
  22. package/claude/skills/sd-plan/SKILL.md +1 -3
  23. package/claude/skills/sd-plan-dev/SKILL.md +94 -111
  24. package/claude/skills/sd-plan-dev/code-quality-reviewer-prompt.md +1 -1
  25. package/claude/skills/sd-plan-dev/final-review-prompt.md +1 -1
  26. package/claude/skills/sd-plan-dev/spec-reviewer-prompt.md +1 -1
  27. package/claude/skills/sd-readme/SKILL.md +107 -88
  28. package/claude/skills/sd-review/SKILL.md +14 -16
  29. package/claude/skills/sd-skill/SKILL.md +6 -317
  30. package/claude/skills/sd-skill/cso-guide.md +161 -0
  31. package/claude/skills/sd-skill/writing-guide.md +163 -0
  32. package/claude/skills/sd-tdd/SKILL.md +1 -3
  33. package/claude/skills/sd-use/SKILL.md +1 -3
  34. package/claude/skills/sd-worktree/SKILL.md +52 -2
  35. package/dist/commands/auth-add.d.ts +2 -0
  36. package/dist/commands/auth-add.d.ts.map +1 -0
  37. package/dist/commands/auth-add.js +32 -0
  38. package/dist/commands/auth-add.js.map +6 -0
  39. package/dist/commands/auth-list.d.ts +2 -0
  40. package/dist/commands/auth-list.d.ts.map +1 -0
  41. package/dist/commands/auth-list.js +37 -0
  42. package/dist/commands/auth-list.js.map +6 -0
  43. package/dist/commands/auth-remove.d.ts +2 -0
  44. package/dist/commands/auth-remove.d.ts.map +1 -0
  45. package/dist/commands/auth-remove.js +22 -0
  46. package/dist/commands/auth-remove.js.map +6 -0
  47. package/dist/commands/auth-use.d.ts +2 -0
  48. package/dist/commands/auth-use.d.ts.map +1 -0
  49. package/dist/commands/auth-use.js +33 -0
  50. package/dist/commands/auth-use.js.map +6 -0
  51. package/dist/commands/auth-utils.d.ts +11 -0
  52. package/dist/commands/auth-utils.d.ts.map +1 -0
  53. package/dist/commands/auth-utils.js +57 -0
  54. package/dist/commands/auth-utils.js.map +6 -0
  55. package/dist/commands/install.js +3 -3
  56. package/dist/commands/install.js.map +1 -1
  57. package/dist/index.d.ts +5 -0
  58. package/dist/index.d.ts.map +1 -1
  59. package/dist/index.js +5 -0
  60. package/dist/index.js.map +1 -1
  61. package/dist/sd-claude.js +68 -3
  62. package/dist/sd-claude.js.map +1 -1
  63. package/package.json +3 -2
  64. package/scripts/sync-claude-assets.mjs +1 -1
  65. package/src/commands/auth-add.ts +36 -0
  66. package/src/commands/auth-list.ts +44 -0
  67. package/src/commands/auth-remove.ts +26 -0
  68. package/src/commands/auth-use.ts +53 -0
  69. package/src/commands/auth-utils.ts +65 -0
  70. package/src/commands/install.ts +19 -19
  71. package/src/index.ts +5 -0
  72. package/src/sd-claude.ts +81 -3
  73. package/tests/auth-add.spec.ts +74 -0
  74. package/tests/auth-list.spec.ts +175 -0
  75. package/tests/auth-remove.spec.ts +74 -0
  76. package/tests/auth-use.spec.ts +153 -0
  77. package/tests/auth-utils.spec.ts +173 -0
  78. package/claude/skills/sd-explore/SKILL.md +0 -78
@@ -1,26 +1,26 @@
1
1
  # Angular Guidelines (v12 only)
2
2
 
3
- > v13에는 Angular 없음 (SolidJS로 대체됨). 아래 규칙은 v12 전용.
3
+ > v13 has no Angular (replaced by SolidJS). Rules below are v12 only.
4
4
 
5
5
  ## Core Rules
6
6
 
7
- - **Signal 기반**: 상태에 RxJS 사용 금지. `$signal`, `$computed`, `$effect` 사용
8
- - **Standalone only**: 모든 컴포넌트 `standalone: true`
9
- - **OnPush + None**: `ChangeDetectionStrategy.OnPush`, `ViewEncapsulation.None` 필수
10
- - **input()/output()**: `@Input()`, `@Output()` 데코레이터 사용 금지 `input()`, `output()`, `model()` 사용
11
- - **Control flow**: `@if`, `@for`, `@switch` 사용 (`*ngIf`, `*ngFor` 금지)
12
- - **DI**: `inject()` 사용 (생성자 파라미터 주입 금지)
13
- - **아이콘**: `@ng-icons/tabler-icons` 사용 (FontAwesome 아님)
14
- - **패키지 import**: `@simplysm/sd-angular` 루트에서만 import (서브폴더 접근 금지)
7
+ - **Signal-based**: Do not use RxJS for state. Use `$signal`, `$computed`, `$effect`
8
+ - **Standalone only**: All components must have `standalone: true`
9
+ - **OnPush + None**: `ChangeDetectionStrategy.OnPush` and `ViewEncapsulation.None` are mandatory
10
+ - **input()/output()**: Do not use `@Input()`, `@Output()` decorators use `input()`, `output()`, `model()` instead
11
+ - **Control flow**: Use `@if`, `@for`, `@switch` (no `*ngIf`, `*ngFor`)
12
+ - **DI**: Use `inject()` (no constructor parameter injection)
13
+ - **Icons**: Use `@ng-icons/tabler-icons` (not FontAwesome)
14
+ - **Package imports**: Import `@simplysm/sd-angular` only from root (no subfolders)
15
15
 
16
16
  ## Signal Utilities
17
17
 
18
- - `$signal<T>(value?)` — writable signal (`$mark()`로 dirty 표시 가능)
19
- - `$computed(fn)` — 동기 computed. 비동기: `$computed([deps], asyncFn, { initialValue })`
20
- - `$effect(fn)` — 조건 추적 가능: `$effect([() => dep1()], () => { ... })`
21
- - `$arr(signal)` — 배열 조작 (`.insert()`, `.remove()`, `.toggle()`, `.diffs()`)
22
- - `$obj(signal)` — 객체 조작 (`.updateField()`, `.snapshot()`, `.changed()`)
23
- - `$set(signal)` — Set 조작 (`.add()`, `.toggle()`)
18
+ - `$signal<T>(value?)` — writable signal (can mark dirty with `$mark()`)
19
+ - `$computed(fn)` — synchronous computed. Async: `$computed([deps], asyncFn, { initialValue })`
20
+ - `$effect(fn)` — dependency tracking: `$effect([() => dep1()], () => { ... })`
21
+ - `$arr(signal)` — array operations (`.insert()`, `.remove()`, `.toggle()`, `.diffs()`)
22
+ - `$obj(signal)` — object operations (`.updateField()`, `.snapshot()`, `.changed()`)
23
+ - `$set(signal)` — Set operations (`.add()`, `.toggle()`)
24
24
 
25
25
  ## Component Pattern
26
26
 
@@ -66,7 +66,7 @@ sdHmrBootstrapAsync(AppPage, {
66
66
 
67
67
  ## Modal
68
68
 
69
- `ISdModal<T>` 인터페이스 구현:
69
+ Implement `ISdModal<T>` interface:
70
70
 
71
71
  ```typescript
72
72
  export class MyModal implements ISdModal<TResult> {
@@ -74,22 +74,22 @@ export class MyModal implements ISdModal<TResult> {
74
74
  close = output<TResult>();
75
75
  }
76
76
 
77
- // 호출
77
+ // Usage
78
78
  await this.#sdModal.showAsync({
79
79
  type: MyModal,
80
- title: "제목",
80
+ title: "Title",
81
81
  inputs: { param: "value" },
82
82
  });
83
83
  ```
84
84
 
85
85
  ## Data Sheet
86
86
 
87
- CRUD 테이블은 `AbsSdDataSheet<TFilter, TItem, TKey>` 확장:
87
+ CRUD tables extend `AbsSdDataSheet<TFilter, TItem, TKey>`:
88
88
 
89
- - `search()` — 데이터 조회
90
- - `submit()` — 변경사항 저장
91
- - `downloadExcel()` / `uploadExcel()` — 엑셀 연동
92
- - `$mark()` — 수정 dirty 표시 필수
89
+ - `search()` — query data
90
+ - `submit()` — save changes
91
+ - `downloadExcel()` / `uploadExcel()` — Excel integration
92
+ - `$mark()` — mark dirty when cell is modified (mandatory)
93
93
 
94
94
  ## Busy / Error Handling
95
95
 
@@ -106,9 +106,9 @@ try {
106
106
 
107
107
  ## Theming
108
108
 
109
- - CSS 변수 사용: `--theme-primary-default`, `--gap-sm`, `--border-radius-default`
110
- - 하드코딩 색상 금지
111
- - 테마: `"compact"` | `"mobile"` | `"kiosk"` + dark mode
109
+ - Use CSS variables: `--theme-primary-default`, `--gap-sm`, `--border-radius-default`, etc.
110
+ - Do not hardcode colors
111
+ - Themes: `"compact"` | `"mobile"` | `"kiosk"` + dark mode
112
112
 
113
113
  ## Permissions
114
114
 
@@ -1,40 +1,40 @@
1
1
  # ORM Guidelines (v12)
2
2
 
3
- ## Table Definition — Decorator 기반
3
+ ## Table Definition — Decorator-based
4
4
 
5
5
  ```typescript
6
- @Table({ description: "사용자", database: "mydb" })
6
+ @Table({ description: "Users", database: "mydb" })
7
7
  export class User {
8
8
  @Column({ primaryKey: 1, autoIncrement: true, description: "ID" })
9
9
  id!: number;
10
10
 
11
- @Column({ dataType: { type: "STRING", length: 100 }, description: "이름" })
11
+ @Column({ dataType: { type: "STRING", length: 100 }, description: "Name" })
12
12
  name!: string;
13
13
 
14
- @Column({ nullable: true, description: "이메일" })
14
+ @Column({ nullable: true, description: "Email" })
15
15
  email?: string;
16
16
 
17
- @ForeignKey(["departmentId"], () => Department, "부서")
17
+ @ForeignKey(["departmentId"], () => Department, "Department")
18
18
  department?: Department;
19
19
 
20
- @ForeignKeyTarget(() => Order, "user", "주문 목록")
20
+ @ForeignKeyTarget(() => Order, "user", "Orders")
21
21
  orders?: Order[];
22
22
  }
23
23
  ```
24
24
 
25
25
  ### Decorators
26
26
 
27
- - `@Table({ description, database?, schema?, name?, view?, procedure? })` — 테이블/뷰/프로시저 정의
28
- - `@Column({ description, name?, dataType?, nullable?, autoIncrement?, primaryKey? })` — 컬럼 정의
29
- - `@ForeignKey(columnNames, targetTypeFwd, description)` — FK 관계
30
- - `@ForeignKeyTarget(sourceTypeFwd, fkPropertyKey, description, multiplicity?)` — FK 역방향
31
- - `@ReferenceKey(columnNames, targetTypeFwd, description)` — 참조 관계
32
- - `@ReferenceKeyTarget(sourceTypeFwd, refKeyPropertyKey, description, multiplicity?)` — 참조 역방향
33
- - `@Index({ name?, order?, orderBy?, unique? })` — 인덱스
27
+ - `@Table({ description, database?, schema?, name?, view?, procedure? })` — define table/view/procedure
28
+ - `@Column({ description, name?, dataType?, nullable?, autoIncrement?, primaryKey? })` — define column
29
+ - `@ForeignKey(columnNames, targetTypeFwd, description)` — FK relationship
30
+ - `@ForeignKeyTarget(sourceTypeFwd, fkPropertyKey, description, multiplicity?)` — FK reverse relationship
31
+ - `@ReferenceKey(columnNames, targetTypeFwd, description)` — reference relationship
32
+ - `@ReferenceKeyTarget(sourceTypeFwd, refKeyPropertyKey, description, multiplicity?)` — reference reverse relationship
33
+ - `@Index({ name?, order?, orderBy?, unique? })` — index
34
34
 
35
- ### 요구사항
35
+ ### Requirements
36
36
 
37
- - `tsconfig`에 `experimentalDecorators: true`, `emitDecoratorMetadata: true` 필요
37
+ - `tsconfig` requires `experimentalDecorators: true` and `emitDecoratorMetadata: true`
38
38
 
39
39
  ## DbContext
40
40
 
@@ -59,11 +59,11 @@ const users = await db.user
59
59
  .resultAsync();
60
60
 
61
61
  // Insert
62
- await db.user.insertAsync([{ name: "홍길동" }]);
62
+ await db.user.insertAsync([{ name: "John Doe" }]);
63
63
 
64
64
  // Connect with transaction
65
65
  await db.connectAsync(async () => {
66
- await db.user.insertAsync([{ name: "홍길동" }]);
66
+ await db.user.insertAsync([{ name: "John Doe" }]);
67
67
  });
68
68
  ```
69
69
 
@@ -4,7 +4,7 @@ Before starting work, **Read** the relevant reference files from `.claude/refs/`
4
4
 
5
5
  ## Version Detection
6
6
 
7
- `package.json`의 `version` 필드로 메이저 버전을 판별한다.
7
+ Determine the major version by the `version` field in `package.json`.
8
8
 
9
9
  - `12.x.x` → **v12** (< 13)
10
10
  - `13.x.x` → **v13** (>= 13)
@@ -17,32 +17,32 @@ Before starting work, **Read** the relevant reference files from `.claude/refs/`
17
17
  | Working with `.cache/` or Playwright screenshots | `.claude/refs/sd-directories.md` |
18
18
  | Using `@simplysm/*` package APIs | `.claude/refs/sd-simplysm-docs.md` |
19
19
  | Debugging, problem-solving, or planning approach | `.claude/refs/sd-workflow.md` |
20
- | @simplysm/service-\* 사용 시 | `.claude/refs/sd-service.md` |
20
+ | Using `@simplysm/service-*` packages | `.claude/refs/sd-service.md` |
21
21
  | Migrating/porting code from another codebase | `.claude/refs/sd-migration.md` |
22
22
 
23
23
  ## v12 only (< 13)
24
24
 
25
25
  | When | Read this file |
26
26
  | -------------------------------------- | ---------------------------- |
27
- | Angular / @simplysm/sd-angular 작업 시 | `.claude/refs/sd-angular.md` |
28
- | @simplysm/sd-orm-\* 사용 시 | `.claude/refs/sd-orm-v12.md` |
27
+ | Working on Angular / @simplysm/sd-angular | `.claude/refs/sd-angular.md` |
28
+ | Using `@simplysm/sd-orm-*` packages | `.claude/refs/sd-orm-v12.md` |
29
29
 
30
- - v12 **Angular** 기반 (SolidJS 없음)
31
- - ORM **데코레이터** 패턴 (`@Table`, `@Column`)
32
- - 패키지명: `sd-` prefix 사용 (`sd-core-common`, `sd-orm-common` )
33
- - 패키지 매니저: **yarn**
30
+ - v12 is **Angular** based (no SolidJS)
31
+ - ORM uses **decorator** pattern (`@Table`, `@Column`)
32
+ - Package names: use `sd-` prefix (`sd-core-common`, `sd-orm-common`, etc.)
33
+ - Package manager: **yarn**
34
34
 
35
35
  ## v13 only (>= 13)
36
36
 
37
37
  | When | Read this file |
38
38
  | -------------------------------------------- | -------------------------- |
39
- | SolidJS / @simplysm/solid / Tailwind 작업 시 | `.claude/refs/sd-solid.md` |
40
- | @simplysm/orm-\* 사용 시 | `.claude/refs/sd-orm.md` |
39
+ | Working on SolidJS / @simplysm/solid / Tailwind | `.claude/refs/sd-solid.md` |
40
+ | Using `@simplysm/orm-*` packages | `.claude/refs/sd-orm.md` |
41
41
 
42
- - v13 **SolidJS** 기반 (Angular 없음)
43
- - ORM **함수형 빌더** 패턴 (`Table().columns().primaryKey()`)
44
- - 패키지명: prefix 없음 (`core-common`, `orm-common` )
45
- - 패키지 매니저: **pnpm**
42
+ - v13 is **SolidJS** based (no Angular)
43
+ - ORM uses **functional builder** pattern (`Table().columns().primaryKey()`)
44
+ - Package names: no prefix (`core-common`, `orm-common`, etc.)
45
+ - Package manager: **pnpm**
46
46
 
47
47
  ## Rules
48
48
 
@@ -63,7 +63,7 @@ function getOAuthToken() {
63
63
  const credentials = JSON.parse(content);
64
64
  const oauth = credentials.claudeAiOauth;
65
65
 
66
- // 토큰 만료 체크
66
+ // Check token expiration
67
67
  if (oauth?.expiresAt != null && Date.now() > oauth.expiresAt) {
68
68
  return undefined;
69
69
  }
@@ -75,12 +75,12 @@ function getOAuthToken() {
75
75
  }
76
76
 
77
77
  /**
78
- * OAuth 토큰을 사용하여 Anthropic API 사용량 정보를 조회한다.
79
- * @param {string} token - OAuth 액세스 토큰
78
+ * Fetch Anthropic API usage information using OAuth token.
79
+ * @param {string} token - OAuth access token
80
80
  * @returns {Promise<{
81
- * seven_day?: {utilization?: number, resets_at?: string}, // 7 사용량
82
- * daily?: {utilization?: number, resets_at?: string}, // 일일 사용량
83
- * five_hour?: {utilization?: number, resets_at?: string}, // 5시간 사용량
81
+ * seven_day?: {utilization?: number, resets_at?: string}, // 7-day usage
82
+ * daily?: {utilization?: number, resets_at?: string}, // daily usage
83
+ * five_hour?: {utilization?: number, resets_at?: string}, // 5-hour usage
84
84
  * extra_usage?: {is_enabled?: boolean, monthly_limit?: number | null, used_credits?: number} // extra usage
85
85
  * } | undefined>}
86
86
  */
@@ -105,7 +105,7 @@ async function fetchUsage(token) {
105
105
 
106
106
  const data = await response.json();
107
107
 
108
- // 응답 구조 검증
108
+ // Validate response structure
109
109
  if (data == null || typeof data !== "object") {
110
110
  return undefined;
111
111
  }
@@ -167,17 +167,17 @@ function formatTimeRemaining(isoDate) {
167
167
  //#region Main
168
168
 
169
169
  /**
170
- * stdin으로 입력받은 JSON 정보
170
+ * JSON information received from stdin
171
171
  * @typedef {object} StdinInput
172
- * @property {{display_name?: string}} [model] - 모델 정보
173
- * @property {{context_window_size?: number, remaining_context_tokens?: number, current_usage?: {input_tokens?: number, output_tokens?: number, cache_creation_input_tokens?: number, cache_read_input_tokens?: number}}} [context_window] - 컨텍스트 윈도우 정보
174
- * @property {{tokens_used?: number, tokens_limit?: number}} [weekly_token_usage] - 주간 토큰 사용량 (fallback)
172
+ * @property {{display_name?: string}} [model] - Model information
173
+ * @property {{context_window_size?: number, remaining_context_tokens?: number, current_usage?: {input_tokens?: number, output_tokens?: number, cache_creation_input_tokens?: number, cache_read_input_tokens?: number}}} [context_window] - Context window information
174
+ * @property {{tokens_used?: number, tokens_limit?: number}} [weekly_token_usage] - Weekly token usage (fallback)
175
175
  */
176
176
 
177
177
  /**
178
- * Claude Code 상태 표시줄 정보를 출력한다.
179
- * stdin으로 입력받은 JSON과 OAuth API 응답을 조합하여
180
- * 모델명, 컨텍스트 사용량, 일일/주간 사용량을 출력한다.
178
+ * Output Claude Code status bar information.
179
+ * Combine JSON received from stdin with OAuth API response to output
180
+ * model name, context usage, and daily/weekly usage.
181
181
  */
182
182
  async function main() {
183
183
  const inputStr = await readStdin();
@@ -188,11 +188,11 @@ async function main() {
188
188
  try {
189
189
  input = JSON.parse(inputStr);
190
190
  } catch {
191
- // JSON 파싱 실패 객체 사용
191
+ // Use empty object if JSON parsing fails
192
192
  }
193
193
  }
194
194
 
195
- // 기본 정보
195
+ // Basic information
196
196
  const modelName = input.model?.display_name ?? "Unknown";
197
197
  const contextSize = input.context_window?.context_window_size ?? 0;
198
198
  const usage = input.context_window?.current_usage;
@@ -203,7 +203,7 @@ async function main() {
203
203
  (usage?.cache_read_input_tokens ?? 0);
204
204
  const contextPercent = contextSize > 0 ? Math.round((contextUsed / contextSize) * 100) : 0;
205
205
 
206
- // OAuth 토큰으로 사용량 조회 시도
206
+ // Try fetching usage with OAuth token
207
207
  const token = getOAuthToken();
208
208
  let dailyPercent = "?";
209
209
  let dailyResetTime = "";
@@ -214,21 +214,21 @@ async function main() {
214
214
  if (token != null) {
215
215
  const usageResponse = await fetchUsage(token);
216
216
  if (usageResponse != null) {
217
- // daily 또는 five_hour 사용
217
+ // Use daily or five_hour
218
218
  const dailyData = usageResponse.daily ?? usageResponse.five_hour;
219
219
  dailyPercent = formatPercent(dailyData?.utilization);
220
220
  dailyResetTime = formatTimeRemaining(dailyData?.resets_at);
221
221
  weekPercent = formatPercent(usageResponse.seven_day?.utilization);
222
222
  weekResetDay = formatTimeRemaining(usageResponse.seven_day?.resets_at);
223
223
 
224
- // extra usage
224
+ // Extra usage
225
225
  if (usageResponse.extra_usage?.is_enabled && usageResponse.extra_usage.used_credits != null) {
226
226
  extraUsage = `$${(usageResponse.extra_usage.used_credits / 100).toFixed(2)}`;
227
227
  }
228
228
  }
229
229
  }
230
230
 
231
- // fallback: weekly_token_usage
231
+ // Fallback: weekly_token_usage
232
232
  if (weekPercent === "?" && input.weekly_token_usage != null) {
233
233
  const used = input.weekly_token_usage.tokens_used ?? 0;
234
234
  const limit = input.weekly_token_usage.tokens_limit ?? 0;
@@ -237,7 +237,7 @@ async function main() {
237
237
  }
238
238
  }
239
239
 
240
- // 폴더명 + git 브랜치
240
+ // Folder name + git branch
241
241
  const cwd = input.cwd ?? process.cwd();
242
242
  const folderName = path.basename(cwd);
243
243
 
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: sd-api-name-review
3
- description: Public API naming consistency and industry standard alignment review
4
- disable-model-invocation: true
3
+ description: "Public API naming review (explicit invocation only)"
5
4
  model: sonnet
6
5
  ---
7
6
 
@@ -1,8 +1,6 @@
1
1
  ---
2
2
  name: sd-brainstorm
3
- description: Collaborative design exploration through Q&A before implementation
4
- disable-model-invocation: true
5
- model: opus
3
+ description: "Design exploration before implementation (explicit invocation only)"
6
4
  ---
7
5
 
8
6
  # Brainstorming Ideas Into Designs
@@ -119,13 +117,14 @@ You can start from any step or skip steps as needed.
119
117
  ```
120
118
 
121
119
  - The last `⚠️` line is only shown when uncommitted changes exist. Omit it when working tree is clean.
120
+ - If the design does NOT involve code modifications, omit the `/sd-check` step from both paths.
122
121
 
123
122
  - After presenting both paths, **recommend one** based on the design's scope:
124
123
  - Path A recommended: new features, multi-file changes, architectural changes, anything that benefits from isolation
125
124
  - Path B recommended: small bug fixes, single-file changes, config tweaks, minor adjustments
126
125
  - Briefly explain why (1 sentence)
127
126
  - Do NOT auto-proceed to any step. Present the overview with recommendation and wait for the user's choice.
128
- - **Yolo mode**: If the user responds with "Path A: yolo" or "Path B: yolo" (or similar intent like "A yolo", "B 자동"), execute all steps of the chosen path sequentially without stopping between steps.
127
+ - **Yolo mode**: If the user responds with "Path A: yolo" or "Path B: yolo" (or similar intent like "A yolo", "B auto"), execute all steps of the chosen path sequentially without stopping between steps.
129
128
  - **Yolo sd-check — include dependents**: NEVER check only modified packages. Also check all packages that depend on them:
130
129
  1. Identify modified packages from `git diff --name-only`
131
130
  2. Trace reverse dependencies (packages that import from modified packages) using `package.json` or project dependency graph
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: sd-check
3
- description: Code quality verification via typecheck, lint, and tests
4
- disable-model-invocation: true
3
+ description: "Typecheck, lint, test verification (explicit invocation only)"
5
4
  allowed-tools: Bash(npm run check), Bash(npm run typecheck), Bash(npm run lint --fix), Bash(npm run vitest)
6
5
  ---
7
6
 
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: sd-commit
3
- description: Git commit with conventional commit messages
4
- disable-model-invocation: true
3
+ description: "Git commit with conventional messages (explicit invocation only)"
5
4
  argument-hint: "[all]"
6
5
  allowed-tools: Bash(git status:*), Bash(git add:*), Bash(git commit:*)
7
6
  model: haiku
@@ -51,7 +50,7 @@ type(scope): short description
51
50
  | ------------- | ---------------------------------------------------------------------------- |
52
51
  | `type` | `feat`, `fix`, `refactor`, `docs`, `test`, `chore`, `build`, `style`, `perf` |
53
52
  | `scope` | package name or area (e.g., `solid`, `core-common`, `orm-node`) |
54
- | `description` | imperative, lowercase, no period at end |
53
+ | `description` | written in the system's configured language, imperative, lowercase, no period at end |
55
54
 
56
55
  Examples:
57
56
 
@@ -1,7 +1,6 @@
1
1
  ---
2
2
  name: sd-debug
3
- description: Systematic root-cause debugging methodology
4
- disable-model-invocation: true
3
+ description: "Use when the user reports a bug, error, or unexpected behavior and asks to fix or debug it. Triggers: error messages, stack traces, test failures, build failures, 'why is this broken', 'fix this', 'debug this', unexpected behavior investigation."
5
4
  ---
6
5
 
7
6
  # Systematic Debugging
@@ -1,8 +1,6 @@
1
1
  ---
2
2
  name: sd-discuss
3
- description: Evidence-based technical discussion with industry research
4
- disable-model-invocation: true
5
- model: opus
3
+ description: "Technical discussion with industry research (explicit invocation only)"
6
4
  ---
7
5
 
8
6
  # Standards-Based Technical Discussion
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: sd-document
3
+ description: "Use when the user's request involves .docx, .xlsx, .pptx, or .pdf files. Triggers: document reading/analysis, file content extraction, DOCX/XLSX creation, client document review, data export."
4
+ ---
5
+
6
+ # Document Processing
7
+
8
+ ## Overview
9
+
10
+ Read and write document files (.docx/.xlsx/.pptx/.pdf).
11
+ Python scripts extract text and images with location information by format, save images to files, and analyze them with Claude Read.
12
+
13
+ ## Quick Reference
14
+
15
+ | Format | Read | Write | Library |
16
+ |--------|------|-------|---------|
17
+ | DOCX | Yes | Yes | `python-docx` |
18
+ | XLSX | Yes | Yes | `openpyxl`, `pandas` |
19
+ | PPTX | Yes | No | `python-pptx` |
20
+ | PDF | Yes | No | `pdfplumber`, `pypdf` |
21
+
22
+ Missing packages are auto-installed on first script run.
23
+
24
+ ## Reading (Document Analysis)
25
+
26
+ Run extraction scripts by format:
27
+
28
+ ```bash
29
+ python .claude/skills/sd-document/extract_docx.py <filepath>
30
+ python .claude/skills/sd-document/extract_xlsx.py <filepath>
31
+ python .claude/skills/sd-document/extract_pptx.py <filepath>
32
+ python .claude/skills/sd-document/extract_pdf.py <filepath>
33
+ ```
34
+
35
+ ### Output
36
+ - **stdout**: Text and location information (Markdown format)
37
+ - **Image files**: Saved to `<filename>_files/` directory
38
+
39
+ ### Location Information
40
+
41
+ | Format | Location Representation |
42
+ |--------|-------------------------|
43
+ | DOCX | Paragraph flow order (text-image inline) |
44
+ | XLSX | Cell position (A1, B2, etc.) |
45
+ | PPTX | Shape left/top coordinates (inches) + slide number |
46
+ | PDF | Page number |
47
+
48
+ ### Image Analysis
49
+ Open extracted image files with Claude **Read** tool for visual analysis.
50
+
51
+ ### Scanned PDF (OCR)
52
+ If text extraction is empty, the script outputs OCR instructions.
53
+ Tesseract OCR requires OS-level installation (not auto-installable via pip).
54
+
55
+ ## Writing
56
+
57
+ ### DOCX (`python-docx`)
58
+
59
+ For mail templates and simple reports.
60
+
61
+ ```python
62
+ from docx import Document
63
+
64
+ doc = Document() # New document
65
+ # doc = Document("existing.docx") # Edit existing document
66
+ doc.add_heading("Title", level=1)
67
+ doc.add_paragraph("Body content")
68
+ table = doc.add_table(rows=2, cols=3)
69
+ table.cell(0, 0).text = "Item"
70
+ doc.save("output.docx")
71
+ ```
72
+
73
+ Edit existing document: open with `Document("existing.docx")`, replace `paragraph.text`, modify `table.cell().text`.
74
+
75
+ ### XLSX (`openpyxl`)
76
+
77
+ Focuses on data and formulas. Formatting (colors, borders) not required.
78
+
79
+ ```python
80
+ from openpyxl import Workbook
81
+
82
+ wb = Workbook()
83
+ ws = wb.active
84
+ ws["A1"] = "Item"
85
+ ws["B1"] = "Quantity"
86
+ ws.append(["Apple", 10])
87
+ ws.append(["Pear", 20])
88
+ ws["B4"] = "=SUM(B2:B3)"
89
+ wb.save("output.xlsx")
90
+ ```
91
+
92
+ Edit existing file: open with `load_workbook("existing.xlsx")` and modify.
93
+ Export pandas DataFrame: `df.to_excel("output.xlsx", index=False)`
94
+
95
+ ## Common Mistakes
96
+
97
+ - **Character encoding**: Scripts have built-in UTF-8 handling; always extract through scripts
98
+ - **Missing images**: After extraction, remember to read images in `_files/` directory
99
+ - **XLSX data_only**: `load_workbook(data_only=True)` removes formulas — use `data_only=False` to preserve them
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env python3
2
+ """DOCX 파일에서 텍스트와 이미지를 문단 흐름 순서로 추출한다."""
3
+
4
+ import sys
5
+ import io
6
+ import os
7
+ import subprocess
8
+ from pathlib import Path
9
+
10
+ sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
11
+ sys.stderr = io.TextIOWrapper(sys.stderr.buffer, encoding="utf-8", errors="replace")
12
+
13
+
14
+ def ensure_packages():
15
+ packages = {"python-docx": "docx"}
16
+ for pip_name, import_name in packages.items():
17
+ try:
18
+ __import__(import_name)
19
+ except ImportError:
20
+ print(f"패키지 설치 중: {pip_name}...", file=sys.stderr)
21
+ subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name],
22
+ stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
23
+
24
+
25
+ def extract(file_path):
26
+ from docx import Document
27
+ from docx.oxml.ns import qn
28
+
29
+ doc = Document(file_path)
30
+ stem = Path(file_path).stem
31
+ out_dir = Path(file_path).parent / f"{stem}_files"
32
+ img_idx = 0
33
+
34
+ print(f"# {Path(file_path).name}\n")
35
+
36
+ for i, para in enumerate(doc.paragraphs):
37
+ has_image = False
38
+ for run in para.runs:
39
+ drawings = run._element.findall(f".//{qn('wp:inline')}") + run._element.findall(f".//{qn('wp:anchor')}")
40
+ for drawing in drawings:
41
+ blip = drawing.find(f".//{qn('a:blip')}")
42
+ if blip is not None:
43
+ embed = blip.get(qn("r:embed"))
44
+ if embed:
45
+ rel = doc.part.rels.get(embed)
46
+ if rel:
47
+ img_idx += 1
48
+ blob = rel.target_part.blob
49
+ content_type = rel.target_part.content_type
50
+ ext = content_type.split("/")[-1].replace("jpeg", "jpg")
51
+ out_dir.mkdir(parents=True, exist_ok=True)
52
+ img_path = out_dir / f"img_{img_idx:03d}.{ext}"
53
+ img_path.write_bytes(blob)
54
+ print(f"[IMG] {img_path}")
55
+ has_image = True
56
+
57
+ text = para.text.strip()
58
+ if text:
59
+ style = para.style.name if para.style else ""
60
+ prefix = ""
61
+ if "Heading" in style:
62
+ level = style.replace("Heading ", "").replace("Heading", "1")
63
+ try:
64
+ prefix = "#" * int(level) + " "
65
+ except ValueError:
66
+ prefix = "## "
67
+ print(f"{prefix}{text}")
68
+
69
+ if has_image or text:
70
+ pass # already printed
71
+ elif not has_image and not text:
72
+ continue
73
+
74
+ # 표 추출
75
+ for t_idx, table in enumerate(doc.tables):
76
+ print(f"\n### Table {t_idx + 1}\n")
77
+ for row in table.rows:
78
+ cells = [cell.text.strip().replace("\n", " ") for cell in row.cells]
79
+ print("| " + " | ".join(cells) + " |")
80
+
81
+ if img_idx > 0:
82
+ print(f"\n---\n이미지 {img_idx}개 저장: {out_dir}")
83
+ else:
84
+ print("\n---\n이미지 없음")
85
+
86
+
87
+ if __name__ == "__main__":
88
+ if len(sys.argv) < 2:
89
+ print("Usage: python extract_docx.py <file.docx>", file=sys.stderr)
90
+ sys.exit(1)
91
+ ensure_packages()
92
+ extract(sys.argv[1])