@simplysm/sd-claude 13.0.71 → 13.0.74
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 +286 -13
- package/claude/refs/sd-code-conventions.md +11 -0
- package/claude/refs/sd-library-issue.md +7 -0
- package/claude/rules/sd-claude-rules.md +15 -4
- package/claude/rules/sd-refs-linker.md +1 -0
- package/claude/sd-statusline.js +1 -1
- package/claude/skills/sd-brainstorm/SKILL.md +1 -1
- package/claude/skills/sd-check/SKILL.md +15 -6
- package/claude/skills/sd-commit/SKILL.md +2 -0
- package/claude/skills/sd-debug/find-polluter.sh +8 -2
- package/claude/skills/sd-debug/root-cause-tracing.md +2 -2
- package/claude/skills/sd-document/extract_docx.py +5 -5
- package/claude/skills/sd-document/extract_pdf.py +11 -11
- package/claude/skills/sd-document/extract_pptx.py +5 -5
- package/claude/skills/sd-document/extract_xlsx.py +7 -7
- package/claude/skills/sd-email-analyze/email-analyzer.py +28 -28
- package/claude/skills/sd-plan/SKILL.md +11 -2
- package/claude/skills/sd-plan-dev/SKILL.md +5 -3
- package/claude/skills/sd-plan-dev/final-review-prompt.md +3 -3
- package/claude/skills/sd-readme/SKILL.md +86 -106
- package/claude/skills/sd-review/SKILL.md +58 -62
- package/claude/skills/sd-review/api-reviewer-prompt.md +90 -0
- package/claude/skills/sd-review/code-reviewer-prompt.md +85 -0
- package/claude/skills/sd-review/code-simplifier-prompt.md +88 -0
- package/claude/skills/sd-worktree/SKILL.md +10 -8
- package/claude/skills/sd-worktree/sd-worktree.mjs +5 -5
- package/dist/commands/auth-list.d.ts +1 -1
- package/dist/commands/auth-list.d.ts.map +1 -1
- package/dist/commands/auth-list.js +79 -21
- package/dist/commands/auth-list.js.map +1 -1
- package/dist/sd-claude.js +2 -2
- package/dist/sd-claude.js.map +1 -1
- package/package.json +1 -1
- package/src/commands/auth-list.ts +110 -24
- package/src/sd-claude.ts +2 -2
- package/tests/auth-list.spec.ts +42 -19
- package/claude/agents/sd-api-reviewer.md +0 -81
- package/claude/agents/sd-code-reviewer.md +0 -48
- package/claude/agents/sd-code-simplifier.md +0 -47
- package/claude/agents/sd-security-reviewer.md +0 -92
package/README.md
CHANGED
|
@@ -1,24 +1,297 @@
|
|
|
1
1
|
# @simplysm/sd-claude
|
|
2
2
|
|
|
3
|
-
Simplysm Claude Code CLI — asset installer
|
|
3
|
+
Simplysm Claude Code CLI — asset installer and Claude account profile manager.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
```bash
|
|
7
8
|
pnpm add @simplysm/sd-claude
|
|
9
|
+
```
|
|
8
10
|
|
|
9
|
-
|
|
11
|
+
On install, the `postinstall` script automatically copies Claude Code assets (`sd-*` entries) from the package's `claude/` directory into the project's `.claude/` directory and configures the status line in `.claude/settings.json`.
|
|
10
12
|
|
|
11
|
-
|
|
13
|
+
## CLI
|
|
12
14
|
|
|
13
|
-
|
|
14
|
-
|--------|---------|-------------|------|
|
|
15
|
-
| `src/commands/install.ts` | `runInstall` | Copies sd-* Claude Code assets into the project's `.claude/` directory | `-` |
|
|
16
|
-
| `src/commands/auth-utils.ts` | `validateName`, `getProfileDir`, `profileExists`, `listProfiles`, `readCurrentAuth`, `readCurrentCredentials`, `getCurrentUserID` | Shared helpers for reading and locating Claude auth profile data | `auth-utils.spec.ts` |
|
|
17
|
-
| `src/commands/auth-add.ts` | `runAuthAdd` | Saves the current Claude login session as a named auth profile | `auth-add.spec.ts` |
|
|
18
|
-
| `src/commands/auth-use.ts` | `runAuthUse` | Switches the active Claude login to a saved named profile | `auth-use.spec.ts` |
|
|
19
|
-
| `src/commands/auth-list.ts` | `runAuthList` | Lists all saved auth profiles with email and token expiry info | `auth-list.spec.ts` |
|
|
20
|
-
| `src/commands/auth-remove.ts` | `runAuthRemove` | Deletes a saved auth profile from the local profile store | `auth-remove.spec.ts` |
|
|
15
|
+
The package provides the `sd-claude` binary.
|
|
21
16
|
|
|
22
|
-
|
|
17
|
+
```
|
|
18
|
+
sd-claude <command> [options]
|
|
19
|
+
sd-claude --help
|
|
20
|
+
```
|
|
23
21
|
|
|
24
|
-
|
|
22
|
+
### `install`
|
|
23
|
+
|
|
24
|
+
Installs Claude Code assets to the project's `.claude/` directory. This is also run automatically via the `postinstall` script.
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
sd-claude install
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
**Behavior:**
|
|
31
|
+
|
|
32
|
+
- Locates the project root via `INIT_CWD` environment variable or by finding `node_modules` in the path.
|
|
33
|
+
- Copies all `sd-*` entries from the package's `claude/` directory to the project's `.claude/` directory.
|
|
34
|
+
- Removes any previously installed `sd-*` entries before copying (clean install).
|
|
35
|
+
- Adds a `statusLine` entry to `.claude/settings.json` if one is not already configured:
|
|
36
|
+
```json
|
|
37
|
+
{ "type": "command", "command": "node .claude/sd-statusline.js" }
|
|
38
|
+
```
|
|
39
|
+
- Skips installation when running inside the simplysm monorepo at the same major version.
|
|
40
|
+
- Errors during installation are suppressed (logged as warnings) to avoid blocking `pnpm install`.
|
|
41
|
+
|
|
42
|
+
### `auth`
|
|
43
|
+
|
|
44
|
+
Manages Claude account profiles. Profiles are stored in `~/.sd-claude/auth/<name>/`.
|
|
45
|
+
|
|
46
|
+
Each profile stores:
|
|
47
|
+
- `auth.json` — `oauthAccount` and `userID` from `~/.claude.json`
|
|
48
|
+
- `credentials.json` — full contents of `~/.claude/.credentials.json`
|
|
49
|
+
|
|
50
|
+
#### `auth add <name>`
|
|
51
|
+
|
|
52
|
+
Saves the currently logged-in Claude account as a named profile.
|
|
53
|
+
|
|
54
|
+
```bash
|
|
55
|
+
sd-claude auth add <name>
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
- `<name>`: Profile name. Must match `[a-z0-9_-]+`.
|
|
59
|
+
- Reads the current auth state from `~/.claude.json` and `~/.claude/.credentials.json`.
|
|
60
|
+
- Fails if the profile name already exists. Remove it first with `auth remove`.
|
|
61
|
+
- Requires an active Claude login. Run `/login` in Claude Code before saving.
|
|
62
|
+
|
|
63
|
+
**Example:**
|
|
64
|
+
|
|
65
|
+
```bash
|
|
66
|
+
sd-claude auth add personal
|
|
67
|
+
sd-claude auth add work
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
#### `auth use <name>`
|
|
71
|
+
|
|
72
|
+
Switches to a saved Claude account profile.
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
sd-claude auth use <name>
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
- Reads the saved `auth.json` and `credentials.json` from `~/.sd-claude/auth/<name>/`.
|
|
79
|
+
- Updates `~/.claude.json` (only `oauthAccount` and `userID` fields; other fields are preserved).
|
|
80
|
+
- Replaces `~/.claude/.credentials.json` entirely with the saved credentials.
|
|
81
|
+
- Warns if the saved token has expired (run `/login` after switching to refresh).
|
|
82
|
+
|
|
83
|
+
**Example:**
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
sd-claude auth use work
|
|
87
|
+
```
|
|
88
|
+
|
|
89
|
+
#### `auth list`
|
|
90
|
+
|
|
91
|
+
Displays all saved profiles with their active status, email, token expiry, and usage.
|
|
92
|
+
|
|
93
|
+
```bash
|
|
94
|
+
sd-claude auth list
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
Output format per profile:
|
|
98
|
+
|
|
99
|
+
```
|
|
100
|
+
* <name> (<email>) expires: YYYY-MM-DD │ 5h: <pct>(<remaining>) │ 7d: <pct>(<remaining>)
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
- `*` marks the currently active profile (matched by `userID`); inactive profiles show a space.
|
|
104
|
+
- Usage is fetched live from `https://api.anthropic.com/api/oauth/usage` using the profile's OAuth token (5-second timeout). Shows `?` when unavailable or when the token is expired.
|
|
105
|
+
- Usage shows the `daily` window (falling back to `five_hour`) as the `5h` column, and `seven_day` as the `7d` column.
|
|
106
|
+
- Profiles are sorted alphabetically.
|
|
107
|
+
|
|
108
|
+
**Example output:**
|
|
109
|
+
|
|
110
|
+
```
|
|
111
|
+
* work (work@company.com) expires: 2025-12-31 │ 5h: 42%(3h15m) │ 7d: 18%(2d4h)
|
|
112
|
+
personal (personal@gmail.com) expires: 2025-11-01 │ 5h: ? │ 7d: ?
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
#### `auth remove <name>`
|
|
116
|
+
|
|
117
|
+
Removes a saved Claude account profile.
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
sd-claude auth remove <name>
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
- Deletes `~/.sd-claude/auth/<name>/` and all its contents.
|
|
124
|
+
- Warns if the profile being removed is the currently active account (the active session in Claude Code is not affected).
|
|
125
|
+
|
|
126
|
+
**Example:**
|
|
127
|
+
|
|
128
|
+
```bash
|
|
129
|
+
sd-claude auth remove personal
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Programmatic API
|
|
133
|
+
|
|
134
|
+
All commands are also exported as functions for use in Node.js scripts.
|
|
135
|
+
|
|
136
|
+
```ts
|
|
137
|
+
import {
|
|
138
|
+
runInstall,
|
|
139
|
+
runAuthAdd,
|
|
140
|
+
runAuthUse,
|
|
141
|
+
runAuthList,
|
|
142
|
+
runAuthRemove,
|
|
143
|
+
validateName,
|
|
144
|
+
getProfileDir,
|
|
145
|
+
profileExists,
|
|
146
|
+
listProfiles,
|
|
147
|
+
readCurrentAuth,
|
|
148
|
+
readCurrentCredentials,
|
|
149
|
+
getCurrentUserID,
|
|
150
|
+
} from "@simplysm/sd-claude";
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### `runInstall(): void`
|
|
154
|
+
|
|
155
|
+
Runs the install command programmatically. See [`install`](#install) for behavior details.
|
|
156
|
+
|
|
157
|
+
```ts
|
|
158
|
+
import { runInstall } from "@simplysm/sd-claude";
|
|
159
|
+
|
|
160
|
+
runInstall();
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### `runAuthAdd(name: string, homeDir?: string): void`
|
|
164
|
+
|
|
165
|
+
Saves the currently logged-in Claude account as a named profile.
|
|
166
|
+
|
|
167
|
+
- `name`: Profile name (validated against `[a-z0-9_-]+`).
|
|
168
|
+
- `homeDir`: Override for the home directory (defaults to `os.homedir()`). Primarily used in tests.
|
|
169
|
+
|
|
170
|
+
```ts
|
|
171
|
+
import { runAuthAdd } from "@simplysm/sd-claude";
|
|
172
|
+
|
|
173
|
+
runAuthAdd("work");
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
### `runAuthUse(name: string, homeDir?: string): void`
|
|
177
|
+
|
|
178
|
+
Switches to a saved Claude account profile.
|
|
179
|
+
|
|
180
|
+
- `name`: Profile name to switch to.
|
|
181
|
+
- `homeDir`: Override for the home directory.
|
|
182
|
+
|
|
183
|
+
```ts
|
|
184
|
+
import { runAuthUse } from "@simplysm/sd-claude";
|
|
185
|
+
|
|
186
|
+
runAuthUse("work");
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
### `runAuthList(homeDir?: string): Promise<void>`
|
|
190
|
+
|
|
191
|
+
Prints all saved profiles to stdout with status, expiry, and live usage data.
|
|
192
|
+
|
|
193
|
+
- `homeDir`: Override for the home directory.
|
|
194
|
+
|
|
195
|
+
```ts
|
|
196
|
+
import { runAuthList } from "@simplysm/sd-claude";
|
|
197
|
+
|
|
198
|
+
await runAuthList();
|
|
199
|
+
```
|
|
200
|
+
|
|
201
|
+
### `runAuthRemove(name: string, homeDir?: string): void`
|
|
202
|
+
|
|
203
|
+
Removes a saved Claude account profile.
|
|
204
|
+
|
|
205
|
+
- `name`: Profile name to remove.
|
|
206
|
+
- `homeDir`: Override for the home directory.
|
|
207
|
+
|
|
208
|
+
```ts
|
|
209
|
+
import { runAuthRemove } from "@simplysm/sd-claude";
|
|
210
|
+
|
|
211
|
+
runAuthRemove("personal");
|
|
212
|
+
```
|
|
213
|
+
|
|
214
|
+
### `validateName(name: string): void`
|
|
215
|
+
|
|
216
|
+
Validates that a profile name matches `[a-z0-9_-]+`. Throws an `Error` if invalid.
|
|
217
|
+
|
|
218
|
+
```ts
|
|
219
|
+
import { validateName } from "@simplysm/sd-claude";
|
|
220
|
+
|
|
221
|
+
validateName("my-profile"); // OK
|
|
222
|
+
validateName("My Profile"); // throws Error
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
### `getProfileDir(name: string, homeDir?: string): string`
|
|
226
|
+
|
|
227
|
+
Returns the absolute path to the profile directory: `<homeDir>/.sd-claude/auth/<name>`.
|
|
228
|
+
|
|
229
|
+
```ts
|
|
230
|
+
import { getProfileDir } from "@simplysm/sd-claude";
|
|
231
|
+
|
|
232
|
+
const dir = getProfileDir("work");
|
|
233
|
+
// e.g. "/home/user/.sd-claude/auth/work"
|
|
234
|
+
```
|
|
235
|
+
|
|
236
|
+
### `profileExists(name: string, homeDir?: string): boolean`
|
|
237
|
+
|
|
238
|
+
Returns `true` if the profile directory exists.
|
|
239
|
+
|
|
240
|
+
```ts
|
|
241
|
+
import { profileExists } from "@simplysm/sd-claude";
|
|
242
|
+
|
|
243
|
+
if (profileExists("work")) {
|
|
244
|
+
console.log("Profile work exists");
|
|
245
|
+
}
|
|
246
|
+
```
|
|
247
|
+
|
|
248
|
+
### `listProfiles(homeDir?: string): string[]`
|
|
249
|
+
|
|
250
|
+
Returns an array of all saved profile names (directory names under `~/.sd-claude/auth/`). Returns an empty array if no profiles exist.
|
|
251
|
+
|
|
252
|
+
```ts
|
|
253
|
+
import { listProfiles } from "@simplysm/sd-claude";
|
|
254
|
+
|
|
255
|
+
const profiles = listProfiles();
|
|
256
|
+
// e.g. ["personal", "work"]
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
### `readCurrentAuth(homeDir?: string): { oauthAccount: Record<string, unknown>; userID: string }`
|
|
260
|
+
|
|
261
|
+
Reads `oauthAccount` and `userID` from `~/.claude.json`. Throws if not logged in.
|
|
262
|
+
|
|
263
|
+
```ts
|
|
264
|
+
import { readCurrentAuth } from "@simplysm/sd-claude";
|
|
265
|
+
|
|
266
|
+
const { oauthAccount, userID } = readCurrentAuth();
|
|
267
|
+
```
|
|
268
|
+
|
|
269
|
+
### `readCurrentCredentials(homeDir?: string): Record<string, unknown>`
|
|
270
|
+
|
|
271
|
+
Reads and returns the full contents of `~/.claude/.credentials.json`.
|
|
272
|
+
|
|
273
|
+
```ts
|
|
274
|
+
import { readCurrentCredentials } from "@simplysm/sd-claude";
|
|
275
|
+
|
|
276
|
+
const credentials = readCurrentCredentials();
|
|
277
|
+
```
|
|
278
|
+
|
|
279
|
+
### `getCurrentUserID(homeDir?: string): string | undefined`
|
|
280
|
+
|
|
281
|
+
Returns the `userID` from `~/.claude.json`, or `undefined` if the file does not exist or cannot be read.
|
|
282
|
+
|
|
283
|
+
```ts
|
|
284
|
+
import { getCurrentUserID } from "@simplysm/sd-claude";
|
|
285
|
+
|
|
286
|
+
const userID = getCurrentUserID();
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
## Profile Storage Layout
|
|
290
|
+
|
|
291
|
+
```
|
|
292
|
+
~/.sd-claude/
|
|
293
|
+
auth/
|
|
294
|
+
<name>/
|
|
295
|
+
auth.json # { oauthAccount, userID }
|
|
296
|
+
credentials.json # full ~/.claude/.credentials.json snapshot
|
|
297
|
+
```
|
|
@@ -39,8 +39,19 @@ async function readFileAsync() { ... } // Async suffix prohibited
|
|
|
39
39
|
- Not enforced — omit when code is self-explanatory
|
|
40
40
|
- When written, use Korean
|
|
41
41
|
|
|
42
|
+
## Re-export Restriction
|
|
43
|
+
|
|
44
|
+
- Re-export (`export * from`, `export { } from`) is **only allowed in `src/index.ts`**
|
|
45
|
+
- All other files must not re-export — duplicated re-exports make code harder to find and maintain
|
|
46
|
+
|
|
42
47
|
## index.ts Export Pattern
|
|
43
48
|
|
|
44
49
|
- Large packages: `#region`/`#endregion` for sections + `//` for sub-groups
|
|
45
50
|
- Small packages (≤10 exports): `//` comments only
|
|
46
51
|
- Always `export *` (wildcard), never explicit `export type { ... } from "..."`
|
|
52
|
+
|
|
53
|
+
## Type Safety for Public APIs
|
|
54
|
+
|
|
55
|
+
- API changes must be detectable via **typecheck alone** — all affected usage sites must show compile errors
|
|
56
|
+
- Public component props must support **IDE intellisense** (autocomplete, type hints)
|
|
57
|
+
- Avoid `any` in public-facing types; use generics or specific union types instead
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# Simplysm Library Issue Reporting
|
|
2
|
+
|
|
3
|
+
Source code for `@simplysm/*` packages can be found in `node_modules/@simplysm/`. If debugging reveals the root cause is in the simplysm library itself, generate a GitHub issue-formatted text (title, reproduction steps, expected behavior, actual behavior) and display it to the user.
|
|
4
|
+
|
|
5
|
+
**Report facts only — do not suggest fixes or include code location hints. Do not auto-submit the issue — only display the text.**
|
|
6
|
+
|
|
7
|
+
The issue body must NEVER include internal analysis of library code (class names, variable names, style properties, inheritance chains, etc.). Only describe user-observable symptoms.
|
|
@@ -21,9 +21,9 @@ If a referenced file or document cannot be found, **stop immediately and ask the
|
|
|
21
21
|
|
|
22
22
|
### Questions vs. Code Requests — CRITICAL
|
|
23
23
|
|
|
24
|
-
- **If the user asks a question** (e.g., "
|
|
24
|
+
- **If the user asks a question** (e.g., "Why is this like this?", "What is this?", "How does this work?") → **answer with text only**. Do NOT edit, write, or create any files.
|
|
25
25
|
- **If the user discusses, explains, or shares opinions** → **respond with text only**. Do NOT touch any files.
|
|
26
|
-
- **Only edit/write/create files when the user explicitly requests code changes** (e.g., "
|
|
26
|
+
- **Only edit/write/create files when the user explicitly requests code changes** (e.g., "Fix this", "Create this", "Change this", "Add this").
|
|
27
27
|
- Reading files to answer a question is fine. **Modifying files to answer a question is prohibited.**
|
|
28
28
|
|
|
29
29
|
### General Rules
|
|
@@ -32,7 +32,18 @@ If a referenced file or document cannot be found, **stop immediately and ask the
|
|
|
32
32
|
- Do NOT add features, refactoring, improvements, or documentation beyond the requested scope.
|
|
33
33
|
- When in doubt, **ask first** before proceeding.
|
|
34
34
|
- Responses like "I'll create it myself" or "I'll add that as well" are strictly prohibited.
|
|
35
|
+
- **Do NOT comment on code outside the requested change.** This includes:
|
|
36
|
+
- Listing issues you noticed but did not fix
|
|
37
|
+
- Describing what you "left alone" or "did not change"
|
|
38
|
+
- "참고", "suggestions", "by the way", "note", "what I left alone"
|
|
39
|
+
- Any unsolicited observations about surrounding code quality
|
|
40
|
+
- Only describe **what you changed** — nothing else
|
|
35
41
|
|
|
36
|
-
##
|
|
42
|
+
## Asking Clarifying Questions
|
|
37
43
|
|
|
38
|
-
|
|
44
|
+
When you need to ask the user a question, you MUST use the `AskUserQuestion` tool. Do NOT ask questions in plain text.
|
|
45
|
+
|
|
46
|
+
- **Wrong:** Writing questions as text in your response
|
|
47
|
+
- **Right:** Calling the `AskUserQuestion` tool
|
|
48
|
+
|
|
49
|
+
**Before EVERY `AskUserQuestion` call, output `---` as the last line.** The widget clips text above it. No exceptions.
|
|
@@ -19,6 +19,7 @@ Determine the major version by the `version` field in `package.json`.
|
|
|
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
|
+
| Debugging in a project that uses `@simplysm/*` as an external dependency (not the simplysm monorepo itself) | `.claude/refs/sd-library-issue.md` |
|
|
22
23
|
|
|
23
24
|
## v12 only (< 13)
|
|
24
25
|
|
package/claude/sd-statusline.js
CHANGED
|
@@ -241,7 +241,7 @@ async function main() {
|
|
|
241
241
|
const cwd = input.cwd ?? process.cwd();
|
|
242
242
|
const folderName = path.basename(cwd);
|
|
243
243
|
|
|
244
|
-
//
|
|
244
|
+
// Output
|
|
245
245
|
const dailyStr = dailyResetTime ? `${dailyPercent}%(${dailyResetTime})` : `${dailyPercent}%`;
|
|
246
246
|
const weekStr = weekResetDay ? `${weekPercent}%(${weekResetDay})` : `${weekPercent}%`;
|
|
247
247
|
const parts = [folderName, modelName, `${contextPercent}%`, dailyStr, weekStr];
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sd-brainstorm
|
|
3
|
-
description: "
|
|
3
|
+
description: "You MUST use this before any creative work - creating features, building components, adding functionality, or modifying behavior. Explores user intent, requirements and design before implementation."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Brainstorming Ideas Into Designs
|
|
@@ -1,17 +1,26 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sd-check
|
|
3
3
|
description: "Typecheck, lint, test verification (explicit invocation only)"
|
|
4
|
-
allowed-tools: Bash(npm run check), Bash(npm run typecheck), Bash(npm run lint
|
|
4
|
+
allowed-tools: Bash(npm run check:*), Bash(pnpm run check:*), Bash(yarn run check:*), Bash(npm run typecheck:*), Bash(pnpm run typecheck:*), Bash(yarn run typecheck:*), Bash(npm run lint:*), Bash(pnpm run lint:*), Bash(yarn run lint:*), Bash(npm run vitest:*), Bash(pnpm run vitest:*), Bash(yarn run vitest:*)
|
|
5
5
|
---
|
|
6
6
|
|
|
7
7
|
# sd-check
|
|
8
8
|
|
|
9
|
-
Run
|
|
9
|
+
Run `$PM run check`, fix errors, repeat until clean.
|
|
10
|
+
|
|
11
|
+
## Package Manager Detection
|
|
12
|
+
|
|
13
|
+
Before running any commands, detect the package manager:
|
|
14
|
+
- If `pnpm-lock.yaml` exists in project root → use `pnpm`
|
|
15
|
+
- If `yarn.lock` exists in project root → use `yarn`
|
|
16
|
+
- Otherwise → use `npm`
|
|
17
|
+
|
|
18
|
+
`$PM` in all commands below refers to the detected package manager.
|
|
10
19
|
|
|
11
20
|
## Usage
|
|
12
21
|
|
|
13
22
|
```
|
|
14
|
-
|
|
23
|
+
$PM run check [path] [--type typecheck|lint|test]
|
|
15
24
|
```
|
|
16
25
|
|
|
17
26
|
| Example | Effect |
|
|
@@ -25,11 +34,11 @@ Multiple types: `--type typecheck,lint`. No path = full project. No type = all c
|
|
|
25
34
|
|
|
26
35
|
## Workflow
|
|
27
36
|
|
|
28
|
-
1. **Run**
|
|
37
|
+
1. **Run** `$PM run check [path] [--type type]` (timeout: 600000)
|
|
29
38
|
2. **All passed?** Report with actual output numbers → done
|
|
30
39
|
3. **Errors?** Fix in priority order: typecheck → lint → test (fixes cascade)
|
|
31
|
-
- Test failures: run `git
|
|
32
|
-
- **E2E test failures
|
|
40
|
+
- Test failures: **MUST** run `git log` to decide — update test or fix source
|
|
41
|
+
- **E2E test failures**: use Playwright MCP to investigate before fixing
|
|
33
42
|
1. `browser_navigate` to the target URL
|
|
34
43
|
2. `browser_snapshot` / `browser_take_screenshot` (save to `.tmp/playwright/`) to see page state
|
|
35
44
|
3. `browser_console_messages` for JS errors
|
|
@@ -58,6 +58,8 @@ Examples:
|
|
|
58
58
|
- `fix(orm-node): handle null values in bulk insert`
|
|
59
59
|
- `docs: update README with new API examples`
|
|
60
60
|
|
|
61
|
+
> **Note:** The examples above are in English for reference only. The actual description MUST be written in the system's configured language.
|
|
62
|
+
|
|
61
63
|
Use a HEREDOC for multi-line messages when needed.
|
|
62
64
|
|
|
63
65
|
## Execution
|
|
@@ -14,6 +14,12 @@ fi
|
|
|
14
14
|
CHECK_PATH="$1"
|
|
15
15
|
TEST_PATTERN="$2"
|
|
16
16
|
|
|
17
|
+
# Detect package manager: pnpm -> yarn -> npm (default)
|
|
18
|
+
if [ -f "pnpm-lock.yaml" ]; then PM="pnpm"
|
|
19
|
+
elif [ -f "yarn.lock" ]; then PM="yarn"
|
|
20
|
+
else PM="npm"
|
|
21
|
+
fi
|
|
22
|
+
|
|
17
23
|
# Find all test files matching pattern
|
|
18
24
|
readarray -t TEST_FILES < <(find . -path "$TEST_PATTERN" -type f)
|
|
19
25
|
|
|
@@ -37,7 +43,7 @@ for test_file in "${TEST_FILES[@]}"; do
|
|
|
37
43
|
echo "Testing: $test_file"
|
|
38
44
|
|
|
39
45
|
# Run the test
|
|
40
|
-
|
|
46
|
+
$PM test "$test_file" > /dev/null 2>&1 || true
|
|
41
47
|
|
|
42
48
|
# Check if pollution appeared
|
|
43
49
|
if [ -e "$CHECK_PATH" ]; then
|
|
@@ -48,7 +54,7 @@ for test_file in "${TEST_FILES[@]}"; do
|
|
|
48
54
|
ls -la "$CHECK_PATH" 2>/dev/null || echo "(path exists but can't stat)"
|
|
49
55
|
echo ""
|
|
50
56
|
echo "Investigate with:"
|
|
51
|
-
echo "
|
|
57
|
+
echo " $PM test '$test_file' -- --reporter=verbose"
|
|
52
58
|
echo " git diff"
|
|
53
59
|
exit 0
|
|
54
60
|
fi
|
|
@@ -93,10 +93,10 @@ async function gitInit(directory: string) {
|
|
|
93
93
|
|
|
94
94
|
**Critical:** Use `console.error()` in tests (not logger - may not show)
|
|
95
95
|
|
|
96
|
-
**Run and capture
|
|
96
|
+
**Run and capture** (detect PM: `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, otherwise → npm):
|
|
97
97
|
|
|
98
98
|
```bash
|
|
99
|
-
|
|
99
|
+
$PM test 2>&1 | grep 'DEBUG git init'
|
|
100
100
|
```
|
|
101
101
|
|
|
102
102
|
**Analyze stack traces:**
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""Extract text and images from DOCX files in paragraph flow order."""
|
|
3
3
|
|
|
4
4
|
import sys
|
|
5
5
|
import io
|
|
@@ -17,7 +17,7 @@ def ensure_packages():
|
|
|
17
17
|
try:
|
|
18
18
|
__import__(import_name)
|
|
19
19
|
except ImportError:
|
|
20
|
-
print(f"
|
|
20
|
+
print(f"Installing package: {pip_name}...", file=sys.stderr)
|
|
21
21
|
subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name],
|
|
22
22
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
23
23
|
|
|
@@ -71,7 +71,7 @@ def extract(file_path):
|
|
|
71
71
|
elif not has_image and not text:
|
|
72
72
|
continue
|
|
73
73
|
|
|
74
|
-
#
|
|
74
|
+
# Table extraction
|
|
75
75
|
for t_idx, table in enumerate(doc.tables):
|
|
76
76
|
print(f"\n### Table {t_idx + 1}\n")
|
|
77
77
|
for row in table.rows:
|
|
@@ -79,9 +79,9 @@ def extract(file_path):
|
|
|
79
79
|
print("| " + " | ".join(cells) + " |")
|
|
80
80
|
|
|
81
81
|
if img_idx > 0:
|
|
82
|
-
print(f"\n---\n
|
|
82
|
+
print(f"\n---\n{img_idx} image(s) saved: {out_dir}")
|
|
83
83
|
else:
|
|
84
|
-
print("\n---\
|
|
84
|
+
print("\n---\nNo images")
|
|
85
85
|
|
|
86
86
|
|
|
87
87
|
if __name__ == "__main__":
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""Extract text, tables, and images from PDF files page by page."""
|
|
3
3
|
|
|
4
4
|
import sys
|
|
5
5
|
import io
|
|
@@ -16,7 +16,7 @@ def ensure_packages():
|
|
|
16
16
|
try:
|
|
17
17
|
__import__(import_name)
|
|
18
18
|
except ImportError:
|
|
19
|
-
print(f"
|
|
19
|
+
print(f"Installing package: {pip_name}...", file=sys.stderr)
|
|
20
20
|
subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name],
|
|
21
21
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
22
22
|
|
|
@@ -32,7 +32,7 @@ def extract(file_path):
|
|
|
32
32
|
|
|
33
33
|
print(f"# {Path(file_path).name}\n")
|
|
34
34
|
|
|
35
|
-
#
|
|
35
|
+
# Text + table extraction (pdfplumber)
|
|
36
36
|
with pdfplumber.open(file_path) as pdf:
|
|
37
37
|
for page_num, page in enumerate(pdf.pages, 1):
|
|
38
38
|
print(f"## Page {page_num}\n")
|
|
@@ -52,7 +52,7 @@ def extract(file_path):
|
|
|
52
52
|
print("| " + " | ".join(cells) + " |")
|
|
53
53
|
print()
|
|
54
54
|
|
|
55
|
-
#
|
|
55
|
+
# Image extraction (pypdf)
|
|
56
56
|
reader = PdfReader(file_path)
|
|
57
57
|
for page_num, page in enumerate(reader.pages, 1):
|
|
58
58
|
if "/XObject" not in (page.get("/Resources") or {}):
|
|
@@ -79,19 +79,19 @@ def extract(file_path):
|
|
|
79
79
|
img_path.write_bytes(obj._data if hasattr(obj, "_data") else b"")
|
|
80
80
|
print(f"[IMG] (page={page_num}) {img_path}")
|
|
81
81
|
|
|
82
|
-
# OCR
|
|
82
|
+
# OCR notice
|
|
83
83
|
if total_text_len == 0:
|
|
84
|
-
print("\n⚠
|
|
85
|
-
print("OCR
|
|
86
|
-
print(" 1. Tesseract OCR
|
|
84
|
+
print("\n⚠ No text was extracted (may be a scanned PDF).")
|
|
85
|
+
print("OCR is required:")
|
|
86
|
+
print(" 1. Install Tesseract OCR: https://github.com/tesseract-ocr/tesseract")
|
|
87
87
|
print(" 2. pip install pytesseract pdf2image")
|
|
88
|
-
print(" 3. pytesseract.image_to_string()
|
|
88
|
+
print(" 3. Extract with pytesseract.image_to_string()")
|
|
89
89
|
|
|
90
90
|
print()
|
|
91
91
|
if img_idx > 0:
|
|
92
|
-
print(f"---\n
|
|
92
|
+
print(f"---\n{img_idx} image(s) saved: {out_dir}")
|
|
93
93
|
else:
|
|
94
|
-
print("---\
|
|
94
|
+
print("---\nNo images")
|
|
95
95
|
|
|
96
96
|
|
|
97
97
|
if __name__ == "__main__":
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env python3
|
|
2
|
-
"""
|
|
2
|
+
"""Extract text and images from PPTX files with per-slide coordinates."""
|
|
3
3
|
|
|
4
4
|
import sys
|
|
5
5
|
import io
|
|
@@ -16,13 +16,13 @@ def ensure_packages():
|
|
|
16
16
|
try:
|
|
17
17
|
__import__(import_name)
|
|
18
18
|
except ImportError:
|
|
19
|
-
print(f"
|
|
19
|
+
print(f"Installing package: {pip_name}...", file=sys.stderr)
|
|
20
20
|
subprocess.check_call([sys.executable, "-m", "pip", "install", pip_name],
|
|
21
21
|
stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL)
|
|
22
22
|
|
|
23
23
|
|
|
24
24
|
def emu_to_inches(emu):
|
|
25
|
-
"""EMU
|
|
25
|
+
"""Convert EMU to inches (1 decimal place)."""
|
|
26
26
|
if emu is None:
|
|
27
27
|
return "?"
|
|
28
28
|
return f"{emu / 914400:.1f}"
|
|
@@ -64,9 +64,9 @@ def extract(file_path):
|
|
|
64
64
|
print()
|
|
65
65
|
|
|
66
66
|
if img_idx > 0:
|
|
67
|
-
print(f"---\n
|
|
67
|
+
print(f"---\n{img_idx} image(s) saved: {out_dir}")
|
|
68
68
|
else:
|
|
69
|
-
print("---\
|
|
69
|
+
print("---\nNo images")
|
|
70
70
|
|
|
71
71
|
|
|
72
72
|
if __name__ == "__main__":
|