oh-aicoding-tool 0.1.6 → 0.1.8
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/bin/cli.js +87 -85
- package/package.json +2 -2
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
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
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
|
|
|
@@ -87,43 +86,6 @@ function emailIsValid(value) {
|
|
|
87
86
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(String(value || "").trim());
|
|
88
87
|
}
|
|
89
88
|
|
|
90
|
-
function keySeq(raw, key = {}) {
|
|
91
|
-
return String(key.sequence ?? raw ?? "");
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function isCtrlC(raw, key = {}) {
|
|
95
|
-
return Boolean(key.ctrl && key.name === "c") || keySeq(raw, key) === "\x03";
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function isEscape(raw, key = {}) {
|
|
99
|
-
const seq = keySeq(raw, key);
|
|
100
|
-
return key.name === "escape" || seq === "\x1b";
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
function isUpKey(raw, key = {}) {
|
|
104
|
-
const seq = keySeq(raw, key);
|
|
105
|
-
return key.name === "up" || seq === "\x1b[A" || seq === "\x1bOA";
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
function isDownKey(raw, key = {}) {
|
|
109
|
-
const seq = keySeq(raw, key);
|
|
110
|
-
return key.name === "down" || seq === "\x1b[B" || seq === "\x1bOB";
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
function isEnterKey(raw, key = {}) {
|
|
114
|
-
const seq = keySeq(raw, key);
|
|
115
|
-
return key.name === "return" || key.name === "enter" || seq === "\r" || seq === "\n";
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function isSpaceKey(raw, key = {}) {
|
|
119
|
-
return key.name === "space" || keySeq(raw, key) === " ";
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function numberKey(raw, key = {}) {
|
|
123
|
-
const seq = keySeq(raw, key);
|
|
124
|
-
return /^[1-9]$/.test(seq) ? Number.parseInt(seq, 10) : Number.NaN;
|
|
125
|
-
}
|
|
126
|
-
|
|
127
89
|
function renderMultiChoice(title, choices, index, selected, subtitle) {
|
|
128
90
|
renderHeader(subtitle);
|
|
129
91
|
console.log("");
|
|
@@ -139,21 +101,51 @@ function renderMultiChoice(title, choices, index, selected, subtitle) {
|
|
|
139
101
|
if (choice.description) console.log(` ${paint(choice.description, focused ? t.blue : t.muted)}`);
|
|
140
102
|
if (idx < choices.length - 1) console.log("");
|
|
141
103
|
});
|
|
142
|
-
}
|
|
143
|
-
|
|
144
|
-
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function rawKeySeq(raw) {
|
|
107
|
+
if (Buffer.isBuffer(raw)) return raw.toString("latin1");
|
|
108
|
+
return String(raw ?? "");
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function readKeyFromBuffer(buffer) {
|
|
112
|
+
if (!buffer) return { pending: true, rest: "" };
|
|
113
|
+
if (buffer === "\x1b" || buffer === "\x1b[" || buffer === "\x1bO" || buffer === "\x00" || buffer === "\xe0") {
|
|
114
|
+
return { pending: true, rest: buffer };
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const three = buffer.slice(0, 3);
|
|
118
|
+
if (three === "\x1b[A" || three === "\x1bOA") return { key: { name: "up", sequence: three }, rest: buffer.slice(3) };
|
|
119
|
+
if (three === "\x1b[B" || three === "\x1bOB") return { key: { name: "down", sequence: three }, rest: buffer.slice(3) };
|
|
120
|
+
|
|
121
|
+
const two = buffer.slice(0, 2);
|
|
122
|
+
if (two === "\x00H" || two === "\xe0H") return { key: { name: "up", sequence: two }, rest: buffer.slice(2) };
|
|
123
|
+
if (two === "\x00P" || two === "\xe0P") return { key: { name: "down", sequence: two }, rest: buffer.slice(2) };
|
|
124
|
+
if (two === "\r\n") return { key: { name: "enter", sequence: two }, rest: buffer.slice(2) };
|
|
125
|
+
|
|
126
|
+
const seq = buffer[0];
|
|
127
|
+
if (seq === "\x03") return { key: { name: "ctrl-c", sequence: seq }, rest: buffer.slice(1) };
|
|
128
|
+
if (seq === "\r" || seq === "\n") return { key: { name: "enter", sequence: seq }, rest: buffer.slice(1) };
|
|
129
|
+
if (seq === " ") return { key: { name: "space", sequence: seq }, rest: buffer.slice(1) };
|
|
130
|
+
if (seq === "\x1b") return { key: { name: "escape", sequence: seq }, rest: buffer.slice(1) };
|
|
131
|
+
if (/^[1-9]$/.test(seq)) return { key: { name: "number", number: Number.parseInt(seq, 10), sequence: seq }, rest: buffer.slice(1) };
|
|
132
|
+
return { key: { name: seq.toLowerCase(), sequence: seq }, rest: buffer.slice(1) };
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
async function askMultiChoice(rl, title, choices, subtitle) {
|
|
145
136
|
if (process.stdin.isTTY && process.stdout.isTTY) {
|
|
146
137
|
rl.pause();
|
|
147
138
|
return await new Promise((resolve) => {
|
|
148
|
-
let index = 0;
|
|
149
|
-
|
|
150
|
-
const
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
stdin.
|
|
156
|
-
|
|
139
|
+
let index = 0;
|
|
140
|
+
let keyBuffer = "";
|
|
141
|
+
const selected = new Set(choices.filter((choice) => choice.selected).map((choice) => choice.value));
|
|
142
|
+
const stdin = process.stdin;
|
|
143
|
+
|
|
144
|
+
function cleanup(value) {
|
|
145
|
+
stdin.off("data", onData);
|
|
146
|
+
if (stdin.isTTY) stdin.setRawMode(false);
|
|
147
|
+
stdin.pause();
|
|
148
|
+
rl.resume();
|
|
157
149
|
clearScreen();
|
|
158
150
|
resolve(value);
|
|
159
151
|
}
|
|
@@ -162,37 +154,47 @@ async function askMultiChoice(rl, title, choices, subtitle) {
|
|
|
162
154
|
const value = choices[index].value;
|
|
163
155
|
if (selected.has(value)) selected.delete(value);
|
|
164
156
|
else selected.add(value);
|
|
165
|
-
renderMultiChoice(title, choices, index, selected, subtitle);
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
function
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
157
|
+
renderMultiChoice(title, choices, index, selected, subtitle);
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function onData(raw) {
|
|
161
|
+
keyBuffer += rawKeySeq(raw);
|
|
162
|
+
while (keyBuffer) {
|
|
163
|
+
const result = readKeyFromBuffer(keyBuffer);
|
|
164
|
+
if (result.pending) {
|
|
165
|
+
keyBuffer = result.rest;
|
|
166
|
+
return;
|
|
167
|
+
}
|
|
168
|
+
keyBuffer = result.rest;
|
|
169
|
+
const key = result.key;
|
|
170
|
+
|
|
171
|
+
if (key.name === "ctrl-c") return cleanup([]);
|
|
172
|
+
if (key.name === "up") {
|
|
173
|
+
index = (index - 1 + choices.length) % choices.length;
|
|
174
|
+
renderMultiChoice(title, choices, index, selected, subtitle);
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
if (key.name === "down") {
|
|
178
|
+
index = (index + 1) % choices.length;
|
|
179
|
+
renderMultiChoice(title, choices, index, selected, subtitle);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (key.name === "q" || key.name === "escape") return cleanup([]);
|
|
183
|
+
if (key.name === "space") return toggle();
|
|
184
|
+
if (key.name === "enter") return cleanup([...selected]);
|
|
185
|
+
const number = key.name === "number" ? key.number : Number.NaN;
|
|
186
|
+
if (Number.isInteger(number) && choices[number - 1]) {
|
|
187
|
+
index = number - 1;
|
|
188
|
+
toggle();
|
|
189
|
+
}
|
|
179
190
|
}
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
readline.emitKeypressEvents(stdin);
|
|
191
|
-
if (stdin.isTTY) stdin.setRawMode(true);
|
|
192
|
-
stdin.on("keypress", onKeypress);
|
|
193
|
-
stdin.resume();
|
|
194
|
-
renderMultiChoice(title, choices, index, selected, subtitle);
|
|
195
|
-
});
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
if (stdin.isTTY) stdin.setRawMode(true);
|
|
194
|
+
stdin.on("data", onData);
|
|
195
|
+
stdin.resume();
|
|
196
|
+
renderMultiChoice(title, choices, index, selected, subtitle);
|
|
197
|
+
});
|
|
196
198
|
}
|
|
197
199
|
|
|
198
200
|
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.
|
|
3
|
+
"version": "0.1.8",
|
|
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.
|
|
28
|
+
"oh-langfuse": "^0.1.10",
|
|
29
29
|
"oh-aireport": "^0.1.1"
|
|
30
30
|
}
|
|
31
31
|
}
|