@zebralabs/context-cli 0.1.0 → 0.1.2
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 +11 -2
- package/src/context.js +178 -107
package/package.json
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zebralabs/context-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.2",
|
|
4
4
|
"description": "Context-as-Code CLI (help/list/compile/validate)",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
8
8
|
"ctx": "src/context.js"
|
|
9
9
|
},
|
|
10
|
+
"files": [
|
|
11
|
+
"src",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
10
15
|
"dependencies": {
|
|
11
|
-
"yaml": "^2.5.0"
|
|
16
|
+
"yaml": "^2.5.0",
|
|
17
|
+
"extract-zip": "^2.0.1"
|
|
18
|
+
},
|
|
19
|
+
"scripts": {
|
|
20
|
+
"smoke": "node src/context.js help"
|
|
12
21
|
}
|
|
13
22
|
}
|
package/src/context.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
1
|
+
#!/usr/bin/env node
|
|
2
2
|
import os from "node:os";
|
|
3
3
|
import { spawnSync } from "node:child_process";
|
|
4
4
|
import fs from "node:fs";
|
|
@@ -16,16 +16,25 @@ function showHelp() {
|
|
|
16
16
|
Context-as-Code CLI
|
|
17
17
|
|
|
18
18
|
Usage:
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
19
|
+
ctx help
|
|
20
|
+
ctx list
|
|
21
|
+
ctx compile
|
|
22
|
+
ctx validate
|
|
23
|
+
|
|
24
|
+
Pack install:
|
|
25
|
+
ctx pack install <packId> [--repo-root <path>] [--mode SkipExisting|Overwrite]
|
|
26
|
+
([--zip <path>] | [--registry <url> [--version <x.y.z>] --token <token>])
|
|
27
|
+
|
|
28
|
+
Examples (local zip test):
|
|
29
|
+
ctx pack install pack-01-documentation-management --zip D:\\packs\\pack-01-documentation-management-v0.1.0.zip
|
|
30
|
+
|
|
31
|
+
Examples (registry download):
|
|
32
|
+
ctx pack install pack-01-documentation-management --registry https://example.com --token YOURTOKEN
|
|
33
|
+
ctx pack install pack-01-documentation-management --registry https://example.com --token YOURTOKEN --version 0.1.0
|
|
34
|
+
|
|
35
|
+
Notes:
|
|
36
|
+
- Zip must contain: practices-and-standards/install.ps1
|
|
37
|
+
- Installer merges into <repo-root>/practices-and-standards/
|
|
29
38
|
`.trim() + "\n");
|
|
30
39
|
}
|
|
31
40
|
|
|
@@ -62,7 +71,6 @@ function listMarkdownFiles(dir) {
|
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
function stripFrontmatter(text) {
|
|
65
|
-
// Remove YAML frontmatter if the file starts with ---
|
|
66
74
|
if (!text.startsWith("---\n") && !text.startsWith("---\r\n")) return text;
|
|
67
75
|
const lines = text.split(/\r?\n/);
|
|
68
76
|
let fenceCount = 0;
|
|
@@ -76,7 +84,7 @@ function stripFrontmatter(text) {
|
|
|
76
84
|
}
|
|
77
85
|
}
|
|
78
86
|
}
|
|
79
|
-
if (endIndex === -1) return text;
|
|
87
|
+
if (endIndex === -1) return text;
|
|
80
88
|
return lines.slice(endIndex + 1).join("\n");
|
|
81
89
|
}
|
|
82
90
|
|
|
@@ -92,7 +100,6 @@ function orderInstalledPacks(installed, precedence) {
|
|
|
92
100
|
byId.delete(id);
|
|
93
101
|
}
|
|
94
102
|
}
|
|
95
|
-
// stable remainder in original order
|
|
96
103
|
for (const p of installed) {
|
|
97
104
|
if (byId.has(p.id)) {
|
|
98
105
|
ordered.push(p);
|
|
@@ -103,7 +110,6 @@ function orderInstalledPacks(installed, precedence) {
|
|
|
103
110
|
}
|
|
104
111
|
|
|
105
112
|
function resolvePackManifestPath(repoRoot, manifestRel) {
|
|
106
|
-
// manifestRel in your schema is "practices-and-standards/packs/..."
|
|
107
113
|
return path.join(repoRoot, manifestRel);
|
|
108
114
|
}
|
|
109
115
|
|
|
@@ -137,7 +143,6 @@ function cmdValidate(repoRoot, ctx) {
|
|
|
137
143
|
warnings.push("No installed packs found in context.yaml");
|
|
138
144
|
}
|
|
139
145
|
|
|
140
|
-
// duplicate installed ids
|
|
141
146
|
const seen = new Set();
|
|
142
147
|
for (const p of installed) {
|
|
143
148
|
if (!p?.id) errors.push("Installed pack missing id");
|
|
@@ -148,13 +153,11 @@ function cmdValidate(repoRoot, ctx) {
|
|
|
148
153
|
if (!p?.manifest) errors.push(`Pack '${p?.id ?? "unknown"}' missing manifest path`);
|
|
149
154
|
}
|
|
150
155
|
|
|
151
|
-
// precedence unknown ids
|
|
152
156
|
const installedIds = new Set(installed.map(p => p.id).filter(Boolean));
|
|
153
157
|
for (const pid of precedence) {
|
|
154
158
|
if (!installedIds.has(pid)) warnings.push(`precedence references unknown pack id: ${pid}`);
|
|
155
159
|
}
|
|
156
160
|
|
|
157
|
-
// manifest checks
|
|
158
161
|
for (const p of installed) {
|
|
159
162
|
if (!p?.id || !p?.manifest) continue;
|
|
160
163
|
|
|
@@ -165,18 +168,6 @@ function cmdValidate(repoRoot, ctx) {
|
|
|
165
168
|
}
|
|
166
169
|
|
|
167
170
|
const pack = readYamlFile(manifestPath);
|
|
168
|
-
const manifestId = pack?.id;
|
|
169
|
-
if (manifestId && manifestId !== p.id) {
|
|
170
|
-
warnings.push(`Pack '${p.id}' manifest id mismatch: manifest says '${manifestId}'`);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const parts = p.manifest.split(/[\\/]/);
|
|
174
|
-
const packsIdx = parts.indexOf("packs");
|
|
175
|
-
const folderId = (packsIdx >= 0 && parts[packsIdx + 1]) ? parts[packsIdx + 1] : null;
|
|
176
|
-
if (folderId && folderId !== p.id) {
|
|
177
|
-
warnings.push(`Pack '${p.id}' manifest folder mismatch: path folder is '${folderId}'`);
|
|
178
|
-
}
|
|
179
|
-
|
|
180
171
|
const roots = pack?.contributes?.roots ?? [];
|
|
181
172
|
if (!Array.isArray(roots) || roots.length === 0) {
|
|
182
173
|
warnings.push(`Pack '${p.id}' has no contributes.roots`);
|
|
@@ -296,32 +287,41 @@ function ensureDir(p) {
|
|
|
296
287
|
}
|
|
297
288
|
|
|
298
289
|
function writeYamlFile(filePath, obj) {
|
|
299
|
-
|
|
290
|
+
const text = YAML.stringify(obj, { indent: 2 });
|
|
291
|
+
fs.writeFileSync(filePath, text.endsWith("\n") ? text : (text + "\n"), "utf8");
|
|
300
292
|
}
|
|
301
293
|
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
294
|
+
function readYamlFileSafe(filePath) {
|
|
295
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
296
|
+
try {
|
|
297
|
+
return YAML.parse(raw);
|
|
298
|
+
} catch (e) {
|
|
299
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
300
|
+
const backup = `${filePath}.backup-${stamp}.yaml`;
|
|
301
|
+
fs.copyFileSync(filePath, backup);
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
305
304
|
}
|
|
306
305
|
|
|
307
|
-
function
|
|
308
|
-
|
|
306
|
+
function readYamlIfExistsSafe(filePath) {
|
|
307
|
+
if(!fs.existsSync(filePath)) return null;
|
|
308
|
+
return readYamlFileSafe(filePath);
|
|
309
309
|
}
|
|
310
310
|
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
// We'll just try spawning; if it fails, we fallback.
|
|
314
|
-
return "pwsh";
|
|
311
|
+
function isWindows() {
|
|
312
|
+
return process.platform === "win32";
|
|
315
313
|
}
|
|
316
314
|
|
|
317
315
|
function runPwsh(command, { cwd } = {}) {
|
|
318
|
-
|
|
316
|
+
// Prefer pwsh, fallback to powershell (Windows PowerShell)
|
|
317
|
+
let exe = "pwsh";
|
|
319
318
|
let r = spawnSync(exe, ["-NoProfile", "-Command", command], { stdio: "inherit", cwd });
|
|
320
|
-
|
|
321
|
-
|
|
319
|
+
|
|
320
|
+
if ((r.error || r.status !== 0) && isWindows()) {
|
|
322
321
|
exe = "powershell";
|
|
323
322
|
r = spawnSync(exe, ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command], { stdio: "inherit", cwd });
|
|
324
323
|
}
|
|
324
|
+
|
|
325
325
|
if (r.status !== 0) {
|
|
326
326
|
die(`PowerShell command failed (exit ${r.status}): ${command}`);
|
|
327
327
|
}
|
|
@@ -355,7 +355,7 @@ function ensureContextInitialized(repoRoot, registryUrlMaybe) {
|
|
|
355
355
|
ensureDir(psRoot);
|
|
356
356
|
|
|
357
357
|
const ctxPath = path.join(psRoot, "context.yaml");
|
|
358
|
-
let ctx =
|
|
358
|
+
let ctx = readYamlIfExistsSafe(ctxPath);
|
|
359
359
|
|
|
360
360
|
if (!ctx) {
|
|
361
361
|
ctx = {
|
|
@@ -366,7 +366,6 @@ function ensureContextInitialized(repoRoot, registryUrlMaybe) {
|
|
|
366
366
|
};
|
|
367
367
|
writeYamlFile(ctxPath, ctx);
|
|
368
368
|
} else {
|
|
369
|
-
// Ensure schema exists
|
|
370
369
|
if (!ctx.schema) ctx.schema = "context-install/v1";
|
|
371
370
|
if (registryUrlMaybe && !ctx.registry) ctx.registry = registryUrlMaybe;
|
|
372
371
|
if (!Array.isArray(ctx.installed_packs)) ctx.installed_packs = [];
|
|
@@ -377,8 +376,24 @@ function ensureContextInitialized(repoRoot, registryUrlMaybe) {
|
|
|
377
376
|
return { ctxPath, ctx, psRoot };
|
|
378
377
|
}
|
|
379
378
|
|
|
379
|
+
function normalizeContext(ctx) {
|
|
380
|
+
const out = (ctx && typeof ctx === "object") ? ctx : {};
|
|
381
|
+
out.schema = out.schema || "context-install/v1";
|
|
382
|
+
|
|
383
|
+
if (!Array.isArray(out.installed_packs)) out.installed_packs = [];
|
|
384
|
+
if (!Array.isArray(out.precedence)) out.precedence = [];
|
|
385
|
+
|
|
386
|
+
// Defensive: remove accidental duplicates / wrong shapes
|
|
387
|
+
out.installed_packs = out.installed_packs.filter(p => p && typeof p === "object");
|
|
388
|
+
out.precedence = out.precedence.filter(x => typeof x === "string");
|
|
389
|
+
|
|
390
|
+
return out;
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
|
|
380
394
|
function upsertInstalledPack(ctxObj, packId, version) {
|
|
381
395
|
if (!Array.isArray(ctxObj.installed_packs)) ctxObj.installed_packs = [];
|
|
396
|
+
if (!Array.isArray(ctxObj.precedence)) ctxObj.precedence = [];
|
|
382
397
|
|
|
383
398
|
const manifest = `practices-and-standards/packs/${packId}/pack.yaml`;
|
|
384
399
|
const existing = ctxObj.installed_packs.find(p => p.id === packId);
|
|
@@ -390,68 +405,129 @@ function upsertInstalledPack(ctxObj, packId, version) {
|
|
|
390
405
|
ctxObj.installed_packs.push({ id: packId, version, manifest });
|
|
391
406
|
}
|
|
392
407
|
|
|
393
|
-
// Keep precedence additive unless user already configured it
|
|
394
408
|
if (!Array.isArray(ctxObj.precedence)) ctxObj.precedence = [];
|
|
395
409
|
if (!ctxObj.precedence.includes(packId)) ctxObj.precedence.push(packId);
|
|
396
410
|
}
|
|
397
411
|
|
|
398
412
|
async function cmdPackInstall(repoRoot, packId, opts) {
|
|
399
|
-
const
|
|
400
|
-
if (!
|
|
413
|
+
const mode = opts.mode || "SkipExisting";
|
|
414
|
+
if (!["SkipExisting", "Overwrite"].includes(mode)) {
|
|
415
|
+
die("Invalid --mode. Use SkipExisting or Overwrite.");
|
|
416
|
+
}
|
|
401
417
|
|
|
402
|
-
// init or load context
|
|
418
|
+
// init or load context.yaml (always)
|
|
403
419
|
const { ctxPath, ctx } = ensureContextInitialized(repoRoot, opts.registry);
|
|
404
420
|
|
|
405
|
-
//
|
|
406
|
-
const registry = opts.registry || ctx.registry;
|
|
407
|
-
if (!registry) die("No registry configured. Provide --registry <url> or set registry: in practices-and-standards/context.yaml");
|
|
408
|
-
|
|
409
|
-
// Determine version
|
|
410
|
-
let version = opts.version;
|
|
411
|
-
if (!version) {
|
|
412
|
-
const latestUrl = `${registry.replace(/\/$/, "")}/packs/${encodeURIComponent(packId)}/latest`;
|
|
413
|
-
const latest = await fetchJson(latestUrl, token);
|
|
414
|
-
version = latest?.version;
|
|
415
|
-
if (!version) die(`Registry did not return a version from ${latestUrl}`);
|
|
416
|
-
}
|
|
417
|
-
|
|
418
|
-
// Download zip
|
|
421
|
+
// Create temp workspace
|
|
419
422
|
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "context-pack-"));
|
|
420
|
-
const zipPath = path.join(tmpBase, `${packId}-${version}.zip`);
|
|
421
423
|
const extractDir = path.join(tmpBase, "extract");
|
|
422
424
|
ensureDir(extractDir);
|
|
423
425
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
426
|
+
try {
|
|
427
|
+
let zipPath = null;
|
|
428
|
+
let version = opts.version;
|
|
427
429
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
430
|
+
if (opts.zip) {
|
|
431
|
+
zipPath = path.isAbsolute(opts.zip) ? opts.zip : path.join(process.cwd(), opts.zip);
|
|
432
|
+
if (!fs.existsSync(zipPath)) die(`Zip not found: ${zipPath}`);
|
|
433
|
+
// version will be discovered from pack.yaml after extraction if not provided
|
|
434
|
+
} else {
|
|
435
|
+
const token = opts.token;
|
|
436
|
+
if (!token) die("Missing required --token for registry install.");
|
|
431
437
|
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
const extractedPsRoot = path.join(extractDir, "practices-and-standards");
|
|
435
|
-
const installer = path.join(extractedPsRoot, "install.ps1");
|
|
436
|
-
if (!fs.existsSync(installer)) {
|
|
437
|
-
die(`Downloaded pack does not contain practices-and-standards/install.ps1 (looked at: ${installer})`);
|
|
438
|
-
}
|
|
438
|
+
const registry = opts.registry || ctx.registry;
|
|
439
|
+
if (!registry) die("No registry configured. Provide --registry <url> or set registry: in practices-and-standards/context.yaml");
|
|
439
440
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
441
|
+
if (!version) {
|
|
442
|
+
const latestUrl = `${registry.replace(/\/$/, "")}/packs/${encodeURIComponent(packId)}/latest`;
|
|
443
|
+
const latest = await fetchJson(latestUrl, token);
|
|
444
|
+
version = latest?.version;
|
|
445
|
+
if (!version) die(`Registry did not return a version from ${latestUrl}`);
|
|
446
|
+
}
|
|
444
447
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
448
|
+
const dlUrl = `${registry.replace(/\/$/, "")}/packs/${encodeURIComponent(packId)}/${encodeURIComponent(version)}/download`;
|
|
449
|
+
zipPath = path.join(tmpBase, `${packId}-${version}.zip`);
|
|
450
|
+
console.log(`\nDownloading ${packId}@${version}...`);
|
|
451
|
+
await downloadToFile(dlUrl, token, zipPath);
|
|
452
|
+
}
|
|
450
453
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
454
|
+
// Extract zip
|
|
455
|
+
console.log("Extracting...");
|
|
456
|
+
runPwsh(`Expand-Archive -Path "${zipPath}" -DestinationPath "${extractDir}" -Force`);
|
|
457
|
+
|
|
458
|
+
// Find installer
|
|
459
|
+
const installer = path.join(extractDir, "practices-and-standards", "install.ps1");
|
|
460
|
+
if (!fs.existsSync(installer)) {
|
|
461
|
+
die(`Pack zip does not contain practices-and-standards/install.ps1 (looked at: ${installer})`);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
if (!version) {
|
|
465
|
+
const packYamlPath = path.join(
|
|
466
|
+
extractDir,
|
|
467
|
+
"practices-and-standards",
|
|
468
|
+
"packs",
|
|
469
|
+
packId,
|
|
470
|
+
"pack.yaml"
|
|
471
|
+
);
|
|
472
|
+
|
|
473
|
+
if (fs.existsSync(packYamlPath)) {
|
|
474
|
+
const packMeta = readYamlFile(packYamlPath);
|
|
475
|
+
if (packMeta?.version) version = String(packMeta.version);
|
|
476
|
+
}
|
|
477
|
+
|
|
478
|
+
if (!version) version = "0.0.0";
|
|
479
|
+
}
|
|
480
|
+
|
|
481
|
+
// Run installer
|
|
482
|
+
console.log(`Installing (mode=${mode})...`);
|
|
483
|
+
const installCmd = `& "${installer}" -TargetRoot "${repoRoot}" -PackId "${packId}" -Mode "${mode}"`;
|
|
484
|
+
runPwsh(installCmd, { cwd: extractDir });
|
|
485
|
+
|
|
486
|
+
// Update context.yaml (safe read + repair)
|
|
487
|
+
let ctx2 = readYamlFileSafe(ctxPath);
|
|
488
|
+
if (!ctx2) {
|
|
489
|
+
// If YAML was corrupted by an external process, repair by resetting to a clean baseline.
|
|
490
|
+
ctx2 = {
|
|
491
|
+
schema: "context-install/v1",
|
|
492
|
+
...(opts.registry ? { registry: opts.registry } : {}),
|
|
493
|
+
installed_packs: [],
|
|
494
|
+
precedence: []
|
|
495
|
+
};
|
|
496
|
+
}
|
|
497
|
+
|
|
498
|
+
ctx2 = normalizeContext(ctx2);
|
|
499
|
+
|
|
500
|
+
upsertInstalledPack(ctx2, packId, version);
|
|
501
|
+
|
|
502
|
+
if (!Array.isArray(ctx2.precedence)) ctx2.precedence = [];
|
|
503
|
+
if (!ctx2.precedence.includes(packId)) ctx2.precedence.push(packId);
|
|
504
|
+
if (opts.registry && !ctx2.registry) ctx2.registry = opts.registry;
|
|
505
|
+
|
|
506
|
+
writeYamlFile(ctxPath, ctx2);
|
|
507
|
+
|
|
508
|
+
|
|
509
|
+
console.log("\n✅ Pack installed.");
|
|
510
|
+
console.log(`- Pack: ${packId} @ ${version}`);
|
|
511
|
+
console.log(`- Updated: ${path.relative(repoRoot, ctxPath).replaceAll("\\", "/")}`);
|
|
512
|
+
console.log("");
|
|
513
|
+
} finally {
|
|
514
|
+
try { fs.rmSync(tmpBase, { recursive: true, force: true }); } catch {}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
function parseOpts(args) {
|
|
519
|
+
const opts = {};
|
|
520
|
+
for (let i = 0; i < args.length; i++) {
|
|
521
|
+
const a = args[i];
|
|
522
|
+
if (a === "--token") opts.token = args[++i];
|
|
523
|
+
else if (a === "--version") opts.version = args[++i];
|
|
524
|
+
else if (a === "--registry") opts.registry = args[++i];
|
|
525
|
+
else if (a === "--zip") opts.zip = args[++i];
|
|
526
|
+
else if (a === "--mode") opts.mode = args[++i];
|
|
527
|
+
else if (a === "--repo-root") opts.repoRoot = args[++i];
|
|
528
|
+
else die(`Unknown option: ${a}`);
|
|
529
|
+
}
|
|
530
|
+
return opts;
|
|
455
531
|
}
|
|
456
532
|
|
|
457
533
|
|
|
@@ -460,7 +536,12 @@ async function main() {
|
|
|
460
536
|
|
|
461
537
|
if (cmd === "help") { showHelp(); return; }
|
|
462
538
|
|
|
463
|
-
|
|
539
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
540
|
+
// Make sure package.json has version (or hardcode a constant)
|
|
541
|
+
console.log("0.1.2");
|
|
542
|
+
return;
|
|
543
|
+
}
|
|
544
|
+
|
|
464
545
|
if (cmd === "pack") {
|
|
465
546
|
const sub = (process.argv[3] ?? "").toLowerCase();
|
|
466
547
|
if (sub !== "install") {
|
|
@@ -470,22 +551,13 @@ async function main() {
|
|
|
470
551
|
}
|
|
471
552
|
|
|
472
553
|
const packId = process.argv[4];
|
|
473
|
-
if (!packId) die("Usage:
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
const args = process.argv.slice(5);
|
|
477
|
-
const opts = {};
|
|
478
|
-
for (let i = 0; i < args.length; i++) {
|
|
479
|
-
const a = args[i];
|
|
480
|
-
if (a === "--token") opts.token = args[++i];
|
|
481
|
-
else if (a === "--version") opts.version = args[++i];
|
|
482
|
-
else if (a === "--registry") opts.registry = args[++i];
|
|
483
|
-
else die(`Unknown option: ${a}`);
|
|
484
|
-
}
|
|
554
|
+
if (!packId) die("Usage: ctx pack install <packId> [--zip <path>] | [--registry <url> --token <token> ...]");
|
|
555
|
+
|
|
556
|
+
const opts = parseOpts(process.argv.slice(5));
|
|
485
557
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
558
|
+
const repoRoot = opts.repoRoot
|
|
559
|
+
? (path.isAbsolute(opts.repoRoot) ? opts.repoRoot : path.join(process.cwd(), opts.repoRoot))
|
|
560
|
+
: (findRepoContextRoot(process.cwd()) ?? process.cwd());
|
|
489
561
|
|
|
490
562
|
await cmdPackInstall(repoRoot, packId, opts);
|
|
491
563
|
return;
|
|
@@ -514,4 +586,3 @@ async function main() {
|
|
|
514
586
|
}
|
|
515
587
|
|
|
516
588
|
main().catch(e => die(e?.stack || String(e)));
|
|
517
|
-
|