claude-cli-advanced-starter-pack 1.0.4 → 1.0.7
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 +69 -5
- package/package.json +69 -69
- package/src/commands/init.js +315 -37
- package/src/commands/setup-wizard.js +736 -110
- package/src/data/releases.json +349 -0
- package/src/utils/version-check.js +512 -0
- package/templates/commands/project-impl.template.md +320 -0
- package/templates/commands/update-check.template.md +322 -0
- package/templates/hooks/ccasp-update-check.template.js +241 -0
|
@@ -0,0 +1,512 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Version Check Utility
|
|
3
|
+
*
|
|
4
|
+
* Handles npm version checking, comparison, caching, and update state tracking.
|
|
5
|
+
* Designed for background execution with non-blocking checks.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import { execSync } from 'child_process';
|
|
9
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
|
|
10
|
+
import { join, dirname } from 'path';
|
|
11
|
+
import { fileURLToPath } from 'url';
|
|
12
|
+
|
|
13
|
+
const __filename = fileURLToPath(import.meta.url);
|
|
14
|
+
const __dirname = dirname(__filename);
|
|
15
|
+
|
|
16
|
+
// Package name for npm registry lookup
|
|
17
|
+
const PACKAGE_NAME = 'claude-cli-advanced-starter-pack';
|
|
18
|
+
|
|
19
|
+
// Cache duration: 1 hour (in milliseconds)
|
|
20
|
+
const CACHE_DURATION = 60 * 60 * 1000;
|
|
21
|
+
|
|
22
|
+
// Update notification duration: 1 day (in milliseconds)
|
|
23
|
+
const UPDATE_NOTIFICATION_DURATION = 24 * 60 * 60 * 1000;
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Get the current installed version from package.json
|
|
27
|
+
*/
|
|
28
|
+
export function getCurrentVersion() {
|
|
29
|
+
try {
|
|
30
|
+
const packagePath = join(__dirname, '..', '..', 'package.json');
|
|
31
|
+
const packageJson = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
32
|
+
return packageJson.version;
|
|
33
|
+
} catch {
|
|
34
|
+
return '0.0.0';
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Get the state file path for the current project
|
|
40
|
+
*/
|
|
41
|
+
function getStateFilePath(projectDir = process.cwd()) {
|
|
42
|
+
return join(projectDir, '.claude', 'config', 'ccasp-state.json');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load the update state from the project's .claude folder
|
|
47
|
+
*/
|
|
48
|
+
export function loadUpdateState(projectDir = process.cwd()) {
|
|
49
|
+
const statePath = getStateFilePath(projectDir);
|
|
50
|
+
|
|
51
|
+
if (!existsSync(statePath)) {
|
|
52
|
+
return {
|
|
53
|
+
lastCheckTimestamp: 0,
|
|
54
|
+
lastCheckResult: null,
|
|
55
|
+
lastSeenVersion: null,
|
|
56
|
+
dismissedVersions: [],
|
|
57
|
+
installedFeatures: [],
|
|
58
|
+
skippedFeatures: [],
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
try {
|
|
63
|
+
return JSON.parse(readFileSync(statePath, 'utf8'));
|
|
64
|
+
} catch {
|
|
65
|
+
return {
|
|
66
|
+
lastCheckTimestamp: 0,
|
|
67
|
+
lastCheckResult: null,
|
|
68
|
+
lastSeenVersion: null,
|
|
69
|
+
dismissedVersions: [],
|
|
70
|
+
installedFeatures: [],
|
|
71
|
+
skippedFeatures: [],
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Save the update state to the project's .claude folder
|
|
78
|
+
*/
|
|
79
|
+
export function saveUpdateState(state, projectDir = process.cwd()) {
|
|
80
|
+
const statePath = getStateFilePath(projectDir);
|
|
81
|
+
const configDir = dirname(statePath);
|
|
82
|
+
|
|
83
|
+
if (!existsSync(configDir)) {
|
|
84
|
+
mkdirSync(configDir, { recursive: true });
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Compare two semantic versions
|
|
92
|
+
* Returns: 1 if v1 > v2, -1 if v1 < v2, 0 if equal
|
|
93
|
+
*/
|
|
94
|
+
export function compareVersions(v1, v2) {
|
|
95
|
+
const parts1 = v1.split('.').map(Number);
|
|
96
|
+
const parts2 = v2.split('.').map(Number);
|
|
97
|
+
|
|
98
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
99
|
+
const p1 = parts1[i] || 0;
|
|
100
|
+
const p2 = parts2[i] || 0;
|
|
101
|
+
|
|
102
|
+
if (p1 > p2) return 1;
|
|
103
|
+
if (p1 < p2) return -1;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
return 0;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Check npm registry for the latest version
|
|
111
|
+
* Returns null if check fails (network error, etc.)
|
|
112
|
+
*/
|
|
113
|
+
export async function checkLatestVersion() {
|
|
114
|
+
try {
|
|
115
|
+
// Use npm view command to get latest version
|
|
116
|
+
const result = execSync(`npm view ${PACKAGE_NAME} version`, {
|
|
117
|
+
encoding: 'utf8',
|
|
118
|
+
timeout: 10000, // 10 second timeout
|
|
119
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
return result.trim();
|
|
123
|
+
} catch {
|
|
124
|
+
// Silently fail - network might be unavailable
|
|
125
|
+
return null;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Get detailed package info from npm registry
|
|
131
|
+
*/
|
|
132
|
+
export async function getPackageInfo() {
|
|
133
|
+
try {
|
|
134
|
+
const result = execSync(`npm view ${PACKAGE_NAME} --json`, {
|
|
135
|
+
encoding: 'utf8',
|
|
136
|
+
timeout: 15000,
|
|
137
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
return JSON.parse(result);
|
|
141
|
+
} catch {
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
/**
|
|
147
|
+
* Load release notes from the releases.json file
|
|
148
|
+
*/
|
|
149
|
+
export function loadReleaseNotes() {
|
|
150
|
+
try {
|
|
151
|
+
const releasesPath = join(__dirname, '..', 'data', 'releases.json');
|
|
152
|
+
if (existsSync(releasesPath)) {
|
|
153
|
+
return JSON.parse(readFileSync(releasesPath, 'utf8'));
|
|
154
|
+
}
|
|
155
|
+
} catch {
|
|
156
|
+
// Fall through to default
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
return { releases: [] };
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* Get release notes for a specific version
|
|
164
|
+
*/
|
|
165
|
+
export function getReleaseNotes(version) {
|
|
166
|
+
const { releases } = loadReleaseNotes();
|
|
167
|
+
return releases.find((r) => r.version === version) || null;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* Get all release notes since a specific version
|
|
172
|
+
*/
|
|
173
|
+
export function getReleasesSince(sinceVersion) {
|
|
174
|
+
const { releases } = loadReleaseNotes();
|
|
175
|
+
|
|
176
|
+
return releases.filter((r) => compareVersions(r.version, sinceVersion) > 0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Get new features available since a specific version
|
|
181
|
+
*/
|
|
182
|
+
export function getNewFeaturesSince(sinceVersion) {
|
|
183
|
+
const releases = getReleasesSince(sinceVersion);
|
|
184
|
+
|
|
185
|
+
const features = {
|
|
186
|
+
commands: [],
|
|
187
|
+
agents: [],
|
|
188
|
+
skills: [],
|
|
189
|
+
hooks: [],
|
|
190
|
+
other: [],
|
|
191
|
+
};
|
|
192
|
+
|
|
193
|
+
for (const release of releases) {
|
|
194
|
+
if (release.newFeatures) {
|
|
195
|
+
if (release.newFeatures.commands) {
|
|
196
|
+
features.commands.push(...release.newFeatures.commands);
|
|
197
|
+
}
|
|
198
|
+
if (release.newFeatures.agents) {
|
|
199
|
+
features.agents.push(...release.newFeatures.agents);
|
|
200
|
+
}
|
|
201
|
+
if (release.newFeatures.skills) {
|
|
202
|
+
features.skills.push(...release.newFeatures.skills);
|
|
203
|
+
}
|
|
204
|
+
if (release.newFeatures.hooks) {
|
|
205
|
+
features.hooks.push(...release.newFeatures.hooks);
|
|
206
|
+
}
|
|
207
|
+
if (release.newFeatures.other) {
|
|
208
|
+
features.other.push(...release.newFeatures.other);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
return features;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
/**
|
|
217
|
+
* Check if update notification should be shown
|
|
218
|
+
* Returns false if:
|
|
219
|
+
* - Update was dismissed and is now more than 1 day old
|
|
220
|
+
* - User has already seen this version
|
|
221
|
+
*/
|
|
222
|
+
export function shouldShowUpdateNotification(state, latestVersion) {
|
|
223
|
+
const currentVersion = getCurrentVersion();
|
|
224
|
+
|
|
225
|
+
// No update available
|
|
226
|
+
if (!latestVersion || compareVersions(latestVersion, currentVersion) <= 0) {
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// Check if this version was dismissed
|
|
231
|
+
if (state.dismissedVersions?.includes(latestVersion)) {
|
|
232
|
+
return false;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// Check if we have a cached check result with timestamp
|
|
236
|
+
if (state.lastCheckResult && state.lastCheckTimestamp) {
|
|
237
|
+
const timeSinceCheck = Date.now() - state.lastCheckTimestamp;
|
|
238
|
+
|
|
239
|
+
// If check is more than 1 day old, don't show notification
|
|
240
|
+
// (user needs to run check again to see new updates)
|
|
241
|
+
if (timeSinceCheck > UPDATE_NOTIFICATION_DURATION) {
|
|
242
|
+
return false;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
return true;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
/**
|
|
250
|
+
* Perform a version check (with caching)
|
|
251
|
+
* This is the main entry point for version checking
|
|
252
|
+
*/
|
|
253
|
+
export async function performVersionCheck(projectDir = process.cwd(), forceCheck = false) {
|
|
254
|
+
const state = loadUpdateState(projectDir);
|
|
255
|
+
const currentVersion = getCurrentVersion();
|
|
256
|
+
const now = Date.now();
|
|
257
|
+
|
|
258
|
+
// Check if we have a recent cached result
|
|
259
|
+
if (!forceCheck && state.lastCheckTimestamp && state.lastCheckResult) {
|
|
260
|
+
const timeSinceCheck = now - state.lastCheckTimestamp;
|
|
261
|
+
|
|
262
|
+
if (timeSinceCheck < CACHE_DURATION) {
|
|
263
|
+
// Return cached result
|
|
264
|
+
return {
|
|
265
|
+
currentVersion,
|
|
266
|
+
latestVersion: state.lastCheckResult.latestVersion,
|
|
267
|
+
updateAvailable: compareVersions(state.lastCheckResult.latestVersion, currentVersion) > 0,
|
|
268
|
+
cached: true,
|
|
269
|
+
shouldNotify: shouldShowUpdateNotification(state, state.lastCheckResult.latestVersion),
|
|
270
|
+
newFeatures: getNewFeaturesSince(currentVersion),
|
|
271
|
+
releaseNotes: getReleasesSince(currentVersion),
|
|
272
|
+
};
|
|
273
|
+
}
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
// Perform fresh check
|
|
277
|
+
const latestVersion = await checkLatestVersion();
|
|
278
|
+
|
|
279
|
+
if (latestVersion) {
|
|
280
|
+
// Update state with new check result
|
|
281
|
+
state.lastCheckTimestamp = now;
|
|
282
|
+
state.lastCheckResult = { latestVersion };
|
|
283
|
+
saveUpdateState(state, projectDir);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const updateAvailable = latestVersion && compareVersions(latestVersion, currentVersion) > 0;
|
|
287
|
+
|
|
288
|
+
return {
|
|
289
|
+
currentVersion,
|
|
290
|
+
latestVersion: latestVersion || state.lastCheckResult?.latestVersion || currentVersion,
|
|
291
|
+
updateAvailable,
|
|
292
|
+
cached: false,
|
|
293
|
+
shouldNotify: shouldShowUpdateNotification(state, latestVersion),
|
|
294
|
+
newFeatures: updateAvailable ? getNewFeaturesSince(currentVersion) : null,
|
|
295
|
+
releaseNotes: updateAvailable ? getReleasesSince(currentVersion) : [],
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
/**
|
|
300
|
+
* Dismiss update notification for a specific version
|
|
301
|
+
*/
|
|
302
|
+
export function dismissUpdateNotification(version, projectDir = process.cwd()) {
|
|
303
|
+
const state = loadUpdateState(projectDir);
|
|
304
|
+
|
|
305
|
+
if (!state.dismissedVersions) {
|
|
306
|
+
state.dismissedVersions = [];
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
if (!state.dismissedVersions.includes(version)) {
|
|
310
|
+
state.dismissedVersions.push(version);
|
|
311
|
+
}
|
|
312
|
+
|
|
313
|
+
state.lastSeenVersion = version;
|
|
314
|
+
saveUpdateState(state, projectDir);
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
/**
|
|
318
|
+
* Mark a feature as installed
|
|
319
|
+
*/
|
|
320
|
+
export function markFeatureInstalled(featureName, projectDir = process.cwd()) {
|
|
321
|
+
const state = loadUpdateState(projectDir);
|
|
322
|
+
|
|
323
|
+
if (!state.installedFeatures) {
|
|
324
|
+
state.installedFeatures = [];
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (!state.installedFeatures.includes(featureName)) {
|
|
328
|
+
state.installedFeatures.push(featureName);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// Remove from skipped if it was there
|
|
332
|
+
if (state.skippedFeatures) {
|
|
333
|
+
state.skippedFeatures = state.skippedFeatures.filter((f) => f !== featureName);
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
saveUpdateState(state, projectDir);
|
|
337
|
+
}
|
|
338
|
+
|
|
339
|
+
/**
|
|
340
|
+
* Mark a feature as skipped (user chose not to install)
|
|
341
|
+
*/
|
|
342
|
+
export function markFeatureSkipped(featureName, projectDir = process.cwd()) {
|
|
343
|
+
const state = loadUpdateState(projectDir);
|
|
344
|
+
|
|
345
|
+
if (!state.skippedFeatures) {
|
|
346
|
+
state.skippedFeatures = [];
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
if (!state.skippedFeatures.includes(featureName)) {
|
|
350
|
+
state.skippedFeatures.push(featureName);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
saveUpdateState(state, projectDir);
|
|
354
|
+
}
|
|
355
|
+
|
|
356
|
+
/**
|
|
357
|
+
* Get features that are available but not yet installed or skipped
|
|
358
|
+
*/
|
|
359
|
+
export function getAvailableFeatures(projectDir = process.cwd()) {
|
|
360
|
+
const state = loadUpdateState(projectDir);
|
|
361
|
+
const currentVersion = getCurrentVersion();
|
|
362
|
+
const allNewFeatures = getNewFeaturesSince('0.0.0'); // Get all features ever added
|
|
363
|
+
|
|
364
|
+
const installed = state.installedFeatures || [];
|
|
365
|
+
const skipped = state.skippedFeatures || [];
|
|
366
|
+
|
|
367
|
+
const available = {
|
|
368
|
+
commands: allNewFeatures.commands.filter((f) => !installed.includes(f.name) && !skipped.includes(f.name)),
|
|
369
|
+
agents: allNewFeatures.agents.filter((f) => !installed.includes(f.name) && !skipped.includes(f.name)),
|
|
370
|
+
skills: allNewFeatures.skills.filter((f) => !installed.includes(f.name) && !skipped.includes(f.name)),
|
|
371
|
+
hooks: allNewFeatures.hooks.filter((f) => !installed.includes(f.name) && !skipped.includes(f.name)),
|
|
372
|
+
other: allNewFeatures.other.filter((f) => !installed.includes(f.name) && !skipped.includes(f.name)),
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
return available;
|
|
376
|
+
}
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Format update notification for terminal display
|
|
380
|
+
*/
|
|
381
|
+
export function formatUpdateBanner(checkResult) {
|
|
382
|
+
if (!checkResult.updateAvailable || !checkResult.shouldNotify) {
|
|
383
|
+
return null;
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
const lines = [
|
|
387
|
+
'',
|
|
388
|
+
` ┌${'─'.repeat(60)}┐`,
|
|
389
|
+
` │ ${'🆕 UPDATE AVAILABLE'.padEnd(58)} │`,
|
|
390
|
+
` │ ${`v${checkResult.currentVersion} → v${checkResult.latestVersion}`.padEnd(58)} │`,
|
|
391
|
+
` ├${'─'.repeat(60)}┤`,
|
|
392
|
+
];
|
|
393
|
+
|
|
394
|
+
// Add summary of new features
|
|
395
|
+
if (checkResult.newFeatures) {
|
|
396
|
+
const { commands, agents, skills, hooks } = checkResult.newFeatures;
|
|
397
|
+
const featureCounts = [];
|
|
398
|
+
|
|
399
|
+
if (commands.length > 0) featureCounts.push(`${commands.length} command(s)`);
|
|
400
|
+
if (agents.length > 0) featureCounts.push(`${agents.length} agent(s)`);
|
|
401
|
+
if (skills.length > 0) featureCounts.push(`${skills.length} skill(s)`);
|
|
402
|
+
if (hooks.length > 0) featureCounts.push(`${hooks.length} hook(s)`);
|
|
403
|
+
|
|
404
|
+
if (featureCounts.length > 0) {
|
|
405
|
+
lines.push(` │ ${`New: ${featureCounts.join(', ')}`.padEnd(58)} │`);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
lines.push(` │ ${' '.repeat(58)} │`);
|
|
410
|
+
lines.push(` │ ${'Run: npm update -g claude-cli-advanced-starter-pack'.padEnd(58)} │`);
|
|
411
|
+
lines.push(` │ ${'Then: ccasp wizard → Prior Releases to add features'.padEnd(58)} │`);
|
|
412
|
+
lines.push(` └${'─'.repeat(60)}┘`);
|
|
413
|
+
lines.push('');
|
|
414
|
+
|
|
415
|
+
return lines.join('\n');
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
/**
|
|
419
|
+
* Format update notification for Claude Code CLI (markdown)
|
|
420
|
+
*/
|
|
421
|
+
export function formatUpdateMarkdown(checkResult) {
|
|
422
|
+
if (!checkResult.updateAvailable) {
|
|
423
|
+
return null;
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
let md = `## 🆕 Update Available\n\n`;
|
|
427
|
+
md += `**Current:** v${checkResult.currentVersion} → **Latest:** v${checkResult.latestVersion}\n\n`;
|
|
428
|
+
|
|
429
|
+
if (checkResult.releaseNotes && checkResult.releaseNotes.length > 0) {
|
|
430
|
+
md += `### What's New\n\n`;
|
|
431
|
+
|
|
432
|
+
for (const release of checkResult.releaseNotes) {
|
|
433
|
+
md += `#### v${release.version} (${release.date})\n`;
|
|
434
|
+
md += `${release.summary}\n\n`;
|
|
435
|
+
|
|
436
|
+
if (release.highlights && release.highlights.length > 0) {
|
|
437
|
+
for (const highlight of release.highlights) {
|
|
438
|
+
md += `- ${highlight}\n`;
|
|
439
|
+
}
|
|
440
|
+
md += '\n';
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
if (checkResult.newFeatures) {
|
|
446
|
+
const { commands, agents, skills, hooks } = checkResult.newFeatures;
|
|
447
|
+
const hasFeatures = commands.length + agents.length + skills.length + hooks.length > 0;
|
|
448
|
+
|
|
449
|
+
if (hasFeatures) {
|
|
450
|
+
md += `### New Features Available\n\n`;
|
|
451
|
+
|
|
452
|
+
if (commands.length > 0) {
|
|
453
|
+
md += `**Commands:**\n`;
|
|
454
|
+
for (const cmd of commands) {
|
|
455
|
+
md += `- \`/${cmd.name}\` - ${cmd.description}\n`;
|
|
456
|
+
}
|
|
457
|
+
md += '\n';
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
if (agents.length > 0) {
|
|
461
|
+
md += `**Agents:**\n`;
|
|
462
|
+
for (const agent of agents) {
|
|
463
|
+
md += `- \`${agent.name}\` - ${agent.description}\n`;
|
|
464
|
+
}
|
|
465
|
+
md += '\n';
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
if (skills.length > 0) {
|
|
469
|
+
md += `**Skills:**\n`;
|
|
470
|
+
for (const skill of skills) {
|
|
471
|
+
md += `- \`${skill.name}\` - ${skill.description}\n`;
|
|
472
|
+
}
|
|
473
|
+
md += '\n';
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
if (hooks.length > 0) {
|
|
477
|
+
md += `**Hooks:**\n`;
|
|
478
|
+
for (const hook of hooks) {
|
|
479
|
+
md += `- \`${hook.name}\` - ${hook.description}\n`;
|
|
480
|
+
}
|
|
481
|
+
md += '\n';
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
md += `### Update Instructions\n\n`;
|
|
487
|
+
md += `\`\`\`bash\nnpm update -g claude-cli-advanced-starter-pack\n\`\`\`\n\n`;
|
|
488
|
+
md += `After updating, run \`/update-check\` to add new features to your project.\n`;
|
|
489
|
+
|
|
490
|
+
return md;
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export default {
|
|
494
|
+
getCurrentVersion,
|
|
495
|
+
loadUpdateState,
|
|
496
|
+
saveUpdateState,
|
|
497
|
+
compareVersions,
|
|
498
|
+
checkLatestVersion,
|
|
499
|
+
getPackageInfo,
|
|
500
|
+
loadReleaseNotes,
|
|
501
|
+
getReleaseNotes,
|
|
502
|
+
getReleasesSince,
|
|
503
|
+
getNewFeaturesSince,
|
|
504
|
+
shouldShowUpdateNotification,
|
|
505
|
+
performVersionCheck,
|
|
506
|
+
dismissUpdateNotification,
|
|
507
|
+
markFeatureInstalled,
|
|
508
|
+
markFeatureSkipped,
|
|
509
|
+
getAvailableFeatures,
|
|
510
|
+
formatUpdateBanner,
|
|
511
|
+
formatUpdateMarkdown,
|
|
512
|
+
};
|