@savvy-web/cli 2.11.3 → 3.0.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 (40) hide show
  1. package/README.md +3 -3
  2. package/bin/savvy.js +5 -10
  3. package/cli/index.js +13 -27
  4. package/commands/changeset/commands/check.js +3 -3
  5. package/commands/changeset/commands/config-validate.js +3 -3
  6. package/commands/changeset/commands/deps-detect.js +8 -8
  7. package/commands/changeset/commands/deps-regen.js +7 -7
  8. package/commands/changeset/commands/init.js +3 -3
  9. package/commands/changeset/commands/lint.js +4 -4
  10. package/commands/changeset/commands/transform.js +5 -5
  11. package/commands/changeset/commands/validate-file.js +2 -2
  12. package/commands/changeset/commands/version.js +2 -2
  13. package/commands/changeset/utils/config-gate.js +1 -1
  14. package/commands/check.js +4 -4
  15. package/commands/clean.js +4 -4
  16. package/commands/commit/check.js +2 -2
  17. package/commands/commit/hooks/post-commit-verify.js +2 -2
  18. package/commands/commit/hooks/pre-commit-message.js +2 -2
  19. package/commands/commit/hooks/session-start.js +2 -2
  20. package/commands/commit/init.js +2 -2
  21. package/commands/commit/lint.js +3 -3
  22. package/commands/init.js +6 -6
  23. package/commands/lint/biome-version.js +1 -1
  24. package/commands/lint/check.js +7 -3
  25. package/commands/lint/fmt.js +3 -3
  26. package/commands/lint/init.js +8 -8
  27. package/commands/repos/commands/add.js +7 -7
  28. package/commands/repos/commands/deregister.js +3 -3
  29. package/commands/repos/commands/note.js +6 -6
  30. package/commands/repos/commands/pin.js +4 -4
  31. package/commands/repos/commands/remove.js +3 -3
  32. package/commands/repos/commands/rename.js +4 -4
  33. package/commands/repos/commands/restore.js +3 -3
  34. package/commands/repos/commands/status.js +4 -4
  35. package/commands/repos/commands/sync.js +2 -2
  36. package/index.d.ts +0 -59
  37. package/index.js +1 -2
  38. package/main.d.ts +7 -0
  39. package/main.js +35 -0
  40. package/package.json +15 -10
package/README.md CHANGED
@@ -91,12 +91,12 @@ npx savvy changeset --help
91
91
  The package also exports the assembled command tree and its handlers for embedding `savvy` in another program:
92
92
 
93
93
  ```ts
94
- import { runCli } from "@savvy-web/cli";
94
+ import { main } from "@savvy-web/cli/main";
95
95
 
96
- await runCli(process.argv);
96
+ main();
97
97
  ```
98
98
 
99
- The individual command groups (`changesetCommand`, `commitCommand`, `lintCommand`) and their named handlers are exported from the package root.
99
+ The `.` barrel exports the command groups (`changesetCommand`, `commitCommand`, `lintCommand`) and their named handlers only.
100
100
 
101
101
  ## License
102
102
 
package/bin/savvy.js CHANGED
@@ -1,18 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { runCli } from "../cli/index.js";
2
+ import { main } from "../main.js";
3
3
 
4
- //#region src/bin/cli.ts
4
+ //#region src/bin.ts
5
5
  /**
6
- * Binary entry point for the `savvy` CLI.
7
- *
8
- * This file is the `bin` target in `package.json`. It delegates immediately
9
- * to {@link runCli} which assembles the `\@effect/cli` command tree and
10
- * executes it via `\@effect/platform-node`.
11
- *
6
+ * The `savvy` bin. Imports `main` and calls it. Nothing else, ever.
12
7
  * @internal
13
8
  */
14
- /* v8 ignore start -- CLI bootstrap; commands tested individually */
15
- runCli();
9
+ /* v8 ignore start -- bin shim; covered by the silk bins e2e */
10
+ main();
16
11
  /* v8 ignore stop */
17
12
 
18
13
  //#endregion
package/cli/index.js CHANGED
@@ -1,18 +1,18 @@
1
1
  import { changesetCommand } from "../commands/changeset/index.js";
2
2
  import { checkCommand } from "../commands/check.js";
3
- import { cleanCommand } from "../commands/clean.js";
4
3
  import { commitCommand } from "../commands/commit/index.js";
5
4
  import { initCommand } from "../commands/init.js";
6
5
  import { lintCommand } from "../commands/lint/index.js";
7
6
  import { reposCommand } from "../commands/repos/index.js";
8
- import { NodeRuntime, NodeServices } from "@effect/platform-node";
9
- import { ToolDiscovery } from "@effected/commands";
7
+ import { cleanCommand } from "../commands/clean.js";
8
+ import { Command } from "effect/unstable/cli";
9
+ import { BiomeSchemaSync, ChangesetConfigReader, Changesets, ConfigDiscovery, Repos, SilkPublishability } from "@savvy-web/silk-effects";
10
+ import { Layer } from "effect";
10
11
  import { Git } from "@effected/git";
11
- import { ManagedSection } from "@effected/templates";
12
12
  import { PackageManagerDetector, WorkspaceDiscovery, WorkspaceRoot, Workspaces } from "@effected/workspaces";
13
- import { BiomeSchemaSync, ChangesetConfigReader, Changesets, ConfigDiscovery, Repos, SilkPublishability } from "@savvy-web/silk-effects";
14
- import { Effect, Layer } from "effect";
15
- import { Command } from "effect/unstable/cli";
13
+ import { ManagedSection } from "@effected/templates";
14
+ import { ToolDiscovery } from "@effected/commands";
15
+ import { NodeServices } from "@effect/platform-node";
16
16
 
17
17
  //#region src/cli/index.ts
18
18
  /**
@@ -73,11 +73,6 @@ const rootCommand = Command.make("savvy").pipe(Command.withSubcommands([
73
73
  reposCommand
74
74
  ]));
75
75
  /**
76
- * CLI application: reads argv from the Stdio service provided by NodeServices.
77
- * (v4's `Command.run` takes only `version` — the name comes from the root command.)
78
- */
79
- const cli = Command.run(rootCommand, { version: "2.11.3" });
80
- /**
81
76
  * Shared workspace services from `@effected/workspaces`, wired as a
82
77
  * self-contained unit and built ONCE (layers memoize by reference).
83
78
  * `WorkspaceDiscovery.layer()` is root-bound at first use via `WorkspaceRoot`
@@ -125,23 +120,14 @@ const ReposGroupLive = Layer.mergeAll(Repos.ReposManager.layer, Repos.ReposDrift
125
120
  */
126
121
  const LocalExecLive = Workspaces.localExecLayer();
127
122
  const ToolDiscoveryGroupLive = ToolDiscovery.layer.pipe(Layer.provide(LocalExecLive), Layer.provide(WorkspaceLive));
128
- const AppLive = Layer.mergeAll(ToolDiscoveryGroupLive, InspectorAndAnalyzerLive, ReposGroupLive).pipe(Layer.provideMerge(BaseLive), Layer.provideMerge(NodeServices.layer));
129
123
  /**
130
- * Bootstrap and run the `savvy` CLI application.
131
- *
132
- * @remarks
133
- * `Command.run` returns an Effect reading `process.argv` from the Stdio
134
- * service; the merged layer stack is provided and execution handed to
135
- * `NodeRuntime.runMain`, whose default reporting covers defects (the v3
136
- * `Cause.defects` wrapper is gone). No type casts: the layer graph is
137
- * validated by the compiler.
138
- *
139
- * @internal
124
+ * The merged runtime Layer stack satisfying every command's service
125
+ * requirements. Provided to `rootCommand`'s assembled `Command.run` Effect by
126
+ * `main()` in `../main.js`; this module never calls `NodeRuntime.runMain`
127
+ * itself.
140
128
  */
141
- function runCli() {
142
- NodeRuntime.runMain(cli.pipe(Effect.provide(AppLive)));
143
- }
129
+ const AppLive = Layer.mergeAll(ToolDiscoveryGroupLive, InspectorAndAnalyzerLive, ReposGroupLive).pipe(Layer.provideMerge(BaseLive), Layer.provideMerge(NodeServices.layer));
144
130
  /* v8 ignore stop */
145
131
 
146
132
  //#endregion
147
- export { runCli };
133
+ export { AppLive, rootCommand };
@@ -1,7 +1,7 @@
1
- import { Changesets } from "@savvy-web/silk-effects";
2
- import { Effect } from "effect";
3
1
  import { Argument, Command } from "effect/unstable/cli";
4
2
  import { resolve } from "node:path";
3
+ import { Changesets } from "@savvy-web/silk-effects";
4
+ import { Effect } from "effect";
5
5
 
6
6
  //#region src/commands/changeset/commands/check.ts
7
7
  /**
@@ -27,7 +27,7 @@ import { resolve } from "node:path";
27
27
  */
28
28
  const { ChangesetLinter } = Changesets;
29
29
  /* v8 ignore next */
30
- const dirArg = Argument.directory("dir").pipe(Argument.withDefault(".changeset"));
30
+ const dirArg = Argument.Directory("dir").pipe(Argument.withDefault(".changeset"));
31
31
  /**
32
32
  * Run the check validation pipeline on all changeset files in `dir`.
33
33
  *
@@ -1,7 +1,7 @@
1
- import { Changesets } from "@savvy-web/silk-effects";
2
- import { Effect } from "effect";
3
1
  import { Argument, Command } from "effect/unstable/cli";
4
2
  import { resolve } from "node:path";
3
+ import { Changesets } from "@savvy-web/silk-effects";
4
+ import { Effect } from "effect";
5
5
 
6
6
  //#region src/commands/changeset/commands/config-validate.ts
7
7
  /**
@@ -26,7 +26,7 @@ import { resolve } from "node:path";
26
26
  */
27
27
  const { ConfigInspector } = Changesets;
28
28
  /* v8 ignore next */
29
- const dirArg = Argument.directory("dir").pipe(Argument.withDefault("."));
29
+ const dirArg = Argument.Directory("dir").pipe(Argument.withDefault("."));
30
30
  /**
31
31
  * Run validation. Logs a one-line OK on success; logs the error and sets
32
32
  * `process.exitCode = 1` on failure.
@@ -1,7 +1,7 @@
1
- import { Changesets } from "@savvy-web/silk-effects";
2
- import { Console, Effect, Option } from "effect";
3
1
  import { Command, Flag } from "effect/unstable/cli";
4
2
  import { resolve } from "node:path";
3
+ import { Changesets } from "@savvy-web/silk-effects";
4
+ import { Console, Effect, Option } from "effect";
5
5
 
6
6
  //#region src/commands/changeset/commands/deps-detect.ts
7
7
  /**
@@ -29,12 +29,12 @@ import { resolve } from "node:path";
29
29
  */
30
30
  const { DepsRegen, serializeDependencyTableToMarkdown } = Changesets;
31
31
  /* v8 ignore start -- CLI option definitions */
32
- const fromOption = Flag.string("from").pipe(Flag.withDescription("Older ref to diff from (defaults to merge-base with base branch)"), Flag.optional);
33
- const toOption = Flag.string("to").pipe(Flag.withDescription("Newer ref to diff to (defaults to working tree)"), Flag.optional);
34
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Project root (defaults to the current working directory)"), Flag.withDefault("."));
35
- const packageOption = Flag.string("package").pipe(Flag.withDescription("Restrict output to a single workspace package"), Flag.optional);
36
- const jsonOption = Flag.boolean("json").pipe(Flag.withDescription("Emit JSON (default)"), Flag.withDefault(false));
37
- const markdownOption = Flag.boolean("markdown").pipe(Flag.withDescription("Emit one CSH005 markdown block per workspace package"), Flag.withDefault(false));
32
+ const fromOption = Flag.String("from").pipe(Flag.withDescription("Older ref to diff from (defaults to merge-base with base branch)"), Flag.optional);
33
+ const toOption = Flag.String("to").pipe(Flag.withDescription("Newer ref to diff to (defaults to working tree)"), Flag.optional);
34
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Project root (defaults to the current working directory)"), Flag.withDefault("."));
35
+ const packageOption = Flag.String("package").pipe(Flag.withDescription("Restrict output to a single workspace package"), Flag.optional);
36
+ const jsonOption = Flag.Boolean("json").pipe(Flag.withDescription("Emit JSON (default)"), Flag.withDefault(false));
37
+ const markdownOption = Flag.Boolean("markdown").pipe(Flag.withDescription("Emit one CSH005 markdown block per workspace package"), Flag.withDefault(false));
38
38
  /* v8 ignore stop */
39
39
  /**
40
40
  * Render a per-workspace diff as markdown — one frontmatter+section block
@@ -1,7 +1,7 @@
1
- import { Changesets } from "@savvy-web/silk-effects";
2
- import { Console, Effect, Option } from "effect";
3
1
  import { Command, Flag } from "effect/unstable/cli";
4
2
  import { resolve } from "node:path";
3
+ import { Changesets } from "@savvy-web/silk-effects";
4
+ import { Console, Effect, Option } from "effect";
5
5
 
6
6
  //#region src/commands/changeset/commands/deps-regen.ts
7
7
  /**
@@ -48,11 +48,11 @@ import { resolve } from "node:path";
48
48
  */
49
49
  const { DepsRegen } = Changesets;
50
50
  /* v8 ignore start -- CLI option definitions */
51
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Project root (defaults to the current working directory)"), Flag.withDefault("."));
52
- const baseOption = Flag.string("base").pipe(Flag.withDescription("Override the base branch (defaults to config baseBranch)"), Flag.optional);
53
- const packageOption = Flag.string("package").pipe(Flag.withDescription("Restrict regeneration to a single workspace package"), Flag.optional);
54
- const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withDescription("Print the plan without writing or deleting"), Flag.withDefault(false));
55
- const jsonOption = Flag.boolean("json").pipe(Flag.withDescription("Emit a structured plan as JSON"), Flag.withDefault(false));
51
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Project root (defaults to the current working directory)"), Flag.withDefault("."));
52
+ const baseOption = Flag.String("base").pipe(Flag.withDescription("Override the base branch (defaults to config baseBranch)"), Flag.optional);
53
+ const packageOption = Flag.String("package").pipe(Flag.withDescription("Restrict regeneration to a single workspace package"), Flag.optional);
54
+ const dryRunOption = Flag.Boolean("dry-run").pipe(Flag.withDescription("Print the plan without writing or deleting"), Flag.withDefault(false));
55
+ const jsonOption = Flag.Boolean("json").pipe(Flag.withDescription("Emit a structured plan as JSON"), Flag.withDefault(false));
56
56
  /* v8 ignore stop */
57
57
  /**
58
58
  * Handler exported for direct invocation in tests.
@@ -1,10 +1,10 @@
1
- import { Git } from "@effected/git";
2
- import { WorkspaceRoot } from "@effected/workspaces";
1
+ import { join } from "node:path";
3
2
  import { Changesets } from "@savvy-web/silk-effects";
4
3
  import { Data, Effect, Option, Result, Schema } from "effect";
5
- import { join } from "node:path";
6
4
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
5
+ import { Git } from "@effected/git";
7
6
  import { Jsonc, JsoncEdit, JsoncModifier } from "@effected/jsonc";
7
+ import { WorkspaceRoot } from "@effected/workspaces";
8
8
 
9
9
  //#region src/commands/changeset/commands/init.ts
10
10
  /**
@@ -1,7 +1,7 @@
1
- import { Changesets } from "@savvy-web/silk-effects";
2
- import { Console, Effect } from "effect";
3
1
  import { Argument, Command, Flag } from "effect/unstable/cli";
4
2
  import { resolve } from "node:path";
3
+ import { Changesets } from "@savvy-web/silk-effects";
4
+ import { Console, Effect } from "effect";
5
5
 
6
6
  //#region src/commands/changeset/commands/lint.ts
7
7
  /**
@@ -28,8 +28,8 @@ import { resolve } from "node:path";
28
28
  */
29
29
  const { ChangesetLinter } = Changesets;
30
30
  /* v8 ignore start -- CLI option definitions; handler tested via runLint */
31
- const dirArg = Argument.directory("dir").pipe(Argument.withDefault(".changeset"));
32
- const quietOption = Flag.boolean("quiet").pipe(Flag.withAlias("q"), Flag.withDescription("Only output errors, no summary"), Flag.withDefault(false));
31
+ const dirArg = Argument.Directory("dir").pipe(Argument.withDefault(".changeset"));
32
+ const quietOption = Flag.Boolean("quiet").pipe(Flag.withAlias("q"), Flag.withDescription("Only output errors, no summary"), Flag.withDefault(false));
33
33
  /* v8 ignore stop */
34
34
  /**
35
35
  * Run machine-readable lint validation on all changeset files in `dir`.
@@ -1,8 +1,8 @@
1
1
  import { requireValidConfig } from "../utils/config-gate.js";
2
- import { Changesets } from "@savvy-web/silk-effects";
3
- import { Effect } from "effect";
4
2
  import { Argument, Command, Flag } from "effect/unstable/cli";
5
3
  import { dirname, resolve } from "node:path";
4
+ import { Changesets } from "@savvy-web/silk-effects";
5
+ import { Effect } from "effect";
6
6
  import { readFileSync, writeFileSync } from "node:fs";
7
7
 
8
8
  //#region src/commands/changeset/commands/transform.ts
@@ -38,9 +38,9 @@ import { readFileSync, writeFileSync } from "node:fs";
38
38
  */
39
39
  const { ChangelogTransformer } = Changesets;
40
40
  /* v8 ignore start -- CLI option definitions; handler tested via runTransform */
41
- const fileArg = Argument.file("file").pipe(Argument.withDefault("CHANGELOG.md"));
42
- const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withAlias("n"), Flag.withDescription("Print transformed output instead of writing"), Flag.withDefault(false));
43
- const checkOption = Flag.boolean("check").pipe(Flag.withAlias("c"), Flag.withDescription("Exit 1 if file would change (for CI)"), Flag.withDefault(false));
41
+ const fileArg = Argument.File("file").pipe(Argument.withDefault("CHANGELOG.md"));
42
+ const dryRunOption = Flag.Boolean("dry-run").pipe(Flag.withAlias("n"), Flag.withDescription("Print transformed output instead of writing"), Flag.withDefault(false));
43
+ const checkOption = Flag.Boolean("check").pipe(Flag.withAlias("c"), Flag.withDescription("Exit 1 if file would change (for CI)"), Flag.withDefault(false));
44
44
  /* v8 ignore stop */
45
45
  /**
46
46
  * Run the remark transform pipeline on a single changelog file.
@@ -1,6 +1,6 @@
1
+ import { Argument, Command } from "effect/unstable/cli";
1
2
  import { Changesets } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { Argument, Command } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/changeset/commands/validate-file.ts
6
6
  /**
@@ -19,7 +19,7 @@ import { Argument, Command } from "effect/unstable/cli";
19
19
  */
20
20
  const { ChangesetLinter } = Changesets;
21
21
  /* v8 ignore next */
22
- const fileArg = Argument.file("file");
22
+ const fileArg = Argument.File("file");
23
23
  /**
24
24
  * Run lint validation on a single changeset file.
25
25
  *
@@ -1,7 +1,7 @@
1
1
  import { requireValidConfig } from "../utils/config-gate.js";
2
+ import { Command, Flag } from "effect/unstable/cli";
2
3
  import { Changesets } from "@savvy-web/silk-effects";
3
4
  import { Effect } from "effect";
4
- import { Command, Flag } from "effect/unstable/cli";
5
5
 
6
6
  //#region src/commands/changeset/commands/version.ts
7
7
  /**
@@ -15,7 +15,7 @@ import { Command, Flag } from "effect/unstable/cli";
15
15
  * @internal
16
16
  */
17
17
  /* v8 ignore start -- CLI option definitions; handler tested via runVersion */
18
- const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withAlias("n"), Flag.withDescription("Compute and report the release without writing anything"), Flag.withDefault(false));
18
+ const dryRunOption = Flag.Boolean("dry-run").pipe(Flag.withAlias("n"), Flag.withDescription("Compute and report the release without writing anything"), Flag.withDefault(false));
19
19
  /* v8 ignore stop */
20
20
  /**
21
21
  * Validate config, then natively apply (or dry-run) the release via
@@ -1,6 +1,6 @@
1
+ import { join, resolve } from "node:path";
1
2
  import { Changesets } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { join, resolve } from "node:path";
4
4
  import { existsSync } from "node:fs";
5
5
 
6
6
  //#region src/commands/changeset/utils/config-gate.ts
package/commands/check.js CHANGED
@@ -2,8 +2,8 @@ import { runChangesetCheck } from "./changeset/commands/check.js";
2
2
  import "./changeset/index.js";
3
3
  import { runCommitCheck } from "./commit/check.js";
4
4
  import { runLintCheck } from "./lint/check.js";
5
- import { Effect, Result } from "effect";
6
5
  import { Command, Flag } from "effect/unstable/cli";
6
+ import { Effect, Result } from "effect";
7
7
 
8
8
  //#region src/commands/check.ts
9
9
  /**
@@ -18,15 +18,15 @@ import { Command, Flag } from "effect/unstable/cli";
18
18
  * regardless of individual failures and then surface the full set of errors.
19
19
  *
20
20
  * Runtime layer provision (ManagedSection, FileSystem, WorkspaceRoot,
21
- * ToolDiscovery, etc.) is deferred to Task B7 (root `runCli`). The Effect
21
+ * ToolDiscovery, etc.) is deferred to Task B7 (root `main`). The Effect
22
22
  * returned by `checkCommand`'s handler therefore carries the full union of the
23
23
  * three handlers' requirements in its R channel.
24
24
  *
25
25
  * @internal
26
26
  */
27
27
  /* v8 ignore start -- CLI option definitions; orchestration logic tested via runCheck */
28
- const changesetDirOption = Flag.string("changeset-dir").pipe(Flag.withDescription("Path to the changeset directory"), Flag.withDefault(".changeset"));
29
- const quietOption = Flag.boolean("quiet").pipe(Flag.withAlias("q"), Flag.withDescription("Only output warnings from lint check"), Flag.withDefault(false));
28
+ const changesetDirOption = Flag.String("changeset-dir").pipe(Flag.withDescription("Path to the changeset directory"), Flag.withDefault(".changeset"));
29
+ const quietOption = Flag.Boolean("quiet").pipe(Flag.withAlias("q"), Flag.withDescription("Only output warnings from lint check"), Flag.withDefault(false));
30
30
  /* v8 ignore stop */
31
31
  /**
32
32
  * Run all three check step Effects without short-circuiting.
package/commands/clean.js CHANGED
@@ -1,7 +1,7 @@
1
- import { WorkspaceDiscovery } from "@effected/workspaces";
2
- import { Data, Effect } from "effect";
3
1
  import { Command, Flag } from "effect/unstable/cli";
4
2
  import { join, sep } from "node:path";
3
+ import { Data, Effect } from "effect";
4
+ import { WorkspaceDiscovery } from "@effected/workspaces";
5
5
  import { glob, realpath, rm } from "node:fs/promises";
6
6
 
7
7
  //#region src/commands/clean.ts
@@ -165,8 +165,8 @@ function runClean(opts) {
165
165
  });
166
166
  }
167
167
  /* v8 ignore start -- CLI option/registration; orchestration tested via runClean */
168
- const globsOption = Flag.string("globs").pipe(Flag.withAlias("g"), Flag.withDescription(`Comma-separated glob patterns to remove from each workspace root (default: ${DEFAULT_GLOBS.join(",")})`), Flag.withDefault(DEFAULT_GLOBS.join(",")));
169
- const dryRunOption = Flag.boolean("dry-run").pipe(Flag.withAlias("n"), Flag.withDescription("Report what would be removed without deleting anything"), Flag.withDefault(false));
168
+ const globsOption = Flag.String("globs").pipe(Flag.withAlias("g"), Flag.withDescription(`Comma-separated glob patterns to remove from each workspace root (default: ${DEFAULT_GLOBS.join(",")})`), Flag.withDefault(DEFAULT_GLOBS.join(",")));
169
+ const dryRunOption = Flag.Boolean("dry-run").pipe(Flag.withAlias("n"), Flag.withDescription("Report what would be removed without deleting anything"), Flag.withDefault(false));
170
170
  const _cleanCommand = Command.make("clean", {
171
171
  globs: globsOption,
172
172
  dryRun: dryRunOption
@@ -1,9 +1,9 @@
1
1
  import { HUSKY_HOOK_PATH, POST_CHECKOUT_HOOK_PATH, POST_COMMIT_HOOK_PATH, POST_MERGE_HOOK_PATH } from "./constants.js";
2
2
  import { SECTION_DEF, savvyCommitBlock } from "./init.js";
3
- import { CheckOutcome, ManagedSection } from "@effected/templates";
4
- import { VersioningStrategy } from "@effected/workspaces";
5
3
  import { ChangesetConfigReader, Commitlint, SavvyBaseSection, SavvyHooksSection, SavvyToolchainSection, savvyBasePreamble, savvyHooksHygiene, savvyInstallBlock, savvyToolchainCheck } from "@savvy-web/silk-effects";
6
4
  import { Effect, FileSystem, Option } from "effect";
5
+ import { VersioningStrategy } from "@effected/workspaces";
6
+ import { CheckOutcome, ManagedSection } from "@effected/templates";
7
7
 
8
8
  //#region src/commands/commit/check.ts
9
9
  /** Unicode cross symbol. */
@@ -1,8 +1,8 @@
1
1
  import { buildCommitlintInvocation } from "../commitlint-invocation.js";
2
- import { Git } from "@effected/git";
2
+ import { Command } from "effect/unstable/cli";
3
3
  import { Commitlint } from "@savvy-web/silk-effects";
4
4
  import { Effect } from "effect";
5
- import { Command } from "effect/unstable/cli";
5
+ import { Git } from "@effected/git";
6
6
  import { promisify } from "node:util";
7
7
  import { execFile } from "node:child_process";
8
8
 
@@ -1,7 +1,7 @@
1
- import { Commitlint } from "@savvy-web/silk-effects";
2
- import { Effect, Schema } from "effect";
3
1
  import { Command } from "effect/unstable/cli";
4
2
  import { resolve } from "node:path";
3
+ import { Commitlint } from "@savvy-web/silk-effects";
4
+ import { Effect, Schema } from "effect";
5
5
 
6
6
  //#region src/commands/commit/hooks/pre-commit-message.ts
7
7
  /**
@@ -1,7 +1,7 @@
1
- import { Commitlint } from "@savvy-web/silk-effects";
2
- import { Effect } from "effect";
3
1
  import { Command } from "effect/unstable/cli";
4
2
  import { resolve } from "node:path";
3
+ import { Commitlint } from "@savvy-web/silk-effects";
4
+ import { Effect } from "effect";
5
5
 
6
6
  //#region src/commands/commit/hooks/session-start.ts
7
7
  /**
@@ -1,8 +1,8 @@
1
1
  import { HUSKY_HOOK_PATH, POST_CHECKOUT_HOOK_PATH, POST_COMMIT_HOOK_PATH, POST_MERGE_HOOK_PATH } from "./constants.js";
2
- import { CommentStyle, ManagedSection, SectionId } from "@effected/templates";
2
+ import { dirname } from "node:path";
3
3
  import { SavvyBaseSection, SavvyHooksSection, SavvyToolchainSection, savvyBasePreamble, savvyHooksHygiene, savvyInstallBlock, savvyToolSection, savvyToolchainCheck } from "@savvy-web/silk-effects";
4
4
  import { Effect, FileSystem } from "effect";
5
- import { dirname } from "node:path";
5
+ import { CommentStyle, ManagedSection, SectionId } from "@effected/templates";
6
6
  import { chmod } from "node:fs/promises";
7
7
 
8
8
  //#region src/commands/commit/init.ts
@@ -1,8 +1,8 @@
1
1
  import { buildCommitlintInvocation } from "./commitlint-invocation.js";
2
- import { Git } from "@effected/git";
2
+ import { Argument, CliError, Command } from "effect/unstable/cli";
3
3
  import { Commitlint } from "@savvy-web/silk-effects";
4
4
  import { Effect } from "effect";
5
- import { Argument, CliError, Command } from "effect/unstable/cli";
5
+ import { Git } from "@effected/git";
6
6
  import { promisify } from "node:util";
7
7
  import { execFile } from "node:child_process";
8
8
 
@@ -79,7 +79,7 @@ function runCommitLint(file) {
79
79
  });
80
80
  }
81
81
  /* v8 ignore next */
82
- const fileArg = Argument.file("file", { mustExist: true });
82
+ const fileArg = Argument.File("file", { mustExist: true });
83
83
  /* v8 ignore next 3 -- CLI registration; handler tested via runCommitLint */
84
84
  const lintCommand = Command.make("lint", { file: fileArg }, ({ file }) => runCommitLint(file)).pipe(Command.withDescription("Validate a candidate commit-message file against the Silk commitlint preset"));
85
85
 
package/commands/init.js CHANGED
@@ -2,8 +2,8 @@ import { runChangesetInit } from "./changeset/commands/init.js";
2
2
  import "./changeset/index.js";
3
3
  import { runCommitInit } from "./commit/init.js";
4
4
  import { runLintInit } from "./lint/init.js";
5
- import { Effect } from "effect";
6
5
  import { Command, Flag } from "effect/unstable/cli";
6
+ import { Effect } from "effect";
7
7
 
8
8
  //#region src/commands/init.ts
9
9
  /**
@@ -15,7 +15,7 @@ import { Command, Flag } from "effect/unstable/cli";
15
15
  * are injected so the orchestration logic is unit-testable in isolation.
16
16
  *
17
17
  * Runtime layer provision (ManagedSection, FileSystem, WorkspaceRoot,
18
- * BiomeSchemaSync, etc.) is deferred to Task B7 (root `runCli`). The Effect
18
+ * BiomeSchemaSync, etc.) is deferred to Task B7 (root `main`). The Effect
19
19
  * returned by `initCommand`'s handler therefore carries the full union of the
20
20
  * three handlers' requirements in its R channel.
21
21
  *
@@ -24,10 +24,10 @@ import { Command, Flag } from "effect/unstable/cli";
24
24
  const DEFAULT_COMMIT_CONFIG = "lib/configs/commitlint.config.ts";
25
25
  const DEFAULT_LINT_CONFIG = "lib/configs/lint-staged.config.ts";
26
26
  /* v8 ignore start -- CLI option definitions; orchestration logic tested via runInit */
27
- const forceOption = Flag.boolean("force").pipe(Flag.withAlias("f"), Flag.withDescription("Overwrite existing config files and hooks across all tools"), Flag.withDefault(false));
28
- const commitConfigOption = Flag.string("commit-config").pipe(Flag.withDescription("Relative path for the commitlint config file"), Flag.withDefault(DEFAULT_COMMIT_CONFIG));
29
- const lintConfigOption = Flag.string("lint-config").pipe(Flag.withDescription("Relative path for the lint-staged config file"), Flag.withDefault(DEFAULT_LINT_CONFIG));
30
- const lintPresetOption = Flag.choice("lint-preset", [
27
+ const forceOption = Flag.Boolean("force").pipe(Flag.withAlias("f"), Flag.withDescription("Overwrite existing config files and hooks across all tools"), Flag.withDefault(false));
28
+ const commitConfigOption = Flag.String("commit-config").pipe(Flag.withDescription("Relative path for the commitlint config file"), Flag.withDefault(DEFAULT_COMMIT_CONFIG));
29
+ const lintConfigOption = Flag.String("lint-config").pipe(Flag.withDescription("Relative path for the lint-staged config file"), Flag.withDefault(DEFAULT_LINT_CONFIG));
30
+ const lintPresetOption = Flag.Literals("lint-preset", [
31
31
  "minimal",
32
32
  "standard",
33
33
  "silk"
@@ -19,7 +19,7 @@
19
19
  *
20
20
  * @internal
21
21
  */
22
- const BIOME_VERSION = "2.5.12";
22
+ const BIOME_VERSION = "2.5.13";
23
23
 
24
24
  //#endregion
25
25
  export { BIOME_VERSION };
@@ -1,10 +1,10 @@
1
1
  import { BIOME_VERSION } from "./biome-version.js";
2
- import { Tool, ToolDiscovery } from "@effected/commands";
3
- import { CheckOutcome, ManagedSection } from "@effected/templates";
4
2
  import { ConfigDiscovery, Lint, SavvyBaseSection, SavvyHooksSection, SavvyToolchainSection, savvyBasePreamble, savvyHooksHygiene, savvyInstallBlock, savvyToolchainCheck } from "@savvy-web/silk-effects";
5
3
  import { Effect, FileSystem, Option } from "effect";
6
4
  import { Jsonc } from "@effected/jsonc";
5
+ import { CheckOutcome, ManagedSection } from "@effected/templates";
7
6
  import { isDeepStrictEqual } from "node:util";
7
+ import { Tool, ToolDiscovery } from "@effected/commands";
8
8
 
9
9
  //#region src/commands/lint/check.ts
10
10
  /**
@@ -53,6 +53,10 @@ function findConfigFile(fs) {
53
53
  /**
54
54
  * Find the first existing config file from a list of candidates using ConfigDiscovery.
55
55
  *
56
+ * The engine takes no ambient cwd; this CLI front end owns the process, so the
57
+ * discovery root is the process cwd the rest of `lint check` already resolves
58
+ * its relative paths against.
59
+ *
56
60
  * @param discovery - ConfigDiscovery service
57
61
  * @param names - Config file names to search for (in priority order)
58
62
  * @returns The config file path or null
@@ -60,7 +64,7 @@ function findConfigFile(fs) {
60
64
  function findConfig(discovery, names) {
61
65
  return Effect.gen(function* () {
62
66
  for (const name of names) {
63
- const result = yield* discovery.find(name);
67
+ const result = yield* discovery.find(name, { cwd: process.cwd() });
64
68
  if (result) return result.path;
65
69
  }
66
70
  return null;
@@ -1,6 +1,6 @@
1
+ import { Argument, Command, Flag } from "effect/unstable/cli";
1
2
  import { Lint } from "@savvy-web/silk-effects";
2
3
  import { Effect, Option } from "effect";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
4
4
  import { existsSync, readFileSync, writeFileSync } from "node:fs";
5
5
  import { Yaml } from "@effected/yaml";
6
6
 
@@ -16,7 +16,7 @@ import { Yaml } from "@effected/yaml";
16
16
  * @internal
17
17
  */
18
18
  /** Repeated file path arguments. */
19
- const filesArg = Argument.file("files", { mustExist: true }).pipe(Argument.variadic());
19
+ const filesArg = Argument.File("files", { mustExist: true }).pipe(Argument.variadic());
20
20
  /**
21
21
  * Formatting options forwarded from `Lint.Yaml.fmtCommand`.
22
22
  *
@@ -25,7 +25,7 @@ const filesArg = Argument.file("files", { mustExist: true }).pipe(Argument.varia
25
25
  * handler only reach the formatter across this flag. Omitting it would make
26
26
  * `fmtCommand({ format })` and `create({ format })` write different bytes.
27
27
  */
28
- const yamlFormatOption = Flag.string("format").pipe(Flag.withDescription("JSON-encoded @effected/yaml formatting options"), Flag.optional);
28
+ const yamlFormatOption = Flag.String("format").pipe(Flag.withDescription("JSON-encoded @effected/yaml formatting options"), Flag.optional);
29
29
  /** Sort package.json files with sort-package-json. */
30
30
  const packageJsonCommand = Command.make("package-json", { files: filesArg }, ({ files }) => Effect.sync(() => {
31
31
  for (const filepath of files) {
@@ -1,10 +1,10 @@
1
1
  import { BIOME_VERSION } from "./biome-version.js";
2
- import { ManagedSection } from "@effected/templates";
3
- import { WorkspaceDiscovery } from "@effected/workspaces";
2
+ import { dirname, join } from "node:path";
4
3
  import { BiomeSchemaSync, LIFECYCLE_SCRIPTS_CONFIG_KEY, Lint, SavvyBaseSection, SavvyHooksSection, SavvyToolchainSection, publishesBuiltLinkDirectory, savvyBasePreamble, savvyHooksHygiene, savvyInstallBlock, savvyToolchainCheck } from "@savvy-web/silk-effects";
5
4
  import { Effect, FileSystem } from "effect";
6
- import { dirname, join } from "node:path";
7
5
  import { Jsonc, JsoncEdit, JsoncModifier } from "@effected/jsonc";
6
+ import { WorkspaceDiscovery } from "@effected/workspaces";
7
+ import { ManagedSection } from "@effected/templates";
8
8
  import { chmod } from "node:fs/promises";
9
9
  import { isDeepStrictEqual } from "node:util";
10
10
 
@@ -138,10 +138,10 @@ function writeMarkdownlintConfig(fs, preset, force) {
138
138
  * needed on the happy path.
139
139
  *
140
140
  * Discovery failing yields an EMPTY list, which the caller reads as "no roots
141
- * to enumerate — scan `BiomeSchemaSync`'s own default directory instead". The
142
- * cwd is deliberately not named here: resolving it is the sync service's job,
143
- * and an ambient `process.cwd()` read in this function would be a second,
144
- * divergent source of truth for the same directory.
141
+ * to enumerate — scan the process cwd instead". The cwd is deliberately not
142
+ * named here: `syncBiomeSchemas` resolves it exactly once for the fallback
143
+ * pass, and an ambient `process.cwd()` read in this function would be a
144
+ * second, divergent source of truth for the same directory.
145
145
  *
146
146
  * The two ways discovery fails mean different things and are reported differently:
147
147
  *
@@ -212,7 +212,7 @@ function syncBiomeSchemas() {
212
212
  return Effect.gen(function* () {
213
213
  const syncer = yield* BiomeSchemaSync;
214
214
  const roots = yield* biomeConfigRoots();
215
- const passes = roots.length > 0 ? roots.map((cwd) => ({ cwd })) : [void 0];
215
+ const passes = roots.length > 0 ? roots.map((cwd) => ({ cwd })) : [{ cwd: process.cwd() }];
216
216
  for (const options of passes) yield* syncer.sync(BIOME_VERSION, options).pipe(Effect.flatMap((result) => Effect.gen(function* () {
217
217
  for (const configPath of result.current) yield* Effect.log(`${CHECK_MARK} ${configPath}: biome $schema up-to-date`);
218
218
  for (const configPath of result.updated) yield* Effect.log(`${CHECK_MARK} Updated $schema in ${configPath}`);
@@ -1,6 +1,6 @@
1
+ import { Argument, Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Effect, Option } from "effect";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/add.ts
6
6
  /**
@@ -29,12 +29,12 @@ import { Argument, Command, Flag } from "effect/unstable/cli";
29
29
  * @internal
30
30
  */
31
31
  /* v8 ignore start -- CLI option/arg definitions */
32
- const urlArg = Argument.string("url");
33
- const refOption = Flag.string("ref").pipe(Flag.withDescription("Ref (tag, branch, or commit) to check out"));
34
- const purposeOption = Flag.string("purpose").pipe(Flag.withDescription("Why this repo is vendored"));
35
- const nameOption = Flag.string("name").pipe(Flag.withDescription("Vendored directory name; defaults to the URL's last path segment"), Flag.optional);
36
- const sparseOption = Flag.string("sparse").pipe(Flag.withDescription("Sparse-checkout path; repeatable"), Flag.atLeast(0));
37
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root to add within"), Flag.withDefault("."));
32
+ const urlArg = Argument.String("url");
33
+ const refOption = Flag.String("ref").pipe(Flag.withDescription("Ref (tag, branch, or commit) to check out"));
34
+ const purposeOption = Flag.String("purpose").pipe(Flag.withDescription("Why this repo is vendored"));
35
+ const nameOption = Flag.String("name").pipe(Flag.withDescription("Vendored directory name; defaults to the URL's last path segment"), Flag.optional);
36
+ const sparseOption = Flag.String("sparse").pipe(Flag.withDescription("Sparse-checkout path; repeatable"), Flag.atLeast(0));
37
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root to add within"), Flag.withDefault("."));
38
38
  /* v8 ignore stop */
39
39
  /**
40
40
  * Add handler; exported for tests.
@@ -1,6 +1,6 @@
1
+ import { Argument, Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/deregister.ts
6
6
  /**
@@ -36,8 +36,8 @@ import { Argument, Command, Flag } from "effect/unstable/cli";
36
36
  * @internal
37
37
  */
38
38
  /* v8 ignore start -- CLI option/arg definitions */
39
- const sectionArg = Argument.string("section");
40
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root to deregister within"), Flag.withDefault("."));
39
+ const sectionArg = Argument.String("section");
40
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root to deregister within"), Flag.withDefault("."));
41
41
  /* v8 ignore stop */
42
42
  /**
43
43
  * Deregister handler; exported for tests.
@@ -1,6 +1,6 @@
1
+ import { Argument, Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/note.ts
6
6
  /**
@@ -52,11 +52,11 @@ const runReposNote = (cwd, name, op) => Effect.gen(function* () {
52
52
  return Effect.log(error.message);
53
53
  }));
54
54
  /* v8 ignore start -- CLI registration; handler tested via runReposNote */
55
- const nameArg = Argument.string("name");
56
- const noteTextArg = Argument.string("text");
57
- const noteIdArg = Argument.string("id");
58
- const intoOption = Flag.choice("into", ["layout", "startHere"]).pipe(Flag.withDescription("Curated orientation field to promote the note into"));
59
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root whose manifest holds the notes"), Flag.withDefault("."));
55
+ const nameArg = Argument.String("name");
56
+ const noteTextArg = Argument.String("text");
57
+ const noteIdArg = Argument.String("id");
58
+ const intoOption = Flag.Literals("into", ["layout", "startHere"]).pipe(Flag.withDescription("Curated orientation field to promote the note into"));
59
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root whose manifest holds the notes"), Flag.withDefault("."));
60
60
  const _noteGroup = Command.make("note").pipe(Command.withSharedFlags({ cwd: cwdOption }));
61
61
  const addLeaf = Command.make("add", {
62
62
  name: nameArg,
@@ -1,6 +1,6 @@
1
+ import { Argument, Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/pin.ts
6
6
  /**
@@ -28,9 +28,9 @@ import { Argument, Command, Flag } from "effect/unstable/cli";
28
28
  * @internal
29
29
  */
30
30
  /* v8 ignore start -- CLI option/arg definitions */
31
- const nameArg = Argument.string("name");
32
- const refArg = Argument.string("ref");
33
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root to pin within"), Flag.withDefault("."));
31
+ const nameArg = Argument.String("name");
32
+ const refArg = Argument.String("ref");
33
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root to pin within"), Flag.withDefault("."));
34
34
  /* v8 ignore stop */
35
35
  /**
36
36
  * Pin handler; exported for tests.
@@ -1,6 +1,6 @@
1
+ import { Argument, Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/remove.ts
6
6
  /**
@@ -33,8 +33,8 @@ import { Argument, Command, Flag } from "effect/unstable/cli";
33
33
  * @internal
34
34
  */
35
35
  /* v8 ignore start -- CLI option/arg definitions */
36
- const nameArg = Argument.string("name");
37
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root to remove within"), Flag.withDefault("."));
36
+ const nameArg = Argument.String("name");
37
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root to remove within"), Flag.withDefault("."));
38
38
  /* v8 ignore stop */
39
39
  /**
40
40
  * Remove handler; exported for tests.
@@ -1,6 +1,6 @@
1
+ import { Argument, Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/rename.ts
6
6
  /**
@@ -34,9 +34,9 @@ import { Argument, Command, Flag } from "effect/unstable/cli";
34
34
  * @internal
35
35
  */
36
36
  /* v8 ignore start -- CLI option/arg definitions */
37
- const oldNameArg = Argument.string("old-name");
38
- const newNameArg = Argument.string("new-name");
39
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root to rename within"), Flag.withDefault("."));
37
+ const oldNameArg = Argument.String("old-name");
38
+ const newNameArg = Argument.String("new-name");
39
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root to rename within"), Flag.withDefault("."));
40
40
  /* v8 ignore stop */
41
41
  /**
42
42
  * Rename handler; exported for tests.
@@ -1,6 +1,6 @@
1
+ import { Argument, Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { Argument, Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/restore.ts
6
6
  /**
@@ -40,8 +40,8 @@ import { Argument, Command, Flag } from "effect/unstable/cli";
40
40
  * @internal
41
41
  */
42
42
  /* v8 ignore start -- CLI option/arg definitions */
43
- const namesArg = Argument.string("name").pipe(Argument.variadic());
44
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root to restore within"), Flag.withDefault("."));
43
+ const namesArg = Argument.String("name").pipe(Argument.variadic());
44
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root to restore within"), Flag.withDefault("."));
45
45
  /* v8 ignore stop */
46
46
  /**
47
47
  * Restore handler; exported for tests.
@@ -1,6 +1,6 @@
1
+ import { Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Console, Effect } from "effect";
3
- import { Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/status.ts
6
6
  /**
@@ -35,9 +35,9 @@ import { Command, Flag } from "effect/unstable/cli";
35
35
  * @internal
36
36
  */
37
37
  /* v8 ignore start -- CLI option definitions */
38
- const jsonOption = Flag.boolean("json").pipe(Flag.withDescription("Emit the structured drift report as JSON"), Flag.withDefault(false));
39
- const driftOption = Flag.boolean("drift").pipe(Flag.withDescription("Also reconcile the manifest, .gitmodules, worktree, and git submodule status"), Flag.withDefault(false));
40
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root to inspect"), Flag.withDefault("."));
38
+ const jsonOption = Flag.Boolean("json").pipe(Flag.withDescription("Emit the structured drift report as JSON"), Flag.withDefault(false));
39
+ const driftOption = Flag.Boolean("drift").pipe(Flag.withDescription("Also reconcile the manifest, .gitmodules, worktree, and git submodule status"), Flag.withDefault(false));
40
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root to inspect"), Flag.withDefault("."));
41
41
  /* v8 ignore stop */
42
42
  /**
43
43
  * Drift report handler; exported for tests.
@@ -1,6 +1,6 @@
1
+ import { Command, Flag } from "effect/unstable/cli";
1
2
  import { Repos } from "@savvy-web/silk-effects";
2
3
  import { Effect } from "effect";
3
- import { Command, Flag } from "effect/unstable/cli";
4
4
 
5
5
  //#region src/commands/repos/commands/sync.ts
6
6
  /**
@@ -28,7 +28,7 @@ import { Command, Flag } from "effect/unstable/cli";
28
28
  * @internal
29
29
  */
30
30
  /* v8 ignore start -- CLI option definitions */
31
- const cwdOption = Flag.directory("cwd").pipe(Flag.withDescription("Repo root to sync"), Flag.withDefault("."));
31
+ const cwdOption = Flag.Directory("cwd").pipe(Flag.withDescription("Repo root to sync"), Flag.withDefault("."));
32
32
  /* v8 ignore stop */
33
33
  /**
34
34
  * Sync handler; exported for tests.
package/index.d.ts CHANGED
@@ -8,65 +8,6 @@ import { ManagedSection, SectionFileError, SectionParseError, SectionRenderError
8
8
  import { PlatformError } from "effect/PlatformError";
9
9
  import { ToolDiscovery } from "@effected/commands";
10
10
  import { JsoncParseError } from "@effected/jsonc";
11
- //#region src/cli/index.d.ts
12
- /**
13
- * Root `savvy` CLI entry point using `effect/unstable/cli`.
14
- *
15
- * @remarks
16
- * Assembles the five Phase-B command pieces — the `init` and `check` top-level
17
- * orchestrators plus the `changeset`, `commit`, and `lint` groups — under a
18
- * single `savvy` root command, then provides the merged runtime Layer stack
19
- * that satisfies every command's service requirements.
20
- *
21
- * The layer stack wires the silk-effects service layers over their v4
22
- * requirement channels:
23
- *
24
- * - `NodeServices.layer` — `FileSystem`, `Path`, `ChildProcessSpawner`, `Stdio`,
25
- * and `Terminal`, consumed by every config reader, workspace service, git
26
- * layer, and the CLI framework itself.
27
- * - `Git.layer` (from `@effected/git`) — the typed git service backing
28
- * `BranchAnalyzer`, `ReposManager`, the commit hooks
29
- * (`readBranchInfo` / `readSigningDiagnostic`), and `detectGitHubRepo`.
30
- * - Workspace services from `@effected/workspaces` — `WorkspaceRoot.layer`,
31
- * `PackageManagerDetector.layer`, and a single root-bound
32
- * `WorkspaceDiscovery.layer()` (the kit resolves the root through
33
- * `WorkspaceRoot` from `process.cwd()` lazily on first use, so the CLI's
34
- * startup cwd is the discovery root).
35
- * - Flat silk-effects services — `ChangesetConfigReader.layer`,
36
- * `SilkPublishability.layer`, `ManagedSection.layer` (from
37
- * `@effected/templates`), `BiomeSchemaSync.layer`,
38
- * and `ConfigDiscovery.layer`. Tool resolution is the kit's
39
- * `ToolDiscovery.layer` over `ChildProcessSpawner` plus a `LocalExec`, which
40
- * `Workspaces.localExecLayer()` supplies from the detected package manager.
41
- * Versioning classification
42
- * is a pure `@effected/workspaces` value operation over `WorkspaceDiscovery`
43
- * and the silk `PublishabilityDetector`, so it needs no layer of its own.
44
- * - Changesets-namespace services — `Changesets.ConfigInspector.layer`,
45
- * `Changesets.ReleasePlanner.layer`, and `Changesets.BranchAnalyzer.layer`
46
- * sharing a single `ConfigInspector` via `provideMerge`. `DepsRegen` is NOT
47
- * part of AppLive: its graph is root-bound at layer build, so the deps
48
- * commands compose `Changesets.makeDepsRegenDefault({ cwd })` per invocation
49
- * against their parsed `--cwd` (platform services flow up to
50
- * `NodeServices.layer` here).
51
- *
52
- * The CLI version is injected at build time via `process.env.__PACKAGE_VERSION__`.
53
- *
54
- * @internal
55
- */
56
- /**
57
- * Bootstrap and run the `savvy` CLI application.
58
- *
59
- * @remarks
60
- * `Command.run` returns an Effect reading `process.argv` from the Stdio
61
- * service; the merged layer stack is provided and execution handed to
62
- * `NodeRuntime.runMain`, whose default reporting covers defects (the v3
63
- * `Cause.defects` wrapper is gone). No type casts: the layer graph is
64
- * validated by the compiler.
65
- *
66
- * @internal
67
- */
68
- export declare function runCli(): void;
69
- //#endregion
70
11
  //#region src/commands/changeset/commands/check.d.ts
71
12
  /**
72
13
  * Run the check validation pipeline on all changeset files in `dir`.
package/index.js CHANGED
@@ -12,6 +12,5 @@ import { lintCommand } from "./commands/lint/index.js";
12
12
  import { runReposStatus } from "./commands/repos/commands/status.js";
13
13
  import { runReposSync } from "./commands/repos/commands/sync.js";
14
14
  import { reposCommand } from "./commands/repos/index.js";
15
- import { runCli } from "./cli/index.js";
16
15
 
17
- export { changesetCommand, checkCommand, commitCommand, initCommand, lintCommand, reposCommand, runChangesetCheck, runChangesetInit, runCheck, runCli, runCommitCheck, runCommitInit, runInit, runLintCheck, runLintInit, runReposStatus, runReposSync };
16
+ export { changesetCommand, checkCommand, commitCommand, initCommand, lintCommand, reposCommand, runChangesetCheck, runChangesetInit, runCheck, runCommitCheck, runCommitInit, runInit, runLintCheck, runLintInit, runReposStatus, runReposSync };
package/main.d.ts ADDED
@@ -0,0 +1,7 @@
1
+ //#region src/main.d.ts
2
+ /**
3
+ * Bootstrap and run the `savvy` CLI application.
4
+ */
5
+ export declare const main: () => void;
6
+ //#endregion
7
+ //# sourceMappingURL=main.d.ts.map
package/main.js ADDED
@@ -0,0 +1,35 @@
1
+ import { AppLive, rootCommand } from "./cli/index.js";
2
+ import { Command } from "effect/unstable/cli";
3
+ import { Effect } from "effect";
4
+ import { NodeRuntime } from "@effect/platform-node";
5
+
6
+ //#region src/main.ts
7
+ /**
8
+ * Owns the process. `index.ts` must stay importable without side effects;
9
+ * never import this module from the barrel.
10
+ *
11
+ * @remarks
12
+ * Assembles the `Command.run` Effect over `rootCommand`, provides the merged
13
+ * `AppLive` runtime layer stack from `./cli/index.js`, and hands execution to
14
+ * `NodeRuntime.runMain`, whose default reporting covers defects (the v3
15
+ * `Cause.defects` wrapper is gone). No type casts: the layer graph is
16
+ * validated by the compiler.
17
+ *
18
+ * @internal
19
+ */
20
+ /* v8 ignore start -- bootstrap; commands tested individually */
21
+ /**
22
+ * CLI application: reads argv from the Stdio service provided by NodeServices.
23
+ * (v4's `Command.run` takes only `version` — the name comes from the root command.)
24
+ */
25
+ const cli = Command.run(rootCommand, { version: "3.0.0" });
26
+ /**
27
+ * Bootstrap and run the `savvy` CLI application.
28
+ */
29
+ const main = () => {
30
+ NodeRuntime.runMain(cli.pipe(Effect.provide(AppLive)));
31
+ };
32
+ /* v8 ignore stop */
33
+
34
+ //#endregion
35
+ export { main };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/cli",
3
- "version": "2.11.3",
3
+ "version": "3.0.0",
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",
@@ -25,20 +25,25 @@
25
25
  "import": "./index.js",
26
26
  "default": "./index.js"
27
27
  },
28
+ "./main": {
29
+ "types": "./main.d.ts",
30
+ "import": "./main.js",
31
+ "default": "./main.js"
32
+ },
28
33
  "./package.json": "./package.json"
29
34
  },
30
35
  "bin": {
31
36
  "savvy": "bin/savvy.js"
32
37
  },
33
38
  "dependencies": {
34
- "@effect/platform-node": "4.0.0-rc.112",
35
- "@effected/commands": "^0.6.1",
36
- "@effected/git": "^0.14.0",
37
- "@effected/jsonc": "^0.9.0",
38
- "@effected/templates": "^0.5.0",
39
- "@effected/workspaces": "^0.20.3",
40
- "@effected/yaml": "^0.14.0",
41
- "@savvy-web/silk-effects": "7.5.2",
42
- "effect": "4.0.0-rc.112"
39
+ "@effect/platform-node": "4.0.0-rc.115",
40
+ "@effected/commands": "^0.7.0",
41
+ "@effected/git": "^0.15.0",
42
+ "@effected/jsonc": "^0.11.0",
43
+ "@effected/templates": "^0.6.0",
44
+ "@effected/workspaces": "^0.21.0",
45
+ "@effected/yaml": "^0.15.0",
46
+ "@savvy-web/silk-effects": "8.0.0",
47
+ "effect": "4.0.0-rc.115"
43
48
  }
44
49
  }