@savvy-web/silk-effects 5.8.0 → 5.9.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.
package/index.d.ts CHANGED
@@ -9188,6 +9188,25 @@ declare const ReposRestoreResult: Schema.Struct<{
9188
9188
  }>;
9189
9189
  /** @public */
9190
9190
  type ReposRestoreResult = typeof ReposRestoreResult.Type;
9191
+ /**
9192
+ * Result of clearing a stale submodule registration from the superproject's
9193
+ * LOCAL git config.
9194
+ *
9195
+ * @remarks
9196
+ * `section` is the registration name the section was cleared under (e.g.
9197
+ * `.repos/effect-old`), and `removedKeys` lists the full config keys the
9198
+ * removed section actually carried — the achieved-state report, read from the
9199
+ * config BEFORE the removal so a caller can see exactly what is gone. There
9200
+ * is no `commitMessage`: the local config is unversioned, so a deregister
9201
+ * stages nothing and leaves nothing to commit.
9202
+ * @public
9203
+ */
9204
+ declare const ReposDeregisterResult: Schema.Struct<{
9205
+ readonly section: Schema.String;
9206
+ readonly removedKeys: Schema.$Array<Schema.String>;
9207
+ }>;
9208
+ /** @public */
9209
+ type ReposDeregisterResult = typeof ReposDeregisterResult.Type;
9191
9210
  /**
9192
9211
  * Result of an agent-note mutation against a vendored repo.
9193
9212
  * @public
@@ -9233,6 +9252,24 @@ declare class ReposConfigStore extends ReposConfigStore_base {
9233
9252
  }
9234
9253
  //#endregion
9235
9254
  //#region src/repos/services/drift.d.ts
9255
+ /**
9256
+ * Extracts the submodule NAME from a `submodule.<name>.<property>` config key.
9257
+ *
9258
+ * @remarks
9259
+ * A submodule's name is routinely a PATH (`git submodule add` derives the
9260
+ * section name from the path), so the name itself contains dots — which means
9261
+ * the key cannot be split on `.` and read positionally. Strip the fixed
9262
+ * `submodule.` prefix and the final `.<property>` segment instead; everything
9263
+ * between is the name, dots and all. Returns `undefined` for any key that is
9264
+ * not a `submodule.*.*` key.
9265
+ *
9266
+ * Shared with `ReposManager.deregister`, which must attribute config keys to
9267
+ * a registration name the exact same way this check does — two parsers here
9268
+ * would let the report and the remedy disagree about what a section is named.
9269
+ *
9270
+ * @internal
9271
+ */
9272
+ declare const submoduleNameFromKey: (key: string) => string | undefined;
9236
9273
  /**
9237
9274
  * The {@link ReposDrift} service shape.
9238
9275
  * @public
@@ -9424,6 +9461,31 @@ interface ReposManagerShape {
9424
9461
  */
9425
9462
  readonly rename: (root: string, oldName: string, newName: string) => Effect.Effect<ReposRenameResult, ReposConfigError | GitSubmoduleError | RepoNotFoundError | ReposLockdownError>;
9426
9463
  readonly restore: (root: string, names?: ReadonlyArray<string>) => Effect.Effect<ReposRestoreResult, ReposConfigError | GitSubmoduleError | RepoNotFoundError | ReposLockdownError>;
9464
+ /**
9465
+ * Clears a STALE submodule registration — a `submodule.<section>.*`
9466
+ * section in the superproject's LOCAL git config left behind by a rename
9467
+ * or an unvendoring, which `git submodule status` then lists as a phantom
9468
+ * entry the manifest has never heard of (`ReposDrift` reports it as
9469
+ * `localRegistrationDivergence`).
9470
+ *
9471
+ * `section` is the registration name exactly as the drift report states it
9472
+ * (e.g. `.repos/effect-old`) — the `submodule.` prefix is implied, never
9473
+ * passed. Refuses (typed `ReposConfigError`): a section outside
9474
+ * `.repos/` (this machinery does not own it — it may be a real submodule
9475
+ * of the host repo), the canonical registration of a live manifest entry,
9476
+ * and a registration whose module gitdir still backs a live entry under a
9477
+ * DIVERGED name (the same gitdir attribution `ReposDrift` uses — that
9478
+ * state's remedy is a re-vendor, not a deregister) — UNLESS that entry's
9479
+ * canonical section is also registered, in which case the old-named twin
9480
+ * is a genuine orphan and removing it is a crashed-rename recovery's own
9481
+ * final step. Reconciling a LIVE
9482
+ * registration is `sync`'s job and unvendoring is `remove`'s, so
9483
+ * deregistration only ever touches state nothing else owns. Touches only
9484
+ * the superproject's local config — no vendored worktree — so unlike the
9485
+ * other mutating ops it needs no lockdown bracket and stages nothing to
9486
+ * commit.
9487
+ */
9488
+ readonly deregister: (root: string, section: string) => Effect.Effect<ReposDeregisterResult, ReposConfigError | GitSubmoduleError>;
9427
9489
  }
9428
9490
  declare const ReposManager_base: Context.ServiceClass<ReposManager, "@savvy-web/silk-effects/ReposManager", ReposManagerShape>;
9429
9491
  /**
@@ -9431,8 +9493,10 @@ declare const ReposManager_base: Context.ServiceClass<ReposManager, "@savvy-web/
9431
9493
  * (presence, dirtiness, stale notes), reconciles the working tree with the
9432
9494
  * manifest, vendors new entries (`add`), re-pins existing entries to a new
9433
9495
  * ref (`pin`), adds/removes/promotes agent notes (`note`), unvendors
9434
- * (`remove`), renames (`rename`), and explicitly hard-resets dirty
9435
- * checkouts back to their pinned commit (`restore`).
9496
+ * (`remove`), renames (`rename`), explicitly hard-resets dirty
9497
+ * checkouts back to their pinned commit (`restore`), and clears stale
9498
+ * submodule registrations from the superproject's local git config
9499
+ * (`deregister`).
9436
9500
  * @public
9437
9501
  */
9438
9502
  declare class ReposManager extends ReposManager_base {
@@ -9450,7 +9514,7 @@ declare class ReposManager extends ReposManager_base {
9450
9514
  static readonly layer: Layer.Layer<ReposManager, never, ReposConfigStore | Git | FileSystem.FileSystem | Path.Path | ReposLockdown>;
9451
9515
  }
9452
9516
  declare namespace index_d_exports$4 {
9453
- export { DriftKind, GitSubmoduleError, GitSubmoduleErrorBase, MANIFEST_PATH, NOTE_LIMIT, NoteNotFoundError, NoteNotFoundErrorBase, REPOS_DIR, RepoDrift, RepoEntry, RepoName, RepoNotFoundError, RepoNotFoundErrorBase, RepoNote, RepoOrientation, RepoStatusEntry, ReposAddResult, ReposConfigError, ReposConfigErrorBase, ReposConfigStore, ReposConfigStoreShape, ReposDrift, ReposDriftReport, ReposDriftShape, ReposLockdown, ReposLockdownError, ReposLockdownErrorBase, ReposLockdownShape, ReposManager, ReposManagerShape, ReposManifestFile, ReposNoteResult, ReposPinResult, ReposRemoveResult, ReposRenameResult, ReposRestoreResult, ReposStatusReport, ReposSyncReport, STALE_LOCK_MAX_AGE_MS, resolveModuleDir };
9517
+ export { DriftKind, GitSubmoduleError, GitSubmoduleErrorBase, MANIFEST_PATH, NOTE_LIMIT, NoteNotFoundError, NoteNotFoundErrorBase, REPOS_DIR, RepoDrift, RepoEntry, RepoName, RepoNotFoundError, RepoNotFoundErrorBase, RepoNote, RepoOrientation, RepoStatusEntry, ReposAddResult, ReposConfigError, ReposConfigErrorBase, ReposConfigStore, ReposConfigStoreShape, ReposDeregisterResult, ReposDrift, ReposDriftReport, ReposDriftShape, ReposLockdown, ReposLockdownError, ReposLockdownErrorBase, ReposLockdownShape, ReposManager, ReposManagerShape, ReposManifestFile, ReposNoteResult, ReposPinResult, ReposRemoveResult, ReposRenameResult, ReposRestoreResult, ReposStatusReport, ReposSyncReport, STALE_LOCK_MAX_AGE_MS, resolveModuleDir, submoduleNameFromKey };
9454
9518
  }
9455
9519
  //#endregion
9456
9520
  //#region src/schemas/BiomeConfig.d.ts
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/silk-effects",
3
- "version": "5.8.0",
3
+ "version": "5.9.0",
4
4
  "private": false,
5
5
  "description": "Shared Effect library for Silk Suite conventions",
6
6
  "homepage": "https://github.com/savvy-web/systems/tree/main/packages/silk-effects",
@@ -34,13 +34,13 @@
34
34
  "@changesets/get-github-info": "^1.0.0",
35
35
  "@changesets/get-release-plan": "^5.0.0",
36
36
  "@effected/commands": "^0.4.0",
37
- "@effected/git": "^0.7.0",
37
+ "@effected/git": "^0.8.0",
38
38
  "@effected/glob": "^0.3.0",
39
39
  "@effected/jsonc": "^0.6.0",
40
- "@effected/package-json": "^0.8.0",
40
+ "@effected/package-json": "^0.9.0",
41
41
  "@effected/templates": "^0.2.0",
42
42
  "@effected/walker": "^0.4.0",
43
- "@effected/workspaces": "^0.12.0",
43
+ "@effected/workspaces": "^0.13.0",
44
44
  "@effected/yaml": "^0.8.0",
45
45
  "@manypkg/get-packages": "^3.1.0",
46
46
  "mdast-util-heading-range": "^4.0.0",
package/repos/index.js CHANGED
@@ -3,10 +3,10 @@ import { MANIFEST_PATH, REPOS_DIR } from "./constants.js";
3
3
  import { GitSubmoduleError, GitSubmoduleErrorBase, NoteNotFoundError, NoteNotFoundErrorBase, RepoNotFoundError, RepoNotFoundErrorBase, ReposConfigError, ReposConfigErrorBase, ReposLockdownError, ReposLockdownErrorBase } from "./errors.js";
4
4
  import { DriftKind, RepoDrift, ReposDriftReport } from "./schemas/drift.js";
5
5
  import { RepoEntry, RepoName, RepoNote, RepoOrientation, ReposManifestFile } from "./schemas/manifest.js";
6
- import { RepoStatusEntry, ReposAddResult, ReposNoteResult, ReposPinResult, ReposRemoveResult, ReposRenameResult, ReposRestoreResult, ReposStatusReport, ReposSyncReport } from "./schemas/reports.js";
6
+ import { RepoStatusEntry, ReposAddResult, ReposDeregisterResult, ReposNoteResult, ReposPinResult, ReposRemoveResult, ReposRenameResult, ReposRestoreResult, ReposStatusReport, ReposSyncReport } from "./schemas/reports.js";
7
7
  import { ReposConfigStore } from "./services/config-store.js";
8
8
  import { ReposLockdown, resolveModuleDir } from "./services/lockdown.js";
9
- import { ReposDrift } from "./services/drift.js";
9
+ import { ReposDrift, submoduleNameFromKey } from "./services/drift.js";
10
10
  import { ReposManager, STALE_LOCK_MAX_AGE_MS } from "./services/manager.js";
11
11
 
12
12
  //#region src/repos/index.ts
@@ -31,6 +31,7 @@ var repos_exports = /* @__PURE__ */ __exportAll({
31
31
  ReposConfigError: () => ReposConfigError,
32
32
  ReposConfigErrorBase: () => ReposConfigErrorBase,
33
33
  ReposConfigStore: () => ReposConfigStore,
34
+ ReposDeregisterResult: () => ReposDeregisterResult,
34
35
  ReposDrift: () => ReposDrift,
35
36
  ReposDriftReport: () => ReposDriftReport,
36
37
  ReposLockdown: () => ReposLockdown,
@@ -46,8 +47,9 @@ var repos_exports = /* @__PURE__ */ __exportAll({
46
47
  ReposStatusReport: () => ReposStatusReport,
47
48
  ReposSyncReport: () => ReposSyncReport,
48
49
  STALE_LOCK_MAX_AGE_MS: () => STALE_LOCK_MAX_AGE_MS,
49
- resolveModuleDir: () => resolveModuleDir
50
+ resolveModuleDir: () => resolveModuleDir,
51
+ submoduleNameFromKey: () => submoduleNameFromKey
50
52
  });
51
53
 
52
54
  //#endregion
53
- export { DriftKind, GitSubmoduleError, GitSubmoduleErrorBase, MANIFEST_PATH, NoteNotFoundError, NoteNotFoundErrorBase, REPOS_DIR, RepoDrift, RepoEntry, RepoName, RepoNotFoundError, RepoNotFoundErrorBase, RepoNote, RepoOrientation, RepoStatusEntry, ReposAddResult, ReposConfigError, ReposConfigErrorBase, ReposConfigStore, ReposDrift, ReposDriftReport, ReposLockdown, ReposLockdownError, ReposLockdownErrorBase, ReposManager, ReposManifestFile, ReposNoteResult, ReposPinResult, ReposRemoveResult, ReposRenameResult, ReposRestoreResult, ReposStatusReport, ReposSyncReport, STALE_LOCK_MAX_AGE_MS, repos_exports, resolveModuleDir };
55
+ export { DriftKind, GitSubmoduleError, GitSubmoduleErrorBase, MANIFEST_PATH, NoteNotFoundError, NoteNotFoundErrorBase, REPOS_DIR, RepoDrift, RepoEntry, RepoName, RepoNotFoundError, RepoNotFoundErrorBase, RepoNote, RepoOrientation, RepoStatusEntry, ReposAddResult, ReposConfigError, ReposConfigErrorBase, ReposConfigStore, ReposDeregisterResult, ReposDrift, ReposDriftReport, ReposLockdown, ReposLockdownError, ReposLockdownErrorBase, ReposManager, ReposManifestFile, ReposNoteResult, ReposPinResult, ReposRemoveResult, ReposRenameResult, ReposRestoreResult, ReposStatusReport, ReposSyncReport, STALE_LOCK_MAX_AGE_MS, repos_exports, resolveModuleDir, submoduleNameFromKey };
@@ -150,6 +150,23 @@ const ReposRestoreResult = Schema.Struct({
150
150
  stillDirty: Schema.Array(Schema.String)
151
151
  });
152
152
  /**
153
+ * Result of clearing a stale submodule registration from the superproject's
154
+ * LOCAL git config.
155
+ *
156
+ * @remarks
157
+ * `section` is the registration name the section was cleared under (e.g.
158
+ * `.repos/effect-old`), and `removedKeys` lists the full config keys the
159
+ * removed section actually carried — the achieved-state report, read from the
160
+ * config BEFORE the removal so a caller can see exactly what is gone. There
161
+ * is no `commitMessage`: the local config is unversioned, so a deregister
162
+ * stages nothing and leaves nothing to commit.
163
+ * @public
164
+ */
165
+ const ReposDeregisterResult = Schema.Struct({
166
+ section: Schema.String,
167
+ removedKeys: Schema.Array(Schema.String)
168
+ });
169
+ /**
153
170
  * Result of an agent-note mutation against a vendored repo.
154
171
  * @public
155
172
  */
@@ -165,4 +182,4 @@ const ReposNoteResult = Schema.Struct({
165
182
  });
166
183
 
167
184
  //#endregion
168
- export { RepoStatusEntry, ReposAddResult, ReposNoteResult, ReposPinResult, ReposRemoveResult, ReposRenameResult, ReposRestoreResult, ReposStatusReport, ReposSyncReport };
185
+ export { RepoStatusEntry, ReposAddResult, ReposDeregisterResult, ReposNoteResult, ReposPinResult, ReposRemoveResult, ReposRenameResult, ReposRestoreResult, ReposStatusReport, ReposSyncReport };
@@ -17,6 +17,12 @@ import { Git, Gitmodules } from "@effected/git";
17
17
  * `submodule.` prefix and the final `.<property>` segment instead; everything
18
18
  * between is the name, dots and all. Returns `undefined` for any key that is
19
19
  * not a `submodule.*.*` key.
20
+ *
21
+ * Shared with `ReposManager.deregister`, which must attribute config keys to
22
+ * a registration name the exact same way this check does — two parsers here
23
+ * would let the report and the remedy disagree about what a section is named.
24
+ *
25
+ * @internal
20
26
  */
21
27
  const submoduleNameFromKey = (key) => {
22
28
  if (!key.startsWith("submodule.")) return;
@@ -91,7 +97,7 @@ var ReposDrift = class extends Context.Service()("@savvy-web/silk-effects/ReposD
91
97
  found.push(RepoDrift.make({
92
98
  name: registeredName,
93
99
  kind: "localRegistrationDivergence",
94
- detail: `local git config registers submodule "${registeredName}", which matches no manifest entry — a stale registration left behind by a rename or an unvendoring; clear it with \`git config --remove-section submodule.${registeredName}\``,
100
+ detail: `local git config registers submodule "${registeredName}", which matches no manifest entry — a stale registration left behind by a rename or an unvendoring; clear it with \`savvy repos deregister ${registeredName}\``,
95
101
  observedValue: registeredName
96
102
  }));
97
103
  }
@@ -247,4 +253,4 @@ var ReposDrift = class extends Context.Service()("@savvy-web/silk-effects/ReposD
247
253
  };
248
254
 
249
255
  //#endregion
250
- export { ReposDrift };
256
+ export { ReposDrift, submoduleNameFromKey };
@@ -3,6 +3,7 @@ import { GitSubmoduleError, NoteNotFoundError, RepoNotFoundError, ReposConfigErr
3
3
  import { RepoName } from "../schemas/manifest.js";
4
4
  import { ReposConfigStore } from "./config-store.js";
5
5
  import { ReposLockdown, resolveModuleDir } from "./lockdown.js";
6
+ import { submoduleNameFromKey } from "./drift.js";
6
7
  import { Clock, Context, Effect, Exit, FileSystem, Layer, Option, Path, Result, Schema } from "effect";
7
8
  import { Git, GitConfig, Gitmodules, LsRemoteEntry } from "@effected/git";
8
9
  import { createHash } from "node:crypto";
@@ -31,8 +32,10 @@ const STALE_LOCK_MAX_AGE_MS = 6e5;
31
32
  * (presence, dirtiness, stale notes), reconciles the working tree with the
32
33
  * manifest, vendors new entries (`add`), re-pins existing entries to a new
33
34
  * ref (`pin`), adds/removes/promotes agent notes (`note`), unvendors
34
- * (`remove`), renames (`rename`), and explicitly hard-resets dirty
35
- * checkouts back to their pinned commit (`restore`).
35
+ * (`remove`), renames (`rename`), explicitly hard-resets dirty
36
+ * checkouts back to their pinned commit (`restore`), and clears stale
37
+ * submodule registrations from the superproject's local git config
38
+ * (`deregister`).
36
39
  * @public
37
40
  */
38
41
  var ReposManager = class extends Context.Service()("@savvy-web/silk-effects/ReposManager") {
@@ -158,12 +161,12 @@ var ReposManager = class extends Context.Service()("@savvy-web/silk-effects/Repo
158
161
  yield* git.add(root, [".gitmodules", repoPathRel]).pipe(Effect.mapError(asSubmoduleError(`git add .gitmodules ${repoPathRel}`, root)));
159
162
  registered.push(name);
160
163
  } else if (!present) {
161
- yield* git.configSet(root, updateKey, "checkout").pipe(Effect.mapError(asSubmoduleError(`git config ${updateKey} checkout`, root)));
162
164
  yield* git.submoduleUpdate(root, {
163
165
  init: true,
166
+ checkout: true,
164
167
  depth: 1,
165
168
  paths: [repoPathRel]
166
- }).pipe(Effect.mapError(asSubmoduleError(`git submodule update --init --depth 1 -- ${repoPathRel}`, root)), Effect.ensuring(Effect.ignore(assertBoundaryMarker)));
169
+ }).pipe(Effect.mapError(asSubmoduleError(`git submodule update --init --checkout --depth 1 -- ${repoPathRel}`, root)));
167
170
  initialized.push(name);
168
171
  } else upToDate.push(name);
169
172
  if (entry.sparse && entry.sparse.length > 0) {
@@ -647,6 +650,70 @@ var ReposManager = class extends Context.Service()("@savvy-web/silk-effects/Repo
647
650
  stillDirty
648
651
  };
649
652
  });
653
+ /**
654
+ * Resolve the file git's `--local` scope actually targets for `root`.
655
+ * A plain checkout is `<root>/.git/config`; a LINKED WORKTREE's
656
+ * `<root>/.git` is a pointer FILE whose `gitdir:` names
657
+ * `.git/worktrees/<name>`, and the local config git uses there is the
658
+ * SHARED one at the main gitdir named by that dir's `commondir` file.
659
+ * Every unreadable/malformed step degrades to the plain
660
+ * `<root>/.git/config` path rather than failing — the same
661
+ * never-fails posture as `resolveModuleDir`.
662
+ */
663
+ const resolveLocalConfigPath = (root) => Effect.gen(function* () {
664
+ const dotGit = path.join(root, ".git");
665
+ const fallback = path.join(dotGit, "config");
666
+ const info = yield* fs.stat(dotGit).pipe(Effect.option);
667
+ if (Option.isNone(info) || info.value.type === "Directory") return fallback;
668
+ const content = yield* fs.readFileString(dotGit).pipe(Effect.option);
669
+ if (Option.isNone(content)) return fallback;
670
+ const match = /^gitdir:\s*(.+)$/m.exec(content.value);
671
+ if (!match?.[1]) return fallback;
672
+ const pointer = match[1].trim();
673
+ const gitdir = path.isAbsolute(pointer) ? pointer : path.resolve(root, pointer);
674
+ const commondir = yield* fs.readFileString(path.join(gitdir, "commondir")).pipe(Effect.option);
675
+ if (Option.isSome(commondir)) {
676
+ const common = commondir.value.trim();
677
+ return path.join(path.isAbsolute(common) ? common : path.resolve(gitdir, common), "config");
678
+ }
679
+ return path.join(gitdir, "config");
680
+ });
681
+ const deregister = (root, section) => Effect.gen(function* () {
682
+ if (!section.startsWith(`${".repos"}/`) || section === `${".repos"}/`) return yield* Effect.fail(new ReposConfigError({
683
+ path: MANIFEST_PATH,
684
+ reason: `"${section}" is not a ${REPOS_DIR}/ registration — deregister only clears stale vendored-repo sections; clear an unrelated submodule registration with git directly`,
685
+ kind: "invalid"
686
+ }));
687
+ const manifest = yield* configStore.read(root).pipe(Effect.catchTag("ReposConfigError", (error) => error.kind === "missing" ? Effect.succeed({ repos: {} }) : Effect.fail(error)));
688
+ for (const name of Object.keys(manifest.repos)) if (section === `${".repos"}/${name}`) return yield* Effect.fail(new ReposConfigError({
689
+ path: MANIFEST_PATH,
690
+ reason: `"${section}" is the canonical registration of manifest entry "${name}" — deregister clears STALE registrations only; use remove to unvendor, or sync to reconcile`,
691
+ kind: "invalid"
692
+ }));
693
+ const localConfigPath = yield* resolveLocalConfigPath(root);
694
+ const configEntries = yield* git.configList(root, { file: localConfigPath }).pipe(Effect.mapError(asSubmoduleError(`git config -f ${localConfigPath} --list`, root)));
695
+ const registeredNames = new Set(configEntries.map((configEntry) => submoduleNameFromKey(configEntry.key)).filter((registeredName) => registeredName !== void 0));
696
+ const removedKeys = configEntries.map((configEntry) => configEntry.key).filter((key) => submoduleNameFromKey(key) === section);
697
+ if (removedKeys.length === 0) return yield* Effect.fail(new ReposConfigError({
698
+ path: localConfigPath,
699
+ reason: `no submodule.${section} section registered in the local git config — nothing to deregister`,
700
+ kind: "invalid"
701
+ }));
702
+ const modulesRoot = path.join(root, ".git", "modules");
703
+ for (const name of Object.keys(manifest.repos)) {
704
+ const moduleDir = yield* resolveModuleDir(fs, path, root, name);
705
+ if (path.relative(modulesRoot, moduleDir) === section && !registeredNames.has(`${".repos"}/${name}`)) return yield* Effect.fail(new ReposConfigError({
706
+ path: MANIFEST_PATH,
707
+ reason: `"${section}" is the registration backing manifest entry "${name}" (its module gitdir lives there under a diverged name, and no canonical submodule.${REPOS_DIR}/${name} registration exists) — deregister clears STALE registrations only; re-vendor the entry instead (remove, then add with the orientation the remove result hands back)`,
708
+ kind: "invalid"
709
+ }));
710
+ }
711
+ yield* git.configRemoveSection(root, `submodule.${section}`).pipe(Effect.mapError(asSubmoduleError(`git config --remove-section submodule.${section}`, root)));
712
+ return {
713
+ section,
714
+ removedKeys
715
+ };
716
+ });
650
717
  return {
651
718
  status,
652
719
  sync,
@@ -655,7 +722,8 @@ var ReposManager = class extends Context.Service()("@savvy-web/silk-effects/Repo
655
722
  note,
656
723
  remove,
657
724
  rename,
658
- restore
725
+ restore,
726
+ deregister
659
727
  };
660
728
  }));
661
729
  };