superlab 0.1.5 → 0.1.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 +26 -0
- package/README.zh-CN.md +26 -0
- package/bin/superlab.cjs +74 -2
- package/lib/install.cjs +159 -6
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,6 +22,8 @@ cd your-project
|
|
|
22
22
|
superlab init
|
|
23
23
|
```
|
|
24
24
|
|
|
25
|
+
After `init` or `update`, start a new Codex or Claude session. If the new `/lab:*` commands still do not show up, restart the app.
|
|
26
|
+
|
|
25
27
|
If the package has not been published yet, install from GitHub instead:
|
|
26
28
|
|
|
27
29
|
```bash
|
|
@@ -90,6 +92,30 @@ superlab update --all-projects
|
|
|
90
92
|
|
|
91
93
|
`superlab init` writes `.superlab/install.json` inside each project and registers the project in the user-level registry so `update --all-projects` knows what to refresh.
|
|
92
94
|
|
|
95
|
+
Legacy projects from older `superlab` versions may already contain `.codex/`, `.claude/`, or `.superlab/` assets without `.superlab/install.json`. `superlab update` now reconstructs that metadata and upgrades the project in place.
|
|
96
|
+
|
|
97
|
+
## Version
|
|
98
|
+
|
|
99
|
+
Show the CLI version and the current project asset version:
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
superlab version
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Show only the current project asset version:
|
|
106
|
+
|
|
107
|
+
```bash
|
|
108
|
+
superlab version --project
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Show a specific project:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
superlab version --target /path/to/project
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
If a project still uses an older pre-metadata installation, `superlab version` reports `project: legacy` until you run `superlab update`.
|
|
118
|
+
|
|
93
119
|
## Language
|
|
94
120
|
|
|
95
121
|
The installer chooses the display language from your system locale. If it detects Chinese locale values, it installs Chinese-facing command and skill text; otherwise it falls back to English.
|
package/README.zh-CN.md
CHANGED
|
@@ -20,6 +20,8 @@ cd your-project
|
|
|
20
20
|
superlab init
|
|
21
21
|
```
|
|
22
22
|
|
|
23
|
+
执行完 `init` 或 `update` 后,请新开一个 Codex 或 Claude 会话;如果新的 `/lab:*` 命令仍未生效,再重启应用。
|
|
24
|
+
|
|
23
25
|
如果 npm 还没发布,也可以直接从 GitHub 安装:
|
|
24
26
|
|
|
25
27
|
```bash
|
|
@@ -88,6 +90,30 @@ superlab update --all-projects
|
|
|
88
90
|
|
|
89
91
|
`superlab init` 会在项目内写入 `.superlab/install.json`,并在用户级 registry 里登记项目路径,所以 `update --all-projects` 才知道要刷新哪些项目。
|
|
90
92
|
|
|
93
|
+
较老版本的 `superlab` 项目可能已经有 `.codex/`、`.claude/` 或 `.superlab/` 资产,但还没有 `.superlab/install.json`。现在 `superlab update` 会自动重建这份 metadata,然后原地升级该项目。
|
|
94
|
+
|
|
95
|
+
## 版本查询
|
|
96
|
+
|
|
97
|
+
查看当前 CLI 版本和当前目录项目的资产版本:
|
|
98
|
+
|
|
99
|
+
```bash
|
|
100
|
+
superlab version
|
|
101
|
+
```
|
|
102
|
+
|
|
103
|
+
只看当前项目资产版本:
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
superlab version --project
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
查看指定项目:
|
|
110
|
+
|
|
111
|
+
```bash
|
|
112
|
+
superlab version --target /path/to/project
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
如果项目还是旧的 pre-metadata 安装,`superlab version` 会显示 `project: legacy`;执行一次 `superlab update` 后就会切换到正式版本号。
|
|
116
|
+
|
|
91
117
|
## 语言
|
|
92
118
|
|
|
93
119
|
安装器会根据系统 locale 自动猜测展示语言。检测到中文 locale 时会安装中文命令和技能文案;否则默认安装英文文案。
|
package/bin/superlab.cjs
CHANGED
|
@@ -2,13 +2,22 @@
|
|
|
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,
|
|
9
11
|
} = require("../lib/install.cjs");
|
|
10
12
|
const { promptSelect } = require("../lib/init_tui.cjs");
|
|
11
13
|
|
|
14
|
+
function restartReminder(lang) {
|
|
15
|
+
if (lang === "zh") {
|
|
16
|
+
return "项目资产已更新。请新开一个 Codex/Claude 会话;如果仍未生效,再重启应用。";
|
|
17
|
+
}
|
|
18
|
+
return "Project assets updated. Start a new Codex/Claude session; if changes still do not appear, restart the app.";
|
|
19
|
+
}
|
|
20
|
+
|
|
12
21
|
function printHelp() {
|
|
13
22
|
console.log(`superlab
|
|
14
23
|
|
|
@@ -17,11 +26,13 @@ Usage:
|
|
|
17
26
|
superlab install [--target <dir>] [--platform codex|claude|both|all] [--lang en|zh] [--force]
|
|
18
27
|
superlab update [--target <dir>]
|
|
19
28
|
superlab update --all-projects
|
|
29
|
+
superlab version [--target <dir>] [--global|--project]
|
|
20
30
|
|
|
21
31
|
Commands:
|
|
22
32
|
init Initialize /lab commands, skills, templates, and scripts in a target
|
|
23
33
|
install Backward-compatible alias for init
|
|
24
34
|
update Refresh an initialized project or all registered projects
|
|
35
|
+
version Show installed CLI version and project asset version
|
|
25
36
|
help Show this help message
|
|
26
37
|
`);
|
|
27
38
|
}
|
|
@@ -89,6 +100,56 @@ function parseUpdateArgs(argv) {
|
|
|
89
100
|
return options;
|
|
90
101
|
}
|
|
91
102
|
|
|
103
|
+
function parseVersionArgs(argv) {
|
|
104
|
+
const options = {
|
|
105
|
+
targetDir: process.cwd(),
|
|
106
|
+
globalOnly: false,
|
|
107
|
+
projectOnly: false,
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
111
|
+
const value = argv[index];
|
|
112
|
+
if (value === "--target") {
|
|
113
|
+
options.targetDir = path.resolve(argv[index + 1]);
|
|
114
|
+
index += 1;
|
|
115
|
+
} else if (value === "--global") {
|
|
116
|
+
options.globalOnly = true;
|
|
117
|
+
} else if (value === "--project") {
|
|
118
|
+
options.projectOnly = true;
|
|
119
|
+
} else {
|
|
120
|
+
throw new Error(`Unknown option: ${value}`);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (options.globalOnly && options.projectOnly) {
|
|
125
|
+
throw new Error("Use either --global or --project, not both.");
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return options;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function printVersion(options) {
|
|
132
|
+
const lines = [];
|
|
133
|
+
if (!options.projectOnly) {
|
|
134
|
+
lines.push(`cli: ${PACKAGE_VERSION}`);
|
|
135
|
+
}
|
|
136
|
+
if (!options.globalOnly) {
|
|
137
|
+
const projectInfo = getProjectVersionInfo({ targetDir: options.targetDir });
|
|
138
|
+
if (projectInfo.status === "managed") {
|
|
139
|
+
lines.push(`project: ${projectInfo.package_version}`);
|
|
140
|
+
lines.push(`platform: ${projectInfo.platform}`);
|
|
141
|
+
lines.push(`language: ${projectInfo.lang}`);
|
|
142
|
+
} else if (projectInfo.status === "legacy") {
|
|
143
|
+
lines.push("project: legacy");
|
|
144
|
+
lines.push(`platform: ${projectInfo.platform}`);
|
|
145
|
+
lines.push(`language: ${projectInfo.lang}`);
|
|
146
|
+
} else {
|
|
147
|
+
lines.push("project: not initialized");
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
console.log(lines.join("\n"));
|
|
151
|
+
}
|
|
152
|
+
|
|
92
153
|
function shouldUseInteractiveInit(options) {
|
|
93
154
|
if (options.lang && options.platform) {
|
|
94
155
|
return false;
|
|
@@ -140,10 +201,15 @@ async function main() {
|
|
|
140
201
|
return;
|
|
141
202
|
}
|
|
142
203
|
|
|
143
|
-
if (!["init", "install", "update"].includes(command)) {
|
|
204
|
+
if (!["init", "install", "update", "version"].includes(command)) {
|
|
144
205
|
throw new Error(`Unknown command: ${command}`);
|
|
145
206
|
}
|
|
146
207
|
|
|
208
|
+
if (command === "version") {
|
|
209
|
+
printVersion(parseVersionArgs(rest));
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
|
|
147
213
|
if (command === "update") {
|
|
148
214
|
const options = parseUpdateArgs(rest);
|
|
149
215
|
if (options.allProjects) {
|
|
@@ -155,6 +221,7 @@ async function main() {
|
|
|
155
221
|
for (const project of result.skipped) {
|
|
156
222
|
console.log(`skipped: ${project.path} (${project.reason})`);
|
|
157
223
|
}
|
|
224
|
+
console.log(restartReminder(detectLanguage()));
|
|
158
225
|
return;
|
|
159
226
|
}
|
|
160
227
|
|
|
@@ -162,6 +229,10 @@ async function main() {
|
|
|
162
229
|
console.log(`superlab updated in ${options.targetDir}`);
|
|
163
230
|
console.log(`platform: ${metadata.platform}`);
|
|
164
231
|
console.log(`language: ${metadata.lang}`);
|
|
232
|
+
if (metadata.migration) {
|
|
233
|
+
console.log(`migration: ${metadata.migration}`);
|
|
234
|
+
}
|
|
235
|
+
console.log(restartReminder(metadata.lang));
|
|
165
236
|
return;
|
|
166
237
|
}
|
|
167
238
|
|
|
@@ -170,10 +241,11 @@ async function main() {
|
|
|
170
241
|
if (options.platform === "all") {
|
|
171
242
|
options.platform = "both";
|
|
172
243
|
}
|
|
173
|
-
installSuperlab(options);
|
|
244
|
+
installSuperlab({ ...options, registerProject: true });
|
|
174
245
|
console.log(`superlab installed into ${options.targetDir}`);
|
|
175
246
|
console.log(`platform: ${options.platform}`);
|
|
176
247
|
console.log(`language: ${options.lang}`);
|
|
248
|
+
console.log(restartReminder(options.lang));
|
|
177
249
|
}
|
|
178
250
|
|
|
179
251
|
main().catch((error) => {
|
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,
|