oh-skillhub 0.1.12 → 0.1.13

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 +89 -3
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-skillhub",
3
- "version": "0.1.12",
3
+ "version": "0.1.13",
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 {
@@ -678,7 +682,8 @@ function renderRawActionMenu(cursor) {
678
682
  ];
679
683
  ACTION_CHOICES.forEach((choice, index) => {
680
684
  const pointer = index === cursor ? ">" : " ";
681
- const row = `${pointer} [ ] ${choice.label.padEnd(22, " ")} ${choice.hint}`;
685
+ const highlighted = index === cursor;
686
+ const row = `${pointer} ${rawCheckbox(highlighted, highlighted)} ${choice.label.padEnd(22, " ")} ${choice.hint}`;
682
687
  lines.push(index === cursor ? colorize(row, ANSI.reverse, ANSI.bold) : row);
683
688
  });
684
689
  lines.push("");
@@ -700,7 +705,8 @@ function renderRawAgentMenu(cursor) {
700
705
  ];
701
706
  AGENT_CHOICES.forEach((choice, index) => {
702
707
  const pointer = index === cursor ? ">" : " ";
703
- const row = `${pointer} [ ] ${choice.label.padEnd(10, " ")} ${choice.hint}`;
708
+ const highlighted = index === cursor;
709
+ const row = `${pointer} ${rawCheckbox(highlighted, highlighted)} ${choice.label.padEnd(10, " ")} ${choice.hint}`;
704
710
  lines.push(index === cursor ? colorize(row, ANSI.reverse, ANSI.bold) : row);
705
711
  });
706
712
  lines.push("");
@@ -738,6 +744,85 @@ function renderRawTuiMenu(choices, cursor, selected, agent = "codex") {
738
744
  return `${lines.join("\n")}\n`;
739
745
  }
740
746
 
747
+ function runRawCleanSelection(input, output, skills) {
748
+ return new Promise((resolve, reject) => {
749
+ let cursor = 0;
750
+ const selected = new Set([cursor]);
751
+ const wasRaw = input.isRaw;
752
+
753
+ readline.emitKeypressEvents(input);
754
+ input.setRawMode(true);
755
+
756
+ function render() {
757
+ output.write("\x1b[2J\x1b[H");
758
+ output.write(renderRawCleanSelectionMenu(skills, cursor, selected));
759
+ }
760
+
761
+ function cleanup() {
762
+ input.removeListener("keypress", onKeypress);
763
+ input.setRawMode(Boolean(wasRaw));
764
+ output.write("\x1b[?25h");
765
+ }
766
+
767
+ function onKeypress(_str, key) {
768
+ if (key && key.ctrl && key.name === "c") {
769
+ cleanup();
770
+ reject(new Error("Cancelled."));
771
+ return;
772
+ }
773
+ if (key && (key.name === "down" || key.name === "j")) {
774
+ cursor = (cursor + 1) % skills.length;
775
+ render();
776
+ return;
777
+ }
778
+ if (key && (key.name === "up" || key.name === "k")) {
779
+ cursor = (cursor - 1 + skills.length) % skills.length;
780
+ render();
781
+ return;
782
+ }
783
+ if (key && key.name === "space") {
784
+ if (selected.has(cursor)) selected.delete(cursor);
785
+ else selected.add(cursor);
786
+ render();
787
+ return;
788
+ }
789
+ if (key && key.name === "return") {
790
+ cleanup();
791
+ resolve(selected.size ? Array.from(selected).sort((a, b) => a - b) : [cursor]);
792
+ }
793
+ }
794
+
795
+ output.write("\x1b[?25l");
796
+ render();
797
+ input.on("keypress", onKeypress);
798
+ });
799
+ }
800
+
801
+ function renderRawCleanSelectionMenu(skills, cursor, selected) {
802
+ const width = 92;
803
+ const line = `+${"-".repeat(width - 2)}+`;
804
+ const lines = [
805
+ line,
806
+ rawHeaderLine("OH SkillHub", width, ANSI.cyan, ANSI.bold),
807
+ rawHeaderLine("OpenHarmony Skills Cleaner", width, ANSI.dim),
808
+ line,
809
+ "",
810
+ colorize("Detected skills", ANSI.bold),
811
+ colorize(" Up/Down or j/k: move Space: select Enter: clean Ctrl+C: cancel", ANSI.dim),
812
+ "",
813
+ ];
814
+ skills.forEach((skill, index) => {
815
+ const pointer = index === cursor ? ">" : " ";
816
+ const highlighted = index === cursor;
817
+ const checkbox = rawCheckbox(selected.has(index), highlighted);
818
+ const group = `${skill.domain || "unknown"}/${skill.stage || "unknown"}`;
819
+ const row = `${pointer} ${checkbox} ${skill.name.padEnd(36, " ")} ${skill.status.padEnd(9, " ")} ${group}`;
820
+ lines.push(highlighted ? colorize(row, ANSI.reverse, ANSI.bold) : row);
821
+ });
822
+ lines.push("");
823
+ return `${lines.join("\n")}\n`;
824
+ }
825
+
741
826
  function colorize(value, ...codes) {
742
827
  return `${codes.join("")}${value}${ANSI.reset}`;
743
828
  }
@@ -947,6 +1032,7 @@ module.exports = {
947
1032
  renderCleanPlan,
948
1033
  renderRawActionMenu,
949
1034
  renderRawAgentMenu,
1035
+ renderRawCleanSelectionMenu,
950
1036
  parseSelection,
951
1037
  renderRawTuiMenu,
952
1038
  renderTuiMenu,