repolith 0.2.0 → 0.3.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 +8 -1
- package/dist/cli.js +125 -46
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> Make a set of independent git repos feel like one monorepo — without touching git internals, GitHub, or CI.
|
|
4
4
|
|
|
5
|
-
**Status: v0.
|
|
5
|
+
**Status: v0.3 — CLI + MCP server.** All CLI commands (`sync`, `checkout`, `status`, `grep`, `log`, `diff`, `exec`, `init`, `bisect`, `state`, `freeze`, `open`) are implemented and tested — with `--json` on the read commands — plus an **MCP server** (`repolith mcp`) so AI agents can query and restore workspace state, and a VS Code extension. APIs may still change pre-1.0.
|
|
6
6
|
|
|
7
7
|
## What it is
|
|
8
8
|
|
|
@@ -24,6 +24,8 @@ npm i -g repolith
|
|
|
24
24
|
bun add -g repolith
|
|
25
25
|
```
|
|
26
26
|
|
|
27
|
+
**VS Code extension:** search "repolith" in the Extensions view, or install [`stanicky.repolith-vscode`](https://marketplace.visualstudio.com/items?itemName=stanicky.repolith-vscode).
|
|
28
|
+
|
|
27
29
|
## Quick start
|
|
28
30
|
|
|
29
31
|
```bash
|
|
@@ -34,8 +36,13 @@ repolith grep "TODO" # search across all repos at once
|
|
|
34
36
|
repolith exec "npm test" # run a command in every repo
|
|
35
37
|
repolith checkout # restore every repo to the locked commit (deterministic)
|
|
36
38
|
repolith bisect --good good.lock.json --test "npm test" # find the repo+commit that broke the system
|
|
39
|
+
repolith state --json # print the atomic hash + per-repo commits (scriptable)
|
|
40
|
+
repolith freeze snap.json # write a shareable snapshot of the current state
|
|
41
|
+
repolith open snap.json # reconstruct the exact system from a shared snapshot
|
|
37
42
|
```
|
|
38
43
|
|
|
44
|
+
`status`, `grep`, `log`, `diff`, and `state` all accept `--json` for scripting and agent/CI use.
|
|
45
|
+
|
|
39
46
|
## For AI agents (MCP)
|
|
40
47
|
|
|
41
48
|
`repolith` ships an [MCP](https://modelcontextprotocol.io) server so coding agents can query and reconstruct multi-repo state deterministically — *git pins a repo; repolith pins a system.*
|
package/dist/cli.js
CHANGED
|
@@ -17058,24 +17058,21 @@ Sync completed with errors — lockfile NOT updated.`);
|
|
|
17058
17058
|
import { readFile as readFile3 } from "node:fs/promises";
|
|
17059
17059
|
import { existsSync as existsSync2 } from "node:fs";
|
|
17060
17060
|
import { resolve as resolve2, join as join3 } from "node:path";
|
|
17061
|
-
async function
|
|
17061
|
+
async function restoreState(manifestPath, state) {
|
|
17062
17062
|
const manifestDir = resolve2(manifestPath, "..");
|
|
17063
17063
|
const manifest = parseManifest(await readFile3(manifestPath, "utf8"));
|
|
17064
|
-
const lock = await readLockfile(manifestDir);
|
|
17065
|
-
if (!lock)
|
|
17066
|
-
throw new Error("no repolith.lock.json — run `repolith sync` first");
|
|
17067
17064
|
const results = await runAll(manifest.repos, async (repo) => {
|
|
17068
|
-
const
|
|
17069
|
-
if (!
|
|
17070
|
-
throw new Error(`
|
|
17065
|
+
const pinned = state.repos[repo.name];
|
|
17066
|
+
if (!pinned)
|
|
17067
|
+
throw new Error(`state has no commit for "${repo.name}"`);
|
|
17071
17068
|
const dest = join3(manifestDir, repo.path);
|
|
17072
17069
|
if (!existsSync2(dest))
|
|
17073
17070
|
await gitClone(repo.url, dest, repo.ref);
|
|
17074
17071
|
await gitFetch(dest);
|
|
17075
|
-
await gitCheckoutCommit(dest,
|
|
17072
|
+
await gitCheckoutCommit(dest, pinned.commit);
|
|
17076
17073
|
const head = (await gitRun(dest, ["rev-parse", "HEAD"])).stdout.trim();
|
|
17077
|
-
if (head !==
|
|
17078
|
-
throw new Error(`HEAD ${head.slice(0, 8)} !=
|
|
17074
|
+
if (head !== pinned.commit) {
|
|
17075
|
+
throw new Error(`HEAD ${head.slice(0, 8)} != pinned ${pinned.commit.slice(0, 8)}`);
|
|
17079
17076
|
}
|
|
17080
17077
|
return head;
|
|
17081
17078
|
});
|
|
@@ -17088,11 +17085,17 @@ async function restoreToLock(manifestPath) {
|
|
|
17088
17085
|
repos[r.repo.name] = { url: r.repo.url, ref: r.repo.ref, commit: r.value };
|
|
17089
17086
|
}
|
|
17090
17087
|
const hash = computeHash(repos);
|
|
17091
|
-
if (errors.length === 0 && hash !==
|
|
17092
|
-
throw new Error(`restored hash ${hash.slice(0, 12)} !=
|
|
17088
|
+
if (errors.length === 0 && hash !== state.hash) {
|
|
17089
|
+
throw new Error(`restored hash ${hash.slice(0, 12)} != expected ${state.hash.slice(0, 12)} — refusing to claim reproducibility`);
|
|
17093
17090
|
}
|
|
17094
17091
|
return { workspace: manifest.name, hash, repos, errors };
|
|
17095
17092
|
}
|
|
17093
|
+
async function restoreToLock(manifestPath) {
|
|
17094
|
+
const lock = await readLockfile(resolve2(manifestPath, ".."));
|
|
17095
|
+
if (!lock)
|
|
17096
|
+
throw new Error("no repolith.lock.json — run `repolith sync` first");
|
|
17097
|
+
return restoreState(manifestPath, lock);
|
|
17098
|
+
}
|
|
17096
17099
|
async function checkoutCommand(manifestPath) {
|
|
17097
17100
|
console.log("Restoring workspace to locked state…");
|
|
17098
17101
|
const { workspace, hash, errors } = await restoreToLock(manifestPath);
|
|
@@ -17125,11 +17128,16 @@ async function repoStatus(dest) {
|
|
|
17125
17128
|
} catch {}
|
|
17126
17129
|
return { branch: branch.trim(), dirty: porcelain.trim().length > 0, ahead, behind };
|
|
17127
17130
|
}
|
|
17128
|
-
async function statusCommand(manifestPath) {
|
|
17131
|
+
async function statusCommand(manifestPath, json = false) {
|
|
17129
17132
|
const manifestDir = resolve3(manifestPath, "..");
|
|
17130
17133
|
const toml = await readFile4(manifestPath, "utf8");
|
|
17131
17134
|
const manifest = parseManifest(toml);
|
|
17132
17135
|
const results = await runAll(manifest.repos, (repo) => repoStatus(join4(manifestDir, repo.path)));
|
|
17136
|
+
if (json) {
|
|
17137
|
+
const data = results.map((r) => r.ok ? { repo: r.repo.name, ...r.value } : { repo: r.repo.name, error: r.error.message });
|
|
17138
|
+
console.log(JSON.stringify(data, null, 2));
|
|
17139
|
+
return;
|
|
17140
|
+
}
|
|
17133
17141
|
const nameWidth = Math.max(4, ...manifest.repos.map((r) => r.name.length));
|
|
17134
17142
|
for (const r of results) {
|
|
17135
17143
|
const name = r.repo.name.padEnd(nameWidth);
|
|
@@ -17183,34 +17191,37 @@ async function execCommand(command, manifestPath) {
|
|
|
17183
17191
|
// src/commands/grep.ts
|
|
17184
17192
|
import { readFile as readFile6 } from "node:fs/promises";
|
|
17185
17193
|
import { resolve as resolve5, join as join6 } from "node:path";
|
|
17186
|
-
async function grepCommand(pattern, extraArgs, manifestPath) {
|
|
17194
|
+
async function grepCommand(pattern, extraArgs, manifestPath, json = false) {
|
|
17187
17195
|
const manifestDir = resolve5(manifestPath, "..");
|
|
17188
17196
|
const toml = await readFile6(manifestPath, "utf8");
|
|
17189
17197
|
const manifest = parseManifest(toml);
|
|
17190
17198
|
const results = await runAll(manifest.repos, async (repo) => {
|
|
17191
17199
|
const dest = join6(manifestDir, repo.path);
|
|
17192
|
-
const { stdout } = await gitRun(dest, ["grep", "--color=never", "-n",
|
|
17200
|
+
const { stdout } = await gitRun(dest, ["grep", "--color=never", "-n", ...extraArgs, pattern]).catch(() => ({ stdout: "", stderr: "" }));
|
|
17193
17201
|
return stdout;
|
|
17194
17202
|
});
|
|
17195
|
-
|
|
17203
|
+
const matches = [];
|
|
17196
17204
|
for (const r of results) {
|
|
17197
|
-
if (
|
|
17198
|
-
|
|
17199
|
-
|
|
17200
|
-
|
|
17201
|
-
|
|
17202
|
-
|
|
17203
|
-
|
|
17205
|
+
if (r.ok && r.value.trim())
|
|
17206
|
+
matches.push({ repo: r.repo.name, hits: r.value.trim().split(`
|
|
17207
|
+
`) });
|
|
17208
|
+
}
|
|
17209
|
+
if (json) {
|
|
17210
|
+
console.log(JSON.stringify(matches, null, 2));
|
|
17211
|
+
} else {
|
|
17212
|
+
for (const m of matches) {
|
|
17213
|
+
for (const line of m.hits)
|
|
17214
|
+
console.log(`[${m.repo}] ${line}`);
|
|
17204
17215
|
}
|
|
17205
17216
|
}
|
|
17206
|
-
if (
|
|
17217
|
+
if (matches.length === 0)
|
|
17207
17218
|
process.exit(1);
|
|
17208
17219
|
}
|
|
17209
17220
|
|
|
17210
17221
|
// src/commands/log.ts
|
|
17211
17222
|
import { readFile as readFile7 } from "node:fs/promises";
|
|
17212
17223
|
import { resolve as resolve6, join as join7 } from "node:path";
|
|
17213
|
-
async function logCommand2(extraArgs, manifestPath) {
|
|
17224
|
+
async function logCommand2(extraArgs, manifestPath, json = false) {
|
|
17214
17225
|
const manifestDir = resolve6(manifestPath, "..");
|
|
17215
17226
|
const toml = await readFile7(manifestPath, "utf8");
|
|
17216
17227
|
const manifest = parseManifest(toml);
|
|
@@ -17219,6 +17230,11 @@ async function logCommand2(extraArgs, manifestPath) {
|
|
|
17219
17230
|
const { stdout } = await gitRun(dest, ["log", "--oneline", ...extraArgs]);
|
|
17220
17231
|
return stdout.trim();
|
|
17221
17232
|
});
|
|
17233
|
+
if (json) {
|
|
17234
|
+
const data = results.map((r) => r.ok ? { repo: r.repo.name, log: r.value } : { repo: r.repo.name, error: r.error.message });
|
|
17235
|
+
console.log(JSON.stringify(data, null, 2));
|
|
17236
|
+
return;
|
|
17237
|
+
}
|
|
17222
17238
|
for (const r of results) {
|
|
17223
17239
|
console.log(`
|
|
17224
17240
|
=== [${r.repo.name}] ===`);
|
|
@@ -17235,7 +17251,7 @@ async function logCommand2(extraArgs, manifestPath) {
|
|
|
17235
17251
|
// src/commands/diff.ts
|
|
17236
17252
|
import { readFile as readFile8 } from "node:fs/promises";
|
|
17237
17253
|
import { resolve as resolve7, join as join8 } from "node:path";
|
|
17238
|
-
async function diffCommand(extraArgs, manifestPath) {
|
|
17254
|
+
async function diffCommand(extraArgs, manifestPath, json = false) {
|
|
17239
17255
|
const manifestDir = resolve7(manifestPath, "..");
|
|
17240
17256
|
const toml = await readFile8(manifestPath, "utf8");
|
|
17241
17257
|
const manifest = parseManifest(toml);
|
|
@@ -17244,17 +17260,21 @@ async function diffCommand(extraArgs, manifestPath) {
|
|
|
17244
17260
|
const { stdout } = await gitRun(dest, ["diff", ...extraArgs]);
|
|
17245
17261
|
return stdout;
|
|
17246
17262
|
});
|
|
17247
|
-
|
|
17248
|
-
|
|
17249
|
-
|
|
17250
|
-
|
|
17251
|
-
|
|
17252
|
-
|
|
17253
|
-
|
|
17254
|
-
|
|
17263
|
+
const anyDiff = results.some((r) => r.ok && r.value.trim().length > 0);
|
|
17264
|
+
if (json) {
|
|
17265
|
+
const data = results.map((r) => r.ok ? { repo: r.repo.name, diff: r.value } : { repo: r.repo.name, error: r.error.message });
|
|
17266
|
+
console.log(JSON.stringify(data, null, 2));
|
|
17267
|
+
} else {
|
|
17268
|
+
for (const r of results) {
|
|
17269
|
+
if (!r.ok) {
|
|
17270
|
+
console.error(`=== [${r.repo.name}] ERROR: ${r.error.message}`);
|
|
17271
|
+
continue;
|
|
17272
|
+
}
|
|
17273
|
+
if (r.value.trim()) {
|
|
17274
|
+
console.log(`
|
|
17255
17275
|
=== [${r.repo.name}] ===`);
|
|
17256
|
-
|
|
17257
|
-
|
|
17276
|
+
process.stdout.write(r.value);
|
|
17277
|
+
}
|
|
17258
17278
|
}
|
|
17259
17279
|
}
|
|
17260
17280
|
if (anyDiff)
|
|
@@ -39950,7 +39970,7 @@ var jsonResult = (data) => ({
|
|
|
39950
39970
|
});
|
|
39951
39971
|
function buildMcpServer(manifestPath, opts) {
|
|
39952
39972
|
const manifestDir = resolve8(manifestPath, "..");
|
|
39953
|
-
const server = new McpServer({ name: "repolith", version: "0.
|
|
39973
|
+
const server = new McpServer({ name: "repolith", version: "0.3.1" });
|
|
39954
39974
|
const loadManifest = async () => parseManifest(await readFile9(manifestPath, "utf8"));
|
|
39955
39975
|
server.registerTool("repolith_state", {
|
|
39956
39976
|
title: "Workspace state",
|
|
@@ -40114,26 +40134,85 @@ async function bisect(opts) {
|
|
|
40114
40134
|
}
|
|
40115
40135
|
}
|
|
40116
40136
|
|
|
40137
|
+
// src/commands/freeze.ts
|
|
40138
|
+
import { readFile as readFile11, writeFile as writeFile3 } from "node:fs/promises";
|
|
40139
|
+
import { resolve as resolve10, join as join12 } from "node:path";
|
|
40140
|
+
async function buildState(manifestPath) {
|
|
40141
|
+
const manifestDir = resolve10(manifestPath, "..");
|
|
40142
|
+
const manifest = parseManifest(await readFile11(manifestPath, "utf8"));
|
|
40143
|
+
const results = await runAll(manifest.repos, (repo) => gitCurrentCommit(join12(manifestDir, repo.path)));
|
|
40144
|
+
const repos = {};
|
|
40145
|
+
for (const r of results) {
|
|
40146
|
+
if (!r.ok)
|
|
40147
|
+
throw new Error(`${r.repo.name}: ${r.error.message}`);
|
|
40148
|
+
repos[r.repo.name] = { url: r.repo.url, ref: r.repo.ref, commit: r.value };
|
|
40149
|
+
}
|
|
40150
|
+
return { version: 1, repos, hash: computeHash(repos) };
|
|
40151
|
+
}
|
|
40152
|
+
async function freezeCommand(manifestPath, outPath) {
|
|
40153
|
+
const state = await buildState(manifestPath);
|
|
40154
|
+
await writeFile3(outPath, JSON.stringify(state, null, 2) + `
|
|
40155
|
+
`, "utf8");
|
|
40156
|
+
console.log(`Froze ${Object.keys(state.repos).length} repos → ${outPath}`);
|
|
40157
|
+
console.log(`Workspace hash: ${state.hash}`);
|
|
40158
|
+
}
|
|
40159
|
+
async function stateCommand(manifestPath, json2 = false) {
|
|
40160
|
+
const state = await buildState(manifestPath);
|
|
40161
|
+
if (json2) {
|
|
40162
|
+
console.log(JSON.stringify(state, null, 2));
|
|
40163
|
+
return;
|
|
40164
|
+
}
|
|
40165
|
+
console.log(`hash: ${state.hash}`);
|
|
40166
|
+
for (const [name, r] of Object.entries(state.repos)) {
|
|
40167
|
+
console.log(` ${name} ${r.commit.slice(0, 12)} (${r.ref})`);
|
|
40168
|
+
}
|
|
40169
|
+
}
|
|
40170
|
+
|
|
40171
|
+
// src/commands/open.ts
|
|
40172
|
+
import { readFile as readFile12 } from "node:fs/promises";
|
|
40173
|
+
async function openCommand(manifestPath, stateFilePath) {
|
|
40174
|
+
const state = JSON.parse(await readFile12(stateFilePath, "utf8"));
|
|
40175
|
+
console.log(`Opening shared state from ${stateFilePath}…`);
|
|
40176
|
+
const { workspace, hash: hash2, errors: errors3 } = await restoreState(manifestPath, state);
|
|
40177
|
+
for (const e of errors3)
|
|
40178
|
+
console.error(` ERROR ${e.repo}: ${e.message}`);
|
|
40179
|
+
if (errors3.length) {
|
|
40180
|
+
console.error(`
|
|
40181
|
+
Open completed with errors.`);
|
|
40182
|
+
process.exit(1);
|
|
40183
|
+
}
|
|
40184
|
+
console.log(`"${workspace}" restored to ${hash2.slice(0, 12)}… from shared state`);
|
|
40185
|
+
}
|
|
40186
|
+
|
|
40117
40187
|
// src/cli.ts
|
|
40118
40188
|
function fail(e) {
|
|
40119
40189
|
console.error(e.message);
|
|
40120
40190
|
process.exit(1);
|
|
40121
40191
|
}
|
|
40122
40192
|
var program2 = new Command;
|
|
40123
|
-
program2.name("repolith").description("Make a set of independent git repos feel like one monorepo").version("0.
|
|
40193
|
+
program2.name("repolith").description("Make a set of independent git repos feel like one monorepo").version("0.3.1");
|
|
40124
40194
|
program2.command("sync").description("Clone missing repos, update all to their tracked refs, and write the lockfile").argument("[manifest]", "Path to repolith.toml", "repolith.toml").action(async (manifest) => {
|
|
40125
40195
|
await syncCommand(manifest).catch(fail);
|
|
40126
40196
|
});
|
|
40127
40197
|
program2.command("checkout").description("Restore every repo to the commit pinned in repolith.lock.json (deterministic system restore)").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").action(async (opts) => {
|
|
40128
40198
|
await checkoutCommand(opts.manifest).catch(fail);
|
|
40129
40199
|
});
|
|
40130
|
-
program2.command("
|
|
40131
|
-
await
|
|
40200
|
+
program2.command("state").description("Print the atomic workspace hash + each repo's current commit").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").option("--json", "Output structured JSON", false).action(async (opts) => {
|
|
40201
|
+
await stateCommand(opts.manifest, opts.json ?? false).catch(fail);
|
|
40202
|
+
});
|
|
40203
|
+
program2.command("freeze").description("Write a shareable snapshot of the current state to a file").argument("[outfile]", "Output path", "repolith.state.json").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").action(async (outfile, opts) => {
|
|
40204
|
+
await freezeCommand(opts.manifest, outfile).catch(fail);
|
|
40205
|
+
});
|
|
40206
|
+
program2.command("open").description("Reconstruct the workspace from a shared state file (from `repolith freeze`)").argument("<statefile>", "Path to a repolith.state.json").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").action(async (statefile, opts) => {
|
|
40207
|
+
await openCommand(opts.manifest, statefile).catch(fail);
|
|
40208
|
+
});
|
|
40209
|
+
program2.command("status").description("Show branch + dirty/clean + ahead/behind for every repo").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").option("--json", "Output structured JSON", false).action(async (opts) => {
|
|
40210
|
+
await statusCommand(opts.manifest, opts.json ?? false).catch(fail);
|
|
40132
40211
|
});
|
|
40133
40212
|
program2.command("exec").description("Run a shell command in every repo (quote the command)").argument("<command>", 'Command to run, e.g. "npm test"').option("--manifest <path>", "Path to repolith.toml", "repolith.toml").action(async (command, opts) => {
|
|
40134
40213
|
await execCommand(command, opts.manifest).catch(fail);
|
|
40135
40214
|
});
|
|
40136
|
-
program2.command("grep").description("Search across all repos (uses git grep)").argument("<pattern>", "Search pattern").option("-i, --ignore-case", "Case-insensitive match").option("-l, --files-with-matches", "Print only filenames").option("-w, --word-regexp", "Match whole words only").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").allowUnknownOption().action(async (pattern, opts) => {
|
|
40215
|
+
program2.command("grep").description("Search across all repos (uses git grep)").argument("<pattern>", "Search pattern").option("-i, --ignore-case", "Case-insensitive match").option("-l, --files-with-matches", "Print only filenames").option("-w, --word-regexp", "Match whole words only").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").option("--json", "Output structured JSON", false).allowUnknownOption().action(async (pattern, opts) => {
|
|
40137
40216
|
const extra = [];
|
|
40138
40217
|
if (opts.ignoreCase)
|
|
40139
40218
|
extra.push("-i");
|
|
@@ -40141,19 +40220,19 @@ program2.command("grep").description("Search across all repos (uses git grep)").
|
|
|
40141
40220
|
extra.push("-l");
|
|
40142
40221
|
if (opts.wordRegexp)
|
|
40143
40222
|
extra.push("-w");
|
|
40144
|
-
await grepCommand(pattern, extra, opts.manifest).catch(fail);
|
|
40223
|
+
await grepCommand(pattern, extra, opts.manifest, opts.json ?? false).catch(fail);
|
|
40145
40224
|
});
|
|
40146
|
-
program2.command("log").description("Show git log for all repos").option("-n, --max-count <n>", "Limit number of commits", "10").option("--since <date>", "Show commits more recent than date").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").action(async (opts) => {
|
|
40225
|
+
program2.command("log").description("Show git log for all repos").option("-n, --max-count <n>", "Limit number of commits", "10").option("--since <date>", "Show commits more recent than date").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").option("--json", "Output structured JSON", false).action(async (opts) => {
|
|
40147
40226
|
const extra = ["-n", opts.maxCount];
|
|
40148
40227
|
if (opts.since)
|
|
40149
40228
|
extra.push(`--since=${opts.since}`);
|
|
40150
|
-
await logCommand2(extra, opts.manifest).catch(fail);
|
|
40229
|
+
await logCommand2(extra, opts.manifest, opts.json ?? false).catch(fail);
|
|
40151
40230
|
});
|
|
40152
|
-
program2.command("diff").description("Show git diff across all repos").option("--staged", "Show staged changes").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").allowUnknownOption().action(async (opts) => {
|
|
40231
|
+
program2.command("diff").description("Show git diff across all repos").option("--staged", "Show staged changes").option("--manifest <path>", "Path to repolith.toml", "repolith.toml").option("--json", "Output structured JSON", false).allowUnknownOption().action(async (opts) => {
|
|
40153
40232
|
const extra = [];
|
|
40154
40233
|
if (opts.staged)
|
|
40155
40234
|
extra.push("--staged");
|
|
40156
|
-
await diffCommand(extra, opts.manifest).catch(fail);
|
|
40235
|
+
await diffCommand(extra, opts.manifest, opts.json ?? false).catch(fail);
|
|
40157
40236
|
});
|
|
40158
40237
|
program2.command("init").description("Interactively create a repolith.toml").option("-d, --dir <path>", "Directory to create repolith.toml in", ".").option("-f, --force", "Overwrite an existing repolith.toml").action(async (opts) => {
|
|
40159
40238
|
await initCommand(opts.dir, opts.force ?? false).catch(fail);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "repolith",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.1",
|
|
4
4
|
"description": "Make a set of independent git repos feel like one monorepo — TOML manifest, atomic lockfile hash, parallel git dispatch, a VS Code extension, and an MCP server for AI agents.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"git",
|