@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.
- package/README.md +12 -601
- package/claude/agents/sd-api-reviewer.md +0 -1
- package/claude/agents/sd-code-reviewer.md +0 -1
- package/claude/agents/sd-code-simplifier.md +1 -1
- package/claude/agents/sd-security-reviewer.md +0 -1
- package/claude/refs/sd-angular.md +26 -26
- package/claude/refs/sd-orm-v12.md +17 -17
- package/claude/rules/sd-refs-linker.md +14 -14
- package/claude/sd-statusline.js +21 -21
- package/claude/skills/sd-api-name-review/SKILL.md +1 -2
- package/claude/skills/sd-brainstorm/SKILL.md +3 -4
- package/claude/skills/sd-check/SKILL.md +1 -2
- package/claude/skills/sd-commit/SKILL.md +2 -3
- package/claude/skills/sd-debug/SKILL.md +1 -2
- package/claude/skills/sd-discuss/SKILL.md +1 -3
- package/claude/skills/sd-document/SKILL.md +99 -0
- package/claude/skills/sd-document/extract_docx.py +92 -0
- package/claude/skills/sd-document/extract_pdf.py +102 -0
- package/claude/skills/sd-document/extract_pptx.py +77 -0
- package/claude/skills/sd-document/extract_xlsx.py +83 -0
- package/claude/skills/sd-email-analyze/SKILL.md +6 -6
- package/claude/skills/sd-plan/SKILL.md +1 -3
- package/claude/skills/sd-plan-dev/SKILL.md +94 -111
- package/claude/skills/sd-plan-dev/code-quality-reviewer-prompt.md +1 -1
- package/claude/skills/sd-plan-dev/final-review-prompt.md +1 -1
- package/claude/skills/sd-plan-dev/spec-reviewer-prompt.md +1 -1
- package/claude/skills/sd-readme/SKILL.md +107 -88
- package/claude/skills/sd-review/SKILL.md +14 -16
- package/claude/skills/sd-skill/SKILL.md +6 -317
- package/claude/skills/sd-skill/cso-guide.md +161 -0
- package/claude/skills/sd-skill/writing-guide.md +163 -0
- package/claude/skills/sd-tdd/SKILL.md +1 -3
- package/claude/skills/sd-use/SKILL.md +1 -3
- package/claude/skills/sd-worktree/SKILL.md +52 -2
- package/dist/commands/auth-add.d.ts +2 -0
- package/dist/commands/auth-add.d.ts.map +1 -0
- package/dist/commands/auth-add.js +32 -0
- package/dist/commands/auth-add.js.map +6 -0
- package/dist/commands/auth-list.d.ts +2 -0
- package/dist/commands/auth-list.d.ts.map +1 -0
- package/dist/commands/auth-list.js +37 -0
- package/dist/commands/auth-list.js.map +6 -0
- package/dist/commands/auth-remove.d.ts +2 -0
- package/dist/commands/auth-remove.d.ts.map +1 -0
- package/dist/commands/auth-remove.js +22 -0
- package/dist/commands/auth-remove.js.map +6 -0
- package/dist/commands/auth-use.d.ts +2 -0
- package/dist/commands/auth-use.d.ts.map +1 -0
- package/dist/commands/auth-use.js +33 -0
- package/dist/commands/auth-use.js.map +6 -0
- package/dist/commands/auth-utils.d.ts +11 -0
- package/dist/commands/auth-utils.d.ts.map +1 -0
- package/dist/commands/auth-utils.js +57 -0
- package/dist/commands/auth-utils.js.map +6 -0
- package/dist/commands/install.js +3 -3
- package/dist/commands/install.js.map +1 -1
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/dist/index.js.map +1 -1
- package/dist/sd-claude.js +68 -3
- package/dist/sd-claude.js.map +1 -1
- package/package.json +3 -2
- package/scripts/sync-claude-assets.mjs +1 -1
- package/src/commands/auth-add.ts +36 -0
- package/src/commands/auth-list.ts +44 -0
- package/src/commands/auth-remove.ts +26 -0
- package/src/commands/auth-use.ts +53 -0
- package/src/commands/auth-utils.ts +65 -0
- package/src/commands/install.ts +19 -19
- package/src/index.ts +5 -0
- package/src/sd-claude.ts +81 -3
- package/tests/auth-add.spec.ts +74 -0
- package/tests/auth-list.spec.ts +175 -0
- package/tests/auth-remove.spec.ts +74 -0
- package/tests/auth-use.spec.ts +153 -0
- package/tests/auth-utils.spec.ts +173 -0
- package/claude/skills/sd-explore/SKILL.md +0 -78
|
@@ -1,26 +1,26 @@
|
|
|
1
1
|
# Angular Guidelines (v12 only)
|
|
2
2
|
|
|
3
|
-
> v13
|
|
3
|
+
> v13 has no Angular (replaced by SolidJS). Rules below are v12 only.
|
|
4
4
|
|
|
5
5
|
## Core Rules
|
|
6
6
|
|
|
7
|
-
- **Signal
|
|
8
|
-
- **Standalone only**:
|
|
9
|
-
- **OnPush + None**: `ChangeDetectionStrategy.OnPush
|
|
10
|
-
- **input()/output()**: `@Input()`, `@Output()`
|
|
11
|
-
- **Control flow**: `@if`, `@for`, `@switch`
|
|
12
|
-
- **DI**: `inject()`
|
|
13
|
-
-
|
|
14
|
-
-
|
|
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 (
|
|
19
|
-
- `$computed(fn)` —
|
|
20
|
-
- `$effect(fn)` —
|
|
21
|
-
- `$arr(signal)` —
|
|
22
|
-
- `$obj(signal)` —
|
|
23
|
-
- `$set(signal)` — Set
|
|
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
|
|
87
|
+
CRUD tables extend `AbsSdDataSheet<TFilter, TItem, TKey>`:
|
|
88
88
|
|
|
89
|
-
- `search()` —
|
|
90
|
-
- `submit()` —
|
|
91
|
-
- `downloadExcel()` / `uploadExcel()` —
|
|
92
|
-
- `$mark()` —
|
|
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
|
|
110
|
-
-
|
|
111
|
-
-
|
|
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: "
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
|
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
|
|
28
|
-
|
|
|
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
|
|
31
|
-
- ORM
|
|
32
|
-
-
|
|
33
|
-
-
|
|
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
|
|
40
|
-
|
|
|
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
|
|
43
|
-
- ORM
|
|
44
|
-
-
|
|
45
|
-
-
|
|
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
|
|
package/claude/sd-statusline.js
CHANGED
|
@@ -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
|
-
*
|
|
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
|
-
*
|
|
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] -
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
|
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
|
-
//
|
|
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
|
-
//
|
|
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
|
-
//
|
|
240
|
+
// Folder name + git branch
|
|
241
241
|
const cwd = input.cwd ?? process.cwd();
|
|
242
242
|
const folderName = path.basename(cwd);
|
|
243
243
|
|
|
@@ -1,8 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sd-brainstorm
|
|
3
|
-
description:
|
|
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
|
|
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:
|
|
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
|
|
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:
|
|
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:
|
|
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])
|