@savvy-web/cli 1.5.10 → 1.6.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/cli/index.js CHANGED
@@ -4,9 +4,10 @@ import { cleanCommand } from "../commands/clean.js";
4
4
  import { commitCommand } from "../commands/commit/index.js";
5
5
  import { initCommand } from "../commands/init.js";
6
6
  import { lintCommand } from "../commands/lint/index.js";
7
+ import { reposCommand } from "../commands/repos/index.js";
7
8
  import { Command } from "@effect/cli";
8
9
  import { NodeContext, NodeRuntime } from "@effect/platform-node";
9
- import { BiomeSchemaSyncLive, ChangesetConfigLive, ChangesetConfigReaderLive, Changesets, ConfigDiscoveryLive, ManagedSectionLive, SilkPublishabilityDetectorLive, ToolDiscoveryLive, VersioningStrategyLive } from "@savvy-web/silk-effects";
10
+ import { BiomeSchemaSyncLive, ChangesetConfigLive, ChangesetConfigReaderLive, Changesets, ConfigDiscoveryLive, ManagedSectionLive, Repos, SilkPublishabilityDetectorLive, ToolDiscoveryLive, VersioningStrategyLive } from "@savvy-web/silk-effects";
10
11
  import { Effect, Layer } from "effect";
11
12
  import { PackageManagerDetectorLive, PointInTimeWorkspaceLive, PublishabilityDetectorLive, WorkspaceDiscoveryLive, WorkspaceRootLive } from "workspaces-effect";
12
13
 
@@ -52,11 +53,12 @@ const rootCommand = Command.make("savvy").pipe(Command.withSubcommands([
52
53
  cleanCommand,
53
54
  commitCommand,
54
55
  changesetCommand,
55
- lintCommand
56
+ lintCommand,
57
+ reposCommand
56
58
  ]));
57
59
  const cli = Command.run(rootCommand, {
58
60
  name: "savvy",
59
- version: "1.5.10"
61
+ version: "1.6.1"
60
62
  });
61
63
  /**
62
64
  * Shared base layer: workspace services, the changeset config reader, and the
@@ -128,7 +130,13 @@ const InspectorAndAnalyzerLive = Changesets.BranchAnalyzerLive.pipe(Layer.provid
128
130
  * remaining `CommandExecutor`/`FileSystem`/`Path` flow up to `NodeContext.layer`.
129
131
  */
130
132
  const DepsRegenGroupLive = Changesets.DepsRegenLive.pipe(Layer.provide(InspectorAndAnalyzerLive), Layer.provide(PointInTimeWorkspaceLive.pipe(Layer.provide(WorkspaceLive))), Layer.provide(PublishabilityDetectorLive), Layer.provide(ChangesetConfigLive.pipe(Layer.provide(ChangesetConfigReaderLive))));
131
- const AppLive = Layer.mergeAll(ToolDiscoveryLive, VersioningStrategyLive, InspectorAndAnalyzerLive, DepsRegenGroupLive).pipe(Layer.provideMerge(BaseLive), Layer.provideMerge(NodeContext.layer));
133
+ /**
134
+ * `Repos.ReposManager`, provided its `Repos.ReposConfigStoreLive` dependency.
135
+ * Both draw their platform requirements (`FileSystem`, `Path`,
136
+ * `CommandExecutor`) from `NodeContext.layer` below.
137
+ */
138
+ const ReposGroupLive = Repos.ReposManagerLive.pipe(Layer.provide(Repos.ReposConfigStoreLive));
139
+ const AppLive = Layer.mergeAll(ToolDiscoveryLive, VersioningStrategyLive, InspectorAndAnalyzerLive, DepsRegenGroupLive, ReposGroupLive).pipe(Layer.provideMerge(BaseLive), Layer.provideMerge(NodeContext.layer));
132
140
  /**
133
141
  * Bootstrap and run the `savvy` CLI application.
134
142
  *
@@ -0,0 +1,73 @@
1
+ import { Args, Command, Options } from "@effect/cli";
2
+ import { Repos } from "@savvy-web/silk-effects";
3
+ import { Effect, Option } from "effect";
4
+
5
+ //#region src/commands/repos/commands/add.ts
6
+ /**
7
+ * `repos add` command -- vendor a new reference repo under `.repos/`.
8
+ *
9
+ * @remarks
10
+ * A thin adapter over {@link Repos.ReposManager.add}: adds a shallow git
11
+ * submodule, checks it out at the requested ref, optionally applies a
12
+ * sparse-checkout, and writes a new manifest entry -- it does not commit, so
13
+ * the caller reviews and commits the staged `.gitmodules`/manifest/gitlink
14
+ * change. A `ReposConfigError` with kind `"missing"` -- no manifest yet --
15
+ * is the common, friendly case and always exits 0 (the manifest is created
16
+ * on demand by `add` itself, so this only fires on a read that raced a
17
+ * concurrent removal). A `ReposConfigError` with kind `"invalid"` means the
18
+ * manifest exists but is corrupt or unreadable, and `GitSubmoduleError`
19
+ * means the underlying git command failed -- both are real failures, logged
20
+ * and reported via a non-zero exit code.
21
+ *
22
+ * @example
23
+ * ```bash
24
+ * savvy repos add https://github.com/foo/bar --ref v1.0.0 --purpose "vendor demo"
25
+ * savvy repos add https://github.com/foo/bar --ref main --purpose "vendor demo" --sparse src --sparse docs --name bar-vendored
26
+ * ```
27
+ *
28
+ * @internal
29
+ */
30
+ /* v8 ignore start -- CLI option/arg definitions */
31
+ const urlArg = Args.text({ name: "url" });
32
+ const refOption = Options.text("ref").pipe(Options.withDescription("Ref (tag, branch, or commit) to check out"));
33
+ const purposeOption = Options.text("purpose").pipe(Options.withDescription("Why this repo is vendored"));
34
+ const nameOption = Options.text("name").pipe(Options.withDescription("Vendored directory name; defaults to the URL's last path segment"), Options.optional);
35
+ const sparseOption = Options.text("sparse").pipe(Options.withDescription("Sparse-checkout path; repeatable"), Options.repeated);
36
+ const cwdOption = Options.directory("cwd").pipe(Options.withDescription("Repo root to add within"), Options.withDefault("."));
37
+ /* v8 ignore stop */
38
+ /**
39
+ * Add handler; exported for tests.
40
+ *
41
+ * @internal
42
+ */
43
+ const runReposAdd = (cwd, opts) => Effect.gen(function* () {
44
+ const result = yield* (yield* Repos.ReposManager).add(cwd, opts);
45
+ yield* Effect.log(`${result.name} @ ${result.ref} -> ${result.path}`);
46
+ yield* Effect.log("staged — review and commit");
47
+ }).pipe(Effect.catchTag("ReposConfigError", (error) => {
48
+ if (error.kind === "missing") return Effect.log("no .repos/config.json — nothing vendored");
49
+ process.exitCode = 1;
50
+ return Effect.log(error.message);
51
+ }), Effect.catchTag("GitSubmoduleError", (error) => {
52
+ process.exitCode = 1;
53
+ return Effect.log(error.message);
54
+ }));
55
+ /* v8 ignore start -- CLI registration; handler tested via runReposAdd */
56
+ const addCommand = Command.make("add", {
57
+ url: urlArg,
58
+ ref: refOption,
59
+ purpose: purposeOption,
60
+ name: nameOption,
61
+ sparse: sparseOption,
62
+ cwd: cwdOption
63
+ }, ({ url, ref, purpose, name, sparse, cwd }) => runReposAdd(cwd, {
64
+ url,
65
+ ref,
66
+ purpose,
67
+ ...Option.isSome(name) ? { name: name.value } : {},
68
+ ...sparse.length > 0 ? { sparse } : {}
69
+ })).pipe(Command.withDescription("Vendor a new reference repo under .repos/; stages the change without committing"));
70
+ /* v8 ignore stop */
71
+
72
+ //#endregion
73
+ export { addCommand, runReposAdd };
@@ -0,0 +1,106 @@
1
+ import { Args, Command, Options } from "@effect/cli";
2
+ import { Repos } from "@savvy-web/silk-effects";
3
+ import { Effect } from "effect";
4
+
5
+ //#region src/commands/repos/commands/note.ts
6
+ /**
7
+ * `repos note` command group -- agent notes for a vendored `.repos/` repo.
8
+ *
9
+ * @remarks
10
+ * A thin adapter over {@link Repos.ReposManager.note}: `add` appends a note
11
+ * (capped at the manifest's per-repo note limit), `remove` deletes one, and
12
+ * `promote` folds a note's text into the entry's curated orientation
13
+ * (`layout` or `startHere`) and removes it from the note list. `name` and
14
+ * `--cwd` are parsed on the parent `note` command and threaded to whichever
15
+ * leaf ran via Effect's `Command.Context` mechanism (the parent `Command`
16
+ * doubles as an `Effect` requiring its own context tag, which
17
+ * `Command.withSubcommands` provides to the chosen leaf's handler).
18
+ *
19
+ * A `ReposConfigError` with kind `"missing"` -- nothing vendored yet -- is
20
+ * the common, friendly case and always exits 0. A `ReposConfigError` with
21
+ * kind `"invalid"` means the manifest exists but is corrupt or unreadable,
22
+ * `RepoNotFoundError` means the named repo isn't in the manifest, and
23
+ * `NoteNotFoundError` means the note id doesn't exist on that repo -- all
24
+ * three are real failures, logged and reported via a non-zero exit code.
25
+ *
26
+ * @example
27
+ * ```bash
28
+ * savvy repos note my-repo add "entry point is src/index.ts"
29
+ * savvy repos note my-repo remove n-1234
30
+ * savvy repos note my-repo promote n-1234 --into startHere
31
+ * ```
32
+ *
33
+ * @internal
34
+ */
35
+ /**
36
+ * Note handler; exported for tests.
37
+ *
38
+ * @internal
39
+ */
40
+ const runReposNote = (cwd, name, op) => Effect.gen(function* () {
41
+ const result = yield* (yield* Repos.ReposManager).note(cwd, name, op);
42
+ yield* Effect.log(`${result.name}: ${result.op} note ${result.id} (${result.noteCount} notes)`);
43
+ }).pipe(Effect.catchTag("ReposConfigError", (error) => {
44
+ if (error.kind === "missing") return Effect.log("no .repos/config.json — nothing vendored");
45
+ process.exitCode = 1;
46
+ return Effect.log(error.message);
47
+ }), Effect.catchTag("RepoNotFoundError", (error) => {
48
+ process.exitCode = 1;
49
+ return Effect.log(error.message);
50
+ }), Effect.catchTag("NoteNotFoundError", (error) => {
51
+ process.exitCode = 1;
52
+ return Effect.log(error.message);
53
+ }));
54
+ /* v8 ignore start -- CLI registration; handler tested via runReposNote */
55
+ const nameArg = Args.text({ name: "name" });
56
+ const noteTextArg = Args.text({ name: "text" });
57
+ const noteIdArg = Args.text({ name: "id" });
58
+ const intoOption = Options.choice("into", ["layout", "startHere"]).pipe(Options.withDescription("Curated orientation field to promote the note into"));
59
+ const cwdOption = Options.directory("cwd").pipe(Options.withDescription("Repo root whose manifest holds the notes"), Options.withDefault("."));
60
+ const _noteGroup = Command.make("note", {
61
+ name: nameArg,
62
+ cwd: cwdOption
63
+ });
64
+ const addLeaf = Command.make("add", { note: noteTextArg }, ({ note }) => Effect.gen(function* () {
65
+ const { name, cwd } = yield* _noteGroup;
66
+ yield* runReposNote(cwd, name, {
67
+ op: "add",
68
+ note
69
+ });
70
+ })).pipe(Command.withDescription("Append an agent note to a vendored repo"));
71
+ const removeLeaf = Command.make("remove", { id: noteIdArg }, ({ id }) => Effect.gen(function* () {
72
+ const { name, cwd } = yield* _noteGroup;
73
+ yield* runReposNote(cwd, name, {
74
+ op: "remove",
75
+ id
76
+ });
77
+ })).pipe(Command.withDescription("Remove an agent note from a vendored repo"));
78
+ const promoteLeaf = Command.make("promote", {
79
+ id: noteIdArg,
80
+ into: intoOption
81
+ }, ({ id, into }) => Effect.gen(function* () {
82
+ const { name, cwd } = yield* _noteGroup;
83
+ yield* runReposNote(cwd, name, {
84
+ op: "promote",
85
+ id,
86
+ into
87
+ });
88
+ })).pipe(Command.withDescription("Promote an agent note into curated orientation (layout or startHere)"));
89
+ const _noteCommand = _noteGroup.pipe(Command.withSubcommands([
90
+ addLeaf,
91
+ removeLeaf,
92
+ promoteLeaf
93
+ ]), Command.withDescription("Agent notes for a vendored repo: add, remove, promote"));
94
+ /**
95
+ * The `savvy repos note` command group.
96
+ *
97
+ * @remarks
98
+ * Typed as `unknown` at the export boundary to avoid TypeScript declaration-emit
99
+ * errors from Effect's internal types (TS4023), matching the `reposCommand`
100
+ * export pattern.
101
+ */
102
+ const noteCommand = _noteCommand;
103
+ /* v8 ignore stop */
104
+
105
+ //#endregion
106
+ export { noteCommand, runReposNote };
@@ -0,0 +1,64 @@
1
+ import { Args, Command, Options } from "@effect/cli";
2
+ import { Repos } from "@savvy-web/silk-effects";
3
+ import { Effect } from "effect";
4
+
5
+ //#region src/commands/repos/commands/pin.ts
6
+ /**
7
+ * `repos pin` command -- re-pin a vendored `.repos/` submodule to a new ref.
8
+ *
9
+ * @remarks
10
+ * A thin adapter over {@link Repos.ReposManager.pin}: shallow-fetches the new
11
+ * ref, detaches HEAD onto it, rewrites the manifest entry, and stages the
12
+ * gitlink plus the manifest -- it does not commit, so the caller reviews and
13
+ * commits the staged change with the ready-made message the result carries.
14
+ * A `ReposConfigError` with kind `"missing"` -- nothing vendored yet -- is
15
+ * the common, friendly case and always exits 0. A `ReposConfigError` with
16
+ * kind `"invalid"` means the manifest exists but is corrupt or unreadable,
17
+ * `GitSubmoduleError` means the underlying git command failed, and
18
+ * `RepoNotFoundError` means the named repo isn't in the manifest -- all
19
+ * three are real failures, logged and reported via a non-zero exit code.
20
+ *
21
+ * @example
22
+ * ```bash
23
+ * savvy repos pin my-repo v2.0.0
24
+ * ```
25
+ *
26
+ * @internal
27
+ */
28
+ /* v8 ignore start -- CLI option/arg definitions */
29
+ const nameArg = Args.text({ name: "name" });
30
+ const refArg = Args.text({ name: "ref" });
31
+ const cwdOption = Options.directory("cwd").pipe(Options.withDescription("Repo root to pin within"), Options.withDefault("."));
32
+ /* v8 ignore stop */
33
+ /**
34
+ * Pin handler; exported for tests.
35
+ *
36
+ * @internal
37
+ */
38
+ const runReposPin = (cwd, name, ref) => Effect.gen(function* () {
39
+ const result = yield* (yield* Repos.ReposManager).pin(cwd, name, ref);
40
+ yield* Effect.log(`${result.name}: ${result.oldCommit ?? "unknown"} -> ${result.newCommit}`);
41
+ yield* Effect.log(result.commitMessage);
42
+ yield* Effect.log("staged — review and commit");
43
+ for (const staleId of result.staleNoteIds) yield* Effect.log(`warning: note ${staleId} is now stale against ${ref}`);
44
+ }).pipe(Effect.catchTag("ReposConfigError", (error) => {
45
+ if (error.kind === "missing") return Effect.log("no .repos/config.json — nothing vendored");
46
+ process.exitCode = 1;
47
+ return Effect.log(error.message);
48
+ }), Effect.catchTag("GitSubmoduleError", (error) => {
49
+ process.exitCode = 1;
50
+ return Effect.log(error.message);
51
+ }), Effect.catchTag("RepoNotFoundError", (error) => {
52
+ process.exitCode = 1;
53
+ return Effect.log(error.message);
54
+ }));
55
+ /* v8 ignore start -- CLI registration; handler tested via runReposPin */
56
+ const pinCommand = Command.make("pin", {
57
+ name: nameArg,
58
+ ref: refArg,
59
+ cwd: cwdOption
60
+ }, ({ name, ref, cwd }) => runReposPin(cwd, name, ref)).pipe(Command.withDescription("Re-pin a vendored repo to a new ref; stages the change without committing"));
61
+ /* v8 ignore stop */
62
+
63
+ //#endregion
64
+ export { pinCommand, runReposPin };
@@ -0,0 +1,70 @@
1
+ import { Command, Options } from "@effect/cli";
2
+ import { Repos } from "@savvy-web/silk-effects";
3
+ import { Console, Effect } from "effect";
4
+
5
+ //#region src/commands/repos/commands/status.ts
6
+ /**
7
+ * `repos status` command -- drift report for vendored `.repos/` submodules.
8
+ *
9
+ * @remarks
10
+ * A thin adapter over {@link Repos.ReposManager.status}: reports, per vendored
11
+ * repo, whether the submodule is present, dirty, and whether any agent notes
12
+ * have gone stale relative to the pinned ref. A `ReposConfigError` with kind
13
+ * `"missing"` is the common, friendly case (nothing has been vendored yet) --
14
+ * not an error -- so it is rendered as a plain message (or an empty JSON
15
+ * report, in `--json` mode) with exit code 0. A `ReposConfigError` with kind
16
+ * `"invalid"` means the manifest exists but is corrupt or unreadable -- that
17
+ * is a real failure, logged and reported via a non-zero exit code.
18
+ *
19
+ * @example
20
+ * ```bash
21
+ * savvy repos status
22
+ * savvy repos status --json
23
+ * ```
24
+ *
25
+ * @internal
26
+ */
27
+ /* v8 ignore start -- CLI option definitions */
28
+ const jsonOption = Options.boolean("json").pipe(Options.withDescription("Emit the structured drift report as JSON"), Options.withDefault(false));
29
+ const cwdOption = Options.directory("cwd").pipe(Options.withDescription("Repo root to inspect"), Options.withDefault("."));
30
+ /* v8 ignore stop */
31
+ /**
32
+ * Drift report handler; exported for tests.
33
+ *
34
+ * @internal
35
+ */
36
+ const runReposStatus = (cwd, json) => Effect.gen(function* () {
37
+ const report = yield* (yield* Repos.ReposManager).status(cwd);
38
+ if (!report.clean) process.exitCode = 1;
39
+ if (json) {
40
+ yield* Console.log(JSON.stringify(report, null, 2));
41
+ return;
42
+ }
43
+ for (const repo of report.repos) {
44
+ const flags = [
45
+ repo.present ? void 0 : "missing",
46
+ repo.dirty ? "dirty" : void 0,
47
+ repo.staleNoteIds.length > 0 ? `${repo.staleNoteIds.length} stale notes` : void 0
48
+ ].filter((f) => f !== void 0);
49
+ yield* Effect.log(`${repo.name} @ ${repo.ref}${flags.length > 0 ? ` [${flags.join(", ")}]` : " [ok]"}`);
50
+ }
51
+ }).pipe(Effect.catchTag("ReposConfigError", (error) => {
52
+ if (error.kind === "missing") {
53
+ if (json) return Console.log(JSON.stringify({
54
+ repos: [],
55
+ clean: true
56
+ }, null, 2));
57
+ return Effect.log("no .repos/config.json — nothing vendored");
58
+ }
59
+ process.exitCode = 1;
60
+ return Effect.log(error.message);
61
+ }));
62
+ /* v8 ignore start -- CLI registration; handler tested via runReposStatus */
63
+ const statusCommand = Command.make("status", {
64
+ json: jsonOption,
65
+ cwd: cwdOption
66
+ }, ({ json, cwd }) => runReposStatus(cwd, json)).pipe(Command.withDescription("Drift report: gitlink vs manifest ref, dirty and unsynced submodules"));
67
+ /* v8 ignore stop */
68
+
69
+ //#endregion
70
+ export { runReposStatus, statusCommand };
@@ -0,0 +1,53 @@
1
+ import { Command, Options } from "@effect/cli";
2
+ import { Repos } from "@savvy-web/silk-effects";
3
+ import { Effect } from "effect";
4
+
5
+ //#region src/commands/repos/commands/sync.ts
6
+ /**
7
+ * `repos sync` command -- reconcile vendored `.repos/` submodules with the manifest.
8
+ *
9
+ * @remarks
10
+ * A thin adapter over {@link Repos.ReposManager.sync}: initializes missing
11
+ * submodules, re-applies sparse-checkout patterns, and clears stale git
12
+ * locks left behind by an interrupted fetch. Sync is idempotent repair, so
13
+ * a `ReposConfigError` with kind `"missing"` -- the common, friendly case
14
+ * (nothing to sync yet) -- always exits 0. A `ReposConfigError` with kind
15
+ * `"invalid"` means the manifest exists but is corrupt or unreadable, and
16
+ * `GitSubmoduleError` means the underlying git command failed -- both are
17
+ * real failures, logged and reported via a non-zero exit code.
18
+ *
19
+ * @example
20
+ * ```bash
21
+ * savvy repos sync
22
+ * ```
23
+ *
24
+ * @internal
25
+ */
26
+ /* v8 ignore start -- CLI option definitions */
27
+ const cwdOption = Options.directory("cwd").pipe(Options.withDescription("Repo root to sync"), Options.withDefault("."));
28
+ /* v8 ignore stop */
29
+ /**
30
+ * Sync handler; exported for tests.
31
+ *
32
+ * @internal
33
+ */
34
+ const runReposSync = (cwd) => Effect.gen(function* () {
35
+ const report = yield* (yield* Repos.ReposManager).sync(cwd);
36
+ for (const name of report.clearedLocks) yield* Effect.log(`${name}: cleared stale lock`);
37
+ for (const name of report.initialized) yield* Effect.log(`${name}: initialized`);
38
+ for (const name of report.sparseApplied) yield* Effect.log(`${name}: sparse-checkout applied`);
39
+ if (report.initialized.length === 0 && report.sparseApplied.length === 0 && report.clearedLocks.length === 0) yield* Effect.log("all vendored repos up to date");
40
+ }).pipe(Effect.catchTag("ReposConfigError", (error) => {
41
+ if (error.kind === "missing") return Effect.log("no .repos/config.json — nothing vendored");
42
+ process.exitCode = 1;
43
+ return Effect.log(error.message);
44
+ }), Effect.catchTag("GitSubmoduleError", (error) => {
45
+ process.exitCode = 1;
46
+ return Effect.log(error.message);
47
+ }));
48
+ /* v8 ignore start -- CLI registration; handler tested via runReposSync */
49
+ const syncCommand = Command.make("sync", { cwd: cwdOption }, ({ cwd }) => runReposSync(cwd)).pipe(Command.withDescription("Reconcile vendored submodules with the manifest: init missing, apply sparse, clear locks"));
50
+ /* v8 ignore stop */
51
+
52
+ //#endregion
53
+ export { runReposSync, syncCommand };
@@ -0,0 +1,28 @@
1
+ import { addCommand, runReposAdd } from "./commands/add.js";
2
+ import { noteCommand, runReposNote } from "./commands/note.js";
3
+ import { pinCommand, runReposPin } from "./commands/pin.js";
4
+ import { runReposStatus, statusCommand } from "./commands/status.js";
5
+ import { runReposSync, syncCommand } from "./commands/sync.js";
6
+ import { Command } from "@effect/cli";
7
+
8
+ //#region src/commands/repos/index.ts
9
+ /* v8 ignore start -- CLI registration; each command tested via exported handler */
10
+ const _reposCommand = Command.make("repos").pipe(Command.withSubcommands([
11
+ statusCommand,
12
+ syncCommand,
13
+ pinCommand,
14
+ addCommand,
15
+ noteCommand
16
+ ]), Command.withDescription("Vendored reference repos under .repos/"));
17
+ /**
18
+ * The `savvy repos` command group.
19
+ *
20
+ * @remarks
21
+ * Typed as `unknown` at the export boundary to avoid TypeScript declaration-emit
22
+ * errors from Effect's internal types (TS4023), matching the `changesetCommand`
23
+ * export pattern.
24
+ */
25
+ const reposCommand = _reposCommand;
26
+
27
+ //#endregion
28
+ export { reposCommand, runReposStatus, runReposSync };
package/index.d.ts CHANGED
@@ -3,7 +3,7 @@ import { Effect } from "effect";
3
3
  import { WorkspaceDiscovery, WorkspaceRoot } from "workspaces-effect";
4
4
  import { FileSystem } from "@effect/platform";
5
5
  import { PlatformError } from "@effect/platform/Error";
6
- import { BiomeSchemaSync, ConfigDiscovery, ManagedSection, SectionParseError, SectionWriteError, ToolDiscovery, VersioningStrategy } from "@savvy-web/silk-effects";
6
+ import { BiomeSchemaSync, ConfigDiscovery, ManagedSection, Repos, SectionParseError, SectionWriteError, ToolDiscovery, VersioningStrategy } from "@savvy-web/silk-effects";
7
7
  import { JsoncParseError } from "jsonc-effect";
8
8
  //#region src/cli/index.d.ts
9
9
  /**
@@ -232,5 +232,32 @@ declare function runLintInit(opts: {
232
232
  */
233
233
  declare const lintCommand: Command.Command<"lint", any, any, any>;
234
234
  //#endregion
235
- export { changesetCommand, checkCommand, commitCommand, initCommand, lintCommand, runChangesetCheck, runChangesetInit, runCheck, runCli, runCommitCheck, runCommitInit, runInit, runLintCheck, runLintInit };
235
+ //#region src/commands/repos/commands/status.d.ts
236
+ /**
237
+ * Drift report handler; exported for tests.
238
+ *
239
+ * @internal
240
+ */
241
+ declare const runReposStatus: (cwd: string, json: boolean) => Effect.Effect<void, Repos.GitSubmoduleError, Repos.ReposManager>;
242
+ //#endregion
243
+ //#region src/commands/repos/commands/sync.d.ts
244
+ /**
245
+ * Sync handler; exported for tests.
246
+ *
247
+ * @internal
248
+ */
249
+ declare const runReposSync: (cwd: string) => Effect.Effect<void, never, Repos.ReposManager>;
250
+ //#endregion
251
+ //#region src/commands/repos/index.d.ts
252
+ /**
253
+ * The `savvy repos` command group.
254
+ *
255
+ * @remarks
256
+ * Typed as `unknown` at the export boundary to avoid TypeScript declaration-emit
257
+ * errors from Effect's internal types (TS4023), matching the `changesetCommand`
258
+ * export pattern.
259
+ */
260
+ declare const reposCommand: Command.Command<"repos", any, any, any>;
261
+ //#endregion
262
+ export { changesetCommand, checkCommand, commitCommand, initCommand, lintCommand, reposCommand, runChangesetCheck, runChangesetInit, runCheck, runCli, runCommitCheck, runCommitInit, runInit, runLintCheck, runLintInit, runReposStatus, runReposSync };
236
263
  //# sourceMappingURL=index.d.ts.map
package/index.js CHANGED
@@ -9,6 +9,9 @@ import { commitCommand } from "./commands/commit/index.js";
9
9
  import { runLintInit } from "./commands/lint/init.js";
10
10
  import { initCommand, runInit } from "./commands/init.js";
11
11
  import { lintCommand } from "./commands/lint/index.js";
12
+ import { runReposStatus } from "./commands/repos/commands/status.js";
13
+ import { runReposSync } from "./commands/repos/commands/sync.js";
14
+ import { reposCommand } from "./commands/repos/index.js";
12
15
  import { runCli } from "./cli/index.js";
13
16
 
14
- export { changesetCommand, checkCommand, commitCommand, initCommand, lintCommand, runChangesetCheck, runChangesetInit, runCheck, runCli, runCommitCheck, runCommitInit, runInit, runLintCheck, runLintInit };
17
+ export { changesetCommand, checkCommand, commitCommand, initCommand, lintCommand, reposCommand, runChangesetCheck, runChangesetInit, runCheck, runCli, runCommitCheck, runCommitInit, runInit, runLintCheck, runLintInit, runReposStatus, runReposSync };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/cli",
3
- "version": "1.5.10",
3
+ "version": "1.6.1",
4
4
  "private": false,
5
5
  "description": "The savvy CLI — unified commit, changeset, and lint commands for the Silk Suite",
6
6
  "homepage": "https://github.com/savvy-web/systems/tree/main/packages/cli",
@@ -41,10 +41,10 @@
41
41
  "@effect/sql": "^0.51.1",
42
42
  "@effect/typeclass": "^0.40.0",
43
43
  "@effect/workflow": "^0.18.2",
44
- "@savvy-web/silk-effects": "3.2.5",
44
+ "@savvy-web/silk-effects": "3.3.1",
45
45
  "effect": "^3.21.4",
46
46
  "jsonc-effect": "^0.3.1",
47
- "workspaces-effect": "^2.0.3",
47
+ "workspaces-effect": "^2.1.0",
48
48
  "yaml": "^2.9.0"
49
49
  }
50
50
  }