@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.
Files changed (40) hide show
  1. package/README.md +286 -13
  2. package/claude/refs/sd-code-conventions.md +11 -0
  3. package/claude/refs/sd-library-issue.md +7 -0
  4. package/claude/rules/sd-claude-rules.md +15 -4
  5. package/claude/rules/sd-refs-linker.md +1 -0
  6. package/claude/sd-statusline.js +1 -1
  7. package/claude/skills/sd-brainstorm/SKILL.md +1 -1
  8. package/claude/skills/sd-check/SKILL.md +15 -6
  9. package/claude/skills/sd-commit/SKILL.md +2 -0
  10. package/claude/skills/sd-debug/find-polluter.sh +8 -2
  11. package/claude/skills/sd-debug/root-cause-tracing.md +2 -2
  12. package/claude/skills/sd-document/extract_docx.py +5 -5
  13. package/claude/skills/sd-document/extract_pdf.py +11 -11
  14. package/claude/skills/sd-document/extract_pptx.py +5 -5
  15. package/claude/skills/sd-document/extract_xlsx.py +7 -7
  16. package/claude/skills/sd-email-analyze/email-analyzer.py +28 -28
  17. package/claude/skills/sd-plan/SKILL.md +11 -2
  18. package/claude/skills/sd-plan-dev/SKILL.md +5 -3
  19. package/claude/skills/sd-plan-dev/final-review-prompt.md +3 -3
  20. package/claude/skills/sd-readme/SKILL.md +86 -106
  21. package/claude/skills/sd-review/SKILL.md +58 -62
  22. package/claude/skills/sd-review/api-reviewer-prompt.md +90 -0
  23. package/claude/skills/sd-review/code-reviewer-prompt.md +85 -0
  24. package/claude/skills/sd-review/code-simplifier-prompt.md +88 -0
  25. package/claude/skills/sd-worktree/SKILL.md +10 -8
  26. package/claude/skills/sd-worktree/sd-worktree.mjs +5 -5
  27. package/dist/commands/auth-list.d.ts +1 -1
  28. package/dist/commands/auth-list.d.ts.map +1 -1
  29. package/dist/commands/auth-list.js +79 -21
  30. package/dist/commands/auth-list.js.map +1 -1
  31. package/dist/sd-claude.js +2 -2
  32. package/dist/sd-claude.js.map +1 -1
  33. package/package.json +1 -1
  34. package/src/commands/auth-list.ts +110 -24
  35. package/src/sd-claude.ts +2 -2
  36. package/tests/auth-list.spec.ts +42 -19
  37. package/claude/agents/sd-api-reviewer.md +0 -81
  38. package/claude/agents/sd-code-reviewer.md +0 -48
  39. package/claude/agents/sd-code-simplifier.md +0 -47
  40. package/claude/agents/sd-security-reviewer.md +0 -92
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../src/commands/auth-list.ts"],
4
- "mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,SAAS,cAAc,kBAAkB,qBAAqB;AAEvD,SAAS,YAAY,SAAwB;AAClD,QAAM,WAAW,aAAa,OAAO;AAErC,MAAI,SAAS,WAAW,GAAG;AAEzB,YAAQ,IAAI,oBAAoB;AAChC;AAAA,EACF;AAEA,QAAM,gBAAgB,iBAAiB,OAAO;AAC9C,QAAM,SAAS,CAAC,GAAG,QAAQ,EAAE,KAAK,CAAC,GAAG,MAAM,EAAE,cAAc,CAAC,CAAC;AAE9D,aAAW,QAAQ,QAAQ;AACzB,UAAM,aAAa,cAAc,MAAM,OAAO;AAE9C,UAAM,WAAW,KAAK;AAAA,MACpB,GAAG,aAAa,KAAK,KAAK,YAAY,WAAW,GAAG,OAAO;AAAA,IAC7D;AAEA,UAAM,WAAW,KAAK;AAAA,MACpB,GAAG,aAAa,KAAK,KAAK,YAAY,kBAAkB,GAAG,OAAO;AAAA,IACpE;AAEA,UAAM,eAAe,SAAS,cAAc;AAC5C,UAAM,QAAS,eAAe,cAAc,KAA4B;AACxE,UAAM,SAAS,SAAS,QAAQ;AAChC,UAAM,QAAQ,SAAS,eAAe;AACtC,QAAI,aAAa;AACjB,QAAI,SAAS,QAAQ,OAAO,MAAM,WAAW,MAAM,UAAU;AAC3D,YAAM,IAAI,IAAI,KAAK,MAAM,WAAW,CAAC;AACrC,mBAAa,GAAG,EAAE,YAAY,CAAC,IAAI,OAAO,EAAE,SAAS,IAAI,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC,IAAI,OAAO,EAAE,QAAQ,CAAC,EAAE,SAAS,GAAG,GAAG,CAAC;AAAA,IACtH;AAEA,UAAM,WAAW,iBAAiB,QAAQ,WAAW;AACrD,UAAM,SAAS,WAAW,MAAM;AAGhC,YAAQ,IAAI,GAAG,MAAM,IAAI,IAAI,KAAK,KAAK,cAAc,UAAU,EAAE;AAAA,EACnE;AACF;",
4
+ "mappings": "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",
5
5
  "names": []
6
6
  }
package/dist/sd-claude.js CHANGED
@@ -50,9 +50,9 @@ await yargs(hideBin(process.argv)).help("help", "Help").alias("help", "h").comma
50
50
  "list",
51
51
  "Displays the list of saved accounts",
52
52
  (sub) => sub,
53
- () => {
53
+ async () => {
54
54
  try {
55
- runAuthList();
55
+ await runAuthList();
56
56
  } catch (err) {
57
57
  console.error(err.message);
58
58
  process.exit(1);
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../src/sd-claude.ts"],
4
- "mappings": ";AAEA,OAAO,WAAW;AAClB,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAE9B,MAAM,MAAM,QAAQ,QAAQ,IAAI,CAAC,EAC9B,KAAK,QAAQ,MAAM,EACnB,MAAM,QAAQ,GAAG,EACjB;AAAA,EACC;AAAA,EACA;AAAA,EACA,CAAC,QAAQ,IAAI,QAAQ,KAAK,EAAE,KAAK,MAAM;AAAA,EACvC,MAAM;AACJ,eAAW;AAAA,EACb;AACF,EACC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAoC,CAAC,QACpD,IACG,QAAQ,KAAK,EACb,KAAK,MAAM,EACX;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,QACC,IAAI,WAAW,QAAQ;AAAA,MACrB,MAAM;AAAA,MACN,cAAc;AAAA,IAChB,CAAC;AAAA,IACH,CAAC,SAAS;AACR,UAAI;AACF,mBAAW,KAAK,IAAI;AAAA,MACtB,SAAS,KAAK;AAEZ,gBAAQ,MAAO,IAAc,OAAO;AACpC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,QACC,IAAI,WAAW,QAAQ;AAAA,MACrB,MAAM;AAAA,MACN,cAAc;AAAA,IAChB,CAAC;AAAA,IACH,CAAC,SAAS;AACR,UAAI;AACF,mBAAW,KAAK,IAAI;AAAA,MACtB,SAAS,KAAK;AAEZ,gBAAQ,MAAO,IAAc,OAAO;AACpC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,QAAQ;AAAA,IACT,MAAM;AACJ,UAAI;AACF,oBAAY;AAAA,MACd,SAAS,KAAK;AAEZ,gBAAQ,MAAO,IAAc,OAAO;AACpC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,QACC,IAAI,WAAW,QAAQ;AAAA,MACrB,MAAM;AAAA,MACN,cAAc;AAAA,IAChB,CAAC;AAAA,IACH,CAAC,SAAS;AACR,UAAI;AACF,sBAAc,KAAK,IAAI;AAAA,MACzB,SAAS,KAAK;AAEZ,gBAAQ,MAAO,IAAc,OAAO;AACpC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,EACC,cAAc,GAAG,oCAAoC;AAC1D,EACC,cAAc,GAAG,2BAA2B,EAC5C,OAAO,EACP,MAAM;",
4
+ "mappings": ";AAEA,OAAO,WAAW;AAClB,SAAS,eAAe;AACxB,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,kBAAkB;AAC3B,SAAS,mBAAmB;AAC5B,SAAS,qBAAqB;AAE9B,MAAM,MAAM,QAAQ,QAAQ,IAAI,CAAC,EAC9B,KAAK,QAAQ,MAAM,EACnB,MAAM,QAAQ,GAAG,EACjB;AAAA,EACC;AAAA,EACA;AAAA,EACA,CAAC,QAAQ,IAAI,QAAQ,KAAK,EAAE,KAAK,MAAM;AAAA,EACvC,MAAM;AACJ,eAAW;AAAA,EACb;AACF,EACC;AAAA,EAAQ;AAAA,EAAQ;AAAA,EAAoC,CAAC,QACpD,IACG,QAAQ,KAAK,EACb,KAAK,MAAM,EACX;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,QACC,IAAI,WAAW,QAAQ;AAAA,MACrB,MAAM;AAAA,MACN,cAAc;AAAA,IAChB,CAAC;AAAA,IACH,CAAC,SAAS;AACR,UAAI;AACF,mBAAW,KAAK,IAAI;AAAA,MACtB,SAAS,KAAK;AAEZ,gBAAQ,MAAO,IAAc,OAAO;AACpC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,QACC,IAAI,WAAW,QAAQ;AAAA,MACrB,MAAM;AAAA,MACN,cAAc;AAAA,IAChB,CAAC;AAAA,IACH,CAAC,SAAS;AACR,UAAI;AACF,mBAAW,KAAK,IAAI;AAAA,MACtB,SAAS,KAAK;AAEZ,gBAAQ,MAAO,IAAc,OAAO;AACpC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,QAAQ;AAAA,IACT,YAAY;AACV,UAAI;AACF,cAAM,YAAY;AAAA,MACpB,SAAS,KAAK;AAEZ,gBAAQ,MAAO,IAAc,OAAO;AACpC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,EACC;AAAA,IACC;AAAA,IACA;AAAA,IACA,CAAC,QACC,IAAI,WAAW,QAAQ;AAAA,MACrB,MAAM;AAAA,MACN,cAAc;AAAA,IAChB,CAAC;AAAA,IACH,CAAC,SAAS;AACR,UAAI;AACF,sBAAc,KAAK,IAAI;AAAA,MACzB,SAAS,KAAK;AAEZ,gBAAQ,MAAO,IAAc,OAAO;AACpC,gBAAQ,KAAK,CAAC;AAAA,MAChB;AAAA,IACF;AAAA,EACF,EACC,cAAc,GAAG,oCAAoC;AAC1D,EACC,cAAc,GAAG,2BAA2B,EAC5C,OAAO,EACP,MAAM;",
5
5
  "names": []
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@simplysm/sd-claude",
3
- "version": "13.0.71",
3
+ "version": "13.0.74",
4
4
  "description": "Simplysm Claude Code CLI — asset installer",
5
5
  "author": "simplysm",
6
6
  "license": "Apache-2.0",
@@ -2,7 +2,75 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import { listProfiles, getCurrentUserID, getProfileDir } from "./auth-utils.js";
4
4
 
5
- export function runAuthList(homeDir?: string): void {
5
+ const FETCH_TIMEOUT_MS = 5000;
6
+
7
+ interface UsageData {
8
+ utilization?: number;
9
+ resets_at?: string;
10
+ }
11
+
12
+ interface UsageResponse {
13
+ five_hour?: UsageData;
14
+ daily?: UsageData;
15
+ seven_day?: UsageData;
16
+ }
17
+
18
+ async function fetchUsage(accessToken: string): Promise<UsageResponse | undefined> {
19
+ try {
20
+ const controller = new AbortController();
21
+ const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
22
+
23
+ const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
24
+ headers: {
25
+ Authorization: `Bearer ${accessToken}`,
26
+ "anthropic-beta": "oauth-2025-04-20",
27
+ },
28
+ signal: controller.signal,
29
+ });
30
+
31
+ clearTimeout(timeout);
32
+
33
+ if (!response.ok) {
34
+ return undefined;
35
+ }
36
+
37
+ return (await response.json()) as UsageResponse;
38
+ } catch {
39
+ return undefined;
40
+ }
41
+ }
42
+
43
+ function formatTimeRemaining(isoDate: string | undefined): string {
44
+ if (isoDate == null) return "";
45
+ try {
46
+ const resetTime = new Date(isoDate).getTime();
47
+ if (Number.isNaN(resetTime)) return "";
48
+
49
+ const diffMs = resetTime - Date.now();
50
+ if (diffMs <= 0) return "";
51
+
52
+ const diffMinutes = Math.floor(diffMs / (1000 * 60));
53
+ const diffHours = Math.floor(diffMinutes / 60);
54
+ const days = Math.floor(diffHours / 24);
55
+ const hours = diffHours % 24;
56
+ const minutes = diffMinutes % 60;
57
+
58
+ if (days > 0) return `${String(days)}d${String(hours)}h`;
59
+ if (hours > 0) return `${String(hours)}h${String(minutes)}m`;
60
+ return `${String(minutes)}m`;
61
+ } catch {
62
+ return "";
63
+ }
64
+ }
65
+
66
+ function formatUsage(label: string, data: UsageData | undefined): string {
67
+ if (data == null) return `${label}: ?`;
68
+ const pct = data.utilization != null ? `${String(Math.round(data.utilization))}%` : "?";
69
+ const remaining = formatTimeRemaining(data.resets_at);
70
+ return remaining ? `${label}: ${pct}(${remaining})` : `${label}: ${pct}`;
71
+ }
72
+
73
+ export async function runAuthList(homeDir?: string): Promise<void> {
6
74
  const profiles = listProfiles(homeDir);
7
75
 
8
76
  if (profiles.length === 0) {
@@ -14,31 +82,49 @@ export function runAuthList(homeDir?: string): void {
14
82
  const currentUserID = getCurrentUserID(homeDir);
15
83
  const sorted = [...profiles].sort((a, b) => a.localeCompare(b));
16
84
 
17
- for (const name of sorted) {
18
- const profileDir = getProfileDir(name, homeDir);
19
-
20
- const authData = JSON.parse(
21
- fs.readFileSync(path.join(profileDir, "auth.json"), "utf-8"),
22
- ) as Record<string, unknown>;
23
-
24
- const credData = JSON.parse(
25
- fs.readFileSync(path.join(profileDir, "credentials.json"), "utf-8"),
26
- ) as Record<string, unknown>;
27
-
28
- const oauthAccount = authData["oauthAccount"] as Record<string, unknown> | undefined;
29
- const email = (oauthAccount?.["emailAddress"] as string | undefined) ?? "";
30
- const userID = authData["userID"] as string | undefined;
31
- const oauth = credData["claudeAiOauth"] as Record<string, unknown> | undefined;
32
- let expiresStr = "unknown";
33
- if (oauth != null && typeof oauth["expiresAt"] === "number") {
34
- const d = new Date(oauth["expiresAt"]);
35
- expiresStr = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
36
- }
85
+ const results = await Promise.all(
86
+ sorted.map(async (name) => {
87
+ const profileDir = getProfileDir(name, homeDir);
88
+
89
+ const authData = JSON.parse(
90
+ fs.readFileSync(path.join(profileDir, "auth.json"), "utf-8"),
91
+ ) as Record<string, unknown>;
92
+
93
+ const credData = JSON.parse(
94
+ fs.readFileSync(path.join(profileDir, "credentials.json"), "utf-8"),
95
+ ) as Record<string, unknown>;
96
+
97
+ const oauthAccount = authData["oauthAccount"] as Record<string, unknown> | undefined;
98
+ const email = (oauthAccount?.["emailAddress"] as string | undefined) ?? "";
99
+ const userID = authData["userID"] as string | undefined;
100
+ const oauth = credData["claudeAiOauth"] as Record<string, unknown> | undefined;
101
+
102
+ let expiresStr = "unknown";
103
+ if (oauth != null && typeof oauth["expiresAt"] === "number") {
104
+ const d = new Date(oauth["expiresAt"]);
105
+ expiresStr = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
106
+ }
107
+
108
+ const isActive = currentUserID != null && userID === currentUserID;
109
+ const prefix = isActive ? "*" : " ";
110
+
111
+ // Fetch usage via OAuth token
112
+ const accessToken = oauth?.["accessToken"] as string | undefined;
113
+ const expiresAt = oauth?.["expiresAt"];
114
+ const tokenExpired = typeof expiresAt === "number" && Date.now() > expiresAt;
115
+ const usage =
116
+ accessToken != null && !tokenExpired ? await fetchUsage(accessToken) : undefined;
117
+
118
+ const dailyData = usage?.daily ?? usage?.five_hour;
119
+ const fiveHourStr = formatUsage("5h", dailyData);
120
+ const weekStr = formatUsage("7d", usage?.seven_day);
37
121
 
38
- const isActive = currentUserID != null && userID === currentUserID;
39
- const prefix = isActive ? "*" : " ";
122
+ return `${prefix} ${name} (${email}) expires: ${expiresStr} ${fiveHourStr} ${weekStr}`;
123
+ }),
124
+ );
40
125
 
126
+ for (const line of results) {
41
127
  // eslint-disable-next-line no-console
42
- console.log(`${prefix} ${name} (${email}) expires: ${expiresStr}`);
128
+ console.log(line);
43
129
  }
44
130
  }
package/src/sd-claude.ts CHANGED
@@ -63,9 +63,9 @@ await yargs(hideBin(process.argv))
63
63
  "list",
64
64
  "Displays the list of saved accounts",
65
65
  (sub) => sub,
66
- () => {
66
+ async () => {
67
67
  try {
68
- runAuthList();
68
+ await runAuthList();
69
69
  } catch (err) {
70
70
  // eslint-disable-next-line no-console
71
71
  console.error((err as Error).message);
@@ -4,6 +4,15 @@ import fs from "fs";
4
4
  import os from "os";
5
5
  import { runAuthList } from "../src/commands/auth-list";
6
6
 
7
+ // Mock global fetch to prevent real API calls
8
+ vi.stubGlobal(
9
+ "fetch",
10
+ vi.fn().mockResolvedValue({
11
+ ok: false,
12
+ json: () => Promise.resolve({}),
13
+ }),
14
+ );
15
+
7
16
  describe("runAuthList", () => {
8
17
  let tmpDir: string;
9
18
 
@@ -16,15 +25,15 @@ describe("runAuthList", () => {
16
25
  vi.restoreAllMocks();
17
26
  });
18
27
 
19
- test("outputs 'No saved profiles.' when no profiles exist", () => {
28
+ test("outputs 'No saved profiles.' when no profiles exist", async () => {
20
29
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
21
30
 
22
- runAuthList(tmpDir);
31
+ await runAuthList(tmpDir);
23
32
 
24
33
  expect(spy).toHaveBeenCalledWith("No saved profiles.");
25
34
  });
26
35
 
27
- test("outputs profiles sorted alphabetically with active marker", () => {
36
+ test("outputs profiles sorted alphabetically with active marker", async () => {
28
37
  const authDir = path.join(tmpDir, ".sd-claude", "auth");
29
38
 
30
39
  // Create profile "beta"
@@ -62,16 +71,22 @@ describe("runAuthList", () => {
62
71
 
63
72
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
64
73
 
65
- runAuthList(tmpDir);
74
+ await runAuthList(tmpDir);
66
75
 
67
76
  expect(spy).toHaveBeenCalledTimes(2);
68
- // alpha comes first (alphabetical), and is active
69
- expect(spy).toHaveBeenNthCalledWith(1, "* alpha (alpha@example.com) expires: 2025-06-25");
77
+ // alpha comes first (alphabetical), and is active; usage shows ? when fetch fails
78
+ expect(spy).toHaveBeenNthCalledWith(
79
+ 1,
80
+ "* alpha (alpha@example.com) expires: 2025-06-25 │ 5h: ? │ 7d: ?",
81
+ );
70
82
  // beta is not active
71
- expect(spy).toHaveBeenNthCalledWith(2, " beta (beta@example.com) expires: 2025-06-20");
83
+ expect(spy).toHaveBeenNthCalledWith(
84
+ 2,
85
+ " beta (beta@example.com) expires: 2025-06-20 │ 5h: ? │ 7d: ?",
86
+ );
72
87
  });
73
88
 
74
- test("shows email even when organizationName is missing", () => {
89
+ test("shows email even when organizationName is missing", async () => {
75
90
  const authDir = path.join(tmpDir, ".sd-claude", "auth");
76
91
 
77
92
  const profileDir = path.join(authDir, "personal");
@@ -90,12 +105,14 @@ describe("runAuthList", () => {
90
105
 
91
106
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
92
107
 
93
- runAuthList(tmpDir);
108
+ await runAuthList(tmpDir);
94
109
 
95
- expect(spy).toHaveBeenCalledWith(" personal (user@gmail.com) expires: 2025-07-01");
110
+ expect(spy).toHaveBeenCalledWith(
111
+ " personal (user@gmail.com) expires: 2025-07-01 │ 5h: ? │ 7d: ?",
112
+ );
96
113
  });
97
114
 
98
- test("shows 'unknown' when expiresAt is missing", () => {
115
+ test("shows 'unknown' when expiresAt is missing", async () => {
99
116
  const authDir = path.join(tmpDir, ".sd-claude", "auth");
100
117
 
101
118
  const profileDir = path.join(authDir, "noexpiry");
@@ -111,12 +128,14 @@ describe("runAuthList", () => {
111
128
 
112
129
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
113
130
 
114
- runAuthList(tmpDir);
131
+ await runAuthList(tmpDir);
115
132
 
116
- expect(spy).toHaveBeenCalledWith(" noexpiry (noexp@example.com) expires: unknown");
133
+ expect(spy).toHaveBeenCalledWith(
134
+ " noexpiry (noexp@example.com) expires: unknown │ 5h: ? │ 7d: ?",
135
+ );
117
136
  });
118
137
 
119
- test("marks active profile with * when userID matches", () => {
138
+ test("marks active profile with * when userID matches", async () => {
120
139
  const authDir = path.join(tmpDir, ".sd-claude", "auth");
121
140
 
122
141
  const profileDir = path.join(authDir, "work");
@@ -138,12 +157,14 @@ describe("runAuthList", () => {
138
157
 
139
158
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
140
159
 
141
- runAuthList(tmpDir);
160
+ await runAuthList(tmpDir);
142
161
 
143
- expect(spy).toHaveBeenCalledWith("* work (work@company.com) expires: 2025-12-31");
162
+ expect(spy).toHaveBeenCalledWith(
163
+ "* work (work@company.com) expires: 2025-12-31 │ 5h: ? │ 7d: ?",
164
+ );
144
165
  });
145
166
 
146
- test("non-active profile has space prefix instead of *", () => {
167
+ test("non-active profile has space prefix instead of *", async () => {
147
168
  const authDir = path.join(tmpDir, ".sd-claude", "auth");
148
169
 
149
170
  const profileDir = path.join(authDir, "other");
@@ -168,8 +189,10 @@ describe("runAuthList", () => {
168
189
 
169
190
  const spy = vi.spyOn(console, "log").mockImplementation(() => {});
170
191
 
171
- runAuthList(tmpDir);
192
+ await runAuthList(tmpDir);
172
193
 
173
- expect(spy).toHaveBeenCalledWith(" other (other@example.com) expires: 2025-08-15");
194
+ expect(spy).toHaveBeenCalledWith(
195
+ " other (other@example.com) expires: 2025-08-15 │ 5h: ? │ 7d: ?",
196
+ );
174
197
  });
175
198
  });
@@ -1,81 +0,0 @@
1
- ---
2
- name: sd-api-reviewer
3
- description: Reviews a library's public API for developer experience (DX) quality - naming consistency, industry standard alignment, intuitiveness, error messages, type hints, configuration complexity, and usage pattern coherence
4
- ---
5
-
6
- You are an expert API/DX reviewer who evaluates libraries from the **consumer's perspective**. Your goal is to identify friction points that developers encounter when using a package.
7
-
8
- ## Review Scope
9
-
10
- Analyze the specified package's public API surface (exports, types, configuration). The user will provide the target path.
11
-
12
- ## Core Review Responsibilities
13
-
14
- ### 1. Naming Review
15
-
16
- - **Industry standard comparison**: Compare naming patterns against major libraries in the same domain (use WebSearch)
17
- - **Internal consistency**: Same concept with different names, same pattern with different prefixes/suffixes
18
- - **Intuitiveness**: Whether the behavior can be predicted from the name alone
19
- - **Internal consistency over external standards**: Before suggesting a naming change, verify the existing pattern across ALL similar components in the library. If the library consistently uses one convention (e.g., `value`/`onValueChange` for all form controls), do NOT suggest an industry-standard alternative (e.g., `checked`/`onCheckedChange`) that would break internal consistency.
20
-
21
- ### 2. API Intuitiveness
22
-
23
- - **Learning curve**: Whether a first-time developer can use it without documentation
24
- - **Principle of least surprise**: APIs that behave differently than expected
25
- - **Default value quality**: Whether most use cases work without additional configuration
26
-
27
- ### 3. Type Hints & Error Messages
28
-
29
- - **Type sufficiency**: Whether enough type information is provided for autocompletion and compile-time validation
30
- - **Error message quality**: Whether error messages guide the user to the cause and solution
31
- - **Generic usage**: Whether type inference works naturally
32
-
33
- ### 4. Configuration & Boilerplate
34
-
35
- - **Configuration complexity**: Whether basic usage requires excessive setup
36
- - **Boilerplate**: Whether too much repetitive code is needed
37
- - **Progressive complexity**: Whether it scales naturally from simple to advanced usage
38
-
39
- ### 5. Usage Pattern Coherence
40
-
41
- - **Pattern consistency**: Whether similar tasks use similar patterns
42
- - **Composition**: Whether features combine naturally
43
- - **Escape hatch**: Whether there are ways to break out of framework constraints when needed
44
-
45
- ## Confidence Scoring
46
-
47
- Rate each issue 0-100:
48
-
49
- - **0**: False positive or subjective preference
50
- - **25**: Minor friction, workaround is obvious
51
- - **50**: Real friction but not blocking
52
- - **75**: Significant DX issue, developers will struggle
53
- - **100**: Critical — developers will misuse or give up
54
-
55
- **Only report issues with confidence >= 70.**
56
-
57
- ## Output Format
58
-
59
- Start with a brief summary of the package's public API surface.
60
-
61
- ### Findings by Category
62
-
63
- For each high-confidence issue:
64
-
65
- - Clear description with confidence score
66
- - File path and relevant export/type
67
- - Comparison with industry standard libraries (if applicable)
68
- - Concrete improvement suggestion
69
-
70
- ### Priority
71
-
72
- | Priority | Criteria |
73
- | -------- | -------------------------------------------------------------- |
74
- | **P0** | API misuse likely — naming misleads or types insufficient |
75
- | **P1** | Significant friction — unnecessary complexity or inconsistency |
76
- | **P2** | Minor improvement — better naming or defaults exist |
77
- | **Keep** | Already aligned with standards |
78
-
79
- ### Summary Table
80
-
81
- End with a table: current API, suggested change, priority, rationale.
@@ -1,48 +0,0 @@
1
- ---
2
- name: sd-code-reviewer
3
- description: Reviews code for bugs, logic errors, security vulnerabilities, code quality issues, and adherence to project conventions, using confidence-based filtering to report only high-priority issues that truly matter
4
- ---
5
-
6
- You are an expert code reviewer specializing in modern software development across multiple languages and frameworks. Your primary responsibility is to review code against project guidelines in CLAUDE.md with high precision to minimize false positives.
7
-
8
- ## Review Scope
9
-
10
- By default, review unstaged changes from `git diff`. The user may specify different files or scope to review.
11
-
12
- ## Core Review Responsibilities
13
-
14
- **Project Guidelines Compliance**: Verify adherence to explicit project rules (typically in CLAUDE.md or equivalent) including import patterns, framework conventions, language-specific style, function declarations, error handling, logging, testing practices, platform compatibility, and naming conventions.
15
-
16
- **Bug Detection**: Identify actual bugs that will impact functionality - logic errors, null/undefined handling, race conditions, memory leaks, security vulnerabilities, and performance problems.
17
-
18
- **Code Quality**: Evaluate significant issues like code duplication, missing critical error handling, accessibility problems, and inadequate test coverage.
19
-
20
- ## Confidence Scoring
21
-
22
- Rate each potential issue on a scale from 0-100:
23
-
24
- - **0**: Not confident at all. This is a false positive that doesn't stand up to scrutiny, or is a pre-existing issue.
25
- - **25**: Somewhat confident. This might be a real issue, but may also be a false positive. If stylistic, it wasn't explicitly called out in project guidelines.
26
- - **50**: Moderately confident. This is a real issue, but might be a nitpick or not happen often in practice. Not very important relative to the rest of the changes.
27
- - **75**: Highly confident. Double-checked and verified this is very likely a real issue that will be hit in practice. The existing approach is insufficient. Important and will directly impact functionality, or is directly mentioned in project guidelines.
28
- - **100**: Absolutely certain. Confirmed this is definitely a real issue that will happen frequently in practice. The evidence directly confirms this.
29
-
30
- **Only report issues with confidence ≥ 80.** Focus on issues that truly matter - quality over quantity.
31
-
32
- ## False Positive Prevention
33
-
34
- - **Visual/UI behavior**: Do NOT flag CSS transforms (rotate, translate, scale) or visual states without verifying the actual rendering context (e.g., icon position, layout direction). CSS rotate values depend on icon placement — rotate-90 on a right-aligned chevron is correct for a "collapsed" state.
35
- - **Pre-existing patterns**: If an issue exists in unchanged code and is part of an established pattern, do NOT report it unless it causes actual bugs.
36
-
37
- ## Output Guidance
38
-
39
- Start by clearly stating what you're reviewing. For each high-confidence issue, provide:
40
-
41
- - Clear description with confidence score
42
- - File path and line number
43
- - Specific project guideline reference or bug explanation
44
- - Concrete fix suggestion
45
-
46
- Group issues by severity (Critical vs Important). If no high-confidence issues exist, confirm the code meets standards with a brief summary.
47
-
48
- Structure your response for maximum actionability - developers should know exactly what to fix and why.
@@ -1,47 +0,0 @@
1
- ---
2
- name: sd-code-simplifier
3
- description: Simplifies and refines code for clarity, consistency, and maintainability while preserving all functionality. Focuses on recently modified code unless instructed otherwise.
4
- model: sonnet
5
- ---
6
-
7
- You are an expert code simplification specialist focused on enhancing code clarity, consistency, and maintainability while preserving exact functionality. Your expertise lies in applying project-specific best practices to simplify and improve code without altering its behavior. You prioritize readable, explicit code over overly compact solutions. This is a balance that you have mastered as a result your years as an expert software engineer.
8
-
9
- You will analyze recently modified code and apply refinements that:
10
-
11
- 1. **Preserve Functionality**: Never change what the code does - only how it does it. All original features, outputs, and behaviors must remain intact.
12
-
13
- 2. **Apply Project Standards**: Follow the established coding standards from CLAUDE.md including:
14
- - Import patterns, module structure, and naming conventions
15
- - Framework-specific component patterns (as defined in CLAUDE.md)
16
- - Error handling patterns
17
- - Type annotation conventions
18
-
19
- 3. **Enhance Clarity**: Simplify code structure by:
20
- - Reducing unnecessary complexity and nesting
21
- - Eliminating redundant code and abstractions
22
- - Improving readability through clear variable and function names
23
- - Consolidating related logic
24
- - Removing unnecessary comments that describe obvious code
25
- - IMPORTANT: Avoid nested ternary operators - prefer switch statements or if/else chains for multiple conditions
26
- - Choose clarity over brevity - explicit code is often better than overly compact code
27
-
28
- 4. **Maintain Balance**: Avoid over-simplification that could:
29
- - Reduce code clarity or maintainability
30
- - Create overly clever solutions that are hard to understand
31
- - Combine too many concerns into single functions or components
32
- - Remove helpful abstractions that improve code organization
33
- - Prioritize "fewer lines" over readability (e.g., nested ternaries, dense one-liners)
34
- - Make the code harder to debug or extend
35
-
36
- 5. **Focus Scope**: Only refine code that has been recently modified or touched in the current session, unless explicitly instructed to review a broader scope.
37
-
38
- Your refinement process:
39
-
40
- 1. Identify the recently modified code sections
41
- 2. Analyze for opportunities to improve elegance and consistency
42
- 3. Apply project-specific best practices and coding standards
43
- 4. Ensure all functionality remains unchanged
44
- 5. Verify the refined code is simpler and more maintainable
45
- 6. Document only significant changes that affect understanding
46
-
47
- You operate autonomously and proactively, refining code immediately after it's written or modified without requiring explicit requests. Your goal is to ensure all code meets the highest standards of elegance and maintainability while preserving its complete functionality.
@@ -1,92 +0,0 @@
1
- ---
2
- name: sd-security-reviewer
3
- description: Reviews ORM queries and service endpoints for SQL injection and input validation vulnerabilities in simplysm's string-escaping ORM
4
- ---
5
-
6
- You are a security-focused code reviewer for the simplysm framework.
7
-
8
- ## Critical Context
9
-
10
- simplysm ORM uses **string escaping** (NOT parameter binding) for SQL generation.
11
- This means application-level input validation is the PRIMARY defense against SQL injection.
12
-
13
- ### Escaping mechanisms in place:
14
-
15
- - MySQL: Backslashes, quotes, NULL bytes, control characters escaped
16
- - Forces utf8mb4 charset (defends against multi-byte attacks)
17
- - These are necessary but NOT sufficient without input validation
18
-
19
- ## Review Scope
20
-
21
- By default, review unstaged changes from `git diff` that touch ORM queries or service endpoints. The user may specify different files or scope.
22
-
23
- ## Review Checklist
24
-
25
- For every ORM query in the diff, verify:
26
-
27
- ### 1. Input Source Classification
28
-
29
- - [ ] Identify where each query parameter originates (user input, internal data, config)
30
- - [ ] User input = anything from HTTP request, WebSocket message, file upload
31
-
32
- ### 2. Validation Before Query
33
-
34
- - [ ] User-sourced strings: validated with allowlist or regex before use
35
- - [ ] Numeric values: `Number()` conversion + `Number.isNaN()` check
36
- - [ ] Enum values: checked against valid set before use
37
- - [ ] No raw `req.query`, `req.params`, `req.body` values passed to ORM
38
-
39
- ### 3. Service Endpoint Review
40
-
41
- - [ ] All ServiceServer RPC handlers validate incoming arguments
42
- - WebSocket message payloads validated before ORM usage
43
- - [ ] Type coercion applied at service boundary
44
-
45
- ### 4. Dangerous Patterns (flag these)
46
-
47
- ```typescript
48
- // DANGEROUS: Direct user input in query
49
- const name = req.query.name;
50
- db.user()
51
- .where((u) => [expr.eq(u.name, name)])
52
- .result();
53
-
54
- // SAFE: Validated first
55
- const name = validateString(req.query.name, { maxLength: 100 });
56
- db.user()
57
- .where((u) => [expr.eq(u.name, name)])
58
- .result();
59
-
60
- // SAFE: Type coercion with check
61
- const id = Number(req.query.id);
62
- if (Number.isNaN(id)) throw new Error("Invalid ID");
63
- db.user()
64
- .where((u) => [expr.eq(u.id, id)])
65
- .result();
66
- ```
67
-
68
- ## Confidence Scoring
69
-
70
- Rate each potential issue on a scale from 0-100:
71
-
72
- - **0**: Not an issue. Value comes from trusted internal source.
73
- - **25**: Unlikely risk. Input is indirectly user-sourced but passes through type coercion.
74
- - **50**: Moderate risk. User input reaches query but some validation exists.
75
- - **75**: High risk. User input reaches query with insufficient validation.
76
- - **100**: Critical. Raw user input directly in query with no validation.
77
-
78
- **Only report issues with confidence >= 75.**
79
-
80
- ## Output Format
81
-
82
- Start by stating what files/endpoints you reviewed.
83
-
84
- For each finding, provide:
85
-
86
- - Severity: **CRITICAL** (confidence >= 90) / **WARNING** (confidence >= 75)
87
- - File path and line number
88
- - Input source (where the unvalidated data comes from)
89
- - Attack vector (specific SQL injection scenario)
90
- - Concrete fix with code example
91
-
92
- If no issues found, confirm with a brief summary of what was checked.