opencode-worktree 0.3.0 → 0.3.2
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 +30 -2
- package/bin/opencode-worktree +0 -3
- package/package.json +2 -1
- package/script/build.ts +2 -0
- package/src/cli.ts +11 -1
- package/src/config.ts +5 -0
- package/src/opencode.ts +20 -11
- package/src/ui.ts +140 -39
package/README.md
CHANGED
|
@@ -43,7 +43,7 @@ opencode-worktree /path/to/your/repo
|
|
|
43
43
|
|
|
44
44
|
- `Up`/`Down` or `j`/`k`: navigate
|
|
45
45
|
- `Enter`: open selected worktree in opencode (or toggle selection in delete mode)
|
|
46
|
-
- `o`: open worktree folder in file manager (
|
|
46
|
+
- `o`: open worktree folder in file manager or custom editor (configurable)
|
|
47
47
|
- `d`: enter multi-select delete mode (press again to confirm deletion)
|
|
48
48
|
- `n`: create new worktree
|
|
49
49
|
- `c`: edit configuration (post-create hooks)
|
|
@@ -67,7 +67,10 @@ When you first run `opencode-worktree` in a repository without a configuration f
|
|
|
67
67
|
|
|
68
68
|
### Editing configuration
|
|
69
69
|
|
|
70
|
-
Press `c` at any time to edit your configuration.
|
|
70
|
+
Press `c` at any time to edit your configuration. You can configure:
|
|
71
|
+
|
|
72
|
+
- **Post-create hook**: Command to run after creating a worktree (e.g., `npm install`)
|
|
73
|
+
- **Open command**: Custom command for opening worktree folders (e.g., `webstorm`, `code`)
|
|
71
74
|
|
|
72
75
|
### Post-create hooks
|
|
73
76
|
|
|
@@ -95,6 +98,31 @@ The hook output is streamed to the TUI in real-time. If the hook fails, you can
|
|
|
95
98
|
}
|
|
96
99
|
```
|
|
97
100
|
|
|
101
|
+
### Custom open command
|
|
102
|
+
|
|
103
|
+
Use a custom command when pressing `o` to open worktree folders. Useful for opening in your preferred IDE.
|
|
104
|
+
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"openCommand": "webstorm"
|
|
108
|
+
}
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
**Examples:**
|
|
112
|
+
|
|
113
|
+
```json
|
|
114
|
+
{
|
|
115
|
+
"openCommand": "code"
|
|
116
|
+
}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
```json
|
|
120
|
+
{
|
|
121
|
+
"postCreateHook": "npm install",
|
|
122
|
+
"openCommand": "webstorm"
|
|
123
|
+
}
|
|
124
|
+
```
|
|
125
|
+
|
|
98
126
|
## Update notifications
|
|
99
127
|
|
|
100
128
|
When a new version is published to npm, the CLI will show a non-intrusive update message on the next run.
|
package/bin/opencode-worktree
CHANGED
|
@@ -5,7 +5,6 @@ import path from 'node:path';
|
|
|
5
5
|
import os from 'node:os';
|
|
6
6
|
import { spawn } from 'node:child_process';
|
|
7
7
|
import { fileURLToPath } from 'node:url';
|
|
8
|
-
import updateNotifier from 'update-notifier';
|
|
9
8
|
|
|
10
9
|
const __filename = fileURLToPath(import.meta.url);
|
|
11
10
|
const __dirname = path.dirname(__filename);
|
|
@@ -13,8 +12,6 @@ const pkg = JSON.parse(
|
|
|
13
12
|
fs.readFileSync(new URL('../package.json', import.meta.url), 'utf8'),
|
|
14
13
|
);
|
|
15
14
|
|
|
16
|
-
updateNotifier({ pkg }).notify({ isGlobal: true });
|
|
17
|
-
|
|
18
15
|
const detectPlatformAndArch = () => {
|
|
19
16
|
let platform;
|
|
20
17
|
switch (os.platform()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "opencode-worktree",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.2",
|
|
4
4
|
"private": false,
|
|
5
5
|
"type": "module",
|
|
6
6
|
"description": "TUI for managing git worktrees with opencode integration.",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"devDependencies": {
|
|
49
49
|
"@types/bun": "^1.2.0",
|
|
50
50
|
"@types/node": "^24.2.0",
|
|
51
|
+
"@types/update-notifier": "^6.0.8",
|
|
51
52
|
"typescript": "^5.9.3"
|
|
52
53
|
}
|
|
53
54
|
}
|
package/script/build.ts
CHANGED
|
@@ -107,6 +107,8 @@ const buildBinary = async (target: Target) => {
|
|
|
107
107
|
OTUI_TREE_SITTER_WORKER_PATH: JSON.stringify(
|
|
108
108
|
bunfsRoot + workerRelativePath,
|
|
109
109
|
),
|
|
110
|
+
__PACKAGE_VERSION__: JSON.stringify(version),
|
|
111
|
+
__PACKAGE_NAME__: JSON.stringify(packageName),
|
|
110
112
|
},
|
|
111
113
|
});
|
|
112
114
|
} catch (error) {
|
package/src/cli.ts
CHANGED
|
@@ -1,9 +1,19 @@
|
|
|
1
1
|
import { runApp } from "./ui.js";
|
|
2
2
|
|
|
3
|
+
// Build-time injected constants (defined in script/build.ts)
|
|
4
|
+
// Use typeof check to provide fallbacks for dev mode (bun run dev)
|
|
5
|
+
declare const __PACKAGE_VERSION__: string | undefined;
|
|
6
|
+
declare const __PACKAGE_NAME__: string | undefined;
|
|
7
|
+
|
|
8
|
+
const pkg = {
|
|
9
|
+
name: typeof __PACKAGE_NAME__ !== "undefined" ? __PACKAGE_NAME__ : "opencode-worktree",
|
|
10
|
+
version: typeof __PACKAGE_VERSION__ !== "undefined" ? __PACKAGE_VERSION__ : "dev",
|
|
11
|
+
};
|
|
12
|
+
|
|
3
13
|
// Accept optional path argument: opencode-worktree [path]
|
|
4
14
|
const targetPath = process.argv[2] || process.cwd();
|
|
5
15
|
|
|
6
|
-
runApp(targetPath).catch((error: unknown) => {
|
|
16
|
+
runApp(targetPath, pkg).catch((error: unknown) => {
|
|
7
17
|
console.error("Failed to start OpenTUI worktree selector.");
|
|
8
18
|
console.error(error);
|
|
9
19
|
process.exit(1);
|
package/src/config.ts
CHANGED
|
@@ -3,6 +3,7 @@ import { join } from "node:path";
|
|
|
3
3
|
|
|
4
4
|
export type Config = {
|
|
5
5
|
postCreateHook?: string;
|
|
6
|
+
openCommand?: string; // Custom command to open worktree folder (e.g., "webstorm", "code")
|
|
6
7
|
};
|
|
7
8
|
|
|
8
9
|
const CONFIG_FILENAME = ".opencode-worktree.json";
|
|
@@ -46,6 +47,10 @@ export const loadRepoConfig = (repoRoot: string): Config => {
|
|
|
46
47
|
config.postCreateHook = parsed.postCreateHook;
|
|
47
48
|
}
|
|
48
49
|
|
|
50
|
+
if (typeof parsed.openCommand === "string") {
|
|
51
|
+
config.openCommand = parsed.openCommand;
|
|
52
|
+
}
|
|
53
|
+
|
|
49
54
|
return config;
|
|
50
55
|
} catch {
|
|
51
56
|
// If we can't read or parse the config, return empty
|
package/src/opencode.ts
CHANGED
|
@@ -20,23 +20,32 @@ export const launchOpenCode = (cwd: string): void => {
|
|
|
20
20
|
};
|
|
21
21
|
|
|
22
22
|
/**
|
|
23
|
-
* Open a path in the system file manager
|
|
23
|
+
* Open a path in the system file manager or with a custom command
|
|
24
|
+
* If customCommand is provided, uses that instead of the system default
|
|
24
25
|
*/
|
|
25
|
-
export const openInFileManager = (path: string): boolean => {
|
|
26
|
-
const platform = process.platform;
|
|
26
|
+
export const openInFileManager = (path: string, customCommand?: string): boolean => {
|
|
27
27
|
let command: string;
|
|
28
28
|
let args: string[];
|
|
29
29
|
|
|
30
|
-
if (
|
|
31
|
-
command
|
|
32
|
-
|
|
33
|
-
} else if (platform === "win32") {
|
|
34
|
-
command = "explorer";
|
|
30
|
+
if (customCommand) {
|
|
31
|
+
// Use custom command (e.g., "webstorm", "code")
|
|
32
|
+
command = customCommand;
|
|
35
33
|
args = [path];
|
|
36
34
|
} else {
|
|
37
|
-
//
|
|
38
|
-
|
|
39
|
-
|
|
35
|
+
// Use system default
|
|
36
|
+
const platform = process.platform;
|
|
37
|
+
|
|
38
|
+
if (platform === "darwin") {
|
|
39
|
+
command = "open";
|
|
40
|
+
args = [path];
|
|
41
|
+
} else if (platform === "win32") {
|
|
42
|
+
command = "explorer";
|
|
43
|
+
args = [path];
|
|
44
|
+
} else {
|
|
45
|
+
// Linux and others
|
|
46
|
+
command = "xdg-open";
|
|
47
|
+
args = [path];
|
|
48
|
+
}
|
|
40
49
|
}
|
|
41
50
|
|
|
42
51
|
try {
|
package/src/ui.ts
CHANGED
|
@@ -10,6 +10,7 @@ import {
|
|
|
10
10
|
type KeyEvent,
|
|
11
11
|
type SelectOption,
|
|
12
12
|
} from "@opentui/core";
|
|
13
|
+
import updateNotifier from "update-notifier";
|
|
13
14
|
import { basename } from "node:path";
|
|
14
15
|
import {
|
|
15
16
|
createWorktree,
|
|
@@ -45,14 +46,22 @@ const CONFIRM_UNLINK_VALUE: ConfirmAction = "unlink";
|
|
|
45
46
|
const CONFIRM_DELETE_VALUE: ConfirmAction = "delete";
|
|
46
47
|
const CONFIRM_CANCEL_VALUE: ConfirmAction = "cancel";
|
|
47
48
|
|
|
48
|
-
export
|
|
49
|
+
export type PackageInfo = {
|
|
50
|
+
name: string;
|
|
51
|
+
version: string;
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export const runApp = async (
|
|
55
|
+
targetPath: string,
|
|
56
|
+
pkg?: PackageInfo,
|
|
57
|
+
): Promise<void> => {
|
|
49
58
|
const renderer = await createCliRenderer({
|
|
50
59
|
exitOnCtrlC: false,
|
|
51
60
|
targetFps: 30,
|
|
52
61
|
});
|
|
53
62
|
|
|
54
63
|
renderer.setBackgroundColor("transparent");
|
|
55
|
-
new WorktreeSelector(renderer, targetPath);
|
|
64
|
+
new WorktreeSelector(renderer, targetPath, pkg);
|
|
56
65
|
};
|
|
57
66
|
|
|
58
67
|
class WorktreeSelector {
|
|
@@ -60,6 +69,7 @@ class WorktreeSelector {
|
|
|
60
69
|
private statusText: TextRenderable;
|
|
61
70
|
private instructions: TextRenderable;
|
|
62
71
|
private title: TextRenderable;
|
|
72
|
+
private versionNotice: TextRenderable | null = null;
|
|
63
73
|
|
|
64
74
|
private inputContainer: BoxRenderable | null = null;
|
|
65
75
|
private branchInput: InputRenderable | null = null;
|
|
@@ -91,12 +101,15 @@ class WorktreeSelector {
|
|
|
91
101
|
// Config editor state
|
|
92
102
|
private isEditingConfig = false;
|
|
93
103
|
private configContainer: BoxRenderable | null = null;
|
|
94
|
-
private
|
|
104
|
+
private configHookInput: InputRenderable | null = null;
|
|
105
|
+
private configOpenInput: InputRenderable | null = null;
|
|
106
|
+
private configActiveField: "hook" | "open" = "hook";
|
|
95
107
|
private isFirstTimeSetup = false;
|
|
96
108
|
|
|
97
109
|
constructor(
|
|
98
110
|
private renderer: CliRenderer,
|
|
99
111
|
private targetPath: string,
|
|
112
|
+
private pkg?: PackageInfo,
|
|
100
113
|
) {
|
|
101
114
|
// Load worktrees first to get initial options
|
|
102
115
|
this.repoRoot = resolveRepoRoot(this.targetPath);
|
|
@@ -113,6 +126,34 @@ class WorktreeSelector {
|
|
|
113
126
|
});
|
|
114
127
|
this.renderer.root.add(this.title);
|
|
115
128
|
|
|
129
|
+
// Display version or update notification in title line
|
|
130
|
+
if (this.pkg) {
|
|
131
|
+
const notifier = updateNotifier({ pkg: this.pkg });
|
|
132
|
+
|
|
133
|
+
let noticeContent: string;
|
|
134
|
+
let noticeColor: string;
|
|
135
|
+
|
|
136
|
+
if (notifier.update) {
|
|
137
|
+
// Update available
|
|
138
|
+
noticeContent = `Update: ${notifier.update.current} → ${notifier.update.latest} (npm i -g)`;
|
|
139
|
+
noticeColor = "#F59E0B"; // Amber
|
|
140
|
+
} else {
|
|
141
|
+
// On latest version
|
|
142
|
+
noticeContent = `v${this.pkg.version}`;
|
|
143
|
+
noticeColor = "#64748B"; // Subtle gray
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
this.versionNotice = new TextRenderable(renderer, {
|
|
147
|
+
id: "version-notice",
|
|
148
|
+
position: "absolute",
|
|
149
|
+
left: 78 - noticeContent.length,
|
|
150
|
+
top: 1,
|
|
151
|
+
content: noticeContent,
|
|
152
|
+
fg: noticeColor,
|
|
153
|
+
});
|
|
154
|
+
this.renderer.root.add(this.versionNotice);
|
|
155
|
+
}
|
|
156
|
+
|
|
116
157
|
this.selectElement = new SelectRenderable(renderer, {
|
|
117
158
|
id: "worktree-selector",
|
|
118
159
|
position: "absolute",
|
|
@@ -263,8 +304,21 @@ class WorktreeSelector {
|
|
|
263
304
|
return;
|
|
264
305
|
}
|
|
265
306
|
if (key.name === "return") {
|
|
266
|
-
|
|
267
|
-
|
|
307
|
+
this.handleConfigSave();
|
|
308
|
+
return;
|
|
309
|
+
}
|
|
310
|
+
if (key.name === "tab") {
|
|
311
|
+
// Switch between fields
|
|
312
|
+
if (this.configActiveField === "hook") {
|
|
313
|
+
this.configActiveField = "open";
|
|
314
|
+
this.configHookInput?.blur();
|
|
315
|
+
this.configOpenInput?.focus();
|
|
316
|
+
} else {
|
|
317
|
+
this.configActiveField = "hook";
|
|
318
|
+
this.configOpenInput?.blur();
|
|
319
|
+
this.configHookInput?.focus();
|
|
320
|
+
}
|
|
321
|
+
this.renderer.requestRender();
|
|
268
322
|
return;
|
|
269
323
|
}
|
|
270
324
|
return;
|
|
@@ -350,9 +404,17 @@ class WorktreeSelector {
|
|
|
350
404
|
return;
|
|
351
405
|
}
|
|
352
406
|
|
|
353
|
-
|
|
407
|
+
// Load config to check for custom open command
|
|
408
|
+
const config = this.repoRoot ? loadRepoConfig(this.repoRoot) : {};
|
|
409
|
+
const customCommand = config.openCommand;
|
|
410
|
+
|
|
411
|
+
const success = openInFileManager(worktree.path, customCommand);
|
|
354
412
|
if (success) {
|
|
355
|
-
|
|
413
|
+
if (customCommand) {
|
|
414
|
+
this.setStatus(`Opened ${worktree.path} with ${customCommand}.`, "success");
|
|
415
|
+
} else {
|
|
416
|
+
this.setStatus(`Opened ${worktree.path} in file manager.`, "success");
|
|
417
|
+
}
|
|
356
418
|
} else {
|
|
357
419
|
this.setStatus("Failed to open file manager.", "error");
|
|
358
420
|
}
|
|
@@ -658,6 +720,7 @@ class WorktreeSelector {
|
|
|
658
720
|
}
|
|
659
721
|
|
|
660
722
|
this.isEditingConfig = true;
|
|
723
|
+
this.configActiveField = "hook";
|
|
661
724
|
this.selectElement.visible = false;
|
|
662
725
|
this.selectElement.blur();
|
|
663
726
|
|
|
@@ -665,8 +728,8 @@ class WorktreeSelector {
|
|
|
665
728
|
const existingConfig = loadRepoConfig(this.repoRoot);
|
|
666
729
|
|
|
667
730
|
const title = this.isFirstTimeSetup
|
|
668
|
-
? "First-time Setup:
|
|
669
|
-
: "Edit
|
|
731
|
+
? "First-time Setup: Project Configuration"
|
|
732
|
+
: "Edit Project Configuration";
|
|
670
733
|
|
|
671
734
|
this.configContainer = new BoxRenderable(this.renderer, {
|
|
672
735
|
id: "config-container",
|
|
@@ -674,7 +737,7 @@ class WorktreeSelector {
|
|
|
674
737
|
left: 2,
|
|
675
738
|
top: 3,
|
|
676
739
|
width: 76,
|
|
677
|
-
height:
|
|
740
|
+
height: 12,
|
|
678
741
|
borderStyle: "single",
|
|
679
742
|
borderColor: "#38BDF8",
|
|
680
743
|
title,
|
|
@@ -684,50 +747,76 @@ class WorktreeSelector {
|
|
|
684
747
|
});
|
|
685
748
|
this.renderer.root.add(this.configContainer);
|
|
686
749
|
|
|
687
|
-
|
|
688
|
-
|
|
750
|
+
// Post-create hook field
|
|
751
|
+
const hookLabel = new TextRenderable(this.renderer, {
|
|
752
|
+
id: "config-hook-label",
|
|
689
753
|
position: "absolute",
|
|
690
754
|
left: 1,
|
|
691
755
|
top: 1,
|
|
692
|
-
content: "
|
|
756
|
+
content: "Post-create hook (e.g., npm install):",
|
|
693
757
|
fg: "#94A3B8",
|
|
694
758
|
});
|
|
695
|
-
this.configContainer.add(
|
|
759
|
+
this.configContainer.add(hookLabel);
|
|
760
|
+
|
|
761
|
+
this.configHookInput = new InputRenderable(this.renderer, {
|
|
762
|
+
id: "config-hook-input",
|
|
763
|
+
position: "absolute",
|
|
764
|
+
left: 1,
|
|
765
|
+
top: 2,
|
|
766
|
+
width: 72,
|
|
767
|
+
placeholder: "npm install",
|
|
768
|
+
value: existingConfig.postCreateHook || "",
|
|
769
|
+
focusedBackgroundColor: "#1E293B",
|
|
770
|
+
backgroundColor: "#1E293B",
|
|
771
|
+
});
|
|
772
|
+
this.configContainer.add(this.configHookInput);
|
|
696
773
|
|
|
697
|
-
|
|
698
|
-
|
|
774
|
+
// Open command field
|
|
775
|
+
const openLabel = new TextRenderable(this.renderer, {
|
|
776
|
+
id: "config-open-label",
|
|
699
777
|
position: "absolute",
|
|
700
778
|
left: 1,
|
|
701
779
|
top: 4,
|
|
702
|
-
content: "
|
|
703
|
-
fg: "#
|
|
780
|
+
content: "Open folder command (e.g., webstorm, code):",
|
|
781
|
+
fg: "#94A3B8",
|
|
704
782
|
});
|
|
705
|
-
this.configContainer.add(
|
|
783
|
+
this.configContainer.add(openLabel);
|
|
706
784
|
|
|
707
|
-
this.
|
|
708
|
-
id: "config-
|
|
785
|
+
this.configOpenInput = new InputRenderable(this.renderer, {
|
|
786
|
+
id: "config-open-input",
|
|
709
787
|
position: "absolute",
|
|
710
788
|
left: 1,
|
|
711
|
-
top:
|
|
789
|
+
top: 5,
|
|
712
790
|
width: 72,
|
|
713
|
-
placeholder: "
|
|
714
|
-
value: existingConfig.
|
|
791
|
+
placeholder: "open (default)",
|
|
792
|
+
value: existingConfig.openCommand || "",
|
|
715
793
|
focusedBackgroundColor: "#1E293B",
|
|
716
794
|
backgroundColor: "#1E293B",
|
|
717
795
|
});
|
|
718
|
-
this.configContainer.add(this.
|
|
796
|
+
this.configContainer.add(this.configOpenInput);
|
|
797
|
+
|
|
798
|
+
// Help text
|
|
799
|
+
const helpText = new TextRenderable(this.renderer, {
|
|
800
|
+
id: "config-help",
|
|
801
|
+
position: "absolute",
|
|
802
|
+
left: 1,
|
|
803
|
+
top: 7,
|
|
804
|
+
content: "Tab to switch fields • Leave empty to use defaults",
|
|
805
|
+
fg: "#64748B",
|
|
806
|
+
});
|
|
807
|
+
this.configContainer.add(helpText);
|
|
719
808
|
|
|
720
|
-
this.instructions.content = "Enter
|
|
809
|
+
this.instructions.content = "Tab switch • Enter save • Esc cancel";
|
|
721
810
|
this.setStatus(
|
|
722
811
|
this.isFirstTimeSetup
|
|
723
|
-
? "Welcome! Configure your
|
|
724
|
-
: "Edit
|
|
812
|
+
? "Welcome! Configure your project settings."
|
|
813
|
+
: "Edit project configuration.",
|
|
725
814
|
"info"
|
|
726
815
|
);
|
|
727
816
|
|
|
728
817
|
// Delay focus to prevent the triggering keypress from being captured
|
|
729
818
|
setTimeout(() => {
|
|
730
|
-
this.
|
|
819
|
+
this.configHookInput?.focus();
|
|
731
820
|
this.renderer.requestRender();
|
|
732
821
|
}, 0);
|
|
733
822
|
}
|
|
@@ -736,14 +825,18 @@ class WorktreeSelector {
|
|
|
736
825
|
this.isEditingConfig = false;
|
|
737
826
|
this.isFirstTimeSetup = false;
|
|
738
827
|
|
|
739
|
-
if (this.
|
|
740
|
-
this.
|
|
828
|
+
if (this.configHookInput) {
|
|
829
|
+
this.configHookInput.blur();
|
|
830
|
+
}
|
|
831
|
+
if (this.configOpenInput) {
|
|
832
|
+
this.configOpenInput.blur();
|
|
741
833
|
}
|
|
742
834
|
|
|
743
835
|
if (this.configContainer) {
|
|
744
836
|
this.renderer.root.remove(this.configContainer.id);
|
|
745
837
|
this.configContainer = null;
|
|
746
|
-
this.
|
|
838
|
+
this.configHookInput = null;
|
|
839
|
+
this.configOpenInput = null;
|
|
747
840
|
}
|
|
748
841
|
|
|
749
842
|
this.selectElement.visible = true;
|
|
@@ -757,27 +850,35 @@ class WorktreeSelector {
|
|
|
757
850
|
}, 0);
|
|
758
851
|
}
|
|
759
852
|
|
|
760
|
-
private handleConfigSave(
|
|
853
|
+
private handleConfigSave(): void {
|
|
761
854
|
if (!this.repoRoot) {
|
|
762
855
|
this.setStatus("No git repository found.", "error");
|
|
763
856
|
this.hideConfigEditor();
|
|
764
857
|
return;
|
|
765
858
|
}
|
|
766
859
|
|
|
767
|
-
const
|
|
860
|
+
const hookValue = (this.configHookInput?.value || "").trim();
|
|
861
|
+
const openValue = (this.configOpenInput?.value || "").trim();
|
|
768
862
|
const config: Config = {};
|
|
769
863
|
|
|
770
|
-
if (
|
|
771
|
-
config.postCreateHook =
|
|
864
|
+
if (hookValue) {
|
|
865
|
+
config.postCreateHook = hookValue;
|
|
866
|
+
}
|
|
867
|
+
if (openValue) {
|
|
868
|
+
config.openCommand = openValue;
|
|
772
869
|
}
|
|
773
870
|
|
|
774
871
|
const success = saveRepoConfig(this.repoRoot, config);
|
|
775
872
|
|
|
776
873
|
if (success) {
|
|
777
|
-
|
|
778
|
-
|
|
874
|
+
const changes: string[] = [];
|
|
875
|
+
if (hookValue) changes.push(`hook: "${hookValue}"`);
|
|
876
|
+
if (openValue) changes.push(`open: "${openValue}"`);
|
|
877
|
+
|
|
878
|
+
if (changes.length > 0) {
|
|
879
|
+
this.setStatus(`Config saved: ${changes.join(", ")}`, "success");
|
|
779
880
|
} else {
|
|
780
|
-
this.setStatus("
|
|
881
|
+
this.setStatus("Config cleared.", "success");
|
|
781
882
|
}
|
|
782
883
|
} else {
|
|
783
884
|
this.setStatus("Failed to save config.", "error");
|