@zebralabs/context-cli 0.1.0 → 0.1.3
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 +185 -113
package/package.json
CHANGED
|
@@ -1,13 +1,22 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@zebralabs/context-cli",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
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,23 +16,32 @@ 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>/docs/practices-and-standards/
|
|
29
38
|
`.trim() + "\n");
|
|
30
39
|
}
|
|
31
40
|
|
|
32
41
|
function findRepoContextRoot(startDir) {
|
|
33
42
|
let dir = startDir;
|
|
34
43
|
while (true) {
|
|
35
|
-
const candidate = path.join(dir, "practices-and-standards", "context.yaml");
|
|
44
|
+
const candidate = path.join(dir, "docs", "practices-and-standards", "context.yaml");
|
|
36
45
|
if (fs.existsSync(candidate)) return dir;
|
|
37
46
|
const parent = path.dirname(dir);
|
|
38
47
|
if (parent === dir) return null;
|
|
@@ -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`);
|
|
@@ -208,7 +199,8 @@ function cmdValidate(repoRoot, ctx) {
|
|
|
208
199
|
|
|
209
200
|
function cmdCompile(repoRoot, ctx) {
|
|
210
201
|
const installed = orderInstalledPacks(ctx.installed_packs ?? [], ctx.precedence ?? []);
|
|
211
|
-
const
|
|
202
|
+
const psRoot = path.join(repoRoot, "docs", "practices-and-standards");
|
|
203
|
+
const outDir = path.join(psRoot, ".compiled");
|
|
212
204
|
fs.mkdirSync(outDir, { recursive: true });
|
|
213
205
|
|
|
214
206
|
const promptPath = path.join(outDir, "system-prompt.md");
|
|
@@ -296,32 +288,41 @@ function ensureDir(p) {
|
|
|
296
288
|
}
|
|
297
289
|
|
|
298
290
|
function writeYamlFile(filePath, obj) {
|
|
299
|
-
|
|
291
|
+
const text = YAML.stringify(obj, { indent: 2 });
|
|
292
|
+
fs.writeFileSync(filePath, text.endsWith("\n") ? text : (text + "\n"), "utf8");
|
|
300
293
|
}
|
|
301
294
|
|
|
302
|
-
function
|
|
303
|
-
|
|
304
|
-
|
|
295
|
+
function readYamlFileSafe(filePath) {
|
|
296
|
+
const raw = fs.readFileSync(filePath, "utf8");
|
|
297
|
+
try {
|
|
298
|
+
return YAML.parse(raw);
|
|
299
|
+
} catch (e) {
|
|
300
|
+
const stamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
301
|
+
const backup = `${filePath}.backup-${stamp}.yaml`;
|
|
302
|
+
fs.copyFileSync(filePath, backup);
|
|
303
|
+
return null;
|
|
304
|
+
}
|
|
305
305
|
}
|
|
306
306
|
|
|
307
|
-
function
|
|
308
|
-
|
|
307
|
+
function readYamlIfExistsSafe(filePath) {
|
|
308
|
+
if(!fs.existsSync(filePath)) return null;
|
|
309
|
+
return readYamlFileSafe(filePath);
|
|
309
310
|
}
|
|
310
311
|
|
|
311
|
-
function
|
|
312
|
-
|
|
313
|
-
// We'll just try spawning; if it fails, we fallback.
|
|
314
|
-
return "pwsh";
|
|
312
|
+
function isWindows() {
|
|
313
|
+
return process.platform === "win32";
|
|
315
314
|
}
|
|
316
315
|
|
|
317
316
|
function runPwsh(command, { cwd } = {}) {
|
|
318
|
-
|
|
317
|
+
// Prefer pwsh, fallback to powershell (Windows PowerShell)
|
|
318
|
+
let exe = "pwsh";
|
|
319
319
|
let r = spawnSync(exe, ["-NoProfile", "-Command", command], { stdio: "inherit", cwd });
|
|
320
|
-
|
|
321
|
-
|
|
320
|
+
|
|
321
|
+
if ((r.error || r.status !== 0) && isWindows()) {
|
|
322
322
|
exe = "powershell";
|
|
323
323
|
r = spawnSync(exe, ["-NoProfile", "-ExecutionPolicy", "Bypass", "-Command", command], { stdio: "inherit", cwd });
|
|
324
324
|
}
|
|
325
|
+
|
|
325
326
|
if (r.status !== 0) {
|
|
326
327
|
die(`PowerShell command failed (exit ${r.status}): ${command}`);
|
|
327
328
|
}
|
|
@@ -351,11 +352,11 @@ async function fetchJson(url, token) {
|
|
|
351
352
|
}
|
|
352
353
|
|
|
353
354
|
function ensureContextInitialized(repoRoot, registryUrlMaybe) {
|
|
354
|
-
const psRoot = path.join(repoRoot, "practices-and-standards");
|
|
355
|
+
const psRoot = path.join(repoRoot, "docs", "practices-and-standards");
|
|
355
356
|
ensureDir(psRoot);
|
|
356
357
|
|
|
357
358
|
const ctxPath = path.join(psRoot, "context.yaml");
|
|
358
|
-
let ctx =
|
|
359
|
+
let ctx = readYamlIfExistsSafe(ctxPath);
|
|
359
360
|
|
|
360
361
|
if (!ctx) {
|
|
361
362
|
ctx = {
|
|
@@ -366,7 +367,6 @@ function ensureContextInitialized(repoRoot, registryUrlMaybe) {
|
|
|
366
367
|
};
|
|
367
368
|
writeYamlFile(ctxPath, ctx);
|
|
368
369
|
} else {
|
|
369
|
-
// Ensure schema exists
|
|
370
370
|
if (!ctx.schema) ctx.schema = "context-install/v1";
|
|
371
371
|
if (registryUrlMaybe && !ctx.registry) ctx.registry = registryUrlMaybe;
|
|
372
372
|
if (!Array.isArray(ctx.installed_packs)) ctx.installed_packs = [];
|
|
@@ -377,10 +377,26 @@ function ensureContextInitialized(repoRoot, registryUrlMaybe) {
|
|
|
377
377
|
return { ctxPath, ctx, psRoot };
|
|
378
378
|
}
|
|
379
379
|
|
|
380
|
+
function normalizeContext(ctx) {
|
|
381
|
+
const out = (ctx && typeof ctx === "object") ? ctx : {};
|
|
382
|
+
out.schema = out.schema || "context-install/v1";
|
|
383
|
+
|
|
384
|
+
if (!Array.isArray(out.installed_packs)) out.installed_packs = [];
|
|
385
|
+
if (!Array.isArray(out.precedence)) out.precedence = [];
|
|
386
|
+
|
|
387
|
+
// Defensive: remove accidental duplicates / wrong shapes
|
|
388
|
+
out.installed_packs = out.installed_packs.filter(p => p && typeof p === "object");
|
|
389
|
+
out.precedence = out.precedence.filter(x => typeof x === "string");
|
|
390
|
+
|
|
391
|
+
return out;
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
|
|
380
395
|
function upsertInstalledPack(ctxObj, packId, version) {
|
|
381
396
|
if (!Array.isArray(ctxObj.installed_packs)) ctxObj.installed_packs = [];
|
|
397
|
+
if (!Array.isArray(ctxObj.precedence)) ctxObj.precedence = [];
|
|
382
398
|
|
|
383
|
-
const manifest = `practices-and-standards/packs/${packId}/pack.yaml`;
|
|
399
|
+
const manifest = `docs/practices-and-standards/packs/${packId}/pack.yaml`;
|
|
384
400
|
const existing = ctxObj.installed_packs.find(p => p.id === packId);
|
|
385
401
|
|
|
386
402
|
if (existing) {
|
|
@@ -390,68 +406,129 @@ function upsertInstalledPack(ctxObj, packId, version) {
|
|
|
390
406
|
ctxObj.installed_packs.push({ id: packId, version, manifest });
|
|
391
407
|
}
|
|
392
408
|
|
|
393
|
-
// Keep precedence additive unless user already configured it
|
|
394
409
|
if (!Array.isArray(ctxObj.precedence)) ctxObj.precedence = [];
|
|
395
410
|
if (!ctxObj.precedence.includes(packId)) ctxObj.precedence.push(packId);
|
|
396
411
|
}
|
|
397
412
|
|
|
398
413
|
async function cmdPackInstall(repoRoot, packId, opts) {
|
|
399
|
-
const
|
|
400
|
-
if (!
|
|
414
|
+
const mode = opts.mode || "SkipExisting";
|
|
415
|
+
if (!["SkipExisting", "Overwrite"].includes(mode)) {
|
|
416
|
+
die("Invalid --mode. Use SkipExisting or Overwrite.");
|
|
417
|
+
}
|
|
401
418
|
|
|
402
|
-
// init or load context
|
|
419
|
+
// init or load context.yaml (always)
|
|
403
420
|
const { ctxPath, ctx } = ensureContextInitialized(repoRoot, opts.registry);
|
|
404
421
|
|
|
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
|
|
422
|
+
// Create temp workspace
|
|
419
423
|
const tmpBase = fs.mkdtempSync(path.join(os.tmpdir(), "context-pack-"));
|
|
420
|
-
const zipPath = path.join(tmpBase, `${packId}-${version}.zip`);
|
|
421
424
|
const extractDir = path.join(tmpBase, "extract");
|
|
422
425
|
ensureDir(extractDir);
|
|
423
426
|
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
+
try {
|
|
428
|
+
let zipPath = null;
|
|
429
|
+
let version = opts.version;
|
|
427
430
|
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
+
if (opts.zip) {
|
|
432
|
+
zipPath = path.isAbsolute(opts.zip) ? opts.zip : path.join(process.cwd(), opts.zip);
|
|
433
|
+
if (!fs.existsSync(zipPath)) die(`Zip not found: ${zipPath}`);
|
|
434
|
+
// version will be discovered from pack.yaml after extraction if not provided
|
|
435
|
+
} else {
|
|
436
|
+
const token = opts.token;
|
|
437
|
+
if (!token) die("Missing required --token for registry install.");
|
|
431
438
|
|
|
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
|
-
}
|
|
439
|
+
const registry = opts.registry || ctx.registry;
|
|
440
|
+
if (!registry) die("No registry configured. Provide --registry <url> or set registry: in docs/practices-and-standards/context.yaml");
|
|
439
441
|
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
442
|
+
if (!version) {
|
|
443
|
+
const latestUrl = `${registry.replace(/\/$/, "")}/packs/${encodeURIComponent(packId)}/latest`;
|
|
444
|
+
const latest = await fetchJson(latestUrl, token);
|
|
445
|
+
version = latest?.version;
|
|
446
|
+
if (!version) die(`Registry did not return a version from ${latestUrl}`);
|
|
447
|
+
}
|
|
444
448
|
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
449
|
+
const dlUrl = `${registry.replace(/\/$/, "")}/packs/${encodeURIComponent(packId)}/${encodeURIComponent(version)}/download`;
|
|
450
|
+
zipPath = path.join(tmpBase, `${packId}-${version}.zip`);
|
|
451
|
+
console.log(`\nDownloading ${packId}@${version}...`);
|
|
452
|
+
await downloadToFile(dlUrl, token, zipPath);
|
|
453
|
+
}
|
|
450
454
|
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
+
// Extract zip
|
|
456
|
+
console.log("Extracting...");
|
|
457
|
+
runPwsh(`Expand-Archive -Path "${zipPath}" -DestinationPath "${extractDir}" -Force`);
|
|
458
|
+
|
|
459
|
+
// Find installer
|
|
460
|
+
const installer = path.join(extractDir, "practices-and-standards", "install.ps1");
|
|
461
|
+
if (!fs.existsSync(installer)) {
|
|
462
|
+
die(`Pack zip does not contain practices-and-standards/install.ps1 (looked at: ${installer})`);
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
if (!version) {
|
|
466
|
+
const packYamlPath = path.join(
|
|
467
|
+
extractDir,
|
|
468
|
+
"practices-and-standards",
|
|
469
|
+
"packs",
|
|
470
|
+
packId,
|
|
471
|
+
"pack.yaml"
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
if (fs.existsSync(packYamlPath)) {
|
|
475
|
+
const packMeta = readYamlFile(packYamlPath);
|
|
476
|
+
if (packMeta?.version) version = String(packMeta.version);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
if (!version) version = "0.0.0";
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Run installer
|
|
483
|
+
console.log(`Installing (mode=${mode})...`);
|
|
484
|
+
const installCmd = `& "${installer}" -TargetRoot "${repoRoot}" -PackId "${packId}" -Mode "${mode}"`;
|
|
485
|
+
runPwsh(installCmd, { cwd: extractDir });
|
|
486
|
+
|
|
487
|
+
// Update context.yaml (safe read + repair)
|
|
488
|
+
let ctx2 = readYamlFileSafe(ctxPath);
|
|
489
|
+
if (!ctx2) {
|
|
490
|
+
// If YAML was corrupted by an external process, repair by resetting to a clean baseline.
|
|
491
|
+
ctx2 = {
|
|
492
|
+
schema: "context-install/v1",
|
|
493
|
+
...(opts.registry ? { registry: opts.registry } : {}),
|
|
494
|
+
installed_packs: [],
|
|
495
|
+
precedence: []
|
|
496
|
+
};
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
ctx2 = normalizeContext(ctx2);
|
|
500
|
+
|
|
501
|
+
upsertInstalledPack(ctx2, packId, version);
|
|
502
|
+
|
|
503
|
+
if (!Array.isArray(ctx2.precedence)) ctx2.precedence = [];
|
|
504
|
+
if (!ctx2.precedence.includes(packId)) ctx2.precedence.push(packId);
|
|
505
|
+
if (opts.registry && !ctx2.registry) ctx2.registry = opts.registry;
|
|
506
|
+
|
|
507
|
+
writeYamlFile(ctxPath, ctx2);
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
console.log("\n✅ Pack installed.");
|
|
511
|
+
console.log(`- Pack: ${packId} @ ${version}`);
|
|
512
|
+
console.log(`- Updated: ${path.relative(repoRoot, ctxPath).replaceAll("\\", "/")}`);
|
|
513
|
+
console.log("");
|
|
514
|
+
} finally {
|
|
515
|
+
try { fs.rmSync(tmpBase, { recursive: true, force: true }); } catch {}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
function parseOpts(args) {
|
|
520
|
+
const opts = {};
|
|
521
|
+
for (let i = 0; i < args.length; i++) {
|
|
522
|
+
const a = args[i];
|
|
523
|
+
if (a === "--token") opts.token = args[++i];
|
|
524
|
+
else if (a === "--version") opts.version = args[++i];
|
|
525
|
+
else if (a === "--registry") opts.registry = args[++i];
|
|
526
|
+
else if (a === "--zip") opts.zip = args[++i];
|
|
527
|
+
else if (a === "--mode") opts.mode = args[++i];
|
|
528
|
+
else if (a === "--repo-root") opts.repoRoot = args[++i];
|
|
529
|
+
else die(`Unknown option: ${a}`);
|
|
530
|
+
}
|
|
531
|
+
return opts;
|
|
455
532
|
}
|
|
456
533
|
|
|
457
534
|
|
|
@@ -460,7 +537,12 @@ async function main() {
|
|
|
460
537
|
|
|
461
538
|
if (cmd === "help") { showHelp(); return; }
|
|
462
539
|
|
|
463
|
-
|
|
540
|
+
if (cmd === "--version" || cmd === "-v" || cmd === "version") {
|
|
541
|
+
// Make sure package.json has version (or hardcode a constant)
|
|
542
|
+
console.log("0.1.3");
|
|
543
|
+
return;
|
|
544
|
+
}
|
|
545
|
+
|
|
464
546
|
if (cmd === "pack") {
|
|
465
547
|
const sub = (process.argv[3] ?? "").toLowerCase();
|
|
466
548
|
if (sub !== "install") {
|
|
@@ -470,22 +552,13 @@ async function main() {
|
|
|
470
552
|
}
|
|
471
553
|
|
|
472
554
|
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
|
-
}
|
|
555
|
+
if (!packId) die("Usage: ctx pack install <packId> [--zip <path>] | [--registry <url> --token <token> ...]");
|
|
556
|
+
|
|
557
|
+
const opts = parseOpts(process.argv.slice(5));
|
|
485
558
|
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
559
|
+
const repoRoot = opts.repoRoot
|
|
560
|
+
? (path.isAbsolute(opts.repoRoot) ? opts.repoRoot : path.join(process.cwd(), opts.repoRoot))
|
|
561
|
+
: (findRepoContextRoot(process.cwd()) ?? process.cwd());
|
|
489
562
|
|
|
490
563
|
await cmdPackInstall(repoRoot, packId, opts);
|
|
491
564
|
return;
|
|
@@ -493,9 +566,9 @@ async function main() {
|
|
|
493
566
|
|
|
494
567
|
// Existing behavior: these require an existing context.yaml
|
|
495
568
|
const repoRoot = findRepoContextRoot(process.cwd());
|
|
496
|
-
if (!repoRoot) die("Could not find practices-and-standards/context.yaml in this directory or any parent.");
|
|
569
|
+
if (!repoRoot) die("Could not find docs/practices-and-standards/context.yaml in this directory or any parent.");
|
|
497
570
|
|
|
498
|
-
const contextPath = path.join(repoRoot, "practices-and-standards", "context.yaml");
|
|
571
|
+
const contextPath = path.join(repoRoot, "docs", "practices-and-standards", "context.yaml");
|
|
499
572
|
const ctx = readYamlFile(contextPath);
|
|
500
573
|
|
|
501
574
|
if (!ctx?.schema || ctx.schema !== "context-install/v1") {
|
|
@@ -514,4 +587,3 @@ async function main() {
|
|
|
514
587
|
}
|
|
515
588
|
|
|
516
589
|
main().catch(e => die(e?.stack || String(e)));
|
|
517
|
-
|