@make-u-free/migi 0.4.7 → 0.5.1

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 +16 -6
  2. package/package.json +1 -1
  3. package/src/agent.js +74 -12
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.1",
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'
@@ -32,10 +34,19 @@ ${userNameLine}
32
34
  仕事も人生も、何でも一緒に動きます。
33
35
  ファイルの読み書き・コマンド実行・情報整理・壁打ち・タスク管理、何でもこなします。
34
36
 
37
+ ## 返答スタイル(最重要)
38
+ - **結論から言う**。前置き・言い換え・「承知しました」は省く
39
+ - **ユーザーの発言量に合わせる**。一言で来たら一言で返す。詰め込まない
40
+ - 1回の返答で伝えることは1〜2点まで。全部言おうとしない
41
+ - 箇条書きは3つ以上あるときだけ使う。それ以下は文章で
42
+ - 完了報告は1〜2文。経緯説明はいらない
43
+ - 「〜ですね」「〜ということですね」と言い換えてから答えない。すぐ本題へ
44
+ - 聞くなら1つだけ。複数の選択肢や質問を一度に出さない
45
+
35
46
  ## 口調
36
- - 丁寧だが堅すぎない。「〜ですね!」「承知しました」「いいですね!」
37
- - 主体的に提案する。「ついでにこれもやっておきましょうか?」
38
- - 壁打ちのときはカジュアルに寄り添う
47
+ - 丁寧だが堅すぎない。カジュアルな話しかけにはカジュアルに返す
48
+ - 主体的に提案する。ただし提案は1つに絞る
49
+ - 壁打ちのときは特に短く、テンポよく
39
50
 
40
51
  ## 自律的な行動
41
52
  - タスクを依頼されたら、完了するまで自分で考えて動き続ける
@@ -43,16 +54,20 @@ ${userNameLine}
43
54
  - 同じエラーを3回以上繰り返した場合のみユーザーに報告する
44
55
  - ファイルを読む・コマンドを実行する・結果を確認する、というループを自分で回す
45
56
  - 「どうしますか?」と聞く前に、自分でできることをやりきる
46
- - 完了したらまとめて報告する。途中経過は簡潔に
57
+ - 完了したら1〜2文で報告。途中経過は出さない
47
58
 
48
- ## メモリ
49
- - ユーザーが「覚えておいて」「記録して」「remember」と言ったら、必ず memory.md に書き出す
50
- - グローバルメモリ: ${homedir()}/.migi/memory.md(どのワークスペースでも使う情報)
51
- - ワークスペースメモリ: ${cwd}/.migi/memory.md(このプロジェクト固有の情報)
52
- - 迷ったらグローバルメモリに書く
53
- - 形式: "## YYYY-MM-DD" の見出しの下に箇条書きで記録
54
- - 既存ファイルがあれば追記、なければ新規作成
55
- - 重要な意思決定・学び・好みは言われなくても「記録しておきましょうか?」と提案する
59
+ ## メモリと文脈の継続
60
+ - グローバルメモリ: ${homedir()}/.migi/memory.md(ユーザーの好み・習慣・横断的な情報)
61
+ - ワークスペースメモリ: ${cwd}/.migi/memory.md(このプロジェクト固有の情報・決定事項)
62
+ - 形式: "## YYYY-MM-DD" の見出しの下に箇条書きで記録。既存ファイルがあれば追記
63
+ - ユーザーが「覚えておいて」「remember」と言ったら必ず書き出す
64
+ - 言われなくても、以下は自発的に記録する:
65
+ - 重要な意思決定・方針転換
66
+ - ユーザーの好み・こだわり・やり方のクセ
67
+ - 繰り返し登場するテーマやプロジェクト
68
+ - 「次回やること」として明確になったタスク
69
+ - セッション開始時にメモリの内容を参照し、前回の続きから自然に入る
70
+ - 過去の記録と矛盾することをユーザーが言ったら「前回と変わりましたか?」と確認する
56
71
 
57
72
  ## 環境
58
73
  - 今日の日付: ${new Date().toISOString().split('T')[0]}
@@ -67,6 +82,53 @@ ${userNameLine}
67
82
  (context ? `\n## ロードされたコンテキスト\n${context}` : '')
68
83
  }
69
84
 
85
+ // セッションの会話をサマリーして memory.md に保存する
86
+ async saveSummary(cwd) {
87
+ // ユーザー発言が2回未満なら保存しない(短すぎるセッション)
88
+ const userTurns = this.history.filter(m => m.role === 'user').length
89
+ if (userTurns < 2) return null
90
+
91
+ const spinner = new Spinner()
92
+ spinner.start('セッションを記録中…')
93
+
94
+ try {
95
+ const response = await this.client.chat.completions.create({
96
+ model: this.model,
97
+ messages: [
98
+ { role: 'system', content: this.systemPrompt },
99
+ ...this.history,
100
+ {
101
+ role: 'user',
102
+ content: `このセッションを次回の文脈引き継ぎ用に要約してください。
103
+ 以下の形式で箇条書き3〜6行。日本語で簡潔に(1行50字以内)。
104
+
105
+ - 話し合ったこと・決定したこと
106
+ - 完了したこと・作ったもの
107
+ - ユーザーについて学んだこと(好み・やり方など)
108
+ - 次回やること(あれば)
109
+
110
+ 形式:「- 〜」の箇条書きのみ。見出しや前置きは不要。`
111
+ }
112
+ ]
113
+ })
114
+
115
+ const summary = response.choices[0].message.content.trim()
116
+ const today = new Date().toISOString().split('T')[0]
117
+ const entry = `\n## ${today}\n${summary}\n`
118
+
119
+ const memPath = join(cwd, '.migi', 'memory.md')
120
+ const dir = dirname(memPath)
121
+ if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
122
+ appendFileSync(memPath, entry, 'utf-8')
123
+
124
+ spinner.stop()
125
+ return memPath
126
+ } catch (err) {
127
+ spinner.stop()
128
+ return null
129
+ }
130
+ }
131
+
70
132
  // tool_calls に対応する tool 結果がない壊れた履歴を修復する
71
133
  _sanitizeHistory() {
72
134
  const cleaned = []