@simplysm/sd-claude 13.0.78 → 13.0.80

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 (64) 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/skills/sd-api-review/SKILL.md +89 -0
  5. package/claude/skills/sd-check/SKILL.md +55 -57
  6. package/claude/skills/sd-commit/SKILL.md +37 -42
  7. package/claude/skills/sd-debug/SKILL.md +75 -265
  8. package/claude/skills/sd-document/SKILL.md +63 -53
  9. package/claude/skills/sd-document/_common.py +94 -0
  10. package/claude/skills/sd-document/extract_docx.py +19 -48
  11. package/claude/skills/sd-document/extract_pdf.py +22 -50
  12. package/claude/skills/sd-document/extract_pptx.py +17 -40
  13. package/claude/skills/sd-document/extract_xlsx.py +19 -40
  14. package/claude/skills/sd-email-analyze/SKILL.md +23 -31
  15. package/claude/skills/sd-email-analyze/email-analyzer.py +79 -65
  16. package/claude/skills/sd-init/SKILL.md +133 -0
  17. package/claude/skills/sd-plan/SKILL.md +69 -120
  18. package/claude/skills/sd-readme/SKILL.md +106 -131
  19. package/claude/skills/sd-review/SKILL.md +38 -155
  20. package/claude/skills/sd-simplify/SKILL.md +59 -0
  21. package/package.json +3 -2
  22. package/README.md +0 -297
  23. package/claude/refs/sd-angular.md +0 -127
  24. package/claude/refs/sd-code-conventions.md +0 -155
  25. package/claude/refs/sd-directories.md +0 -7
  26. package/claude/refs/sd-library-issue.md +0 -7
  27. package/claude/refs/sd-migration.md +0 -7
  28. package/claude/refs/sd-orm-v12.md +0 -81
  29. package/claude/refs/sd-orm.md +0 -23
  30. package/claude/refs/sd-service.md +0 -5
  31. package/claude/refs/sd-simplysm-docs.md +0 -52
  32. package/claude/refs/sd-solid.md +0 -68
  33. package/claude/refs/sd-workflow.md +0 -25
  34. package/claude/rules/sd-refs-linker.md +0 -52
  35. package/claude/sd-statusline.js +0 -296
  36. package/claude/skills/sd-api-name-review/SKILL.md +0 -154
  37. package/claude/skills/sd-brainstorm/SKILL.md +0 -215
  38. package/claude/skills/sd-debug/condition-based-waiting-example.ts +0 -158
  39. package/claude/skills/sd-debug/condition-based-waiting.md +0 -114
  40. package/claude/skills/sd-debug/defense-in-depth.md +0 -128
  41. package/claude/skills/sd-debug/find-polluter.sh +0 -64
  42. package/claude/skills/sd-debug/root-cause-tracing.md +0 -168
  43. package/claude/skills/sd-discuss/SKILL.md +0 -91
  44. package/claude/skills/sd-explore/SKILL.md +0 -118
  45. package/claude/skills/sd-plan-dev/SKILL.md +0 -294
  46. package/claude/skills/sd-plan-dev/code-quality-reviewer-prompt.md +0 -49
  47. package/claude/skills/sd-plan-dev/final-review-prompt.md +0 -50
  48. package/claude/skills/sd-plan-dev/implementer-prompt.md +0 -60
  49. package/claude/skills/sd-plan-dev/spec-reviewer-prompt.md +0 -45
  50. package/claude/skills/sd-review/api-reviewer-prompt.md +0 -75
  51. package/claude/skills/sd-review/code-reviewer-prompt.md +0 -82
  52. package/claude/skills/sd-review/convention-checker-prompt.md +0 -61
  53. package/claude/skills/sd-review/refactoring-analyzer-prompt.md +0 -92
  54. package/claude/skills/sd-skill/SKILL.md +0 -417
  55. package/claude/skills/sd-skill/anthropic-best-practices.md +0 -156
  56. package/claude/skills/sd-skill/cso-guide.md +0 -161
  57. package/claude/skills/sd-skill/examples/CLAUDE_MD_TESTING.md +0 -200
  58. package/claude/skills/sd-skill/persuasion-principles.md +0 -220
  59. package/claude/skills/sd-skill/testing-skills-with-subagents.md +0 -408
  60. package/claude/skills/sd-skill/writing-guide.md +0 -159
  61. package/claude/skills/sd-tdd/SKILL.md +0 -385
  62. package/claude/skills/sd-tdd/testing-anti-patterns.md +0 -317
  63. package/claude/skills/sd-use/SKILL.md +0 -67
  64. package/claude/skills/sd-worktree/SKILL.md +0 -78
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.
@@ -1,81 +0,0 @@
1
- # ORM Guidelines (v12)
2
-
3
- ## Table Definition — Decorator-based
4
-
5
- ```typescript
6
- @Table({ description: "Users", database: "mydb" })
7
- export class User {
8
- @Column({ primaryKey: 1, autoIncrement: true, description: "ID" })
9
- id!: number;
10
-
11
- @Column({ dataType: { type: "STRING", length: 100 }, description: "Name" })
12
- name!: string;
13
-
14
- @Column({ nullable: true, description: "Email" })
15
- email?: string;
16
-
17
- @ForeignKey(["departmentId"], () => Department, "Department")
18
- department?: Department;
19
-
20
- @ForeignKeyTarget(() => Order, "user", "Orders")
21
- orders?: Order[];
22
- }
23
- ```
24
-
25
- ### Decorators
26
-
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
-
35
- ### Requirements
36
-
37
- - `tsconfig` requires `experimentalDecorators: true` and `emitDecoratorMetadata: true`
38
-
39
- ## DbContext
40
-
41
- ```typescript
42
- export abstract class MyDbContext extends DbContext {
43
- user = new Queryable(this, User);
44
- order = new Queryable(this, Order);
45
-
46
- get migrations(): Type<IDbMigration>[] {
47
- return [Migration001, Migration002];
48
- }
49
- }
50
- ```
51
-
52
- ## Query
53
-
54
- ```typescript
55
- // Select
56
- const users = await db.user
57
- .select((item) => ({ id: item.id, name: item.name }))
58
- .where((item) => [db.qh.equal(item.id, userId)])
59
- .resultAsync();
60
-
61
- // Insert
62
- await db.user.insertAsync([{ name: "John Doe" }]);
63
-
64
- // Connect with transaction
65
- await db.connectAsync(async () => {
66
- await db.user.insertAsync([{ name: "John Doe" }]);
67
- });
68
- ```
69
-
70
- ## SQL Injection Prevention
71
-
72
- ORM uses string escaping (not parameter binding). **Always validate user input before ORM queries.**
73
-
74
- ```typescript
75
- const userId = Number(req.query.id);
76
- if (Number.isNaN(userId)) throw new Error("Invalid ID");
77
- await db.user
78
- .select((item) => ({ id: item.id, name: item.name }))
79
- .where((item) => [db.qh.equal(item.id, userId)])
80
- .resultAsync();
81
- ```