@simplysm/sd-claude 13.0.82 → 13.0.84
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -0
- package/claude/rules/sd-claude-rules.md +10 -6
- package/claude/rules/sd-simplysm-usage.md +5 -5
- package/claude/skills/sd-api-review/SKILL.md +51 -51
- package/claude/skills/sd-check/SKILL.md +40 -40
- package/claude/skills/sd-commit/SKILL.md +31 -31
- package/claude/skills/sd-debug/SKILL.md +77 -68
- package/claude/skills/sd-document/SKILL.md +56 -56
- package/claude/skills/sd-email-analyze/SKILL.md +22 -22
- package/claude/skills/sd-init/SKILL.md +63 -63
- package/claude/skills/sd-plan/SKILL.md +62 -61
- package/claude/skills/sd-readme/SKILL.md +59 -59
- package/claude/skills/sd-review/SKILL.md +35 -35
- package/claude/skills/sd-simplify/SKILL.md +30 -30
- package/package.json +3 -19
- package/scripts/postinstall.mjs +126 -9
- package/scripts/sd-entries.mjs +33 -0
- package/scripts/sync-claude-assets.mjs +4 -21
- package/dist/commands/auth-add.d.ts +0 -2
- package/dist/commands/auth-add.d.ts.map +0 -1
- package/dist/commands/auth-add.js +0 -32
- package/dist/commands/auth-add.js.map +0 -6
- package/dist/commands/auth-list.d.ts +0 -2
- package/dist/commands/auth-list.d.ts.map +0 -1
- package/dist/commands/auth-list.js +0 -95
- package/dist/commands/auth-list.js.map +0 -6
- package/dist/commands/auth-remove.d.ts +0 -2
- package/dist/commands/auth-remove.d.ts.map +0 -1
- package/dist/commands/auth-remove.js +0 -22
- package/dist/commands/auth-remove.js.map +0 -6
- package/dist/commands/auth-use.d.ts +0 -2
- package/dist/commands/auth-use.d.ts.map +0 -1
- package/dist/commands/auth-use.js +0 -33
- package/dist/commands/auth-use.js.map +0 -6
- package/dist/commands/auth-utils.d.ts +0 -11
- package/dist/commands/auth-utils.d.ts.map +0 -1
- package/dist/commands/auth-utils.js +0 -57
- package/dist/commands/auth-utils.js.map +0 -6
- package/dist/commands/install.d.ts +0 -2
- package/dist/commands/install.d.ts.map +0 -1
- package/dist/commands/install.js +0 -127
- package/dist/commands/install.js.map +0 -6
- package/dist/index.d.ts +0 -7
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js +0 -7
- package/dist/index.js.map +0 -6
- package/dist/sd-claude.d.ts +0 -3
- package/dist/sd-claude.d.ts.map +0 -1
- package/dist/sd-claude.js +0 -78
- package/dist/sd-claude.js.map +0 -6
- package/src/commands/auth-add.ts +0 -36
- package/src/commands/auth-list.ts +0 -130
- package/src/commands/auth-remove.ts +0 -26
- package/src/commands/auth-use.ts +0 -53
- package/src/commands/auth-utils.ts +0 -65
- package/src/commands/install.ts +0 -183
- package/src/index.ts +0 -7
- package/src/sd-claude.ts +0 -98
- package/tests/auth-add.spec.ts +0 -74
- package/tests/auth-list.spec.ts +0 -198
- package/tests/auth-remove.spec.ts +0 -74
- package/tests/auth-use.spec.ts +0 -153
- package/tests/auth-utils.spec.ts +0 -173
|
@@ -1,65 +1,65 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sd-review
|
|
3
|
-
description:
|
|
3
|
+
description: Used when requesting "bug review", "sd-review", "code review", "find bugs", etc. Analyzes code at the specified path for potential bugs, then creates a plan and applies fixes.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# SD Review —
|
|
6
|
+
# SD Review — Potential Bug Detection
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Reads the code at the specified path, analyzes it for potential bugs, then creates and executes a plan via the `/sd-plan` process.
|
|
9
9
|
|
|
10
|
-
ARGUMENTS:
|
|
10
|
+
ARGUMENTS: Target path (required). Specify any path within the repo.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
## Step 1:
|
|
14
|
+
## Step 1: Validate Arguments
|
|
15
15
|
|
|
16
|
-
1.
|
|
17
|
-
2.
|
|
16
|
+
1. Extract the target path from ARGUMENTS.
|
|
17
|
+
2. If no path is provided, display "Please specify a target path. Example: `/sd-review packages/my-pkg`" and stop.
|
|
18
18
|
|
|
19
|
-
## Step 2:
|
|
19
|
+
## Step 2: Bug Analysis (Do Not Modify Code)
|
|
20
20
|
|
|
21
|
-
|
|
22
|
-
|
|
21
|
+
Read the code at the target path and search for potential bugs from the following 5 perspectives.
|
|
22
|
+
Do not modify the code under any circumstances. Only compile and output a list of findings.
|
|
23
23
|
|
|
24
|
-
|
|
25
|
-
1.
|
|
26
|
-
2. **Null/Undefined
|
|
27
|
-
3.
|
|
28
|
-
4.
|
|
29
|
-
5.
|
|
24
|
+
**Analysis Perspectives:**
|
|
25
|
+
1. **Logic/Correctness** — Incorrect conditions, off-by-one errors, wrong operators, unintended branching
|
|
26
|
+
2. **Null/Undefined Safety** — Missing null checks, unused optional chaining, misuse of type assertions
|
|
27
|
+
3. **Error Handling** — Swallowed errors, missing catch blocks, improper error propagation
|
|
28
|
+
4. **Edge Cases** — Empty arrays/strings, boundary values, concurrency/race conditions, missing await
|
|
29
|
+
5. **Resource Management** — Unclosed connections, event listener leaks, memory leak patterns
|
|
30
30
|
|
|
31
|
-
|
|
31
|
+
Write each finding in the following format:
|
|
32
32
|
```
|
|
33
|
-
-
|
|
33
|
+
- **filepath:line** — Problem description — Suggested fix
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
-
|
|
36
|
+
If no findings are discovered, display "No potential bugs were found." and stop.
|
|
37
37
|
|
|
38
|
-
## Step 3: sd-plan
|
|
38
|
+
## Step 3: Create Plan via sd-plan
|
|
39
39
|
|
|
40
|
-
|
|
40
|
+
Using the list of findings from Step 2 as the task description, invoke `sd-plan` via the Skill tool. Pass the following as args:
|
|
41
41
|
|
|
42
42
|
```
|
|
43
|
-
|
|
44
|
-
|
|
43
|
+
The following are potential bug fixes **analyzed and suggested by the LLM**.
|
|
44
|
+
Since these fixes were not explicitly requested by the user, treat them as uncertain.
|
|
45
45
|
|
|
46
|
-
##
|
|
47
|
-
|
|
46
|
+
## Target
|
|
47
|
+
<target path>
|
|
48
48
|
|
|
49
|
-
## LLM
|
|
50
|
-
|
|
49
|
+
## LLM-Suggested Fixes
|
|
50
|
+
When asking the user about uncertain fixes, **always present** the following information first so the user can understand the context.
|
|
51
51
|
|
|
52
52
|
```
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
53
|
+
Fix:
|
|
54
|
+
- Filepath:line:
|
|
55
|
+
- Problem description:
|
|
56
|
+
- Current code: (excerpt of the relevant code)
|
|
57
|
+
- Suggested fix:
|
|
58
58
|
```
|
|
59
59
|
|
|
60
|
-
<
|
|
60
|
+
<Full list of findings from Step 2>
|
|
61
61
|
```
|
|
62
62
|
|
|
63
|
-
## Step 4:
|
|
63
|
+
## Step 4: Execute Plan
|
|
64
64
|
|
|
65
|
-
sd-plan
|
|
65
|
+
Once sd-plan completes and produces a finalized plan, apply the code modifications according to that plan.
|
|
@@ -1,59 +1,59 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: sd-simplify
|
|
3
|
-
description: "
|
|
3
|
+
description: Used when requesting "code simplification", "simplify", "sd-simplify", "code refinement", etc. Analyzes code at a specified path, creates a plan, and applies modifications.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
|
-
# SD Simplify —
|
|
6
|
+
# SD Simplify — Path-Specific Code Simplification
|
|
7
7
|
|
|
8
|
-
|
|
8
|
+
Analyzes code at a specified path using the built-in `/simplify`, then creates and executes a plan via the `/sd-plan` process.
|
|
9
9
|
|
|
10
|
-
ARGUMENTS:
|
|
10
|
+
ARGUMENTS: Target path (required). Specify any path within the repository.
|
|
11
11
|
|
|
12
12
|
---
|
|
13
13
|
|
|
14
|
-
## Step 1:
|
|
14
|
+
## Step 1: Validate Arguments
|
|
15
15
|
|
|
16
|
-
1.
|
|
17
|
-
2.
|
|
16
|
+
1. Extract the target path from ARGUMENTS.
|
|
17
|
+
2. If no path is provided, display the message "Please specify a target path. Example: `/sd-simplify packages/my-pkg`" and stop.
|
|
18
18
|
|
|
19
|
-
## Step 2: simplify
|
|
19
|
+
## Step 2: simplify Analysis (Do Not Modify)
|
|
20
20
|
|
|
21
|
-
|
|
21
|
+
Invoke `simplify` using the Skill tool. Pass the following instructions as args:
|
|
22
22
|
|
|
23
23
|
```
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
-
|
|
24
|
+
Review the current codebase at the <target path> path. (Not recently changed code)
|
|
25
|
+
Do NOT modify any code. Only compile and output a list of items to fix.
|
|
26
|
+
Write each item in the following format:
|
|
27
|
+
- **file-path:line** — Problem description — Suggested improvement
|
|
28
28
|
```
|
|
29
29
|
|
|
30
|
-
|
|
30
|
+
Replace the `<target path>` placeholder with the actual path extracted in Step 1.
|
|
31
31
|
|
|
32
|
-
## Step 3: sd-plan
|
|
32
|
+
## Step 3: Create a Plan with sd-plan
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
Using the list of items to fix from Step 2 as the task description, invoke `sd-plan` using the Skill tool. Pass the following as args:
|
|
35
35
|
|
|
36
36
|
```
|
|
37
|
-
|
|
38
|
-
|
|
37
|
+
The following are code improvement suggestions **proposed by an LLM analysis**.
|
|
38
|
+
Since these suggestions were not explicitly requested by the user, treat them as unclear.
|
|
39
39
|
|
|
40
|
-
##
|
|
41
|
-
|
|
40
|
+
## Target
|
|
41
|
+
<target path>
|
|
42
42
|
|
|
43
|
-
## LLM
|
|
44
|
-
|
|
43
|
+
## LLM-Suggested Improvements
|
|
44
|
+
When asking the user about unclear suggestions, **always present** the following details first so the user can understand the context.
|
|
45
45
|
|
|
46
46
|
```
|
|
47
|
-
|
|
48
|
-
-
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
47
|
+
Suggestion:
|
|
48
|
+
- File path:line:
|
|
49
|
+
- Problem description:
|
|
50
|
+
- Current code: (excerpt of the relevant code)
|
|
51
|
+
- Suggested improvement:
|
|
52
52
|
```
|
|
53
53
|
|
|
54
|
-
<
|
|
54
|
+
<Full list of items to fix from Step 2>
|
|
55
55
|
```
|
|
56
56
|
|
|
57
|
-
## Step 4:
|
|
57
|
+
## Step 4: Execute the Plan
|
|
58
58
|
|
|
59
|
-
sd-plan
|
|
59
|
+
Once sd-plan completes and a finalized plan is produced, modify the code according to that plan.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@simplysm/sd-claude",
|
|
3
|
-
"version": "13.0.
|
|
4
|
-
"description": "Simplysm Claude Code
|
|
3
|
+
"version": "13.0.84",
|
|
4
|
+
"description": "Simplysm Claude Code asset installer",
|
|
5
5
|
"author": "simplysm",
|
|
6
6
|
"license": "Apache-2.0",
|
|
7
7
|
"repository": {
|
|
@@ -10,27 +10,11 @@
|
|
|
10
10
|
"directory": "packages/sd-claude"
|
|
11
11
|
},
|
|
12
12
|
"type": "module",
|
|
13
|
-
"main": "./dist/index.js",
|
|
14
|
-
"types": "./dist/index.d.ts",
|
|
15
13
|
"files": [
|
|
16
|
-
"dist",
|
|
17
|
-
"src",
|
|
18
|
-
"tests",
|
|
19
14
|
"scripts",
|
|
20
|
-
"claude"
|
|
21
|
-
"docs"
|
|
15
|
+
"claude"
|
|
22
16
|
],
|
|
23
|
-
"sideEffects": false,
|
|
24
|
-
"dependencies": {
|
|
25
|
-
"yargs": "^18.0.0"
|
|
26
|
-
},
|
|
27
|
-
"devDependencies": {
|
|
28
|
-
"@types/yargs": "^17.0.35"
|
|
29
|
-
},
|
|
30
17
|
"scripts": {
|
|
31
18
|
"postinstall": "node scripts/postinstall.mjs"
|
|
32
|
-
},
|
|
33
|
-
"bin": {
|
|
34
|
-
"sd-claude": "./dist/sd-claude.js"
|
|
35
19
|
}
|
|
36
20
|
}
|
package/scripts/postinstall.mjs
CHANGED
|
@@ -1,15 +1,132 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
2
|
+
* Installs Claude Code assets to the project's .claude/ directory.
|
|
3
|
+
* postinstall hook — 실패해도 pnpm install을 차단하지 않는다.
|
|
4
4
|
*/
|
|
5
|
-
import
|
|
6
|
-
import
|
|
5
|
+
import fs from "fs";
|
|
6
|
+
import path from "path";
|
|
7
7
|
import { fileURLToPath } from "url";
|
|
8
|
-
import {
|
|
8
|
+
import { collectSdEntries, forEachSdEntry } from "./sd-entries.mjs";
|
|
9
9
|
|
|
10
|
-
|
|
11
|
-
const
|
|
10
|
+
try {
|
|
11
|
+
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
12
|
+
// scripts/ → package root
|
|
13
|
+
const pkgRoot = path.resolve(__dirname, "..");
|
|
14
|
+
const sourceDir = path.join(pkgRoot, "claude");
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
const projectRoot = findProjectRoot(__dirname);
|
|
17
|
+
if (projectRoot == null) {
|
|
18
|
+
console.log("[@simplysm/sd-claude] Could not find project root, skipping installation.");
|
|
19
|
+
process.exit(0);
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Skip execution if this is the simplysm monorepo with the same major version
|
|
23
|
+
if (isSimplysmMonorepoSameMajor(projectRoot, pkgRoot)) {
|
|
24
|
+
process.exit(0);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// Skip if the source directory doesn't exist (claude/ may not exist in monorepo dev environment)
|
|
28
|
+
if (!fs.existsSync(sourceDir)) {
|
|
29
|
+
process.exit(0);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const sourceEntries = collectSdEntries(sourceDir);
|
|
33
|
+
if (sourceEntries.length === 0) {
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const targetDir = path.join(projectRoot, ".claude");
|
|
38
|
+
|
|
39
|
+
cleanSdEntries(targetDir);
|
|
40
|
+
copySdEntries(sourceDir, targetDir, sourceEntries);
|
|
41
|
+
setupSettings(targetDir);
|
|
42
|
+
|
|
43
|
+
console.log(`[@simplysm/sd-claude] Installed ${sourceEntries.length} sd-* entries.`);
|
|
44
|
+
} catch (err) {
|
|
45
|
+
// Ignore errors to prevent postinstall failure from blocking the entire pnpm install
|
|
46
|
+
console.warn("[@simplysm/sd-claude] postinstall warning:", err.message);
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/** Finds the project root from INIT_CWD or node_modules path. */
|
|
50
|
+
function findProjectRoot(dirname) {
|
|
51
|
+
if (process.env["INIT_CWD"] != null) {
|
|
52
|
+
return process.env["INIT_CWD"];
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const sep = path.sep;
|
|
56
|
+
const marker = sep + "node_modules" + sep;
|
|
57
|
+
const idx = dirname.indexOf(marker);
|
|
58
|
+
return idx !== -1 ? dirname.substring(0, idx) : undefined;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Checks if this is the simplysm monorepo with the same major version. */
|
|
62
|
+
function isSimplysmMonorepoSameMajor(projectRoot, pkgRoot) {
|
|
63
|
+
const projectPkgPath = path.join(projectRoot, "package.json");
|
|
64
|
+
if (!fs.existsSync(projectPkgPath)) return false;
|
|
65
|
+
|
|
66
|
+
const projectPkg = JSON.parse(fs.readFileSync(projectPkgPath, "utf-8"));
|
|
67
|
+
if (projectPkg.name !== "simplysm") return false;
|
|
68
|
+
|
|
69
|
+
const sdClaudePkgPath = path.join(pkgRoot, "package.json");
|
|
70
|
+
if (!fs.existsSync(sdClaudePkgPath)) return false;
|
|
71
|
+
|
|
72
|
+
const sdClaudePkg = JSON.parse(fs.readFileSync(sdClaudePkgPath, "utf-8"));
|
|
73
|
+
|
|
74
|
+
const projectMajor = projectPkg.version?.split(".")[0];
|
|
75
|
+
const sdClaudeMajor = sdClaudePkg.version?.split(".")[0];
|
|
76
|
+
return projectMajor != null && projectMajor === sdClaudeMajor;
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/** Removes existing sd-* entries. */
|
|
80
|
+
function cleanSdEntries(targetDir) {
|
|
81
|
+
if (!fs.existsSync(targetDir)) return;
|
|
82
|
+
forEachSdEntry(targetDir, (rel) => {
|
|
83
|
+
fs.rmSync(path.join(targetDir, rel), { recursive: true });
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
/** Copies sd-* entries. */
|
|
88
|
+
function copySdEntries(sourceDir, targetDir, entries) {
|
|
89
|
+
fs.mkdirSync(targetDir, { recursive: true });
|
|
90
|
+
for (const entry of entries) {
|
|
91
|
+
const src = path.join(sourceDir, entry);
|
|
92
|
+
const dest = path.join(targetDir, entry);
|
|
93
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
94
|
+
fs.cpSync(src, dest, { recursive: true });
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/** Ensures statusLine and SessionStart hooks are configured in settings.json. */
|
|
99
|
+
function setupSettings(targetDir) {
|
|
100
|
+
const settingsPath = path.join(targetDir, "settings.json");
|
|
101
|
+
|
|
102
|
+
let settings = {};
|
|
103
|
+
if (fs.existsSync(settingsPath)) {
|
|
104
|
+
settings = JSON.parse(fs.readFileSync(settingsPath, "utf-8"));
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// statusLine: always overwrite
|
|
108
|
+
settings["statusLine"] = { type: "command", command: "python .claude/sd-statusline.py" };
|
|
109
|
+
|
|
110
|
+
// SessionStart: ensure sd-session-start hook exists with correct config
|
|
111
|
+
const sdSessionEntry = {
|
|
112
|
+
matcher: "startup|resume|clear|compact",
|
|
113
|
+
hooks: [{ type: "command", command: "bash .claude/sd-session-start.sh" }],
|
|
114
|
+
};
|
|
115
|
+
|
|
116
|
+
const sessionStart = settings["SessionStart"];
|
|
117
|
+
|
|
118
|
+
if (sessionStart == null) {
|
|
119
|
+
settings["SessionStart"] = [sdSessionEntry];
|
|
120
|
+
} else {
|
|
121
|
+
const idx = sessionStart.findIndex((entry) =>
|
|
122
|
+
entry.hooks?.some((hook) => hook.command.includes("sd-session-start")),
|
|
123
|
+
);
|
|
124
|
+
if (idx >= 0) {
|
|
125
|
+
sessionStart[idx] = sdSessionEntry;
|
|
126
|
+
} else {
|
|
127
|
+
sessionStart.push(sdSessionEntry);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + "\n");
|
|
15
132
|
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Iterates over sd-* entries in a directory (root level + one level of subdirectories).
|
|
6
|
+
* @param {string} dir - Base directory to scan
|
|
7
|
+
* @param {(relativePath: string) => void} callback - Called with each sd-* entry's relative path
|
|
8
|
+
*/
|
|
9
|
+
export function forEachSdEntry(dir, callback) {
|
|
10
|
+
for (const dirent of fs.readdirSync(dir, { withFileTypes: true })) {
|
|
11
|
+
if (dirent.name.startsWith("sd-")) {
|
|
12
|
+
callback(dirent.name);
|
|
13
|
+
} else if (dirent.isDirectory()) {
|
|
14
|
+
const subPath = path.join(dir, dirent.name);
|
|
15
|
+
for (const name of fs.readdirSync(subPath)) {
|
|
16
|
+
if (name.startsWith("sd-")) {
|
|
17
|
+
callback(path.join(dirent.name, name));
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Collects all sd-* entry relative paths from a directory.
|
|
26
|
+
* @param {string} dir - Base directory to scan
|
|
27
|
+
* @returns {string[]} Array of relative paths
|
|
28
|
+
*/
|
|
29
|
+
export function collectSdEntries(dir) {
|
|
30
|
+
const entries = [];
|
|
31
|
+
forEachSdEntry(dir, (rel) => entries.push(rel));
|
|
32
|
+
return entries;
|
|
33
|
+
}
|
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
*/
|
|
5
5
|
import fs from "fs";
|
|
6
6
|
import path from "path";
|
|
7
|
+
import { collectSdEntries } from "./sd-entries.mjs";
|
|
7
8
|
|
|
8
9
|
const cliDir = process.cwd();
|
|
9
10
|
const projectRoot = path.resolve(cliDir, "../..");
|
|
@@ -15,31 +16,13 @@ if (fs.existsSync(targetDir)) {
|
|
|
15
16
|
fs.rmSync(targetDir, { recursive: true });
|
|
16
17
|
}
|
|
17
18
|
|
|
18
|
-
// sd-* 항목 탐색
|
|
19
|
-
const allEntries =
|
|
19
|
+
// sd-* 항목 탐색 및 복사
|
|
20
|
+
const allEntries = collectSdEntries(claudeDir);
|
|
20
21
|
|
|
21
|
-
// 루트 레벨: sd-*
|
|
22
|
-
for (const name of fs.readdirSync(claudeDir)) {
|
|
23
|
-
if (name.startsWith("sd-")) {
|
|
24
|
-
allEntries.push(name);
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
// 서브 디렉토리: */sd-*
|
|
29
|
-
for (const dirent of fs.readdirSync(claudeDir, { withFileTypes: true })) {
|
|
30
|
-
if (!dirent.isDirectory() || dirent.name.startsWith("sd-")) continue;
|
|
31
|
-
const subPath = path.join(claudeDir, dirent.name);
|
|
32
|
-
for (const name of fs.readdirSync(subPath)) {
|
|
33
|
-
if (name.startsWith("sd-")) {
|
|
34
|
-
allEntries.push(path.join(dirent.name, name));
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
// 복사
|
|
40
22
|
for (const entry of allEntries) {
|
|
41
23
|
const src = path.join(claudeDir, entry);
|
|
42
24
|
const dest = path.join(targetDir, entry);
|
|
25
|
+
fs.mkdirSync(path.dirname(dest), { recursive: true });
|
|
43
26
|
fs.cpSync(src, dest, { recursive: true });
|
|
44
27
|
}
|
|
45
28
|
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-add.d.ts","sourceRoot":"","sources":["../../src/commands/auth-add.ts"],"names":[],"mappings":"AAUA,wBAAgB,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAyB/D"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import {
|
|
4
|
-
validateName,
|
|
5
|
-
profileExists,
|
|
6
|
-
getProfileDir,
|
|
7
|
-
readCurrentAuth,
|
|
8
|
-
readCurrentCredentials
|
|
9
|
-
} from "./auth-utils.js";
|
|
10
|
-
function runAuthAdd(name, homeDir) {
|
|
11
|
-
validateName(name);
|
|
12
|
-
if (profileExists(name, homeDir)) {
|
|
13
|
-
throw new Error(
|
|
14
|
-
`Profile '${name}' already exists. Remove it first with: sd-claude auth remove ${name}`
|
|
15
|
-
);
|
|
16
|
-
}
|
|
17
|
-
const { oauthAccount, userID } = readCurrentAuth(homeDir);
|
|
18
|
-
const credentials = readCurrentCredentials(homeDir);
|
|
19
|
-
const profileDir = getProfileDir(name, homeDir);
|
|
20
|
-
fs.mkdirSync(profileDir, { recursive: true });
|
|
21
|
-
fs.writeFileSync(
|
|
22
|
-
path.join(profileDir, "auth.json"),
|
|
23
|
-
JSON.stringify({ oauthAccount, userID }, null, 2)
|
|
24
|
-
);
|
|
25
|
-
fs.writeFileSync(path.join(profileDir, "credentials.json"), JSON.stringify(credentials, null, 2));
|
|
26
|
-
const email = oauthAccount["emailAddress"];
|
|
27
|
-
console.log(`Saved profile '${name}' (${email ?? userID})`);
|
|
28
|
-
}
|
|
29
|
-
export {
|
|
30
|
-
runAuthAdd
|
|
31
|
-
};
|
|
32
|
-
//# sourceMappingURL=auth-add.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/commands/auth-add.ts"],
|
|
4
|
-
"mappings": "AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB;AAAA,EACE;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,OACK;AAEA,SAAS,WAAW,MAAc,SAAwB;AAC/D,eAAa,IAAI;AAEjB,MAAI,cAAc,MAAM,OAAO,GAAG;AAChC,UAAM,IAAI;AAAA,MACR,YAAY,IAAI,iEAAiE,IAAI;AAAA,IACvF;AAAA,EACF;AAEA,QAAM,EAAE,cAAc,OAAO,IAAI,gBAAgB,OAAO;AACxD,QAAM,cAAc,uBAAuB,OAAO;AAElD,QAAM,aAAa,cAAc,MAAM,OAAO;AAC9C,KAAG,UAAU,YAAY,EAAE,WAAW,KAAK,CAAC;AAE5C,KAAG;AAAA,IACD,KAAK,KAAK,YAAY,WAAW;AAAA,IACjC,KAAK,UAAU,EAAE,cAAc,OAAO,GAAG,MAAM,CAAC;AAAA,EAClD;AAEA,KAAG,cAAc,KAAK,KAAK,YAAY,kBAAkB,GAAG,KAAK,UAAU,aAAa,MAAM,CAAC,CAAC;AAEhG,QAAM,QAAQ,aAAa,cAAc;AAEzC,UAAQ,IAAI,kBAAkB,IAAI,MAAM,SAAS,MAAM,GAAG;AAC5D;",
|
|
5
|
-
"names": []
|
|
6
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-list.d.ts","sourceRoot":"","sources":["../../src/commands/auth-list.ts"],"names":[],"mappings":"AAwEA,wBAAsB,WAAW,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAyDjE"}
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import fs from "fs";
|
|
2
|
-
import path from "path";
|
|
3
|
-
import { listProfiles, getCurrentUserID, getProfileDir } from "./auth-utils.js";
|
|
4
|
-
const FETCH_TIMEOUT_MS = 5e3;
|
|
5
|
-
async function fetchUsage(accessToken) {
|
|
6
|
-
try {
|
|
7
|
-
const controller = new AbortController();
|
|
8
|
-
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
9
|
-
const response = await fetch("https://api.anthropic.com/api/oauth/usage", {
|
|
10
|
-
headers: {
|
|
11
|
-
Authorization: `Bearer ${accessToken}`,
|
|
12
|
-
"anthropic-beta": "oauth-2025-04-20"
|
|
13
|
-
},
|
|
14
|
-
signal: controller.signal
|
|
15
|
-
});
|
|
16
|
-
clearTimeout(timeout);
|
|
17
|
-
if (!response.ok) {
|
|
18
|
-
return void 0;
|
|
19
|
-
}
|
|
20
|
-
return await response.json();
|
|
21
|
-
} catch {
|
|
22
|
-
return void 0;
|
|
23
|
-
}
|
|
24
|
-
}
|
|
25
|
-
function formatTimeRemaining(isoDate) {
|
|
26
|
-
if (isoDate == null) return "";
|
|
27
|
-
try {
|
|
28
|
-
const resetTime = new Date(isoDate).getTime();
|
|
29
|
-
if (Number.isNaN(resetTime)) return "";
|
|
30
|
-
const diffMs = resetTime - Date.now();
|
|
31
|
-
if (diffMs <= 0) return "";
|
|
32
|
-
const diffMinutes = Math.floor(diffMs / (1e3 * 60));
|
|
33
|
-
const diffHours = Math.floor(diffMinutes / 60);
|
|
34
|
-
const days = Math.floor(diffHours / 24);
|
|
35
|
-
const hours = diffHours % 24;
|
|
36
|
-
const minutes = diffMinutes % 60;
|
|
37
|
-
if (days > 0) return `${String(days)}d${String(hours)}h`;
|
|
38
|
-
if (hours > 0) return `${String(hours)}h${String(minutes)}m`;
|
|
39
|
-
return `${String(minutes)}m`;
|
|
40
|
-
} catch {
|
|
41
|
-
return "";
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
function formatUsage(label, data) {
|
|
45
|
-
if (data == null) return `${label}: ?`;
|
|
46
|
-
const pct = data.utilization != null ? `${String(Math.round(data.utilization))}%` : "?";
|
|
47
|
-
const remaining = formatTimeRemaining(data.resets_at);
|
|
48
|
-
return remaining ? `${label}: ${pct}(${remaining})` : `${label}: ${pct}`;
|
|
49
|
-
}
|
|
50
|
-
async function runAuthList(homeDir) {
|
|
51
|
-
const profiles = listProfiles(homeDir);
|
|
52
|
-
if (profiles.length === 0) {
|
|
53
|
-
console.log("No saved profiles.");
|
|
54
|
-
return;
|
|
55
|
-
}
|
|
56
|
-
const currentUserID = getCurrentUserID(homeDir);
|
|
57
|
-
const sorted = [...profiles].sort((a, b) => a.localeCompare(b));
|
|
58
|
-
const results = await Promise.all(
|
|
59
|
-
sorted.map(async (name) => {
|
|
60
|
-
const profileDir = getProfileDir(name, homeDir);
|
|
61
|
-
const authData = JSON.parse(
|
|
62
|
-
fs.readFileSync(path.join(profileDir, "auth.json"), "utf-8")
|
|
63
|
-
);
|
|
64
|
-
const credData = JSON.parse(
|
|
65
|
-
fs.readFileSync(path.join(profileDir, "credentials.json"), "utf-8")
|
|
66
|
-
);
|
|
67
|
-
const oauthAccount = authData["oauthAccount"];
|
|
68
|
-
const email = oauthAccount?.["emailAddress"] ?? "";
|
|
69
|
-
const userID = authData["userID"];
|
|
70
|
-
const oauth = credData["claudeAiOauth"];
|
|
71
|
-
let expiresStr = "unknown";
|
|
72
|
-
if (oauth != null && typeof oauth["expiresAt"] === "number") {
|
|
73
|
-
const d = new Date(oauth["expiresAt"]);
|
|
74
|
-
expiresStr = `${d.getFullYear()}-${String(d.getMonth() + 1).padStart(2, "0")}-${String(d.getDate()).padStart(2, "0")}`;
|
|
75
|
-
}
|
|
76
|
-
const isActive = currentUserID != null && userID === currentUserID;
|
|
77
|
-
const prefix = isActive ? "*" : " ";
|
|
78
|
-
const accessToken = oauth?.["accessToken"];
|
|
79
|
-
const expiresAt = oauth?.["expiresAt"];
|
|
80
|
-
const tokenExpired = typeof expiresAt === "number" && Date.now() > expiresAt;
|
|
81
|
-
const usage = accessToken != null && !tokenExpired ? await fetchUsage(accessToken) : void 0;
|
|
82
|
-
const dailyData = usage?.daily ?? usage?.five_hour;
|
|
83
|
-
const fiveHourStr = formatUsage("5h", dailyData);
|
|
84
|
-
const weekStr = formatUsage("7d", usage?.seven_day);
|
|
85
|
-
return `${prefix} ${name} (${email}) expires: ${expiresStr} \u2502 ${fiveHourStr} \u2502 ${weekStr}`;
|
|
86
|
-
})
|
|
87
|
-
);
|
|
88
|
-
for (const line of results) {
|
|
89
|
-
console.log(line);
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
export {
|
|
93
|
-
runAuthList
|
|
94
|
-
};
|
|
95
|
-
//# sourceMappingURL=auth-list.js.map
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"version": 3,
|
|
3
|
-
"sources": ["../../src/commands/auth-list.ts"],
|
|
4
|
-
"mappings": "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",
|
|
5
|
-
"names": []
|
|
6
|
-
}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"auth-remove.d.ts","sourceRoot":"","sources":["../../src/commands/auth-remove.ts"],"names":[],"mappings":"AAIA,wBAAgB,aAAa,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,IAAI,CAqBlE"}
|