@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
@@ -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
- ```
@@ -1,23 +0,0 @@
1
- # ORM Guidelines
2
-
3
- ## Table Definition
4
-
5
- ```typescript
6
- const User = Table("User")
7
- .database("mydb")
8
- .columns((c) => ({ id: c.bigint().autoIncrement(), name: c.varchar(100) }))
9
- .primaryKey("id");
10
- ```
11
-
12
- ## SQL Injection Prevention
13
-
14
- ORM uses string escaping (not parameter binding). **Always validate user input before ORM queries.**
15
-
16
- ```typescript
17
- const userId = Number(req.query.id);
18
- if (Number.isNaN(userId)) throw new Error("Invalid ID");
19
- await db
20
- .user()
21
- .where((u) => [expr.eq(u.id, userId)])
22
- .result();
23
- ```
@@ -1,5 +0,0 @@
1
- # Service Guidelines
2
-
3
- - `ServiceServer`: Fastify-based HTTP/WebSocket server
4
- - `ServiceClient`: WebSocket client, RPC calls
5
- - `ServiceProtocol`: Message split/merge (300KB chunks when >3MB)
@@ -1,52 +0,0 @@
1
- # @simplysm Package Documentation
2
-
3
- When you need API details, usage examples, or component props for `@simplysm/*` packages,
4
- read the package's README.md from node_modules.
5
-
6
- ## How to use
7
-
8
- Read the package README directly:
9
-
10
- ```
11
- node_modules/@simplysm/{package-name}/README.md
12
- ```
13
-
14
- If not found (pnpm hoisting), try:
15
-
16
- ```
17
- packages/*/node_modules/@simplysm/{package-name}/README.md
18
- ```
19
-
20
- ## When to use
21
-
22
- **MANDATORY**: Read the relevant README BEFORE any of the following:
23
-
24
- - Writing new code that uses `@simplysm/*` APIs
25
- - Fixing type errors or bugs in code that uses `@simplysm/*` APIs
26
- - Making assumptions about type mappings (e.g., DB column types → TypeScript types)
27
- - Refactoring or migrating code that depends on `@simplysm/*` packages
28
-
29
- Do NOT guess API behavior or type mappings — always verify from the README first.
30
-
31
- ## Available Packages
32
-
33
- | Package | Description |
34
- | ------------------------------ | --------------------------------------------------------------- |
35
- | `core-common` | Common utilities, custom types (DateTime, DateOnly, Time, Uuid) |
36
- | `core-browser` | Browser-specific extensions |
37
- | `core-node` | Node.js utilities (filesystem, workers) |
38
- | `orm-common` | ORM query builder, table schema definitions |
39
- | `orm-node` | DB connectors (MySQL, MSSQL, PostgreSQL) |
40
- | `service-common` | Service protocol, type definitions |
41
- | `service-client` | WebSocket client |
42
- | `service-server` | Fastify-based HTTP/WebSocket server |
43
- | `solid` | SolidJS UI components + Tailwind CSS |
44
- | `excel` | Excel (.xlsx) read/write |
45
- | `storage` | FTP/SFTP client |
46
- | `sd-cli` | Build, lint, typecheck CLI tool |
47
- | `claude` | Claude Code skills/agents (auto-installs via postinstall) |
48
- | `eslint-plugin` | Custom ESLint rules |
49
- | `capacitor-plugin-auto-update` | Auto update |
50
- | `capacitor-plugin-broadcast` | Broadcast |
51
- | `capacitor-plugin-file-system` | File system |
52
- | `capacitor-plugin-usb-storage` | USB storage |
@@ -1,68 +0,0 @@
1
- # SolidJS Guidelines
2
-
3
- **SolidJS is NOT React!**
4
-
5
- ## Core Concepts
6
-
7
- - Component functions run **once** at mount (not on every state change)
8
- - Fine-grained reactivity: unchanged signals don't re-evaluate expressions
9
- - `createMemo`: only for expensive computations used in multiple places
10
- - **Props destructuring prohibited** → use `props.xxx`
11
- - Conditionals: `<Show>`, Lists: `<For>`
12
- - No SSR → browser APIs usable directly
13
- - Responsive: Mobile UI below 520px
14
- - Chrome 84+ target
15
- - CSS NOT transpiled → no `aspect-ratio`, `inset`, `:is()`, `:where()`
16
-
17
- ## Props Design
18
-
19
- - Props that don't need parameters must accept plain values (`editable={perms().edit}`), not wrapped in functions (`editable={() => perms().edit}`) — use function props only when parameters are needed (callbacks)
20
-
21
- ## Implementation Rules
22
-
23
- - Prefer signals/stores over Provider/Context
24
- - Check existing patterns before introducing abstractions
25
- - Before modifying components: always Read the file to check existing props/patterns
26
-
27
- ## Hook Naming
28
-
29
- - `create*`: Reactive hooks wrapping SolidJS primitives
30
- - `use*`: Hooks depending on Provider Context
31
- - Others: no hook prefix
32
-
33
- ## Compound Components
34
-
35
- All sub-components via dot notation only (`Parent.Child`).
36
-
37
- - Export using `Object.assign` pattern:
38
- ```ts
39
- export const Select = Object.assign(SelectInnerComponent, {
40
- Item: SelectItem,
41
- Header: SelectHeader,
42
- Action: SelectAction,
43
- ItemTemplate: SelectItemTemplate,
44
- });
45
- ```
46
- - Do NOT declare a separate type or interface for the compound component (e.g., `SelectComponent`, `TabsComponent`)
47
- - Do NOT use type assertions on the export (e.g., `as SelectComponent`)
48
- - Don't export sub-components separately (export parent only)
49
- - UI elements → compound sub-components, non-rendering config (state, behavior, callbacks) → props
50
-
51
- ## Tailwind CSS
52
-
53
- - `darkMode: "class"`, `aspectRatio` plugin disabled (Chrome 84)
54
- - Semantic colors: `primary`(blue), `info`(sky), `success`(green), `warning`(amber), `danger`(red), `base`(zinc) → never use `zinc-*` directly
55
- - Heights: `field`, `field-sm`, `field-lg`
56
- - z-index: `sidebar`(100), `sidebar-backdrop`(99), `dropdown`(1000)
57
- - Default `rem`, use `em` for text-relative sizing (e.g., Icon)
58
- - Use `clsx()` with semantic grouping + `twMerge()` for conflict resolution
59
- - Before modifying styles: Read existing class patterns of the same component
60
- - **Class strings inline**: Do not extract class strings into separate variables — write them directly in the JSX return
61
- - **`*.styles.ts` files**: Tailwind class strings must be wrapped with `clsx()` for IntelliSense support
62
-
63
- ## Application View Naming (`client-*`)
64
-
65
- - `~Sheet` — List view based on DataSheet (e.g., `UserSheet`)
66
- - `~Detail` — Single record detail/edit view (e.g., `MyInfoDetail`)
67
- - `~View` — Everything else (e.g., `LoginView`, `MainView`)
68
- - Directory: `src/views/` (usable as standalone page, embedded component, or modal)
@@ -1,25 +0,0 @@
1
- # Workflow Rules
2
-
3
- - **No auto-proceeding after skill completion**: When the user explicitly invokes a skill, report the result and **stop** once the skill finishes. Do not guess the next step and proceed arbitrarily. Wait for explicit user instructions if further work is needed.
4
- - **Exception — yolo mode**: When a skill explicitly defines a "yolo mode" that chains multiple skills sequentially (e.g., sd-plan's "Path A: yolo"), auto-proceeding is permitted for the duration of that chain. Each step MUST still be invoked via the Skill tool.
5
-
6
- ## Problem-Solving Principles
7
-
8
- - **Root cause first**: When encountering errors or unexpected behavior, always investigate the root cause before attempting a fix. Do not apply workarounds, hacks, or surface-level patches.
9
- - **No band-aid fixes**: Avoid techniques like suppressing errors, adding defensive checks to hide symptoms, bypassing validation, or inflating timeout values. These mask the real problem and create technical debt.
10
- - **Consider refactoring**: If the root cause reveals a design flaw or structural issue, propose a refactoring approach rather than working around it. A proper fix — even if larger in scope — is better than a fragile workaround.
11
- - **Trace the full chain**: Follow the error or issue through the entire call chain (caller -> callee -> dependencies) to understand why it happens, not just where it happens.
12
- - **When uncertain, ask**: If the root cause is unclear or the fix requires significant changes, discuss with the user before proceeding. Present findings and options rather than silently applying a quick fix.
13
-
14
- ## Pre-coding Checklist
15
-
16
- - Before creating new files: Glob/Read similar existing files to check structure and patterns
17
- - Before modifying functions/classes: Read the file to understand existing code style
18
- - When unsure about API/method usage: Check signatures in source code
19
- - **Always search the local codebase first.** Do not search the web or external docs until you have confirmed the answer is not in local code.
20
- - **If confidence is low, ask the user instead of writing code**
21
-
22
- ## Memory Policy
23
-
24
- - **Do NOT use auto memory** (`~/.claude/projects/.../memory/`). It is environment-specific and does not persist across machines.
25
- - All persistent knowledge belongs in `.claude/rules/` or project docs (committed to git).
@@ -1,52 +0,0 @@
1
- # Reference Guide
2
-
3
- Before starting work, **Read** the relevant reference files from `.claude/refs/`.
4
-
5
- ## Version Detection
6
-
7
- Determine the major version by the `version` field in `package.json`.
8
-
9
- - `12.x.x` → **v12** (< 13)
10
- - `13.x.x` → **v13** (>= 13)
11
-
12
- ## Common (all versions)
13
-
14
- | When | Read this file |
15
- | ------------------------------------------------ | ------------------------------------- |
16
- | Writing, modifying, or reviewing code | `.claude/refs/sd-code-conventions.md` |
17
- | Working with `.cache/` or Playwright screenshots | `.claude/refs/sd-directories.md` |
18
- | Using `@simplysm/*` package APIs | `.claude/refs/sd-simplysm-docs.md` |
19
- | Debugging, problem-solving, or planning approach | `.claude/refs/sd-workflow.md` |
20
- | Using `@simplysm/service-*` packages | `.claude/refs/sd-service.md` |
21
- | Migrating/porting code from another codebase | `.claude/refs/sd-migration.md` |
22
- | Debugging in a project that uses `@simplysm/*` as an external dependency (not the simplysm monorepo itself) | `.claude/refs/sd-library-issue.md` |
23
-
24
- ## v12 only (< 13)
25
-
26
- | When | Read this file |
27
- | -------------------------------------- | ---------------------------- |
28
- | Working on Angular / @simplysm/sd-angular | `.claude/refs/sd-angular.md` |
29
- | Using `@simplysm/sd-orm-*` packages | `.claude/refs/sd-orm-v12.md` |
30
-
31
- - v12 is **Angular** based (no SolidJS)
32
- - ORM uses **decorator** pattern (`@Table`, `@Column`)
33
- - Package names: use `sd-` prefix (`sd-core-common`, `sd-orm-common`, etc.)
34
- - Package manager: **yarn**
35
-
36
- ## v13 only (>= 13)
37
-
38
- | When | Read this file |
39
- | -------------------------------------------- | -------------------------- |
40
- | Working on SolidJS / @simplysm/solid / Tailwind | `.claude/refs/sd-solid.md` |
41
- | Using `@simplysm/orm-*` packages | `.claude/refs/sd-orm.md` |
42
-
43
- - v13 is **SolidJS** based (no Angular)
44
- - ORM uses **functional builder** pattern (`Table().columns().primaryKey()`)
45
- - Package names: no prefix (`core-common`, `orm-common`, etc.)
46
- - Package manager: **pnpm**
47
-
48
- ## Rules
49
-
50
- - Read the reference BEFORE starting the related work (not after)
51
- - You may read multiple references if the task spans multiple areas
52
- - If unsure whether a reference applies, read it — the cost of reading is low
@@ -1,296 +0,0 @@
1
- // @ts-check
2
- import fs from "fs";
3
- import os from "os";
4
- import path from "path";
5
- import { stdin } from "process";
6
-
7
- //#region Constants
8
-
9
- const STDIN_TIMEOUT_MS = 5000;
10
- const FETCH_TIMEOUT_MS = 3000;
11
- const CACHE_TTL_MS = 60_000; // 1 minutes
12
- const CACHE_PATH = path.join(os.homedir(), ".claude", "usage-api-cache.json");
13
-
14
- //#endregion
15
-
16
- //#region Stdin
17
-
18
- /** @returns {Promise<string>} */
19
- function readStdin() {
20
- return new Promise((resolve) => {
21
- let data = "";
22
-
23
- const cleanup = () => {
24
- stdin.removeAllListeners("data");
25
- stdin.removeAllListeners("end");
26
- stdin.removeAllListeners("error");
27
- };
28
-
29
- const timeout = setTimeout(() => {
30
- cleanup();
31
- resolve("");
32
- }, STDIN_TIMEOUT_MS);
33
-
34
- stdin.setEncoding("utf8");
35
- stdin.on("data", (chunk) => {
36
- data += chunk;
37
- });
38
- stdin.on("end", () => {
39
- clearTimeout(timeout);
40
- cleanup();
41
- resolve(data);
42
- });
43
- stdin.on("error", () => {
44
- clearTimeout(timeout);
45
- cleanup();
46
- resolve("");
47
- });
48
- });
49
- }
50
-
51
- //#endregion
52
-
53
- //#region OAuth
54
-
55
- /** @returns {string | undefined} */
56
- function getOAuthToken() {
57
- try {
58
- const configDir = process.env.CLAUDE_CONFIG_DIR ?? path.join(os.homedir(), ".claude");
59
- const credentialsPath = path.join(configDir, ".credentials.json");
60
- if (!fs.existsSync(credentialsPath)) {
61
- return undefined;
62
- }
63
-
64
- const content = fs.readFileSync(credentialsPath, "utf-8");
65
- const credentials = JSON.parse(content);
66
- const oauth = credentials.claudeAiOauth;
67
-
68
- // Check token expiration
69
- if (oauth?.expiresAt != null && Date.now() > oauth.expiresAt) {
70
- return undefined;
71
- }
72
-
73
- return oauth?.accessToken;
74
- } catch {
75
- return undefined;
76
- }
77
- }
78
-
79
- /**
80
- * Read cached usage data from local file.
81
- * @returns {{ data: object, timestamp: number } | undefined}
82
- */
83
- function readCache() {
84
- try {
85
- if (!fs.existsSync(CACHE_PATH)) return undefined;
86
- const content = fs.readFileSync(CACHE_PATH, "utf-8");
87
- return JSON.parse(content);
88
- } catch {
89
- return undefined;
90
- }
91
- }
92
-
93
- /**
94
- * Write usage data to local cache file.
95
- * @param {object} data
96
- */
97
- function writeCache(data) {
98
- try {
99
- fs.writeFileSync(CACHE_PATH, JSON.stringify({ data, timestamp: Date.now() }), "utf-8");
100
- } catch {
101
- // ignore write errors
102
- }
103
- }
104
-
105
- /**
106
- * Fetch Anthropic API usage information using OAuth token, with 1-minute cache.
107
- * @param {string} token - OAuth access token
108
- * @param {string} version - Claude Code version
109
- * @returns {Promise<{
110
- * seven_day?: {utilization?: number, resets_at?: string},
111
- * daily?: {utilization?: number, resets_at?: string},
112
- * five_hour?: {utilization?: number, resets_at?: string},
113
- * extra_usage?: {is_enabled?: boolean, monthly_limit?: number | null, used_credits?: number}
114
- * } | undefined>}
115
- */
116
- async function fetchUsage(token, version) {
117
- // Return cached data if still valid
118
- const cache = readCache();
119
- if (cache != null && Date.now() - cache.timestamp < CACHE_TTL_MS) {
120
- return cache.data;
121
- }
122
-
123
- try {
124
- const controller = new AbortController();
125
- const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
126
-
127
- const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
128
- headers: {
129
- "Authorization": `Bearer ${token}`,
130
- "Accept": "application/json",
131
- 'anthropic-beta': 'oauth-2025-04-20'
132
- },
133
- signal: controller.signal,
134
- });
135
-
136
- clearTimeout(timeout);
137
-
138
- if (!response.ok) {
139
- // API failed — update timestamp to prevent retry for TTL duration
140
- writeCache(cache?.data ?? {});
141
- return undefined;
142
- }
143
-
144
- const data = await response.json();
145
-
146
- if (data == null || typeof data !== "object") {
147
- writeCache(cache?.data ?? {});
148
- return undefined;
149
- }
150
-
151
- writeCache(data);
152
- return data;
153
- } catch {
154
- // Network error — update timestamp to prevent retry for TTL duration
155
- writeCache(cache?.data ?? {});
156
- return undefined;
157
- }
158
- }
159
-
160
- //#endregion
161
-
162
- //#region Formatting
163
-
164
- /**
165
- * @param {number | undefined} value
166
- * @returns {string}
167
- */
168
- function formatPercent(value) {
169
- if (value == null) return "?";
170
- return Math.round(value).toString();
171
- }
172
-
173
- /**
174
- * @param {string | undefined} isoDate
175
- * @returns {string}
176
- */
177
- function formatTimeRemaining(isoDate) {
178
- if (isoDate == null) return "";
179
- try {
180
- const resetTime = new Date(isoDate).getTime();
181
- if (Number.isNaN(resetTime)) return "";
182
-
183
- const now = Date.now();
184
- const diffMs = resetTime - now;
185
-
186
- if (diffMs <= 0) return "";
187
-
188
- const diffMinutes = Math.floor(diffMs / (1000 * 60));
189
- const diffHours = Math.floor(diffMinutes / 60);
190
- const days = Math.floor(diffHours / 24);
191
- const hours = diffHours % 24;
192
- const minutes = diffMinutes % 60;
193
-
194
- if (days > 0) {
195
- return `${days}d${hours}h`;
196
- }
197
- if (hours > 0) {
198
- return `${hours}h${minutes}m`;
199
- }
200
- return `${minutes}m`;
201
- } catch {
202
- return "";
203
- }
204
- }
205
-
206
- //#endregion
207
-
208
- //#region Main
209
-
210
- /**
211
- * JSON information received from stdin
212
- * @typedef {object} StdinInput
213
- * @property {{display_name?: string}} [model] - Model information
214
- * @property {{context_window_size?: number, remaining_context_tokens?: number, current_usage?: {input_tokens?: number, output_tokens?: number, cache_creation_input_tokens?: number, cache_read_input_tokens?: number}}} [context_window] - Context window information
215
- * @property {{tokens_used?: number, tokens_limit?: number}} [weekly_token_usage] - Weekly token usage (fallback)
216
- * @property {string} [version] - Claude Code version
217
- */
218
-
219
- /**
220
- * Output Claude Code status bar information.
221
- * Combine JSON received from stdin with OAuth API response to output
222
- * model name, context usage, and daily/weekly usage.
223
- */
224
- async function main() {
225
- const inputStr = await readStdin();
226
- /** @type {StdinInput} */
227
- let input = {};
228
-
229
- if (inputStr !== "") {
230
- try {
231
- input = JSON.parse(inputStr);
232
- } catch {
233
- // Use empty object if JSON parsing fails
234
- }
235
- }
236
-
237
- // Basic information
238
- const modelName = input.model?.display_name ?? "Unknown";
239
- const contextSize = input.context_window?.context_window_size ?? 0;
240
- const usage = input.context_window?.current_usage;
241
- const contextUsed =
242
- (usage?.input_tokens ?? 0) +
243
- (usage?.output_tokens ?? 0) +
244
- (usage?.cache_creation_input_tokens ?? 0) +
245
- (usage?.cache_read_input_tokens ?? 0);
246
- const contextPercent = contextSize > 0 ? Math.round((contextUsed / contextSize) * 100) : 0;
247
-
248
- // Try fetching usage with OAuth token
249
- const token = getOAuthToken();
250
- let dailyPercent = "?";
251
- let dailyResetTime = "";
252
- let weekPercent = "?";
253
- let weekResetDay = "";
254
- let extraUsage = "";
255
-
256
- if (token != null) {
257
- const usageResponse = await fetchUsage(token, input.version ?? "unknown");
258
- if (usageResponse != null) {
259
- // Use daily or five_hour
260
- const dailyData = usageResponse.daily ?? usageResponse.five_hour;
261
- dailyPercent = formatPercent(dailyData?.utilization);
262
- dailyResetTime = formatTimeRemaining(dailyData?.resets_at);
263
- weekPercent = formatPercent(usageResponse.seven_day?.utilization);
264
- weekResetDay = formatTimeRemaining(usageResponse.seven_day?.resets_at);
265
-
266
- // Extra usage
267
- if (usageResponse.extra_usage?.is_enabled && usageResponse.extra_usage.used_credits != null) {
268
- extraUsage = `$${(usageResponse.extra_usage.used_credits / 100).toFixed(2)}`;
269
- }
270
- }
271
- }
272
-
273
- // Fallback: weekly_token_usage
274
- if (weekPercent === "?" && input.weekly_token_usage != null) {
275
- const used = input.weekly_token_usage.tokens_used ?? 0;
276
- const limit = input.weekly_token_usage.tokens_limit ?? 0;
277
- if (limit > 0) {
278
- weekPercent = Math.round((used / limit) * 100).toString();
279
- }
280
- }
281
-
282
- // Folder name + git branch
283
- const cwd = input.cwd ?? process.cwd();
284
- const folderName = path.basename(cwd);
285
-
286
- // Output
287
- const dailyStr = dailyResetTime ? `${dailyPercent}%(${dailyResetTime})` : `${dailyPercent}%`;
288
- const weekStr = weekResetDay ? `${weekPercent}%(${weekResetDay})` : `${weekPercent}%`;
289
- const parts = [folderName, modelName, `${contextPercent}%`, dailyStr, weekStr];
290
- if (extraUsage) parts.push(extraUsage);
291
- console.log(parts.join(" │ "));
292
- }
293
-
294
- void main();
295
-
296
- //#endregion