@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 +1 -1
- package/src/agent.js +70 -35
- package/src/context.js +15 -2
package/package.json
CHANGED
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,
|
|
5
|
-
import { join
|
|
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
|
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
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
|
-
//
|
|
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
|
|
107
|
-
|
|
108
|
-
|
|
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
|
-
|
|
111
|
-
const
|
|
112
|
-
|
|
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
|
|
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.
|
|
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')
|