@trendai-crem/claude-skills 0.7.2 → 0.9.0

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 (63) hide show
  1. package/cli.js +226 -25
  2. package/marketplace.json +14 -0
  3. package/package.json +2 -1
  4. package/skills/atlassian-tools/SKILL.md +0 -361
  5. package/skills/atlassian-tools/__pycache__/storage_utils.cpython-313.pyc +0 -0
  6. package/skills/atlassian-tools/confluence_cli.py +0 -796
  7. package/skills/atlassian-tools/confluence_client/README.md +0 -154
  8. package/skills/atlassian-tools/confluence_client/__init__.py +0 -44
  9. package/skills/atlassian-tools/confluence_client/__pycache__/__init__.cpython-313.pyc +0 -0
  10. package/skills/atlassian-tools/confluence_client/__pycache__/client.cpython-313.pyc +0 -0
  11. package/skills/atlassian-tools/confluence_client/__pycache__/commands.cpython-313.pyc +0 -0
  12. package/skills/atlassian-tools/confluence_client/__pycache__/converter.cpython-313.pyc +0 -0
  13. package/skills/atlassian-tools/confluence_client/__pycache__/exceptions.cpython-313.pyc +0 -0
  14. package/skills/atlassian-tools/confluence_client/__pycache__/storage.cpython-313.pyc +0 -0
  15. package/skills/atlassian-tools/confluence_client/client.py +0 -577
  16. package/skills/atlassian-tools/confluence_client/commands.py +0 -613
  17. package/skills/atlassian-tools/confluence_client/converter.py +0 -892
  18. package/skills/atlassian-tools/confluence_client/exceptions.py +0 -52
  19. package/skills/atlassian-tools/confluence_client/storage.py +0 -718
  20. package/skills/atlassian-tools/jira_cli.py +0 -891
  21. package/skills/atlassian-tools/jira_client/README.md +0 -235
  22. package/skills/atlassian-tools/jira_client/__init__.py +0 -34
  23. package/skills/atlassian-tools/jira_client/adf_converter.py +0 -845
  24. package/skills/atlassian-tools/jira_client/client.py +0 -366
  25. package/skills/atlassian-tools/jira_client/commands.py +0 -792
  26. package/skills/atlassian-tools/jira_client/exceptions.py +0 -48
  27. package/skills/atlassian-tools/jira_client/storage.py +0 -701
  28. package/skills/atlassian-tools/pyproject.toml +0 -20
  29. package/skills/atlassian-tools/references/reference.md +0 -473
  30. package/skills/atlassian-tools/storage_utils.py +0 -29
  31. package/skills/atlassian-tools/tests/__init__.py +0 -1
  32. package/skills/atlassian-tools/tests/test_adf_converter.py +0 -1521
  33. package/skills/atlassian-tools/tests/test_converter.py +0 -1243
  34. package/skills/atlassian-tools/tests/test_storage_path.py +0 -125
  35. package/skills/cpp/SKILL.md +0 -350
  36. package/skills/cpp/references/best-practices.md +0 -299
  37. package/skills/cpp/references/formatting.md +0 -298
  38. package/skills/cpp/references/naming.md +0 -280
  39. package/skills/go/SKILL.md +0 -337
  40. package/skills/go/references/best-practices.md +0 -262
  41. package/skills/go/references/formatting.md +0 -247
  42. package/skills/go/references/naming.md +0 -286
  43. package/skills/java/SKILL.md +0 -289
  44. package/skills/java/references/best-practices.md +0 -295
  45. package/skills/java/references/formatting.md +0 -315
  46. package/skills/java/references/naming.md +0 -211
  47. package/skills/javascript/SKILL.md +0 -246
  48. package/skills/javascript/references/best-practices.md +0 -289
  49. package/skills/javascript/references/formatting.md +0 -256
  50. package/skills/javascript/references/naming.md +0 -172
  51. package/skills/python/SKILL.md +0 -250
  52. package/skills/python/references/best-practices.md +0 -275
  53. package/skills/python/references/formatting.md +0 -230
  54. package/skills/python/references/naming.md +0 -218
  55. package/skills/shell/SKILL.md +0 -239
  56. package/skills/shell/references/best-practices.md +0 -256
  57. package/skills/shell/references/formatting.md +0 -287
  58. package/skills/shell/references/naming.md +0 -202
  59. package/skills/typescript/SKILL.md +0 -262
  60. package/skills/typescript/references/best-practices.md +0 -304
  61. package/skills/typescript/references/formatting.md +0 -218
  62. package/skills/typescript/references/naming.md +0 -198
  63. package/skills/wiki-generation/SKILL.md +0 -121
package/cli.js CHANGED
@@ -38,6 +38,14 @@ const teamSkills = readdirSync(join(__dir, 'skills'), { withFileTypes: true })
38
38
 
39
39
  const teamOk = run(['skills', 'add', __dir, '--all', '-g', '-y'], 'team skills');
40
40
 
41
+ // 3. Marketplace plugins — failures are non-fatal
42
+ let marketplaceResults = [];
43
+ try {
44
+ marketplaceResults = installMarketplacePlugins();
45
+ } catch (err) {
46
+ console.error(`\nmarketplace plugin installation failed unexpectedly: ${err.message}`);
47
+ }
48
+
41
49
  // Summary
42
50
  console.log('\nResults:');
43
51
  externalResults.forEach(({ label, ok }) => console.log(` ${ok ? '✓' : '✗'} ${label}`));
@@ -46,6 +54,9 @@ if (teamOk) {
46
54
  } else {
47
55
  console.log(` ✗ team skills`);
48
56
  }
57
+ marketplaceResults.forEach(({ plugin, action, ok }) =>
58
+ console.log(` ${ok ? '✓' : '✗'} ${plugin} (${action})`)
59
+ );
49
60
 
50
61
  if (!teamOk) {
51
62
  console.error('\nFATAL: Team skills installation failed.');
@@ -57,23 +68,178 @@ if (externalFailed.length > 0) {
57
68
  console.warn(`\nWARN: ${externalFailed.length} external source(s) failed — team skills installed successfully.`);
58
69
  }
59
70
 
71
+ const marketplaceFailed = marketplaceResults.filter(r => !r.ok);
72
+ if (marketplaceFailed.length > 0) {
73
+ console.warn(`\nWARN: ${marketplaceFailed.length} marketplace plugin(s) failed — team skills installed successfully.`);
74
+ }
75
+
60
76
  // Configure update-check hooks
61
77
  setupAutoUpdate();
62
78
 
79
+ // Reads a JSON file and returns its parsed value if it is a non-null, non-array object.
80
+ // Returns `fallback` on any error (missing file, parse failure, wrong type).
81
+ function readJsonObject(filePath, fallback, label) {
82
+ if (!existsSync(filePath)) return fallback;
83
+ try {
84
+ const parsed = JSON.parse(readFileSync(filePath, 'utf8'));
85
+ if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) return parsed;
86
+ console.warn(`\nWARN: Ignoring invalid ${label}: expected JSON object`);
87
+ } catch (err) {
88
+ console.warn(`\nWARN: Ignoring invalid ${label}: ${err.message}`);
89
+ }
90
+ return fallback;
91
+ }
92
+
93
+ // Extracts the registered source URL for a marketplace from known_marketplaces.json.
94
+ // Handles both string entries and object entries with a `source` field.
95
+ function getRegisteredMarketplaceSource(known, marketplaceName) {
96
+ const entry = known[marketplaceName];
97
+ if (typeof entry === 'string') return entry;
98
+ if (entry && typeof entry === 'object' && !Array.isArray(entry)) {
99
+ return typeof entry.source === 'string' ? entry.source : null;
100
+ }
101
+ return null;
102
+ }
103
+
104
+ // Normalizes a marketplace source URL to a canonical host/path form for comparison.
105
+ // Treats SSH and HTTPS URLs pointing to the same repo as equivalent.
106
+ // Returns null if the URL cannot be parsed.
107
+ function normalizeMarketplaceSource(rawSource) {
108
+ const sshMatch = /^git@([^:]+):(.+?)(?:\.git)?$/.exec(rawSource);
109
+ if (sshMatch) {
110
+ return `${sshMatch[1].toLowerCase()}/${sshMatch[2].replace(/^\/+/, '').replace(/\.git$/, '')}`;
111
+ }
112
+ try {
113
+ const url = new URL(rawSource);
114
+ return `${url.hostname.toLowerCase()}/${url.pathname.replace(/^\/+/, '').replace(/\.git$/, '')}`;
115
+ } catch {
116
+ return null;
117
+ }
118
+ }
119
+
120
+ function installMarketplacePlugins() {
121
+ const configPath = join(__dir, 'marketplace.json');
122
+ if (!existsSync(configPath)) return [];
123
+
124
+ // Parse and validate marketplace.json — all failures are non-fatal (SCD-7)
125
+ const config = readJsonObject(configPath, null, 'marketplace.json');
126
+ const { marketplace, plugins } = config ?? {};
127
+
128
+ // Input validation + allowlisting (SCD-1, OWASP-A04)
129
+ // Leading `-` is rejected to prevent argv injection: `claude plugin install -s` parses as flag.
130
+ const SAFE_NAME = /^(?!-)[A-Za-z0-9_][A-Za-z0-9_-]*$/;
131
+ const SAFE_SOURCE = /^(git@[\w.-]+:[\w.\-/]+\.git|https:\/\/[\w.-]+\/[\w.\-/]+\.git)$/;
132
+
133
+ if (
134
+ typeof marketplace?.name !== 'string' ||
135
+ typeof marketplace?.source !== 'string' ||
136
+ !Array.isArray(plugins)
137
+ ) {
138
+ console.error('\nInvalid marketplace.json: missing required fields (marketplace.name, marketplace.source, plugins[])');
139
+ return [];
140
+ }
141
+ if (!SAFE_NAME.test(marketplace.name) || !SAFE_SOURCE.test(marketplace.source)) {
142
+ console.error('\nInvalid marketplace name or source URL in marketplace.json');
143
+ return [];
144
+ }
145
+ // Reject the entire config on any invalid plugin name (fail-closed, not silent filter).
146
+ const invalidPlugins = plugins.filter(p => typeof p !== 'string' || !SAFE_NAME.test(p));
147
+ if (invalidPlugins.length > 0) {
148
+ console.error(`\nInvalid plugin names in marketplace.json: ${invalidPlugins.map(p => JSON.stringify(p)).join(', ')}`);
149
+ return [];
150
+ }
151
+ const validPlugins = plugins;
152
+
153
+ const { name: marketplaceName, source } = marketplace;
154
+ console.log(`\nInstalling marketplace plugins (${marketplaceName})...\n`);
155
+
156
+ // Register marketplace if not already known — verify source matches to prevent supply-chain mismatch (OWASP-A08)
157
+ const knownPath = join(homedir(), '.claude', 'plugins', 'known_marketplaces.json');
158
+ // Distinguish "file missing" (first-run, safe) from "file corrupted" (fail-closed).
159
+ const knownExists = existsSync(knownPath);
160
+ const known = readJsonObject(knownPath, knownExists ? null : {}, 'known_marketplaces.json');
161
+ if (knownExists && known === null) {
162
+ console.error('\nInvalid known_marketplaces.json: refusing to register marketplace automatically');
163
+ return validPlugins.map(plugin => ({ plugin, action: 'skipped (invalid marketplace registry)', ok: false }));
164
+ }
165
+ const registeredSource = getRegisteredMarketplaceSource(known, marketplaceName);
166
+ const normalizedSource = normalizeMarketplaceSource(source);
167
+ const normalizedRegistered = registeredSource ? normalizeMarketplaceSource(registeredSource) : null;
168
+
169
+ if (registeredSource && (!normalizedRegistered || normalizedRegistered !== normalizedSource)) {
170
+ console.error(`\nMarketplace source mismatch for ${marketplaceName}: registered as ${registeredSource}, but marketplace.json specifies ${source}`);
171
+ return validPlugins.map(plugin => ({ plugin, action: 'skipped (source mismatch)', ok: false }));
172
+ }
173
+
174
+ if (!registeredSource) {
175
+ try {
176
+ execFileSync('claude', ['plugin', 'marketplace', 'add', source, '--scope', 'user'], {
177
+ stdio: 'inherit',
178
+ });
179
+ } catch (error) {
180
+ const exitInfo = error.status != null ? `exit ${error.status}` : error.code ?? error.message;
181
+ console.error(`\nFailed to register marketplace: ${marketplaceName} (${exitInfo})`);
182
+ return validPlugins.map(plugin => ({ plugin, action: 'skipped (no marketplace)', ok: false }));
183
+ }
184
+ }
185
+
186
+ // Always update marketplace to pull latest plugin versions before install/update
187
+ try {
188
+ execFileSync('claude', ['plugin', 'marketplace', 'update', marketplaceName], { stdio: 'inherit' });
189
+ } catch (error) {
190
+ const exitInfo = error.status != null ? `exit ${error.status}` : error.code ?? error.message;
191
+ console.warn(`\nWARN: Failed to update marketplace ${marketplaceName} (${exitInfo}) — using cached version`);
192
+ }
193
+
194
+ // Load installed plugin registry (SCD-7)
195
+ const installedPath = join(homedir(), '.claude', 'plugins', 'installed_plugins.json');
196
+ const installedData = readJsonObject(installedPath, {}, 'installed_plugins.json');
197
+ // installed_plugins.json v2 nests under "plugins", v1 is flat
198
+ const rawInstalled = installedData.plugins ?? installedData;
199
+ const installed = (typeof rawInstalled === 'object' && rawInstalled !== null && !Array.isArray(rawInstalled))
200
+ ? rawInstalled
201
+ : {};
202
+
203
+ return validPlugins.map(plugin => {
204
+ const key = `${plugin}@${marketplaceName}`;
205
+ const isInstalled = Object.hasOwn(installed, key); // Object.hasOwn avoids prototype chain traversal (SCD-7)
206
+ const action = isInstalled ? 'update' : 'install';
207
+ const args = isInstalled
208
+ ? ['plugin', 'update', key]
209
+ : ['plugin', 'install', key, '--scope', 'user'];
210
+ try {
211
+ execFileSync('claude', args, { stdio: 'inherit' });
212
+ return { plugin, action, ok: true };
213
+ } catch (error) {
214
+ const exitInfo = error.status != null ? `exit ${error.status}` : error.code ?? error.message;
215
+ console.error(`\nFailed: ${key} ${action} (${exitInfo})`);
216
+ return { plugin, action, ok: false };
217
+ }
218
+ });
219
+ }
220
+
63
221
  function setupAutoUpdate() {
64
222
  const { version: installedVersion } = JSON.parse(
65
223
  readFileSync(join(__dir, 'package.json'), 'utf8')
66
224
  );
67
225
 
68
- const claudeDir = join(homedir(), '.claude');
69
- const hooksDir = join(claudeDir, 'hooks');
70
- const hookScript = join(hooksDir, 'auto-update-claude-skills.sh');
71
- const stampFile = join(homedir(), '.cache', 'claude-skills-version-check.json');
72
- const settingsPath = join(claudeDir, 'settings.json');
226
+ const claudeDir = join(homedir(), '.claude');
227
+ const hooksDir = join(claudeDir, 'hooks');
228
+ const cacheDir = join(homedir(), '.cache');
229
+ const installScript = join(hooksDir, 'claude-skills-install-update.sh');
230
+ const notifyScript = join(hooksDir, 'auto-update-claude-skills.sh');
231
+ const installStamp = join(cacheDir, 'claude-skills-install-check.json');
232
+ const notifyStamp = join(cacheDir, 'claude-skills-version-check.json');
233
+ const settingsPath = join(claudeDir, 'settings.json');
73
234
 
74
235
  mkdirSync(hooksDir, { recursive: true });
75
- mkdirSync(join(homedir(), '.cache'), { recursive: true });
76
- writeFileSync(hookScript, buildHookScript(stampFile, installedVersion), { mode: 0o755 });
236
+ mkdirSync(cacheDir, { recursive: true });
237
+
238
+ // SessionStart: auto-install if update available (24h throttle, synchronous)
239
+ writeFileSync(installScript, buildInstallScript(installStamp, installedVersion), { mode: 0o755 });
240
+
241
+ // UserPromptSubmit: notify only if update available (2h throttle)
242
+ writeFileSync(notifyScript, buildNotifyScript(notifyStamp, installedVersion), { mode: 0o755 });
77
243
 
78
244
  // Merge hooks into ~/.claude/settings.json (idempotent)
79
245
  let settings = {};
@@ -89,22 +255,22 @@ function setupAutoUpdate() {
89
255
 
90
256
  settings.hooks ??= {};
91
257
 
92
- // SessionStart — check once at session start
258
+ // SessionStart — auto-install update (24h throttle)
93
259
  settings.hooks.SessionStart ??= [];
94
260
  settings.hooks.SessionStart = settings.hooks.SessionStart.filter(
95
- e => !e.hooks?.some(h => typeof h.command === 'string' && h.command === hookScript)
261
+ e => !e.hooks?.some(h => typeof h.command === 'string' && h.command === installScript)
96
262
  );
97
263
  settings.hooks.SessionStart.push({
98
- hooks: [{ type: 'command', command: hookScript }]
264
+ hooks: [{ type: 'command', command: installScript }]
99
265
  });
100
266
 
101
- // UserPromptSubmit — periodic in-session check (throttled by stamp file)
267
+ // UserPromptSubmit — notify only (2h throttle)
102
268
  settings.hooks.UserPromptSubmit ??= [];
103
269
  settings.hooks.UserPromptSubmit = settings.hooks.UserPromptSubmit.filter(
104
- e => !e.hooks?.some(h => typeof h.command === 'string' && h.command === hookScript)
270
+ e => !e.hooks?.some(h => typeof h.command === 'string' && h.command === notifyScript)
105
271
  );
106
272
  settings.hooks.UserPromptSubmit.push({
107
- hooks: [{ type: 'command', command: hookScript }]
273
+ hooks: [{ type: 'command', command: notifyScript }]
108
274
  });
109
275
 
110
276
  // Atomic write
@@ -112,27 +278,24 @@ function setupAutoUpdate() {
112
278
  writeFileSync(tmpPath, JSON.stringify(settings, null, 2) + '\n');
113
279
  renameSync(tmpPath, settingsPath);
114
280
 
115
- console.log('\n✓ Update-check hooks configured (SessionStart + UserPromptSubmit)');
281
+ console.log('\n✓ Update hooks configured (SessionStart: auto-install, UserPromptSubmit: notify)');
116
282
  }
117
283
 
118
- function buildHookScript(stampFile, installedVersion) {
284
+ function buildInstallScript(stampFile, installedVersion) {
119
285
  if (!/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(installedVersion)) {
120
286
  throw new Error(`Invalid version format in package.json: ${installedVersion}`);
121
287
  }
122
-
123
288
  return `#!/usr/bin/env bash
124
- # claude-skills update check runs at session start and periodically during session.
125
- # If a newer version is available, outputs a systemMessage notification (no auto-install).
289
+ # SessionStart: auto-install claude-skills update if available (24h throttle).
126
290
  set -euo pipefail
127
291
 
128
292
  STAMP="${stampFile}"
129
293
  PACKAGE="@trendai-crem/claude-skills"
130
294
  INSTALLED="${installedVersion}"
131
- MIN_INTERVAL=$((60 * 60 * 2)) # check at most every 2h
295
+ MIN_INTERVAL=$((60 * 60 * 24)) # at most once per 24h
132
296
 
133
297
  mkdir -p "$(dirname "$STAMP")"
134
298
 
135
- # Throttle: skip if checked recently
136
299
  LAST_TS=0
137
300
  if [ -f "$STAMP" ]; then
138
301
  LAST_TS=$(python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get('ts',0))" "$STAMP" 2>/dev/null || echo 0)
@@ -142,18 +305,56 @@ fi
142
305
  NOW=$(date +%s)
143
306
  [ $(( NOW - LAST_TS )) -lt $MIN_INTERVAL ] && exit 0
144
307
 
145
- # Update timestamp
146
308
  python3 -c "import json,sys; json.dump({'ts': int(sys.argv[1])}, open(sys.argv[2],'w'))" "$NOW" "$STAMP"
147
309
 
148
- # Check latest version on npm
149
310
  LATEST=$(npm view "$PACKAGE" version 2>/dev/null || echo "")
150
311
  [ -z "$LATEST" ] && exit 0
151
312
  [ "$LATEST" = "$INSTALLED" ] && exit 0
313
+ [[ "$LATEST" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]] || exit 0
314
+
315
+ # Install update synchronously
316
+ INSTALL_DIR="$(mktemp -d)"
317
+ if npm install --prefix "$INSTALL_DIR" "$PACKAGE@$LATEST" --silent 2>/dev/null \
318
+ && node "$INSTALL_DIR/node_modules/$PACKAGE/cli.js" 2>/dev/null; then
319
+ rm -rf "$INSTALL_DIR"
320
+ python3 -c "import json,sys; print(json.dumps({'systemMessage': 'claude-skills updated: ' + sys.argv[1] + ' \u2192 ' + sys.argv[2]}))" "$INSTALLED" "$LATEST"
321
+ else
322
+ rm -rf "$INSTALL_DIR"
323
+ fi
324
+ `;
325
+ }
326
+
327
+ function buildNotifyScript(stampFile, installedVersion) {
328
+ if (!/^\d+\.\d+\.\d+(-[\w.]+)?$/.test(installedVersion)) {
329
+ throw new Error(`Invalid version format in package.json: ${installedVersion}`);
330
+ }
331
+ return `#!/usr/bin/env bash
332
+ # UserPromptSubmit: notify if claude-skills update available (2h throttle, no auto-install).
333
+ set -euo pipefail
334
+
335
+ STAMP="${stampFile}"
336
+ PACKAGE="@trendai-crem/claude-skills"
337
+ INSTALLED="${installedVersion}"
338
+ MIN_INTERVAL=$((60 * 60 * 2)) # at most every 2h
339
+
340
+ mkdir -p "$(dirname "$STAMP")"
152
341
 
153
- # Validate semver
342
+ LAST_TS=0
343
+ if [ -f "$STAMP" ]; then
344
+ LAST_TS=$(python3 -c "import json,sys; print(json.load(open(sys.argv[1])).get('ts',0))" "$STAMP" 2>/dev/null || echo 0)
345
+ fi
346
+ [[ "$LAST_TS" =~ ^[0-9]+$ ]] || LAST_TS=0
347
+
348
+ NOW=$(date +%s)
349
+ [ $(( NOW - LAST_TS )) -lt $MIN_INTERVAL ] && exit 0
350
+
351
+ python3 -c "import json,sys; json.dump({'ts': int(sys.argv[1])}, open(sys.argv[2],'w'))" "$NOW" "$STAMP"
352
+
353
+ LATEST=$(npm view "$PACKAGE" version 2>/dev/null || echo "")
354
+ [ -z "$LATEST" ] && exit 0
355
+ [ "$LATEST" = "$INSTALLED" ] && exit 0
154
356
  [[ "$LATEST" =~ ^[0-9]+\.[0-9]+\.[0-9]+(-[a-zA-Z0-9.]+)?$ ]] || exit 0
155
357
 
156
- # Notify via Claude Code systemMessage (shows in UI)
157
- python3 -c "import json,sys; print(json.dumps({'systemMessage': 'claude-skills update: ' + sys.argv[1] + ' \u2192 ' + sys.argv[2] + chr(10) + 'Run: npx @trendai-crem/claude-skills@latest'}))" "$INSTALLED" "$LATEST"
358
+ python3 -c "import json,sys; print(json.dumps({'systemMessage': 'claude-skills update available: ' + sys.argv[1] + ' \u2192 ' + sys.argv[2] + chr(10) + 'Run: npx @trendai-crem/claude-skills@latest'}))" "$INSTALLED" "$LATEST"
158
359
  `;
159
360
  }
@@ -0,0 +1,14 @@
1
+ {
2
+ "marketplace": {
3
+ "name": "ai-skill-marketplace",
4
+ "source": "git@github.com:trend-ai-taskforce/ai-skill-marketplace.git"
5
+ },
6
+ "plugins": [
7
+ "wiki-tools",
8
+ "atlassian-tools",
9
+ "google-style-guides",
10
+ "l2-automation",
11
+ "service-doc-generator",
12
+ "claude-on-teams"
13
+ ]
14
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@trendai-crem/claude-skills",
3
- "version": "0.7.2",
3
+ "version": "0.9.0",
4
4
  "description": "Claude Code skills installer for the trendai-crem team",
5
5
  "license": "UNLICENSED",
6
6
  "repository": {
@@ -12,6 +12,7 @@
12
12
  },
13
13
  "files": [
14
14
  "cli.js",
15
+ "marketplace.json",
15
16
  "skills/"
16
17
  ],
17
18
  "type": "module",