@make-u-free/migi 0.5.0 → 0.5.2

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@make-u-free/migi",
3
- "version": "0.5.0",
3
+ "version": "0.5.2",
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,8 +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
+ import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs'
5
+ import { join } from 'path'
6
6
  import { toolSchemas, teamsToolSchema, executeTool } from './tools.js'
7
7
  import { createPermissionChecker } from './permissions.js'
8
8
  import { httpsAgent } from './tls.js'
@@ -34,10 +34,19 @@ ${userNameLine}
34
34
  仕事も人生も、何でも一緒に動きます。
35
35
  ファイルの読み書き・コマンド実行・情報整理・壁打ち・タスク管理、何でもこなします。
36
36
 
37
+ ## 返答スタイル(最重要)
38
+ - **結論から言う**。前置き・言い換え・「承知しました」は省く
39
+ - **ユーザーの発言量に合わせる**。一言で来たら一言で返す。詰め込まない
40
+ - 1回の返答で伝えることは1〜2点まで。全部言おうとしない
41
+ - 箇条書きは3つ以上あるときだけ使う。それ以下は文章で
42
+ - 完了報告は1〜2文。経緯説明はいらない
43
+ - 「〜ですね」「〜ということですね」と言い換えてから答えない。すぐ本題へ
44
+ - 聞くなら1つだけ。複数の選択肢や質問を一度に出さない
45
+
37
46
  ## 口調
38
- - 丁寧だが堅すぎない。「〜ですね!」「承知しました」「いいですね!」
39
- - 主体的に提案する。「ついでにこれもやっておきましょうか?」
40
- - 壁打ちのときはカジュアルに寄り添う
47
+ - 丁寧だが堅すぎない。カジュアルな話しかけにはカジュアルに返す
48
+ - 主体的に提案する。ただし提案は1つに絞る
49
+ - 壁打ちのときは特に短く、テンポよく
41
50
 
42
51
  ## 自律的な行動
43
52
  - タスクを依頼されたら、完了するまで自分で考えて動き続ける
@@ -45,20 +54,21 @@ ${userNameLine}
45
54
  - 同じエラーを3回以上繰り返した場合のみユーザーに報告する
46
55
  - ファイルを読む・コマンドを実行する・結果を確認する、というループを自分で回す
47
56
  - 「どうしますか?」と聞く前に、自分でできることをやりきる
48
- - 完了したらまとめて報告する。途中経過は簡潔に
57
+ - 完了したら1〜2文で報告。途中経過は出さない
49
58
 
50
59
  ## メモリと文脈の継続
51
- - グローバルメモリ: ${homedir()}/.migi/memory.md(ユーザーの好み・習慣・横断的な情報)
52
- - ワークスペースメモリ: ${cwd}/.migi/memory.md(このプロジェクト固有の情報・決定事項)
53
- - 形式: "## YYYY-MM-DD" の見出しの下に箇条書きで記録。既存ファイルがあれば追記
54
- - ユーザーが「覚えておいて」「remember」と言ったら必ず書き出す
55
- - 言われなくても、以下は自発的に記録する:
56
- - 重要な意思決定・方針転換
57
- - ユーザーの好み・こだわり・やり方のクセ
58
- - 繰り返し登場するテーマやプロジェクト
59
- - 「次回やること」として明確になったタスク
60
- - セッション開始時にメモリの内容を参照し、前回の続きから自然に入る
61
- - 過去の記録と矛盾することをユーザーが言ったら「前回と変わりましたか?」と確認する
60
+ ワークスペースメモリは ${cwd}/.migi/memory/ に構造化して保存する:
61
+ - projects.md ── 進行中の仕事・状況
62
+ - feedback.md ── ユーザーの好み・作業スタイル・こだわり
63
+ - next-actions.md ── 次回やること(毎セッション更新)
64
+
65
+ グローバルメモリ: ${homedir()}/.migi/memory.md(横断的なユーザー情報)
66
+
67
+ 運用ルール:
68
+ - セッション開始時にメモリを参照し、前回の続きから自然に入る
69
+ - ユーザーが「覚えておいて」と言ったら write_file で即座に該当ファイルを更新
70
+ - 重要な決定・好み・方針転換は言われなくても「記録しておきましょうか?」と提案
71
+ - 過去の記録と矛盾したら「前回と変わりましたか?」と確認する
62
72
 
63
73
  ## 環境
64
74
  - 今日の日付: ${new Date().toISOString().split('T')[0]}
@@ -73,9 +83,8 @@ ${userNameLine}
73
83
  (context ? `\n## ロードされたコンテキスト\n${context}` : '')
74
84
  }
75
85
 
76
- // セッションの会話をサマリーして memory.md に保存する
86
+ // セッション終了時にメモリファイルを構造化更新する
77
87
  async saveSummary(cwd) {
78
- // ユーザー発言が2回未満なら保存しない(短すぎるセッション)
79
88
  const userTurns = this.history.filter(m => m.role === 'user').length
80
89
  if (userTurns < 2) return null
81
90
 
@@ -83,6 +92,20 @@ ${userNameLine}
83
92
  spinner.start('セッションを記録中…')
84
93
 
85
94
  try {
95
+ const memDir = join(cwd, '.migi', 'memory')
96
+ const files = ['projects.md', 'feedback.md', 'next-actions.md']
97
+
98
+ // 既存ファイルの内容を読む
99
+ const current = {}
100
+ for (const f of files) {
101
+ const p = join(memDir, f)
102
+ current[f] = existsSync(p) ? readFileSync(p, 'utf-8').trim() : '(未記録)'
103
+ }
104
+
105
+ const currentDump = files
106
+ .map(f => `### ${f}\n${current[f]}`)
107
+ .join('\n\n')
108
+
86
109
  const response = await this.client.chat.completions.create({
87
110
  model: this.model,
88
111
  messages: [
@@ -90,30 +113,42 @@ ${userNameLine}
90
113
  ...this.history,
91
114
  {
92
115
  role: 'user',
93
- content: `このセッションを次回の文脈引き継ぎ用に要約してください。
94
- 以下の形式で箇条書き3〜6行。日本語で簡潔に(1行50字以内)。
116
+ content: `このセッションの内容を踏まえ、以下のメモリファイルを更新してください。
95
117
 
96
- - 話し合ったこと・決定したこと
97
- - 完了したこと・作ったもの
98
- - ユーザーについて学んだこと(好み・やり方など)
99
- - 次回やること(あれば)
118
+ 現在の内容:
119
+ ${currentDump}
100
120
 
101
- 形式:「- 〜」の箇条書きのみ。見出しや前置きは不要。`
121
+ JSON形式のみで返答(他のテキスト不要):
122
+ {
123
+ "projects.md": "進行中の仕事・状況(15行以内)",
124
+ "feedback.md": "ユーザーの好み・作業スタイル・こだわり(15行以内)",
125
+ "next-actions.md": "次回やること(今回判明したもののみ・前回分は消す)"
126
+ }
127
+
128
+ ルール: 新情報は追加、古い情報は上書き、不要なものは削除。変化なければそのまま返す。`
102
129
  }
103
- ]
130
+ ],
131
+ response_format: { type: 'json_object' }
104
132
  })
105
133
 
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`
134
+ const updates = JSON.parse(response.choices[0].message.content)
135
+
136
+ if (!existsSync(memDir)) mkdirSync(memDir, { recursive: true })
137
+
138
+ const updated = []
139
+ for (const [filename, content] of Object.entries(updates)) {
140
+ if (files.includes(filename) && content?.trim() && content.trim() !== '(未記録)') {
141
+ writeFileSync(join(memDir, filename), content.trim() + '\n', 'utf-8')
142
+ updated.push(filename)
143
+ }
144
+ }
109
145
 
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')
146
+ // インデックスを更新
147
+ const indexLines = updated.map(f => `- [memory/${f}](memory/${f})`).join('\n')
148
+ writeFileSync(join(cwd, '.migi', 'memory.md'), `# メモリインデックス\n\n${indexLines}\n`, 'utf-8')
114
149
 
115
150
  spinner.stop()
116
- return memPath
151
+ return memDir
117
152
  } catch (err) {
118
153
  spinner.stop()
119
154
  return null
package/src/context.js CHANGED
@@ -34,8 +34,21 @@ export async function loadContext(cwd = process.cwd()) {
34
34
  // 1. グローバルメモリ (~/.migi/memory.md)
35
35
  load('グローバルメモリ', join(homedir(), '.migi', 'memory.md'))
36
36
 
37
- // 2. ワークスペースメモリ (.migi/memory.md)
38
- load('ワークスペースメモリ', join(cwd, '.migi', 'memory.md'))
37
+ // 2. ワークスペースメモリ: インデックス + memory/ 以下の個別ファイル
38
+ load('ワークスペースメモリ(インデックス)', join(cwd, '.migi', 'memory.md'))
39
+ const memDir = join(cwd, '.migi', 'memory')
40
+ if (existsSync(memDir)) {
41
+ const memFiles = await glob('*.md', { cwd: memDir })
42
+ // next-actions を最後に(直近の文脈として読ませる)
43
+ memFiles.sort((a, b) => {
44
+ if (a === 'next-actions.md') return 1
45
+ if (b === 'next-actions.md') return -1
46
+ return a.localeCompare(b)
47
+ })
48
+ for (const f of memFiles) {
49
+ load(`メモリ/${f}`, join(memDir, f))
50
+ }
51
+ }
39
52
 
40
53
  // 3. ルートの MIGI.md → CLAUDE.md
41
54
  loadWithFallback('', cwd, 'CLAUDE.md')