@make-u-free/migi 0.5.2 → 0.5.4
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/migi.js +25 -2
- package/package.json +2 -1
- package/src/tools.js +41 -0
package/bin/migi.js
CHANGED
|
@@ -79,6 +79,29 @@ function sepWithLabel(label) {
|
|
|
79
79
|
}
|
|
80
80
|
|
|
81
81
|
// ---- チャット入力(Enter送信 / Shift+Enter改行)----
|
|
82
|
+
// 全角文字(日本語・絵文字など)は端末上で2カラム幅を占める
|
|
83
|
+
// string.length はコードポイント数なので、カーソル位置計算に使うと日本語入力でズレる
|
|
84
|
+
function displayWidth(str) {
|
|
85
|
+
let w = 0
|
|
86
|
+
for (const ch of str) {
|
|
87
|
+
const cp = ch.codePointAt(0)
|
|
88
|
+
const wide =
|
|
89
|
+
(cp >= 0x1100 && cp <= 0x115F) || // Hangul Jamo
|
|
90
|
+
(cp >= 0x2E80 && cp <= 0x303F) || // CJK Radicals
|
|
91
|
+
(cp >= 0x3040 && cp <= 0x33FF) || // Hiragana〜CJK Compat
|
|
92
|
+
(cp >= 0x3400 && cp <= 0x9FFF) || // CJK Unified
|
|
93
|
+
(cp >= 0xAC00 && cp <= 0xD7FF) || // Hangul Syllables
|
|
94
|
+
(cp >= 0xF900 && cp <= 0xFAFF) || // CJK Compat Ideographs
|
|
95
|
+
(cp >= 0xFE10 && cp <= 0xFE1F) || // Vertical Forms
|
|
96
|
+
(cp >= 0xFE30 && cp <= 0xFE6F) || // CJK Compat Forms
|
|
97
|
+
(cp >= 0xFF01 && cp <= 0xFF60) || // Fullwidth ASCII
|
|
98
|
+
(cp >= 0xFFE0 && cp <= 0xFFE6) || // Fullwidth Signs
|
|
99
|
+
(cp >= 0x1F300 && cp <= 0x1FAFF) // Emoji
|
|
100
|
+
w += wide ? 2 : 1
|
|
101
|
+
}
|
|
102
|
+
return w
|
|
103
|
+
}
|
|
104
|
+
|
|
82
105
|
async function readChatInput() {
|
|
83
106
|
return new Promise((resolve) => {
|
|
84
107
|
const PFIRST = ' > '
|
|
@@ -132,9 +155,9 @@ async function readChatInput() {
|
|
|
132
155
|
if (linesFromBottom > 0) buf += `\x1b[${linesFromBottom}A`
|
|
133
156
|
buf += '\r'
|
|
134
157
|
|
|
135
|
-
// ⑤
|
|
158
|
+
// ⑤ カーソルを入力内容の末尾へ(全角文字は2カラム幅なので displayWidth を使う)
|
|
136
159
|
const prefix = curLine === 0 ? PFIRST : PCONT
|
|
137
|
-
buf += `\x1b[${prefix.length + lines[curLine]
|
|
160
|
+
buf += `\x1b[${prefix.length + displayWidth(lines[curLine]) + 1}G`
|
|
138
161
|
|
|
139
162
|
cursorLine = curLine
|
|
140
163
|
process.stdout.write(buf)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@make-u-free/migi",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "Your AI right-hand agent. Works anywhere, with any LLM API.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
"dependencies": {
|
|
13
13
|
"adm-zip": "^0.5.16",
|
|
14
14
|
"chalk": "^5.3.0",
|
|
15
|
+
"diff": "^8.0.4",
|
|
15
16
|
"dotenv": "^16.4.0",
|
|
16
17
|
"glob": "^11.0.0",
|
|
17
18
|
"openai": "^4.0.0",
|
package/src/tools.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, writeFileSync, appendFileSync, existsSync, mkdirSync } from 'fs'
|
|
2
2
|
import { execSync } from 'child_process'
|
|
3
3
|
import { dirname, extname } from 'path'
|
|
4
|
+
import { diffLines } from 'diff'
|
|
4
5
|
import { request } from 'https'
|
|
5
6
|
import { glob } from 'glob'
|
|
6
7
|
import xlsxPkg from 'xlsx'
|
|
@@ -126,6 +127,42 @@ export const teamsToolSchema = {
|
|
|
126
127
|
}
|
|
127
128
|
}
|
|
128
129
|
|
|
130
|
+
// ---- diff 表示 ----
|
|
131
|
+
|
|
132
|
+
import chalk from 'chalk'
|
|
133
|
+
|
|
134
|
+
function showDiff(path, oldContent, newContent) {
|
|
135
|
+
const MAX_LINES = 50 // 長すぎる diff は省略
|
|
136
|
+
|
|
137
|
+
if (oldContent === null) {
|
|
138
|
+
console.log(chalk.green(` + ${path} (新規作成)`))
|
|
139
|
+
return
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (oldContent === newContent) {
|
|
143
|
+
console.log(chalk.dim(` = ${path} (変更なし)`))
|
|
144
|
+
return
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
const parts = diffLines(oldContent, newContent)
|
|
148
|
+
let shown = 0
|
|
149
|
+
let truncated = false
|
|
150
|
+
|
|
151
|
+
for (const part of parts) {
|
|
152
|
+
if (!part.added && !part.removed) continue
|
|
153
|
+
const lines = part.value.replace(/\n$/, '').split('\n')
|
|
154
|
+
for (const line of lines) {
|
|
155
|
+
if (shown >= MAX_LINES) { truncated = true; break }
|
|
156
|
+
if (part.added) console.log(chalk.green(` + ${line}`))
|
|
157
|
+
if (part.removed) console.log(chalk.red(` - ${line}`))
|
|
158
|
+
shown++
|
|
159
|
+
}
|
|
160
|
+
if (truncated) break
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
if (truncated) console.log(chalk.dim(` … (省略)`))
|
|
164
|
+
}
|
|
165
|
+
|
|
129
166
|
// ---- ツール実行 ----
|
|
130
167
|
|
|
131
168
|
export async function executeTool(name, args, opts = {}) {
|
|
@@ -209,14 +246,18 @@ export async function executeTool(name, args, opts = {}) {
|
|
|
209
246
|
case 'write_file': {
|
|
210
247
|
const dir = dirname(args.path)
|
|
211
248
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
249
|
+
const oldContent = existsSync(args.path) ? readFileSync(args.path, 'utf-8') : null
|
|
212
250
|
writeFileSync(args.path, args.content, 'utf-8')
|
|
251
|
+
showDiff(args.path, oldContent, args.content)
|
|
213
252
|
return `完了: ${args.path} に書き込みました`
|
|
214
253
|
}
|
|
215
254
|
|
|
216
255
|
case 'append_file': {
|
|
217
256
|
const dir = dirname(args.path)
|
|
218
257
|
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
258
|
+
const base = existsSync(args.path) ? readFileSync(args.path, 'utf-8') : ''
|
|
219
259
|
appendFileSync(args.path, args.content, 'utf-8')
|
|
260
|
+
showDiff(args.path, base, base + args.content)
|
|
220
261
|
return `完了: ${args.path} に追記しました`
|
|
221
262
|
}
|
|
222
263
|
|