arashi 1.24.1 → 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 +38 -28
- package/bin/arashi.js +47 -16
- package/bin/update-options.js +80 -0
- package/bin/update.js +89 -8
- package/package.json +13 -9
- package/schema/config.schema.json +4 -1
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|--
|
|
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,15 +166,17 @@ 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 --
|
|
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.
|
|
174
178
|
|
|
175
|
-
`--tab` is a CLI-only launch disposition for `create` and `switch`; it is never persisted. It requests a true terminal tab or documented managed-context equivalent, implies launch (and selection for `create`), overrides automatic parent-shell `cd`, and never degrades to a window or another launcher. Unsupported adapters fail with `TAB_DISPOSITION_UNSUPPORTED`; launch/preflight failures use `LAUNCH_FAILED`. Human-only tab launch is incompatible with `--json`.
|
|
179
|
+
`--tab` is a CLI-only launch disposition for `create` and `switch`; it is never persisted. It requests a true terminal tab or documented managed-context equivalent, implies launch (and selection for `create`), overrides automatic parent-shell `cd`, and never degrades to a window or another launcher. For switch, it bypasses configured `sesh` or `herdr` launch defaults; for create, it bypasses configured generic or editor-scoped launch defaults. An explicit launcher selector remains authoritative. Unsupported adapters fail with `TAB_DISPOSITION_UNSUPPORTED`; launch/preflight failures use `LAUNCH_FAILED`. Human-only tab launch is incompatible with `--json`.
|
|
176
180
|
|
|
177
181
|
### Managed Git ignore rules
|
|
178
182
|
|
|
@@ -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
|
|
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
|
-
|
|
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
|
-
|
|
220
|
-
arashi shell init
|
|
221
|
-
arashi
|
|
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.
|
|
@@ -229,24 +247,16 @@ For automated installs, set `ARASHI_SHELL_INTEGRATION=yes` to enable it without
|
|
|
229
247
|
|
|
230
248
|
## Hooks
|
|
231
249
|
|
|
232
|
-
Arashi can run lifecycle hooks during `arashi create` and `arashi remove`.
|
|
233
|
-
|
|
234
|
-
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
- `pre-remove.sh`
|
|
238
|
-
- `post-remove.sh`
|
|
239
|
-
- Repository-specific hooks:
|
|
240
|
-
- `pre-create.<repo>.sh`
|
|
241
|
-
- `post-create.<repo>.sh`
|
|
242
|
-
- Scoped remove hooks:
|
|
243
|
-
- repository scope: `repos/<repo>/.arashi/hooks/pre-remove.sh` and `post-remove.sh`
|
|
244
|
-
- global shared: `~/.arashi/hooks/pre-remove.sh` and `post-remove.sh`
|
|
245
|
-
- global targeted: `~/.arashi/hooks/<repo>/pre-remove.sh` and `post-remove.sh`
|
|
246
|
-
|
|
247
|
-
For `arashi remove`, hook execution order is: repository scope -> workspace-root scope -> global targeted scope -> global shared scope.
|
|
250
|
+
Arashi can run lifecycle hooks during `arashi create` and `arashi remove`. Configured create uses
|
|
251
|
+
workspace `pre-create`/`post-create` plus repository-specific `pre-create.<repo>` and
|
|
252
|
+
`post-create.<repo>` files. Configured remove evaluates repository, workspace, user-global targeted,
|
|
253
|
+
and user-global shared scopes once per target repository. Standalone mode activates only targeted
|
|
254
|
+
and shared user-global hooks.
|
|
248
255
|
|
|
249
|
-
|
|
256
|
+
POSIX uses executable `.sh` files. Windows uses one case-insensitive `.ps1`, `.cmd`, or `.bat`
|
|
257
|
+
candidate per location and never runs `.sh` implicitly. Hook failures participate in create rollback
|
|
258
|
+
or remove finalization, all hooks default to a 300000 ms timeout, and JSON results expose the ordered
|
|
259
|
+
ledger at `data.hookOutcomes` on success or `error.details.hookOutcomes` on failure.
|
|
250
260
|
|
|
251
261
|
See [`docs/hooks.md`](./docs/hooks.md) for hook behavior, environment variables, and examples.
|
|
252
262
|
|
|
@@ -345,13 +355,13 @@ Example config header:
|
|
|
345
355
|
|
|
346
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`.
|
|
347
357
|
|
|
348
|
-
Explicit launcher flags take precedence over `--cd` / `--
|
|
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.
|
|
349
359
|
|
|
350
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.
|
|
351
361
|
|
|
352
362
|
Use `defaults.create` for terminal `arashi create` behavior. Use `defaults.editors.<host>.create` for editor-specific overrides such as VS Code extension create flows. Supported hosts are `vscode`, `cursor`, and `kiro`. Each scope has one canonical `launch` choice: `none` | `auto` | `sesh` | `herdr`. `switch` stays independent, while any enabled launch implies switch handling for the newly created primary worktree.
|
|
353
363
|
|
|
354
|
-
Create precedence is: reject `--sesh` plus `--herdr`; then explicit `--sesh` / `--herdr`; `--launch`; `--no-launch`; the matching configured scope; and built-in `none`. An editor-hosted invocation does not fall back to terminal or another editor scope.
|
|
364
|
+
Create precedence is: reject `--sesh` plus `--herdr`; then explicit `--sesh` / `--herdr`; `--tab` or `--launch`; `--no-launch`; the matching configured scope; and built-in `none`. `--tab` bypasses the matching configured scope unless an explicit launcher selector is present. An editor-hosted invocation does not fall back to terminal or another editor scope.
|
|
355
365
|
|
|
356
366
|
Legacy create booleans plus `launchMode` / `launch_mode` remain readable for a bounded compatibility window. Accepted combinations warn on stderr with the exact canonical replacement and do not rewrite the file. Disabled launch plus a launcher, conflicting aliases, and conflicting canonical/legacy choices are rejected before workspace mutation.
|
|
357
367
|
|
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
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
112
|
-
|
|
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
|
-
|
|
156
|
-
|
|
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
|
|
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
|
-
|
|
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.
|
|
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
|
-
"
|
|
57
|
-
"build:
|
|
58
|
-
"build:
|
|
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@
|
|
105
|
+
"packageManager": "pnpm@11.20.0"
|
|
102
106
|
}
|
|
@@ -34,7 +34,10 @@
|
|
|
34
34
|
"description": "Optional workspace-level hooks settings",
|
|
35
35
|
"properties": {
|
|
36
36
|
"timeout": {
|
|
37
|
-
"description": "
|
|
37
|
+
"description": "Lifecycle-hook timeout in milliseconds (default: 300000)",
|
|
38
|
+
"maximum": 2147483647,
|
|
39
|
+
"minimum": 1,
|
|
40
|
+
"multipleOf": 1,
|
|
38
41
|
"type": "number"
|
|
39
42
|
}
|
|
40
43
|
},
|