@sven1103/opencode-worktree-workflow 0.6.0 → 0.6.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.
Files changed (3) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +44 -1
  3. package/src/index.js +33 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sven1103/opencode-worktree-workflow",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "description": "OpenCode plugin for creating and cleaning up git worktrees.",
5
5
  "type": "module",
6
6
  "main": "./src/index.js",
package/src/cli.js CHANGED
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
 
3
3
  import { execFile } from "node:child_process";
4
+ import fs from "node:fs";
4
5
  import { fileURLToPath } from "node:url";
5
6
  import { promisify } from "node:util";
6
7
 
@@ -68,6 +69,31 @@ function printUsage() {
68
69
  );
69
70
  }
70
71
 
72
+ function printSubcommandUsage(command) {
73
+ if (command === "wt-new") {
74
+ process.stdout.write(
75
+ [
76
+ "Usage:",
77
+ " opencode-worktree-workflow wt-new <title> [--json]",
78
+ "",
79
+ "Create a synced worktree and branch from the configured base branch.",
80
+ ].join("\n") + "\n",
81
+ );
82
+ return;
83
+ }
84
+
85
+ if (command === "wt-clean") {
86
+ process.stdout.write(
87
+ [
88
+ "Usage:",
89
+ " opencode-worktree-workflow wt-clean [preview|apply] [selectors...] [--json]",
90
+ "",
91
+ "Preview connected worktrees or remove safe and explicitly selected review worktrees.",
92
+ ].join("\n") + "\n",
93
+ );
94
+ }
95
+ }
96
+
71
97
  export function parseCliArgs(argv) {
72
98
  const outputJson = argv.includes("--json");
73
99
  const args = argv.filter((arg) => arg !== "--json");
@@ -84,6 +110,11 @@ export async function run(argv = process.argv.slice(2)) {
84
110
  return;
85
111
  }
86
112
 
113
+ if ((command === "wt-new" || command === "wt-clean") && rest.some((arg) => arg === "--help" || arg === "-h" || arg === "help")) {
114
+ printSubcommandUsage(command);
115
+ return;
116
+ }
117
+
87
118
  const plugin = await WorktreeWorkflowPlugin({
88
119
  $: createShell(process.cwd()),
89
120
  directory: process.cwd(),
@@ -120,7 +151,19 @@ export async function run(argv = process.argv.slice(2)) {
120
151
  process.stdout.write(`${result.message || JSON.stringify(result, null, 2)}\n`);
121
152
  }
122
153
 
123
- const invokedAsScript = process.argv[1] && fileURLToPath(import.meta.url) === process.argv[1];
154
+ export function isInvokedAsScript(argvPath = process.argv[1]) {
155
+ if (!argvPath) {
156
+ return false;
157
+ }
158
+
159
+ try {
160
+ return fs.realpathSync(argvPath) === fileURLToPath(import.meta.url);
161
+ } catch {
162
+ return fileURLToPath(import.meta.url) === argvPath;
163
+ }
164
+ }
165
+
166
+ const invokedAsScript = isInvokedAsScript();
124
167
 
125
168
  if (invokedAsScript) {
126
169
  run().catch((error) => {
package/src/index.js CHANGED
@@ -24,6 +24,14 @@ async function pathExists(targetPath) {
24
24
  }
25
25
  }
26
26
 
27
+ function isMissingGitRepositoryError(message) {
28
+ return /not a git repository/i.test(message);
29
+ }
30
+
31
+ function isMissingRemoteError(message, remote) {
32
+ return new RegExp(`No such remote:?\s+${remote}|does not appear to be a git repository|Could not read from remote repository`, "i").test(message);
33
+ }
34
+
27
35
  async function readJsonFile(filePath) {
28
36
  if (!(await pathExists(filePath))) {
29
37
  return null;
@@ -376,6 +384,8 @@ export const __internal = {
376
384
  buildCleanupPreviewResult,
377
385
  buildPrepareResult,
378
386
  classifyEntry,
387
+ isMissingGitRepositoryError,
388
+ isMissingRemoteError,
379
389
  parseCleanupRawArguments,
380
390
  normalizeCleanupArgs,
381
391
  toStructuredCleanupFailure,
@@ -470,8 +480,18 @@ export const WorktreeWorkflowPlugin = async ({ $, directory }) => {
470
480
  }
471
481
 
472
482
  async function getRepoRoot() {
473
- const result = await git(["rev-parse", "--show-toplevel"]);
474
- return result.stdout;
483
+ try {
484
+ const result = await git(["rev-parse", "--show-toplevel"]);
485
+ return result.stdout;
486
+ } catch (error) {
487
+ if (isMissingGitRepositoryError(error.message || "")) {
488
+ throw new Error(
489
+ "This command must run inside a git repository. Initialize a repository first or run it from an existing repo root.",
490
+ );
491
+ }
492
+
493
+ throw error;
494
+ }
475
495
  }
476
496
 
477
497
  async function loadWorkflowConfig(repoRoot) {
@@ -557,7 +577,17 @@ export const WorktreeWorkflowPlugin = async ({ $, directory }) => {
557
577
  }
558
578
 
559
579
  async function getBaseRef(repoRoot, remote, baseBranch) {
560
- await git(["fetch", "--prune", remote, baseBranch], { cwd: repoRoot });
580
+ try {
581
+ await git(["fetch", "--prune", remote, baseBranch], { cwd: repoRoot });
582
+ } catch (error) {
583
+ if (isMissingRemoteError(error.message || "", remote)) {
584
+ throw new Error(
585
+ `Could not fetch base branch information from remote \"${remote}\". Configure the expected remote in .opencode/worktree-workflow.json or add that remote to this repository.`,
586
+ );
587
+ }
588
+
589
+ throw error;
590
+ }
561
591
 
562
592
  const remoteRef = `refs/remotes/${remote}/${baseBranch}`;
563
593
  const remoteExists = await git(["show-ref", "--verify", "--quiet", remoteRef], {