@savvy-web/cli 1.0.0 → 1.1.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
@@ -56,7 +56,7 @@ const rootCommand = Command.make("savvy").pipe(Command.withSubcommands([
56
56
  ]));
57
57
  const cli = Command.run(rootCommand, {
58
58
  name: "savvy",
59
- version: "1.0.0"
59
+ version: "1.1.1"
60
60
  });
61
61
  /**
62
62
  * Shared base layer: workspace services, the changeset config reader, and the
@@ -1,4 +1,3 @@
1
- import { Command, Options } from "@effect/cli";
2
1
  import { Changesets } from "@savvy-web/silk-effects";
3
2
  import { Data, Effect, Schema } from "effect";
4
3
  import { WorkspaceRoot } from "workspaces-effect";
@@ -33,8 +32,7 @@ import { applyEdits, modify, parse } from "jsonc-effect";
33
32
  *
34
33
  * @remarks
35
34
  * `runChangesetInit` backs the changeset step of the unified `savvy init`
36
- * orchestrator; there is no standalone `savvy changeset init` subcommand. The
37
- * exported `initCommand` is retained only as a direct test entry point.
35
+ * orchestrator; there is no standalone `savvy changeset init` subcommand.
38
36
  *
39
37
  * @example
40
38
  * ```bash
@@ -105,12 +103,6 @@ var InitError = class extends InitErrorBase {
105
103
  return `Init failed at ${this.step}: ${this.reason}`;
106
104
  }
107
105
  };
108
- /* v8 ignore start -- CLI option definitions; handler functions tested individually */
109
- const forceOption = Options.boolean("force").pipe(Options.withAlias("f"), Options.withDescription("Overwrite existing config files"));
110
- const quietOption = Options.boolean("quiet").pipe(Options.withAlias("q"), Options.withDescription("Silence warnings, always exit 0"));
111
- const skipMarkdownlintOption = Options.boolean("skip-markdownlint").pipe(Options.withDescription("Skip registering rules in base markdownlint config"));
112
- const checkOption = Options.boolean("check").pipe(Options.withDescription("Check configuration without writing (for postinstall scripts)"));
113
- /* v8 ignore stop */
114
106
  /**
115
107
  * Detect the `owner/repo` slug from the git remote origin URL.
116
108
  *
@@ -621,14 +613,6 @@ function runChangesetInit(opts) {
621
613
  }
622
614
  })));
623
615
  }
624
- /* v8 ignore start -- CLI orchestration; individual functions tested separately */
625
- const initCommand = Command.make("init", {
626
- force: forceOption,
627
- quiet: quietOption,
628
- skipMarkdownlint: skipMarkdownlintOption,
629
- check: checkOption
630
- }, (opts) => runChangesetInit(opts)).pipe(Command.withDescription("Bootstrap a repo for @savvy-web/changesets"));
631
- /* v8 ignore stop */
632
616
 
633
617
  //#endregion
634
618
  export { runChangesetInit };
@@ -1,6 +1,5 @@
1
- import { HUSKY_HOOK_PATH, POST_CHECKOUT_HOOK_PATH, POST_MERGE_HOOK_PATH } from "./constants.js";
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 { Command } from "@effect/cli";
4
3
  import { CheckResult, Commitlint, ManagedSection, SavvyBaseSection, SavvyHooksSection, VersioningStrategy, savvyBasePreamble, savvyHooksHygiene } from "@savvy-web/silk-effects";
5
4
  import { Effect } from "effect";
6
5
  import { WorkspaceDiscovery } from "workspaces-effect";
@@ -133,7 +132,11 @@ function runCommitCheck() {
133
132
  yield* Effect.log(`${BULLET} Commit section: not found (run 'savvy init' to add)`);
134
133
  }
135
134
  }
136
- for (const hookPath of [POST_CHECKOUT_HOOK_PATH, POST_MERGE_HOOK_PATH]) {
135
+ for (const hookPath of [
136
+ POST_CHECKOUT_HOOK_PATH,
137
+ POST_MERGE_HOOK_PATH,
138
+ POST_COMMIT_HOOK_PATH
139
+ ]) {
137
140
  if (!(yield* fs.exists(hookPath))) {
138
141
  sectionsHealthy = false;
139
142
  yield* Effect.log(`${BULLET} Hygiene hook: ${hookPath} not found (run 'savvy init' to add)`);
@@ -163,8 +166,6 @@ function runCommitCheck() {
163
166
  else yield* Effect.log(`${"✓"} Commitlint is configured correctly.`);
164
167
  });
165
168
  }
166
- /* v8 ignore next 3 -- CLI registration; handler tested via runCommitCheck */
167
- const checkCommand = Command.make("check", {}, () => runCommitCheck()).pipe(Command.withDescription("Check current commitlint configuration and detected settings"));
168
169
 
169
170
  //#endregion
170
171
  export { runCommitCheck };
@@ -5,6 +5,8 @@ const HUSKY_HOOK_PATH = ".husky/commit-msg";
5
5
  const POST_CHECKOUT_HOOK_PATH = ".husky/post-checkout";
6
6
  /** Husky post-merge hook path (savvy-hooks hygiene). */
7
7
  const POST_MERGE_HOOK_PATH = ".husky/post-merge";
8
+ /** Husky post-commit hook path (savvy-hooks hygiene). */
9
+ const POST_COMMIT_HOOK_PATH = ".husky/post-commit";
8
10
 
9
11
  //#endregion
10
- export { HUSKY_HOOK_PATH, POST_CHECKOUT_HOOK_PATH, POST_MERGE_HOOK_PATH };
12
+ export { HUSKY_HOOK_PATH, POST_CHECKOUT_HOOK_PATH, POST_COMMIT_HOOK_PATH, POST_MERGE_HOOK_PATH };
@@ -1,5 +1,4 @@
1
- import { HUSKY_HOOK_PATH, POST_CHECKOUT_HOOK_PATH, POST_MERGE_HOOK_PATH } from "./constants.js";
2
- import { Command, Options } from "@effect/cli";
1
+ import { HUSKY_HOOK_PATH, POST_CHECKOUT_HOOK_PATH, POST_COMMIT_HOOK_PATH, POST_MERGE_HOOK_PATH } from "./constants.js";
3
2
  import { ManagedSection, SavvyBaseSection, SavvyHooksSection, SectionDefinition, savvyBasePreamble, savvyHooksHygiene, savvyToolSection } from "@savvy-web/silk-effects";
4
3
  import { Effect } from "effect";
5
4
  import { dirname } from "node:path";
@@ -14,13 +13,11 @@ import { chmod } from "node:fs/promises";
14
13
  */
15
14
  /** Executable file permission mode. */
16
15
  const EXECUTABLE_MODE = 493;
17
- /** Default path for the commitlint config file. */
18
- const DEFAULT_CONFIG_PATH = "lib/configs/commitlint.config.ts";
19
16
  /** Section definition for the savvy-commit tool section (identity for read/check/remove). */
20
17
  const SECTION_DEF = SectionDefinition.make({ toolName: "savvy-commit" });
21
18
  /** Header written when creating a fresh commit-msg hook. */
22
19
  const COMMIT_MSG_HEADER = "#!/usr/bin/env sh\n# Commit-msg hook with savvy managed sections\n# Custom hooks can go above, below, or between the managed sections\n\n";
23
- /** Header written when creating a fresh hygiene hook (post-checkout / post-merge). */
20
+ /** Header written when creating a fresh hygiene hook (post-checkout / post-merge / post-commit). */
24
21
  const HYGIENE_HEADER = "#!/usr/bin/env sh\n# Managed by savvy-hooks\n# Custom hooks can go above or below the managed section\n\n";
25
22
  /**
26
23
  * Build the commitlint command run inside the savvy-commit tool section.
@@ -49,10 +46,6 @@ function ensureHookFile(path, header) {
49
46
  if (!(yield* fs.exists(path))) yield* fs.writeFileString(path, header);
50
47
  });
51
48
  }
52
- /* v8 ignore start -- CLI option definitions; handler tested individually */
53
- const forceOption = Options.boolean("force").pipe(Options.withAlias("f"), Options.withDescription("Overwrite the commit-msg hook and config file entirely (managed sections in post-checkout/post-merge are never force-reset)"), Options.withDefault(false));
54
- const configOption = Options.text("config").pipe(Options.withAlias("c"), Options.withDescription("Relative path for the commitlint config file (from repo root)"), Options.withDefault(DEFAULT_CONFIG_PATH));
55
- /* v8 ignore stop */
56
49
  /** Content for the commitlint config file. */
57
50
  const CONFIG_CONTENT = `import { CommitlintConfig } from "@savvy-web/silk/commitlint";
58
51
 
@@ -89,7 +82,11 @@ function runCommitInit(opts) {
89
82
  const commitResults = yield* ms.syncMany(HUSKY_HOOK_PATH, [SavvyBaseSection.block(savvyBasePreamble()), savvyCommitBlock(config)]);
90
83
  yield* makeExecutable(HUSKY_HOOK_PATH);
91
84
  yield* Effect.log(`${"✓"} ${force ? "Replaced" : "Synced"} ${HUSKY_HOOK_PATH} (${commitResults.map((r) => r._tag).join(", ")})`);
92
- for (const hookPath of [POST_CHECKOUT_HOOK_PATH, POST_MERGE_HOOK_PATH]) {
85
+ for (const hookPath of [
86
+ POST_CHECKOUT_HOOK_PATH,
87
+ POST_MERGE_HOOK_PATH,
88
+ POST_COMMIT_HOOK_PATH
89
+ ]) {
93
90
  yield* ensureHookFile(hookPath, HYGIENE_HEADER);
94
91
  yield* ms.sync(hookPath, SavvyHooksSection.block(savvyHooksHygiene()));
95
92
  yield* makeExecutable(hookPath);
@@ -105,23 +102,6 @@ function runCommitInit(opts) {
105
102
  yield* Effect.log("\nDone! Install @commitlint/cli if not already installed.");
106
103
  });
107
104
  }
108
- /**
109
- * Init command implementation.
110
- *
111
- * @remarks
112
- * Writes:
113
- * - `.husky/commit-msg` — savvy-base preamble + savvy-commit tool section.
114
- * - `.husky/post-checkout` and `.husky/post-merge` — savvy-hooks hygiene
115
- * (co-owned with `@savvy-web/lint-staged`; idempotent).
116
- * - The commitlint config file.
117
- *
118
- * Users may add custom commands above, below, or between the managed sections.
119
- */
120
- /* v8 ignore next 3 -- CLI registration; handler tested via runCommitInit */
121
- const initCommand = Command.make("init", {
122
- force: forceOption,
123
- config: configOption
124
- }, (opts) => runCommitInit(opts)).pipe(Command.withDescription("Initialize commitlint configuration and husky hooks"));
125
105
 
126
106
  //#endregion
127
107
  export { SECTION_DEF, runCommitInit, savvyCommitBlock };
@@ -1,4 +1,3 @@
1
- import { Command, Options } from "@effect/cli";
2
1
  import { CheckResult, ConfigDiscovery, Lint, ManagedSection, SavvyBaseSection, SavvyHooksSection, ToolDefinition, ToolDiscovery, savvyBasePreamble, savvyHooksHygiene } from "@savvy-web/silk-effects";
3
2
  import { Effect } from "effect";
4
3
  import { parse } from "jsonc-effect";
@@ -133,9 +132,6 @@ function checkBiomeSchemas() {
133
132
  };
134
133
  });
135
134
  }
136
- /* v8 ignore start -- CLI option definition; handler tested individually */
137
- const quietOption = Options.boolean("quiet").pipe(Options.withAlias("q"), Options.withDescription("Only output warnings (for postinstall usage)"), Options.withDefault(false));
138
- /* v8 ignore stop */
139
135
  /**
140
136
  * Run the lint check validation pipeline.
141
137
  *
@@ -191,7 +187,11 @@ function runLintCheck(opts) {
191
187
  warnings.push(`${WARNING} No husky pre-commit hook found.\n Run 'savvy init' to create it.`);
192
188
  }
193
189
  if (!foundConfig) warnings.push(`${WARNING} No lint-staged config file found.\n Run 'savvy init' to create one.`);
194
- const shellHookPaths = [Lint.POST_CHECKOUT_HOOK_PATH, Lint.POST_MERGE_HOOK_PATH];
190
+ const shellHookPaths = [
191
+ Lint.POST_CHECKOUT_HOOK_PATH,
192
+ Lint.POST_MERGE_HOOK_PATH,
193
+ Lint.POST_COMMIT_HOOK_PATH
194
+ ];
195
195
  const shellHookStatuses = [];
196
196
  for (const hookPath of shellHookPaths) {
197
197
  if (!(yield* fs.exists(hookPath))) {
@@ -299,8 +299,6 @@ function runLintCheck(opts) {
299
299
  else yield* Effect.log(`${CHECK_MARK} Lint-staged is configured correctly.`);
300
300
  });
301
301
  }
302
- /* v8 ignore next 3 -- CLI registration; handler tested via runLintCheck */
303
- const checkCommand = Command.make("check", { quiet: quietOption }, (opts) => runLintCheck(opts)).pipe(Command.withDescription("Check current lint-staged configuration and tool availability"));
304
302
 
305
303
  //#endregion
306
304
  export { runLintCheck };
@@ -1,4 +1,3 @@
1
- import { Command, Options } from "@effect/cli";
2
1
  import { BiomeSchemaSync, Lint, ManagedSection, SavvyBaseSection, SavvyHooksSection, savvyBasePreamble, savvyHooksHygiene } from "@savvy-web/silk-effects";
3
2
  import { Effect } from "effect";
4
3
  import { dirname } from "node:path";
@@ -26,7 +25,7 @@ const JSONC_FORMAT = {
26
25
  };
27
26
  /** Header written when creating a fresh pre-commit hook. */
28
27
  const PRE_COMMIT_HEADER = "#!/usr/bin/env sh\n# Pre-commit hook with savvy managed sections\n# Custom hooks can go above, below, or between the managed sections\n\n";
29
- /** Header written when creating a fresh hygiene hook (post-checkout / post-merge). */
28
+ /** Header written when creating a fresh hygiene hook (post-checkout / post-merge / post-commit). */
30
29
  const HYGIENE_HEADER = "#!/usr/bin/env sh\n# Managed by savvy-hooks\n# Custom hooks can go above or below the managed section\n\n";
31
30
  /**
32
31
  * Check if a preset includes the ShellScripts handler.
@@ -127,15 +126,6 @@ function syncBiomeSchemas() {
127
126
  for (const configPath of result.updated) yield* Effect.log(`${CHECK_MARK} Updated $schema in ${configPath}`);
128
127
  });
129
128
  }
130
- /* v8 ignore start -- CLI option definitions; handler tested individually */
131
- const forceOption = Options.boolean("force").pipe(Options.withAlias("f"), Options.withDescription("Overwrite the pre-commit hook and config file entirely (managed sections in post-checkout/post-merge are never force-reset)"), Options.withDefault(false));
132
- const configOption = Options.text("config").pipe(Options.withAlias("c"), Options.withDescription("Relative path for the lint-staged config file (from repo root)"), Options.withDefault(Lint.DEFAULT_CONFIG_PATH));
133
- const presetOption = Options.choice("preset", [
134
- "minimal",
135
- "standard",
136
- "silk"
137
- ]).pipe(Options.withAlias("p"), Options.withDescription("Preset to use: minimal, standard, or silk"), Options.withDefault("silk"));
138
- /* v8 ignore stop */
139
129
  /** Make a file executable. */
140
130
  function makeExecutable(path) {
141
131
  return Effect.tryPromise({
@@ -174,7 +164,11 @@ function runLintInit(opts) {
174
164
  const preCommitResults = yield* ms.syncMany(Lint.HUSKY_HOOK_PATH, [SavvyBaseSection.block(savvyBasePreamble()), Lint.savvyLintBlock(config)]);
175
165
  yield* makeExecutable(Lint.HUSKY_HOOK_PATH);
176
166
  yield* Effect.log(`${CHECK_MARK} ${force ? "Replaced" : "Synced"} ${Lint.HUSKY_HOOK_PATH} (${preCommitResults.map((r) => r._tag).join(", ")})`);
177
- if (presetIncludesShellScripts(preset)) for (const hookPath of [Lint.POST_CHECKOUT_HOOK_PATH, Lint.POST_MERGE_HOOK_PATH]) {
167
+ if (presetIncludesShellScripts(preset)) for (const hookPath of [
168
+ Lint.POST_CHECKOUT_HOOK_PATH,
169
+ Lint.POST_MERGE_HOOK_PATH,
170
+ Lint.POST_COMMIT_HOOK_PATH
171
+ ]) {
178
172
  yield* ensureHookFile(hookPath, HYGIENE_HEADER);
179
173
  yield* ms.remove(hookPath, Lint.LegacySavvyLintHygieneDef);
180
174
  yield* ms.sync(hookPath, SavvyHooksSection.block(savvyHooksHygiene()));
@@ -193,29 +187,6 @@ function runLintInit(opts) {
193
187
  yield* Effect.log("\nDone! Lint-staged is ready to use.");
194
188
  });
195
189
  }
196
- /**
197
- * Init command implementation.
198
- *
199
- * @remarks
200
- * Writes:
201
- * - `.husky/pre-commit` — `savvy-base` preamble + `savvy-lint` tool section, in order
202
- * (via `ManagedSection.syncMany`).
203
- * - `.husky/post-checkout` and `.husky/post-merge` — co-owned `savvy-hooks` hygiene
204
- * (idempotent, shared with `@savvy-web/commitlint`). Migrates legacy `SAVVY-LINT`
205
- * hygiene blocks by removing them before writing the new section.
206
- * - `lib/configs/.markdownlint-cli2.jsonc` config (when preset includes Markdown).
207
- * - lint-staged config at the specified path.
208
- *
209
- * Users may add custom commands above, below, or between the managed sections.
210
- * `--force` resets only the pre-commit hook and the config file; the hygiene sections
211
- * are always reconciled with `sync`.
212
- */
213
- /* v8 ignore next 3 -- CLI registration; handler tested via runLintInit */
214
- const initCommand = Command.make("init", {
215
- force: forceOption,
216
- config: configOption,
217
- preset: presetOption
218
- }, (opts) => runLintInit(opts)).pipe(Command.withDescription("Initialize lint-staged configuration and husky hooks"));
219
190
 
220
191
  //#endregion
221
192
  export { runLintInit };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@savvy-web/cli",
3
- "version": "1.0.0",
3
+ "version": "1.1.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",
@@ -31,9 +31,12 @@
31
31
  },
32
32
  "dependencies": {
33
33
  "@effect/cli": "^0.75.2",
34
+ "@effect/cluster": "^0.59.0",
34
35
  "@effect/platform": "^0.96.1",
35
36
  "@effect/platform-node": "^0.107.0",
36
- "@savvy-web/silk-effects": "1.1.0",
37
+ "@effect/rpc": "^0.75.1",
38
+ "@effect/sql": "^0.51.1",
39
+ "@savvy-web/silk-effects": "1.3.0",
37
40
  "effect": "^3.21.3",
38
41
  "jsonc-effect": "^0.2.1",
39
42
  "workspaces-effect": "^1.2.0",