@imfelixyeung/git-swarm 0.0.4 → 0.1.0

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.
Files changed (3) hide show
  1. package/README.md +1 -0
  2. package/dist/cli.js +689 -8
  3. package/package.json +13 -2
package/README.md CHANGED
@@ -42,6 +42,7 @@ git swarm checkout feature/my-branch
42
42
  | `pull [remote] [branch]` | Pull from remotes across all repos |
43
43
  | `fetch [--prune]` | Fetch from remotes across all repos |
44
44
  | `checkout <branch>` | Switch to a branch across all repos |
45
+ | `diff [--cached] [--stat]` | Show file changes across all repos |
45
46
  | `grep <pattern>` | Run `git grep` across all repos |
46
47
  | `find-branch <branch>` | Search all repos for a branch by name |
47
48
  | `remote` | List remotes for each repo |
package/dist/cli.js CHANGED
@@ -6256,7 +6256,7 @@ var program = new Command;
6256
6256
  var package_default = {
6257
6257
  name: "@imfelixyeung/git-swarm",
6258
6258
  description: "Manage multiple Git repositories with ease.",
6259
- version: "0.0.4",
6259
+ version: "0.1.0",
6260
6260
  repository: {
6261
6261
  type: "git",
6262
6262
  url: "https://github.com/imfelixyeung/git-swarm"
@@ -6277,18 +6277,29 @@ var package_default = {
6277
6277
  typecheck: "tsc --noEmit",
6278
6278
  biome: "biome check",
6279
6279
  ci: "biome ci",
6280
- test: "bun test"
6280
+ test: "bun test",
6281
+ "config-json-schema:update": "bun run src/scripts/make-config-json-schema.ts > src/config/schema.json",
6282
+ changeset: "changeset",
6283
+ "changeset:version": "changeset version && biome check --write",
6284
+ release: "bun run build && changeset publish"
6281
6285
  },
6282
6286
  devDependencies: {
6283
6287
  "@biomejs/biome": "2.5.12",
6288
+ "@changesets/changelog-github": "^1.0.1",
6289
+ "@changesets/cli": "^3.0.2",
6284
6290
  "@types/bun": "latest"
6285
6291
  },
6292
+ publishConfig: {
6293
+ access: "public",
6294
+ provenance: true
6295
+ },
6286
6296
  peerDependencies: {
6287
6297
  typescript: "^7"
6288
6298
  },
6289
6299
  dependencies: {
6290
6300
  "cli-table3": "^0.6.5",
6291
6301
  commander: "^15.0.0",
6302
+ dedent: "^1.7.2",
6292
6303
  "p-limit": "^7.3.2",
6293
6304
  picocolors: "^1.1.1",
6294
6305
  "simple-git": "^3.36.0",
@@ -11702,6 +11713,9 @@ async function glob(globInput, options) {
11702
11713
  return crawler ? formatPaths(await crawler.withPromise(), relative) : [];
11703
11714
  }
11704
11715
 
11716
+ // src/config/index.ts
11717
+ var {YAML } = globalThis.Bun;
11718
+
11705
11719
  // node_modules/zod/v4/core/util.js
11706
11720
  function getEnumValues(entries) {
11707
11721
  const numericValues = Object.values(entries).filter((v) => typeof v === "number");
@@ -11737,6 +11751,14 @@ function cleanRegex(source) {
11737
11751
  const end = source.endsWith("$") ? source.length - 1 : source.length;
11738
11752
  return source.slice(start, end);
11739
11753
  }
11754
+ function floatSafeRemainder(val, step) {
11755
+ const ratio = val / step;
11756
+ const roundedRatio = Math.round(ratio);
11757
+ const tolerance = 4 * Number.EPSILON * Math.max(Math.abs(ratio), 1);
11758
+ if (Math.abs(ratio - roundedRatio) < tolerance)
11759
+ return 0;
11760
+ return ratio - roundedRatio;
11761
+ }
11740
11762
  function assignProp(target, prop, value) {
11741
11763
  Object.defineProperty(target, prop, {
11742
11764
  value,
@@ -11843,6 +11865,13 @@ function optionalKeys(shape) {
11843
11865
  return shape[k]._zod.optin !== undefined && shape[k]._zod.optout === "optional";
11844
11866
  });
11845
11867
  }
11868
+ var NUMBER_FORMAT_RANGES = /* @__PURE__ */ (() => ({
11869
+ safeint: [Number.MIN_SAFE_INTEGER, Number.MAX_SAFE_INTEGER],
11870
+ int32: [-2147483648, 2147483647],
11871
+ uint32: [0, 4294967295],
11872
+ float32: [-340282346638528860000000000000000000000, 340282346638528860000000000000000000000],
11873
+ float64: [-Number.MAX_VALUE, Number.MAX_VALUE]
11874
+ }))();
11846
11875
  function pick2(schema, mask) {
11847
11876
  const currDef = schema._zod.def;
11848
11877
  const checks = currDef.checks;
@@ -12620,6 +12649,8 @@ var string = (params) => {
12620
12649
  const regex = params ? `[\\s\\S]{${params?.minimum ?? 0},${params?.maximum ?? ""}}` : `[\\s\\S]*`;
12621
12650
  return new RegExp(`^${regex}$`);
12622
12651
  };
12652
+ var integer = /^-?\d+$/;
12653
+ var number = /^-?\d+(?:\.\d+)?$/;
12623
12654
  var lowercase = /^[^A-Z]*$/;
12624
12655
  var uppercase = /^[^a-z]*$/;
12625
12656
 
@@ -12634,6 +12665,168 @@ var _whenHasLength = (payload) => {
12634
12665
  const val = payload.value;
12635
12666
  return !nullish(val) && val.length !== undefined;
12636
12667
  };
12668
+ var numericOriginMap = {
12669
+ number: "number",
12670
+ bigint: "bigint",
12671
+ object: "date"
12672
+ };
12673
+ var $ZodCheckLessThan = /* @__PURE__ */ $constructor("$ZodCheckLessThan", (inst, def) => {
12674
+ $ZodCheck.init(inst, def);
12675
+ const origin = numericOriginMap[typeof def.value];
12676
+ inst._zod.onattach.push((inst) => {
12677
+ const bag = inst._zod.bag;
12678
+ const curr = (def.inclusive ? bag.maximum : bag.exclusiveMaximum) ?? Number.POSITIVE_INFINITY;
12679
+ if (def.value < curr) {
12680
+ if (def.inclusive)
12681
+ bag.maximum = def.value;
12682
+ else
12683
+ bag.exclusiveMaximum = def.value;
12684
+ }
12685
+ });
12686
+ inst._zod.check = (payload) => {
12687
+ if (def.inclusive ? payload.value <= def.value : payload.value < def.value) {
12688
+ return;
12689
+ }
12690
+ payload.issues.push({
12691
+ origin: numericOriginMap[typeof payload.value] ?? origin,
12692
+ code: "too_big",
12693
+ maximum: typeof def.value === "object" ? def.value.getTime() : def.value,
12694
+ input: payload.value,
12695
+ inclusive: def.inclusive,
12696
+ inst,
12697
+ continue: !def.abort
12698
+ });
12699
+ };
12700
+ });
12701
+ var $ZodCheckGreaterThan = /* @__PURE__ */ $constructor("$ZodCheckGreaterThan", (inst, def) => {
12702
+ $ZodCheck.init(inst, def);
12703
+ const origin = numericOriginMap[typeof def.value];
12704
+ inst._zod.onattach.push((inst) => {
12705
+ const bag = inst._zod.bag;
12706
+ const curr = (def.inclusive ? bag.minimum : bag.exclusiveMinimum) ?? Number.NEGATIVE_INFINITY;
12707
+ if (def.value > curr) {
12708
+ if (def.inclusive)
12709
+ bag.minimum = def.value;
12710
+ else
12711
+ bag.exclusiveMinimum = def.value;
12712
+ }
12713
+ });
12714
+ inst._zod.check = (payload) => {
12715
+ if (def.inclusive ? payload.value >= def.value : payload.value > def.value) {
12716
+ return;
12717
+ }
12718
+ payload.issues.push({
12719
+ origin: numericOriginMap[typeof payload.value] ?? origin,
12720
+ code: "too_small",
12721
+ minimum: typeof def.value === "object" ? def.value.getTime() : def.value,
12722
+ input: payload.value,
12723
+ inclusive: def.inclusive,
12724
+ inst,
12725
+ continue: !def.abort
12726
+ });
12727
+ };
12728
+ });
12729
+ var $ZodCheckMultipleOf = /* @__PURE__ */ $constructor("$ZodCheckMultipleOf", (inst, def) => {
12730
+ $ZodCheck.init(inst, def);
12731
+ inst._zod.onattach.push((inst) => {
12732
+ var _a;
12733
+ (_a = inst._zod.bag).multipleOf ?? (_a.multipleOf = def.value);
12734
+ });
12735
+ inst._zod.check = (payload) => {
12736
+ if (typeof payload.value !== typeof def.value)
12737
+ throw new Error("Cannot mix number and bigint in multiple_of check.");
12738
+ const isMultiple = typeof payload.value === "bigint" ? def.value !== BigInt(0) && payload.value % def.value === BigInt(0) : floatSafeRemainder(payload.value, def.value) === 0;
12739
+ if (isMultiple)
12740
+ return;
12741
+ payload.issues.push({
12742
+ origin: typeof payload.value,
12743
+ code: "not_multiple_of",
12744
+ divisor: def.value,
12745
+ input: payload.value,
12746
+ inst,
12747
+ continue: !def.abort
12748
+ });
12749
+ };
12750
+ });
12751
+ var $ZodCheckNumberFormat = /* @__PURE__ */ $constructor("$ZodCheckNumberFormat", (inst, def) => {
12752
+ $ZodCheck.init(inst, def);
12753
+ def.format = def.format || "float64";
12754
+ const isInt = def.format?.includes("int");
12755
+ const origin = isInt ? "int" : "number";
12756
+ const [minimum, maximum] = NUMBER_FORMAT_RANGES[def.format];
12757
+ inst._zod.onattach.push((inst) => {
12758
+ const bag = inst._zod.bag;
12759
+ bag.format = def.format;
12760
+ bag.minimum = minimum;
12761
+ bag.maximum = maximum;
12762
+ if (isInt)
12763
+ bag.pattern = integer;
12764
+ });
12765
+ inst._zod.check = (payload) => {
12766
+ const input = payload.value;
12767
+ if (isInt) {
12768
+ if (!Number.isInteger(input)) {
12769
+ payload.issues.push({
12770
+ expected: origin,
12771
+ format: def.format,
12772
+ code: "invalid_type",
12773
+ continue: false,
12774
+ input,
12775
+ inst
12776
+ });
12777
+ return;
12778
+ }
12779
+ if (!Number.isSafeInteger(input)) {
12780
+ if (input > 0) {
12781
+ payload.issues.push({
12782
+ input,
12783
+ code: "too_big",
12784
+ maximum: Number.MAX_SAFE_INTEGER,
12785
+ note: "Integers must be within the safe integer range.",
12786
+ inst,
12787
+ origin,
12788
+ inclusive: true,
12789
+ continue: !def.abort
12790
+ });
12791
+ } else {
12792
+ payload.issues.push({
12793
+ input,
12794
+ code: "too_small",
12795
+ minimum: Number.MIN_SAFE_INTEGER,
12796
+ note: "Integers must be within the safe integer range.",
12797
+ inst,
12798
+ origin,
12799
+ inclusive: true,
12800
+ continue: !def.abort
12801
+ });
12802
+ }
12803
+ return;
12804
+ }
12805
+ }
12806
+ if (input < minimum) {
12807
+ payload.issues.push({
12808
+ origin: "number",
12809
+ input,
12810
+ code: "too_small",
12811
+ minimum,
12812
+ inclusive: true,
12813
+ inst,
12814
+ continue: !def.abort
12815
+ });
12816
+ }
12817
+ if (input > maximum) {
12818
+ payload.issues.push({
12819
+ origin: "number",
12820
+ input,
12821
+ code: "too_big",
12822
+ maximum,
12823
+ inclusive: true,
12824
+ inst,
12825
+ continue: !def.abort
12826
+ });
12827
+ }
12828
+ };
12829
+ });
12637
12830
  var $ZodCheckMaxLength = /* @__PURE__ */ $constructor("$ZodCheckMaxLength", (inst, def) => {
12638
12831
  var _a;
12639
12832
  $ZodCheck.init(inst, def);
@@ -13362,6 +13555,33 @@ var $ZodJWT = /* @__PURE__ */ $constructor("$ZodJWT", (inst, def) => {
13362
13555
  });
13363
13556
  };
13364
13557
  });
13558
+ var $ZodNumber = /* @__PURE__ */ $constructor("$ZodNumber", (inst, def) => {
13559
+ $ZodType.init(inst, def);
13560
+ inst._zod.pattern = inst._zod.bag.pattern ?? number;
13561
+ inst._zod.parse = (payload, _ctx) => {
13562
+ if (def.coerce)
13563
+ try {
13564
+ payload.value = Number(payload.value);
13565
+ } catch (_) {}
13566
+ const input = payload.value;
13567
+ if (typeof input === "number" && !Number.isNaN(input) && Number.isFinite(input)) {
13568
+ return payload;
13569
+ }
13570
+ const received = typeof input === "number" ? Number.isNaN(input) ? "NaN" : !Number.isFinite(input) ? String(input) : undefined : undefined;
13571
+ payload.issues.push({
13572
+ expected: "number",
13573
+ code: "invalid_type",
13574
+ input,
13575
+ inst,
13576
+ ...received ? { received } : {}
13577
+ });
13578
+ return payload;
13579
+ };
13580
+ });
13581
+ var $ZodNumberFormat = /* @__PURE__ */ $constructor("$ZodNumberFormat", (inst, def) => {
13582
+ $ZodCheckNumberFormat.init(inst, def);
13583
+ $ZodNumber.init(inst, def);
13584
+ });
13365
13585
  var $ZodUnknown = /* @__PURE__ */ $constructor("$ZodUnknown", (inst, def) => {
13366
13586
  $ZodType.init(inst, def);
13367
13587
  inst._zod.parse = (payload) => payload;
@@ -14801,6 +15021,22 @@ function _isoDuration(Class, params) {
14801
15021
  ...normalizeParams(params)
14802
15022
  });
14803
15023
  }
15024
+ function _number(Class, params) {
15025
+ return new Class({
15026
+ type: "number",
15027
+ checks: [],
15028
+ ...normalizeParams(params)
15029
+ });
15030
+ }
15031
+ function _int(Class, params) {
15032
+ return new Class({
15033
+ type: "number",
15034
+ check: "number_format",
15035
+ abort: false,
15036
+ format: "safeint",
15037
+ ...normalizeParams(params)
15038
+ });
15039
+ }
14804
15040
  function _unknown(Class) {
14805
15041
  return new Class({
14806
15042
  type: "unknown"
@@ -14812,6 +15048,45 @@ function _never(Class, params) {
14812
15048
  ...normalizeParams(params)
14813
15049
  });
14814
15050
  }
15051
+ function _lt(value, params) {
15052
+ return new $ZodCheckLessThan({
15053
+ check: "less_than",
15054
+ ...normalizeParams(params),
15055
+ value,
15056
+ inclusive: false
15057
+ });
15058
+ }
15059
+ function _lte(value, params) {
15060
+ return new $ZodCheckLessThan({
15061
+ check: "less_than",
15062
+ ...normalizeParams(params),
15063
+ value,
15064
+ inclusive: true
15065
+ });
15066
+ }
15067
+ function _gt(value, params) {
15068
+ return new $ZodCheckGreaterThan({
15069
+ check: "greater_than",
15070
+ ...normalizeParams(params),
15071
+ value,
15072
+ inclusive: false
15073
+ });
15074
+ }
15075
+ function _gte(value, params) {
15076
+ return new $ZodCheckGreaterThan({
15077
+ check: "greater_than",
15078
+ ...normalizeParams(params),
15079
+ value,
15080
+ inclusive: true
15081
+ });
15082
+ }
15083
+ function _multipleOf(value, params) {
15084
+ return new $ZodCheckMultipleOf({
15085
+ check: "multiple_of",
15086
+ ...normalizeParams(params),
15087
+ value
15088
+ });
15089
+ }
14815
15090
  function _maxLength(maximum, params) {
14816
15091
  const ch = new $ZodCheckMaxLength({
14817
15092
  check: "max_length",
@@ -15510,6 +15785,43 @@ var stringProcessor = (schema, ctx, _json, _params) => {
15510
15785
  }
15511
15786
  }
15512
15787
  };
15788
+ var numberProcessor = (schema, ctx, _json, params) => {
15789
+ const json = _json;
15790
+ const { minimum, maximum, format, multipleOf, exclusiveMaximum, exclusiveMinimum } = schema._zod.bag;
15791
+ if (typeof format === "string" && format.includes("int"))
15792
+ json.type = "integer";
15793
+ else
15794
+ json.type = "number";
15795
+ const exMin = typeof exclusiveMinimum === "number" && exclusiveMinimum >= (minimum ?? Number.NEGATIVE_INFINITY);
15796
+ const exMax = typeof exclusiveMaximum === "number" && exclusiveMaximum <= (maximum ?? Number.POSITIVE_INFINITY);
15797
+ const legacy = ctx.target === "draft-04" || ctx.target === "openapi-3.0";
15798
+ if (exMin) {
15799
+ if (legacy) {
15800
+ json.minimum = exclusiveMinimum;
15801
+ json.exclusiveMinimum = true;
15802
+ } else {
15803
+ json.exclusiveMinimum = exclusiveMinimum;
15804
+ }
15805
+ } else if (typeof minimum === "number") {
15806
+ json.minimum = minimum;
15807
+ }
15808
+ if (exMax) {
15809
+ if (legacy) {
15810
+ json.maximum = exclusiveMaximum;
15811
+ json.exclusiveMaximum = true;
15812
+ } else {
15813
+ json.exclusiveMaximum = exclusiveMaximum;
15814
+ }
15815
+ } else if (typeof maximum === "number") {
15816
+ json.maximum = maximum;
15817
+ }
15818
+ if (typeof multipleOf === "number") {
15819
+ if (Number.isFinite(multipleOf) && multipleOf !== 0)
15820
+ json.multipleOf = Math.abs(multipleOf);
15821
+ else
15822
+ handleUnrepresentable(schema, ctx, json, params, `A multipleOf divisor of ${multipleOf} cannot be represented in JSON Schema`);
15823
+ }
15824
+ };
15513
15825
  var neverProcessor = (_schema, _ctx, json, _params) => {
15514
15826
  json.not = {};
15515
15827
  };
@@ -16185,6 +16497,73 @@ var ZodJWT = /* @__PURE__ */ $constructor("ZodJWT", (inst, def) => {
16185
16497
  $ZodJWT.init(inst, def);
16186
16498
  ZodStringFormat.init(inst, def);
16187
16499
  });
16500
+ var ZodNumber = /* @__PURE__ */ $constructor("ZodNumber", (inst, def) => {
16501
+ $ZodNumber.init(inst, def);
16502
+ ZodType.init(inst, def);
16503
+ inst._zod.processJSONSchema = (ctx, json, params) => numberProcessor(inst, ctx, json, params);
16504
+ const bag = inst._zod.bag;
16505
+ inst.minValue = Math.max(bag.minimum ?? Number.NEGATIVE_INFINITY, bag.exclusiveMinimum ?? Number.NEGATIVE_INFINITY) ?? null;
16506
+ inst.maxValue = Math.min(bag.maximum ?? Number.POSITIVE_INFINITY, bag.exclusiveMaximum ?? Number.POSITIVE_INFINITY) ?? null;
16507
+ inst.isInt = (bag.format ?? "").includes("int") || Number.isSafeInteger(bag.multipleOf ?? 0.5);
16508
+ inst.isFinite = true;
16509
+ inst.format = bag.format ?? null;
16510
+ }, {
16511
+ gt(value, params) {
16512
+ return this.check(_gt(value, params));
16513
+ },
16514
+ gte(value, params) {
16515
+ return this.check(_gte(value, params));
16516
+ },
16517
+ min(value, params) {
16518
+ return this.check(_gte(value, params));
16519
+ },
16520
+ lt(value, params) {
16521
+ return this.check(_lt(value, params));
16522
+ },
16523
+ lte(value, params) {
16524
+ return this.check(_lte(value, params));
16525
+ },
16526
+ max(value, params) {
16527
+ return this.check(_lte(value, params));
16528
+ },
16529
+ int(params) {
16530
+ return this.check(int(params));
16531
+ },
16532
+ safe(params) {
16533
+ return this.check(int(params));
16534
+ },
16535
+ positive(params) {
16536
+ return this.check(_gt(0, params));
16537
+ },
16538
+ nonnegative(params) {
16539
+ return this.check(_gte(0, params));
16540
+ },
16541
+ negative(params) {
16542
+ return this.check(_lt(0, params));
16543
+ },
16544
+ nonpositive(params) {
16545
+ return this.check(_lte(0, params));
16546
+ },
16547
+ multipleOf(value, params) {
16548
+ return this.check(_multipleOf(value, params));
16549
+ },
16550
+ step(value, params) {
16551
+ return this.check(_multipleOf(value, params));
16552
+ },
16553
+ finite() {
16554
+ return this;
16555
+ }
16556
+ });
16557
+ function number2(params) {
16558
+ return _number(ZodNumber, params);
16559
+ }
16560
+ var ZodNumberFormat = /* @__PURE__ */ $constructor("ZodNumberFormat", (inst, def) => {
16561
+ $ZodNumberFormat.init(inst, def);
16562
+ ZodNumber.init(inst, def);
16563
+ });
16564
+ function int(params) {
16565
+ return _int(ZodNumberFormat, params);
16566
+ }
16188
16567
  var ZodUnknown = /* @__PURE__ */ $constructor("ZodUnknown", (inst, def) => {
16189
16568
  $ZodUnknown.init(inst, def);
16190
16569
  ZodType.init(inst, def);
@@ -16527,6 +16906,61 @@ function refine(fn, _params = {}) {
16527
16906
  function superRefine(fn, params) {
16528
16907
  return _superRefine(fn, params);
16529
16908
  }
16909
+ // src/config/index.ts
16910
+ var CONFIG_FILE_NAME = "git-swarm.config.yaml";
16911
+ var defaults = {
16912
+ options: {
16913
+ parallel: 1,
16914
+ where: ""
16915
+ }
16916
+ };
16917
+ var configSchema = object({
16918
+ repositories: array(object({
16919
+ path: string2()
16920
+ })).nullish(),
16921
+ options: object({
16922
+ parallel: number2().gte(0).nullish().default(defaults.options.parallel),
16923
+ where: string2().nullish().default(defaults.options.where)
16924
+ }).nullish()
16925
+ });
16926
+ var defaultConfig = {
16927
+ options: {
16928
+ parallel: defaults.options.parallel,
16929
+ where: defaults.options.where
16930
+ }
16931
+ };
16932
+ var file = () => Bun.file(CONFIG_FILE_NAME);
16933
+ var exists2 = async () => file().exists();
16934
+ var write = async (config) => {
16935
+ await file().write(YAML.stringify(config, null, 4));
16936
+ };
16937
+ var cache = null;
16938
+ var get = async () => {
16939
+ if (cache !== null) {
16940
+ return cache;
16941
+ }
16942
+ if (!await file().exists()) {
16943
+ cache = defaultConfig;
16944
+ return cache;
16945
+ }
16946
+ const contents = await file().text();
16947
+ const config = YAML.parse(contents);
16948
+ const result = await configSchema.parseAsync(config);
16949
+ cache = result;
16950
+ return result;
16951
+ };
16952
+ var getOption = async (key) => {
16953
+ const config = await get();
16954
+ return config.options?.[key] ?? defaultConfig.options[key];
16955
+ };
16956
+ var config2 = {
16957
+ file,
16958
+ exists: exists2,
16959
+ write,
16960
+ get,
16961
+ getOption
16962
+ };
16963
+
16530
16964
  // src/utils/array-has-overlaps.ts
16531
16965
  var arrayHasOverlaps = (a, b) => {
16532
16966
  return !new Set(a).isDisjointFrom(new Set(b));
@@ -16666,7 +17100,16 @@ var repoMatchesFilter = async (repo, filters) => {
16666
17100
  };
16667
17101
 
16668
17102
  // src/git/discover.ts
16669
- async function* findGitRepositoryPaths(root) {
17103
+ async function* findGitRepositoryPaths(root, options) {
17104
+ if (!options.skipConfig) {
17105
+ const repos = await config2.get().then((c) => c.repositories);
17106
+ if (repos) {
17107
+ for (const repo of repos) {
17108
+ yield repo.path;
17109
+ }
17110
+ return;
17111
+ }
17112
+ }
16670
17113
  const matches = await glob("**/.git", {
16671
17114
  cwd: root,
16672
17115
  dot: true,
@@ -16678,8 +17121,8 @@ async function* findGitRepositoryPaths(root) {
16678
17121
  yield dirname2(path);
16679
17122
  }
16680
17123
  }
16681
- async function* findGitRepositories(root, filter) {
16682
- for await (const path of findGitRepositoryPaths(root)) {
17124
+ async function* findGitRepositories(root, filter, options = { skipConfig: false }) {
17125
+ for await (const path of findGitRepositoryPaths(root, options)) {
16683
17126
  const repo = {
16684
17127
  path: { absolute: path, relative: relative2(root, path) || "." },
16685
17128
  git: esm_default(path, { baseDir: path })
@@ -16749,10 +17192,246 @@ var checkoutCommand = new Command("checkout").description("Switch branches").arg
16749
17192
  console.log(table.toString());
16750
17193
  });
16751
17194
 
17195
+ // node_modules/dedent/dist/dedent.mjs
17196
+ function ownKeys(object, enumerableOnly) {
17197
+ var keys = Object.keys(object);
17198
+ if (Object.getOwnPropertySymbols) {
17199
+ var symbols = Object.getOwnPropertySymbols(object);
17200
+ enumerableOnly && (symbols = symbols.filter(function(sym) {
17201
+ return Object.getOwnPropertyDescriptor(object, sym).enumerable;
17202
+ })), keys.push.apply(keys, symbols);
17203
+ }
17204
+ return keys;
17205
+ }
17206
+ function _objectSpread(target) {
17207
+ for (var i = 1;i < arguments.length; i++) {
17208
+ var source = arguments[i] != null ? arguments[i] : {};
17209
+ i % 2 ? ownKeys(Object(source), true).forEach(function(key) {
17210
+ _defineProperty(target, key, source[key]);
17211
+ }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function(key) {
17212
+ Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key));
17213
+ });
17214
+ }
17215
+ return target;
17216
+ }
17217
+ function _defineProperty(obj, key, value) {
17218
+ key = _toPropertyKey(key);
17219
+ if (key in obj) {
17220
+ Object.defineProperty(obj, key, { value, enumerable: true, configurable: true, writable: true });
17221
+ } else {
17222
+ obj[key] = value;
17223
+ }
17224
+ return obj;
17225
+ }
17226
+ function _toPropertyKey(arg) {
17227
+ var key = _toPrimitive(arg, "string");
17228
+ return typeof key === "symbol" ? key : String(key);
17229
+ }
17230
+ function _toPrimitive(input, hint) {
17231
+ if (typeof input !== "object" || input === null)
17232
+ return input;
17233
+ var prim = input[Symbol.toPrimitive];
17234
+ if (prim !== undefined) {
17235
+ var res = prim.call(input, hint || "default");
17236
+ if (typeof res !== "object")
17237
+ return res;
17238
+ throw new TypeError("@@toPrimitive must return a primitive value.");
17239
+ }
17240
+ return (hint === "string" ? String : Number)(input);
17241
+ }
17242
+ var dedent = createDedent({});
17243
+ var dedent_default = dedent;
17244
+ function createDedent(options) {
17245
+ dedent.withOptions = (newOptions) => createDedent(_objectSpread(_objectSpread({}, options), newOptions));
17246
+ return dedent;
17247
+ function dedent(strings, ...values) {
17248
+ const raw = typeof strings === "string" ? [strings] : strings.raw;
17249
+ const {
17250
+ alignValues = false,
17251
+ escapeSpecialCharacters = Array.isArray(strings),
17252
+ trimWhitespace = true
17253
+ } = options;
17254
+ let result = "";
17255
+ for (let i = 0;i < raw.length; i++) {
17256
+ let next = raw[i];
17257
+ if (escapeSpecialCharacters) {
17258
+ next = next.replace(/\\\n[ \t]*/g, "").replace(/\\`/g, "`").replace(/\\\$/g, "$").replace(/\\\{/g, "{");
17259
+ }
17260
+ result += next;
17261
+ if (i < values.length) {
17262
+ const value = alignValues ? alignValue(values[i], result) : values[i];
17263
+ result += value;
17264
+ }
17265
+ }
17266
+ const lines = result.split(`
17267
+ `);
17268
+ let mindent = null;
17269
+ for (const l of lines) {
17270
+ const m = l.match(/^(\s+)\S+/);
17271
+ if (m) {
17272
+ const indent = m[1].length;
17273
+ if (!mindent) {
17274
+ mindent = indent;
17275
+ } else {
17276
+ mindent = Math.min(mindent, indent);
17277
+ }
17278
+ }
17279
+ }
17280
+ if (mindent !== null) {
17281
+ const m = mindent;
17282
+ result = lines.map((l) => l[0] === " " || l[0] === "\t" ? l.slice(m) : l).join(`
17283
+ `);
17284
+ }
17285
+ if (trimWhitespace) {
17286
+ result = result.trim();
17287
+ }
17288
+ if (escapeSpecialCharacters) {
17289
+ result = result.replace(/\\n/g, `
17290
+ `).replace(/\\t/g, "\t").replace(/\\r/g, "\r").replace(/\\v/g, "\v").replace(/\\b/g, "\b").replace(/\\f/g, "\f").replace(/\\0/g, "\x00").replace(/\\x([\da-fA-F]{2})/g, (_, h) => String.fromCharCode(parseInt(h, 16))).replace(/\\u\{([\da-fA-F]{1,6})\}/g, (_, h) => String.fromCodePoint(parseInt(h, 16))).replace(/\\u([\da-fA-F]{4})/g, (_, h) => String.fromCharCode(parseInt(h, 16)));
17291
+ }
17292
+ if (typeof Bun !== "undefined") {
17293
+ result = result.replace(/\\u(?:\{([\da-fA-F]{1,6})\}|([\da-fA-F]{4}))/g, (_, braced, unbraced) => {
17294
+ var _ref;
17295
+ const hex = (_ref = braced !== null && braced !== undefined ? braced : unbraced) !== null && _ref !== undefined ? _ref : "";
17296
+ return String.fromCodePoint(parseInt(hex, 16));
17297
+ });
17298
+ }
17299
+ return result;
17300
+ }
17301
+ }
17302
+ function alignValue(value, precedingText) {
17303
+ if (typeof value !== "string" || !value.includes(`
17304
+ `)) {
17305
+ return value;
17306
+ }
17307
+ const currentLine = precedingText.slice(precedingText.lastIndexOf(`
17308
+ `) + 1);
17309
+ const indentMatch = currentLine.match(/^(\s+)/);
17310
+ if (indentMatch) {
17311
+ const indent = indentMatch[1];
17312
+ return value.replace(/\n/g, `
17313
+ ${indent}`);
17314
+ }
17315
+ return value;
17316
+ }
17317
+
16752
17318
  // src/utils/colour.ts
16753
17319
  var import_picocolors = __toESM(require_picocolors(), 1);
16754
17320
  var c3 = import_picocolors.default;
16755
17321
 
17322
+ // src/program/commands/config/init/command.ts
17323
+ var initCommand = new Command("init").description(dedent_default`
17324
+ Initialises a git swarm config.
17325
+ Creates a ${CONFIG_FILE_NAME} if they don't exist.
17326
+ This does not run 'git init'
17327
+ `).option("--force", "Force creation of a fresh config file even if one already exists").passThroughOptions().action(async (options) => {
17328
+ if (!options.force && await config2.exists()) {
17329
+ console.log(c3.red(`${CONFIG_FILE_NAME} already exists.`));
17330
+ process.exit();
17331
+ }
17332
+ const programOptions = getProgramOptions();
17333
+ const root = process.cwd();
17334
+ const repos = await Array.fromAsync(findGitRepositories(root, programOptions.where, {
17335
+ skipConfig: true
17336
+ }));
17337
+ repos.sort((a, b) => a.path.relative.localeCompare(b.path.relative));
17338
+ const configData = {
17339
+ ...defaultConfig,
17340
+ repositories: repos.map((repo) => ({ path: repo.path.relative }))
17341
+ };
17342
+ await config2.write(configData);
17343
+ console.log(c3.green(`${CONFIG_FILE_NAME} created successfully, with ${repos.length} repositores.`));
17344
+ });
17345
+
17346
+ // src/program/commands/config/refresh/command.ts
17347
+ var refreshCommand = new Command("refresh").description(dedent_default`
17348
+ Refreshes the git swarm config with new repositores.
17349
+ Other options are kept as is.'
17350
+ `).option("--force", "Force creation of a fresh config file even if one already exists").passThroughOptions().action(async () => {
17351
+ if (!await config2.exists()) {
17352
+ console.log(c3.red(`${CONFIG_FILE_NAME} missing. Run ${c3.bold("`git swarm init`")} first`));
17353
+ process.exit();
17354
+ }
17355
+ const oldConfig = await config2.get();
17356
+ const programOptions = getProgramOptions();
17357
+ const root = process.cwd();
17358
+ const repos = await Array.fromAsync(findGitRepositories(root, programOptions.where, {
17359
+ skipConfig: true
17360
+ }));
17361
+ repos.sort((a, b) => a.path.relative.localeCompare(b.path.relative));
17362
+ const configData = {
17363
+ ...oldConfig,
17364
+ repositories: repos.map((repo) => ({ path: repo.path.relative }))
17365
+ };
17366
+ await config2.write(configData);
17367
+ console.log(c3.green(`${CONFIG_FILE_NAME} refreshed successfully, with ${repos.length} repositores.`));
17368
+ });
17369
+
17370
+ // src/program/commands/config/command.ts
17371
+ var configCommand = new Command("config").description("Commands related to git swarm configuration").enablePositionalOptions().addCommand(initCommand).addCommand(refreshCommand);
17372
+
17373
+ // src/program/commands/diff/command.ts
17374
+ var getFileRow = (path, file) => {
17375
+ if (file.binary) {
17376
+ return [path, c3.gray(file.file), c3.gray("binary"), "", ""];
17377
+ }
17378
+ return [
17379
+ path,
17380
+ file.file,
17381
+ String(file.changes),
17382
+ c3.green(`+${file.insertions}`),
17383
+ c3.red(`-${file.deletions}`)
17384
+ ];
17385
+ };
17386
+ var getStatRow = (path, summary) => [
17387
+ path,
17388
+ String(summary.changed),
17389
+ c3.green(`+${summary.insertions}`),
17390
+ c3.red(`-${summary.deletions}`)
17391
+ ];
17392
+ var diffCommand = new Command("diff").description("Show changes across all repositories").argument("[rev]").option("--cached", "show changes staged in the index").option("--stat", "show the diff summary for each repository").action(async (rev, options) => {
17393
+ const programOptions = getProgramOptions();
17394
+ const root = process.cwd();
17395
+ const diffArgs = options.cached ? ["--cached"] : [];
17396
+ const results = await forEachRepo(root, "diffing repositories", async ({ path, git }) => {
17397
+ const summary = await git.diffSummary(rev ? [rev, ...diffArgs] : diffArgs).catch(catchError);
17398
+ if (summary instanceof Error) {
17399
+ return null;
17400
+ }
17401
+ if (summary.changed === 0) {
17402
+ return null;
17403
+ }
17404
+ return { path: path.relative, summary };
17405
+ }, programOptions);
17406
+ const diffs = filterNotNull(results);
17407
+ if (diffs.length === 0) {
17408
+ return;
17409
+ }
17410
+ const head = options.stat ? ["path", "files", "insertions", "deletions"] : ["path", "file", "changes", "insertions", "deletions"];
17411
+ const table = new CliTable({ head });
17412
+ for (const { path, summary } of diffs) {
17413
+ if (options.stat) {
17414
+ table.push(getStatRow(path, summary));
17415
+ } else {
17416
+ table.push(...summary.files.map((file) => getFileRow(path, file)));
17417
+ }
17418
+ }
17419
+ console.log(table.toString());
17420
+ const repos = diffs.length;
17421
+ const files = diffs.reduce((sum, d) => sum + d.summary.changed, 0);
17422
+ const insertions = diffs.reduce((sum, d) => sum + d.summary.insertions, 0);
17423
+ const deletions = diffs.reduce((sum, d) => sum + d.summary.deletions, 0);
17424
+ console.log([
17425
+ c3.bold(`${repos} repos`),
17426
+ c3.gray("\xB7"),
17427
+ c3.bold(`${files} files`),
17428
+ c3.gray("\xB7"),
17429
+ c3.green(`+${insertions}`),
17430
+ c3.red(`-${deletions}`)
17431
+ ].join(" "));
17432
+ process.exitCode = 1;
17433
+ });
17434
+
16756
17435
  // src/program/commands/exec/command.ts
16757
17436
  var runCommand = async (name, cwd, command) => {
16758
17437
  const subprocess = Bun.spawn({
@@ -17057,10 +17736,12 @@ var statusCommand = new Command("status").description("Show the working tree sta
17057
17736
  });
17058
17737
 
17059
17738
  // src/program/options/parallel.ts
17060
- var parallelOption = new Option("--parallel <count>", "run git in parallel").default(1, "sequential").argParser((value) => Number(value));
17739
+ var defaultValue = await config2.getOption("parallel");
17740
+ var parallelOption = new Option("--parallel <count>", "run git in parallel").default(defaultValue, defaultValue === 1 ? "sequential" : defaultValue.toString()).argParser((value) => Number(value));
17061
17741
 
17062
17742
  // src/program/options/where.ts
17063
- var whereOption = new Option("--where <query>", "filter repos by a query string").default({}, "all repos").argParser((value) => {
17743
+ var defaultWhere = await config2.getOption("where");
17744
+ var whereOption = new Option("--where <query>", "filter repos by a query string").default(parseQueryString(defaultWhere), defaultWhere || "all repos").argParser((value) => {
17064
17745
  const parsed = parseQueryString(value);
17065
17746
  if (parsed instanceof Error) {
17066
17747
  throw new InvalidArgumentError(parsed.message);
@@ -17071,7 +17752,7 @@ var whereOption = new Option("--where <query>", "filter repos by a query string"
17071
17752
  // src/program/index.ts
17072
17753
  var program2 = new Command;
17073
17754
  var getProgramOptions = () => program2.opts();
17074
- program2.name("git-swarm").description(package_default.description).version(package_default.version).enablePositionalOptions().addOption(parallelOption).addOption(whereOption).addCommand(checkoutCommand).addCommand(execCommand).addCommand(fetchCommand).addCommand(findBranchCommand).addCommand(grepCommand).addCommand(listCommand).addCommand(pullCommand).addCommand(remoteCommand).addCommand(statusCommand);
17755
+ program2.name("git-swarm").description(package_default.description).version(package_default.version).enablePositionalOptions().addOption(parallelOption).addOption(whereOption).addCommand(checkoutCommand).addCommand(configCommand).addCommand(diffCommand).addCommand(execCommand).addCommand(fetchCommand).addCommand(findBranchCommand).addCommand(grepCommand).addCommand(listCommand).addCommand(pullCommand).addCommand(remoteCommand).addCommand(statusCommand);
17075
17756
 
17076
17757
  // src/cli.ts
17077
17758
  program2.parse();
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@imfelixyeung/git-swarm",
3
3
  "description": "Manage multiple Git repositories with ease.",
4
- "version": "0.0.4",
4
+ "version": "0.1.0",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "https://github.com/imfelixyeung/git-swarm"
@@ -22,18 +22,29 @@
22
22
  "typecheck": "tsc --noEmit",
23
23
  "biome": "biome check",
24
24
  "ci": "biome ci",
25
- "test": "bun test"
25
+ "test": "bun test",
26
+ "config-json-schema:update": "bun run src/scripts/make-config-json-schema.ts > src/config/schema.json",
27
+ "changeset": "changeset",
28
+ "changeset:version": "changeset version && biome check --write",
29
+ "release": "bun run build && changeset publish"
26
30
  },
27
31
  "devDependencies": {
28
32
  "@biomejs/biome": "2.5.12",
33
+ "@changesets/changelog-github": "^1.0.1",
34
+ "@changesets/cli": "^3.0.2",
29
35
  "@types/bun": "latest"
30
36
  },
37
+ "publishConfig": {
38
+ "access": "public",
39
+ "provenance": true
40
+ },
31
41
  "peerDependencies": {
32
42
  "typescript": "^7"
33
43
  },
34
44
  "dependencies": {
35
45
  "cli-table3": "^0.6.5",
36
46
  "commander": "^15.0.0",
47
+ "dedent": "^1.7.2",
37
48
  "p-limit": "^7.3.2",
38
49
  "picocolors": "^1.1.1",
39
50
  "simple-git": "^3.36.0",