@make-u-free/migi 0.5.20 → 0.5.22

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 (3) hide show
  1. package/bin/migi.js +54 -20
  2. package/package.json +1 -1
  3. package/src/agent.js +11 -8
package/bin/migi.js CHANGED
@@ -73,18 +73,28 @@ const agent = new MigiAgent({ context, promptFn, apiKey, model, name: agentName,
73
73
  const today = new Date().toISOString().split('T')[0]
74
74
  console.log('\n' + chalk.bold.cyan(`─── ${agentName} `) + chalk.dim('─'.repeat(Math.max(0, (process.stdout.columns || 80) - agentName.length - 5))))
75
75
  try {
76
- await agent.chat(
77
- `起動した。以下の手順で今日の状況を確認して、簡潔にダッシュボードを表示してから、今一番優先すべきことを1つだけ提案して:\n` +
78
- `1. todos/${today}.md を read_file で読んで未完了タスクを確認(ファイルがなければスキップ)\n` +
79
- `2. search_content で「- \\[ \\]」を .company/ ディレクトリ全体から検索して、部署ごとの未完了タスクも集約する\n` +
80
- `3. .migi/memory/next-actions.md があれば読む\n` +
81
- `4. todos/ と .company/ の両方を合わせたダッシュボード(未完了の件数サマリー、ソース別)をコンパクトに出して、一言で「今日はこれから」と提案する\n` +
82
- ` - 未完了タスクは 1. 2. 3. と通し番号を付けて表示する(ファイルには書かない、表示だけ)\n` +
83
- ` - ユーザーが「N番完了」と言ったら、その番号のタスクを特定してファイルの [ ] [x] に書き換える\n` +
84
- `(詳細な説明はいらない。テンポよく)`
85
- )
76
+ const _abort = new AbortController()
77
+ const _sigint = () => { process.stdout.write(chalk.yellow('\n キャンセルしました\n')); _abort.abort() }
78
+ process.once('SIGINT', _sigint)
79
+ try {
80
+ await agent.chat(
81
+ `起動した。以下の手順で今日の状況を確認して、簡潔にダッシュボードを表示してから、今一番優先すべきことを1つだけ提案して:\n` +
82
+ `1. todos/${today}.md read_file で読んで未完了タスクを確認(ファイルがなければスキップ)\n` +
83
+ `2. search_content で「- \\[ \\]」を .company/ ディレクトリ全体から検索して、部署ごとの未完了タスクも集約する\n` +
84
+ `3. .migi/memory/next-actions.md があれば読む\n` +
85
+ `4. todos/ と .company/ の両方を合わせたダッシュボード(未完了の件数サマリー、ソース別)をコンパクトに出して、一言で「今日はこれから」と提案する\n` +
86
+ ` - 未完了タスクは 1. 2. 3. と通し番号を付けて表示する(ファイルには書かない、表示だけ)\n` +
87
+ ` - ユーザーが「N番完了」と言ったら、その番号のタスクを特定してファイルの [ ] を [x] に書き換える\n` +
88
+ `(詳細な説明はいらない。テンポよく)`,
89
+ _abort.signal
90
+ )
91
+ } finally {
92
+ process.removeListener('SIGINT', _sigint)
93
+ }
86
94
  } catch (err) {
87
- console.error(chalk.red(' 起動チェック失敗: ' + err.message))
95
+ if (err.name !== 'AbortError') {
96
+ console.error(chalk.red(' 起動チェック失敗: ' + err.message))
97
+ }
88
98
  }
89
99
  }
90
100
 
@@ -193,11 +203,22 @@ async function readChatInput() {
193
203
  }
194
204
 
195
205
  if (key.ctrl && key.name === 'c') {
196
- if (drawPending) { drawPending = false; draw() }
197
- process.stdout.write(`\x1b[${drawnLines - 1 - curLine}B\n`)
198
- process.stdin.removeListener('keypress', onKey)
199
- if (process.stdin.isTTY) process.stdin.setRawMode(false)
200
- resolve(null)
206
+ const isEmpty = lines.every(l => l.length === 0)
207
+ if (!isEmpty) {
208
+ // バッファをクリアして再描画(終了しない)
209
+ lines.splice(0, lines.length, '')
210
+ curLine = 0
211
+ cursorPos = 0
212
+ scheduleDraw()
213
+ } else {
214
+ // 空の状態で Ctrl+C → ヒントだけ出してそのまま
215
+ process.stdout.write(`\x1b[${drawnLines - 1 - curLine}B\r\n`)
216
+ process.stdout.write(chalk.dim(' 終了するには /exit を入力してください') + '\r\n')
217
+ drawnLines = 0
218
+ cursorLine = 0
219
+ draw()
220
+ }
221
+ return
201
222
  }
202
223
 
203
224
  if (key.name === 'return') {
@@ -340,6 +361,14 @@ async function prompt() {
340
361
  return prompt()
341
362
  }
342
363
 
364
+ // --- Ctrl+C で処理キャンセル(アプリ継続)---
365
+ const abortController = new AbortController()
366
+ const sigintHandler = () => {
367
+ process.stdout.write(chalk.yellow('\n キャンセルしました\n'))
368
+ abortController.abort()
369
+ }
370
+ process.once('SIGINT', sigintHandler)
371
+
343
372
  // --- スキルルーティング ---
344
373
  const parsed = parseSkillInput(input)
345
374
  if (parsed) {
@@ -348,12 +377,15 @@ async function prompt() {
348
377
  console.log('\n' + sepWithLabel(chalk.bold.cyan(agentName) + chalk.dim(` [スキル: ${parsed.name}]`)))
349
378
  const expanded = expandSkill(skill.content, parsed.args)
350
379
  try {
351
- await agent.chat(expanded)
380
+ await agent.chat(expanded, abortController.signal)
352
381
  } catch (err) {
353
- console.error(chalk.red('\n エラー: ' + err.message + '\n'))
382
+ if (err.name !== 'AbortError') console.error(chalk.red('\n エラー: ' + err.message + '\n'))
383
+ } finally {
384
+ process.removeListener('SIGINT', sigintHandler)
354
385
  }
355
386
  return prompt()
356
387
  } else {
388
+ process.removeListener('SIGINT', sigintHandler)
357
389
  console.log(chalk.yellow(`\n スキル「${parsed.name}」が見つかりません。`))
358
390
  console.log(chalk.dim(` .migi/skills/${parsed.name}.md を作成してください。`))
359
391
  return prompt()
@@ -363,9 +395,11 @@ async function prompt() {
363
395
  // --- 通常チャット ---
364
396
  console.log('\n' + sepWithLabel(chalk.bold.cyan(agentName)))
365
397
  try {
366
- await agent.chat(input)
398
+ await agent.chat(input, abortController.signal)
367
399
  } catch (err) {
368
- console.error(chalk.red('\n エラー: ' + err.message + '\n'))
400
+ if (err.name !== 'AbortError') console.error(chalk.red('\n エラー: ' + err.message + '\n'))
401
+ } finally {
402
+ process.removeListener('SIGINT', sigintHandler)
369
403
  }
370
404
 
371
405
  prompt()
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@make-u-free/migi",
3
- "version": "0.5.20",
3
+ "version": "0.5.22",
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
@@ -206,7 +206,7 @@ JSON形式のみで返答(他のテキスト不要):
206
206
  this.history = cleaned
207
207
  }
208
208
 
209
- async chat(userMessage) {
209
+ async chat(userMessage, signal = null) {
210
210
  this._sanitizeHistory()
211
211
  this.history.push({ role: 'user', content: userMessage })
212
212
 
@@ -220,13 +220,16 @@ JSON形式のみで返答(他のテキスト不要):
220
220
  while (true) {
221
221
  spinner.start('考え中…')
222
222
 
223
- const stream = await this.client.chat.completions.create({
224
- model: this.model,
225
- messages,
226
- tools: this.tools,
227
- tool_choice: 'auto',
228
- stream: true
229
- })
223
+ const stream = await this.client.chat.completions.create(
224
+ {
225
+ model: this.model,
226
+ messages,
227
+ tools: this.tools,
228
+ tool_choice: 'auto',
229
+ stream: true
230
+ },
231
+ signal ? { signal } : {}
232
+ )
230
233
 
231
234
  let content = ''
232
235
  const tcMap = {} // tool_calls をインデックスで蓄積