@simplysm/sd-claude 13.0.72 → 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 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
- ## Source Index
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
- ### Commands
13
+ ## CLI
12
14
 
13
- | Source | Exports | Description | Test |
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
- ## License
17
+ ```
18
+ sd-claude <command> [options]
19
+ sd-claude --help
20
+ ```
23
21
 
24
- Apache-2.0
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
@@ -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
- ## ⚠️ CRITICAL — NEVER SKIP
42
+ ## Asking Clarifying Questions
37
43
 
38
- **Before EVERY `AskUserQuestion` call, output `---` as the last line.** The widget clips text above it. No exceptions.
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.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: sd-brainstorm
3
- description: "Design exploration before implementation (explicit invocation only)"
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 --fix), Bash(npm run vitest)
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 `npm run check`, fix errors, repeat until clean.
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
- npm run check [path] [--type typecheck|lint|test]
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** `npm run check [path] [--type type]` (timeout: 600000)
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 diff` to decide — update test or fix source
32
- - **E2E test failures** (browser/solid/service project): use Playwright MCP to investigate before fixing
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
- npm test "$test_file" > /dev/null 2>&1 || true
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 " npm test '$test_file' -- --reporter=verbose"
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
- npm test 2>&1 | grep 'DEBUG git init'
99
+ $PM test 2>&1 | grep 'DEBUG git init'
100
100
  ```
101
101
 
102
102
  **Analyze stack traces:**
@@ -49,6 +49,15 @@ Assume they are a skilled developer, but know almost nothing about our toolset o
49
49
  ---
50
50
  ```
51
51
 
52
+ ## Package Manager Detection
53
+
54
+ When writing run commands in the plan, detect the package manager:
55
+ - If `pnpm-lock.yaml` exists in project root → use `pnpm`
56
+ - If `yarn.lock` exists in project root → use `yarn`
57
+ - Otherwise → use `npm`
58
+
59
+ `$PM` in the task template below refers to the detected package manager.
60
+
52
61
  ## Task Structure
53
62
 
54
63
  ```markdown
@@ -70,7 +79,7 @@ test("specific behavior", () => {
70
79
 
71
80
  **Step 2: Run test to verify it fails**
72
81
 
73
- Run: `pnpm vitest exact/path/to/tests/file.spec.ts --run`
82
+ Run: `$PM run vitest exact/path/to/tests/file.spec.ts --run`
74
83
  Expected: FAIL with "functionUnderTest is not defined"
75
84
 
76
85
  **Step 3: Write minimal implementation**
@@ -83,7 +92,7 @@ function functionUnderTest(input: InputType): OutputType {
83
92
 
84
93
  **Step 4: Run test to verify it passes**
85
94
 
86
- Run: `pnpm vitest exact/path/to/tests/file.spec.ts --run`
95
+ Run: `$PM run vitest exact/path/to/tests/file.spec.ts --run`
87
96
  Expected: PASS
88
97
 
89
98
  **Step 5: Commit**
@@ -221,11 +221,13 @@ Done!
221
221
 
222
222
  ## Batch Integration Check
223
223
 
224
- Between batches, run targeted verification on affected packages before starting the next batch:
224
+ Between batches, run targeted verification on affected packages before starting the next batch.
225
+
226
+ Detect the package manager first (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, otherwise → npm):
225
227
 
226
228
  ```bash
227
- npm run typecheck [affected packages]
228
- npm run lint [affected packages]
229
+ $PM run typecheck [affected packages]
230
+ $PM run lint [affected packages]
229
231
  ```
230
232
 
231
233
  This catches cross-task integration issues early — especially when the next batch depends on the current batch's output. Do NOT skip this even if individual task reviews passed.
@@ -37,9 +37,9 @@ Individual tasks already passed spec and quality reviews. Focus on cross-task in
37
37
 
38
38
  ### Verification
39
39
 
40
- Run and report results:
41
- - `npm run typecheck [affected packages]`
42
- - `npm run lint [affected packages]`
40
+ Detect the package manager (`pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, otherwise → npm), then run and report results:
41
+ - `$PM run typecheck [affected packages]`
42
+ - `$PM run lint [affected packages]`
43
43
 
44
44
  ### Report
45
45