@savvy-web/cli 2.7.6 → 2.7.7

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
@@ -76,7 +76,7 @@ const rootCommand = Command.make("savvy").pipe(Command.withSubcommands([
76
76
  * CLI application: reads argv from the Stdio service provided by NodeServices.
77
77
  * (v4's `Command.run` takes only `version` — the name comes from the root command.)
78
78
  */
79
- const cli = Command.run(rootCommand, { version: "2.7.6" });
79
+ const cli = Command.run(rootCommand, { version: "2.7.7" });
80
80
  /**
81
81
  * Shared workspace services from `@effected/workspaces`, wired as a
82
82
  * self-contained unit and built ONCE (layers memoize by reference).
@@ -1,5 +1,6 @@
1
1
  import { BIOME_VERSION } from "./biome-version.js";
2
2
  import { ManagedSection } from "@effected/templates";
3
+ import { WorkspaceDiscovery } from "@effected/workspaces";
3
4
  import { BiomeSchemaSync, Lint, SavvyBaseSection, SavvyHooksSection, savvyBasePreamble, savvyHooksHygiene } from "@savvy-web/silk-effects";
4
5
  import { Effect, FileSystem } from "effect";
5
6
  import { dirname } from "node:path";
@@ -116,15 +117,63 @@ function writeMarkdownlintConfig(fs, preset, force) {
116
117
  });
117
118
  }
118
119
  /**
120
+ * Every directory a biome config may sit in: the workspace root plus each leaf
121
+ * package the workspace `packages:` patterns enumerate.
122
+ *
123
+ * @remarks
124
+ * `BiomeSchemaSync` scans a single directory per call, so a monorepo needs the
125
+ * roots enumerated up front. `listPackages` already includes the workspace root
126
+ * itself (the entry whose `relativePath` is `"."`), so no extra cwd pass is
127
+ * needed on the happy path.
128
+ *
129
+ * Discovery failing yields an EMPTY list, which the caller reads as "no roots
130
+ * to enumerate — scan `BiomeSchemaSync`'s own default directory instead". The
131
+ * cwd is deliberately not named here: resolving it is the sync service's job,
132
+ * and an ambient `process.cwd()` read in this function would be a second,
133
+ * divergent source of truth for the same directory.
134
+ *
135
+ * The two ways discovery fails mean different things and are reported differently:
136
+ *
137
+ * - `WorkspaceRootNotFoundError` is the normal shape of a plain single-package
138
+ * project (no `pnpm-workspace.yaml`, no `workspaces` field). Nothing to say.
139
+ * - Anything else (an unenumerable `packages:` pattern, a leaf manifest missing
140
+ * a `version`) means leaves exist but could not be enumerated, so the fallback
141
+ * silently under-scans. That earns a warning.
142
+ *
143
+ * @returns Effect yielding the directories to scan, never failing
144
+ */
145
+ function biomeConfigRoots() {
146
+ return Effect.gen(function* () {
147
+ return yield* (yield* WorkspaceDiscovery).listPackages().pipe(Effect.map((packages) => packages.map((pkg) => pkg.path)), Effect.catchTag("WorkspaceRootNotFoundError", () => Effect.succeed([])), Effect.catch((e) => Effect.as(Effect.log(`${WARNING} Only syncing biome $schema in the current directory: ${e.message}`), [])));
148
+ });
149
+ }
150
+ /**
119
151
  * Find and sync biome config `$schema` URLs to match the pinned {@link BIOME_VERSION}.
120
152
  *
153
+ * @remarks
154
+ * Covers every workspace root, not just the repository root: a leaf package
155
+ * carrying its own `biome.json`/`biome.jsonc` is updated in the same pass.
156
+ * A failure on one root is reported and skipped rather than aborting the rest,
157
+ * so one unreadable or malformed config cannot strand the others on a stale
158
+ * schema URL.
159
+ *
160
+ * Exported for the handler tests, which drive this seam over an in-memory
161
+ * volume — `runLintInit` itself chmods hook files through `node:fs/promises`,
162
+ * which no `FileSystem` double can intercept.
163
+ *
121
164
  * @returns Effect that syncs biome schemas and logs results
165
+ *
166
+ * @internal
122
167
  */
123
168
  function syncBiomeSchemas() {
124
169
  return Effect.gen(function* () {
125
- const result = yield* (yield* BiomeSchemaSync).sync(BIOME_VERSION);
126
- for (const configPath of result.current) yield* Effect.log(`${CHECK_MARK} ${configPath}: biome $schema up-to-date`);
127
- for (const configPath of result.updated) yield* Effect.log(`${CHECK_MARK} Updated $schema in ${configPath}`);
170
+ const syncer = yield* BiomeSchemaSync;
171
+ const roots = yield* biomeConfigRoots();
172
+ const passes = roots.length > 0 ? roots.map((cwd) => ({ cwd })) : [void 0];
173
+ for (const options of passes) yield* syncer.sync(BIOME_VERSION, options).pipe(Effect.flatMap((result) => Effect.gen(function* () {
174
+ for (const configPath of result.current) yield* Effect.log(`${CHECK_MARK} ${configPath}: biome $schema up-to-date`);
175
+ for (const configPath of result.updated) yield* Effect.log(`${CHECK_MARK} Updated $schema in ${configPath}`);
176
+ })), Effect.catchTag("BiomeSyncError", (e) => Effect.log(`${WARNING} Could not sync biome $schema: ${e.message}`)));
128
177
  });
129
178
  }
130
179
  /** Make a file executable. */
@@ -177,7 +226,7 @@ function runLintInit(opts) {
177
226
  yield* Effect.log(`${CHECK_MARK} Synced ${hookPath}`);
178
227
  }
179
228
  if (presetIncludesMarkdown(preset)) yield* writeMarkdownlintConfig(fs, preset, force);
180
- yield* syncBiomeSchemas().pipe(Effect.catchTag("BiomeSyncError", (e) => Effect.log(`${WARNING} Could not sync biome $schema: ${e.message}`)));
229
+ yield* syncBiomeSchemas();
181
230
  if ((yield* fs.exists(config)) && !force) yield* Effect.log(`${WARNING} ${config} already exists (use --force to overwrite)`);
182
231
  else {
183
232
  const configDir = dirname(config);
@@ -190,4 +239,4 @@ function runLintInit(opts) {
190
239
  }
191
240
 
192
241
  //#endregion
193
- export { runLintInit };
242
+ export { runLintInit, syncBiomeSchemas };
package/index.d.ts CHANGED
@@ -219,7 +219,7 @@ declare const initCommand: Command.Command<"init", {
219
219
  readonly commitConfig: string;
220
220
  readonly lintConfig: string;
221
221
  readonly lintPreset: "minimal" | "silk" | "standard";
222
- }, {}, Error | import("effect/PlatformError").PlatformError | import("@effected/templates").SectionFileError | import("@effected/templates").SectionParseError | import("@effected/templates").SectionRenderError, import("@savvy-web/silk-effects").BiomeSchemaSync | import("effect/FileSystem").FileSystem | import("@effected/git").Git | import("@effected/templates").ManagedSection | import("@effected/workspaces").WorkspaceRoot>;
222
+ }, {}, Error | import("effect/PlatformError").PlatformError | import("@effected/templates").SectionFileError | import("@effected/templates").SectionParseError | import("@effected/templates").SectionRenderError, import("@savvy-web/silk-effects").BiomeSchemaSync | import("effect/FileSystem").FileSystem | import("@effected/git").Git | import("@effected/templates").ManagedSection | import("@effected/workspaces").WorkspaceDiscovery | import("@effected/workspaces").WorkspaceRoot>;
223
223
  //#endregion
224
224
  //#region src/commands/lint/check.d.ts
225
225
  /**
@@ -253,7 +253,7 @@ declare function runLintInit(opts: {
253
253
  force: boolean;
254
254
  config: string;
255
255
  preset: "minimal" | "standard" | "silk";
256
- }): Effect.Effect<void, Error | SectionParseError | SectionRenderError | SectionFileError | PlatformError, ManagedSection | FileSystem.FileSystem | BiomeSchemaSync>;
256
+ }): Effect.Effect<void, Error | SectionParseError | SectionRenderError | SectionFileError | PlatformError, ManagedSection | FileSystem.FileSystem | BiomeSchemaSync | WorkspaceDiscovery>;
257
257
  //#endregion
258
258
  //#region src/commands/lint/index.d.ts
259
259
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/cli",
3
- "version": "2.7.6",
3
+ "version": "2.7.7",
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",