@solaqua/gji 0.6.1 → 0.7.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.
Files changed (80) hide show
  1. package/README.md +26 -1
  2. package/dist/back.d.ts +1 -1
  3. package/dist/back.js +23 -17
  4. package/dist/clean.d.ts +1 -1
  5. package/dist/clean.js +44 -35
  6. package/dist/cli.d.ts +1 -1
  7. package/dist/cli.js +264 -164
  8. package/dist/completion.js +3 -3
  9. package/dist/config-command.js +5 -5
  10. package/dist/config.js +41 -35
  11. package/dist/conflict.d.ts +1 -1
  12. package/dist/conflict.js +14 -6
  13. package/dist/editor.js +29 -9
  14. package/dist/file-sync.d.ts +1 -0
  15. package/dist/file-sync.js +15 -11
  16. package/dist/git.d.ts +1 -1
  17. package/dist/git.js +21 -19
  18. package/dist/gji-bundle.mjs +1709 -850
  19. package/dist/go.d.ts +2 -2
  20. package/dist/go.js +39 -26
  21. package/dist/headless.js +1 -1
  22. package/dist/history-command.js +3 -3
  23. package/dist/history.js +12 -12
  24. package/dist/hooks.js +16 -16
  25. package/dist/index.js +13 -9
  26. package/dist/init.d.ts +2 -2
  27. package/dist/init.js +106 -94
  28. package/dist/install-prompt.d.ts +3 -3
  29. package/dist/install-prompt.js +46 -28
  30. package/dist/ls.d.ts +2 -2
  31. package/dist/ls.js +29 -29
  32. package/dist/new.d.ts +2 -2
  33. package/dist/new.js +96 -81
  34. package/dist/open.d.ts +2 -2
  35. package/dist/open.js +24 -21
  36. package/dist/package-manager.js +96 -45
  37. package/dist/pr.d.ts +2 -2
  38. package/dist/pr.js +47 -34
  39. package/dist/remove.d.ts +1 -1
  40. package/dist/remove.js +39 -27
  41. package/dist/repo-registry.js +45 -19
  42. package/dist/repo.js +29 -28
  43. package/dist/root.js +3 -3
  44. package/dist/shell-completion.d.ts +1 -1
  45. package/dist/shell-completion.js +65 -37
  46. package/dist/shell-handoff.js +2 -2
  47. package/dist/shell.d.ts +1 -1
  48. package/dist/shell.js +4 -4
  49. package/dist/status.d.ts +5 -5
  50. package/dist/status.js +23 -23
  51. package/dist/sync-files-command.d.ts +10 -0
  52. package/dist/sync-files-command.js +137 -0
  53. package/dist/sync.js +23 -15
  54. package/dist/trigger-hook.js +9 -5
  55. package/dist/warp.js +66 -34
  56. package/dist/worktree-info.d.ts +9 -9
  57. package/dist/worktree-info.js +31 -29
  58. package/dist/worktree-management.d.ts +1 -1
  59. package/dist/worktree-management.js +26 -11
  60. package/dist/worktree-prompts.js +5 -5
  61. package/man/man1/gji-back.1 +1 -1
  62. package/man/man1/gji-clean.1 +1 -1
  63. package/man/man1/gji-completion.1 +1 -1
  64. package/man/man1/gji-config.1 +1 -1
  65. package/man/man1/gji-go.1 +1 -1
  66. package/man/man1/gji-history.1 +1 -1
  67. package/man/man1/gji-init.1 +1 -1
  68. package/man/man1/gji-ls.1 +1 -1
  69. package/man/man1/gji-new.1 +1 -1
  70. package/man/man1/gji-open.1 +1 -1
  71. package/man/man1/gji-pr.1 +1 -1
  72. package/man/man1/gji-remove.1 +1 -1
  73. package/man/man1/gji-root.1 +1 -1
  74. package/man/man1/gji-status.1 +1 -1
  75. package/man/man1/gji-sync-files.1 +23 -0
  76. package/man/man1/gji-sync.1 +1 -1
  77. package/man/man1/gji-trigger-hook.1 +1 -1
  78. package/man/man1/gji-warp.1 +1 -1
  79. package/man/man1/gji.1 +5 -1
  80. package/package.json +8 -2
package/dist/cli.js CHANGED
@@ -1,32 +1,33 @@
1
- import { createRequire } from 'node:module';
2
- import { Command } from 'commander';
3
- import updateNotifier from 'update-notifier';
4
- import { runBackCommand } from './back.js';
5
- import { runCleanCommand } from './clean.js';
6
- import { runHistoryCommand } from './history-command.js';
7
- import { runCompletionCommand } from './completion.js';
8
- import { runConfigCommand } from './config-command.js';
9
- import { runGoCommand } from './go.js';
10
- import { runOpenCommand } from './open.js';
11
- import { isHeadless } from './headless.js';
12
- import { runInitCommand } from './init.js';
13
- import { runLsCommand } from './ls.js';
14
- import { runNewCommand } from './new.js';
15
- import { runPrCommand } from './pr.js';
16
- import { runRemoveCommand } from './remove.js';
17
- import { registerRepo } from './repo-registry.js';
18
- import { detectRepository } from './repo.js';
19
- import { runRootCommand } from './root.js';
20
- import { runStatusCommand } from './status.js';
21
- import { runSyncCommand } from './sync.js';
22
- import { runTriggerHookCommand } from './trigger-hook.js';
23
- import { runWarpCommand } from './warp.js';
1
+ import { createRequire } from "node:module";
2
+ import { Command } from "commander";
3
+ import updateNotifier from "update-notifier";
4
+ import { runBackCommand } from "./back.js";
5
+ import { runCleanCommand } from "./clean.js";
6
+ import { runCompletionCommand } from "./completion.js";
7
+ import { runConfigCommand } from "./config-command.js";
8
+ import { runGoCommand } from "./go.js";
9
+ import { isHeadless } from "./headless.js";
10
+ import { runHistoryCommand } from "./history-command.js";
11
+ import { runInitCommand } from "./init.js";
12
+ import { runLsCommand } from "./ls.js";
13
+ import { runNewCommand } from "./new.js";
14
+ import { runOpenCommand } from "./open.js";
15
+ import { runPrCommand } from "./pr.js";
16
+ import { runRemoveCommand } from "./remove.js";
17
+ import { detectRepository } from "./repo.js";
18
+ import { registerRepo } from "./repo-registry.js";
19
+ import { runRootCommand } from "./root.js";
20
+ import { runStatusCommand } from "./status.js";
21
+ import { runSyncCommand } from "./sync.js";
22
+ import { runSyncFilesCommand } from "./sync-files-command.js";
23
+ import { runTriggerHookCommand } from "./trigger-hook.js";
24
+ import { runWarpCommand } from "./warp.js";
24
25
  export function createProgram() {
25
26
  const program = new Command();
26
27
  const packageMetadata = readPackageMetadata();
27
28
  program
28
- .name('gji')
29
- .description('Context switching without the mess.')
29
+ .name("gji")
30
+ .description("Context switching without the mess.")
30
31
  .version(packageMetadata.version)
31
32
  .showHelpAfterError()
32
33
  .showSuggestionAfterError();
@@ -35,10 +36,10 @@ export function createProgram() {
35
36
  }
36
37
  function readPackageMetadata() {
37
38
  const require = createRequire(import.meta.url);
38
- const packageJson = require('../package.json');
39
+ const packageJson = require("../package.json");
39
40
  return {
40
- name: typeof packageJson.name === 'string' ? packageJson.name : 'gji',
41
- version: typeof packageJson.version === 'string' ? packageJson.version : '0.0.0',
41
+ name: typeof packageJson.name === "string" ? packageJson.name : "gji",
42
+ version: typeof packageJson.version === "string" ? packageJson.version : "0.0.0",
42
43
  };
43
44
  }
44
45
  export async function runCli(argv, options = {}) {
@@ -59,7 +60,7 @@ export async function runCli(argv, options = {}) {
59
60
  }
60
61
  try {
61
62
  attachCommandActions(program, { cwd, stderr, stdout });
62
- await program.parseAsync(['node', 'gji', ...argv], { from: 'node' });
63
+ await program.parseAsync(["node", "gji", ...argv], { from: "node" });
63
64
  return { exitCode: 0 };
64
65
  }
65
66
  catch (error) {
@@ -81,19 +82,19 @@ async function maybeNotifyForUpdates(argv) {
81
82
  }
82
83
  }
83
84
  function shouldSkipUpdateNotification(argv) {
84
- return (argv.length === 0
85
- || argv.includes('--json')
86
- || argv.some(isHelpOrVersionArgument)
87
- || isHeadless()
88
- || process.stdout.isTTY !== true
89
- || process.stderr.isTTY !== true);
85
+ return (argv.length === 0 ||
86
+ argv.includes("--json") ||
87
+ argv.some(isHelpOrVersionArgument) ||
88
+ isHeadless() ||
89
+ process.stdout.isTTY !== true ||
90
+ process.stderr.isTTY !== true);
90
91
  }
91
92
  function isHelpOrVersionArgument(argument) {
92
- return (argument === '--help'
93
- || argument === '-h'
94
- || argument === 'help'
95
- || argument === '--version'
96
- || argument === '-V');
93
+ return (argument === "--help" ||
94
+ argument === "-h" ||
95
+ argument === "help" ||
96
+ argument === "--version" ||
97
+ argument === "-V");
97
98
  }
98
99
  function defaultNotifyForUpdates(pkg) {
99
100
  const notifier = updateNotifier({ pkg });
@@ -106,132 +107,162 @@ function maybeRegisterCurrentRepo(cwd) {
106
107
  }
107
108
  function registerCommands(program) {
108
109
  program
109
- .command('new [branch]')
110
- .description('create a new branch or detached linked worktree')
111
- .option('-f, --force', 'remove and recreate the worktree if the target path already exists')
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, …)')
115
- .option('--dry-run', 'show what would be created without executing any git commands or writing files')
116
- .option('--json', 'emit JSON on success or error instead of human-readable output')
117
- .action(notImplemented('new'));
110
+ .command("new [branch]")
111
+ .description("create a new branch or detached linked worktree")
112
+ .option("-f, --force", "remove and recreate the worktree if the target path already exists")
113
+ .option("--detached", "create a detached worktree without a branch")
114
+ .option("--open", "open the new worktree in an editor after creation")
115
+ .option("--editor <cli>", "editor CLI to use with --open (code, cursor, zed, …)")
116
+ .option("--dry-run", "show what would be created without executing any git commands or writing files")
117
+ .option("--json", "emit JSON on success or error instead of human-readable output")
118
+ .action(notImplemented("new"));
118
119
  program
119
- .command('init [shell]')
120
- .description('print or install shell integration')
121
- .option('--write', 'write the integration to the shell config file')
122
- .action(notImplemented('init'));
120
+ .command("init [shell]")
121
+ .description("print or install shell integration")
122
+ .option("--write", "write the integration to the shell config file")
123
+ .action(notImplemented("init"));
123
124
  program
124
- .command('completion [shell]')
125
- .description('print shell completion definitions')
126
- .action(notImplemented('completion'));
125
+ .command("completion [shell]")
126
+ .description("print shell completion definitions")
127
+ .action(notImplemented("completion"));
127
128
  program
128
- .command('pr <ref>')
129
- .description('fetch a pull request by number, #number, or URL into a linked worktree')
130
- .option('--dry-run', 'show what would be created without executing any git commands or writing files')
131
- .option('--json', 'emit JSON on success or error instead of human-readable output')
132
- .action(notImplemented('pr'));
129
+ .command("pr <ref>")
130
+ .description("fetch a pull request by number, #number, or URL into a linked worktree")
131
+ .option("--dry-run", "show what would be created without executing any git commands or writing files")
132
+ .option("--json", "emit JSON on success or error instead of human-readable output")
133
+ .action(notImplemented("pr"));
133
134
  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'));
135
+ .command("back [n]")
136
+ .description("navigate to the previously visited worktree, optionally N steps back")
137
+ .option("--print", "print the resolved worktree path explicitly")
138
+ .action(notImplemented("back"));
138
139
  program
139
- .command('history')
140
- .description('show navigation history')
141
- .option('--json', 'print history as JSON')
142
- .action(notImplemented('history'));
140
+ .command("history")
141
+ .description("show navigation history")
142
+ .option("--json", "print history as JSON")
143
+ .action(notImplemented("history"));
143
144
  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'));
145
+ .command("open [branch]")
146
+ .description("open the worktree in an editor")
147
+ .option("--editor <cli>", "editor CLI to use (code, cursor, zed, windsurf, subl, …)")
148
+ .option("--save", "save the chosen editor to global config")
149
+ .option("--workspace", "generate a .code-workspace file before opening (VS Code / Cursor / Windsurf)")
150
+ .action(notImplemented("open"));
150
151
  program
151
- .command('go [branch]')
152
- .alias('jump')
153
- .description('print or select a worktree path')
154
- .option('--print', 'print the resolved worktree path explicitly')
155
- .action(notImplemented('go'));
152
+ .command("go [branch]")
153
+ .alias("jump")
154
+ .description("print or select a worktree path")
155
+ .option("--print", "print the resolved worktree path explicitly")
156
+ .action(notImplemented("go"));
156
157
  program
157
- .command('root')
158
- .description('print the main repository root path')
159
- .option('--print', 'print the resolved repository root path explicitly')
160
- .action(notImplemented('root'));
158
+ .command("root")
159
+ .description("print the main repository root path")
160
+ .option("--print", "print the resolved repository root path explicitly")
161
+ .action(notImplemented("root"));
161
162
  program
162
- .command('status')
163
- .description('summarize repository and worktree health')
164
- .option('--json', 'print repository and worktree health as JSON')
165
- .action(notImplemented('status'));
163
+ .command("status")
164
+ .description("summarize repository and worktree health")
165
+ .option("--json", "print repository and worktree health as JSON")
166
+ .action(notImplemented("status"));
166
167
  program
167
- .command('sync')
168
- .description('fetch and update one or all worktrees')
169
- .option('--all', 'sync every worktree in the repository')
170
- .option('--json', 'emit JSON on success or error instead of human-readable output')
171
- .action(notImplemented('sync'));
168
+ .command("sync")
169
+ .description("fetch and update one or all worktrees")
170
+ .option("--all", "sync every worktree in the repository")
171
+ .option("--json", "emit JSON on success or error instead of human-readable output")
172
+ .action(notImplemented("sync"));
173
+ const syncFilesCommand = program
174
+ .command("sync-files")
175
+ .description("manage local files copied into new worktrees")
176
+ .option("--json", "emit JSON instead of human-readable output")
177
+ .action(notImplemented("sync-files"));
178
+ syncFilesCommand
179
+ .command("list")
180
+ .description("list files synced into new worktrees for this repo")
181
+ .option("--json", "emit JSON instead of human-readable output")
182
+ .action(notImplemented("sync-files list"));
183
+ syncFilesCommand
184
+ .command("add <paths...>")
185
+ .description("add repo-local sync files to global config")
186
+ .option("--json", "emit JSON instead of human-readable output")
187
+ .action(notImplemented("sync-files add"));
188
+ syncFilesCommand
189
+ .command("remove <paths...>")
190
+ .alias("rm")
191
+ .description("remove repo-local sync files from global config")
192
+ .option("--json", "emit JSON instead of human-readable output")
193
+ .action(notImplemented("sync-files remove"));
172
194
  program
173
- .command('ls')
174
- .description('list active worktrees')
175
- .option('--compact', 'show only branch and path columns')
176
- .option('--json', 'print active worktrees as JSON')
177
- .action(notImplemented('ls'));
195
+ .command("ls")
196
+ .description("list active worktrees")
197
+ .option("--compact", "show only branch and path columns")
198
+ .option("--json", "print active worktrees as JSON")
199
+ .action(notImplemented("ls"));
178
200
  program
179
- .command('clean')
180
- .description('interactively prune linked worktrees')
181
- .option('-f, --force', 'bypass prompts, force-remove dirty worktrees, and force-delete unmerged branches')
182
- .option('--stale', 'only target clean worktrees whose upstream is gone and branch is merged into the default branch')
183
- .option('--dry-run', 'show what would be deleted without removing anything')
184
- .option('--json', 'emit JSON on success or error instead of human-readable output')
185
- .action(notImplemented('clean'));
201
+ .command("clean")
202
+ .description("interactively prune linked worktrees")
203
+ .option("-f, --force", "bypass prompts, force-remove dirty worktrees, and force-delete unmerged branches")
204
+ .option("--stale", "only target clean worktrees whose upstream is gone and branch is merged into the default branch")
205
+ .option("--dry-run", "show what would be deleted without removing anything")
206
+ .option("--json", "emit JSON on success or error instead of human-readable output")
207
+ .action(notImplemented("clean"));
186
208
  program
187
- .command('remove [branch]')
188
- .alias('rm')
189
- .description('remove a linked worktree and delete its branch when present')
190
- .option('-f, --force', 'bypass prompts, force-remove a dirty worktree, and force-delete an unmerged branch')
191
- .option('--dry-run', 'show what would be deleted without removing anything')
192
- .option('--json', 'emit JSON on success or error instead of human-readable output')
193
- .action(notImplemented('remove'));
209
+ .command("remove [branch]")
210
+ .alias("rm")
211
+ .description("remove a linked worktree and delete its branch when present")
212
+ .option("-f, --force", "bypass prompts, force-remove a dirty worktree, and force-delete an unmerged branch")
213
+ .option("--dry-run", "show what would be deleted without removing anything")
214
+ .option("--json", "emit JSON on success or error instead of human-readable output")
215
+ .action(notImplemented("remove"));
194
216
  program
195
- .command('trigger-hook <hook>')
196
- .description('run a named hook (afterCreate, afterEnter, beforeRemove) in the current worktree')
197
- .action(notImplemented('trigger-hook'));
217
+ .command("trigger-hook <hook>")
218
+ .description("run a named hook (afterCreate, afterEnter, beforeRemove) in the current worktree")
219
+ .action(notImplemented("trigger-hook"));
198
220
  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')
221
+ .command("warp [branch]")
222
+ .description("jump to any worktree across all known repos")
223
+ .option("-n, --new [branch]", "create a new worktree in a registered repo")
202
224
  // --print is the shell-wrapper bypass signal (see SHELL_WRAPPED_COMMANDS in init.ts).
203
225
  // 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'));
226
+ .option("--print", "print the resolved worktree path without changing directory")
227
+ .option("--json", "emit JSON on success or error instead of human-readable output")
228
+ .action(notImplemented("warp"));
207
229
  const configCommand = program
208
- .command('config')
209
- .description('manage global config defaults')
210
- .action(notImplemented('config'));
230
+ .command("config")
231
+ .description("manage global config defaults")
232
+ .action(notImplemented("config"));
211
233
  configCommand
212
- .command('get [key]')
213
- .description('print the global config or a single key')
214
- .action(notImplemented('config get'));
234
+ .command("get [key]")
235
+ .description("print the global config or a single key")
236
+ .action(notImplemented("config get"));
215
237
  configCommand
216
- .command('set <key> <value>')
217
- .description('set a global config value')
218
- .action(notImplemented('config set'));
238
+ .command("set <key> <value>")
239
+ .description("set a global config value")
240
+ .action(notImplemented("config set"));
219
241
  configCommand
220
- .command('unset <key>')
221
- .description('remove a global config value')
222
- .action(notImplemented('config unset'));
242
+ .command("unset <key>")
243
+ .description("remove a global config value")
244
+ .action(notImplemented("config unset"));
223
245
  }
224
246
  function attachCommandActions(program, options) {
225
247
  program.commands
226
- .find((command) => command.name() === 'new')
248
+ .find((command) => command.name() === "new")
227
249
  ?.action(async (branch, commandOptions) => {
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 });
250
+ const exitCode = await runNewCommand({
251
+ ...options,
252
+ branch,
253
+ detached: commandOptions.detached,
254
+ dryRun: commandOptions.dryRun,
255
+ editor: commandOptions.editor,
256
+ force: commandOptions.force,
257
+ json: commandOptions.json,
258
+ open: commandOptions.open,
259
+ });
229
260
  if (exitCode !== 0) {
230
261
  throw commanderExit(exitCode);
231
262
  }
232
263
  });
233
264
  program.commands
234
- .find((command) => command.name() === 'init')
265
+ .find((command) => command.name() === "init")
235
266
  ?.action(async (shell, commandOptions) => {
236
267
  const exitCode = await runInitCommand({
237
268
  cwd: options.cwd,
@@ -245,7 +276,7 @@ function attachCommandActions(program, options) {
245
276
  }
246
277
  });
247
278
  program.commands
248
- .find((command) => command.name() === 'completion')
279
+ .find((command) => command.name() === "completion")
249
280
  ?.action(async (shell) => {
250
281
  const exitCode = await runCompletionCommand({
251
282
  shell,
@@ -257,15 +288,22 @@ function attachCommandActions(program, options) {
257
288
  }
258
289
  });
259
290
  program.commands
260
- .find((command) => command.name() === 'pr')
291
+ .find((command) => command.name() === "pr")
261
292
  ?.action(async (number, commandOptions) => {
262
- const exitCode = await runPrCommand({ cwd: options.cwd, dryRun: commandOptions.dryRun, json: commandOptions.json, number, stderr: options.stderr, stdout: options.stdout });
293
+ const exitCode = await runPrCommand({
294
+ cwd: options.cwd,
295
+ dryRun: commandOptions.dryRun,
296
+ json: commandOptions.json,
297
+ number,
298
+ stderr: options.stderr,
299
+ stdout: options.stdout,
300
+ });
263
301
  if (exitCode !== 0) {
264
302
  throw commanderExit(exitCode);
265
303
  }
266
304
  });
267
305
  program.commands
268
- .find((command) => command.name() === 'back')
306
+ .find((command) => command.name() === "back")
269
307
  ?.action(async (n, commandOptions) => {
270
308
  if (n !== undefined && !/^\d+$/.test(n)) {
271
309
  options.stderr(`gji back: invalid step count: ${n}\n`);
@@ -284,7 +322,7 @@ function attachCommandActions(program, options) {
284
322
  }
285
323
  });
286
324
  program.commands
287
- .find((command) => command.name() === 'history')
325
+ .find((command) => command.name() === "history")
288
326
  ?.action(async (commandOptions) => {
289
327
  const exitCode = await runHistoryCommand({
290
328
  cwd: options.cwd,
@@ -296,7 +334,7 @@ function attachCommandActions(program, options) {
296
334
  }
297
335
  });
298
336
  program.commands
299
- .find((command) => command.name() === 'open')
337
+ .find((command) => command.name() === "open")
300
338
  ?.action(async (branch, commandOptions) => {
301
339
  const exitCode = await runOpenCommand({
302
340
  branch,
@@ -312,7 +350,7 @@ function attachCommandActions(program, options) {
312
350
  }
313
351
  });
314
352
  program.commands
315
- .find((command) => command.name() === 'go')
353
+ .find((command) => command.name() === "go")
316
354
  ?.action(async (branch, commandOptions) => {
317
355
  const exitCode = await runGoCommand({
318
356
  branch,
@@ -326,7 +364,7 @@ function attachCommandActions(program, options) {
326
364
  }
327
365
  });
328
366
  program.commands
329
- .find((command) => command.name() === 'root')
367
+ .find((command) => command.name() === "root")
330
368
  ?.action(async (commandOptions) => {
331
369
  const exitCode = await runRootCommand({
332
370
  cwd: options.cwd,
@@ -338,7 +376,7 @@ function attachCommandActions(program, options) {
338
376
  }
339
377
  });
340
378
  program.commands
341
- .find((command) => command.name() === 'status')
379
+ .find((command) => command.name() === "status")
342
380
  ?.action(async (commandOptions) => {
343
381
  const exitCode = await runStatusCommand({
344
382
  cwd: options.cwd,
@@ -350,7 +388,7 @@ function attachCommandActions(program, options) {
350
388
  }
351
389
  });
352
390
  program.commands
353
- .find((command) => command.name() === 'sync')
391
+ .find((command) => command.name() === "sync")
354
392
  ?.action(async (commandOptions) => {
355
393
  const exitCode = await runSyncCommand({
356
394
  all: commandOptions.all,
@@ -363,8 +401,66 @@ function attachCommandActions(program, options) {
363
401
  throw commanderExit(exitCode);
364
402
  }
365
403
  });
404
+ const syncFilesCommand = program.commands.find((command) => command.name() === "sync-files");
405
+ syncFilesCommand?.action(async (commandOptions) => {
406
+ const exitCode = await runSyncFilesCommand({
407
+ action: "list",
408
+ cwd: options.cwd,
409
+ json: commandOptions.json,
410
+ stderr: options.stderr,
411
+ stdout: options.stdout,
412
+ });
413
+ if (exitCode !== 0) {
414
+ throw commanderExit(exitCode);
415
+ }
416
+ });
417
+ syncFilesCommand?.commands
418
+ .find((command) => command.name() === "list")
419
+ ?.action(async (commandOptions) => {
420
+ const exitCode = await runSyncFilesCommand({
421
+ action: "list",
422
+ cwd: options.cwd,
423
+ json: commandOptions.json || syncFilesCommand?.opts().json,
424
+ stderr: options.stderr,
425
+ stdout: options.stdout,
426
+ });
427
+ if (exitCode !== 0) {
428
+ throw commanderExit(exitCode);
429
+ }
430
+ });
431
+ syncFilesCommand?.commands
432
+ .find((command) => command.name() === "add")
433
+ ?.action(async (paths, commandOptions) => {
434
+ const exitCode = await runSyncFilesCommand({
435
+ action: "add",
436
+ cwd: options.cwd,
437
+ json: commandOptions.json || syncFilesCommand?.opts().json,
438
+ paths,
439
+ stderr: options.stderr,
440
+ stdout: options.stdout,
441
+ });
442
+ if (exitCode !== 0) {
443
+ throw commanderExit(exitCode);
444
+ }
445
+ });
446
+ const runSyncFilesRemoveCommand = async (paths, commandOptions) => {
447
+ const exitCode = await runSyncFilesCommand({
448
+ action: "remove",
449
+ cwd: options.cwd,
450
+ json: commandOptions.json || syncFilesCommand?.opts().json,
451
+ paths,
452
+ stderr: options.stderr,
453
+ stdout: options.stdout,
454
+ });
455
+ if (exitCode !== 0) {
456
+ throw commanderExit(exitCode);
457
+ }
458
+ };
459
+ syncFilesCommand?.commands
460
+ .find((command) => command.name() === "remove")
461
+ ?.action(runSyncFilesRemoveCommand);
366
462
  program.commands
367
- .find((command) => command.name() === 'ls')
463
+ .find((command) => command.name() === "ls")
368
464
  ?.action(async (commandOptions) => {
369
465
  const exitCode = await runLsCommand({
370
466
  compact: commandOptions.compact,
@@ -377,7 +473,7 @@ function attachCommandActions(program, options) {
377
473
  }
378
474
  });
379
475
  program.commands
380
- .find((command) => command.name() === 'clean')
476
+ .find((command) => command.name() === "clean")
381
477
  ?.action(async (commandOptions) => {
382
478
  const exitCode = await runCleanCommand({
383
479
  cwd: options.cwd,
@@ -407,10 +503,10 @@ function attachCommandActions(program, options) {
407
503
  }
408
504
  };
409
505
  program.commands
410
- .find((command) => command.name() === 'remove')
506
+ .find((command) => command.name() === "remove")
411
507
  ?.action(runRemovalCommand);
412
508
  program.commands
413
- .find((command) => command.name() === 'trigger-hook')
509
+ .find((command) => command.name() === "trigger-hook")
414
510
  ?.action(async (hook) => {
415
511
  const exitCode = await runTriggerHookCommand({
416
512
  cwd: options.cwd,
@@ -422,11 +518,11 @@ function attachCommandActions(program, options) {
422
518
  }
423
519
  });
424
520
  program.commands
425
- .find((command) => command.name() === 'warp')
521
+ .find((command) => command.name() === "warp")
426
522
  ?.action(async (branch, commandOptions) => {
427
523
  const newFlag = commandOptions.new;
428
524
  const newWorktree = newFlag !== undefined && newFlag !== false;
429
- const newBranch = typeof newFlag === 'string' ? newFlag : undefined;
525
+ const newBranch = typeof newFlag === "string" ? newFlag : undefined;
430
526
  const exitCode = await runWarpCommand({
431
527
  branch: newWorktree ? (newBranch ?? branch) : branch,
432
528
  cwd: options.cwd,
@@ -439,7 +535,7 @@ function attachCommandActions(program, options) {
439
535
  throw commanderExit(exitCode);
440
536
  }
441
537
  });
442
- const configCommand = program.commands.find((command) => command.name() === 'config');
538
+ const configCommand = program.commands.find((command) => command.name() === "config");
443
539
  configCommand?.action(async () => {
444
540
  const exitCode = await runConfigCommand({
445
541
  cwd: options.cwd,
@@ -449,9 +545,11 @@ function attachCommandActions(program, options) {
449
545
  throw commanderExit(exitCode);
450
546
  }
451
547
  });
452
- configCommand?.commands.find((command) => command.name() === 'get')?.action(async (key) => {
548
+ configCommand?.commands
549
+ .find((command) => command.name() === "get")
550
+ ?.action(async (key) => {
453
551
  const exitCode = await runConfigCommand({
454
- action: 'get',
552
+ action: "get",
455
553
  cwd: options.cwd,
456
554
  key,
457
555
  stdout: options.stdout,
@@ -461,10 +559,10 @@ function attachCommandActions(program, options) {
461
559
  }
462
560
  });
463
561
  configCommand?.commands
464
- .find((command) => command.name() === 'set')
562
+ .find((command) => command.name() === "set")
465
563
  ?.action(async (key, value) => {
466
564
  const exitCode = await runConfigCommand({
467
- action: 'set',
565
+ action: "set",
468
566
  cwd: options.cwd,
469
567
  key,
470
568
  stdout: options.stdout,
@@ -474,9 +572,11 @@ function attachCommandActions(program, options) {
474
572
  throw commanderExit(exitCode);
475
573
  }
476
574
  });
477
- configCommand?.commands.find((command) => command.name() === 'unset')?.action(async (key) => {
575
+ configCommand?.commands
576
+ .find((command) => command.name() === "unset")
577
+ ?.action(async (key) => {
478
578
  const exitCode = await runConfigCommand({
479
- action: 'unset',
579
+ action: "unset",
480
580
  cwd: options.cwd,
481
581
  key,
482
582
  stdout: options.stdout,
@@ -493,14 +593,14 @@ function notImplemented(commandName) {
493
593
  }
494
594
  function commanderExit(exitCode) {
495
595
  const error = new Error(`Command exited with code ${exitCode}.`);
496
- error.code = 'commander.executeSubCommandAsync';
596
+ error.code = "commander.executeSubCommandAsync";
497
597
  error.exitCode = exitCode;
498
598
  return error;
499
599
  }
500
600
  function isCommanderExit(error) {
501
601
  return (error instanceof Error &&
502
- 'code' in error &&
503
- 'exitCode' in error &&
504
- typeof error.code === 'string' &&
505
- typeof error.exitCode === 'number');
602
+ "code" in error &&
603
+ "exitCode" in error &&
604
+ typeof error.code === "string" &&
605
+ typeof error.exitCode === "number");
506
606
  }