@solaqua/gji 0.4.1 → 0.6.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.
- package/README.md +37 -3
- package/dist/back.d.ts +13 -0
- package/dist/back.js +74 -0
- package/dist/cli.js +107 -1
- package/dist/config.js +1 -0
- package/dist/editor.d.ts +8 -0
- package/dist/editor.js +17 -0
- package/dist/gji-bundle.mjs +2839 -2124
- package/dist/go.js +24 -4
- package/dist/history-command.d.ts +7 -0
- package/dist/history-command.js +15 -0
- package/dist/history.d.ts +9 -0
- package/dist/history.js +46 -0
- package/dist/hooks.d.ts +5 -4
- package/dist/hooks.js +61 -9
- package/dist/init.js +32 -10
- package/dist/install-prompt.js +9 -1
- package/dist/new.d.ts +3 -0
- package/dist/new.js +36 -1
- package/dist/open.d.ts +20 -0
- package/dist/open.js +155 -0
- package/dist/pr.js +3 -0
- package/dist/repo-registry.d.ts +8 -0
- package/dist/repo-registry.js +52 -0
- package/dist/warp.d.ts +20 -0
- package/dist/warp.js +196 -0
- package/man/man1/gji-back.1 +13 -0
- package/man/man1/gji-clean.1 +1 -1
- package/man/man1/gji-completion.1 +1 -1
- package/man/man1/gji-config.1 +1 -1
- package/man/man1/gji-go.1 +1 -1
- package/man/man1/gji-history.1 +13 -0
- package/man/man1/gji-init.1 +1 -1
- package/man/man1/gji-ls.1 +1 -1
- package/man/man1/gji-new.1 +7 -1
- package/man/man1/gji-open.1 +19 -0
- package/man/man1/gji-pr.1 +1 -1
- package/man/man1/gji-remove.1 +1 -1
- package/man/man1/gji-root.1 +1 -1
- package/man/man1/gji-status.1 +1 -1
- package/man/man1/gji-sync.1 +1 -1
- package/man/man1/gji-trigger-hook.1 +1 -1
- package/man/man1/gji-warp.1 +19 -0
- package/man/man1/gji.1 +19 -1
- package/package.json +4 -2
package/README.md
CHANGED
|
@@ -107,6 +107,9 @@ gji completion fish > ~/.config/fish/completions/gji.fish
|
|
|
107
107
|
# start a new task
|
|
108
108
|
gji new feature/dark-mode
|
|
109
109
|
|
|
110
|
+
# start a task and open it straight in your editor
|
|
111
|
+
gji new feature/dark-mode --open --editor cursor
|
|
112
|
+
|
|
110
113
|
# review a pull request
|
|
111
114
|
gji pr 1234
|
|
112
115
|
|
|
@@ -117,6 +120,10 @@ gji status
|
|
|
117
120
|
gji go feature/dark-mode
|
|
118
121
|
gji go main
|
|
119
122
|
|
|
123
|
+
# open any worktree in an editor (interactive picker)
|
|
124
|
+
gji open
|
|
125
|
+
gji open feature/dark-mode --editor code
|
|
126
|
+
|
|
120
127
|
# clean up when done
|
|
121
128
|
gji remove feature/dark-mode
|
|
122
129
|
```
|
|
@@ -234,8 +241,9 @@ path=$(gji root --print)
|
|
|
234
241
|
|
|
235
242
|
| Command | Description |
|
|
236
243
|
|---|---|
|
|
237
|
-
| `gji new [branch] [--detached] [--json]` | create branch + worktree, cd in (validates branch name against Git rules) |
|
|
244
|
+
| `gji new [branch] [--detached] [--open] [--editor <cli>] [--json]` | create branch + worktree, cd in (validates branch name against Git rules) |
|
|
238
245
|
| `gji pr <ref> [--json]` | fetch PR ref, create worktree, cd in |
|
|
246
|
+
| `gji open [branch] [--editor <cli>] [--save] [--workspace]` | open a worktree in an editor |
|
|
239
247
|
| `gji go [branch] [--print]` | jump to a worktree |
|
|
240
248
|
| `gji root [--print]` | jump to the main repo root |
|
|
241
249
|
| `gji status [--json]` | repo overview, worktree health, ahead/behind |
|
|
@@ -260,6 +268,7 @@ No setup required. Optional config lives in:
|
|
|
260
268
|
| Key | Description |
|
|
261
269
|
|---|---|
|
|
262
270
|
| `branchPrefix` | prefix added to new branch names (e.g. `"feature/"`) |
|
|
271
|
+
| `editor` | default editor CLI for `gji open` and `gji new --open` (e.g. `"cursor"`, `"code"`, `"zed"`); set automatically with `gji open --save` |
|
|
263
272
|
| `worktreePath` | base directory for new worktrees (absolute or `~/…`); overrides the default `../worktrees/<repo>/` layout |
|
|
264
273
|
| `syncRemote` | remote for `gji sync` (default: `origin`) |
|
|
265
274
|
| `syncDefaultBranch` | branch to rebase onto (default: remote `HEAD`) |
|
|
@@ -314,8 +323,8 @@ Run scripts automatically at key lifecycle moments:
|
|
|
314
323
|
```json
|
|
315
324
|
{
|
|
316
325
|
"hooks": {
|
|
317
|
-
"afterCreate": "pnpm install",
|
|
318
|
-
"afterEnter": "
|
|
326
|
+
"afterCreate": ["pnpm", "install"],
|
|
327
|
+
"afterEnter": ["printf", "switched to %s\n", "{{branch}}"],
|
|
319
328
|
"beforeRemove": "pnpm run cleanup"
|
|
320
329
|
}
|
|
321
330
|
}
|
|
@@ -329,6 +338,31 @@ Run scripts automatically at key lifecycle moments:
|
|
|
329
338
|
|
|
330
339
|
Hooks receive `{{branch}}`, `{{path}}`, `{{repo}}` as template variables and `GJI_BRANCH`, `GJI_PATH`, `GJI_REPO` as environment variables. A failing hook emits a warning but never aborts the command.
|
|
331
340
|
|
|
341
|
+
Prefer argv-array hooks for simple commands:
|
|
342
|
+
|
|
343
|
+
```json
|
|
344
|
+
{
|
|
345
|
+
"hooks": {
|
|
346
|
+
"afterCreate": ["pnpm", "install"],
|
|
347
|
+
"afterEnter": ["printf", "switched to %s at %s\n", "{{branch}}", "{{path}}"]
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
Array hooks run without a shell and pass each array item as exactly one argument. Use string hooks only when you need shell features like `&&`, pipes, redirects, shell functions, or `nvm use`.
|
|
353
|
+
|
|
354
|
+
Template values are interpolated before the shell parses string hooks, so avoid putting `{{branch}}`, `{{path}}`, or `{{repo}}` directly into shell strings. For shell-string hooks, the safer pattern is to use the environment variables and double-quote each expansion:
|
|
355
|
+
|
|
356
|
+
```json
|
|
357
|
+
{
|
|
358
|
+
"hooks": {
|
|
359
|
+
"afterCreate": "pnpm install && printf 'ready: %s\n' \"$GJI_PATH\""
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
```
|
|
363
|
+
|
|
364
|
+
Avoid unquoted template values in shell strings, such as `echo {{branch}}` or `cd {{path}}`.
|
|
365
|
+
|
|
332
366
|
Hooks from all three config layers merge per key — different keys from different layers both apply, same key the higher-precedence layer wins:
|
|
333
367
|
|
|
334
368
|
```jsonc
|
package/dist/back.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type HistoryEntry } from './history.js';
|
|
2
|
+
export declare const BACK_OUTPUT_FILE_ENV = "GJI_BACK_OUTPUT_FILE";
|
|
3
|
+
export interface BackCommandOptions {
|
|
4
|
+
cwd: string;
|
|
5
|
+
home?: string;
|
|
6
|
+
n?: number;
|
|
7
|
+
print?: boolean;
|
|
8
|
+
stderr: (chunk: string) => void;
|
|
9
|
+
stdout: (chunk: string) => void;
|
|
10
|
+
}
|
|
11
|
+
export declare function runBackCommand(options: BackCommandOptions): Promise<number>;
|
|
12
|
+
export declare function formatHistoryList(history: HistoryEntry[], cwd: string): string;
|
|
13
|
+
export declare function formatAge(timestamp: number): string;
|
package/dist/back.js
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { access } from 'node:fs/promises';
|
|
2
|
+
import { basename } from 'node:path';
|
|
3
|
+
import { loadEffectiveConfig } from './config.js';
|
|
4
|
+
import { extractHooks, runHook } from './hooks.js';
|
|
5
|
+
import { appendHistory, loadHistory } from './history.js';
|
|
6
|
+
import { detectRepository } from './repo.js';
|
|
7
|
+
import { writeShellOutput } from './shell-handoff.js';
|
|
8
|
+
export const BACK_OUTPUT_FILE_ENV = 'GJI_BACK_OUTPUT_FILE';
|
|
9
|
+
export async function runBackCommand(options) {
|
|
10
|
+
const history = await loadHistory(options.home);
|
|
11
|
+
const steps = options.n ?? 1;
|
|
12
|
+
if (steps < 1) {
|
|
13
|
+
options.stderr('gji back: step count must be at least 1\n');
|
|
14
|
+
return 1;
|
|
15
|
+
}
|
|
16
|
+
let found = 0;
|
|
17
|
+
let target;
|
|
18
|
+
for (const entry of history) {
|
|
19
|
+
if (entry.path === options.cwd)
|
|
20
|
+
continue;
|
|
21
|
+
try {
|
|
22
|
+
await access(entry.path);
|
|
23
|
+
found++;
|
|
24
|
+
if (found === steps) {
|
|
25
|
+
target = entry;
|
|
26
|
+
break;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
// Path no longer exists — skip to the next entry
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
if (!target) {
|
|
34
|
+
options.stderr('gji back: no previous worktree in history\n');
|
|
35
|
+
options.stderr("Hint: Use 'gji go', 'gji new', or 'gji pr' to navigate between worktrees\n");
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const repository = await detectRepository(target.path);
|
|
40
|
+
const config = await loadEffectiveConfig(repository.repoRoot, options.home, options.stderr);
|
|
41
|
+
const hooks = extractHooks(config);
|
|
42
|
+
await runHook(hooks.afterEnter, target.path, { branch: target.branch ?? undefined, path: target.path, repo: basename(repository.repoRoot) }, options.stderr);
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Not in a git repo or hooks unavailable — proceed without hook
|
|
46
|
+
}
|
|
47
|
+
await appendHistory(target.path, target.branch, options.home);
|
|
48
|
+
await writeShellOutput(BACK_OUTPUT_FILE_ENV, target.path, options.stdout);
|
|
49
|
+
return 0;
|
|
50
|
+
}
|
|
51
|
+
export function formatHistoryList(history, cwd) {
|
|
52
|
+
const branchWidth = Math.max('BRANCH'.length, ...history.map((e) => (e.branch ?? '(detached)').length));
|
|
53
|
+
const lines = [' ' + 'BRANCH'.padEnd(branchWidth) + ' WHEN PATH'];
|
|
54
|
+
for (const entry of history) {
|
|
55
|
+
const isCurrent = entry.path === cwd;
|
|
56
|
+
const branch = (entry.branch ?? '(detached)').padEnd(branchWidth);
|
|
57
|
+
const when = formatAge(entry.timestamp).padEnd(10);
|
|
58
|
+
lines.push(`${isCurrent ? '*' : ' '} ${branch} ${when} ${entry.path}`);
|
|
59
|
+
}
|
|
60
|
+
return lines.join('\n') + '\n';
|
|
61
|
+
}
|
|
62
|
+
export function formatAge(timestamp) {
|
|
63
|
+
const seconds = Math.floor((Date.now() - timestamp) / 1000);
|
|
64
|
+
if (seconds < 60)
|
|
65
|
+
return 'just now';
|
|
66
|
+
const minutes = Math.floor(seconds / 60);
|
|
67
|
+
if (minutes < 60)
|
|
68
|
+
return `${minutes}m ago`;
|
|
69
|
+
const hours = Math.floor(minutes / 60);
|
|
70
|
+
if (hours < 24)
|
|
71
|
+
return `${hours}h ago`;
|
|
72
|
+
const days = Math.floor(hours / 24);
|
|
73
|
+
return `${days}d ago`;
|
|
74
|
+
}
|
package/dist/cli.js
CHANGED
|
@@ -1,20 +1,26 @@
|
|
|
1
1
|
import { createRequire } from 'node:module';
|
|
2
2
|
import { Command } from 'commander';
|
|
3
3
|
import updateNotifier from 'update-notifier';
|
|
4
|
+
import { runBackCommand } from './back.js';
|
|
4
5
|
import { runCleanCommand } from './clean.js';
|
|
6
|
+
import { runHistoryCommand } from './history-command.js';
|
|
5
7
|
import { runCompletionCommand } from './completion.js';
|
|
6
8
|
import { runConfigCommand } from './config-command.js';
|
|
7
9
|
import { runGoCommand } from './go.js';
|
|
10
|
+
import { runOpenCommand } from './open.js';
|
|
8
11
|
import { isHeadless } from './headless.js';
|
|
9
12
|
import { runInitCommand } from './init.js';
|
|
10
13
|
import { runLsCommand } from './ls.js';
|
|
11
14
|
import { runNewCommand } from './new.js';
|
|
12
15
|
import { runPrCommand } from './pr.js';
|
|
13
16
|
import { runRemoveCommand } from './remove.js';
|
|
17
|
+
import { registerRepo } from './repo-registry.js';
|
|
18
|
+
import { detectRepository } from './repo.js';
|
|
14
19
|
import { runRootCommand } from './root.js';
|
|
15
20
|
import { runStatusCommand } from './status.js';
|
|
16
21
|
import { runSyncCommand } from './sync.js';
|
|
17
22
|
import { runTriggerHookCommand } from './trigger-hook.js';
|
|
23
|
+
import { runWarpCommand } from './warp.js';
|
|
18
24
|
export function createProgram() {
|
|
19
25
|
const program = new Command();
|
|
20
26
|
const packageMetadata = readPackageMetadata();
|
|
@@ -37,6 +43,7 @@ function readPackageMetadata() {
|
|
|
37
43
|
}
|
|
38
44
|
export async function runCli(argv, options = {}) {
|
|
39
45
|
await maybeNotifyForUpdates(argv);
|
|
46
|
+
maybeRegisterCurrentRepo(options.cwd ?? process.cwd());
|
|
40
47
|
const program = createProgram();
|
|
41
48
|
const cwd = options.cwd ?? process.cwd();
|
|
42
49
|
const stdout = options.stdout ?? (() => undefined);
|
|
@@ -92,12 +99,19 @@ function defaultNotifyForUpdates(pkg) {
|
|
|
92
99
|
const notifier = updateNotifier({ pkg });
|
|
93
100
|
notifier.notify();
|
|
94
101
|
}
|
|
102
|
+
function maybeRegisterCurrentRepo(cwd) {
|
|
103
|
+
detectRepository(cwd)
|
|
104
|
+
.then(({ repoRoot }) => registerRepo(repoRoot))
|
|
105
|
+
.catch(() => undefined);
|
|
106
|
+
}
|
|
95
107
|
function registerCommands(program) {
|
|
96
108
|
program
|
|
97
109
|
.command('new [branch]')
|
|
98
110
|
.description('create a new branch or detached linked worktree')
|
|
99
111
|
.option('-f, --force', 'remove and recreate the worktree if the target path already exists')
|
|
100
112
|
.option('--detached', 'create a detached worktree without a branch')
|
|
113
|
+
.option('--open', 'open the new worktree in an editor after creation')
|
|
114
|
+
.option('--editor <cli>', 'editor CLI to use with --open (code, cursor, zed, …)')
|
|
101
115
|
.option('--dry-run', 'show what would be created without executing any git commands or writing files')
|
|
102
116
|
.option('--json', 'emit JSON on success or error instead of human-readable output')
|
|
103
117
|
.action(notImplemented('new'));
|
|
@@ -116,8 +130,26 @@ function registerCommands(program) {
|
|
|
116
130
|
.option('--dry-run', 'show what would be created without executing any git commands or writing files')
|
|
117
131
|
.option('--json', 'emit JSON on success or error instead of human-readable output')
|
|
118
132
|
.action(notImplemented('pr'));
|
|
133
|
+
program
|
|
134
|
+
.command('back [n]')
|
|
135
|
+
.description('navigate to the previously visited worktree, optionally N steps back')
|
|
136
|
+
.option('--print', 'print the resolved worktree path explicitly')
|
|
137
|
+
.action(notImplemented('back'));
|
|
138
|
+
program
|
|
139
|
+
.command('history')
|
|
140
|
+
.description('show navigation history')
|
|
141
|
+
.option('--json', 'print history as JSON')
|
|
142
|
+
.action(notImplemented('history'));
|
|
143
|
+
program
|
|
144
|
+
.command('open [branch]')
|
|
145
|
+
.description('open the worktree in an editor')
|
|
146
|
+
.option('--editor <cli>', 'editor CLI to use (code, cursor, zed, windsurf, subl, …)')
|
|
147
|
+
.option('--save', 'save the chosen editor to global config')
|
|
148
|
+
.option('--workspace', 'generate a .code-workspace file before opening (VS Code / Cursor / Windsurf)')
|
|
149
|
+
.action(notImplemented('open'));
|
|
119
150
|
program
|
|
120
151
|
.command('go [branch]')
|
|
152
|
+
.alias('jump')
|
|
121
153
|
.description('print or select a worktree path')
|
|
122
154
|
.option('--print', 'print the resolved worktree path explicitly')
|
|
123
155
|
.action(notImplemented('go'));
|
|
@@ -163,6 +195,15 @@ function registerCommands(program) {
|
|
|
163
195
|
.command('trigger-hook <hook>')
|
|
164
196
|
.description('run a named hook (afterCreate, afterEnter, beforeRemove) in the current worktree')
|
|
165
197
|
.action(notImplemented('trigger-hook'));
|
|
198
|
+
program
|
|
199
|
+
.command('warp [branch]')
|
|
200
|
+
.description('jump to any worktree across all known repos')
|
|
201
|
+
.option('-n, --new [branch]', 'create a new worktree in a registered repo')
|
|
202
|
+
// --print is the shell-wrapper bypass signal (see SHELL_WRAPPED_COMMANDS in init.ts).
|
|
203
|
+
// The shell omits GJI_WARP_OUTPUT_FILE, so writeShellOutput falls through to stdout.
|
|
204
|
+
.option('--print', 'print the resolved worktree path without changing directory')
|
|
205
|
+
.option('--json', 'emit JSON on success or error instead of human-readable output')
|
|
206
|
+
.action(notImplemented('warp'));
|
|
166
207
|
const configCommand = program
|
|
167
208
|
.command('config')
|
|
168
209
|
.description('manage global config defaults')
|
|
@@ -184,7 +225,7 @@ function attachCommandActions(program, options) {
|
|
|
184
225
|
program.commands
|
|
185
226
|
.find((command) => command.name() === 'new')
|
|
186
227
|
?.action(async (branch, commandOptions) => {
|
|
187
|
-
const exitCode = await runNewCommand({ ...options, branch, detached: commandOptions.detached, dryRun: commandOptions.dryRun, force: commandOptions.force, json: commandOptions.json });
|
|
228
|
+
const exitCode = await runNewCommand({ ...options, branch, detached: commandOptions.detached, dryRun: commandOptions.dryRun, editor: commandOptions.editor, force: commandOptions.force, json: commandOptions.json, open: commandOptions.open });
|
|
188
229
|
if (exitCode !== 0) {
|
|
189
230
|
throw commanderExit(exitCode);
|
|
190
231
|
}
|
|
@@ -223,6 +264,53 @@ function attachCommandActions(program, options) {
|
|
|
223
264
|
throw commanderExit(exitCode);
|
|
224
265
|
}
|
|
225
266
|
});
|
|
267
|
+
program.commands
|
|
268
|
+
.find((command) => command.name() === 'back')
|
|
269
|
+
?.action(async (n, commandOptions) => {
|
|
270
|
+
if (n !== undefined && !/^\d+$/.test(n)) {
|
|
271
|
+
options.stderr(`gji back: invalid step count: ${n}\n`);
|
|
272
|
+
throw commanderExit(1);
|
|
273
|
+
}
|
|
274
|
+
const steps = n !== undefined ? parseInt(n, 10) : undefined;
|
|
275
|
+
const exitCode = await runBackCommand({
|
|
276
|
+
cwd: options.cwd,
|
|
277
|
+
n: steps,
|
|
278
|
+
print: commandOptions.print,
|
|
279
|
+
stderr: options.stderr,
|
|
280
|
+
stdout: options.stdout,
|
|
281
|
+
});
|
|
282
|
+
if (exitCode !== 0) {
|
|
283
|
+
throw commanderExit(exitCode);
|
|
284
|
+
}
|
|
285
|
+
});
|
|
286
|
+
program.commands
|
|
287
|
+
.find((command) => command.name() === 'history')
|
|
288
|
+
?.action(async (commandOptions) => {
|
|
289
|
+
const exitCode = await runHistoryCommand({
|
|
290
|
+
cwd: options.cwd,
|
|
291
|
+
json: commandOptions.json,
|
|
292
|
+
stdout: options.stdout,
|
|
293
|
+
});
|
|
294
|
+
if (exitCode !== 0) {
|
|
295
|
+
throw commanderExit(exitCode);
|
|
296
|
+
}
|
|
297
|
+
});
|
|
298
|
+
program.commands
|
|
299
|
+
.find((command) => command.name() === 'open')
|
|
300
|
+
?.action(async (branch, commandOptions) => {
|
|
301
|
+
const exitCode = await runOpenCommand({
|
|
302
|
+
branch,
|
|
303
|
+
cwd: options.cwd,
|
|
304
|
+
editor: commandOptions.editor,
|
|
305
|
+
save: commandOptions.save,
|
|
306
|
+
stderr: options.stderr,
|
|
307
|
+
stdout: options.stdout,
|
|
308
|
+
workspace: commandOptions.workspace,
|
|
309
|
+
});
|
|
310
|
+
if (exitCode !== 0) {
|
|
311
|
+
throw commanderExit(exitCode);
|
|
312
|
+
}
|
|
313
|
+
});
|
|
226
314
|
program.commands
|
|
227
315
|
.find((command) => command.name() === 'go')
|
|
228
316
|
?.action(async (branch, commandOptions) => {
|
|
@@ -333,6 +421,24 @@ function attachCommandActions(program, options) {
|
|
|
333
421
|
throw commanderExit(exitCode);
|
|
334
422
|
}
|
|
335
423
|
});
|
|
424
|
+
program.commands
|
|
425
|
+
.find((command) => command.name() === 'warp')
|
|
426
|
+
?.action(async (branch, commandOptions) => {
|
|
427
|
+
const newFlag = commandOptions.new;
|
|
428
|
+
const newWorktree = newFlag !== undefined && newFlag !== false;
|
|
429
|
+
const newBranch = typeof newFlag === 'string' ? newFlag : undefined;
|
|
430
|
+
const exitCode = await runWarpCommand({
|
|
431
|
+
branch: newWorktree ? (newBranch ?? branch) : branch,
|
|
432
|
+
cwd: options.cwd,
|
|
433
|
+
json: commandOptions.json,
|
|
434
|
+
newWorktree,
|
|
435
|
+
stderr: options.stderr,
|
|
436
|
+
stdout: options.stdout,
|
|
437
|
+
});
|
|
438
|
+
if (exitCode !== 0) {
|
|
439
|
+
throw commanderExit(exitCode);
|
|
440
|
+
}
|
|
441
|
+
});
|
|
336
442
|
const configCommand = program.commands.find((command) => command.name() === 'config');
|
|
337
443
|
configCommand?.action(async () => {
|
|
338
444
|
const exitCode = await runConfigCommand({
|
package/dist/config.js
CHANGED
package/dist/editor.d.ts
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface EditorDefinition {
|
|
2
|
+
cli: string;
|
|
3
|
+
name: string;
|
|
4
|
+
newWindowFlag?: string;
|
|
5
|
+
supportsWorkspace: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare const EDITORS: EditorDefinition[];
|
|
8
|
+
export declare function defaultSpawnEditor(cli: string, args: string[]): Promise<void>;
|
package/dist/editor.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { spawn } from 'node:child_process';
|
|
2
|
+
// Ordered by likely popularity among the target audience.
|
|
3
|
+
export const EDITORS = [
|
|
4
|
+
{ cli: 'cursor', name: 'Cursor', newWindowFlag: '--new-window', supportsWorkspace: true },
|
|
5
|
+
{ cli: 'code', name: 'VS Code', newWindowFlag: '--new-window', supportsWorkspace: true },
|
|
6
|
+
{ cli: 'windsurf', name: 'Windsurf', newWindowFlag: '--new-window', supportsWorkspace: true },
|
|
7
|
+
{ cli: 'zed', name: 'Zed', supportsWorkspace: false },
|
|
8
|
+
{ cli: 'subl', name: 'Sublime Text', newWindowFlag: '--new-window', supportsWorkspace: false },
|
|
9
|
+
];
|
|
10
|
+
export async function defaultSpawnEditor(cli, args) {
|
|
11
|
+
const child = spawn(cli, args, { detached: true, stdio: 'ignore' });
|
|
12
|
+
await new Promise((resolve, reject) => {
|
|
13
|
+
child.once('error', reject);
|
|
14
|
+
child.once('spawn', resolve);
|
|
15
|
+
});
|
|
16
|
+
child.unref();
|
|
17
|
+
}
|