@camaradesuk/git-worktree-tools 1.7.0 → 1.8.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 +69 -8
- package/dist/cli/cleanpr.js +11 -5
- package/dist/cli/cleanpr.js.map +1 -1
- package/dist/cli/cleanpr.test.js +10 -1
- package/dist/cli/cleanpr.test.js.map +1 -1
- package/dist/cli/newpr.js +100 -28
- package/dist/cli/newpr.js.map +1 -1
- package/dist/cli/newpr.test.js +24 -6
- package/dist/cli/newpr.test.js.map +1 -1
- package/dist/cli/prs.d.ts +22 -0
- package/dist/cli/prs.d.ts.map +1 -0
- package/dist/cli/prs.js +275 -0
- package/dist/cli/prs.js.map +1 -0
- package/dist/cli/prs.test.d.ts +8 -0
- package/dist/cli/prs.test.d.ts.map +1 -0
- package/dist/cli/prs.test.js +408 -0
- package/dist/cli/prs.test.js.map +1 -0
- package/dist/cli/wt/interactive-menu.d.ts +2 -0
- package/dist/cli/wt/interactive-menu.d.ts.map +1 -1
- package/dist/cli/wt/interactive-menu.js +17 -0
- package/dist/cli/wt/interactive-menu.js.map +1 -1
- package/dist/cli/wt/interactive-menu.test.js +25 -0
- package/dist/cli/wt/interactive-menu.test.js.map +1 -1
- package/dist/cli/wt/prs.d.ts +21 -0
- package/dist/cli/wt/prs.d.ts.map +1 -0
- package/dist/cli/wt/prs.js +251 -0
- package/dist/cli/wt/prs.js.map +1 -0
- package/dist/cli/wt/prs.test.d.ts +5 -0
- package/dist/cli/wt/prs.test.d.ts.map +1 -0
- package/dist/cli/wt/prs.test.js +410 -0
- package/dist/cli/wt/prs.test.js.map +1 -0
- package/dist/cli/wt.d.ts +1 -0
- package/dist/cli/wt.d.ts.map +1 -1
- package/dist/cli/wt.js +5 -0
- package/dist/cli/wt.js.map +1 -1
- package/dist/cli/wtconfig.test.js +3 -0
- package/dist/cli/wtconfig.test.js.map +1 -1
- package/dist/cli/wtlink.d.ts +2 -1
- package/dist/cli/wtlink.d.ts.map +1 -1
- package/dist/cli/wtlink.js +41 -2
- package/dist/cli/wtlink.js.map +1 -1
- package/dist/e2e/cli.e2e.test.js +6 -2
- package/dist/e2e/cli.e2e.test.js.map +1 -1
- package/dist/e2e/helpers/cli-runner.d.ts.map +1 -1
- package/dist/e2e/helpers/cli-runner.js +1 -0
- package/dist/e2e/helpers/cli-runner.js.map +1 -1
- package/dist/e2e/helpers/gh-mock.d.ts.map +1 -1
- package/dist/e2e/helpers/gh-mock.js +55 -1
- package/dist/e2e/helpers/gh-mock.js.map +1 -1
- package/dist/e2e/helpers/pty-wrapper.d.ts +15 -0
- package/dist/e2e/helpers/pty-wrapper.d.ts.map +1 -1
- package/dist/e2e/helpers/pty-wrapper.js +65 -0
- package/dist/e2e/helpers/pty-wrapper.js.map +1 -1
- package/dist/e2e/newpr-full-flow.e2e.test.js +1 -0
- package/dist/e2e/newpr-full-flow.e2e.test.js.map +1 -1
- package/dist/e2e/prs/prs.e2e.test.d.ts +7 -0
- package/dist/e2e/prs/prs.e2e.test.d.ts.map +1 -0
- package/dist/e2e/prs/prs.e2e.test.js +606 -0
- package/dist/e2e/prs/prs.e2e.test.js.map +1 -0
- package/dist/e2e/wt/interactive-menu.e2e.test.d.ts +8 -0
- package/dist/e2e/wt/interactive-menu.e2e.test.d.ts.map +1 -0
- package/dist/e2e/wt/interactive-menu.e2e.test.js +583 -0
- package/dist/e2e/wt/interactive-menu.e2e.test.js.map +1 -0
- package/dist/e2e/wt/wt.e2e.test.js +217 -4
- package/dist/e2e/wt/wt.e2e.test.js.map +1 -1
- package/dist/integration/prs.integration.test.d.ts +8 -0
- package/dist/integration/prs.integration.test.d.ts.map +1 -0
- package/dist/integration/prs.integration.test.js +478 -0
- package/dist/integration/prs.integration.test.js.map +1 -0
- package/dist/lib/config-validation.d.ts.map +1 -1
- package/dist/lib/config-validation.js +56 -0
- package/dist/lib/config-validation.js.map +1 -1
- package/dist/lib/config.d.ts +34 -0
- package/dist/lib/config.d.ts.map +1 -1
- package/dist/lib/config.js +10 -0
- package/dist/lib/config.js.map +1 -1
- package/dist/lib/git.d.ts +32 -0
- package/dist/lib/git.d.ts.map +1 -1
- package/dist/lib/git.js +95 -1
- package/dist/lib/git.js.map +1 -1
- package/dist/lib/git.test.js +118 -1
- package/dist/lib/git.test.js.map +1 -1
- package/dist/lib/github.d.ts +41 -0
- package/dist/lib/github.d.ts.map +1 -1
- package/dist/lib/github.js +109 -0
- package/dist/lib/github.js.map +1 -1
- package/dist/lib/global-check.d.ts +2 -2
- package/dist/lib/global-check.js +3 -3
- package/dist/lib/global-check.js.map +1 -1
- package/dist/lib/global-check.test.js +3 -6
- package/dist/lib/global-check.test.js.map +1 -1
- package/dist/lib/json-output.d.ts +1 -0
- package/dist/lib/json-output.d.ts.map +1 -1
- package/dist/lib/json-output.js +2 -0
- package/dist/lib/json-output.js.map +1 -1
- package/dist/lib/lswt/action-executors.d.ts.map +1 -1
- package/dist/lib/lswt/action-executors.js +2 -1
- package/dist/lib/lswt/action-executors.js.map +1 -1
- package/dist/lib/lswt/action-executors.test.js +1 -0
- package/dist/lib/lswt/action-executors.test.js.map +1 -1
- package/dist/lib/lswt/actions.d.ts +1 -0
- package/dist/lib/lswt/actions.d.ts.map +1 -1
- package/dist/lib/lswt/actions.js +38 -10
- package/dist/lib/lswt/actions.js.map +1 -1
- package/dist/lib/lswt/actions.test.js +34 -22
- package/dist/lib/lswt/actions.test.js.map +1 -1
- package/dist/lib/lswt/interactive.js +8 -8
- package/dist/lib/lswt/interactive.js.map +1 -1
- package/dist/lib/prompts.d.ts.map +1 -1
- package/dist/lib/prompts.js +16 -12
- package/dist/lib/prompts.js.map +1 -1
- package/dist/lib/prs/actions.d.ts +70 -0
- package/dist/lib/prs/actions.d.ts.map +1 -0
- package/dist/lib/prs/actions.js +444 -0
- package/dist/lib/prs/actions.js.map +1 -0
- package/dist/lib/prs/actions.test.d.ts +5 -0
- package/dist/lib/prs/actions.test.d.ts.map +1 -0
- package/dist/lib/prs/actions.test.js +313 -0
- package/dist/lib/prs/actions.test.js.map +1 -0
- package/dist/lib/prs/data.d.ts +48 -0
- package/dist/lib/prs/data.d.ts.map +1 -0
- package/dist/lib/prs/data.js +171 -0
- package/dist/lib/prs/data.js.map +1 -0
- package/dist/lib/prs/data.test.d.ts +5 -0
- package/dist/lib/prs/data.test.d.ts.map +1 -0
- package/dist/lib/prs/data.test.js +417 -0
- package/dist/lib/prs/data.test.js.map +1 -0
- package/dist/lib/prs/details.d.ts +57 -0
- package/dist/lib/prs/details.d.ts.map +1 -0
- package/dist/lib/prs/details.js +246 -0
- package/dist/lib/prs/details.js.map +1 -0
- package/dist/lib/prs/details.test.d.ts +5 -0
- package/dist/lib/prs/details.test.d.ts.map +1 -0
- package/dist/lib/prs/details.test.js +325 -0
- package/dist/lib/prs/details.test.js.map +1 -0
- package/dist/lib/prs/filters.d.ts +56 -0
- package/dist/lib/prs/filters.d.ts.map +1 -0
- package/dist/lib/prs/filters.js +171 -0
- package/dist/lib/prs/filters.js.map +1 -0
- package/dist/lib/prs/filters.test.d.ts +5 -0
- package/dist/lib/prs/filters.test.d.ts.map +1 -0
- package/dist/lib/prs/filters.test.js +312 -0
- package/dist/lib/prs/filters.test.js.map +1 -0
- package/dist/lib/prs/formatters.d.ts +83 -0
- package/dist/lib/prs/formatters.d.ts.map +1 -0
- package/dist/lib/prs/formatters.js +389 -0
- package/dist/lib/prs/formatters.js.map +1 -0
- package/dist/lib/prs/formatters.test.d.ts +2 -0
- package/dist/lib/prs/formatters.test.d.ts.map +1 -0
- package/dist/lib/prs/formatters.test.js +387 -0
- package/dist/lib/prs/formatters.test.js.map +1 -0
- package/dist/lib/prs/interactive.d.ts +50 -0
- package/dist/lib/prs/interactive.d.ts.map +1 -0
- package/dist/lib/prs/interactive.js +453 -0
- package/dist/lib/prs/interactive.js.map +1 -0
- package/dist/lib/prs/interactive.test.d.ts +5 -0
- package/dist/lib/prs/interactive.test.d.ts.map +1 -0
- package/dist/lib/prs/interactive.test.js +364 -0
- package/dist/lib/prs/interactive.test.js.map +1 -0
- package/dist/lib/prs/types.d.ts +152 -0
- package/dist/lib/prs/types.d.ts.map +1 -0
- package/dist/lib/prs/types.js +17 -0
- package/dist/lib/prs/types.js.map +1 -0
- package/dist/lib/wtlink/config-manifest.d.ts +101 -0
- package/dist/lib/wtlink/config-manifest.d.ts.map +1 -0
- package/dist/lib/wtlink/config-manifest.js +219 -0
- package/dist/lib/wtlink/config-manifest.js.map +1 -0
- package/dist/lib/wtlink/config-manifest.test.d.ts +2 -0
- package/dist/lib/wtlink/config-manifest.test.d.ts.map +1 -0
- package/dist/lib/wtlink/config-manifest.test.js +460 -0
- package/dist/lib/wtlink/config-manifest.test.js.map +1 -0
- package/dist/lib/wtlink/link-configs.d.ts.map +1 -1
- package/dist/lib/wtlink/link-configs.js +36 -11
- package/dist/lib/wtlink/link-configs.js.map +1 -1
- package/dist/lib/wtlink/main-menu.d.ts.map +1 -1
- package/dist/lib/wtlink/main-menu.js +58 -50
- package/dist/lib/wtlink/main-menu.js.map +1 -1
- package/dist/lib/wtlink/main-menu.test.js +42 -40
- package/dist/lib/wtlink/main-menu.test.js.map +1 -1
- package/dist/lib/wtlink/manage-manifest.d.ts +9 -0
- package/dist/lib/wtlink/manage-manifest.d.ts.map +1 -1
- package/dist/lib/wtlink/manage-manifest.js +346 -25
- package/dist/lib/wtlink/manage-manifest.js.map +1 -1
- package/dist/lib/wtlink/manage-manifest.test.js +52 -1
- package/dist/lib/wtlink/manage-manifest.test.js.map +1 -1
- package/dist/lib/wtlink/validate-manifest.d.ts.map +1 -1
- package/dist/lib/wtlink/validate-manifest.js +27 -6
- package/dist/lib/wtlink/validate-manifest.js.map +1 -1
- package/package.json +1 -1
- package/schemas/worktreerc.schema.json +26 -1
|
@@ -7,6 +7,8 @@ import * as colors from '../colors.js';
|
|
|
7
7
|
import * as git from '../git.js';
|
|
8
8
|
import * as readline from 'readline';
|
|
9
9
|
import { signal, computed } from '@preact/signals-core';
|
|
10
|
+
import { DEFAULT_MANIFEST_FILE } from '../constants.js';
|
|
11
|
+
import { loadManifestData, saveManifestData } from './config-manifest.js';
|
|
10
12
|
// ============================================================================
|
|
11
13
|
// PURE FUNCTIONS - State computation and derivation
|
|
12
14
|
// ============================================================================
|
|
@@ -89,6 +91,22 @@ export function isItemVisible(item, activeFilters) {
|
|
|
89
91
|
}
|
|
90
92
|
return false;
|
|
91
93
|
}
|
|
94
|
+
/**
|
|
95
|
+
* Execute vim command and return action
|
|
96
|
+
* Parses vim-style commands like :w, :q, :wq, :x, :q!
|
|
97
|
+
*/
|
|
98
|
+
export function executeVimCommand(cmd) {
|
|
99
|
+
const trimmed = cmd.trim().toLowerCase();
|
|
100
|
+
if (trimmed === 'q')
|
|
101
|
+
return 'quit';
|
|
102
|
+
if (trimmed === 'q!')
|
|
103
|
+
return 'force-quit';
|
|
104
|
+
if (trimmed === 'w')
|
|
105
|
+
return 'save';
|
|
106
|
+
if (trimmed === 'wq' || trimmed === 'x')
|
|
107
|
+
return 'save-quit';
|
|
108
|
+
return null; // Unknown command
|
|
109
|
+
}
|
|
92
110
|
/**
|
|
93
111
|
* Build file tree from flat list of file paths
|
|
94
112
|
*/
|
|
@@ -550,6 +568,23 @@ function renderHelp() {
|
|
|
550
568
|
console.log(colors.cyan('║') +
|
|
551
569
|
' ' +
|
|
552
570
|
colors.cyan('║'));
|
|
571
|
+
console.log(colors.cyan('║') +
|
|
572
|
+
' ' +
|
|
573
|
+
colors.bold('Navigation:') +
|
|
574
|
+
' ' +
|
|
575
|
+
colors.cyan('║'));
|
|
576
|
+
console.log(colors.cyan('║') +
|
|
577
|
+
' ↑/↓ or j/k = Navigate up/down ' +
|
|
578
|
+
colors.cyan('║'));
|
|
579
|
+
console.log(colors.cyan('║') +
|
|
580
|
+
' ←/Esc = Go back (or exit at root) ' +
|
|
581
|
+
colors.cyan('║'));
|
|
582
|
+
console.log(colors.cyan('║') +
|
|
583
|
+
' →/Enter = Drill into folder ' +
|
|
584
|
+
colors.cyan('║'));
|
|
585
|
+
console.log(colors.cyan('║') +
|
|
586
|
+
' ' +
|
|
587
|
+
colors.cyan('║'));
|
|
553
588
|
console.log(colors.cyan('║') +
|
|
554
589
|
' ' +
|
|
555
590
|
colors.bold('View Toggles:') +
|
|
@@ -579,10 +614,33 @@ function renderHelp() {
|
|
|
579
614
|
' ' +
|
|
580
615
|
colors.cyan('║'));
|
|
581
616
|
console.log(colors.cyan('║') +
|
|
582
|
-
' Q
|
|
617
|
+
' Q = Quit (confirms if unsaved changes) ' +
|
|
583
618
|
colors.cyan('║'));
|
|
584
619
|
console.log(colors.cyan('║') +
|
|
585
|
-
'
|
|
620
|
+
' Ctrl+C = Force quit without saving ' +
|
|
621
|
+
colors.cyan('║'));
|
|
622
|
+
console.log(colors.cyan('║') +
|
|
623
|
+
' ' +
|
|
624
|
+
colors.cyan('║'));
|
|
625
|
+
console.log(colors.cyan('║') +
|
|
626
|
+
' ' +
|
|
627
|
+
colors.bold('Vim Commands:') +
|
|
628
|
+
' ' +
|
|
629
|
+
colors.cyan('║'));
|
|
630
|
+
console.log(colors.cyan('║') +
|
|
631
|
+
' : = Enter command mode ' +
|
|
632
|
+
colors.cyan('║'));
|
|
633
|
+
console.log(colors.cyan('║') +
|
|
634
|
+
' :w = Save changes (stage for exit) ' +
|
|
635
|
+
colors.cyan('║'));
|
|
636
|
+
console.log(colors.cyan('║') +
|
|
637
|
+
' :q = Quit (confirms if unsaved) ' +
|
|
638
|
+
colors.cyan('║'));
|
|
639
|
+
console.log(colors.cyan('║') +
|
|
640
|
+
' :wq/:x = Save and quit ' +
|
|
641
|
+
colors.cyan('║'));
|
|
642
|
+
console.log(colors.cyan('║') +
|
|
643
|
+
' :q! = Force quit without saving ' +
|
|
586
644
|
colors.cyan('║'));
|
|
587
645
|
console.log(colors.cyan('║') +
|
|
588
646
|
' ' +
|
|
@@ -703,11 +761,14 @@ function renderFooter(state) {
|
|
|
703
761
|
console.log('\n' + colors.dim('─'.repeat(110)));
|
|
704
762
|
// Navigation - spread out with better formatting
|
|
705
763
|
const navParts = [];
|
|
706
|
-
navParts.push(colors.dim('
|
|
764
|
+
navParts.push(colors.dim('↑↓/jk') + ' Navigate');
|
|
707
765
|
if (state.viewMode === 'hierarchical') {
|
|
708
|
-
navParts.push(colors.dim('
|
|
766
|
+
navParts.push(colors.dim('←/esc') + ' Back');
|
|
709
767
|
navParts.push(colors.dim('→') + ' Drill In');
|
|
710
768
|
}
|
|
769
|
+
else {
|
|
770
|
+
navParts.push(colors.dim('esc') + ' Exit');
|
|
771
|
+
}
|
|
711
772
|
const navigation = colors.bold('Navigation: ') + navParts.join(colors.dim(' │ '));
|
|
712
773
|
// Actions - colorful and spaced out
|
|
713
774
|
const actions = colors.bold('Actions: ') +
|
|
@@ -730,16 +791,16 @@ function renderFooter(state) {
|
|
|
730
791
|
' Skipped');
|
|
731
792
|
filterParts.push((state.viewMode === 'flat' ? colors.bold(colors.cyan('V')) : colors.dim('V')) + ' Flat');
|
|
732
793
|
const filters = colors.bold('View: ') + filterParts.join(colors.dim(' │ '));
|
|
733
|
-
// Controls - help and
|
|
794
|
+
// Controls - help, exit, and vim
|
|
734
795
|
const controls = colors.bold('Controls: ') +
|
|
735
796
|
colors.bold('?') +
|
|
736
797
|
' Help' +
|
|
737
798
|
colors.dim(' │ ') +
|
|
738
|
-
colors.bold(
|
|
739
|
-
'
|
|
799
|
+
colors.bold(':') +
|
|
800
|
+
' Vim' +
|
|
740
801
|
colors.dim(' │ ') +
|
|
741
|
-
colors.bold(colors.red('
|
|
742
|
-
'
|
|
802
|
+
colors.bold(colors.red('Q')) +
|
|
803
|
+
' Quit';
|
|
743
804
|
console.log(' ' + filters + colors.dim(' ') + controls);
|
|
744
805
|
}
|
|
745
806
|
function render(state, allFiles, gitRoot, cachedDisplayItems) {
|
|
@@ -773,6 +834,9 @@ async function interactiveManage(allFiles, gitRoot, initialDecisions) {
|
|
|
773
834
|
const navigationStack$ = signal([]);
|
|
774
835
|
const cursorIndex$ = signal(0);
|
|
775
836
|
const scrollOffset$ = signal(0);
|
|
837
|
+
// Vim command mode state
|
|
838
|
+
const commandMode$ = signal(false);
|
|
839
|
+
const commandBuffer$ = signal('');
|
|
776
840
|
// Computed signal for visible items (cached, only recomputes when dependencies change)
|
|
777
841
|
// IMPORTANT: Only read signals that getVisibleItems() actually uses!
|
|
778
842
|
const visibleItems$ = computed(() => {
|
|
@@ -862,17 +926,184 @@ async function interactiveManage(allFiles, gitRoot, initialDecisions) {
|
|
|
862
926
|
}
|
|
863
927
|
process.stdin.removeListener('keypress', onKeypress);
|
|
864
928
|
};
|
|
929
|
+
// Render command line at the bottom of the screen (vim command mode)
|
|
930
|
+
const renderCommandLine = () => {
|
|
931
|
+
// Clear the screen and re-render everything including the command line
|
|
932
|
+
const displayItems = displayItems$.value;
|
|
933
|
+
const state = buildState();
|
|
934
|
+
console.clear();
|
|
935
|
+
renderStatusHeader(state, allFiles, gitRoot);
|
|
936
|
+
const selectedItem = displayItems[cursorIndex$.value];
|
|
937
|
+
renderActionHint(selectedItem, state);
|
|
938
|
+
renderItems(state, displayItems);
|
|
939
|
+
// Custom footer showing command mode
|
|
940
|
+
console.log('\n' + colors.dim('─'.repeat(110)));
|
|
941
|
+
console.log('');
|
|
942
|
+
console.log(colors.bold(' :') + commandBuffer$.value + colors.bold('█'));
|
|
943
|
+
};
|
|
944
|
+
// Handle save operation
|
|
945
|
+
const handleSave = () => {
|
|
946
|
+
// For now, just show a message - the actual save happens on exit
|
|
947
|
+
console.clear();
|
|
948
|
+
console.log(colors.green('✓ Changes staged (will be saved on exit)'));
|
|
949
|
+
setTimeout(() => {
|
|
950
|
+
renderCurrent();
|
|
951
|
+
}, 800);
|
|
952
|
+
return true;
|
|
953
|
+
};
|
|
954
|
+
// Track initial state for detecting unsaved changes
|
|
955
|
+
const initialDecisions = new Map(decisions$.value);
|
|
956
|
+
// Handle quit with confirmation dialog
|
|
957
|
+
const handleQuitWithConfirmation = () => {
|
|
958
|
+
const hasChanges = decisions$.value.size !== initialDecisions.size ||
|
|
959
|
+
Array.from(decisions$.value.entries()).some(([k, v]) => initialDecisions.get(k) !== v);
|
|
960
|
+
if (!hasChanges) {
|
|
961
|
+
console.clear();
|
|
962
|
+
console.log(colors.dim('Exited - no changes made'));
|
|
963
|
+
cleanup();
|
|
964
|
+
resolve(decisions$.value);
|
|
965
|
+
return;
|
|
966
|
+
}
|
|
967
|
+
// Count changes
|
|
968
|
+
let changeCount = 0;
|
|
969
|
+
for (const [key, value] of decisions$.value.entries()) {
|
|
970
|
+
if (initialDecisions.get(key) !== value) {
|
|
971
|
+
changeCount++;
|
|
972
|
+
}
|
|
973
|
+
}
|
|
974
|
+
for (const key of initialDecisions.keys()) {
|
|
975
|
+
if (!decisions$.value.has(key)) {
|
|
976
|
+
changeCount++;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
// Show confirmation dialog
|
|
980
|
+
console.clear();
|
|
981
|
+
console.log('');
|
|
982
|
+
console.log(colors.bold(colors.cyan(' ╔═══════════════════════════════════════════════════════╗')));
|
|
983
|
+
console.log(colors.cyan(' ║') +
|
|
984
|
+
colors.bold(` Save ${changeCount} change${changeCount === 1 ? '' : 's'} before exiting?`) +
|
|
985
|
+
' '.slice(0, 24 - changeCount.toString().length) +
|
|
986
|
+
colors.cyan('║'));
|
|
987
|
+
console.log(colors.cyan(' ║') +
|
|
988
|
+
' ' +
|
|
989
|
+
colors.cyan('║'));
|
|
990
|
+
console.log(colors.cyan(' ║') +
|
|
991
|
+
' ' +
|
|
992
|
+
colors.bold(colors.green('[Y]')) +
|
|
993
|
+
' Save & exit ' +
|
|
994
|
+
colors.bold(colors.red('[N]')) +
|
|
995
|
+
' Discard ' +
|
|
996
|
+
colors.bold('[Esc]') +
|
|
997
|
+
' Cancel ' +
|
|
998
|
+
colors.cyan('║'));
|
|
999
|
+
console.log(colors.bold(colors.cyan(' ╚═══════════════════════════════════════════════════════╝')));
|
|
1000
|
+
console.log('');
|
|
1001
|
+
// Temporarily remove main keypress handler
|
|
1002
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
1003
|
+
// Handle confirmation dialog keys
|
|
1004
|
+
const onConfirmKeypress = (confirmStr, confirmKey) => {
|
|
1005
|
+
if (confirmStr === 'y' || confirmStr === 'Y') {
|
|
1006
|
+
process.stdin.removeListener('keypress', onConfirmKeypress);
|
|
1007
|
+
console.clear();
|
|
1008
|
+
console.log(colors.green('✓ Saving decisions...'));
|
|
1009
|
+
cleanup();
|
|
1010
|
+
resolve(decisions$.value);
|
|
1011
|
+
}
|
|
1012
|
+
else if (confirmStr === 'n' || confirmStr === 'N') {
|
|
1013
|
+
process.stdin.removeListener('keypress', onConfirmKeypress);
|
|
1014
|
+
console.clear();
|
|
1015
|
+
console.log(colors.yellow('✗ Discarded changes'));
|
|
1016
|
+
cleanup();
|
|
1017
|
+
resolve(initialDecisions);
|
|
1018
|
+
}
|
|
1019
|
+
else if (confirmKey.name === 'escape') {
|
|
1020
|
+
// Cancel - restore main handler
|
|
1021
|
+
process.stdin.removeListener('keypress', onConfirmKeypress);
|
|
1022
|
+
process.stdin.on('keypress', onKeypress);
|
|
1023
|
+
renderCurrent();
|
|
1024
|
+
}
|
|
1025
|
+
else if (confirmKey.ctrl && confirmKey.name === 'c') {
|
|
1026
|
+
process.stdin.removeListener('keypress', onConfirmKeypress);
|
|
1027
|
+
console.clear();
|
|
1028
|
+
console.log(colors.yellow('✗ Cancelled'));
|
|
1029
|
+
cleanup();
|
|
1030
|
+
resolve(initialDecisions);
|
|
1031
|
+
}
|
|
1032
|
+
};
|
|
1033
|
+
process.stdin.on('keypress', onConfirmKeypress);
|
|
1034
|
+
};
|
|
865
1035
|
const onKeypress = (str, key) => {
|
|
866
1036
|
const displayItems = displayItems$.value; // Cached!
|
|
867
|
-
|
|
1037
|
+
// Handle vim command mode
|
|
1038
|
+
if (commandMode$.value) {
|
|
1039
|
+
if (key.name === 'escape') {
|
|
1040
|
+
// Exit command mode without executing
|
|
1041
|
+
commandMode$.value = false;
|
|
1042
|
+
commandBuffer$.value = '';
|
|
1043
|
+
renderCurrent();
|
|
1044
|
+
}
|
|
1045
|
+
else if (key.name === 'return') {
|
|
1046
|
+
// Execute the command
|
|
1047
|
+
const action = executeVimCommand(commandBuffer$.value);
|
|
1048
|
+
commandMode$.value = false;
|
|
1049
|
+
commandBuffer$.value = '';
|
|
1050
|
+
if (action === 'save') {
|
|
1051
|
+
handleSave();
|
|
1052
|
+
}
|
|
1053
|
+
else if (action === 'quit') {
|
|
1054
|
+
handleQuitWithConfirmation();
|
|
1055
|
+
}
|
|
1056
|
+
else if (action === 'force-quit') {
|
|
1057
|
+
console.clear();
|
|
1058
|
+
console.log(colors.yellow('✗ Force quit - discarded changes'));
|
|
1059
|
+
cleanup();
|
|
1060
|
+
resolve(initialDecisions);
|
|
1061
|
+
}
|
|
1062
|
+
else if (action === 'save-quit') {
|
|
1063
|
+
console.clear();
|
|
1064
|
+
console.log(colors.green('✓ Saving decisions...'));
|
|
1065
|
+
cleanup();
|
|
1066
|
+
resolve(decisions$.value);
|
|
1067
|
+
}
|
|
1068
|
+
else {
|
|
1069
|
+
// Unknown command - show error briefly
|
|
1070
|
+
console.clear();
|
|
1071
|
+
console.log(colors.red(`Unknown command: :${commandBuffer$.value}`));
|
|
1072
|
+
setTimeout(() => {
|
|
1073
|
+
renderCurrent();
|
|
1074
|
+
}, 800);
|
|
1075
|
+
}
|
|
1076
|
+
}
|
|
1077
|
+
else if (key.name === 'backspace') {
|
|
1078
|
+
commandBuffer$.value = commandBuffer$.value.slice(0, -1);
|
|
1079
|
+
renderCommandLine();
|
|
1080
|
+
}
|
|
1081
|
+
else if (str && str.length === 1 && !key.ctrl) {
|
|
1082
|
+
// Add character to command buffer
|
|
1083
|
+
commandBuffer$.value += str;
|
|
1084
|
+
renderCommandLine();
|
|
1085
|
+
}
|
|
1086
|
+
return;
|
|
1087
|
+
}
|
|
1088
|
+
// Enter vim command mode
|
|
1089
|
+
if (str === ':') {
|
|
1090
|
+
commandMode$.value = true;
|
|
1091
|
+
commandBuffer$.value = '';
|
|
1092
|
+
renderCommandLine();
|
|
1093
|
+
return;
|
|
1094
|
+
}
|
|
1095
|
+
// Navigation: up arrow or k
|
|
1096
|
+
if (key.name === 'up' || str === 'k') {
|
|
868
1097
|
const newIndex = Math.max(0, cursorIndex$.value - 1);
|
|
869
1098
|
cursorIndex$.value = newIndex;
|
|
870
1099
|
renderCurrent();
|
|
1100
|
+
// Navigation: down arrow or j
|
|
871
1101
|
}
|
|
872
|
-
else if (key.name === 'down') {
|
|
1102
|
+
else if (key.name === 'down' || str === 'j') {
|
|
873
1103
|
const newIndex = Math.min(displayItems.length - 1, cursorIndex$.value + 1);
|
|
874
1104
|
cursorIndex$.value = newIndex;
|
|
875
1105
|
renderCurrent();
|
|
1106
|
+
// Navigate into (right arrow)
|
|
876
1107
|
}
|
|
877
1108
|
else if (key.name === 'right') {
|
|
878
1109
|
if (viewMode$.value === 'flat')
|
|
@@ -893,10 +1124,45 @@ async function interactiveManage(allFiles, gitRoot, initialDecisions) {
|
|
|
893
1124
|
scrollOffset$.value = 0;
|
|
894
1125
|
}
|
|
895
1126
|
renderCurrent();
|
|
1127
|
+
// Navigate back (left arrow or Esc)
|
|
896
1128
|
}
|
|
897
|
-
else if (key.name === 'left') {
|
|
898
|
-
|
|
1129
|
+
else if (key.name === 'left' || key.name === 'escape') {
|
|
1130
|
+
// In flat view, Esc exits
|
|
1131
|
+
if (viewMode$.value === 'flat') {
|
|
1132
|
+
if (key.name === 'escape') {
|
|
1133
|
+
// Esc in flat view - check for unsaved changes
|
|
1134
|
+
const hasChanges = decisions$.value.size !== initialDecisions.size ||
|
|
1135
|
+
Array.from(decisions$.value.entries()).some(([k, v]) => initialDecisions.get(k) !== v);
|
|
1136
|
+
if (hasChanges) {
|
|
1137
|
+
handleQuitWithConfirmation();
|
|
1138
|
+
}
|
|
1139
|
+
else {
|
|
1140
|
+
console.clear();
|
|
1141
|
+
console.log(colors.dim('Exited - no changes made'));
|
|
1142
|
+
cleanup();
|
|
1143
|
+
resolve(decisions$.value);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
return;
|
|
1147
|
+
}
|
|
1148
|
+
// In hierarchical view, go back or exit at root
|
|
1149
|
+
if (navigationStack$.value.length === 0) {
|
|
1150
|
+
if (key.name === 'escape') {
|
|
1151
|
+
// Esc at root - check for unsaved changes
|
|
1152
|
+
const hasChanges = decisions$.value.size !== initialDecisions.size ||
|
|
1153
|
+
Array.from(decisions$.value.entries()).some(([k, v]) => initialDecisions.get(k) !== v);
|
|
1154
|
+
if (hasChanges) {
|
|
1155
|
+
handleQuitWithConfirmation();
|
|
1156
|
+
}
|
|
1157
|
+
else {
|
|
1158
|
+
console.clear();
|
|
1159
|
+
console.log(colors.dim('Exited - no changes made'));
|
|
1160
|
+
cleanup();
|
|
1161
|
+
resolve(decisions$.value);
|
|
1162
|
+
}
|
|
1163
|
+
}
|
|
899
1164
|
return;
|
|
1165
|
+
}
|
|
900
1166
|
const stack = navigationStack$.value;
|
|
901
1167
|
navigationStack$.value = stack.slice(0, -1);
|
|
902
1168
|
cursorIndex$.value = 0;
|
|
@@ -953,18 +1219,28 @@ async function interactiveManage(allFiles, gitRoot, initialDecisions) {
|
|
|
953
1219
|
decisions$.value = newDecisions;
|
|
954
1220
|
renderCurrent();
|
|
955
1221
|
}
|
|
1222
|
+
// Quit: q key - show confirmation if there are changes
|
|
956
1223
|
}
|
|
957
1224
|
else if (str === 'q' || str === 'Q') {
|
|
1225
|
+
handleQuitWithConfirmation();
|
|
1226
|
+
// Cancel: x key (deprecated) or Ctrl+C
|
|
1227
|
+
}
|
|
1228
|
+
else if (str === 'x' || str === 'X') {
|
|
1229
|
+
// Deprecated: show notice and handle as Esc
|
|
958
1230
|
console.clear();
|
|
959
|
-
console.log(colors.
|
|
960
|
-
|
|
961
|
-
|
|
1231
|
+
console.log(colors.yellow('Note: x key is deprecated. Use Esc to go back.'));
|
|
1232
|
+
setTimeout(() => {
|
|
1233
|
+
process.stdin.on('keypress', onKeypress);
|
|
1234
|
+
renderCurrent();
|
|
1235
|
+
}, 1500);
|
|
1236
|
+
process.stdin.removeListener('keypress', onKeypress);
|
|
962
1237
|
}
|
|
963
|
-
else if (
|
|
1238
|
+
else if (key.ctrl && key.name === 'c') {
|
|
1239
|
+
// Force quit without confirmation
|
|
964
1240
|
console.clear();
|
|
965
|
-
console.log(colors.yellow('✗ Cancelled
|
|
1241
|
+
console.log(colors.yellow('✗ Cancelled'));
|
|
966
1242
|
cleanup();
|
|
967
|
-
resolve(
|
|
1243
|
+
resolve(initialDecisions);
|
|
968
1244
|
}
|
|
969
1245
|
else if (str === '?') {
|
|
970
1246
|
showHelp$.value = !showHelp$.value;
|
|
@@ -1069,6 +1345,12 @@ function getIgnoredFiles(gitRoot, manifestFile) {
|
|
|
1069
1345
|
}
|
|
1070
1346
|
}
|
|
1071
1347
|
function getManifestEntries(gitRoot, manifestFile) {
|
|
1348
|
+
// Use config-manifest adapter for default manifest file
|
|
1349
|
+
if (manifestFile === DEFAULT_MANIFEST_FILE) {
|
|
1350
|
+
const data = loadManifestData(gitRoot);
|
|
1351
|
+
return data.enabled;
|
|
1352
|
+
}
|
|
1353
|
+
// Legacy behavior for custom manifest files
|
|
1072
1354
|
const manifestPath = path.join(gitRoot, manifestFile);
|
|
1073
1355
|
if (!fs.existsSync(manifestPath))
|
|
1074
1356
|
return [];
|
|
@@ -1078,8 +1360,20 @@ function getManifestEntries(gitRoot, manifestFile) {
|
|
|
1078
1360
|
.filter((x) => x.trim() && !x.startsWith('#'));
|
|
1079
1361
|
}
|
|
1080
1362
|
function getManifestDecisions(gitRoot, manifestFile) {
|
|
1081
|
-
const manifestPath = path.join(gitRoot, manifestFile);
|
|
1082
1363
|
const decisions = new Map();
|
|
1364
|
+
// Use config-manifest adapter for default manifest file
|
|
1365
|
+
if (manifestFile === DEFAULT_MANIFEST_FILE) {
|
|
1366
|
+
const data = loadManifestData(gitRoot);
|
|
1367
|
+
for (const file of data.enabled) {
|
|
1368
|
+
decisions.set(file, 'add');
|
|
1369
|
+
}
|
|
1370
|
+
for (const file of data.disabled) {
|
|
1371
|
+
decisions.set(file, 'comment');
|
|
1372
|
+
}
|
|
1373
|
+
return decisions;
|
|
1374
|
+
}
|
|
1375
|
+
// Legacy behavior for custom manifest files
|
|
1376
|
+
const manifestPath = path.join(gitRoot, manifestFile);
|
|
1083
1377
|
if (!fs.existsSync(manifestPath))
|
|
1084
1378
|
return decisions;
|
|
1085
1379
|
const lines = fs.readFileSync(manifestPath, 'utf-8').split('\n');
|
|
@@ -1317,12 +1611,39 @@ export async function run(argv) {
|
|
|
1317
1611
|
}
|
|
1318
1612
|
}
|
|
1319
1613
|
else {
|
|
1320
|
-
if (
|
|
1321
|
-
|
|
1322
|
-
|
|
1614
|
+
// Determine if using default manifest file (config-based) or custom file (legacy)
|
|
1615
|
+
const usingConfigManifest = manifestFile === DEFAULT_MANIFEST_FILE;
|
|
1616
|
+
if (usingConfigManifest) {
|
|
1617
|
+
// Convert finalEntries to enabled/disabled arrays for config-based storage
|
|
1618
|
+
const enabled = [];
|
|
1619
|
+
const disabled = [];
|
|
1620
|
+
for (const entry of finalEntries) {
|
|
1621
|
+
if (entry.startsWith('#')) {
|
|
1622
|
+
// Extract file path from commented entry (handles various prefixes)
|
|
1623
|
+
const commentContent = entry.substring(1).trim();
|
|
1624
|
+
let filePath = commentContent;
|
|
1625
|
+
const prefixMatch = commentContent.match(/^(TRACKED|DELETED|STALE):\s*(.+)/);
|
|
1626
|
+
if (prefixMatch) {
|
|
1627
|
+
filePath = prefixMatch[2];
|
|
1628
|
+
}
|
|
1629
|
+
disabled.push(filePath);
|
|
1630
|
+
}
|
|
1631
|
+
else {
|
|
1632
|
+
enabled.push(entry);
|
|
1633
|
+
}
|
|
1634
|
+
}
|
|
1635
|
+
saveManifestData(mainWorktreeRoot, enabled, disabled);
|
|
1636
|
+
console.log(colors.green(`Successfully updated wtlink config in .worktreerc.`));
|
|
1637
|
+
}
|
|
1638
|
+
else {
|
|
1639
|
+
// Legacy behavior for custom manifest files
|
|
1640
|
+
if (argv.backup && fs.existsSync(manifestPath)) {
|
|
1641
|
+
fs.copyFileSync(manifestPath, manifestBackupFile);
|
|
1642
|
+
console.log(`Backed up existing manifest to ${manifestBackupFile}`);
|
|
1643
|
+
}
|
|
1644
|
+
fs.writeFileSync(manifestPath, finalEntries.join('\n') + '\n');
|
|
1645
|
+
console.log(colors.green(`Successfully updated ${manifestFile}.`));
|
|
1323
1646
|
}
|
|
1324
|
-
fs.writeFileSync(manifestPath, finalEntries.join('\n') + '\n');
|
|
1325
|
-
console.log(colors.green(`Successfully updated ${manifestFile}.`));
|
|
1326
1647
|
}
|
|
1327
1648
|
}
|
|
1328
1649
|
//# sourceMappingURL=manage-manifest.js.map
|