oh-aicoding-tool 0.1.5 → 0.1.7

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/bin/cli.js +68 -51
  2. package/package.json +3 -3
package/bin/cli.js CHANGED
@@ -1,10 +1,9 @@
1
1
  #!/usr/bin/env node
2
- import fs from "node:fs";
3
- import path from "node:path";
4
- import readline from "node:readline";
5
- import { spawnSync } from "node:child_process";
6
- import { createRequire } from "node:module";
7
- import { createInterface } from "node:readline/promises";
2
+ import fs from "node:fs";
3
+ import path from "node:path";
4
+ import { spawnSync } from "node:child_process";
5
+ import { createRequire } from "node:module";
6
+ import { createInterface } from "node:readline/promises";
8
7
 
9
8
  const require = createRequire(import.meta.url);
10
9
 
@@ -83,11 +82,11 @@ function runPackageBin(packageName, binName, args) {
83
82
  return result.status ?? (result.error ? 1 : 0);
84
83
  }
85
84
 
86
- function emailIsValid(value) {
87
- return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value || "").trim());
88
- }
89
-
90
- function renderMultiChoice(title, choices, index, selected, subtitle) {
85
+ function emailIsValid(value) {
86
+ return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value || "").trim());
87
+ }
88
+
89
+ function renderMultiChoice(title, choices, index, selected, subtitle) {
91
90
  renderHeader(subtitle);
92
91
  console.log("");
93
92
  console.log(paint(title, t.bold, t.blue));
@@ -102,21 +101,39 @@ function renderMultiChoice(title, choices, index, selected, subtitle) {
102
101
  if (choice.description) console.log(` ${paint(choice.description, focused ? t.blue : t.muted)}`);
103
102
  if (idx < choices.length - 1) console.log("");
104
103
  });
105
- }
106
-
107
- async function askMultiChoice(rl, title, choices, subtitle) {
104
+ }
105
+
106
+ function rawKeySeq(raw) {
107
+ if (Buffer.isBuffer(raw)) return raw.toString("latin1");
108
+ return String(raw ?? "");
109
+ }
110
+
111
+ function parseRawKey(raw) {
112
+ const seq = rawKeySeq(raw);
113
+ if (seq === "\x03") return { name: "ctrl-c", sequence: seq };
114
+ if (seq === "\x1b[A" || seq === "\x1bOA" || seq === "\x00H" || seq === "\xe0H") return { name: "up", sequence: seq };
115
+ if (seq === "\x1b[B" || seq === "\x1bOB" || seq === "\x00P" || seq === "\xe0P") return { name: "down", sequence: seq };
116
+ if (seq === "\r" || seq === "\n" || seq === "\r\n") return { name: "enter", sequence: seq };
117
+ if (seq === " ") return { name: "space", sequence: seq };
118
+ if (seq === "\x1b") return { name: "escape", sequence: seq };
119
+ if (/^[1-9]$/.test(seq)) return { name: "number", number: Number.parseInt(seq, 10), sequence: seq };
120
+ if (seq.length === 1) return { name: seq.toLowerCase(), sequence: seq };
121
+ return { name: "", sequence: seq };
122
+ }
123
+
124
+ async function askMultiChoice(rl, title, choices, subtitle) {
108
125
  if (process.stdin.isTTY && process.stdout.isTTY) {
109
126
  rl.pause();
110
127
  return await new Promise((resolve) => {
111
128
  let index = 0;
112
129
  const selected = new Set(choices.filter((choice) => choice.selected).map((choice) => choice.value));
113
130
  const stdin = process.stdin;
114
-
115
- function cleanup(value) {
116
- stdin.off("keypress", onKeypress);
117
- if (stdin.isTTY) stdin.setRawMode(false);
118
- stdin.pause();
119
- rl.resume();
131
+
132
+ function cleanup(value) {
133
+ stdin.off("data", onData);
134
+ if (stdin.isTTY) stdin.setRawMode(false);
135
+ stdin.pause();
136
+ rl.resume();
120
137
  clearScreen();
121
138
  resolve(value);
122
139
  }
@@ -125,37 +142,37 @@ async function askMultiChoice(rl, title, choices, subtitle) {
125
142
  const value = choices[index].value;
126
143
  if (selected.has(value)) selected.delete(value);
127
144
  else selected.add(value);
128
- renderMultiChoice(title, choices, index, selected, subtitle);
129
- }
130
-
131
- function onKeypress(_, key = {}) {
132
- if (key.ctrl && key.name === "c") return cleanup([]);
133
- if (key.name === "q" || key.name === "escape") return cleanup([]);
134
- if (key.name === "up") {
135
- index = (index - 1 + choices.length) % choices.length;
136
- renderMultiChoice(title, choices, index, selected, subtitle);
137
- return;
138
- }
139
- if (key.name === "down") {
140
- index = (index + 1) % choices.length;
141
- renderMultiChoice(title, choices, index, selected, subtitle);
142
- return;
143
- }
144
- if (key.name === "space") return toggle();
145
- if (key.name === "return" || key.name === "enter") return cleanup([...selected]);
146
- const number = Number.parseInt(key.sequence, 10);
147
- if (Number.isInteger(number) && choices[number - 1]) {
148
- index = number - 1;
149
- toggle();
150
- }
151
- }
152
-
153
- readline.emitKeypressEvents(stdin);
154
- if (stdin.isTTY) stdin.setRawMode(true);
155
- stdin.on("keypress", onKeypress);
156
- stdin.resume();
157
- renderMultiChoice(title, choices, index, selected, subtitle);
158
- });
145
+ renderMultiChoice(title, choices, index, selected, subtitle);
146
+ }
147
+
148
+ function onData(raw) {
149
+ const key = parseRawKey(raw);
150
+ if (key.name === "ctrl-c") return cleanup([]);
151
+ if (key.name === "up") {
152
+ index = (index - 1 + choices.length) % choices.length;
153
+ renderMultiChoice(title, choices, index, selected, subtitle);
154
+ return;
155
+ }
156
+ if (key.name === "down") {
157
+ index = (index + 1) % choices.length;
158
+ renderMultiChoice(title, choices, index, selected, subtitle);
159
+ return;
160
+ }
161
+ if (key.name === "q" || key.name === "escape") return cleanup([]);
162
+ if (key.name === "space") return toggle();
163
+ if (key.name === "enter") return cleanup([...selected]);
164
+ const number = key.name === "number" ? key.number : Number.NaN;
165
+ if (Number.isInteger(number) && choices[number - 1]) {
166
+ index = number - 1;
167
+ toggle();
168
+ }
169
+ }
170
+
171
+ if (stdin.isTTY) stdin.setRawMode(true);
172
+ stdin.on("data", onData);
173
+ stdin.resume();
174
+ renderMultiChoice(title, choices, index, selected, subtitle);
175
+ });
159
176
  }
160
177
 
161
178
  choices.forEach((choice, index) => console.log(` ${index + 1}. ${choice.label}`));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "oh-aicoding-tool",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "description": "Interactive installer for AI coding tools: Langfuse tracing and oh-ai-report.",
@@ -25,7 +25,7 @@
25
25
  ],
26
26
  "license": "UNLICENSED",
27
27
  "dependencies": {
28
- "oh-langfuse": "^0.1.8",
29
- "oh-aireport": "^0.1.1"
28
+ "oh-langfuse": "^0.1.10",
29
+ "oh-aireport": "^0.1.1"
30
30
  }
31
31
  }