@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.
- package/cli.js +226 -25
- package/marketplace.json +14 -0
- package/package.json +2 -1
- package/skills/atlassian-tools/SKILL.md +0 -361
- package/skills/atlassian-tools/__pycache__/storage_utils.cpython-313.pyc +0 -0
- package/skills/atlassian-tools/confluence_cli.py +0 -796
- package/skills/atlassian-tools/confluence_client/README.md +0 -154
- package/skills/atlassian-tools/confluence_client/__init__.py +0 -44
- package/skills/atlassian-tools/confluence_client/__pycache__/__init__.cpython-313.pyc +0 -0
- package/skills/atlassian-tools/confluence_client/__pycache__/client.cpython-313.pyc +0 -0
- package/skills/atlassian-tools/confluence_client/__pycache__/commands.cpython-313.pyc +0 -0
- package/skills/atlassian-tools/confluence_client/__pycache__/converter.cpython-313.pyc +0 -0
- package/skills/atlassian-tools/confluence_client/__pycache__/exceptions.cpython-313.pyc +0 -0
- package/skills/atlassian-tools/confluence_client/__pycache__/storage.cpython-313.pyc +0 -0
- package/skills/atlassian-tools/confluence_client/client.py +0 -577
- package/skills/atlassian-tools/confluence_client/commands.py +0 -613
- package/skills/atlassian-tools/confluence_client/converter.py +0 -892
- package/skills/atlassian-tools/confluence_client/exceptions.py +0 -52
- package/skills/atlassian-tools/confluence_client/storage.py +0 -718
- package/skills/atlassian-tools/jira_cli.py +0 -891
- package/skills/atlassian-tools/jira_client/README.md +0 -235
- package/skills/atlassian-tools/jira_client/__init__.py +0 -34
- package/skills/atlassian-tools/jira_client/adf_converter.py +0 -845
- package/skills/atlassian-tools/jira_client/client.py +0 -366
- package/skills/atlassian-tools/jira_client/commands.py +0 -792
- package/skills/atlassian-tools/jira_client/exceptions.py +0 -48
- package/skills/atlassian-tools/jira_client/storage.py +0 -701
- package/skills/atlassian-tools/pyproject.toml +0 -20
- package/skills/atlassian-tools/references/reference.md +0 -473
- package/skills/atlassian-tools/storage_utils.py +0 -29
- package/skills/atlassian-tools/tests/__init__.py +0 -1
- package/skills/atlassian-tools/tests/test_adf_converter.py +0 -1521
- package/skills/atlassian-tools/tests/test_converter.py +0 -1243
- package/skills/atlassian-tools/tests/test_storage_path.py +0 -125
- package/skills/cpp/SKILL.md +0 -350
- package/skills/cpp/references/best-practices.md +0 -299
- package/skills/cpp/references/formatting.md +0 -298
- package/skills/cpp/references/naming.md +0 -280
- package/skills/go/SKILL.md +0 -337
- package/skills/go/references/best-practices.md +0 -262
- package/skills/go/references/formatting.md +0 -247
- package/skills/go/references/naming.md +0 -286
- package/skills/java/SKILL.md +0 -289
- package/skills/java/references/best-practices.md +0 -295
- package/skills/java/references/formatting.md +0 -315
- package/skills/java/references/naming.md +0 -211
- package/skills/javascript/SKILL.md +0 -246
- package/skills/javascript/references/best-practices.md +0 -289
- package/skills/javascript/references/formatting.md +0 -256
- package/skills/javascript/references/naming.md +0 -172
- package/skills/python/SKILL.md +0 -250
- package/skills/python/references/best-practices.md +0 -275
- package/skills/python/references/formatting.md +0 -230
- package/skills/python/references/naming.md +0 -218
- package/skills/shell/SKILL.md +0 -239
- package/skills/shell/references/best-practices.md +0 -256
- package/skills/shell/references/formatting.md +0 -287
- package/skills/shell/references/naming.md +0 -202
- package/skills/typescript/SKILL.md +0 -262
- package/skills/typescript/references/best-practices.md +0 -304
- package/skills/typescript/references/formatting.md +0 -218
- package/skills/typescript/references/naming.md +0 -198
- 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
|
|
69
|
-
const hooksDir
|
|
70
|
-
const
|
|
71
|
-
const
|
|
72
|
-
const
|
|
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(
|
|
76
|
-
|
|
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 —
|
|
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 ===
|
|
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:
|
|
264
|
+
hooks: [{ type: 'command', command: installScript }]
|
|
99
265
|
});
|
|
100
266
|
|
|
101
|
-
// UserPromptSubmit —
|
|
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 ===
|
|
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:
|
|
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
|
|
281
|
+
console.log('\n✓ Update hooks configured (SessionStart: auto-install, UserPromptSubmit: notify)');
|
|
116
282
|
}
|
|
117
283
|
|
|
118
|
-
function
|
|
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
|
|
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 *
|
|
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
|
-
|
|
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
|
-
|
|
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
|
}
|
package/marketplace.json
ADDED
|
@@ -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.
|
|
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",
|