arashi 1.25.0 → 1.26.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/README.md CHANGED
@@ -114,9 +114,10 @@ Windows (PowerShell and Git Bash):
114
114
 
115
115
  Windows manual installation requires `arashi-windows-x64.exe`, `arashi`, `arashi.ps1`, `arashi.bat`, and `arashi-checksums.txt` from the same release. Verify all four payload files, rename the executable to `arashi.bin.exe`, and keep the payload together on PATH.
116
116
 
117
- You can also build from source for local development:
117
+ You can also build from source for local development. Development is pinned to Node.js 24.18.0, which satisfies pnpm 11's Node.js 22.13 minimum, and pnpm 11.20.0; nvm users can select the repository's development runtime with `nvm use`.
118
118
 
119
119
  ```bash
120
+ nvm use # Optional; reads .nvmrc
120
121
  pnpm install
121
122
  pnpm run build
122
123
  ```
@@ -136,7 +137,8 @@ Arashi currently provides these commands:
136
137
  - `arashi status`
137
138
  - `arashi remove <branch|path>`
138
139
  - `arashi prune [--dry-run]` - clean stale Git worktree metadata
139
- - `arashi switch [filter] [--repos|--all] [--tab] [--cd|--no-cd] [--tmux|--sesh|--herdr] [--no-default-launch]`
140
+ - `arashi switch [filter] [--repos|--all] [--tab] [--cd|--launch] [--tmux|--sesh|--herdr] [--ignore-configured-launcher]`
141
+ - `arashi completion <bash|zsh|fish>`
140
142
  - `arashi shell init <bash|zsh|fish>`
141
143
  - `arashi shell install`
142
144
  - `arashi pull`
@@ -164,10 +166,12 @@ arashi switch --repos feature-auth-refresh # child repo worktrees in current wo
164
166
  arashi switch --all feature-auth-refresh # all repos
165
167
  arashi switch --repos docs # repo-name matching in child repos
166
168
  arashi switch --cd feature-auth-refresh # parent-shell cd when shell integration is active
169
+ arashi switch --launch feature-auth-refresh # force launch while preserving a configured launcher
167
170
  arashi switch --tmux feature-auth-refresh # force a new plain tmux window
168
171
  arashi switch --herdr feature-auth-refresh # open or focus a persistent Herdr workspace
169
172
  arashi switch --tab feature-auth-refresh # request a true tab or managed equivalent
170
- arashi switch --no-default-launch # bypass configured sesh/Herdr mode once
173
+ arashi switch --ignore-configured-launcher # bypass configured sesh/Herdr mode once
174
+ arashi switch --launch --ignore-configured-launcher # force generic automatic launch
171
175
  ```
172
176
 
173
177
  Explicit `--tmux` is a per-invocation launcher override for `create` and `switch`; it is not a persisted configuration mode. It requires an active tmux context whose `TMUX` value is non-empty after trimming, uses the selected worktree path as one argv-safe `tmux new-window -c` argument, and does not fall back to another launcher when the prerequisite or launch fails. On `create`, it implies both launch and switch, while validation failures occur before worktree mutation.
@@ -205,7 +209,7 @@ For contributors working on Arashi itself, the project planning workflow in the
205
209
 
206
210
  ## Shell Integration
207
211
 
208
- Use shell integration when you want `arashi switch` to change the current shell directory instead of only opening a new terminal or editor context.
212
+ Use shell integration for parent-shell directory switching and native tab completion. Bash, Zsh, and Fish are supported.
209
213
 
210
214
  The official curl installer can offer this automatically. If you skip it or use npm, install it for the active shell with:
211
215
 
@@ -213,14 +217,28 @@ The official curl installer can offer this automatically. If you skip it or use
213
217
  arashi shell install
214
218
  ```
215
219
 
216
- Or print wrapper code for manual setup:
220
+ `arashi shell install` adds a managed pair to the active shell startup file: the parent-shell wrapper first and completion activation second. Repeating the command is idempotent and upgrades older wrapper-only blocks.
221
+
222
+ For manual setup, activate the wrapper and completion independently:
217
223
 
218
224
  ```bash
219
- arashi shell init bash
220
- arashi shell init zsh
221
- arashi shell init fish
225
+ # Bash
226
+ eval "$(command arashi shell init bash)"
227
+ source <(command arashi completion bash)
228
+
229
+ # Zsh
230
+ eval "$(command arashi shell init zsh)"
231
+ source <(command arashi completion zsh)
232
+
233
+ # Fish
234
+ command arashi shell init fish | source
235
+ command arashi completion fish | source
222
236
  ```
223
237
 
238
+ `arashi shell init <shell>` remains wrapper-only. `arashi completion <shell>` emits only deterministic sourceable shell code, including on npm first use when the platform binary must be installed.
239
+
240
+ Completion covers commands, aliases, options, finite choices, conflicts, and positional boundaries. In an Arashi workspace it also resolves configured repository and group selector segments, switch/remove worktrees and paths, and move source/target references. Dynamic lookup is local and read-only, silent on unavailable or broken metadata, and limited to a 200 ms whole-query budget; it performs no network requests, hooks, prompts, workspace mutations, or child operations. Static completion remains available outside a workspace.
241
+
224
242
  Once installed, you can use `arashi switch --cd <filter>` for one-off parent-shell switching or set `.arashi/config.json` `defaults.switch.mode` to `"cd"` or contextual `"auto"`. The canonical modes are `auto` | `cd` | `launch` | `sesh` | `herdr`.
225
243
 
226
244
  If shell integration is inactive, `arashi switch --cd` warns and skips launch fallback for that invocation.
@@ -337,7 +355,7 @@ Example config header:
337
355
 
338
356
  `defaults.switch.mode` accepts `auto` | `cd` | `launch` | `sesh` | `herdr`. Contextual `auto` checks strict managed contexts in the order tmux → Herdr → cmux → integrated IDE → managed Kitty, then uses parent-shell `cd` when shell integration is active, and otherwise follows terminal/platform launch fallback. An absent mode preserves the built-in automatic `launch` behavior rather than newly preferring `cd`.
339
357
 
340
- Explicit launcher flags take precedence over `--cd` / `--no-cd`, which take precedence over the configured mode and automatic context detection. `--no-default-launch` bypasses only configured `sesh` or `herdr`. `--herdr` remains available on both `create` and `switch`; Herdr launch uses `herdr worktree open` with the Git-resolved non-bare main checkout and selected worktree.
358
+ Explicit launcher flags take precedence over `--cd` / `--launch`, which take precedence over the configured mode and automatic context detection. `--launch` forces launch while preserving a configured launcher. `--ignore-configured-launcher` bypasses only configured `sesh` or `herdr`; combine both canonical flags to force generic automatic launch. `--herdr` remains available on both `create` and `switch`; Herdr launch uses `herdr worktree open` with the Git-resolved non-bare main checkout and selected worktree.
341
359
 
342
360
  Legacy switch-only `launchMode` and `launch_mode` fields remain readable for a bounded compatibility window. Arashi warns with the exact replacement `defaults.switch.mode` on stderr; migrate promptly. Ambiguous `cd` plus an explicit legacy launcher and conflicting legacy aliases are rejected.
343
361
 
package/bin/arashi.js CHANGED
@@ -1,10 +1,11 @@
1
1
  #!/usr/bin/env node
2
2
  import { spawn } from "node:child_process";
3
- import { existsSync } from "node:fs";
4
- import { dirname, join } from "node:path";
5
- import { fileURLToPath, pathToFileURL } from "node:url";
3
+ import { existsSync, realpathSync } from "node:fs";
4
+ import { dirname, join, posix, win32 } from "node:path";
5
+ import { fileURLToPath } from "node:url";
6
6
  import { formatInstallError, getPlatformInfo, installBinary } from "./install-binary.js";
7
7
  import { runNpmManagedUpdate } from "./update.js";
8
+ import { stringifyWrapperJsonEnvelope } from "./update-options.js";
8
9
 
9
10
  const __filename = fileURLToPath(import.meta.url);
10
11
  const __dirname = dirname(__filename);
@@ -65,7 +66,10 @@ export async function ensureInstalled(options = {}) {
65
66
  const rootDir = options.rootDir ?? join(binDir, "..");
66
67
  const platform = options.platform ?? currentPlatform;
67
68
  const arch = options.arch ?? currentArch;
68
- const log = options.log ?? console.log;
69
+ const sourceOutputFirstUse =
70
+ options.argv?.[0] === "completion" ||
71
+ (options.argv?.[0] === "shell" && options.argv?.[1] === "init");
72
+ const log = sourceOutputFirstUse ? () => {} : (options.log ?? console.log);
69
73
  const installBinaryImpl = options.installBinaryImpl ?? installBinary;
70
74
 
71
75
  if (hasRunnableBinary({ arch, binDir, existsSyncImpl: options.existsSyncImpl, platform })) {
@@ -84,12 +88,35 @@ export function isExplicitUpdateCommand(argv) {
84
88
  return argv[0] === "update";
85
89
  }
86
90
 
87
- async function runExplicitInstall(options) {
91
+ async function runExplicitInstall(argv, options) {
88
92
  const binDir = options.binDir ?? __dirname;
89
93
  const rootDir = options.rootDir ?? join(binDir, "..");
90
94
  const installBinaryImpl = options.installBinaryImpl ?? installBinary;
95
+ const json = argv.includes("--json") || argv.includes("-j");
96
+ const log = options.log ?? console.log;
91
97
 
92
- await installBinaryImpl({ ...options, binDir, rootDir });
98
+ try {
99
+ const result = await installBinaryImpl({
100
+ ...options,
101
+ binDir,
102
+ log: json ? () => {} : options.log,
103
+ rootDir,
104
+ });
105
+ if (json) log(stringifyWrapperJsonEnvelope("install", { data: result, ok: true }));
106
+ return 0;
107
+ } catch (error) {
108
+ if (!json) throw error;
109
+ log(
110
+ stringifyWrapperJsonEnvelope("install", {
111
+ error: {
112
+ code: "INSTALL_FAILED",
113
+ message: error instanceof Error ? error.message : String(error),
114
+ },
115
+ ok: false,
116
+ }),
117
+ );
118
+ return 1;
119
+ }
93
120
  }
94
121
 
95
122
  function spawnArashi(argv, options = {}) {
@@ -97,7 +124,7 @@ function spawnArashi(argv, options = {}) {
97
124
  const platform = options.platform ?? currentPlatform;
98
125
  const windows = platform === "win32";
99
126
  const spawnImpl = options.spawnImpl ?? spawn;
100
- const wrapperPath = join(binDir, getWrapperName(platform));
127
+ const wrapperPath = (windows ? win32 : posix).join(binDir, getWrapperName(platform));
101
128
  const binaryPath = resolveBinaryPath({
102
129
  arch: options.arch,
103
130
  binDir,
@@ -108,10 +135,10 @@ function spawnArashi(argv, options = {}) {
108
135
  return new Promise((resolve) => {
109
136
  const child = windows
110
137
  ? spawnImpl(binaryPath, argv, {
111
- stdio: "inherit",
112
- windowsHide: false,
113
- })
114
- : spawnImpl(wrapperPath, argv, { stdio: "inherit" });
138
+ stdio: "inherit",
139
+ windowsHide: false,
140
+ })
141
+ : spawnImpl("/bin/bash", [wrapperPath, ...argv], { stdio: "inherit" });
115
142
 
116
143
  child.on("exit", (code, signal) => {
117
144
  if (typeof code === "number") {
@@ -135,15 +162,14 @@ export async function runEntrypoint(argv = process.argv.slice(2), options = {})
135
162
 
136
163
  try {
137
164
  if (isExplicitInstallCommand(argv)) {
138
- await runExplicitInstall(options);
139
- return 0;
165
+ return await runExplicitInstall(argv.slice(1), options);
140
166
  }
141
167
 
142
168
  if (isExplicitUpdateCommand(argv)) {
143
169
  return runNpmManagedUpdate(argv.slice(1), options);
144
170
  }
145
171
 
146
- await ensureInstalled(options);
172
+ await ensureInstalled({ ...options, argv });
147
173
  } catch (error) {
148
174
  errorLog(formatInstallError(error));
149
175
  return 1;
@@ -152,7 +178,12 @@ export async function runEntrypoint(argv = process.argv.slice(2), options = {})
152
178
  return spawnArashi(argv, options);
153
179
  }
154
180
 
155
- const invokedPath = process.argv[1] ? pathToFileURL(process.argv[1]).href : "";
156
- if (import.meta.url === invokedPath) {
181
+ let invokedDirectly = false;
182
+ try {
183
+ invokedDirectly = Boolean(process.argv[1]) && realpathSync(process.argv[1]) === realpathSync(__filename);
184
+ } catch {
185
+ // A missing or inaccessible argv path cannot be a direct invocation of this module.
186
+ }
187
+ if (invokedDirectly) {
157
188
  process.exitCode = await runEntrypoint();
158
189
  }
@@ -0,0 +1,80 @@
1
+ export const UPDATE_INSPECTION_CONFLICT_CODE = "UPDATE_INSPECTION_CONFLICT";
2
+ export const UPDATE_INSPECTION_CONFLICT_MESSAGE =
3
+ "--check cannot be combined with --dry-run; choose one inspection mode.";
4
+ export const UPDATE_UNKNOWN_OPTION_CODE = "UPDATE_UNKNOWN_OPTION";
5
+
6
+ export class UpdateUnknownOptionError extends Error {
7
+ constructor(options) {
8
+ const suffix = options.length === 1 ? "" : "s";
9
+ super(`Unknown option${suffix}: ${options.join(", ")}`);
10
+ this.name = "UpdateUnknownOptionError";
11
+ this.code = UPDATE_UNKNOWN_OPTION_CODE;
12
+ this.details = { options };
13
+ }
14
+ }
15
+
16
+ export class UpdateInspectionConflictError extends Error {
17
+ constructor() {
18
+ super(UPDATE_INSPECTION_CONFLICT_MESSAGE);
19
+ this.name = "UpdateInspectionConflictError";
20
+ this.code = UPDATE_INSPECTION_CONFLICT_CODE;
21
+ this.details = { options: ["--check", "--dry-run"] };
22
+ }
23
+ }
24
+
25
+ export function parseUpdateArgs(argv = []) {
26
+ const delimiter = argv.indexOf("--");
27
+ const optionArgv = delimiter === -1 ? argv : argv.slice(0, delimiter);
28
+ const recognizedLongs = new Set(["--check", "--dry-run", "--json", "--yes"]);
29
+ const recognizedShorts = new Set(["j", "n", "y"]);
30
+ const groupedShorts = new Set();
31
+ const unknownOptions = [];
32
+ for (const token of optionArgv) {
33
+ if (recognizedLongs.has(token)) continue;
34
+ if (token.startsWith("--")) {
35
+ unknownOptions.push(token);
36
+ continue;
37
+ }
38
+ if (!/^-[^-]/.test(token)) continue;
39
+ const characters = token.slice(1).split("");
40
+ if (!characters.every((character) => recognizedShorts.has(character))) {
41
+ unknownOptions.push(token);
42
+ continue;
43
+ }
44
+ for (const character of characters) groupedShorts.add(character);
45
+ }
46
+ const has = (long, short) => optionArgv.includes(long) || optionArgv.includes(short) || groupedShorts.has(short.slice(1));
47
+ return {
48
+ check: optionArgv.includes("--check"),
49
+ dryRun: has("--dry-run", "-n"),
50
+ json: has("--json", "-j"),
51
+ unknownOptions,
52
+ yes: has("--yes", "-y"),
53
+ };
54
+ }
55
+
56
+ export function assertValidUpdateInspectionOptions(options) {
57
+ if (options.unknownOptions?.length > 0) {
58
+ throw new UpdateUnknownOptionError(options.unknownOptions);
59
+ }
60
+ if (options.check && options.dryRun) {
61
+ throw new UpdateInspectionConflictError();
62
+ }
63
+ }
64
+
65
+ export function createWrapperJsonEnvelope(command, result) {
66
+ if (result.ok) {
67
+ return { command, data: result.data ?? {}, ok: true, schemaVersion: 1, warnings: [] };
68
+ }
69
+ return {
70
+ command,
71
+ error: result.error,
72
+ ok: false,
73
+ schemaVersion: 1,
74
+ warnings: [],
75
+ };
76
+ }
77
+
78
+ export function stringifyWrapperJsonEnvelope(command, result) {
79
+ return JSON.stringify(createWrapperJsonEnvelope(command, result), null, 2);
80
+ }
package/bin/update.js CHANGED
@@ -5,16 +5,15 @@ import { createInterface } from "node:readline/promises";
5
5
  import { fileURLToPath } from "node:url";
6
6
  import { getPlatformInfo, installBinary, MANUAL_INSTALL_URL, PACKAGE_NAME } from "./install-binary.js";
7
7
  import { prepareSpawnCommand } from "./prepare-spawn-command.js";
8
+ import {
9
+ assertValidUpdateInspectionOptions,
10
+ parseUpdateArgs,
11
+ stringifyWrapperJsonEnvelope,
12
+ } from "./update-options.js";
8
13
 
9
14
  export const UPDATE_COMMAND_DESCRIPTION = "Check for and apply Arashi updates";
10
15
 
11
- export function parseUpdateArgs(argv = []) {
12
- return {
13
- check: argv.includes("--check"),
14
- dryRun: argv.includes("--dry-run"),
15
- yes: argv.includes("--yes") || argv.includes("-y"),
16
- };
17
- }
16
+ export { parseUpdateArgs } from "./update-options.js";
18
17
 
19
18
  function normalizeVersion(version) {
20
19
  return String(version ?? "").trim().replace(/^v/, "").split("+")[0];
@@ -247,7 +246,7 @@ function defaultRootDir(binDir) {
247
246
  return join(binDir ?? fileURLToPath(new URL(".", import.meta.url)), "..");
248
247
  }
249
248
 
250
- export async function runNpmManagedUpdate(argv = [], options = {}) {
249
+ async function runNpmManagedUpdateCore(argv = [], options = {}) {
251
250
  const flags = parseUpdateArgs(argv);
252
251
  const log = options.log ?? console.log;
253
252
  const errorLog = options.error ?? console.error;
@@ -305,6 +304,11 @@ export async function runNpmManagedUpdate(argv = [], options = {}) {
305
304
  return 0;
306
305
  }
307
306
 
307
+ if (flags.json) {
308
+ log("JSON inspection: no changes made.");
309
+ return 0;
310
+ }
311
+
308
312
  if (!(await confirmUpdate({ promptImpl: options.promptImpl, yes: flags.yes }))) {
309
313
  errorLog("Update skipped. Rerun with --yes for non-interactive updates or use --dry-run to inspect the plan.");
310
314
  return 1;
@@ -384,12 +388,89 @@ export async function runNpmManagedUpdate(argv = [], options = {}) {
384
388
  }
385
389
  }
386
390
 
391
+ export async function runNpmManagedUpdate(argv = [], options = {}) {
392
+ const flags = parseUpdateArgs(argv);
393
+ const log = options.log ?? console.log;
394
+ const errorLog = options.error ?? console.error;
395
+
396
+ try {
397
+ assertValidUpdateInspectionOptions(flags);
398
+ } catch (error) {
399
+ if (flags.json) {
400
+ log(
401
+ stringifyWrapperJsonEnvelope("update", {
402
+ error: { code: error.code, details: error.details, message: error.message },
403
+ ok: false,
404
+ }),
405
+ );
406
+ } else {
407
+ errorLog(error.message);
408
+ }
409
+ return 2;
410
+ }
411
+
412
+ if (flags.json && flags.yes) {
413
+ log(
414
+ stringifyWrapperJsonEnvelope("update", {
415
+ error: {
416
+ code: "JSON_UNSUPPORTED_FOR_MODE",
417
+ details: { mode: "installer-apply" },
418
+ message: "JSON output is not supported for installer-apply.",
419
+ },
420
+ ok: false,
421
+ }),
422
+ );
423
+ return 1;
424
+ }
425
+
426
+ if (!flags.json) return runNpmManagedUpdateCore(argv, options);
427
+
428
+ const messages = [];
429
+ const errors = [];
430
+ const exitCode = await runNpmManagedUpdateCore(argv, {
431
+ ...options,
432
+ error: (message) => errors.push(message),
433
+ log: (message) => messages.push(message),
434
+ });
435
+ if (exitCode === 0) {
436
+ log(stringifyWrapperJsonEnvelope("update", { data: { messages }, ok: true }));
437
+ } else {
438
+ log(
439
+ stringifyWrapperJsonEnvelope("update", {
440
+ error: {
441
+ code: "UPDATE_FAILED",
442
+ details: { messages },
443
+ message: errors.join("\n") || "Update failed.",
444
+ },
445
+ ok: false,
446
+ }),
447
+ );
448
+ }
449
+ return exitCode;
450
+ }
451
+
387
452
  export async function runDirectBinaryUpdate(argv = [], options = {}) {
388
453
  const flags = parseUpdateArgs(argv);
389
454
  const log = options.log ?? console.log;
390
455
  const errorLog = options.error ?? console.error;
391
456
  const currentVersion = options.currentVersion;
392
457
 
458
+ try {
459
+ assertValidUpdateInspectionOptions(flags);
460
+ } catch (error) {
461
+ if (flags.json) {
462
+ log(
463
+ stringifyWrapperJsonEnvelope("update", {
464
+ error: { code: error.code, details: error.details, message: error.message },
465
+ ok: false,
466
+ }),
467
+ );
468
+ } else {
469
+ errorLog(error.message);
470
+ }
471
+ return 2;
472
+ }
473
+
393
474
  let release;
394
475
  try {
395
476
  release = options.latestRelease ?? (await fetchLatestGitHubRelease(options));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "arashi",
3
- "version": "1.25.0",
3
+ "version": "1.26.1",
4
4
  "description": "Git worktree manager for meta-repositories - The eye of the storm for your development workflow",
5
5
  "keywords": [
6
6
  "cli",
@@ -32,6 +32,7 @@
32
32
  "bin/install-binary.js",
33
33
  "bin/prepare-spawn-command.js",
34
34
  "bin/update.js",
35
+ "bin/update-options.js",
35
36
  "bin/arashi.bat",
36
37
  "bin/arashi.ps1",
37
38
  "schema/config.schema.json",
@@ -50,12 +51,13 @@
50
51
  "type": "module",
51
52
  "scripts": {
52
53
  "dev": "node src/index.ts",
53
- "build": "bun build src/index.ts --compile --minify --outfile bin/arashi.bin",
54
- "build:debug": "bun build src/index.ts --compile --sourcemap --outfile bin/arashi.bin",
55
- "build:all": "pnpm run build:mac && pnpm run build:linux && pnpm run build:windows",
56
- "build:mac": "bun build src/index.ts --compile --target=bun-darwin-arm64 --outfile bin/arashi-macos-arm64",
57
- "build:linux": "bun build src/index.ts --compile --target=bun-linux-x64 --outfile bin/arashi-linux-x64",
58
- "build:windows": "bun build src/index.ts --compile --target=bun-windows-x64 --outfile bin/arashi-windows-x64.exe",
54
+ "build": "pnpm run completion:check && bun build src/index.ts --compile --minify --outfile bin/arashi.bin",
55
+ "build:debug": "pnpm run completion:check && bun build src/index.ts --compile --sourcemap --outfile bin/arashi.bin",
56
+ "build:all": "pnpm run completion:check && pnpm run build:mac && pnpm run build:linux && pnpm run build:windows",
57
+ "buildAll": "pnpm run completion:check && pnpm run build:all",
58
+ "build:mac": "pnpm run completion:check && bun build src/index.ts --compile --target=bun-darwin-arm64 --outfile bin/arashi-macos-arm64",
59
+ "build:linux": "pnpm run completion:check && bun build src/index.ts --compile --target=bun-linux-x64 --outfile bin/arashi-linux-x64",
60
+ "build:windows": "pnpm run completion:check && bun build src/index.ts --compile --target=bun-windows-x64 --outfile bin/arashi-windows-x64.exe",
59
61
  "test": "node scripts/test/run-tests.ts",
60
62
  "typecheck": "tsc --noEmit",
61
63
  "lint": "oxlint -D no-explicit-any .",
@@ -66,10 +68,12 @@
66
68
  "schema:check": "pnpm run schema:publish && git diff --exit-code -- schema/config.schema.json",
67
69
  "contract:generate": "node scripts/contracts/cli-commands.ts",
68
70
  "contract:check": "node scripts/contracts/cli-commands.ts --check",
71
+ "completion:generate": "node scripts/completions.ts",
72
+ "completion:check": "node scripts/completions.ts --check",
69
73
  "format": "oxfmt --config .oxfmtrc.json --write .",
70
74
  "format:check": "oxfmt --config .oxfmtrc.json --check .",
71
75
  "quality:changed": "node scripts/quality/changed-files-quality.ts",
72
- "prepublishOnly": "pnpm run schema:publish",
76
+ "prepublishOnly": "pnpm run completion:check && pnpm run schema:publish",
73
77
  "prepare": "husky"
74
78
  },
75
79
  "devDependencies": {
@@ -98,5 +102,5 @@
98
102
  "engines": {
99
103
  "node": ">=18.0.0"
100
104
  },
101
- "packageManager": "pnpm@10.15.1"
105
+ "packageManager": "pnpm@11.20.0"
102
106
  }