@make-u-free/migi 0.5.16 → 0.5.18
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 +62 -18
- package/package.json +1 -1
- package/src/agent.js +1 -1
package/bin/migi.js
CHANGED
|
@@ -127,14 +127,14 @@ async function readChatInput() {
|
|
|
127
127
|
const PCONT = ' '
|
|
128
128
|
const lines = ['']
|
|
129
129
|
let curLine = 0
|
|
130
|
+
let cursorPos = 0 // 行内のカーソル位置(文字インデックス)
|
|
130
131
|
let drawnLines = 0
|
|
131
|
-
let cursorLine = 0
|
|
132
|
+
let cursorLine = 0 // カーソルの物理行(drawn area 先頭からの offset)
|
|
132
133
|
let drawPending = false
|
|
133
134
|
|
|
134
135
|
emitKeypressEvents(process.stdin)
|
|
135
136
|
if (process.stdin.isTTY) process.stdin.setRawMode(true)
|
|
136
137
|
|
|
137
|
-
// ペースト等の連続入力をまとめて1回の描画にするためのデバウンス
|
|
138
138
|
function scheduleDraw() {
|
|
139
139
|
if (drawPending) return
|
|
140
140
|
drawPending = true
|
|
@@ -145,7 +145,7 @@ async function readChatInput() {
|
|
|
145
145
|
const w = process.stdout.columns || 80
|
|
146
146
|
const newLines = [
|
|
147
147
|
...lines.map((l, i) => chalk.cyan(i === 0 ? PFIRST : PCONT) + l),
|
|
148
|
-
chalk.dim('─'.repeat(w - 1)),
|
|
148
|
+
chalk.dim('─'.repeat(w - 1)),
|
|
149
149
|
chalk.dim(` ✦ ${model} · Alt+Enterで改行 / Enterで送信`)
|
|
150
150
|
]
|
|
151
151
|
const oldDrawnLines = drawnLines
|
|
@@ -153,30 +153,25 @@ async function readChatInput() {
|
|
|
153
153
|
|
|
154
154
|
let buf = ''
|
|
155
155
|
|
|
156
|
-
// ① drawn area 先頭まで戻る(cursorLine = カーソルが今いる物理行)
|
|
157
156
|
if (cursorLine > 0) buf += `\x1b[${cursorLine}A`
|
|
158
157
|
buf += '\r'
|
|
159
158
|
|
|
160
|
-
// ② 各行を上書き。「先クリア→描画」ではなく「描画→行末クリア」でちらつき防止
|
|
161
159
|
for (let i = 0; i < newLines.length; i++) {
|
|
162
160
|
buf += newLines[i] + '\x1b[K'
|
|
163
161
|
if (i < newLines.length - 1) buf += '\r\n'
|
|
164
162
|
}
|
|
165
163
|
|
|
166
|
-
// ③ 行数が減った場合、余分な古い行をクリア
|
|
167
164
|
for (let i = newLines.length; i < oldDrawnLines; i++) {
|
|
168
165
|
buf += '\r\n\x1b[2K'
|
|
169
166
|
}
|
|
170
167
|
|
|
171
|
-
// ④ curLine の行まで戻る
|
|
172
|
-
// step②+③後のカーソル位置は max(新行数, 旧行数)-1 行目
|
|
173
168
|
const linesFromBottom = Math.max(drawnLines, oldDrawnLines) - 1 - curLine
|
|
174
169
|
if (linesFromBottom > 0) buf += `\x1b[${linesFromBottom}A`
|
|
175
170
|
buf += '\r'
|
|
176
171
|
|
|
177
|
-
//
|
|
172
|
+
// カーソルをcursorPosの位置へ(末尾ではなく現在位置)
|
|
178
173
|
const prefix = curLine === 0 ? PFIRST : PCONT
|
|
179
|
-
buf += `\x1b[${prefix.length + displayWidth(lines[curLine]) + 1}G`
|
|
174
|
+
buf += `\x1b[${prefix.length + displayWidth(lines[curLine].slice(0, cursorPos)) + 1}G`
|
|
180
175
|
|
|
181
176
|
cursorLine = curLine
|
|
182
177
|
process.stdout.write(buf)
|
|
@@ -186,7 +181,11 @@ async function readChatInput() {
|
|
|
186
181
|
|
|
187
182
|
const onKey = (str, key) => {
|
|
188
183
|
if (!key) {
|
|
189
|
-
if (str) {
|
|
184
|
+
if (str) {
|
|
185
|
+
lines[curLine] = lines[curLine].slice(0, cursorPos) + str + lines[curLine].slice(cursorPos)
|
|
186
|
+
cursorPos += str.length
|
|
187
|
+
scheduleDraw()
|
|
188
|
+
}
|
|
190
189
|
return
|
|
191
190
|
}
|
|
192
191
|
|
|
@@ -195,17 +194,19 @@ async function readChatInput() {
|
|
|
195
194
|
process.stdout.write(`\x1b[${drawnLines - 1 - curLine}B\n`)
|
|
196
195
|
process.stdin.removeListener('keypress', onKey)
|
|
197
196
|
if (process.stdin.isTTY) process.stdin.setRawMode(false)
|
|
198
|
-
resolve(null)
|
|
197
|
+
resolve(null)
|
|
199
198
|
}
|
|
200
199
|
|
|
201
200
|
if (key.name === 'return') {
|
|
202
|
-
// Alt+Enter(macOS: Option+Enter)または Shift+Enter → 改行
|
|
203
201
|
if (key.meta || key.shift) {
|
|
204
|
-
|
|
202
|
+
// カーソル位置で行を分割して改行
|
|
203
|
+
const rest = lines[curLine].slice(cursorPos)
|
|
204
|
+
lines[curLine] = lines[curLine].slice(0, cursorPos)
|
|
205
|
+
lines.splice(curLine + 1, 0, rest)
|
|
205
206
|
curLine++
|
|
207
|
+
cursorPos = 0
|
|
206
208
|
scheduleDraw()
|
|
207
209
|
} else {
|
|
208
|
-
// Enter → 送信(保留中の描画があれば先に確定)
|
|
209
210
|
if (drawPending) { drawPending = false; draw() }
|
|
210
211
|
const content = lines.join('\n').trim()
|
|
211
212
|
if (!content) return
|
|
@@ -218,10 +219,15 @@ async function readChatInput() {
|
|
|
218
219
|
}
|
|
219
220
|
|
|
220
221
|
if (key.name === 'backspace') {
|
|
221
|
-
if (
|
|
222
|
-
lines[curLine] = lines[curLine].slice(0, -1)
|
|
222
|
+
if (cursorPos > 0) {
|
|
223
|
+
lines[curLine] = lines[curLine].slice(0, cursorPos - 1) + lines[curLine].slice(cursorPos)
|
|
224
|
+
cursorPos--
|
|
223
225
|
scheduleDraw()
|
|
224
226
|
} else if (curLine > 0) {
|
|
227
|
+
// 行頭でbackspace → 前の行に結合
|
|
228
|
+
const prev = lines[curLine - 1]
|
|
229
|
+
cursorPos = prev.length
|
|
230
|
+
lines[curLine - 1] = prev + lines[curLine]
|
|
225
231
|
lines.splice(curLine, 1)
|
|
226
232
|
curLine--
|
|
227
233
|
scheduleDraw()
|
|
@@ -229,8 +235,46 @@ async function readChatInput() {
|
|
|
229
235
|
return
|
|
230
236
|
}
|
|
231
237
|
|
|
238
|
+
if (key.name === 'delete') {
|
|
239
|
+
if (cursorPos < lines[curLine].length) {
|
|
240
|
+
lines[curLine] = lines[curLine].slice(0, cursorPos) + lines[curLine].slice(cursorPos + 1)
|
|
241
|
+
scheduleDraw()
|
|
242
|
+
}
|
|
243
|
+
return
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
if (key.name === 'left') {
|
|
247
|
+
if (cursorPos > 0) { cursorPos--; scheduleDraw() }
|
|
248
|
+
else if (curLine > 0) { curLine--; cursorPos = lines[curLine].length; scheduleDraw() }
|
|
249
|
+
return
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
if (key.name === 'right') {
|
|
253
|
+
if (cursorPos < lines[curLine].length) { cursorPos++; scheduleDraw() }
|
|
254
|
+
else if (curLine < lines.length - 1) { curLine++; cursorPos = 0; scheduleDraw() }
|
|
255
|
+
return
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
if (key.name === 'up' && curLine > 0) {
|
|
259
|
+
curLine--
|
|
260
|
+
cursorPos = Math.min(cursorPos, lines[curLine].length)
|
|
261
|
+
scheduleDraw()
|
|
262
|
+
return
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
if (key.name === 'down' && curLine < lines.length - 1) {
|
|
266
|
+
curLine++
|
|
267
|
+
cursorPos = Math.min(cursorPos, lines[curLine].length)
|
|
268
|
+
scheduleDraw()
|
|
269
|
+
return
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
if (key.name === 'home') { cursorPos = 0; scheduleDraw(); return }
|
|
273
|
+
if (key.name === 'end') { cursorPos = lines[curLine].length; scheduleDraw(); return }
|
|
274
|
+
|
|
232
275
|
if (str && !key.ctrl && !key.meta) {
|
|
233
|
-
lines[curLine]
|
|
276
|
+
lines[curLine] = lines[curLine].slice(0, cursorPos) + str + lines[curLine].slice(cursorPos)
|
|
277
|
+
cursorPos += str.length
|
|
234
278
|
scheduleDraw()
|
|
235
279
|
}
|
|
236
280
|
}
|
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -64,7 +64,7 @@ ${userNameLine}
|
|
|
64
64
|
## 部署の自律的な切り替え(重要)
|
|
65
65
|
|
|
66
66
|
- 会話の内容から、どの部署の話題かを常に判断する
|
|
67
|
-
- 部署に関連する話題が出てきたら、まず
|
|
67
|
+
- 部署に関連する話題が出てきたら、まず .company/[部署名]/ フォルダに MIGI.md または CLAUDE.md があるか確認し、あれば read_file で読んでから作業する
|
|
68
68
|
- 部署が切り替わったと判断したら、新しい部署のファイルを読み直す
|
|
69
69
|
- ユーザーに「どの部署ですか?」と聞かない。会話の文脈から自分で判断する
|
|
70
70
|
- 判断に迷う場合は、list_files で `.company/` 以下を確認して部署一覧を把握してから判断する
|