oh-skillhub 0.1.12 → 0.1.14

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 (2) hide show
  1. package/package.json +1 -1
  2. package/src/cli.js +111 -13
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-skillhub",
3
- "version": "0.1.12",
3
+ "version": "0.1.14",
4
4
  "description": "OpenHarmony Skills installer for Codex, Claude Code, and OpenCode.",
5
5
  "type": "commonjs",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -174,7 +174,7 @@ async function runCleanWithAnswers(options, input, output, scriptedAnswers = nul
174
174
  let selected;
175
175
  let prefilledConfirmation = null;
176
176
  if (!cleanOptions.names.length && !cleanOptions.dryRun) {
177
- if (!hasScriptedAnswers && input.isTTY) {
177
+ if (!hasScriptedAnswers && input.isTTY && output.isTTY) {
178
178
  selected = await promptCleanSelection(input, output, discovered);
179
179
  } else {
180
180
  output.write(renderCleanSelectionMenu(discovered));
@@ -203,6 +203,10 @@ async function runCleanWithAnswers(options, input, output, scriptedAnswers = nul
203
203
  }
204
204
 
205
205
  async function promptCleanSelection(input, output, skills) {
206
+ if (input.isTTY && output.isTTY) {
207
+ const selectedIndexes = await runRawCleanSelection(input, output, skills);
208
+ return selectedIndexes.map((index) => skills[index]);
209
+ }
206
210
  output.write(renderCleanSelectionMenu(skills));
207
211
  const rl = readlinePromises.createInterface({ input, output });
208
212
  try {
@@ -572,6 +576,7 @@ function runRawTuiSelection(input, output, choices, agent = "codex") {
572
576
  function runRawAgentSelection(input, output) {
573
577
  return new Promise((resolve, reject) => {
574
578
  let cursor = 0;
579
+ let selected = 0;
575
580
  const wasRaw = input.isRaw;
576
581
 
577
582
  readline.emitKeypressEvents(input);
@@ -579,7 +584,7 @@ function runRawAgentSelection(input, output) {
579
584
 
580
585
  function render() {
581
586
  output.write("\x1b[2J\x1b[H");
582
- output.write(renderRawAgentMenu(cursor));
587
+ output.write(renderRawAgentMenu(cursor, selected));
583
588
  }
584
589
 
585
590
  function cleanup() {
@@ -604,9 +609,14 @@ function runRawAgentSelection(input, output) {
604
609
  render();
605
610
  return;
606
611
  }
607
- if (key && (key.name === "space" || key.name === "return")) {
612
+ if (key && key.name === "space") {
613
+ selected = cursor;
614
+ render();
615
+ return;
616
+ }
617
+ if (key && key.name === "return") {
608
618
  cleanup();
609
- resolve(AGENT_CHOICES[cursor].agent);
619
+ resolve(AGENT_CHOICES[selected].agent);
610
620
  }
611
621
  }
612
622
 
@@ -619,6 +629,7 @@ function runRawAgentSelection(input, output) {
619
629
  function runRawActionSelection(input, output) {
620
630
  return new Promise((resolve, reject) => {
621
631
  let cursor = 0;
632
+ let selected = 0;
622
633
  const wasRaw = input.isRaw;
623
634
 
624
635
  readline.emitKeypressEvents(input);
@@ -626,7 +637,7 @@ function runRawActionSelection(input, output) {
626
637
 
627
638
  function render() {
628
639
  output.write("\x1b[2J\x1b[H");
629
- output.write(renderRawActionMenu(cursor));
640
+ output.write(renderRawActionMenu(cursor, selected));
630
641
  }
631
642
 
632
643
  function cleanup() {
@@ -651,9 +662,14 @@ function runRawActionSelection(input, output) {
651
662
  render();
652
663
  return;
653
664
  }
654
- if (key && (key.name === "space" || key.name === "return")) {
665
+ if (key && key.name === "space") {
666
+ selected = cursor;
667
+ render();
668
+ return;
669
+ }
670
+ if (key && key.name === "return") {
655
671
  cleanup();
656
- resolve(ACTION_CHOICES[cursor].action);
672
+ resolve(ACTION_CHOICES[selected].action);
657
673
  }
658
674
  }
659
675
 
@@ -663,7 +679,7 @@ function runRawActionSelection(input, output) {
663
679
  });
664
680
  }
665
681
 
666
- function renderRawActionMenu(cursor) {
682
+ function renderRawActionMenu(cursor, selected = cursor) {
667
683
  const width = 76;
668
684
  const line = `+${"-".repeat(width - 2)}+`;
669
685
  const lines = [
@@ -673,19 +689,20 @@ function renderRawActionMenu(cursor) {
673
689
  line,
674
690
  "",
675
691
  colorize("Choose action", ANSI.bold),
676
- colorize(" Up/Down or j/k: move Space/Enter: select Ctrl+C: cancel", ANSI.dim),
692
+ colorize(" Up/Down or j/k: move Space: select Enter: confirm Ctrl+C: cancel", ANSI.dim),
677
693
  "",
678
694
  ];
679
695
  ACTION_CHOICES.forEach((choice, index) => {
680
696
  const pointer = index === cursor ? ">" : " ";
681
- const row = `${pointer} [ ] ${choice.label.padEnd(22, " ")} ${choice.hint}`;
697
+ const highlighted = index === cursor;
698
+ const row = `${pointer} ${rawCheckbox(index === selected, highlighted)} ${choice.label.padEnd(22, " ")} ${choice.hint}`;
682
699
  lines.push(index === cursor ? colorize(row, ANSI.reverse, ANSI.bold) : row);
683
700
  });
684
701
  lines.push("");
685
702
  return `${lines.join("\n")}\n`;
686
703
  }
687
704
 
688
- function renderRawAgentMenu(cursor) {
705
+ function renderRawAgentMenu(cursor, selected = cursor) {
689
706
  const width = 76;
690
707
  const line = `+${"-".repeat(width - 2)}+`;
691
708
  const lines = [
@@ -695,12 +712,13 @@ function renderRawAgentMenu(cursor) {
695
712
  line,
696
713
  "",
697
714
  colorize("Choose install target", ANSI.bold),
698
- colorize(" Up/Down or j/k: move Space/Enter: select Ctrl+C: cancel", ANSI.dim),
715
+ colorize(" Up/Down or j/k: move Space: select Enter: confirm Ctrl+C: cancel", ANSI.dim),
699
716
  "",
700
717
  ];
701
718
  AGENT_CHOICES.forEach((choice, index) => {
702
719
  const pointer = index === cursor ? ">" : " ";
703
- const row = `${pointer} [ ] ${choice.label.padEnd(10, " ")} ${choice.hint}`;
720
+ const highlighted = index === cursor;
721
+ const row = `${pointer} ${rawCheckbox(index === selected, highlighted)} ${choice.label.padEnd(10, " ")} ${choice.hint}`;
704
722
  lines.push(index === cursor ? colorize(row, ANSI.reverse, ANSI.bold) : row);
705
723
  });
706
724
  lines.push("");
@@ -738,6 +756,85 @@ function renderRawTuiMenu(choices, cursor, selected, agent = "codex") {
738
756
  return `${lines.join("\n")}\n`;
739
757
  }
740
758
 
759
+ function runRawCleanSelection(input, output, skills) {
760
+ return new Promise((resolve, reject) => {
761
+ let cursor = 0;
762
+ const selected = new Set([cursor]);
763
+ const wasRaw = input.isRaw;
764
+
765
+ readline.emitKeypressEvents(input);
766
+ input.setRawMode(true);
767
+
768
+ function render() {
769
+ output.write("\x1b[2J\x1b[H");
770
+ output.write(renderRawCleanSelectionMenu(skills, cursor, selected));
771
+ }
772
+
773
+ function cleanup() {
774
+ input.removeListener("keypress", onKeypress);
775
+ input.setRawMode(Boolean(wasRaw));
776
+ output.write("\x1b[?25h");
777
+ }
778
+
779
+ function onKeypress(_str, key) {
780
+ if (key && key.ctrl && key.name === "c") {
781
+ cleanup();
782
+ reject(new Error("Cancelled."));
783
+ return;
784
+ }
785
+ if (key && (key.name === "down" || key.name === "j")) {
786
+ cursor = (cursor + 1) % skills.length;
787
+ render();
788
+ return;
789
+ }
790
+ if (key && (key.name === "up" || key.name === "k")) {
791
+ cursor = (cursor - 1 + skills.length) % skills.length;
792
+ render();
793
+ return;
794
+ }
795
+ if (key && key.name === "space") {
796
+ if (selected.has(cursor)) selected.delete(cursor);
797
+ else selected.add(cursor);
798
+ render();
799
+ return;
800
+ }
801
+ if (key && key.name === "return") {
802
+ cleanup();
803
+ resolve(selected.size ? Array.from(selected).sort((a, b) => a - b) : [cursor]);
804
+ }
805
+ }
806
+
807
+ output.write("\x1b[?25l");
808
+ render();
809
+ input.on("keypress", onKeypress);
810
+ });
811
+ }
812
+
813
+ function renderRawCleanSelectionMenu(skills, cursor, selected) {
814
+ const width = 92;
815
+ const line = `+${"-".repeat(width - 2)}+`;
816
+ const lines = [
817
+ line,
818
+ rawHeaderLine("OH SkillHub", width, ANSI.cyan, ANSI.bold),
819
+ rawHeaderLine("OpenHarmony Skills Cleaner", width, ANSI.dim),
820
+ line,
821
+ "",
822
+ colorize("Detected skills", ANSI.bold),
823
+ colorize(" Up/Down or j/k: move Space: select Enter: clean Ctrl+C: cancel", ANSI.dim),
824
+ "",
825
+ ];
826
+ skills.forEach((skill, index) => {
827
+ const pointer = index === cursor ? ">" : " ";
828
+ const highlighted = index === cursor;
829
+ const checkbox = rawCheckbox(selected.has(index), highlighted);
830
+ const group = `${skill.domain || "unknown"}/${skill.stage || "unknown"}`;
831
+ const row = `${pointer} ${checkbox} ${skill.name.padEnd(36, " ")} ${skill.status.padEnd(9, " ")} ${group}`;
832
+ lines.push(highlighted ? colorize(row, ANSI.reverse, ANSI.bold) : row);
833
+ });
834
+ lines.push("");
835
+ return `${lines.join("\n")}\n`;
836
+ }
837
+
741
838
  function colorize(value, ...codes) {
742
839
  return `${codes.join("")}${value}${ANSI.reset}`;
743
840
  }
@@ -947,6 +1044,7 @@ module.exports = {
947
1044
  renderCleanPlan,
948
1045
  renderRawActionMenu,
949
1046
  renderRawAgentMenu,
1047
+ renderRawCleanSelectionMenu,
950
1048
  parseSelection,
951
1049
  renderRawTuiMenu,
952
1050
  renderTuiMenu,