@make-u-free/migi 0.4.7 → 0.5.0

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
@@ -149,12 +149,11 @@ async function readChatInput() {
149
149
  }
150
150
 
151
151
  if (key.ctrl && key.name === 'c') {
152
- if (drawPending) { drawPending = false; draw() } // カーソル位置を確定させてから終了
152
+ if (drawPending) { drawPending = false; draw() }
153
153
  process.stdout.write(`\x1b[${drawnLines - 1 - curLine}B\n`)
154
154
  process.stdin.removeListener('keypress', onKey)
155
155
  if (process.stdin.isTTY) process.stdin.setRawMode(false)
156
- console.log(chalk.cyan('\n お疲れ様でした!またね。\n'))
157
- process.exit(0)
156
+ resolve(null) // null = 終了シグナル(メインループで後処理)
158
157
  }
159
158
 
160
159
  if (key.name === 'return') {
@@ -198,18 +197,29 @@ async function readChatInput() {
198
197
  })
199
198
  }
200
199
 
200
+ // ---- セッション終了(サマリー保存 → 挨拶 → exit) ----
201
+ async function gracefulExit() {
202
+ const saved = await agent.saveSummary(cwd)
203
+ if (saved) {
204
+ console.log(chalk.dim(`\n セッションを記録しました → ${saved}`))
205
+ }
206
+ console.log(chalk.cyan(`\n お疲れ様でした!またね。\n`))
207
+ process.exit(0)
208
+ }
209
+
201
210
  // ---- メインループ ----
202
211
  async function prompt() {
203
212
  // 入力ボックス上辺(ユーザー名をセパレーターに埋め込む)
204
213
  console.log('\n' + sepWithLabel(chalk.bold.cyan(userName || 'あなた')))
205
214
 
206
- const input = (await readChatInput()).trim()
215
+ const rawInput = await readChatInput()
216
+ if (rawInput === null) return gracefulExit() // Ctrl+C
217
+ const input = rawInput.trim()
207
218
  if (!input) return prompt()
208
219
 
209
220
  // --- ビルトインコマンド ---
210
221
  if (input === '/exit' || input === '/quit') {
211
- console.log(chalk.cyan(`\n お疲れ様でした!またね。\n`))
212
- process.exit(0)
222
+ return gracefulExit()
213
223
  }
214
224
 
215
225
  if (input === '/config') {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@make-u-free/migi",
3
- "version": "0.4.7",
3
+ "version": "0.5.0",
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
@@ -1,6 +1,8 @@
1
1
  import OpenAI from 'openai'
2
2
  import chalk from 'chalk'
3
3
  import { homedir } from 'os'
4
+ import { existsSync, appendFileSync, mkdirSync } from 'fs'
5
+ import { join, dirname } from 'path'
4
6
  import { toolSchemas, teamsToolSchema, executeTool } from './tools.js'
5
7
  import { createPermissionChecker } from './permissions.js'
6
8
  import { httpsAgent } from './tls.js'
@@ -45,14 +47,18 @@ ${userNameLine}
45
47
  - 「どうしますか?」と聞く前に、自分でできることをやりきる
46
48
  - 完了したらまとめて報告する。途中経過は簡潔に
47
49
 
48
- ## メモリ
49
- - ユーザーが「覚えておいて」「記録して」「remember」と言ったら、必ず memory.md に書き出す
50
- - グローバルメモリ: ${homedir()}/.migi/memory.md(どのワークスペースでも使う情報)
51
- - ワークスペースメモリ: ${cwd}/.migi/memory.md(このプロジェクト固有の情報)
52
- - 迷ったらグローバルメモリに書く
53
- - 形式: "## YYYY-MM-DD" の見出しの下に箇条書きで記録
54
- - 既存ファイルがあれば追記、なければ新規作成
55
- - 重要な意思決定・学び・好みは言われなくても「記録しておきましょうか?」と提案する
50
+ ## メモリと文脈の継続
51
+ - グローバルメモリ: ${homedir()}/.migi/memory.md(ユーザーの好み・習慣・横断的な情報)
52
+ - ワークスペースメモリ: ${cwd}/.migi/memory.md(このプロジェクト固有の情報・決定事項)
53
+ - 形式: "## YYYY-MM-DD" の見出しの下に箇条書きで記録。既存ファイルがあれば追記
54
+ - ユーザーが「覚えておいて」「remember」と言ったら必ず書き出す
55
+ - 言われなくても、以下は自発的に記録する:
56
+ - 重要な意思決定・方針転換
57
+ - ユーザーの好み・こだわり・やり方のクセ
58
+ - 繰り返し登場するテーマやプロジェクト
59
+ - 「次回やること」として明確になったタスク
60
+ - セッション開始時にメモリの内容を参照し、前回の続きから自然に入る
61
+ - 過去の記録と矛盾することをユーザーが言ったら「前回と変わりましたか?」と確認する
56
62
 
57
63
  ## 環境
58
64
  - 今日の日付: ${new Date().toISOString().split('T')[0]}
@@ -67,6 +73,53 @@ ${userNameLine}
67
73
  (context ? `\n## ロードされたコンテキスト\n${context}` : '')
68
74
  }
69
75
 
76
+ // セッションの会話をサマリーして memory.md に保存する
77
+ async saveSummary(cwd) {
78
+ // ユーザー発言が2回未満なら保存しない(短すぎるセッション)
79
+ const userTurns = this.history.filter(m => m.role === 'user').length
80
+ if (userTurns < 2) return null
81
+
82
+ const spinner = new Spinner()
83
+ spinner.start('セッションを記録中…')
84
+
85
+ try {
86
+ const response = await this.client.chat.completions.create({
87
+ model: this.model,
88
+ messages: [
89
+ { role: 'system', content: this.systemPrompt },
90
+ ...this.history,
91
+ {
92
+ role: 'user',
93
+ content: `このセッションを次回の文脈引き継ぎ用に要約してください。
94
+ 以下の形式で箇条書き3〜6行。日本語で簡潔に(1行50字以内)。
95
+
96
+ - 話し合ったこと・決定したこと
97
+ - 完了したこと・作ったもの
98
+ - ユーザーについて学んだこと(好み・やり方など)
99
+ - 次回やること(あれば)
100
+
101
+ 形式:「- 〜」の箇条書きのみ。見出しや前置きは不要。`
102
+ }
103
+ ]
104
+ })
105
+
106
+ const summary = response.choices[0].message.content.trim()
107
+ const today = new Date().toISOString().split('T')[0]
108
+ const entry = `\n## ${today}\n${summary}\n`
109
+
110
+ const memPath = join(cwd, '.migi', 'memory.md')
111
+ const dir = dirname(memPath)
112
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
113
+ appendFileSync(memPath, entry, 'utf-8')
114
+
115
+ spinner.stop()
116
+ return memPath
117
+ } catch (err) {
118
+ spinner.stop()
119
+ return null
120
+ }
121
+ }
122
+
70
123
  // tool_calls に対応する tool 結果がない壊れた履歴を修復する
71
124
  _sanitizeHistory() {
72
125
  const cleaned = []