@simplysm/sd-claude 13.0.78 → 13.0.81

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 (68) hide show
  1. package/claude/rules/sd-claude-rules.md +4 -63
  2. package/claude/rules/sd-simplysm-usage.md +7 -0
  3. package/claude/sd-session-start.sh +10 -0
  4. package/claude/sd-statusline.py +249 -0
  5. package/claude/skills/sd-api-review/SKILL.md +89 -0
  6. package/claude/skills/sd-check/SKILL.md +55 -57
  7. package/claude/skills/sd-commit/SKILL.md +37 -42
  8. package/claude/skills/sd-debug/SKILL.md +75 -265
  9. package/claude/skills/sd-document/SKILL.md +63 -53
  10. package/claude/skills/sd-document/_common.py +94 -0
  11. package/claude/skills/sd-document/extract_docx.py +19 -48
  12. package/claude/skills/sd-document/extract_pdf.py +22 -50
  13. package/claude/skills/sd-document/extract_pptx.py +17 -40
  14. package/claude/skills/sd-document/extract_xlsx.py +19 -40
  15. package/claude/skills/sd-email-analyze/SKILL.md +23 -31
  16. package/claude/skills/sd-email-analyze/email-analyzer.py +79 -65
  17. package/claude/skills/sd-init/SKILL.md +133 -0
  18. package/claude/skills/sd-plan/SKILL.md +69 -120
  19. package/claude/skills/sd-readme/SKILL.md +106 -131
  20. package/claude/skills/sd-review/SKILL.md +38 -155
  21. package/claude/skills/sd-simplify/SKILL.md +59 -0
  22. package/dist/commands/install.js +20 -6
  23. package/dist/commands/install.js.map +1 -1
  24. package/package.json +3 -2
  25. package/src/commands/install.ts +29 -7
  26. package/README.md +0 -297
  27. package/claude/refs/sd-angular.md +0 -127
  28. package/claude/refs/sd-code-conventions.md +0 -155
  29. package/claude/refs/sd-directories.md +0 -7
  30. package/claude/refs/sd-library-issue.md +0 -7
  31. package/claude/refs/sd-migration.md +0 -7
  32. package/claude/refs/sd-orm-v12.md +0 -81
  33. package/claude/refs/sd-orm.md +0 -23
  34. package/claude/refs/sd-service.md +0 -5
  35. package/claude/refs/sd-simplysm-docs.md +0 -52
  36. package/claude/refs/sd-solid.md +0 -68
  37. package/claude/refs/sd-workflow.md +0 -25
  38. package/claude/rules/sd-refs-linker.md +0 -52
  39. package/claude/sd-statusline.js +0 -296
  40. package/claude/skills/sd-api-name-review/SKILL.md +0 -154
  41. package/claude/skills/sd-brainstorm/SKILL.md +0 -215
  42. package/claude/skills/sd-debug/condition-based-waiting-example.ts +0 -158
  43. package/claude/skills/sd-debug/condition-based-waiting.md +0 -114
  44. package/claude/skills/sd-debug/defense-in-depth.md +0 -128
  45. package/claude/skills/sd-debug/find-polluter.sh +0 -64
  46. package/claude/skills/sd-debug/root-cause-tracing.md +0 -168
  47. package/claude/skills/sd-discuss/SKILL.md +0 -91
  48. package/claude/skills/sd-explore/SKILL.md +0 -118
  49. package/claude/skills/sd-plan-dev/SKILL.md +0 -294
  50. package/claude/skills/sd-plan-dev/code-quality-reviewer-prompt.md +0 -49
  51. package/claude/skills/sd-plan-dev/final-review-prompt.md +0 -50
  52. package/claude/skills/sd-plan-dev/implementer-prompt.md +0 -60
  53. package/claude/skills/sd-plan-dev/spec-reviewer-prompt.md +0 -45
  54. package/claude/skills/sd-review/api-reviewer-prompt.md +0 -75
  55. package/claude/skills/sd-review/code-reviewer-prompt.md +0 -82
  56. package/claude/skills/sd-review/convention-checker-prompt.md +0 -61
  57. package/claude/skills/sd-review/refactoring-analyzer-prompt.md +0 -92
  58. package/claude/skills/sd-skill/SKILL.md +0 -417
  59. package/claude/skills/sd-skill/anthropic-best-practices.md +0 -156
  60. package/claude/skills/sd-skill/cso-guide.md +0 -161
  61. package/claude/skills/sd-skill/examples/CLAUDE_MD_TESTING.md +0 -200
  62. package/claude/skills/sd-skill/persuasion-principles.md +0 -220
  63. package/claude/skills/sd-skill/testing-skills-with-subagents.md +0 -408
  64. package/claude/skills/sd-skill/writing-guide.md +0 -159
  65. package/claude/skills/sd-tdd/SKILL.md +0 -385
  66. package/claude/skills/sd-tdd/testing-anti-patterns.md +0 -317
  67. package/claude/skills/sd-use/SKILL.md +0 -67
  68. package/claude/skills/sd-worktree/SKILL.md +0 -78
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-claude",
3
- "version": "13.0.78",
3
+ "version": "13.0.81",
4
4
  "description": "Simplysm Claude Code CLI — asset installer",
5
5
  "author": "simplysm",
6
6
  "license": "Apache-2.0",
@@ -17,7 +17,8 @@
17
17
  "src",
18
18
  "tests",
19
19
  "scripts",
20
- "claude"
20
+ "claude",
21
+ "docs"
21
22
  ],
22
23
  "sideEffects": false,
23
24
  "dependencies": {
@@ -39,7 +39,7 @@ export function runInstall(): void {
39
39
 
40
40
  cleanSdEntries(targetDir);
41
41
  copySdEntries(sourceDir, targetDir, sourceEntries);
42
- setupStatusLine(targetDir);
42
+ setupSettings(targetDir);
43
43
 
44
44
  // eslint-disable-next-line no-console
45
45
  console.log(`[@simplysm/sd-claude] Installed ${sourceEntries.length} sd-* entries.`);
@@ -144,18 +144,40 @@ function copySdEntries(sourceDir: string, targetDir: string, entries: string[]):
144
144
  }
145
145
  }
146
146
 
147
- /** Adds statusLine configuration to settings.json. */
148
- function setupStatusLine(targetDir: string): void {
147
+ /** Ensures statusLine and SessionStart hooks are configured in settings.json. */
148
+ function setupSettings(targetDir: string): void {
149
149
  const settingsPath = path.join(targetDir, "settings.json");
150
- const sdStatusLineCommand = "node .claude/sd-statusline.js";
151
150
 
152
151
  let settings: Record<string, unknown> = {};
153
152
  if (fs.existsSync(settingsPath)) {
154
153
  settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8")) as Record<string, unknown>;
155
154
  }
156
155
 
157
- if (settings["statusLine"] == null) {
158
- settings["statusLine"] = { type: "command", command: sdStatusLineCommand };
159
- fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
156
+ // statusLine: always overwrite
157
+ settings["statusLine"] = { type: "command", command: "python .claude/sd-statusline.py" };
158
+
159
+ // SessionStart: ensure sd-session-start hook exists with correct config
160
+ const sdSessionEntry = {
161
+ matcher: "startup|resume|clear|compact",
162
+ hooks: [{ type: "command", command: "bash .claude/sd-session-start.sh" }],
163
+ };
164
+
165
+ const sessionStart = settings["SessionStart"] as
166
+ | Array<{ matcher?: string; hooks?: Array<{ type: string; command: string }> }>
167
+ | undefined;
168
+
169
+ if (sessionStart == null) {
170
+ settings["SessionStart"] = [sdSessionEntry];
171
+ } else {
172
+ const idx = sessionStart.findIndex((entry) =>
173
+ entry.hooks?.some((hook) => hook.command.includes("sd-session-start")),
174
+ );
175
+ if (idx >= 0) {
176
+ sessionStart[idx] = sdSessionEntry;
177
+ } else {
178
+ sessionStart.push(sdSessionEntry);
179
+ }
160
180
  }
181
+
182
+ fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
161
183
  }
package/README.md DELETED
@@ -1,297 +0,0 @@
1
- # @simplysm/sd-claude
2
-
3
- Simplysm Claude Code CLI — asset installer and Claude account profile manager.
4
-
5
- ## Installation
6
-
7
- ```bash
8
- pnpm add @simplysm/sd-claude
9
- ```
10
-
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`.
12
-
13
- ## CLI
14
-
15
- The package provides the `sd-claude` binary.
16
-
17
- ```
18
- sd-claude <command> [options]
19
- sd-claude --help
20
- ```
21
-
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
- ```
@@ -1,127 +0,0 @@
1
- # Angular Guidelines (v12 only)
2
-
3
- > v13 has no Angular (replaced by SolidJS). Rules below are v12 only.
4
-
5
- ## Core Rules
6
-
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
-
16
- ## Signal Utilities
17
-
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
-
25
- ## Component Pattern
26
-
27
- ```typescript
28
- @Component({
29
- selector: "app-my-page",
30
- changeDetection: ChangeDetectionStrategy.OnPush,
31
- encapsulation: ViewEncapsulation.None,
32
- standalone: true,
33
- imports: [SdTextfieldControl, SdButtonControl],
34
- template: `
35
- ...
36
- `,
37
- })
38
- export class MyPage {
39
- #sdToast = inject(SdToastProvider);
40
- #sdModal = inject(SdModalProvider);
41
-
42
- busyCount = $signal(0);
43
- data = $signal<IData>({});
44
-
45
- constructor() {
46
- $effect([], async () => {
47
- await this.#loadData();
48
- });
49
- }
50
- }
51
- ```
52
-
53
- ## Bootstrap
54
-
55
- ```typescript
56
- sdHmrBootstrapAsync(AppPage, {
57
- providers: [
58
- provideRouter([...routes], withHashLocation()),
59
- provideSdAngular({
60
- clientName: "my-app",
61
- defaultTheme: "compact",
62
- }),
63
- ],
64
- });
65
- ```
66
-
67
- ## Modal
68
-
69
- Implement `ISdModal<T>` interface:
70
-
71
- ```typescript
72
- export class MyModal implements ISdModal<TResult> {
73
- param = input<string>();
74
- close = output<TResult>();
75
- }
76
-
77
- // Usage
78
- await this.#sdModal.showAsync({
79
- type: MyModal,
80
- title: "Title",
81
- inputs: { param: "value" },
82
- });
83
- ```
84
-
85
- ## Data Sheet
86
-
87
- CRUD tables extend `AbsSdDataSheet<TFilter, TItem, TKey>`:
88
-
89
- - `search()` — query data
90
- - `submit()` — save changes
91
- - `downloadExcel()` / `uploadExcel()` — Excel integration
92
- - `$mark()` — mark dirty when cell is modified (mandatory)
93
-
94
- ## Busy / Error Handling
95
-
96
- ```typescript
97
- this.busyCount.update((v) => v + 1);
98
- try {
99
- await this.#sdToast.try(async () => {
100
- /* work */
101
- });
102
- } finally {
103
- this.busyCount.update((v) => v - 1);
104
- }
105
- ```
106
-
107
- ## Theming
108
-
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
-
113
- ## Permissions
114
-
115
- ```typescript
116
- perms = usePermsSignal(["base.partner"], ["use", "edit"]);
117
- canEdit = $computed(() => this.perms().includes("edit"));
118
- ```
119
-
120
- ## Routing
121
-
122
- ```typescript
123
- {
124
- path: "my-page",
125
- loadComponent: () => import("./MyPage").then((m) => m.MyPage),
126
- }
127
- ```
@@ -1,155 +0,0 @@
1
- # Code Conventions
2
-
3
- ## Generic Type Parameters
4
-
5
- - Always use descriptive names — single-letter `T` alone is not allowed
6
- - Use `T` prefix + descriptive name: `TItem`, `TData`, `TResult`, `TKey`, `TValue`, `TAuthInfo`
7
-
8
- ## Prototype Extensions
9
-
10
- Importing `@simplysm/core-common` adds extension methods to Array, Map, Set:
11
-
12
- - `Array`: `single()`, `filterExists()`, `groupBy()`, `orderBy()`, etc.
13
- - `Map`: `getOrCreate()`, `update()`
14
- - `Set`: `adds()`, `toggle()`
15
-
16
- Before using extension methods: Verify actual existence in `@simplysm/core-common` extensions (check README or source). Do not guess methods that don't exist.
17
-
18
- ## Function Naming Conventions
19
-
20
- - Do not use `Async` suffix on function names — Async is the default
21
- - When both sync and async versions exist, use `Sync` suffix on the sync function
22
- - **Exception — extensions**: When adding an async version to an existing prototype (e.g., `Array`), follow the original naming convention. If the sync method already exists without a `Sync` suffix, use `Async` suffix for the async version.
23
-
24
- ```typescript
25
- // Good
26
- async function readFile() { ... } // Async (default)
27
- function readFileSync() { ... } // Sync version
28
-
29
- // Bad
30
- async function readFileAsync() { ... } // Async suffix prohibited
31
-
32
- // Exception — Array extension already has mapMany()
33
- Array.prototype.mapManyAsync = async function () { ... } // OK
34
- ```
35
-
36
-
37
- ## File Naming
38
-
39
- - Auxiliary files (`types.ts`, `utils.ts`, etc.) must be prefixed with the main file name (e.g., `CrudSheet.types.ts`)
40
- - File names must be self-identifying without relying on the parent directory
41
-
42
- ## JSDoc Convention
43
-
44
- - Not enforced — omit when code is self-explanatory
45
- - When written, use English
46
-
47
- ## Re-export Restriction
48
-
49
- - Re-export (`export * from`, `export { } from`) is **only allowed in `src/index.ts`**
50
- - All other files must not re-export — duplicated re-exports make code harder to find and maintain
51
-
52
- ## index.ts Export Pattern
53
-
54
- - Use `//` comments to group exports
55
- - Always `export *` (wildcard), never explicit `export type { ... } from "..."`
56
-
57
- ## `#region` / `#endregion`
58
-
59
- - When splitting a large ts/tsx file has a bigger tradeoff than keeping it as-is, use `#region`/`#endregion` to organize sections within the file
60
- - Do not use in simple export files like index.ts
61
-
62
- ## `any` vs `unknown` vs Generics
63
-
64
- Choose based on **what you do with the value**:
65
-
66
- | Type | When | Key rule |
67
- |------|------|----------|
68
- | `any` | Value is **passed through** without property access | No `.prop`, no method calls, no narrowing |
69
- | `unknown` | Value's properties **will be accessed** but type is unknown | Must narrow with type guard before any access |
70
- | Generic `<T>` | **Input/output type relationship** must be preserved | Caller's type flows through to return type |
71
-
72
- ```typescript
73
- // any — pure pass-through, no property access
74
- function logAndStore(value: any): void {
75
- console.log(value); // OK: no property access
76
- storage.push(value); // OK: just forwarding
77
- }
78
-
79
- // unknown — will access properties, must narrow first
80
- function getName(data: unknown): string {
81
- if (typeof data === "object" && data !== null && "name" in data) {
82
- return String((data as { name: unknown }).name);
83
- }
84
- throw new Error("No name property");
85
- }
86
-
87
- // Generic — input type preserved in output
88
- function wrapValue<T>(value: T): { value: T } {
89
- return { value };
90
- }
91
- ```
92
-
93
- **any vs Generic for pass-through:** If the function only forwards a value without accessing it AND the caller does not need type preservation in the return, use `any`. If the caller needs the same type back (input→output relationship), use a generic.
94
-
95
- ## Type Safety for Public APIs
96
-
97
- - API changes must be detectable via **typecheck alone** — all affected usage sites must show compile errors
98
- - Public component props must support **IDE intellisense** (autocomplete, type hints)
99
- - **No `Record<string, any>` for structured props** — define explicit interfaces so consumers get autocomplete
100
-
101
- ```typescript
102
- // Bad — consumers get no autocomplete, changes are invisible
103
- interface TableProps {
104
- columns: Record<string, any>;
105
- onRowClick: Function;
106
- }
107
-
108
- // Good — changes to ColumnDef break consumers at compile time
109
- interface TableProps<TRow> {
110
- columns: ColumnDef<TRow>[];
111
- onRowClick?: (row: TRow) => void;
112
- }
113
- ```
114
-
115
- ## Forced Type Casting Prohibition
116
-
117
- - **`as unknown as X` is prohibited** — dangerous escape hatch that silences real type errors
118
- - **`as X` must be avoided** unless there is no alternative — when tempted, try these fixes first:
119
-
120
- | Type error situation | Fix (instead of `as`) |
121
- |---------------------|-----------------------|
122
- | Missing properties | Add them to the interface |
123
- | Type too wide | Use generics to propagate correctly |
124
- | Unknown shape | Type guard: `if ("prop" in obj)`, `instanceof` |
125
- | Multiple shapes | Discriminated union or overload |
126
- | Wrong at source | Fix the root cause, not the usage site |
127
-
128
- ```typescript
129
- // Bad — casting hides the real problem
130
- const admin = user as unknown as AdminUser;
131
-
132
- // Bad — lazy cast instead of proper narrowing
133
- const name = (event as any).target.name;
134
-
135
- // Good — fix parameter type at the source
136
- function getAdminDashboard(admin: AdminUser): string { ... }
137
-
138
- // Good — narrow unknown with type guard
139
- function getNameFromEvent(event: unknown): string {
140
- if (typeof event === "object" && event !== null && "target" in event) {
141
- const target = event.target;
142
- if (typeof target === "object" && target !== null && "name" in target) {
143
- return String(target.name);
144
- }
145
- }
146
- throw new Error("Invalid event structure");
147
- }
148
- ```
149
-
150
- ## Boolean Prop Defaults
151
-
152
- - Name boolean props so their default value is `false`
153
- - Prefer `hide*`, `disable*` patterns where the feature is ON by default
154
- - This avoids double-negation in JSX: `hideX={false}` is clearer than `showX={true}` when both mean "show X"
155
- - Exception: inherent HTML attributes like `draggable` may default to `true`
@@ -1,7 +0,0 @@
1
- # Directory Reference
2
-
3
- - `.cache/`: Build cache (`eslint.cache`, `typecheck-{env}.tsbuildinfo`, `dts.tsbuildinfo`). Reset: delete `.cache/`
4
- - `.tmp/playwright/`: Playwright MCP output directory
5
- - Screenshots/snapshots must always be saved to the `.tmp/playwright/` directory
6
- - When calling `browser_take_screenshot`, always prefix filename with `.tmp/playwright/` (e.g., `.tmp/playwright/screenshot.png`)
7
- - `PLAYWRIGHT_OUTPUT_DIR` only applies to auto-generated filenames; explicitly specified filenames are resolved relative to cwd
@@ -1,7 +0,0 @@
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.
@@ -1,7 +0,0 @@
1
- # Migration Rules
2
-
3
- When porting/migrating code from another codebase (e.g., v12 Angular → v13 SolidJS):
4
-
5
- 1. **Analyze every line**: Read the original source and all its dependencies (imports, base classes, etc.) line by line. Understand every feature, prop, and behavior. If a dependency cannot be found, ask the user.
6
- 2. **Ask about every difference**: Any change from the original (API, pattern, design, omission, addition) must be asked to the user. Never decide silently.
7
- 3. **Verify after completion**: Compare the result 1:1 with the original and report any omissions or differences to the user.