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,241 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CCASP Update Check Hook
|
|
3
|
+
*
|
|
4
|
+
* Automatically checks for npm updates when Claude Code starts.
|
|
5
|
+
* Runs on first UserPromptSubmit per session, caches results for 1 hour.
|
|
6
|
+
*
|
|
7
|
+
* Event: UserPromptSubmit
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const { execSync } = require('child_process');
|
|
11
|
+
const fs = require('fs');
|
|
12
|
+
const path = require('path');
|
|
13
|
+
|
|
14
|
+
const PACKAGE_NAME = 'claude-cli-advanced-starter-pack';
|
|
15
|
+
const CACHE_DURATION = 60 * 60 * 1000; // 1 hour
|
|
16
|
+
const STATE_FILE = '.claude/config/ccasp-state.json';
|
|
17
|
+
const SESSION_MARKER = '.claude/config/.ccasp-session-checked';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Load state from file
|
|
21
|
+
*/
|
|
22
|
+
function loadState() {
|
|
23
|
+
const statePath = path.join(process.cwd(), STATE_FILE);
|
|
24
|
+
|
|
25
|
+
if (fs.existsSync(statePath)) {
|
|
26
|
+
try {
|
|
27
|
+
return JSON.parse(fs.readFileSync(statePath, 'utf8'));
|
|
28
|
+
} catch {
|
|
29
|
+
// Return default state
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
return {
|
|
34
|
+
lastCheckTimestamp: 0,
|
|
35
|
+
lastCheckResult: null,
|
|
36
|
+
currentVersion: null,
|
|
37
|
+
latestVersion: null,
|
|
38
|
+
updateAvailable: false,
|
|
39
|
+
updateHighlights: [],
|
|
40
|
+
updateFirstDisplayed: false,
|
|
41
|
+
dismissedUntil: 0,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Save state to file
|
|
47
|
+
*/
|
|
48
|
+
function saveState(state) {
|
|
49
|
+
const statePath = path.join(process.cwd(), STATE_FILE);
|
|
50
|
+
const stateDir = path.dirname(statePath);
|
|
51
|
+
|
|
52
|
+
if (!fs.existsSync(stateDir)) {
|
|
53
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
fs.writeFileSync(statePath, JSON.stringify(state, null, 2), 'utf8');
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Get installed version
|
|
61
|
+
*/
|
|
62
|
+
function getCurrentVersion() {
|
|
63
|
+
try {
|
|
64
|
+
// Try global install first
|
|
65
|
+
const result = execSync(`npm list -g ${PACKAGE_NAME} --json 2>/dev/null`, {
|
|
66
|
+
encoding: 'utf8',
|
|
67
|
+
timeout: 5000,
|
|
68
|
+
});
|
|
69
|
+
const data = JSON.parse(result);
|
|
70
|
+
return data.dependencies?.[PACKAGE_NAME]?.version || null;
|
|
71
|
+
} catch {
|
|
72
|
+
// Try local install
|
|
73
|
+
try {
|
|
74
|
+
const pkgPath = path.join(process.cwd(), 'node_modules', PACKAGE_NAME, 'package.json');
|
|
75
|
+
if (fs.existsSync(pkgPath)) {
|
|
76
|
+
const pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8'));
|
|
77
|
+
return pkg.version;
|
|
78
|
+
}
|
|
79
|
+
} catch {
|
|
80
|
+
// Ignore
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Check npm for latest version
|
|
88
|
+
*/
|
|
89
|
+
function checkLatestVersion() {
|
|
90
|
+
try {
|
|
91
|
+
const result = execSync(`npm view ${PACKAGE_NAME} version`, {
|
|
92
|
+
encoding: 'utf8',
|
|
93
|
+
timeout: 10000,
|
|
94
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
95
|
+
});
|
|
96
|
+
return result.trim();
|
|
97
|
+
} catch {
|
|
98
|
+
return null;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
/**
|
|
103
|
+
* Get release highlights from releases.json
|
|
104
|
+
*/
|
|
105
|
+
function getReleaseHighlights(fromVersion, toVersion) {
|
|
106
|
+
try {
|
|
107
|
+
// Try to read from global install
|
|
108
|
+
const globalPath = execSync('npm root -g', { encoding: 'utf8', timeout: 5000 }).trim();
|
|
109
|
+
const releasesPath = path.join(globalPath, PACKAGE_NAME, 'src', 'data', 'releases.json');
|
|
110
|
+
|
|
111
|
+
if (fs.existsSync(releasesPath)) {
|
|
112
|
+
const releases = JSON.parse(fs.readFileSync(releasesPath, 'utf8'));
|
|
113
|
+
const highlights = [];
|
|
114
|
+
|
|
115
|
+
for (const release of releases) {
|
|
116
|
+
if (compareVersions(release.version, fromVersion) > 0 &&
|
|
117
|
+
compareVersions(release.version, toVersion) <= 0) {
|
|
118
|
+
highlights.push({
|
|
119
|
+
version: release.version,
|
|
120
|
+
summary: release.summary,
|
|
121
|
+
highlights: release.highlights || [],
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
return highlights;
|
|
127
|
+
}
|
|
128
|
+
} catch {
|
|
129
|
+
// Ignore
|
|
130
|
+
}
|
|
131
|
+
return [];
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Compare semantic versions
|
|
136
|
+
*/
|
|
137
|
+
function compareVersions(v1, v2) {
|
|
138
|
+
if (!v1 || !v2) return 0;
|
|
139
|
+
const parts1 = v1.split('.').map(Number);
|
|
140
|
+
const parts2 = v2.split('.').map(Number);
|
|
141
|
+
|
|
142
|
+
for (let i = 0; i < Math.max(parts1.length, parts2.length); i++) {
|
|
143
|
+
const p1 = parts1[i] || 0;
|
|
144
|
+
const p2 = parts2[i] || 0;
|
|
145
|
+
if (p1 > p2) return 1;
|
|
146
|
+
if (p1 < p2) return -1;
|
|
147
|
+
}
|
|
148
|
+
return 0;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Check if we've already run this session
|
|
153
|
+
*/
|
|
154
|
+
function hasCheckedThisSession() {
|
|
155
|
+
const markerPath = path.join(process.cwd(), SESSION_MARKER);
|
|
156
|
+
|
|
157
|
+
if (fs.existsSync(markerPath)) {
|
|
158
|
+
try {
|
|
159
|
+
const content = fs.readFileSync(markerPath, 'utf8');
|
|
160
|
+
const timestamp = parseInt(content, 10);
|
|
161
|
+
// Consider session valid for 4 hours
|
|
162
|
+
if (Date.now() - timestamp < 4 * 60 * 60 * 1000) {
|
|
163
|
+
return true;
|
|
164
|
+
}
|
|
165
|
+
} catch {
|
|
166
|
+
// Ignore
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
return false;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Mark session as checked
|
|
174
|
+
*/
|
|
175
|
+
function markSessionChecked() {
|
|
176
|
+
const markerPath = path.join(process.cwd(), SESSION_MARKER);
|
|
177
|
+
const markerDir = path.dirname(markerPath);
|
|
178
|
+
|
|
179
|
+
if (!fs.existsSync(markerDir)) {
|
|
180
|
+
fs.mkdirSync(markerDir, { recursive: true });
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
fs.writeFileSync(markerPath, Date.now().toString(), 'utf8');
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
/**
|
|
187
|
+
* Main hook handler
|
|
188
|
+
*/
|
|
189
|
+
module.exports = async function ccaspUpdateCheck(context) {
|
|
190
|
+
// Only run once per session
|
|
191
|
+
if (hasCheckedThisSession()) {
|
|
192
|
+
return { continue: true };
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
// Mark this session as checked
|
|
196
|
+
markSessionChecked();
|
|
197
|
+
|
|
198
|
+
// Load current state
|
|
199
|
+
const state = loadState();
|
|
200
|
+
const now = Date.now();
|
|
201
|
+
|
|
202
|
+
// Check if cache is still valid
|
|
203
|
+
if (state.lastCheckTimestamp && (now - state.lastCheckTimestamp) < CACHE_DURATION) {
|
|
204
|
+
return { continue: true };
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
// Get current version
|
|
208
|
+
const currentVersion = getCurrentVersion();
|
|
209
|
+
if (!currentVersion) {
|
|
210
|
+
return { continue: true };
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Check for latest version
|
|
214
|
+
const latestVersion = checkLatestVersion();
|
|
215
|
+
if (!latestVersion) {
|
|
216
|
+
return { continue: true };
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
// Update state
|
|
220
|
+
state.lastCheckTimestamp = now;
|
|
221
|
+
state.currentVersion = currentVersion;
|
|
222
|
+
state.latestVersion = latestVersion;
|
|
223
|
+
state.updateAvailable = compareVersions(latestVersion, currentVersion) > 0;
|
|
224
|
+
|
|
225
|
+
// Get release highlights if update available
|
|
226
|
+
if (state.updateAvailable) {
|
|
227
|
+
state.updateHighlights = getReleaseHighlights(currentVersion, latestVersion);
|
|
228
|
+
// Reset first displayed flag for new updates
|
|
229
|
+
if (state.lastSeenUpdateVersion !== latestVersion) {
|
|
230
|
+
state.updateFirstDisplayed = false;
|
|
231
|
+
state.lastSeenUpdateVersion = latestVersion;
|
|
232
|
+
}
|
|
233
|
+
} else {
|
|
234
|
+
state.updateHighlights = [];
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
// Save state
|
|
238
|
+
saveState(state);
|
|
239
|
+
|
|
240
|
+
return { continue: true };
|
|
241
|
+
};
|