superlab 0.1.5 → 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/bin/superlab.cjs +64 -2
- package/lib/install.cjs +159 -6
- package/package.json +1 -1
package/bin/superlab.cjs
CHANGED
|
@@ -2,7 +2,9 @@
|
|
|
2
2
|
|
|
3
3
|
const path = require("node:path");
|
|
4
4
|
const {
|
|
5
|
+
PACKAGE_VERSION,
|
|
5
6
|
detectLanguage,
|
|
7
|
+
getProjectVersionInfo,
|
|
6
8
|
installSuperlab,
|
|
7
9
|
updateAllProjects,
|
|
8
10
|
updateSuperlabProject,
|
|
@@ -17,11 +19,13 @@ Usage:
|
|
|
17
19
|
superlab install [--target <dir>] [--platform codex|claude|both|all] [--lang en|zh] [--force]
|
|
18
20
|
superlab update [--target <dir>]
|
|
19
21
|
superlab update --all-projects
|
|
22
|
+
superlab version [--target <dir>] [--global|--project]
|
|
20
23
|
|
|
21
24
|
Commands:
|
|
22
25
|
init Initialize /lab commands, skills, templates, and scripts in a target
|
|
23
26
|
install Backward-compatible alias for init
|
|
24
27
|
update Refresh an initialized project or all registered projects
|
|
28
|
+
version Show installed CLI version and project asset version
|
|
25
29
|
help Show this help message
|
|
26
30
|
`);
|
|
27
31
|
}
|
|
@@ -89,6 +93,56 @@ function parseUpdateArgs(argv) {
|
|
|
89
93
|
return options;
|
|
90
94
|
}
|
|
91
95
|
|
|
96
|
+
function parseVersionArgs(argv) {
|
|
97
|
+
const options = {
|
|
98
|
+
targetDir: process.cwd(),
|
|
99
|
+
globalOnly: false,
|
|
100
|
+
projectOnly: false,
|
|
101
|
+
};
|
|
102
|
+
|
|
103
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
104
|
+
const value = argv[index];
|
|
105
|
+
if (value === "--target") {
|
|
106
|
+
options.targetDir = path.resolve(argv[index + 1]);
|
|
107
|
+
index += 1;
|
|
108
|
+
} else if (value === "--global") {
|
|
109
|
+
options.globalOnly = true;
|
|
110
|
+
} else if (value === "--project") {
|
|
111
|
+
options.projectOnly = true;
|
|
112
|
+
} else {
|
|
113
|
+
throw new Error(`Unknown option: ${value}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (options.globalOnly && options.projectOnly) {
|
|
118
|
+
throw new Error("Use either --global or --project, not both.");
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
return options;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
function printVersion(options) {
|
|
125
|
+
const lines = [];
|
|
126
|
+
if (!options.projectOnly) {
|
|
127
|
+
lines.push(`cli: ${PACKAGE_VERSION}`);
|
|
128
|
+
}
|
|
129
|
+
if (!options.globalOnly) {
|
|
130
|
+
const projectInfo = getProjectVersionInfo({ targetDir: options.targetDir });
|
|
131
|
+
if (projectInfo.status === "managed") {
|
|
132
|
+
lines.push(`project: ${projectInfo.package_version}`);
|
|
133
|
+
lines.push(`platform: ${projectInfo.platform}`);
|
|
134
|
+
lines.push(`language: ${projectInfo.lang}`);
|
|
135
|
+
} else if (projectInfo.status === "legacy") {
|
|
136
|
+
lines.push("project: legacy");
|
|
137
|
+
lines.push(`platform: ${projectInfo.platform}`);
|
|
138
|
+
lines.push(`language: ${projectInfo.lang}`);
|
|
139
|
+
} else {
|
|
140
|
+
lines.push("project: not initialized");
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
console.log(lines.join("\n"));
|
|
144
|
+
}
|
|
145
|
+
|
|
92
146
|
function shouldUseInteractiveInit(options) {
|
|
93
147
|
if (options.lang && options.platform) {
|
|
94
148
|
return false;
|
|
@@ -140,10 +194,15 @@ async function main() {
|
|
|
140
194
|
return;
|
|
141
195
|
}
|
|
142
196
|
|
|
143
|
-
if (!["init", "install", "update"].includes(command)) {
|
|
197
|
+
if (!["init", "install", "update", "version"].includes(command)) {
|
|
144
198
|
throw new Error(`Unknown command: ${command}`);
|
|
145
199
|
}
|
|
146
200
|
|
|
201
|
+
if (command === "version") {
|
|
202
|
+
printVersion(parseVersionArgs(rest));
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
|
|
147
206
|
if (command === "update") {
|
|
148
207
|
const options = parseUpdateArgs(rest);
|
|
149
208
|
if (options.allProjects) {
|
|
@@ -162,6 +221,9 @@ async function main() {
|
|
|
162
221
|
console.log(`superlab updated in ${options.targetDir}`);
|
|
163
222
|
console.log(`platform: ${metadata.platform}`);
|
|
164
223
|
console.log(`language: ${metadata.lang}`);
|
|
224
|
+
if (metadata.migration) {
|
|
225
|
+
console.log(`migration: ${metadata.migration}`);
|
|
226
|
+
}
|
|
165
227
|
return;
|
|
166
228
|
}
|
|
167
229
|
|
|
@@ -170,7 +232,7 @@ async function main() {
|
|
|
170
232
|
if (options.platform === "all") {
|
|
171
233
|
options.platform = "both";
|
|
172
234
|
}
|
|
173
|
-
installSuperlab(options);
|
|
235
|
+
installSuperlab({ ...options, registerProject: true });
|
|
174
236
|
console.log(`superlab installed into ${options.targetDir}`);
|
|
175
237
|
console.log(`platform: ${options.platform}`);
|
|
176
238
|
console.log(`language: ${options.lang}`);
|
package/lib/install.cjs
CHANGED
|
@@ -137,6 +137,76 @@ function readProjectInstallMetadata(targetDir) {
|
|
|
137
137
|
return JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
138
138
|
}
|
|
139
139
|
|
|
140
|
+
function detectLegacyPlatform(targetDir) {
|
|
141
|
+
const hasCodex =
|
|
142
|
+
fs.existsSync(path.join(targetDir, ".codex", "prompts")) ||
|
|
143
|
+
fs.existsSync(path.join(targetDir, ".codex", "skills", "lab"));
|
|
144
|
+
const hasClaude =
|
|
145
|
+
fs.existsSync(path.join(targetDir, ".claude", "commands", "lab")) ||
|
|
146
|
+
fs.existsSync(path.join(targetDir, ".claude", "skills", "lab"));
|
|
147
|
+
|
|
148
|
+
if (hasCodex && hasClaude) {
|
|
149
|
+
return "both";
|
|
150
|
+
}
|
|
151
|
+
if (hasCodex) {
|
|
152
|
+
return "codex";
|
|
153
|
+
}
|
|
154
|
+
if (hasClaude) {
|
|
155
|
+
return "claude";
|
|
156
|
+
}
|
|
157
|
+
return null;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function looksChinese(text) {
|
|
161
|
+
return /[\u3400-\u9fff]/.test(text);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function detectLegacyLanguage(targetDir) {
|
|
165
|
+
const workflowConfigPath = path.join(targetDir, ".superlab", "config", "workflow.json");
|
|
166
|
+
if (fs.existsSync(workflowConfigPath)) {
|
|
167
|
+
try {
|
|
168
|
+
const config = JSON.parse(fs.readFileSync(workflowConfigPath, "utf8"));
|
|
169
|
+
if (config.workflow_language === "zh" || config.workflow_language === "en") {
|
|
170
|
+
return config.workflow_language;
|
|
171
|
+
}
|
|
172
|
+
} catch {
|
|
173
|
+
// Fall through to file-content heuristics.
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
const probeFiles = [
|
|
178
|
+
path.join(targetDir, ".codex", "prompts", "lab-idea.md"),
|
|
179
|
+
path.join(targetDir, ".claude", "commands", "lab", "idea.md"),
|
|
180
|
+
path.join(targetDir, ".superlab", "templates", "idea.md"),
|
|
181
|
+
];
|
|
182
|
+
|
|
183
|
+
for (const probeFile of probeFiles) {
|
|
184
|
+
if (!fs.existsSync(probeFile)) {
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
const content = fs.readFileSync(probeFile, "utf8");
|
|
188
|
+
return looksChinese(content) ? "zh" : "en";
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
return "en";
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function detectLegacyInstallMetadata(targetDir) {
|
|
195
|
+
const platform = detectLegacyPlatform(targetDir);
|
|
196
|
+
if (!platform) {
|
|
197
|
+
return null;
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
return {
|
|
201
|
+
target_dir: path.resolve(targetDir),
|
|
202
|
+
platform,
|
|
203
|
+
lang: detectLegacyLanguage(targetDir),
|
|
204
|
+
package_version: "legacy",
|
|
205
|
+
updated_at: null,
|
|
206
|
+
legacy: true,
|
|
207
|
+
};
|
|
208
|
+
}
|
|
209
|
+
|
|
140
210
|
function readRegistry({ env = process.env } = {}) {
|
|
141
211
|
const filePath = registryFilePath({ env });
|
|
142
212
|
if (!fs.existsSync(filePath)) {
|
|
@@ -170,6 +240,16 @@ function registerProjectInstall(targetDir, metadata, { env = process.env } = {})
|
|
|
170
240
|
writeRegistry(registry, { env });
|
|
171
241
|
}
|
|
172
242
|
|
|
243
|
+
function isTemporaryTestPath(targetDir) {
|
|
244
|
+
const resolvedTarget = path.resolve(targetDir);
|
|
245
|
+
const tmpRoot = path.resolve(os.tmpdir());
|
|
246
|
+
const relativeToTmp = path.relative(tmpRoot, resolvedTarget);
|
|
247
|
+
if (relativeToTmp.startsWith("..") || path.isAbsolute(relativeToTmp)) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
return path.basename(resolvedTarget).startsWith("superlab-");
|
|
251
|
+
}
|
|
252
|
+
|
|
173
253
|
function detectLanguage({ explicitLang, env = process.env } = {}) {
|
|
174
254
|
if (explicitLang) {
|
|
175
255
|
if (!["en", "zh"].includes(explicitLang)) {
|
|
@@ -243,7 +323,14 @@ function localizeInstalledAssets(targetDir, lang) {
|
|
|
243
323
|
}
|
|
244
324
|
}
|
|
245
325
|
|
|
246
|
-
function installSuperlab({
|
|
326
|
+
function installSuperlab({
|
|
327
|
+
targetDir,
|
|
328
|
+
platform = "both",
|
|
329
|
+
force = false,
|
|
330
|
+
lang,
|
|
331
|
+
env = process.env,
|
|
332
|
+
registerProject = false,
|
|
333
|
+
} = {}) {
|
|
247
334
|
const groups = [];
|
|
248
335
|
if (platform === "codex" || platform === "both") {
|
|
249
336
|
groups.push(...ASSET_GROUPS.codex);
|
|
@@ -267,46 +354,112 @@ function installSuperlab({ targetDir, platform = "both", force = false, lang, en
|
|
|
267
354
|
updated_at: new Date().toISOString(),
|
|
268
355
|
};
|
|
269
356
|
writeProjectInstallMetadata(targetDir, metadata);
|
|
270
|
-
|
|
357
|
+
if (registerProject) {
|
|
358
|
+
registerProjectInstall(targetDir, metadata, { env });
|
|
359
|
+
}
|
|
360
|
+
return metadata;
|
|
271
361
|
}
|
|
272
362
|
|
|
273
363
|
function updateSuperlabProject({ targetDir, env = process.env } = {}) {
|
|
274
|
-
|
|
275
|
-
|
|
364
|
+
let metadata;
|
|
365
|
+
let migratedLegacy = false;
|
|
366
|
+
try {
|
|
367
|
+
metadata = readProjectInstallMetadata(targetDir);
|
|
368
|
+
} catch (error) {
|
|
369
|
+
metadata = detectLegacyInstallMetadata(targetDir);
|
|
370
|
+
if (!metadata) {
|
|
371
|
+
throw error;
|
|
372
|
+
}
|
|
373
|
+
migratedLegacy = true;
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const installedMetadata = installSuperlab({
|
|
276
377
|
targetDir,
|
|
277
378
|
platform: metadata.platform,
|
|
278
379
|
lang: metadata.lang,
|
|
279
380
|
force: true,
|
|
280
381
|
env,
|
|
382
|
+
registerProject: true,
|
|
281
383
|
});
|
|
282
|
-
|
|
384
|
+
if (migratedLegacy) {
|
|
385
|
+
installedMetadata.migration = "legacy project metadata reconstructed";
|
|
386
|
+
}
|
|
387
|
+
return installedMetadata;
|
|
283
388
|
}
|
|
284
389
|
|
|
285
390
|
function updateAllProjects({ env = process.env } = {}) {
|
|
286
391
|
const registry = readRegistry({ env });
|
|
287
392
|
const updated = [];
|
|
288
393
|
const skipped = [];
|
|
394
|
+
const retained = [];
|
|
289
395
|
|
|
290
396
|
for (const project of registry.projects) {
|
|
291
397
|
try {
|
|
398
|
+
if (isTemporaryTestPath(project.path)) {
|
|
399
|
+
skipped.push({ path: project.path, reason: "temporary path pruned from registry" });
|
|
400
|
+
continue;
|
|
401
|
+
}
|
|
292
402
|
if (!fs.existsSync(project.path)) {
|
|
293
403
|
skipped.push({ path: project.path, reason: "missing project path" });
|
|
294
404
|
continue;
|
|
295
405
|
}
|
|
296
|
-
updateSuperlabProject({ targetDir: project.path, env });
|
|
406
|
+
const metadata = updateSuperlabProject({ targetDir: project.path, env });
|
|
297
407
|
updated.push(project.path);
|
|
408
|
+
retained.push({
|
|
409
|
+
path: path.resolve(project.path),
|
|
410
|
+
platform: metadata.platform,
|
|
411
|
+
lang: metadata.lang,
|
|
412
|
+
package_version: metadata.package_version,
|
|
413
|
+
updated_at: metadata.updated_at,
|
|
414
|
+
});
|
|
298
415
|
} catch (error) {
|
|
299
416
|
skipped.push({ path: project.path, reason: error.message });
|
|
417
|
+
if (fs.existsSync(project.path)) {
|
|
418
|
+
retained.push(project);
|
|
419
|
+
}
|
|
300
420
|
}
|
|
301
421
|
}
|
|
302
422
|
|
|
423
|
+
writeRegistry({ projects: retained }, { env });
|
|
303
424
|
return { updated, skipped };
|
|
304
425
|
}
|
|
305
426
|
|
|
427
|
+
function getProjectVersionInfo({ targetDir } = {}) {
|
|
428
|
+
try {
|
|
429
|
+
const metadata = readProjectInstallMetadata(targetDir);
|
|
430
|
+
return {
|
|
431
|
+
status: "managed",
|
|
432
|
+
package_version: metadata.package_version,
|
|
433
|
+
platform: metadata.platform,
|
|
434
|
+
lang: metadata.lang,
|
|
435
|
+
};
|
|
436
|
+
} catch {
|
|
437
|
+
const legacyMetadata = detectLegacyInstallMetadata(targetDir);
|
|
438
|
+
if (!legacyMetadata) {
|
|
439
|
+
return {
|
|
440
|
+
status: "missing",
|
|
441
|
+
package_version: null,
|
|
442
|
+
platform: null,
|
|
443
|
+
lang: null,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
return {
|
|
447
|
+
status: "legacy",
|
|
448
|
+
package_version: "legacy",
|
|
449
|
+
platform: legacyMetadata.platform,
|
|
450
|
+
lang: legacyMetadata.lang,
|
|
451
|
+
};
|
|
452
|
+
}
|
|
453
|
+
}
|
|
454
|
+
|
|
306
455
|
module.exports = {
|
|
456
|
+
PACKAGE_VERSION,
|
|
307
457
|
detectLanguage,
|
|
458
|
+
detectLegacyInstallMetadata,
|
|
308
459
|
installSuperlab,
|
|
309
460
|
installMetadataPath,
|
|
461
|
+
isTemporaryTestPath,
|
|
462
|
+
getProjectVersionInfo,
|
|
310
463
|
readProjectInstallMetadata,
|
|
311
464
|
registryFilePath,
|
|
312
465
|
readRegistry,
|