@roulabs/mx 2.0.0 → 2.1.1
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 +2 -2
- package/bin/mx.js +80 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -30,7 +30,7 @@ mx repo add git@github.com:you/app.git # clone a pristine repo into the runtim
|
|
|
30
30
|
mx work new my-feature # create a work (prints its folder path)
|
|
31
31
|
mx work -n my-feature worktree add app # add a worktree on branch my-feature
|
|
32
32
|
mx work -n my-feature port set app web # allocate a free port (across all works)
|
|
33
|
-
mx
|
|
33
|
+
mx info # see repos, works, worktrees, ports
|
|
34
34
|
```
|
|
35
35
|
|
|
36
36
|
Inside a work folder or worktree you can drop `-n` — mx infers the work/repo from your cwd. Read commands accept `--porcelain` for stable JSON; errors are `{"error","code"}` with a non-zero exit.
|
|
@@ -40,7 +40,7 @@ Inside a work folder or worktree you can drop `-n` — mx infers the work/repo f
|
|
|
40
40
|
| command | does |
|
|
41
41
|
|---|---|
|
|
42
42
|
| `mx init [path]` | scaffold/adopt a runtime (`repos/`, `works/`, `.mx-root`, `mx.json`, `CLAUDE.md`) |
|
|
43
|
-
| `mx
|
|
43
|
+
| `mx info [--all] [--porcelain]` | list repos, works, worktrees, ports |
|
|
44
44
|
| `mx sync` | re-stamp the runtime's mx-owned files (`CLAUDE.md`, per-repo/per-work scaffolding) from the current CLI — same-major, non-destructive |
|
|
45
45
|
| `mx update` | self-update the CLI within its major (`npm i -g`); flags a newer major if one exists |
|
|
46
46
|
| `mx migrate` | upgrade an older-version runtime to the version this CLI supports (the only command allowed on a version-mismatched runtime) |
|
package/bin/mx.js
CHANGED
|
@@ -491,7 +491,12 @@ function repoFetch(root, name) {
|
|
|
491
491
|
const rp = repoGitDir(root, name);
|
|
492
492
|
if (!isGitRepo(rp)) throw new MxError(`no such repo: ${name}`, "NO_REPO");
|
|
493
493
|
git(["-C", rp, "fetch", "--all", "--prune", "--tags"]);
|
|
494
|
+
const current = currentBranch(rp);
|
|
494
495
|
gitQuiet(["-C", rp, "merge", "--ff-only", "@{u}"]);
|
|
496
|
+
const base = originDefaultBranch(rp);
|
|
497
|
+
if (base && base !== current) {
|
|
498
|
+
gitQuiet(["-C", rp, "fetch", ".", `refs/remotes/origin/${base}:refs/heads/${base}`]);
|
|
499
|
+
}
|
|
495
500
|
return { name, branch: currentBranch(rp), remoteBranches: remoteBranchList(rp) };
|
|
496
501
|
}
|
|
497
502
|
function repoInfo(root, name) {
|
|
@@ -919,6 +924,7 @@ function statusRuntime(root, opts = {}) {
|
|
|
919
924
|
const works = opts.onlyArchived ? all.filter((w) => w.isArchived === true) : opts.includeArchived ? all : all.filter((w) => w.isArchived !== true);
|
|
920
925
|
return {
|
|
921
926
|
runtime: root,
|
|
927
|
+
version: readRuntimeVersion(root),
|
|
922
928
|
context: { entries: countContextEntries(root) },
|
|
923
929
|
repos: listReposInfo(root),
|
|
924
930
|
works,
|
|
@@ -1050,7 +1056,7 @@ var HELP = `mx \u2014 control panel for the mx runtime
|
|
|
1050
1056
|
|
|
1051
1057
|
Global:
|
|
1052
1058
|
mx init [path] scaffold/adopt a runtime (default ~/mx)
|
|
1053
|
-
mx
|
|
1059
|
+
mx info [--all] [--porcelain] show runtime version, repos, works, ports (active only by default; --all to include archived; alias: mx i)
|
|
1054
1060
|
mx sync re-stamp runtime files (CLAUDE.md, scaffolding) from current templates \u2014 same-major, non-breaking
|
|
1055
1061
|
mx update self-update the mx CLI within its major (npm i -g); flags a newer major if one exists
|
|
1056
1062
|
mx migrate upgrade an older-version runtime to the version this CLI supports (the only command allowed on a mismatched runtime)
|
|
@@ -1059,7 +1065,9 @@ Global:
|
|
|
1059
1065
|
Repos (pristine clones):
|
|
1060
1066
|
mx repo add <git-url> [--name <n>] clone a repo into the runtime
|
|
1061
1067
|
mx repo ls [--porcelain]
|
|
1062
|
-
mx repo -n <name>
|
|
1068
|
+
mx repo -n <name> path print the repo container path (cd "$(mx repo -n <name> path)")
|
|
1069
|
+
mx repo -n <name> fetch git fetch (+ ff the checked-out and base branches)
|
|
1070
|
+
mx repo fetch --all fetch every repo, one by one
|
|
1063
1071
|
mx repo -n <name> info [--porcelain]
|
|
1064
1072
|
mx repo health [--porcelain] pure-local health summary for every pristine clone
|
|
1065
1073
|
mx repo -n <name> health [--porcelain] detailed health for one pristine clone
|
|
@@ -1070,6 +1078,7 @@ Works (features):
|
|
|
1070
1078
|
mx work ls [--all|--archived] [--porcelain] default: active only; --all includes archived; --archived shows archived only
|
|
1071
1079
|
mx work -n <name> info [--porcelain]
|
|
1072
1080
|
mx work -n <name> path print the work folder path (cd "$(mx work -n <name> path)")
|
|
1081
|
+
mx work -n <name> open (or -o) open the work's fullscreen Terminal + editor layout (macOS)
|
|
1073
1082
|
mx work -n <name> describe <text>
|
|
1074
1083
|
mx work -n <name> worktree add <repo> [--branch <b>] [--base <ref>] [--no-hydrate] runs the repo's hydrate.sh after add unless --no-hydrate
|
|
1075
1084
|
mx work -n <name> worktree ls [--porcelain]
|
|
@@ -1200,7 +1209,7 @@ function runGlobal(positionals, flags) {
|
|
|
1200
1209
|
}, res);
|
|
1201
1210
|
return;
|
|
1202
1211
|
}
|
|
1203
|
-
case "
|
|
1212
|
+
case "info": {
|
|
1204
1213
|
const root = requireRuntime({ runtime: flags.runtime });
|
|
1205
1214
|
const data = statusRuntime(root, { includeArchived: flags.all });
|
|
1206
1215
|
emit(() => renderStatus(data), data);
|
|
@@ -1264,7 +1273,7 @@ function renderSelfUpdate(info) {
|
|
|
1264
1273
|
}
|
|
1265
1274
|
function renderStatus(data) {
|
|
1266
1275
|
console.log();
|
|
1267
|
-
console.log(` ${bold("mx")} ${dim("\xB7")} ${data.runtime}`);
|
|
1276
|
+
console.log(` ${bold("mx")} ${dim(`v${data.version}`)} ${dim("\xB7")} ${data.runtime}`);
|
|
1268
1277
|
console.log();
|
|
1269
1278
|
console.log(` ${bold("context")} ${dim(`(${data.context.entries})`)}`);
|
|
1270
1279
|
console.log();
|
|
@@ -1272,21 +1281,19 @@ function renderStatus(data) {
|
|
|
1272
1281
|
if (data.repos.length === 0) {
|
|
1273
1282
|
console.log(` ${dim("none yet \u2014 `mx repo add <git-url>`")}`);
|
|
1274
1283
|
} else {
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
console.log(` \u2022 ${name} ${branch} ${remote}`);
|
|
1284
|
+
for (let i = 0; i < data.repos.length; i++) {
|
|
1285
|
+
if (i > 0) console.log();
|
|
1286
|
+
const r = data.repos[i];
|
|
1287
|
+
console.log(` \u2022 ${bold(r.name)}`);
|
|
1288
|
+
console.log(` ${dim(tildify(r.path))}`);
|
|
1289
|
+
console.log(` ${dim(`${r.branch} ${r.remote ?? "(no remote)"}`)}`);
|
|
1282
1290
|
}
|
|
1283
1291
|
}
|
|
1284
1292
|
console.log();
|
|
1285
1293
|
const visibleArchived = data.works.filter((w) => w.isArchived === true);
|
|
1286
1294
|
const visibleActive = data.works.filter((w) => w.isArchived !== true);
|
|
1287
1295
|
const hiddenArchived = data.archivedWorksCount - visibleArchived.length;
|
|
1288
|
-
|
|
1289
|
-
console.log(` ${bold("works")} ${worksCount}`);
|
|
1296
|
+
console.log(` ${bold("works")}`);
|
|
1290
1297
|
if (data.works.length === 0) {
|
|
1291
1298
|
const empty = hiddenArchived > 0 ? `${hiddenArchived} archived hidden \u2014 pass --all to show` : "none yet \u2014 `mx work new <name>`";
|
|
1292
1299
|
console.log(` ${dim(empty)}`);
|
|
@@ -1305,6 +1312,7 @@ function renderStatus(data) {
|
|
|
1305
1312
|
const chip = w.isArchived === true ? ` ${dim(`[archived ${(w.archived_at ?? "").slice(0, 10)}]`)}` : "";
|
|
1306
1313
|
const styledName = w.isArchived === true ? dim(w.name) : bold(w.name);
|
|
1307
1314
|
console.log(` \u2022 ${styledName}${chip}`);
|
|
1315
|
+
console.log(` ${dim(tildify(w.path))}`);
|
|
1308
1316
|
if (wts.length === 0) {
|
|
1309
1317
|
console.log(` ${dim("(no worktrees)")}`);
|
|
1310
1318
|
continue;
|
|
@@ -1344,22 +1352,53 @@ function dispatchRepo(positionals, flags) {
|
|
|
1344
1352
|
console.log(dim("no repos yet \u2014 `mx repo add <git-url>`"));
|
|
1345
1353
|
return;
|
|
1346
1354
|
}
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
const branch = dim(r.branch.padEnd(branchW));
|
|
1352
|
-
const remote = dim(r.remote ?? "(no remote)");
|
|
1353
|
-
console.log(`\u2022 ${name} ${branch} ${remote}`);
|
|
1355
|
+
for (let i = 0; i < repos.length; i++) {
|
|
1356
|
+
if (i > 0) console.log();
|
|
1357
|
+
const r = repos[i];
|
|
1358
|
+
console.log(`\u2022 ${bold(r.name)}`);
|
|
1354
1359
|
console.log(` ${dim(tildify(r.path))}`);
|
|
1360
|
+
console.log(` ${dim(`${r.branch} ${r.remote ?? "(no remote)"}`)}`);
|
|
1355
1361
|
}
|
|
1356
1362
|
}, repos);
|
|
1357
1363
|
return;
|
|
1358
1364
|
}
|
|
1365
|
+
case "path": {
|
|
1366
|
+
const name = need(
|
|
1367
|
+
flags.name || ctxRepo,
|
|
1368
|
+
"which repo? pass -n <name> or run inside a repo (mx repo -n <name> path)"
|
|
1369
|
+
);
|
|
1370
|
+
const res = repoInfo(root, name);
|
|
1371
|
+
emit(() => console.log(res.path), { name: res.name, path: res.path });
|
|
1372
|
+
return;
|
|
1373
|
+
}
|
|
1359
1374
|
case "fetch": {
|
|
1375
|
+
if (flags.all) {
|
|
1376
|
+
const names = listReposInfo(root).map((r) => r.name);
|
|
1377
|
+
if (names.length === 0) {
|
|
1378
|
+
emit(() => console.log(dim("no repos yet \u2014 `mx repo add <git-url>`")), []);
|
|
1379
|
+
return;
|
|
1380
|
+
}
|
|
1381
|
+
const out = [];
|
|
1382
|
+
const lines = [];
|
|
1383
|
+
for (const n of names) {
|
|
1384
|
+
try {
|
|
1385
|
+
const r = repoFetch(root, n);
|
|
1386
|
+
out.push(r);
|
|
1387
|
+
lines.push(
|
|
1388
|
+
`${check()} ${bold(r.name)} ${dim(`\u2014 ${r.remoteBranches.length} branch(es) on origin, now on ${r.branch}`)}`
|
|
1389
|
+
);
|
|
1390
|
+
} catch (e) {
|
|
1391
|
+
const msg = e instanceof MxError ? e.message : String(e);
|
|
1392
|
+
out.push({ name: n, error: msg });
|
|
1393
|
+
lines.push(`${warn()} ${bold(n)} ${dim(`\u2014 ${msg}`)}`);
|
|
1394
|
+
}
|
|
1395
|
+
}
|
|
1396
|
+
emit(() => lines.forEach((l) => console.log(l)), out);
|
|
1397
|
+
return;
|
|
1398
|
+
}
|
|
1360
1399
|
const name = need(
|
|
1361
1400
|
flags.name || ctxRepo,
|
|
1362
|
-
"which repo? pass -n <name
|
|
1401
|
+
"which repo? pass -n <name>, run inside a repo, or use --all (mx repo -n <name> fetch)"
|
|
1363
1402
|
);
|
|
1364
1403
|
const res = repoFetch(root, name);
|
|
1365
1404
|
emit(
|
|
@@ -1610,7 +1649,8 @@ function need2(v, msg) {
|
|
|
1610
1649
|
return v;
|
|
1611
1650
|
}
|
|
1612
1651
|
function dispatchWork(positionals, flags) {
|
|
1613
|
-
|
|
1652
|
+
let action = positionals[1];
|
|
1653
|
+
if (!action && flags.open) action = "open";
|
|
1614
1654
|
if (action === "new") {
|
|
1615
1655
|
const root2 = requireRuntime({ runtime: flags.runtime });
|
|
1616
1656
|
const name2 = need2(positionals[2], "usage: mx work new <name> [--description <text>] [-o|--open]");
|
|
@@ -1700,6 +1740,22 @@ function dispatchWork(positionals, flags) {
|
|
|
1700
1740
|
emit(() => console.log(res.path), res);
|
|
1701
1741
|
return;
|
|
1702
1742
|
}
|
|
1743
|
+
case "open": {
|
|
1744
|
+
const res = workPath(root, name);
|
|
1745
|
+
try {
|
|
1746
|
+
openWorkLayout(res.path, workspaceFile(root, name));
|
|
1747
|
+
} catch (e) {
|
|
1748
|
+
const msg = e instanceof MxError ? e.message : String(e);
|
|
1749
|
+
process.stderr.write(`${warn()} ${dim(`could not open layout: ${msg}`)}
|
|
1750
|
+
`);
|
|
1751
|
+
return;
|
|
1752
|
+
}
|
|
1753
|
+
emit(
|
|
1754
|
+
() => console.log(`${check()} opened ${bold(name)} ${dim("(Terminal + editor)")}`),
|
|
1755
|
+
{ work: name, opened: true }
|
|
1756
|
+
);
|
|
1757
|
+
return;
|
|
1758
|
+
}
|
|
1703
1759
|
case "describe": {
|
|
1704
1760
|
const text = need2(positionals[2], "usage: mx work -n <name> describe <text>");
|
|
1705
1761
|
const work = workDescribe(root, name, text);
|
|
@@ -1943,7 +1999,7 @@ var VERSION = (() => {
|
|
|
1943
1999
|
function main() {
|
|
1944
2000
|
const { positionals, flags } = parseArgs(process.argv.slice(2));
|
|
1945
2001
|
setPorcelain(flags.porcelain);
|
|
1946
|
-
if (positionals[0] === "
|
|
2002
|
+
if (positionals[0] === "i") positionals[0] = "info";
|
|
1947
2003
|
try {
|
|
1948
2004
|
if (flags.version || positionals[0] === "version") {
|
|
1949
2005
|
emit(() => console.log(`mx ${VERSION}`), { version: VERSION });
|
|
@@ -1955,7 +2011,7 @@ function main() {
|
|
|
1955
2011
|
}
|
|
1956
2012
|
switch (positionals[0]) {
|
|
1957
2013
|
case "init":
|
|
1958
|
-
case "
|
|
2014
|
+
case "info":
|
|
1959
2015
|
case "sync":
|
|
1960
2016
|
case "update":
|
|
1961
2017
|
case "migrate":
|