@khanhcan148/mk 0.1.4 → 0.1.6
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/package.json +1 -1
- package/src/commands/init.js +29 -8
- package/src/commands/update.js +41 -12
package/package.json
CHANGED
package/src/commands/init.js
CHANGED
|
@@ -10,6 +10,7 @@ import { MANIFEST_FILENAME } from '../lib/constants.js';
|
|
|
10
10
|
import { resolveTokenOrLogin } from '../lib/auth.js';
|
|
11
11
|
import { writeToken, readStoredToken } from '../lib/config.js';
|
|
12
12
|
import { downloadAndExtractKit, cleanupTempDir } from '../lib/download.js';
|
|
13
|
+
import { fetchLatestRelease } from '../lib/releases.js';
|
|
13
14
|
|
|
14
15
|
/**
|
|
15
16
|
* Run the init command.
|
|
@@ -19,7 +20,8 @@ import { downloadAndExtractKit, cleanupTempDir } from '../lib/download.js';
|
|
|
19
20
|
* targetDir?: string,
|
|
20
21
|
* manifestPath?: string,
|
|
21
22
|
* scope?: 'project'|'global',
|
|
22
|
-
* dryRun?: boolean
|
|
23
|
+
* dryRun?: boolean,
|
|
24
|
+
* version?: string - Kit version to store in manifest (defaults to pkg.version when omitted)
|
|
23
25
|
* }} params
|
|
24
26
|
* @returns {Promise<{ files: Array, totalSize: number }>}
|
|
25
27
|
*/
|
|
@@ -29,7 +31,8 @@ export async function runInit(params = {}) {
|
|
|
29
31
|
targetDir = resolveTargetDir({ global: false }),
|
|
30
32
|
manifestPath = resolveManifestPath({ global: false }),
|
|
31
33
|
scope = 'project',
|
|
32
|
-
dryRun = false
|
|
34
|
+
dryRun = false,
|
|
35
|
+
version: explicitVersion
|
|
33
36
|
} = params;
|
|
34
37
|
|
|
35
38
|
// Guard: abort if already installed
|
|
@@ -61,8 +64,10 @@ export async function runInit(params = {}) {
|
|
|
61
64
|
}
|
|
62
65
|
}
|
|
63
66
|
|
|
64
|
-
// Write manifest
|
|
65
|
-
|
|
67
|
+
// Write manifest.
|
|
68
|
+
// Use explicitVersion when provided (e.g. release.version from initAction);
|
|
69
|
+
// fall back to pkg.version for direct runInit calls without a known release version.
|
|
70
|
+
writeManifest(manifestPath, files, explicitVersion ?? pkg.version, scope);
|
|
66
71
|
|
|
67
72
|
const totalSize = fileList.reduce((s, f) => s + f.size, 0);
|
|
68
73
|
return { files: fileList, totalSize, fileCount: fileList.length };
|
|
@@ -81,11 +86,15 @@ export async function initAction(options = {}, deps = {}) {
|
|
|
81
86
|
downloadAndExtractKit: download = downloadAndExtractKit,
|
|
82
87
|
cleanupTempDir: cleanup = cleanupTempDir,
|
|
83
88
|
writeToken: storeToken = writeToken,
|
|
84
|
-
readStoredToken: readToken = readStoredToken
|
|
89
|
+
readStoredToken: readToken = readStoredToken,
|
|
90
|
+
fetchLatestRelease: fetchRelease = fetchLatestRelease,
|
|
91
|
+
// Injectable for tests — allows overriding resolved paths without touching CWD
|
|
92
|
+
targetDir: injectedTargetDir,
|
|
93
|
+
manifestPath: injectedManifestPath
|
|
85
94
|
} = deps;
|
|
86
95
|
|
|
87
|
-
const targetDir = resolveTargetDir(options);
|
|
88
|
-
const manifestPath = resolveManifestPath(options);
|
|
96
|
+
const targetDir = injectedTargetDir ?? resolveTargetDir(options);
|
|
97
|
+
const manifestPath = injectedManifestPath ?? resolveManifestPath(options);
|
|
89
98
|
const scope = options.global ? 'global' : 'project';
|
|
90
99
|
|
|
91
100
|
// Pre-flight: warn if target exists with files
|
|
@@ -115,11 +124,23 @@ export async function initAction(options = {}, deps = {}) {
|
|
|
115
124
|
}
|
|
116
125
|
});
|
|
117
126
|
|
|
127
|
+
// Determine installed version from latest release (more accurate than pkg.version).
|
|
128
|
+
// Falls back silently to pkg.version if no release is available (new repo, API error, etc.)
|
|
129
|
+
let installedVersion;
|
|
130
|
+
if (!options.dryRun) {
|
|
131
|
+
try {
|
|
132
|
+
const release = await fetchRelease(token);
|
|
133
|
+
installedVersion = release.available ? release.version : undefined;
|
|
134
|
+
} catch {
|
|
135
|
+
// Ignore errors — version fallback handled by runInit
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
118
139
|
process.stdout.write('Downloading kit from GitHub...\n');
|
|
119
140
|
tempDir = await download(token);
|
|
120
141
|
const sourceDir = join(tempDir, '.claude');
|
|
121
142
|
|
|
122
|
-
const result = await runInit({ sourceDir, targetDir, manifestPath, scope, dryRun: options.dryRun });
|
|
143
|
+
const result = await runInit({ sourceDir, targetDir, manifestPath, scope, dryRun: options.dryRun, version: installedVersion });
|
|
123
144
|
|
|
124
145
|
if (options.dryRun) {
|
|
125
146
|
process.stdout.write(`Would install ${result.files.length} files:\n`);
|
package/src/commands/update.js
CHANGED
|
@@ -40,7 +40,8 @@ async function defaultPromptUser(question) {
|
|
|
40
40
|
* sourceDir?: string,
|
|
41
41
|
* targetDir?: string,
|
|
42
42
|
* manifestPath?: string,
|
|
43
|
-
* force?: boolean
|
|
43
|
+
* force?: boolean,
|
|
44
|
+
* version?: string - Kit version to store in manifest (defaults to pkg.version when omitted)
|
|
44
45
|
* }} params
|
|
45
46
|
* @returns {Promise<{ updated: string[], added: string[], removed: string[], conflicts: string[], unchanged: string[], upToDate: boolean }>}
|
|
46
47
|
*/
|
|
@@ -49,7 +50,8 @@ export async function runUpdate(params = {}) {
|
|
|
49
50
|
sourceDir = resolveSourceDir(),
|
|
50
51
|
targetDir = resolveTargetDir({ global: false }),
|
|
51
52
|
manifestPath = resolveManifestPath({ global: false }),
|
|
52
|
-
force = false
|
|
53
|
+
force = false,
|
|
54
|
+
version: explicitVersion
|
|
53
55
|
} = params;
|
|
54
56
|
|
|
55
57
|
// Read existing manifest
|
|
@@ -96,13 +98,19 @@ export async function runUpdate(params = {}) {
|
|
|
96
98
|
diff.removed.length === 0 &&
|
|
97
99
|
diff.conflicts.length === 0;
|
|
98
100
|
|
|
101
|
+
// Read package version (fileURLToPath handles Windows drive-letter prefix correctly)
|
|
102
|
+
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8'));
|
|
103
|
+
|
|
99
104
|
if (upToDate) {
|
|
105
|
+
// Files are unchanged but we may still need to record the release version.
|
|
106
|
+
// Without this, manifest.version stays at the old value and the next `mk update`
|
|
107
|
+
// will always report "Update available" even though nothing changed on disk.
|
|
108
|
+
if (explicitVersion && explicitVersion !== manifest.version) {
|
|
109
|
+
updateManifest(manifestPath, manifest.files, explicitVersion);
|
|
110
|
+
}
|
|
100
111
|
return { ...diff, upToDate: true };
|
|
101
112
|
}
|
|
102
113
|
|
|
103
|
-
// Read package version (fileURLToPath handles Windows drive-letter prefix correctly)
|
|
104
|
-
const pkg = JSON.parse(readFileSync(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8'));
|
|
105
|
-
|
|
106
114
|
const newFiles = { ...manifest.files };
|
|
107
115
|
|
|
108
116
|
/**
|
|
@@ -159,8 +167,10 @@ export async function runUpdate(params = {}) {
|
|
|
159
167
|
delete newFiles[relPath];
|
|
160
168
|
}
|
|
161
169
|
|
|
162
|
-
// Update manifest with new file map
|
|
163
|
-
|
|
170
|
+
// Update manifest with new file map.
|
|
171
|
+
// Use explicitVersion when provided (e.g. release.version from updateAction);
|
|
172
|
+
// fall back to pkg.version for direct runUpdate calls or main-branch fallback downloads.
|
|
173
|
+
updateManifest(manifestPath, newFiles, explicitVersion ?? pkg.version);
|
|
164
174
|
|
|
165
175
|
return {
|
|
166
176
|
updated: diff.updated,
|
|
@@ -190,16 +200,18 @@ export async function updateAction(options = {}, deps = {}) {
|
|
|
190
200
|
readStoredToken: readToken = readStoredToken,
|
|
191
201
|
fetchLatestRelease: fetchRelease = fetchLatestRelease,
|
|
192
202
|
compareVersions: cmpVersions = compareVersions,
|
|
193
|
-
promptUser = defaultPromptUser
|
|
203
|
+
promptUser = defaultPromptUser,
|
|
204
|
+
// Injectable for tests — allows overriding resolved paths without touching CWD
|
|
205
|
+
manifestPath: injectedManifestPath
|
|
194
206
|
} = deps;
|
|
195
207
|
|
|
196
|
-
// Read local package version
|
|
208
|
+
// Read local package version (used as fallback when manifest has no version)
|
|
197
209
|
const pkg = JSON.parse(
|
|
198
210
|
readFileSync(fileURLToPath(new URL('../../package.json', import.meta.url)), 'utf8')
|
|
199
211
|
);
|
|
200
212
|
|
|
201
213
|
const targetDir = resolveTargetDir(options);
|
|
202
|
-
const manifestPath = resolveManifestPath(options);
|
|
214
|
+
const manifestPath = injectedManifestPath ?? resolveManifestPath(options);
|
|
203
215
|
|
|
204
216
|
let tempDir = null;
|
|
205
217
|
try {
|
|
@@ -220,7 +232,20 @@ export async function updateAction(options = {}, deps = {}) {
|
|
|
220
232
|
process.stdout.write('Checking for updates...\n');
|
|
221
233
|
const release = await fetchRelease(token);
|
|
222
234
|
|
|
235
|
+
// Read installed version from manifest (more accurate than pkg.version which reflects
|
|
236
|
+
// the CLI package, not the downloaded kit files). Fall back to pkg.version if no manifest.
|
|
237
|
+
let installedVersion = pkg.version;
|
|
238
|
+
if (existsSync(manifestPath)) {
|
|
239
|
+
try {
|
|
240
|
+
const existingManifest = readManifest(manifestPath);
|
|
241
|
+
if (existingManifest?.version) installedVersion = existingManifest.version;
|
|
242
|
+
} catch {
|
|
243
|
+
// Manifest unreadable — proceed with pkg.version fallback
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
|
|
223
247
|
let downloadUrl; // undefined = use default (main branch)
|
|
248
|
+
let releaseVersion; // set when downloading from a specific release tarball
|
|
224
249
|
|
|
225
250
|
if (!release.available) {
|
|
226
251
|
// No release found or API error — warn and fall back to main branch
|
|
@@ -228,7 +253,7 @@ export async function updateAction(options = {}, deps = {}) {
|
|
|
228
253
|
chalk.yellow(`Warning: Could not check latest release (${release.reason}). Falling back to main branch.\n`)
|
|
229
254
|
);
|
|
230
255
|
} else {
|
|
231
|
-
const { needsUpdate, local, remote } = cmpVersions(
|
|
256
|
+
const { needsUpdate, local, remote } = cmpVersions(installedVersion, release.version);
|
|
232
257
|
|
|
233
258
|
if (!needsUpdate) {
|
|
234
259
|
process.stdout.write(chalk.green(`Already up to date (v${local}).\n`));
|
|
@@ -256,6 +281,7 @@ export async function updateAction(options = {}, deps = {}) {
|
|
|
256
281
|
}
|
|
257
282
|
|
|
258
283
|
downloadUrl = release.tarballUrl;
|
|
284
|
+
releaseVersion = release.version;
|
|
259
285
|
}
|
|
260
286
|
|
|
261
287
|
// --- Download and apply ---
|
|
@@ -263,7 +289,10 @@ export async function updateAction(options = {}, deps = {}) {
|
|
|
263
289
|
tempDir = await download(token, downloadUrl !== undefined ? { url: downloadUrl } : {});
|
|
264
290
|
const sourceDir = join(tempDir, '.claude');
|
|
265
291
|
|
|
266
|
-
|
|
292
|
+
// Pass releaseVersion so runUpdate stores it in the manifest.
|
|
293
|
+
// When falling back to main branch (no release), releaseVersion is undefined and
|
|
294
|
+
// runUpdate falls back to pkg.version — which is the correct behaviour for that path.
|
|
295
|
+
const result = await runUpdate({ sourceDir, targetDir, manifestPath, force: options.force, version: releaseVersion });
|
|
267
296
|
|
|
268
297
|
if (result.upToDate) {
|
|
269
298
|
process.stdout.write(chalk.green('Already up to date.\n'));
|