@adonis0123/skill-development 1.0.6 → 1.0.8

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 CHANGED
@@ -77,6 +77,7 @@ skill-name/
77
77
  - [@adonis0123/staged-changes-review](https://www.npmjs.com/package/@adonis0123/staged-changes-review) - 代码审查
78
78
  - [@adonis0123/create-skill](https://www.npmjs.com/package/@adonis0123/create-skill) - 创建新技能包
79
79
  - [@adonis0123/code-doc-generator](https://www.npmjs.com/package/@adonis0123/code-doc-generator) - 代码文档生成
80
+ - [@adonis0123/css-tailwind-styling](https://www.npmjs.com/package/@adonis0123/css-tailwind-styling) - CSS 和 Tailwind 样式规范
80
81
  ## License
81
82
 
82
83
  MIT
package/install-skill.js CHANGED
@@ -23,9 +23,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
 
25
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"));
26
+ var import_fs3 = __toESM(require("fs"));
27
+ var import_path3 = __toESM(require("path"));
28
+ var import_os3 = __toESM(require("os"));
29
29
  var import_child_process = require("child_process");
30
30
 
31
31
  // shared/src/utils.ts
@@ -115,6 +115,110 @@ function readSkillConfig(dir) {
115
115
  }
116
116
  return JSON.parse(import_fs.default.readFileSync(configPath, "utf-8"));
117
117
  }
118
+ function parseYamlFrontmatter(content) {
119
+ const match = content.match(/^---\n([\s\S]*?)\n---\n([\s\S]*)$/);
120
+ if (!match) {
121
+ return null;
122
+ }
123
+ const [, frontmatterStr, body] = match;
124
+ const frontmatter = {};
125
+ const lines = frontmatterStr.split("\n");
126
+ for (const line of lines) {
127
+ const match2 = line.match(/^(\w+):\s*(.*)$/);
128
+ if (match2) {
129
+ const [, key, value] = match2;
130
+ frontmatter[key] = value.replace(/^["']|["']$/g, "").trim();
131
+ }
132
+ }
133
+ return { frontmatter, body };
134
+ }
135
+ function patchSkillMdName(skillMdPath, name) {
136
+ try {
137
+ const content = import_fs.default.readFileSync(skillMdPath, "utf-8");
138
+ const parsed = parseYamlFrontmatter(content);
139
+ if (!parsed) {
140
+ console.warn(" \u26A0 Warning: SKILL.md has no frontmatter, skipping name patch");
141
+ return;
142
+ }
143
+ const { frontmatter, body } = parsed;
144
+ frontmatter.name = name;
145
+ const frontmatterLines = Object.entries(frontmatter).map(
146
+ ([key, value]) => `${key}: ${value}`
147
+ );
148
+ const newContent = `---
149
+ ${frontmatterLines.join("\n")}
150
+ ---
151
+ ${body}`;
152
+ import_fs.default.writeFileSync(skillMdPath, newContent, "utf-8");
153
+ console.log(` \u2713 Patched SKILL.md name: ${name}`);
154
+ } catch (error) {
155
+ const message = error instanceof Error ? error.message : String(error);
156
+ console.warn(` \u26A0 Warning: Failed to patch SKILL.md name: ${message}`);
157
+ }
158
+ }
159
+
160
+ // shared/src/claude-settings.ts
161
+ var import_fs2 = __toESM(require("fs"));
162
+ var import_path2 = __toESM(require("path"));
163
+ var import_os2 = __toESM(require("os"));
164
+ function getClaudeSettingsPath() {
165
+ return import_path2.default.join(import_os2.default.homedir(), ".claude", "settings.json");
166
+ }
167
+ function readClaudeSettings() {
168
+ const settingsPath = getClaudeSettingsPath();
169
+ if (!import_fs2.default.existsSync(settingsPath)) {
170
+ return {};
171
+ }
172
+ try {
173
+ const content = import_fs2.default.readFileSync(settingsPath, "utf-8");
174
+ return JSON.parse(content);
175
+ } catch (error) {
176
+ console.warn(" \u26A0 Warning: Could not parse settings.json, treating as empty");
177
+ return {};
178
+ }
179
+ }
180
+ function writeClaudeSettings(settings) {
181
+ const settingsPath = getClaudeSettingsPath();
182
+ const settingsDir = import_path2.default.dirname(settingsPath);
183
+ if (!import_fs2.default.existsSync(settingsDir)) {
184
+ import_fs2.default.mkdirSync(settingsDir, { recursive: true });
185
+ }
186
+ import_fs2.default.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
187
+ }
188
+ function hookMatcherExists(existingHooks, newMatcher) {
189
+ return existingHooks.some((hook) => hook.matcher === newMatcher.matcher);
190
+ }
191
+ function addClaudeHooks(hooksConfig, skillName) {
192
+ const settings = readClaudeSettings();
193
+ let modified = false;
194
+ if (!settings.hooks || typeof settings.hooks !== "object") {
195
+ settings.hooks = {};
196
+ }
197
+ const hooks = settings.hooks;
198
+ for (const [hookType, hookMatchers] of Object.entries(hooksConfig)) {
199
+ if (!hookMatchers || !Array.isArray(hookMatchers)) {
200
+ continue;
201
+ }
202
+ if (!hooks[hookType] || !Array.isArray(hooks[hookType])) {
203
+ hooks[hookType] = [];
204
+ }
205
+ const existingHooks = hooks[hookType];
206
+ for (const matcher of hookMatchers) {
207
+ if (!hookMatcherExists(existingHooks, matcher)) {
208
+ existingHooks.push(matcher);
209
+ modified = true;
210
+ console.log(` \u2713 Added ${hookType} hook for ${skillName}`);
211
+ } else {
212
+ console.log(` \u2139 ${hookType} hook already exists, skipping`);
213
+ }
214
+ }
215
+ hooks[hookType] = existingHooks;
216
+ }
217
+ if (modified) {
218
+ writeClaudeSettings(settings);
219
+ }
220
+ return modified;
221
+ }
118
222
 
119
223
  // shared/src/install-skill.ts
120
224
  function fetchFromRemote(tempDir, remoteSource) {
@@ -124,7 +228,7 @@ function fetchFromRemote(tempDir, remoteSource) {
124
228
  stdio: "pipe",
125
229
  timeout: 6e4
126
230
  });
127
- if (import_fs2.default.existsSync(import_path2.default.join(tempDir, "SKILL.md"))) {
231
+ if (import_fs3.default.existsSync(import_path3.default.join(tempDir, "SKILL.md"))) {
128
232
  console.log(" \u2713 Fetched latest version from remote");
129
233
  return true;
130
234
  }
@@ -146,14 +250,14 @@ function getSourceDir(config, packageDir) {
146
250
  isRemote: false
147
251
  };
148
252
  }
149
- const tempDir = import_path2.default.join(import_os2.default.tmpdir(), `skill-fetch-${Date.now()}`);
253
+ const tempDir = import_path3.default.join(import_os3.default.tmpdir(), `skill-fetch-${Date.now()}`);
150
254
  const remoteSuccess = fetchFromRemote(tempDir, config.remoteSource);
151
255
  if (remoteSuccess) {
152
256
  return {
153
257
  sourceDir: tempDir,
154
258
  cleanup: () => {
155
259
  try {
156
- import_fs2.default.rmSync(tempDir, { recursive: true, force: true });
260
+ import_fs3.default.rmSync(tempDir, { recursive: true, force: true });
157
261
  } catch {
158
262
  }
159
263
  },
@@ -161,7 +265,7 @@ function getSourceDir(config, packageDir) {
161
265
  };
162
266
  }
163
267
  try {
164
- import_fs2.default.rmSync(tempDir, { recursive: true, force: true });
268
+ import_fs3.default.rmSync(tempDir, { recursive: true, force: true });
165
269
  } catch {
166
270
  }
167
271
  return {
@@ -172,11 +276,11 @@ function getSourceDir(config, packageDir) {
172
276
  };
173
277
  }
174
278
  function updateManifest(skillsDir, config, targetName, isRemote) {
175
- const manifestPath = import_path2.default.join(skillsDir, ".skills-manifest.json");
279
+ const manifestPath = import_path3.default.join(skillsDir, ".skills-manifest.json");
176
280
  let manifest = { skills: {} };
177
- if (import_fs2.default.existsSync(manifestPath)) {
281
+ if (import_fs3.default.existsSync(manifestPath)) {
178
282
  try {
179
- manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
283
+ manifest = JSON.parse(import_fs3.default.readFileSync(manifestPath, "utf-8"));
180
284
  } catch {
181
285
  console.warn(" Warning: Could not parse existing manifest, creating new one");
182
286
  manifest = { skills: {} };
@@ -187,55 +291,74 @@ function updateManifest(skillsDir, config, targetName, isRemote) {
187
291
  version: config.version,
188
292
  installedAt: (/* @__PURE__ */ new Date()).toISOString(),
189
293
  package: config.package || config.name,
190
- path: import_path2.default.join(skillsDir, skillName),
294
+ path: import_path3.default.join(skillsDir, skillName),
191
295
  target: targetName,
192
296
  ...config.remoteSource && { source: config.remoteSource }
193
297
  };
194
- import_fs2.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
298
+ import_fs3.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
195
299
  }
196
300
  function installToTarget(target, config, sourceDir, isRemote) {
197
- var _a;
301
+ var _a, _b;
198
302
  console.log(`
199
303
  \u{1F4E6} Installing to ${target.name}...`);
200
304
  const isGlobal = isGlobalInstall();
201
305
  const location = detectInstallLocation(target.paths, isGlobal);
202
306
  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);
307
+ const targetDir = import_path3.default.join(location.base, skillName);
308
+ const altTargetDir = import_path3.default.join(location.base, config.name);
205
309
  console.log(` Type: ${location.type}${isGlobal ? " (global)" : " (project)"}`);
206
310
  console.log(` Directory: ${targetDir}`);
207
- if (import_fs2.default.existsSync(altTargetDir) && altTargetDir !== targetDir) {
311
+ if (import_fs3.default.existsSync(altTargetDir) && altTargetDir !== targetDir) {
208
312
  console.log(" \u{1F9F9} Cleaning up alternative path format...");
209
313
  removeDir(altTargetDir);
210
314
  console.log(` \u2713 Removed directory: ${config.name}`);
211
315
  }
212
316
  ensureDir(targetDir);
213
- const skillMdSource = import_path2.default.join(sourceDir, "SKILL.md");
214
- if (!import_fs2.default.existsSync(skillMdSource)) {
317
+ const skillMdSource = import_path3.default.join(sourceDir, "SKILL.md");
318
+ if (!import_fs3.default.existsSync(skillMdSource)) {
215
319
  throw new Error("SKILL.md is required but not found");
216
320
  }
217
- import_fs2.default.copyFileSync(skillMdSource, import_path2.default.join(targetDir, "SKILL.md"));
321
+ import_fs3.default.copyFileSync(skillMdSource, import_path3.default.join(targetDir, "SKILL.md"));
218
322
  console.log(" \u2713 Copied SKILL.md");
323
+ if (isRemote && config.remoteSource) {
324
+ patchSkillMdName(import_path3.default.join(targetDir, "SKILL.md"), config.name);
325
+ }
219
326
  const filesToCopy = config.files || {};
220
327
  for (const [source, dest] of Object.entries(filesToCopy)) {
221
- const sourcePath = import_path2.default.join(sourceDir, source);
222
- if (!import_fs2.default.existsSync(sourcePath)) {
328
+ if (source === "SKILL.md") {
329
+ continue;
330
+ }
331
+ const sourcePath = import_path3.default.join(sourceDir, source);
332
+ if (!import_fs3.default.existsSync(sourcePath)) {
223
333
  console.warn(` \u26A0 Warning: ${source} not found, skipping`);
224
334
  continue;
225
335
  }
226
- const destPath = import_path2.default.join(targetDir, dest);
227
- if (import_fs2.default.statSync(sourcePath).isDirectory()) {
336
+ const destPath = import_path3.default.join(targetDir, dest);
337
+ if (import_fs3.default.statSync(sourcePath).isDirectory()) {
228
338
  copyDir(sourcePath, destPath);
229
339
  console.log(` \u2713 Copied directory: ${source}`);
230
340
  } else {
231
- const destDir = import_path2.default.dirname(destPath);
341
+ const destDir = import_path3.default.dirname(destPath);
232
342
  ensureDir(destDir);
233
- import_fs2.default.copyFileSync(sourcePath, destPath);
343
+ import_fs3.default.copyFileSync(sourcePath, destPath);
234
344
  console.log(` \u2713 Copied file: ${source}`);
235
345
  }
236
346
  }
237
347
  updateManifest(location.base, config, target.name, isRemote);
238
- if ((_a = config.hooks) == null ? void 0 : _a.postinstall) {
348
+ if (target.name === "claude-code" && ((_a = config.claudeSettings) == null ? void 0 : _a.hooks)) {
349
+ console.log(" \u{1F527} \u914D\u7F6E Claude Code \u94A9\u5B50...");
350
+ try {
351
+ const skillName2 = extractSkillName(config.name);
352
+ const modified = addClaudeHooks(config.claudeSettings.hooks, skillName2);
353
+ if (modified) {
354
+ console.log(" \u2705 \u94A9\u5B50\u5DF2\u914D\u7F6E\u5230 ~/.claude/settings.json");
355
+ }
356
+ } catch (error) {
357
+ const message = error instanceof Error ? error.message : String(error);
358
+ console.warn(` \u26A0 \u8B66\u544A: \u65E0\u6CD5\u914D\u7F6E\u94A9\u5B50: ${message}`);
359
+ }
360
+ }
361
+ if ((_b = config.hooks) == null ? void 0 : _b.postinstall) {
239
362
  console.log(" \u{1F527} Running postinstall hook...");
240
363
  try {
241
364
  (0, import_child_process.execSync)(config.hooks.postinstall, {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adonis0123/skill-development",
3
- "version": "1.0.6",
3
+ "version": "1.0.8",
4
4
  "description": "Claude Code Skill - 技能开发指南,提供创建有效技能的完整流程和最佳实践。安装时自动从 Anthropic 官方仓库拉取最新版本。",
5
5
  "scripts": {
6
6
  "postinstall": "node install-skill.js",
@@ -23,8 +23,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
23
23
  ));
24
24
 
25
25
  // shared/src/uninstall-skill.ts
26
- var import_fs2 = __toESM(require("fs"));
27
- var import_path2 = __toESM(require("path"));
26
+ var import_fs3 = __toESM(require("fs"));
27
+ var import_path3 = __toESM(require("path"));
28
28
 
29
29
  // shared/src/utils.ts
30
30
  var import_fs = __toESM(require("fs"));
@@ -96,17 +96,77 @@ function readSkillConfig(dir) {
96
96
  return JSON.parse(import_fs.default.readFileSync(configPath, "utf-8"));
97
97
  }
98
98
 
99
+ // shared/src/claude-settings.ts
100
+ var import_fs2 = __toESM(require("fs"));
101
+ var import_path2 = __toESM(require("path"));
102
+ var import_os2 = __toESM(require("os"));
103
+ function getClaudeSettingsPath() {
104
+ return import_path2.default.join(import_os2.default.homedir(), ".claude", "settings.json");
105
+ }
106
+ function readClaudeSettings() {
107
+ const settingsPath = getClaudeSettingsPath();
108
+ if (!import_fs2.default.existsSync(settingsPath)) {
109
+ return {};
110
+ }
111
+ try {
112
+ const content = import_fs2.default.readFileSync(settingsPath, "utf-8");
113
+ return JSON.parse(content);
114
+ } catch (error) {
115
+ console.warn(" \u26A0 Warning: Could not parse settings.json, treating as empty");
116
+ return {};
117
+ }
118
+ }
119
+ function writeClaudeSettings(settings) {
120
+ const settingsPath = getClaudeSettingsPath();
121
+ const settingsDir = import_path2.default.dirname(settingsPath);
122
+ if (!import_fs2.default.existsSync(settingsDir)) {
123
+ import_fs2.default.mkdirSync(settingsDir, { recursive: true });
124
+ }
125
+ import_fs2.default.writeFileSync(settingsPath, JSON.stringify(settings, null, 2), "utf-8");
126
+ }
127
+ function removeClaudeHooks(hooksConfig, skillName) {
128
+ const settings = readClaudeSettings();
129
+ let modified = false;
130
+ if (!settings.hooks || typeof settings.hooks !== "object") {
131
+ return false;
132
+ }
133
+ const hooks = settings.hooks;
134
+ for (const [hookType, hookMatchers] of Object.entries(hooksConfig)) {
135
+ if (!hookMatchers || !Array.isArray(hookMatchers)) {
136
+ continue;
137
+ }
138
+ if (!hooks[hookType] || !Array.isArray(hooks[hookType])) {
139
+ continue;
140
+ }
141
+ const existingHooks = hooks[hookType];
142
+ const initialLength = existingHooks.length;
143
+ const matchersToRemove = hookMatchers.map((m) => m.matcher);
144
+ const filteredHooks = existingHooks.filter(
145
+ (hook) => !matchersToRemove.includes(hook.matcher)
146
+ );
147
+ if (filteredHooks.length < initialLength) {
148
+ hooks[hookType] = filteredHooks;
149
+ modified = true;
150
+ console.log(` \u2713 Removed ${hookType} hook for ${skillName}`);
151
+ }
152
+ }
153
+ if (modified) {
154
+ writeClaudeSettings(settings);
155
+ }
156
+ return modified;
157
+ }
158
+
99
159
  // shared/src/uninstall-skill.ts
100
160
  function updateManifest(skillsDir, config) {
101
- const manifestPath = import_path2.default.join(skillsDir, ".skills-manifest.json");
102
- if (!import_fs2.default.existsSync(manifestPath)) {
161
+ const manifestPath = import_path3.default.join(skillsDir, ".skills-manifest.json");
162
+ if (!import_fs3.default.existsSync(manifestPath)) {
103
163
  return;
104
164
  }
105
165
  try {
106
- const manifest = JSON.parse(import_fs2.default.readFileSync(manifestPath, "utf-8"));
166
+ const manifest = JSON.parse(import_fs3.default.readFileSync(manifestPath, "utf-8"));
107
167
  if (manifest.skills && manifest.skills[config.name]) {
108
168
  delete manifest.skills[config.name];
109
- import_fs2.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
169
+ import_fs3.default.writeFileSync(manifestPath, JSON.stringify(manifest, null, 2));
110
170
  console.log(" \u2713 Updated manifest");
111
171
  }
112
172
  } catch (error) {
@@ -115,25 +175,38 @@ function updateManifest(skillsDir, config) {
115
175
  }
116
176
  }
117
177
  function uninstallFromTarget(target, config) {
178
+ var _a;
118
179
  console.log(`
119
180
  \u{1F5D1}\uFE0F Uninstalling from ${target.name}...`);
120
181
  const isGlobal = isGlobalInstall();
121
182
  const location = detectInstallLocation(target.paths, isGlobal);
122
183
  const skillName = extractSkillName(config.name);
123
- const skillNameTargetDir = import_path2.default.join(location.base, skillName);
124
- const fullPackageNameTargetDir = import_path2.default.join(location.base, config.name);
184
+ const skillNameTargetDir = import_path3.default.join(location.base, skillName);
185
+ const fullPackageNameTargetDir = import_path3.default.join(location.base, config.name);
125
186
  let removed = false;
126
- if (import_fs2.default.existsSync(skillNameTargetDir)) {
187
+ if (import_fs3.default.existsSync(skillNameTargetDir)) {
127
188
  removeDir(skillNameTargetDir);
128
189
  console.log(` \u2713 Removed skill directory: ${skillName}`);
129
190
  removed = true;
130
191
  }
131
- if (import_fs2.default.existsSync(fullPackageNameTargetDir) && fullPackageNameTargetDir !== skillNameTargetDir) {
192
+ if (import_fs3.default.existsSync(fullPackageNameTargetDir) && fullPackageNameTargetDir !== skillNameTargetDir) {
132
193
  removeDir(fullPackageNameTargetDir);
133
194
  console.log(` \u2713 Removed skill directory: ${config.name}`);
134
195
  removed = true;
135
196
  }
136
197
  updateManifest(location.base, config);
198
+ if (target.name === "claude-code" && removed && ((_a = config.claudeSettings) == null ? void 0 : _a.hooks)) {
199
+ try {
200
+ console.log(" \u{1F527} \u79FB\u9664 Claude Code \u94A9\u5B50...");
201
+ const skillName2 = extractSkillName(config.name);
202
+ const modified = removeClaudeHooks(config.claudeSettings.hooks, skillName2);
203
+ if (modified) {
204
+ console.log(" \u2705 \u94A9\u5B50\u5DF2\u4ECE ~/.claude/settings.json \u79FB\u9664");
205
+ }
206
+ } catch (error) {
207
+ console.warn(" \u26A0 \u8B66\u544A: \u65E0\u6CD5\u79FB\u9664\u94A9\u5B50\uFF08\u53EF\u5B89\u5168\u5FFD\u7565\uFF09");
208
+ }
209
+ }
137
210
  if (removed) {
138
211
  console.log(` \u2705 Uninstalled from ${target.name}`);
139
212
  return true;