@adonis0123/react-best-practices 1.0.2 ā 1.0.3
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/.claude-skill.json +30 -1
- package/install-skill.js +281 -173
- package/package.json +2 -2
- package/uninstall-skill.js +150 -77
- package/utils.js +0 -94
package/.claude-skill.json
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-best-practices",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"package": "@adonis0123/react-best-practices",
|
|
5
|
+
"remoteSource": "vercel-labs/agent-skills/skills/react-best-practices",
|
|
5
6
|
"files": {
|
|
6
7
|
"AGENTS.md": "AGENTS.md",
|
|
7
8
|
"README.md": "README.md",
|
|
@@ -14,6 +15,34 @@
|
|
|
14
15
|
"global": ".claude/skills",
|
|
15
16
|
"project": ".claude/skills"
|
|
16
17
|
}
|
|
18
|
+
},
|
|
19
|
+
"cursor": {
|
|
20
|
+
"enabled": true,
|
|
21
|
+
"paths": {
|
|
22
|
+
"global": ".cursor/skills",
|
|
23
|
+
"project": ".cursor/skills"
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"windsurf": {
|
|
27
|
+
"enabled": false,
|
|
28
|
+
"paths": {
|
|
29
|
+
"global": ".windsurf/skills",
|
|
30
|
+
"project": ".windsurf/skills"
|
|
31
|
+
}
|
|
32
|
+
},
|
|
33
|
+
"aider": {
|
|
34
|
+
"enabled": false,
|
|
35
|
+
"paths": {
|
|
36
|
+
"global": ".aider/skills",
|
|
37
|
+
"project": ".aider/skills"
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
"custom": {
|
|
41
|
+
"enabled": false,
|
|
42
|
+
"paths": {
|
|
43
|
+
"global": ".ai-skills",
|
|
44
|
+
"project": ".ai-skills"
|
|
45
|
+
}
|
|
17
46
|
}
|
|
18
47
|
}
|
|
19
48
|
}
|
package/install-skill.js
CHANGED
|
@@ -1,207 +1,315 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const isGlobal = process.env.npm_config_global === 'true';
|
|
14
|
-
|
|
15
|
-
// Determine installation location
|
|
16
|
-
const location = detectInstallLocation(target.paths, isGlobal);
|
|
17
|
-
|
|
18
|
-
// Extract skill name from package name (remove scope prefix)
|
|
19
|
-
const skillName = extractSkillName(config.name);
|
|
20
|
-
|
|
21
|
-
const targetDir = path.join(location.base, skillName);
|
|
22
|
-
|
|
23
|
-
// Alternative path format with full package name (including scope)
|
|
24
|
-
const altTargetDir = path.join(location.base, config.name);
|
|
25
|
-
|
|
26
|
-
console.log(` Type: ${location.type}${isGlobal ? ' (global)' : ' (project)'}`);
|
|
27
|
-
console.log(` Directory: ${targetDir}`);
|
|
28
|
-
|
|
29
|
-
// Clean up alternative path format
|
|
30
|
-
if (fs.existsSync(altTargetDir) && altTargetDir !== targetDir) {
|
|
31
|
-
console.log(` š§¹ Cleaning up alternative path format...`);
|
|
32
|
-
fs.rmSync(altTargetDir, { recursive: true, force: true });
|
|
33
|
-
console.log(` ā Removed directory: ${config.name}`);
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
34
13
|
}
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// shared/src/install-skill.ts
|
|
26
|
+
var import_fs2 = __toESM(require("fs"));
|
|
27
|
+
var import_path2 = __toESM(require("path"));
|
|
28
|
+
var import_os2 = __toESM(require("os"));
|
|
29
|
+
var import_child_process = require("child_process");
|
|
30
|
+
|
|
31
|
+
// shared/src/utils.ts
|
|
32
|
+
var import_fs = __toESM(require("fs"));
|
|
33
|
+
var import_path = __toESM(require("path"));
|
|
34
|
+
var import_os = __toESM(require("os"));
|
|
35
|
+
var CWD = process.env.INIT_CWD || process.cwd();
|
|
36
|
+
var DEFAULT_TARGET = {
|
|
37
|
+
name: "claude-code",
|
|
38
|
+
paths: {
|
|
39
|
+
global: ".claude/skills",
|
|
40
|
+
project: ".claude/skills"
|
|
39
41
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
throw new Error('SKILL.md is required but not found');
|
|
45
|
-
}
|
|
46
|
-
fs.copyFileSync(skillMdSource, path.join(targetDir, 'SKILL.md'));
|
|
47
|
-
console.log(' ā Copied SKILL.md');
|
|
48
|
-
|
|
49
|
-
// Copy other files
|
|
50
|
-
if (config.files) {
|
|
51
|
-
Object.entries(config.files).forEach(([source, dest]) => {
|
|
52
|
-
const sourcePath = path.join(__dirname, source);
|
|
53
|
-
if (!fs.existsSync(sourcePath)) {
|
|
54
|
-
console.warn(` ā Warning: ${source} not found, skipping`);
|
|
55
|
-
return;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
const destPath = path.join(targetDir, dest);
|
|
59
|
-
|
|
60
|
-
if (fs.statSync(sourcePath).isDirectory()) {
|
|
61
|
-
copyDir(sourcePath, destPath);
|
|
62
|
-
console.log(` ā Copied directory: ${source}`);
|
|
63
|
-
} else {
|
|
64
|
-
// Ensure target directory exists
|
|
65
|
-
const destDir = path.dirname(destPath);
|
|
66
|
-
if (!fs.existsSync(destDir)) {
|
|
67
|
-
fs.mkdirSync(destDir, { recursive: true });
|
|
68
|
-
}
|
|
69
|
-
fs.copyFileSync(sourcePath, destPath);
|
|
70
|
-
console.log(` ā Copied file: ${source}`);
|
|
71
|
-
}
|
|
72
|
-
});
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
// Update manifest
|
|
76
|
-
updateManifest(location.base, config, target.name);
|
|
77
|
-
|
|
78
|
-
// Run postinstall hooks
|
|
79
|
-
if (config.hooks && config.hooks.postinstall) {
|
|
80
|
-
console.log(' š§ Running postinstall hook...');
|
|
81
|
-
const { execSync } = require('child_process');
|
|
82
|
-
try {
|
|
83
|
-
execSync(config.hooks.postinstall, {
|
|
84
|
-
cwd: targetDir,
|
|
85
|
-
stdio: 'pipe'
|
|
86
|
-
});
|
|
87
|
-
console.log(' ā Postinstall hook completed');
|
|
88
|
-
} catch (error) {
|
|
89
|
-
console.warn(` ā Warning: postinstall hook failed`);
|
|
90
|
-
}
|
|
42
|
+
};
|
|
43
|
+
function getEnabledTargets(config) {
|
|
44
|
+
if (!config.targets) {
|
|
45
|
+
return [DEFAULT_TARGET];
|
|
91
46
|
}
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
47
|
+
return Object.entries(config.targets).filter(([_, target]) => target.enabled).map(([name, target]) => ({
|
|
48
|
+
name,
|
|
49
|
+
paths: target.paths
|
|
50
|
+
}));
|
|
95
51
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
// Read configuration
|
|
101
|
-
const configPath = path.join(__dirname, '.claude-skill.json');
|
|
102
|
-
if (!fs.existsSync(configPath)) {
|
|
103
|
-
throw new Error('.claude-skill.json not found');
|
|
52
|
+
function extractSkillName(packageName) {
|
|
53
|
+
if (packageName.startsWith("@")) {
|
|
54
|
+
return packageName.split("/")[1] || packageName;
|
|
104
55
|
}
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
return;
|
|
56
|
+
return packageName;
|
|
57
|
+
}
|
|
58
|
+
function detectInstallLocation(targetPaths, isGlobal) {
|
|
59
|
+
if (isGlobal) {
|
|
60
|
+
return {
|
|
61
|
+
type: "personal",
|
|
62
|
+
base: import_path.default.join(import_os.default.homedir(), targetPaths.global)
|
|
63
|
+
};
|
|
114
64
|
}
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
const installedPaths = [];
|
|
123
|
-
for (const target of enabledTargets) {
|
|
124
|
-
try {
|
|
125
|
-
const installPath = installToTarget(target, config);
|
|
126
|
-
installedPaths.push({ target: target.name, path: installPath });
|
|
127
|
-
} catch (error) {
|
|
128
|
-
console.error(`\nā Failed to install to ${target.name}:`, error.message);
|
|
65
|
+
let projectRoot = CWD;
|
|
66
|
+
while (projectRoot !== import_path.default.dirname(projectRoot)) {
|
|
67
|
+
const hasPackageJson = import_fs.default.existsSync(import_path.default.join(projectRoot, "package.json"));
|
|
68
|
+
const hasGit = import_fs.default.existsSync(import_path.default.join(projectRoot, ".git"));
|
|
69
|
+
const isInNodeModules = projectRoot.includes("/node_modules/") || import_path.default.basename(projectRoot) === "node_modules";
|
|
70
|
+
if ((hasPackageJson || hasGit) && !isInNodeModules) {
|
|
71
|
+
break;
|
|
129
72
|
}
|
|
73
|
+
projectRoot = import_path.default.dirname(projectRoot);
|
|
130
74
|
}
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
console.log('='.repeat(60));
|
|
136
|
-
|
|
137
|
-
if (installedPaths.length > 0) {
|
|
138
|
-
console.log('\nInstalled to:');
|
|
139
|
-
installedPaths.forEach(({ target, path: installPath }) => {
|
|
140
|
-
console.log(` ⢠${target}: ${installPath}`);
|
|
141
|
-
});
|
|
142
|
-
|
|
143
|
-
console.log('\nš Next Steps:');
|
|
144
|
-
console.log(' 1. Restart your AI coding tool(s)');
|
|
145
|
-
console.log(' 2. Ask: "What skills are available?"');
|
|
146
|
-
console.log(' 3. Start using your skill!');
|
|
75
|
+
const finalIsInNodeModules = projectRoot.includes("/node_modules/") || import_path.default.basename(projectRoot) === "node_modules";
|
|
76
|
+
if (finalIsInNodeModules) {
|
|
77
|
+
console.warn("\u26A0 Warning: Could not find project root directory, using current directory");
|
|
78
|
+
projectRoot = CWD;
|
|
147
79
|
}
|
|
80
|
+
return {
|
|
81
|
+
type: "project",
|
|
82
|
+
base: import_path.default.join(projectRoot, targetPaths.project)
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
function isGlobalInstall() {
|
|
86
|
+
return process.env.npm_config_global === "true";
|
|
148
87
|
}
|
|
149
|
-
|
|
150
88
|
function copyDir(src, dest) {
|
|
151
|
-
|
|
152
|
-
const entries =
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
const
|
|
156
|
-
const destPath = path.join(dest, entry.name);
|
|
157
|
-
|
|
89
|
+
import_fs.default.mkdirSync(dest, { recursive: true });
|
|
90
|
+
const entries = import_fs.default.readdirSync(src, { withFileTypes: true });
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
const srcPath = import_path.default.join(src, entry.name);
|
|
93
|
+
const destPath = import_path.default.join(dest, entry.name);
|
|
158
94
|
if (entry.isDirectory()) {
|
|
159
95
|
copyDir(srcPath, destPath);
|
|
160
96
|
} else {
|
|
161
|
-
|
|
97
|
+
import_fs.default.copyFileSync(srcPath, destPath);
|
|
162
98
|
}
|
|
163
99
|
}
|
|
164
100
|
}
|
|
101
|
+
function ensureDir(dir) {
|
|
102
|
+
if (!import_fs.default.existsSync(dir)) {
|
|
103
|
+
import_fs.default.mkdirSync(dir, { recursive: true });
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
function removeDir(dir) {
|
|
107
|
+
if (import_fs.default.existsSync(dir)) {
|
|
108
|
+
import_fs.default.rmSync(dir, { recursive: true, force: true });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
111
|
+
function readSkillConfig(dir) {
|
|
112
|
+
const configPath = import_path.default.join(dir, ".claude-skill.json");
|
|
113
|
+
if (!import_fs.default.existsSync(configPath)) {
|
|
114
|
+
throw new Error(".claude-skill.json not found");
|
|
115
|
+
}
|
|
116
|
+
return JSON.parse(import_fs.default.readFileSync(configPath, "utf-8"));
|
|
117
|
+
}
|
|
165
118
|
|
|
166
|
-
|
|
167
|
-
|
|
119
|
+
// shared/src/install-skill.ts
|
|
120
|
+
function fetchFromRemote(tempDir, remoteSource) {
|
|
121
|
+
try {
|
|
122
|
+
console.log(` \u{1F310} Fetching latest from ${remoteSource}...`);
|
|
123
|
+
(0, import_child_process.execSync)(`npx degit ${remoteSource} "${tempDir}" --force`, {
|
|
124
|
+
stdio: "pipe",
|
|
125
|
+
timeout: 6e4
|
|
126
|
+
});
|
|
127
|
+
if (import_fs2.default.existsSync(import_path2.default.join(tempDir, "SKILL.md"))) {
|
|
128
|
+
console.log(" \u2713 Fetched latest version from remote");
|
|
129
|
+
return true;
|
|
130
|
+
}
|
|
131
|
+
console.warn(" \u26A0 Remote fetch succeeded but SKILL.md not found");
|
|
132
|
+
return false;
|
|
133
|
+
} catch (error) {
|
|
134
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
135
|
+
console.warn(` \u26A0 Could not fetch from remote: ${message}`);
|
|
136
|
+
console.log(" \u2139 Using bundled version as fallback");
|
|
137
|
+
return false;
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
function getSourceDir(config, packageDir) {
|
|
141
|
+
if (!config.remoteSource) {
|
|
142
|
+
return {
|
|
143
|
+
sourceDir: packageDir,
|
|
144
|
+
cleanup: () => {
|
|
145
|
+
},
|
|
146
|
+
isRemote: false
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
const tempDir = import_path2.default.join(import_os2.default.tmpdir(), `skill-fetch-${Date.now()}`);
|
|
150
|
+
const remoteSuccess = fetchFromRemote(tempDir, config.remoteSource);
|
|
151
|
+
if (remoteSuccess) {
|
|
152
|
+
return {
|
|
153
|
+
sourceDir: tempDir,
|
|
154
|
+
cleanup: () => {
|
|
155
|
+
try {
|
|
156
|
+
import_fs2.default.rmSync(tempDir, { recursive: true, force: true });
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
},
|
|
160
|
+
isRemote: true
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
try {
|
|
164
|
+
import_fs2.default.rmSync(tempDir, { recursive: true, force: true });
|
|
165
|
+
} catch {
|
|
166
|
+
}
|
|
167
|
+
return {
|
|
168
|
+
sourceDir: packageDir,
|
|
169
|
+
cleanup: () => {
|
|
170
|
+
},
|
|
171
|
+
isRemote: false
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
function updateManifest(skillsDir, config, targetName, isRemote) {
|
|
175
|
+
const manifestPath = import_path2.default.join(skillsDir, ".skills-manifest.json");
|
|
168
176
|
let manifest = { skills: {} };
|
|
169
|
-
|
|
170
|
-
if (fs.existsSync(manifestPath)) {
|
|
177
|
+
if (import_fs2.default.existsSync(manifestPath)) {
|
|
171
178
|
try {
|
|
172
|
-
manifest = JSON.parse(
|
|
173
|
-
} catch
|
|
174
|
-
console.warn(
|
|
179
|
+
manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
|
|
180
|
+
} catch {
|
|
181
|
+
console.warn(" Warning: Could not parse existing manifest, creating new one");
|
|
175
182
|
manifest = { skills: {} };
|
|
176
183
|
}
|
|
177
184
|
}
|
|
178
|
-
|
|
179
|
-
// Extract skill name from package name (remove scope prefix)
|
|
180
|
-
const skillName = config.name.startsWith('@') ?
|
|
181
|
-
config.name.split('/')[1] || config.name :
|
|
182
|
-
config.name;
|
|
183
|
-
|
|
185
|
+
const skillName = extractSkillName(config.name);
|
|
184
186
|
manifest.skills[config.name] = {
|
|
185
187
|
version: config.version,
|
|
186
|
-
installedAt: new Date().toISOString(),
|
|
188
|
+
installedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
187
189
|
package: config.package || config.name,
|
|
188
|
-
path:
|
|
189
|
-
target: targetName
|
|
190
|
+
path: import_path2.default.join(skillsDir, skillName),
|
|
191
|
+
target: targetName,
|
|
192
|
+
...config.remoteSource && { source: config.remoteSource }
|
|
190
193
|
};
|
|
191
|
-
|
|
192
|
-
|
|
194
|
+
import_fs2.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
195
|
+
}
|
|
196
|
+
function installToTarget(target, config, sourceDir, isRemote) {
|
|
197
|
+
var _a;
|
|
198
|
+
console.log(`
|
|
199
|
+
\u{1F4E6} Installing to ${target.name}...`);
|
|
200
|
+
const isGlobal = isGlobalInstall();
|
|
201
|
+
const location = detectInstallLocation(target.paths, isGlobal);
|
|
202
|
+
const skillName = extractSkillName(config.name);
|
|
203
|
+
const targetDir = import_path2.default.join(location.base, skillName);
|
|
204
|
+
const altTargetDir = import_path2.default.join(location.base, config.name);
|
|
205
|
+
console.log(` Type: ${location.type}${isGlobal ? " (global)" : " (project)"}`);
|
|
206
|
+
console.log(` Directory: ${targetDir}`);
|
|
207
|
+
if (import_fs2.default.existsSync(altTargetDir) && altTargetDir !== targetDir) {
|
|
208
|
+
console.log(" \u{1F9F9} Cleaning up alternative path format...");
|
|
209
|
+
removeDir(altTargetDir);
|
|
210
|
+
console.log(` \u2713 Removed directory: ${config.name}`);
|
|
211
|
+
}
|
|
212
|
+
ensureDir(targetDir);
|
|
213
|
+
const skillMdSource = import_path2.default.join(sourceDir, "SKILL.md");
|
|
214
|
+
if (!import_fs2.default.existsSync(skillMdSource)) {
|
|
215
|
+
throw new Error("SKILL.md is required but not found");
|
|
216
|
+
}
|
|
217
|
+
import_fs2.default.copyFileSync(skillMdSource, import_path2.default.join(targetDir, "SKILL.md"));
|
|
218
|
+
console.log(" \u2713 Copied SKILL.md");
|
|
219
|
+
const filesToCopy = config.files || {};
|
|
220
|
+
for (const [source, dest] of Object.entries(filesToCopy)) {
|
|
221
|
+
const sourcePath = import_path2.default.join(sourceDir, source);
|
|
222
|
+
if (!import_fs2.default.existsSync(sourcePath)) {
|
|
223
|
+
console.warn(` \u26A0 Warning: ${source} not found, skipping`);
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
const destPath = import_path2.default.join(targetDir, dest);
|
|
227
|
+
if (import_fs2.default.statSync(sourcePath).isDirectory()) {
|
|
228
|
+
copyDir(sourcePath, destPath);
|
|
229
|
+
console.log(` \u2713 Copied directory: ${source}`);
|
|
230
|
+
} else {
|
|
231
|
+
const destDir = import_path2.default.dirname(destPath);
|
|
232
|
+
ensureDir(destDir);
|
|
233
|
+
import_fs2.default.copyFileSync(sourcePath, destPath);
|
|
234
|
+
console.log(` \u2713 Copied file: ${source}`);
|
|
235
|
+
}
|
|
236
|
+
}
|
|
237
|
+
updateManifest(location.base, config, target.name, isRemote);
|
|
238
|
+
if ((_a = config.hooks) == null ? void 0 : _a.postinstall) {
|
|
239
|
+
console.log(" \u{1F527} Running postinstall hook...");
|
|
240
|
+
try {
|
|
241
|
+
(0, import_child_process.execSync)(config.hooks.postinstall, {
|
|
242
|
+
cwd: targetDir,
|
|
243
|
+
stdio: "pipe"
|
|
244
|
+
});
|
|
245
|
+
console.log(" \u2713 Postinstall hook completed");
|
|
246
|
+
} catch {
|
|
247
|
+
console.warn(" \u26A0 Warning: postinstall hook failed");
|
|
248
|
+
}
|
|
249
|
+
}
|
|
250
|
+
console.log(` \u2705 Installed to ${target.name}`);
|
|
251
|
+
return targetDir;
|
|
252
|
+
}
|
|
253
|
+
function installSkill() {
|
|
254
|
+
console.log("\u{1F680} Installing AI Coding Skill...\n");
|
|
255
|
+
const packageDir = __dirname;
|
|
256
|
+
const config = readSkillConfig(packageDir);
|
|
257
|
+
const enabledTargets = getEnabledTargets(config);
|
|
258
|
+
if (enabledTargets.length === 0) {
|
|
259
|
+
console.warn("\u26A0 No targets enabled in configuration");
|
|
260
|
+
console.warn("Please enable at least one target in .claude-skill.json");
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
console.log(`Installing skill "${config.name}" to ${enabledTargets.length} target(s):`);
|
|
264
|
+
enabledTargets.forEach((target) => {
|
|
265
|
+
console.log(` \u2022 ${target.name}`);
|
|
266
|
+
});
|
|
267
|
+
const { sourceDir, cleanup, isRemote } = getSourceDir(config, packageDir);
|
|
268
|
+
if (isRemote) {
|
|
269
|
+
console.log(`
|
|
270
|
+
\u{1F4E1} Source: Remote (${config.remoteSource})`);
|
|
271
|
+
} else {
|
|
272
|
+
console.log("\n\u{1F4E6} Source: Bundled (local)");
|
|
273
|
+
}
|
|
274
|
+
try {
|
|
275
|
+
const installedPaths = [];
|
|
276
|
+
for (const target of enabledTargets) {
|
|
277
|
+
try {
|
|
278
|
+
const installPath = installToTarget(target, config, sourceDir, isRemote);
|
|
279
|
+
installedPaths.push({ target: target.name, path: installPath });
|
|
280
|
+
} catch (error) {
|
|
281
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
282
|
+
console.error(`
|
|
283
|
+
\u274C Failed to install to ${target.name}:`, message);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
console.log("\n" + "=".repeat(60));
|
|
287
|
+
console.log("\u2705 Installation Complete!");
|
|
288
|
+
console.log("=".repeat(60));
|
|
289
|
+
if (installedPaths.length > 0) {
|
|
290
|
+
console.log("\nInstalled to:");
|
|
291
|
+
installedPaths.forEach(({ target, path: installPath }) => {
|
|
292
|
+
console.log(` \u2022 ${target}: ${installPath}`);
|
|
293
|
+
});
|
|
294
|
+
console.log("\n\u{1F4D6} Next Steps:");
|
|
295
|
+
console.log(" 1. Restart your AI coding tool(s)");
|
|
296
|
+
console.log(' 2. Ask: "What skills are available?"');
|
|
297
|
+
console.log(" 3. Start using your skill!");
|
|
298
|
+
}
|
|
299
|
+
} finally {
|
|
300
|
+
cleanup();
|
|
301
|
+
}
|
|
193
302
|
}
|
|
194
|
-
|
|
195
|
-
// Execute installation
|
|
196
303
|
try {
|
|
197
304
|
installSkill();
|
|
198
305
|
} catch (error) {
|
|
199
|
-
|
|
200
|
-
console.error(
|
|
201
|
-
console.error(
|
|
202
|
-
console.error(
|
|
203
|
-
console.error(
|
|
204
|
-
console.error(
|
|
205
|
-
console.error(
|
|
306
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
307
|
+
console.error("\n\u274C Failed to install skill:", message);
|
|
308
|
+
console.error("\nTroubleshooting:");
|
|
309
|
+
console.error("- Ensure .claude-skill.json exists and is valid JSON");
|
|
310
|
+
console.error("- Ensure SKILL.md exists");
|
|
311
|
+
console.error("- Check file permissions for target directories");
|
|
312
|
+
console.error("- Verify at least one target is enabled in .claude-skill.json");
|
|
313
|
+
console.error("- Try running with sudo for global installation (if needed)");
|
|
206
314
|
process.exit(1);
|
|
207
315
|
}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@adonis0123/react-best-practices",
|
|
3
|
-
"version": "1.0.
|
|
4
|
-
"description": "Claude Code Skill - React å Next.js ę§č½ä¼åęä½³å®č·µęåļ¼ę„čŖ Vercel Engineering",
|
|
3
|
+
"version": "1.0.3",
|
|
4
|
+
"description": "Claude Code Skill - React å Next.js ę§č½ä¼åęä½³å®č·µęåļ¼ę„čŖ Vercel Engineeringćå®č£
ę¶čŖåØä»äøęøøä»åŗęåęę°ēę¬ć",
|
|
5
5
|
"files": [
|
|
6
6
|
"SKILL.md",
|
|
7
7
|
"AGENTS.md",
|
package/uninstall-skill.js
CHANGED
|
@@ -1,87 +1,162 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __copyProps = (to, from, except, desc) => {
|
|
9
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
10
|
+
for (let key of __getOwnPropNames(from))
|
|
11
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
12
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
13
|
+
}
|
|
14
|
+
return to;
|
|
15
|
+
};
|
|
16
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
17
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
18
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
19
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
20
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
21
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
22
|
+
mod
|
|
23
|
+
));
|
|
24
|
+
|
|
25
|
+
// shared/src/uninstall-skill.ts
|
|
26
|
+
var import_fs2 = __toESM(require("fs"));
|
|
27
|
+
var import_path2 = __toESM(require("path"));
|
|
28
|
+
|
|
29
|
+
// shared/src/utils.ts
|
|
30
|
+
var import_fs = __toESM(require("fs"));
|
|
31
|
+
var import_path = __toESM(require("path"));
|
|
32
|
+
var import_os = __toESM(require("os"));
|
|
33
|
+
var CWD = process.env.INIT_CWD || process.cwd();
|
|
34
|
+
var DEFAULT_TARGET = {
|
|
35
|
+
name: "claude-code",
|
|
36
|
+
paths: {
|
|
37
|
+
global: ".claude/skills",
|
|
38
|
+
project: ".claude/skills"
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
function getEnabledTargets(config) {
|
|
42
|
+
if (!config.targets) {
|
|
43
|
+
return [DEFAULT_TARGET];
|
|
44
|
+
}
|
|
45
|
+
return Object.entries(config.targets).filter(([_, target]) => target.enabled).map(([name, target]) => ({
|
|
46
|
+
name,
|
|
47
|
+
paths: target.paths
|
|
48
|
+
}));
|
|
49
|
+
}
|
|
50
|
+
function extractSkillName(packageName) {
|
|
51
|
+
if (packageName.startsWith("@")) {
|
|
52
|
+
return packageName.split("/")[1] || packageName;
|
|
53
|
+
}
|
|
54
|
+
return packageName;
|
|
55
|
+
}
|
|
56
|
+
function detectInstallLocation(targetPaths, isGlobal) {
|
|
57
|
+
if (isGlobal) {
|
|
58
|
+
return {
|
|
59
|
+
type: "personal",
|
|
60
|
+
base: import_path.default.join(import_os.default.homedir(), targetPaths.global)
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
let projectRoot = CWD;
|
|
64
|
+
while (projectRoot !== import_path.default.dirname(projectRoot)) {
|
|
65
|
+
const hasPackageJson = import_fs.default.existsSync(import_path.default.join(projectRoot, "package.json"));
|
|
66
|
+
const hasGit = import_fs.default.existsSync(import_path.default.join(projectRoot, ".git"));
|
|
67
|
+
const isInNodeModules = projectRoot.includes("/node_modules/") || import_path.default.basename(projectRoot) === "node_modules";
|
|
68
|
+
if ((hasPackageJson || hasGit) && !isInNodeModules) {
|
|
69
|
+
break;
|
|
70
|
+
}
|
|
71
|
+
projectRoot = import_path.default.dirname(projectRoot);
|
|
72
|
+
}
|
|
73
|
+
const finalIsInNodeModules = projectRoot.includes("/node_modules/") || import_path.default.basename(projectRoot) === "node_modules";
|
|
74
|
+
if (finalIsInNodeModules) {
|
|
75
|
+
console.warn("\u26A0 Warning: Could not find project root directory, using current directory");
|
|
76
|
+
projectRoot = CWD;
|
|
77
|
+
}
|
|
78
|
+
return {
|
|
79
|
+
type: "project",
|
|
80
|
+
base: import_path.default.join(projectRoot, targetPaths.project)
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
function isGlobalInstall() {
|
|
84
|
+
return process.env.npm_config_global === "true";
|
|
85
|
+
}
|
|
86
|
+
function removeDir(dir) {
|
|
87
|
+
if (import_fs.default.existsSync(dir)) {
|
|
88
|
+
import_fs.default.rmSync(dir, { recursive: true, force: true });
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
function readSkillConfig(dir) {
|
|
92
|
+
const configPath = import_path.default.join(dir, ".claude-skill.json");
|
|
93
|
+
if (!import_fs.default.existsSync(configPath)) {
|
|
94
|
+
throw new Error(".claude-skill.json not found");
|
|
95
|
+
}
|
|
96
|
+
return JSON.parse(import_fs.default.readFileSync(configPath, "utf-8"));
|
|
97
|
+
}
|
|
2
98
|
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
99
|
+
// shared/src/uninstall-skill.ts
|
|
100
|
+
function updateManifest(skillsDir, config) {
|
|
101
|
+
const manifestPath = import_path2.default.join(skillsDir, ".skills-manifest.json");
|
|
102
|
+
if (!import_fs2.default.existsSync(manifestPath)) {
|
|
103
|
+
return;
|
|
104
|
+
}
|
|
105
|
+
try {
|
|
106
|
+
const manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
|
|
107
|
+
if (manifest.skills && manifest.skills[config.name]) {
|
|
108
|
+
delete manifest.skills[config.name];
|
|
109
|
+
import_fs2.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
110
|
+
console.log(" \u2713 Updated manifest");
|
|
111
|
+
}
|
|
112
|
+
} catch (error) {
|
|
113
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
114
|
+
console.warn(" Warning: Could not update manifest:", message);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
9
117
|
function uninstallFromTarget(target, config) {
|
|
10
|
-
console.log(
|
|
11
|
-
|
|
12
|
-
const isGlobal =
|
|
118
|
+
console.log(`
|
|
119
|
+
\u{1F5D1}\uFE0F Uninstalling from ${target.name}...`);
|
|
120
|
+
const isGlobal = isGlobalInstall();
|
|
13
121
|
const location = detectInstallLocation(target.paths, isGlobal);
|
|
14
|
-
|
|
15
|
-
// Extract skill name from package name (remove scope prefix)
|
|
16
122
|
const skillName = extractSkillName(config.name);
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
const skillNameTargetDir = path.join(location.base, skillName);
|
|
20
|
-
|
|
21
|
-
// Path format with full package name (including scope)
|
|
22
|
-
const fullPackageNameTargetDir = path.join(location.base, config.name);
|
|
23
|
-
|
|
123
|
+
const skillNameTargetDir = import_path2.default.join(location.base, skillName);
|
|
124
|
+
const fullPackageNameTargetDir = import_path2.default.join(location.base, config.name);
|
|
24
125
|
let removed = false;
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
fs.rmSync(skillNameTargetDir, { recursive: true, force: true });
|
|
29
|
-
console.log(` ā Removed skill directory: ${skillName}`);
|
|
126
|
+
if (import_fs2.default.existsSync(skillNameTargetDir)) {
|
|
127
|
+
removeDir(skillNameTargetDir);
|
|
128
|
+
console.log(` \u2713 Removed skill directory: ${skillName}`);
|
|
30
129
|
removed = true;
|
|
31
130
|
}
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
fs.rmSync(fullPackageNameTargetDir, { recursive: true, force: true });
|
|
36
|
-
console.log(` ā Removed skill directory: ${config.name}`);
|
|
131
|
+
if (import_fs2.default.existsSync(fullPackageNameTargetDir) && fullPackageNameTargetDir !== skillNameTargetDir) {
|
|
132
|
+
removeDir(fullPackageNameTargetDir);
|
|
133
|
+
console.log(` \u2713 Removed skill directory: ${config.name}`);
|
|
37
134
|
removed = true;
|
|
38
135
|
}
|
|
39
|
-
|
|
40
|
-
// Update manifest
|
|
41
|
-
const manifestPath = path.join(location.base, '.skills-manifest.json');
|
|
42
|
-
if (fs.existsSync(manifestPath)) {
|
|
43
|
-
try {
|
|
44
|
-
const manifest = JSON.parse(fs.readFileSync(manifestPath, 'utf8'));
|
|
45
|
-
if (manifest.skills && manifest.skills[config.name]) {
|
|
46
|
-
delete manifest.skills[config.name];
|
|
47
|
-
fs.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
|
|
48
|
-
console.log(` ā Updated manifest`);
|
|
49
|
-
}
|
|
50
|
-
} catch (error) {
|
|
51
|
-
console.warn(' Warning: Could not update manifest:', error.message);
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
136
|
+
updateManifest(location.base, config);
|
|
55
137
|
if (removed) {
|
|
56
|
-
console.log(`
|
|
138
|
+
console.log(` \u2705 Uninstalled from ${target.name}`);
|
|
57
139
|
return true;
|
|
58
140
|
} else {
|
|
59
|
-
console.log(`
|
|
141
|
+
console.log(` \u2139\uFE0F Skill was not installed in ${target.name}`);
|
|
60
142
|
return false;
|
|
61
143
|
}
|
|
62
144
|
}
|
|
63
|
-
|
|
64
145
|
function uninstallSkill() {
|
|
65
|
-
console.log(
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
146
|
+
console.log("\u{1F5D1}\uFE0F Uninstalling AI Coding Skill...\n");
|
|
147
|
+
const packageDir = __dirname;
|
|
148
|
+
let config;
|
|
149
|
+
try {
|
|
150
|
+
config = readSkillConfig(packageDir);
|
|
151
|
+
} catch {
|
|
152
|
+
console.warn("Warning: .claude-skill.json not found, skipping cleanup");
|
|
71
153
|
return;
|
|
72
154
|
}
|
|
73
|
-
|
|
74
|
-
const config = JSON.parse(fs.readFileSync(configPath, 'utf8'));
|
|
75
|
-
|
|
76
|
-
// Get enabled targets
|
|
77
155
|
const enabledTargets = getEnabledTargets(config);
|
|
78
|
-
|
|
79
156
|
console.log(`Uninstalling skill "${config.name}" from ${enabledTargets.length} target(s):`);
|
|
80
|
-
enabledTargets.forEach(target => {
|
|
81
|
-
console.log(`
|
|
157
|
+
enabledTargets.forEach((target) => {
|
|
158
|
+
console.log(` \u2022 ${target.name}`);
|
|
82
159
|
});
|
|
83
|
-
|
|
84
|
-
// Uninstall from all enabled targets
|
|
85
160
|
const uninstalledFrom = [];
|
|
86
161
|
for (const target of enabledTargets) {
|
|
87
162
|
try {
|
|
@@ -90,29 +165,27 @@ function uninstallSkill() {
|
|
|
90
165
|
uninstalledFrom.push(target.name);
|
|
91
166
|
}
|
|
92
167
|
} catch (error) {
|
|
93
|
-
|
|
168
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
169
|
+
console.error(`
|
|
170
|
+
\u274C Failed to uninstall from ${target.name}:`, message);
|
|
94
171
|
}
|
|
95
172
|
}
|
|
96
|
-
|
|
97
|
-
// Summary
|
|
98
|
-
console.log('\n' + '='.repeat(60));
|
|
173
|
+
console.log("\n" + "=".repeat(60));
|
|
99
174
|
if (uninstalledFrom.length > 0) {
|
|
100
|
-
console.log(
|
|
101
|
-
console.log(
|
|
102
|
-
console.log(
|
|
103
|
-
uninstalledFrom.forEach(target => {
|
|
104
|
-
console.log(`
|
|
175
|
+
console.log("\u2705 Uninstallation Complete!");
|
|
176
|
+
console.log("=".repeat(60));
|
|
177
|
+
console.log("\nUninstalled from:");
|
|
178
|
+
uninstalledFrom.forEach((target) => {
|
|
179
|
+
console.log(` \u2022 ${target}`);
|
|
105
180
|
});
|
|
106
181
|
} else {
|
|
107
|
-
console.log(
|
|
108
|
-
console.log(
|
|
182
|
+
console.log("\u2139\uFE0F Skill was not installed");
|
|
183
|
+
console.log("=".repeat(60));
|
|
109
184
|
}
|
|
110
185
|
}
|
|
111
|
-
|
|
112
|
-
// Execute uninstall
|
|
113
186
|
try {
|
|
114
187
|
uninstallSkill();
|
|
115
188
|
} catch (error) {
|
|
116
|
-
|
|
117
|
-
|
|
189
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
190
|
+
console.error("\n\u26A0\uFE0F Warning during uninstall:", message);
|
|
118
191
|
}
|
package/utils.js
DELETED
|
@@ -1,94 +0,0 @@
|
|
|
1
|
-
const fs = require('fs');
|
|
2
|
-
const path = require('path');
|
|
3
|
-
const os = require('os');
|
|
4
|
-
|
|
5
|
-
const CWD = process.env.INIT_CWD || process.cwd();
|
|
6
|
-
|
|
7
|
-
/**
|
|
8
|
-
* Get enabled target configurations
|
|
9
|
-
*/
|
|
10
|
-
function getEnabledTargets(config) {
|
|
11
|
-
// If no targets configuration, use default Claude Code configuration
|
|
12
|
-
if (!config.targets) {
|
|
13
|
-
return [{
|
|
14
|
-
name: 'claude-code',
|
|
15
|
-
paths: {
|
|
16
|
-
global: '.claude/skills',
|
|
17
|
-
project: '.claude/skills'
|
|
18
|
-
}
|
|
19
|
-
}];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
// Return all enabled targets
|
|
23
|
-
return Object.entries(config.targets)
|
|
24
|
-
.filter(([_, target]) => target.enabled)
|
|
25
|
-
.map(([name, target]) => ({
|
|
26
|
-
name,
|
|
27
|
-
paths: target.paths
|
|
28
|
-
}));
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
/**
|
|
32
|
-
* Extract skill name from package name (remove scope prefix)
|
|
33
|
-
*/
|
|
34
|
-
function extractSkillName(packageName) {
|
|
35
|
-
return packageName.startsWith('@') ?
|
|
36
|
-
packageName.split('/')[1] || packageName :
|
|
37
|
-
packageName;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Detect installation location
|
|
42
|
-
*/
|
|
43
|
-
function detectInstallLocation(targetPaths, isGlobal) {
|
|
44
|
-
if (isGlobal) {
|
|
45
|
-
// Global installation: install to user home directory
|
|
46
|
-
return {
|
|
47
|
-
type: 'personal',
|
|
48
|
-
base: path.join(os.homedir(), targetPaths.global)
|
|
49
|
-
};
|
|
50
|
-
} else {
|
|
51
|
-
// Project-level installation: find the actual project root directory
|
|
52
|
-
let projectRoot = CWD;
|
|
53
|
-
|
|
54
|
-
// Search upward, skip node_modules directories, find the actual project root
|
|
55
|
-
while (projectRoot !== path.dirname(projectRoot)) {
|
|
56
|
-
// Check if this is a project root directory (contains package.json or .git)
|
|
57
|
-
const hasPackageJson = fs.existsSync(path.join(projectRoot, 'package.json'));
|
|
58
|
-
const hasGit = fs.existsSync(path.join(projectRoot, '.git'));
|
|
59
|
-
|
|
60
|
-
// Check if current directory is in node_modules
|
|
61
|
-
const isInNodeModules = projectRoot.includes('/node_modules/') ||
|
|
62
|
-
path.basename(projectRoot) === 'node_modules';
|
|
63
|
-
|
|
64
|
-
if ((hasPackageJson || hasGit) && !isInNodeModules) {
|
|
65
|
-
// Found the actual project root directory
|
|
66
|
-
break;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
// Continue searching upward
|
|
70
|
-
projectRoot = path.dirname(projectRoot);
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// Verify the final path is reasonable
|
|
74
|
-
const finalIsInNodeModules = projectRoot.includes('/node_modules/') ||
|
|
75
|
-
path.basename(projectRoot) === 'node_modules';
|
|
76
|
-
|
|
77
|
-
if (finalIsInNodeModules) {
|
|
78
|
-
// If suitable project root not found, use current working directory (with warning)
|
|
79
|
-
console.warn('ā Warning: Could not find project root directory, using current directory');
|
|
80
|
-
projectRoot = CWD;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
return {
|
|
84
|
-
type: 'project',
|
|
85
|
-
base: path.join(projectRoot, targetPaths.project)
|
|
86
|
-
};
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
module.exports = {
|
|
91
|
-
getEnabledTargets,
|
|
92
|
-
extractSkillName,
|
|
93
|
-
detectInstallLocation
|
|
94
|
-
};
|