@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 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 // カーソルの物理行(drawn area 先頭からの offset)
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)), // 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
- // ⑤ カーソルを入力内容の末尾へ(全角文字は2カラム幅なので displayWidth を使う)
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) { lines[curLine] += str; scheduleDraw() }
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) // 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
- lines.splice(curLine + 1, 0, '')
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 (lines[curLine].length > 0) {
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] += str
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@make-u-free/migi",
3
- "version": "0.5.16",
3
+ "version": "0.5.18",
4
4
  "description": "Your AI right-hand agent. Works anywhere, with any LLM API.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/agent.js CHANGED
@@ -64,7 +64,7 @@ ${userNameLine}
64
64
  ## 部署の自律的な切り替え(重要)
65
65
 
66
66
  - 会話の内容から、どの部署の話題かを常に判断する
67
- - 部署に関連する話題が出てきたら、まず `.company/[部署名]/` フォルダに MIGI.md または CLAUDE.md があるか確認し、あれば read_file で読んでから作業する
67
+ - 部署に関連する話題が出てきたら、まず .company/[部署名]/ フォルダに MIGI.md または CLAUDE.md があるか確認し、あれば read_file で読んでから作業する
68
68
  - 部署が切り替わったと判断したら、新しい部署のファイルを読み直す
69
69
  - ユーザーに「どの部署ですか?」と聞かない。会話の文脈から自分で判断する
70
70
  - 判断に迷う場合は、list_files で `.company/` 以下を確認して部署一覧を把握してから判断する