agent-mind 1.0.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/LICENSE +21 -0
- package/README.md +229 -0
- package/bin/cli.js +38 -0
- package/package.json +33 -0
- package/src/commands/doctor.js +269 -0
- package/src/commands/init.js +345 -0
- package/src/commands/meta.js +34 -0
- package/src/commands/upgrade.js +177 -0
- package/src/index.js +18 -0
- package/src/utils/detect-tools.js +62 -0
- package/src/utils/inject-adapter.js +65 -0
- package/src/utils/template.js +103 -0
- package/src/utils/version.js +71 -0
- package/template/.am-tools/compact.sh +171 -0
- package/template/.am-tools/guide.md +274 -0
- package/template/.am-tools/health-check.sh +165 -0
- package/template/.am-tools/validate.sh +174 -0
- package/template/BOOT.md +71 -0
- package/template/README.md +109 -0
- package/template/VERSION.md +57 -0
- package/template/adapters/claude.md +56 -0
- package/template/adapters/codex.md +33 -0
- package/template/adapters/cursor.md +35 -0
- package/template/adapters/gemini.md +32 -0
- package/template/config.md +33 -0
- package/template/history/episodes/_index.md +13 -0
- package/template/history/maintenance-log.md +9 -0
- package/template/history/reflections/_index.md +11 -0
- package/template/knowledge/domains/_template/failures/_index.md +19 -0
- package/template/knowledge/domains/_template/patterns.md +21 -0
- package/template/knowledge/insights.md +23 -0
- package/template/knowledge/stack/_template.md +20 -0
- package/template/protocols/compaction.md +101 -0
- package/template/protocols/maintenance.md +99 -0
- package/template/protocols/memory-ops.md +89 -0
- package/template/protocols/quality-gate.md +66 -0
- package/template/protocols/workflow.md +81 -0
- package/template/workspace/.gitkeep +0 -0
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const { execSync } = require('child_process');
|
|
5
|
+
const detectTools = require('../utils/detect-tools');
|
|
6
|
+
const { copyTemplate, replaceVarsInDir } = require('../utils/template');
|
|
7
|
+
const { injectSnippet, getSnippet, hasSnippet } = require('../utils/inject-adapter');
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Handle both TTY and piped stdin modes.
|
|
11
|
+
*
|
|
12
|
+
* TTY mode: Use readline.createInterface directly
|
|
13
|
+
* Piped mode: Read all stdin lines upfront into a buffer, then consume them
|
|
14
|
+
*
|
|
15
|
+
* This fixes the issue where piped stdin loses data when multiple readline
|
|
16
|
+
* interfaces are created/destroyed.
|
|
17
|
+
*/
|
|
18
|
+
let rl = null;
|
|
19
|
+
let isTTY = process.stdin.isTTY === true;
|
|
20
|
+
let pipeBuffer = [];
|
|
21
|
+
let pipeIndex = 0;
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Read all stdin lines into buffer (for piped mode only)
|
|
25
|
+
*/
|
|
26
|
+
function initPipeBuffer() {
|
|
27
|
+
return new Promise((resolve) => {
|
|
28
|
+
const lines = [];
|
|
29
|
+
process.stdin.setEncoding('utf8');
|
|
30
|
+
|
|
31
|
+
process.stdin.on('data', (chunk) => {
|
|
32
|
+
const allLines = chunk.split('\n');
|
|
33
|
+
allLines.forEach((line) => {
|
|
34
|
+
if (line.length > 0) {
|
|
35
|
+
lines.push(line);
|
|
36
|
+
}
|
|
37
|
+
});
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
process.stdin.on('end', () => {
|
|
41
|
+
pipeBuffer = lines;
|
|
42
|
+
pipeIndex = 0;
|
|
43
|
+
resolve();
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Get next line from pipe buffer
|
|
50
|
+
*/
|
|
51
|
+
function getNextPipeLine() {
|
|
52
|
+
if (pipeIndex < pipeBuffer.length) {
|
|
53
|
+
return pipeBuffer[pipeIndex++];
|
|
54
|
+
}
|
|
55
|
+
return '';
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Create and return a shared readline interface (TTY mode only)
|
|
60
|
+
*/
|
|
61
|
+
function getRL() {
|
|
62
|
+
if (!rl) {
|
|
63
|
+
rl = readline.createInterface({
|
|
64
|
+
input: process.stdin,
|
|
65
|
+
output: process.stdout,
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
return rl;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function closeRL() {
|
|
72
|
+
if (rl) {
|
|
73
|
+
rl.close();
|
|
74
|
+
rl = null;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Interactive prompt - handles both TTY and piped modes
|
|
80
|
+
*/
|
|
81
|
+
function prompt(question, defaultValue = '') {
|
|
82
|
+
return new Promise((resolve) => {
|
|
83
|
+
const displayQuestion = defaultValue
|
|
84
|
+
? `${question} [${defaultValue}]: `
|
|
85
|
+
: `${question}: `;
|
|
86
|
+
|
|
87
|
+
if (isTTY) {
|
|
88
|
+
// TTY mode: use readline
|
|
89
|
+
getRL().question(displayQuestion, (answer) => {
|
|
90
|
+
resolve(answer.trim() || defaultValue);
|
|
91
|
+
});
|
|
92
|
+
} else {
|
|
93
|
+
// Piped mode: get next line from buffer
|
|
94
|
+
const answer = getNextPipeLine();
|
|
95
|
+
resolve(answer.trim() || defaultValue);
|
|
96
|
+
}
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Confirm prompt - returns true/false
|
|
102
|
+
*/
|
|
103
|
+
function confirm(question) {
|
|
104
|
+
return new Promise((resolve) => {
|
|
105
|
+
const confirmQuestion = `${question} (y/n): `;
|
|
106
|
+
|
|
107
|
+
if (isTTY) {
|
|
108
|
+
// TTY mode: use readline
|
|
109
|
+
getRL().question(confirmQuestion, (answer) => {
|
|
110
|
+
resolve(
|
|
111
|
+
answer.trim().toLowerCase() === 'y' ||
|
|
112
|
+
answer.trim().toLowerCase() === 'yes'
|
|
113
|
+
);
|
|
114
|
+
});
|
|
115
|
+
} else {
|
|
116
|
+
// Piped mode: get next line from buffer
|
|
117
|
+
const answer = getNextPipeLine();
|
|
118
|
+
resolve(
|
|
119
|
+
answer.trim().toLowerCase() === 'y' ||
|
|
120
|
+
answer.trim().toLowerCase() === 'yes'
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Select from multiple choice
|
|
128
|
+
*/
|
|
129
|
+
async function select(question, choices) {
|
|
130
|
+
console.log(`\n${question}`);
|
|
131
|
+
choices.forEach((choice, idx) => {
|
|
132
|
+
console.log(` ${idx + 1}. ${choice}`);
|
|
133
|
+
});
|
|
134
|
+
|
|
135
|
+
while (true) {
|
|
136
|
+
const answer = await prompt('Select', '1');
|
|
137
|
+
const idx = parseInt(answer, 10) - 1;
|
|
138
|
+
if (idx >= 0 && idx < choices.length) {
|
|
139
|
+
return choices[idx];
|
|
140
|
+
}
|
|
141
|
+
console.log('Invalid selection. Please try again.');
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Make shell scripts executable
|
|
147
|
+
*/
|
|
148
|
+
function makeExecutable(dirPath) {
|
|
149
|
+
try {
|
|
150
|
+
const toolsDir = path.join(dirPath, '.am-tools');
|
|
151
|
+
if (fs.existsSync(toolsDir)) {
|
|
152
|
+
const files = fs.readdirSync(toolsDir);
|
|
153
|
+
files.forEach((file) => {
|
|
154
|
+
if (file.endsWith('.sh')) {
|
|
155
|
+
const filePath = path.join(toolsDir, file);
|
|
156
|
+
try {
|
|
157
|
+
execSync(`chmod +x "${filePath}"`, { stdio: 'pipe' });
|
|
158
|
+
} catch {
|
|
159
|
+
// Silently fail — Windows doesn't support chmod
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
} catch {
|
|
165
|
+
// Silently handle errors
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
/**
|
|
170
|
+
* Parse comma-separated values into array
|
|
171
|
+
*/
|
|
172
|
+
function parseCommaList(str) {
|
|
173
|
+
return str
|
|
174
|
+
.split(',')
|
|
175
|
+
.map((item) => item.trim())
|
|
176
|
+
.filter((item) => item.length > 0);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function init() {
|
|
180
|
+
const cwd = process.cwd();
|
|
181
|
+
const agentMindPath = path.join(cwd, '.agent-mind');
|
|
182
|
+
|
|
183
|
+
console.log('\n🚀 Agent Mind Initialization\n');
|
|
184
|
+
|
|
185
|
+
try {
|
|
186
|
+
// Initialize pipe buffer if stdin is piped
|
|
187
|
+
if (!isTTY) {
|
|
188
|
+
await initPipeBuffer();
|
|
189
|
+
}
|
|
190
|
+
// Check if already exists
|
|
191
|
+
if (fs.existsSync(agentMindPath)) {
|
|
192
|
+
console.log('⚠️ Agent Mind is already initialized in this directory.\n');
|
|
193
|
+
const shouldContinue = await confirm(
|
|
194
|
+
'Continue anyway and reinitialize?'
|
|
195
|
+
);
|
|
196
|
+
if (!shouldContinue) {
|
|
197
|
+
console.log('Initialization cancelled.');
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
console.log('Reinitializing...\n');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
// Interactive questions
|
|
204
|
+
const dirName = path.basename(cwd);
|
|
205
|
+
const projectName = await prompt('Project name', dirName);
|
|
206
|
+
const projectDescription = await prompt(
|
|
207
|
+
'Brief project description (optional)',
|
|
208
|
+
''
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
const primaryTool = await select('Primary LLM tool?', [
|
|
212
|
+
'Claude Code',
|
|
213
|
+
'Codex',
|
|
214
|
+
'Gemini CLI',
|
|
215
|
+
'Cursor',
|
|
216
|
+
'Other',
|
|
217
|
+
]);
|
|
218
|
+
|
|
219
|
+
const domainsInput = await prompt(
|
|
220
|
+
'Knowledge domains (comma-separated, optional)',
|
|
221
|
+
''
|
|
222
|
+
);
|
|
223
|
+
const domains = parseCommaList(domainsInput).join(', ');
|
|
224
|
+
|
|
225
|
+
const stackInput = await prompt(
|
|
226
|
+
'Key technologies (comma-separated, optional)',
|
|
227
|
+
''
|
|
228
|
+
);
|
|
229
|
+
const stack = parseCommaList(stackInput).join(', ');
|
|
230
|
+
|
|
231
|
+
// Copy template
|
|
232
|
+
console.log('\n📁 Creating Agent Mind structure...');
|
|
233
|
+
try {
|
|
234
|
+
copyTemplate(agentMindPath);
|
|
235
|
+
} catch (error) {
|
|
236
|
+
throw new Error(`Failed to copy template: ${error.message}`);
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
// Replace variables
|
|
240
|
+
const now = new Date();
|
|
241
|
+
const dateStr = now.toISOString().split('T')[0];
|
|
242
|
+
|
|
243
|
+
const variables = {
|
|
244
|
+
PROJECT_NAME: projectName,
|
|
245
|
+
PROJECT_DESCRIPTION: projectDescription || 'No description provided',
|
|
246
|
+
DATE: dateStr,
|
|
247
|
+
PRIMARY_AGENT: primaryTool,
|
|
248
|
+
DOMAINS: domains || '(none yet — add as project evolves)',
|
|
249
|
+
STACK: stack || '(none yet — add as needed)',
|
|
250
|
+
VERSION: getPackageVersion(),
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
replaceVarsInDir(agentMindPath, variables);
|
|
254
|
+
|
|
255
|
+
// Make scripts executable
|
|
256
|
+
makeExecutable(agentMindPath);
|
|
257
|
+
|
|
258
|
+
console.log('✅ Agent Mind structure created\n');
|
|
259
|
+
|
|
260
|
+
// Detect and integrate with existing tools
|
|
261
|
+
console.log('🔍 Checking for existing LLM tool configurations...\n');
|
|
262
|
+
const detectedTools = detectTools(cwd);
|
|
263
|
+
|
|
264
|
+
if (detectedTools.length > 0) {
|
|
265
|
+
console.log(`Found ${detectedTools.length} tool configuration(s):\n`);
|
|
266
|
+
|
|
267
|
+
for (const tool of detectedTools) {
|
|
268
|
+
const shouldIntegrate = await confirm(
|
|
269
|
+
` Add Agent Mind integration to ${tool.tool} (${tool.configFile})?`
|
|
270
|
+
);
|
|
271
|
+
|
|
272
|
+
if (shouldIntegrate) {
|
|
273
|
+
const result = injectSnippet(tool.path);
|
|
274
|
+
if (result.success) {
|
|
275
|
+
console.log(` ✅ Integrated with ${tool.tool}\n`);
|
|
276
|
+
console.log(' Added snippet:');
|
|
277
|
+
console.log(
|
|
278
|
+
getSnippet()
|
|
279
|
+
.split('\n')
|
|
280
|
+
.map((l) => ' ' + l)
|
|
281
|
+
.join('\n')
|
|
282
|
+
);
|
|
283
|
+
console.log('');
|
|
284
|
+
} else if (result.reason === 'already-present') {
|
|
285
|
+
console.log(
|
|
286
|
+
` ℹ️ ${tool.tool} already has Agent Mind integration\n`
|
|
287
|
+
);
|
|
288
|
+
} else {
|
|
289
|
+
console.log(` ❌ Failed to integrate: ${result.message}\n`);
|
|
290
|
+
}
|
|
291
|
+
} else {
|
|
292
|
+
console.log(`\n Add this to your ${tool.configFile} manually:\n`);
|
|
293
|
+
console.log(' ---');
|
|
294
|
+
console.log(
|
|
295
|
+
getSnippet()
|
|
296
|
+
.split('\n')
|
|
297
|
+
.map((l) => ' ' + l)
|
|
298
|
+
.join('\n')
|
|
299
|
+
);
|
|
300
|
+
console.log(' ---\n');
|
|
301
|
+
}
|
|
302
|
+
}
|
|
303
|
+
} else {
|
|
304
|
+
console.log(
|
|
305
|
+
'No existing LLM tool configs found. You can set up integration later.\n'
|
|
306
|
+
);
|
|
307
|
+
console.log('When ready, add this snippet to your tool config');
|
|
308
|
+
console.log('(CLAUDE.md, AGENTS.md, GEMINI.md, or .cursorrules):\n');
|
|
309
|
+
console.log(
|
|
310
|
+
getSnippet()
|
|
311
|
+
.split('\n')
|
|
312
|
+
.map((l) => ' ' + l)
|
|
313
|
+
.join('\n')
|
|
314
|
+
);
|
|
315
|
+
console.log('');
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
// Success message
|
|
319
|
+
console.log('🎉 Agent Mind is ready!\n');
|
|
320
|
+
console.log('Next steps:');
|
|
321
|
+
console.log(' 1. Your agent reads .agent-mind/BOOT.md at every session start');
|
|
322
|
+
console.log(' 2. Review .agent-mind/config.md to customize settings');
|
|
323
|
+
console.log(' 3. Run `agent-mind doctor` anytime to check memory health\n');
|
|
324
|
+
} finally {
|
|
325
|
+
closeRL();
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Get package version
|
|
331
|
+
*/
|
|
332
|
+
function getPackageVersion() {
|
|
333
|
+
try {
|
|
334
|
+
const packageJsonPath = path.join(__dirname, '../../package.json');
|
|
335
|
+
const content = fs.readFileSync(packageJsonPath, 'utf8');
|
|
336
|
+
const pkg = JSON.parse(content);
|
|
337
|
+
return pkg.version;
|
|
338
|
+
} catch {
|
|
339
|
+
return '1.0.0';
|
|
340
|
+
}
|
|
341
|
+
}
|
|
342
|
+
|
|
343
|
+
module.exports = {
|
|
344
|
+
init,
|
|
345
|
+
};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
const { getPackageVersion } = require('../utils/version');
|
|
2
|
+
|
|
3
|
+
async function version() {
|
|
4
|
+
const ver = getPackageVersion();
|
|
5
|
+
console.log(`agent-mind ${ver}`);
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
async function help() {
|
|
9
|
+
console.log(`
|
|
10
|
+
Agent Mind - Cognitive memory system for LLM agents
|
|
11
|
+
|
|
12
|
+
USAGE:
|
|
13
|
+
agent-mind <command> [options]
|
|
14
|
+
|
|
15
|
+
COMMANDS:
|
|
16
|
+
init Initialize Agent Mind in your project
|
|
17
|
+
upgrade Upgrade Agent Mind to the latest version
|
|
18
|
+
doctor Check Agent Mind health and integrity
|
|
19
|
+
version Show Agent Mind version
|
|
20
|
+
help Show this help message
|
|
21
|
+
|
|
22
|
+
EXAMPLES:
|
|
23
|
+
agent-mind init # Start interactive setup
|
|
24
|
+
agent-mind upgrade # Update existing Agent Mind
|
|
25
|
+
agent-mind doctor # Check system health
|
|
26
|
+
|
|
27
|
+
Learn more at: https://github.com/shikhar1verma/agent-mind
|
|
28
|
+
`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
module.exports = {
|
|
32
|
+
version,
|
|
33
|
+
help
|
|
34
|
+
};
|
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const readline = require('readline');
|
|
4
|
+
const { getPackageVersion, getCurrentVersion } = require('../utils/version');
|
|
5
|
+
const { copyTemplate, replaceVars } = require('../utils/template');
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Confirm prompt - returns true/false
|
|
9
|
+
*/
|
|
10
|
+
function confirm(question) {
|
|
11
|
+
return new Promise(resolve => {
|
|
12
|
+
const rl = readline.createInterface({
|
|
13
|
+
input: process.stdin,
|
|
14
|
+
output: process.stdout
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
rl.question(`${question} (y/n): `, answer => {
|
|
18
|
+
rl.close();
|
|
19
|
+
resolve(answer.toLowerCase() === 'y' || answer.toLowerCase() === 'yes');
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* List of core files that should be updated (not user files)
|
|
26
|
+
*/
|
|
27
|
+
const CORE_FILES = [
|
|
28
|
+
'BOOT.md',
|
|
29
|
+
'VERSION.md',
|
|
30
|
+
'protocols/compaction.md',
|
|
31
|
+
'protocols/maintenance.md',
|
|
32
|
+
'protocols/workflow.md',
|
|
33
|
+
'protocols/quality-gate.md',
|
|
34
|
+
'protocols/memory-ops.md',
|
|
35
|
+
'adapters/claude.md',
|
|
36
|
+
'adapters/codex.md',
|
|
37
|
+
'adapters/gemini.md',
|
|
38
|
+
'adapters/cursor.md'
|
|
39
|
+
];
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Recursively copy core files only
|
|
43
|
+
*/
|
|
44
|
+
function copyOnlyCore(srcDir, destDir) {
|
|
45
|
+
if (!fs.existsSync(destDir)) {
|
|
46
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isCoreFile(relativePath) {
|
|
50
|
+
return CORE_FILES.some(core => {
|
|
51
|
+
// Handle both exact matches and directory prefixes
|
|
52
|
+
return relativePath === core || relativePath.startsWith(core + '/');
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function copyDirFiltered(src, dest, basePath = '') {
|
|
57
|
+
const items = fs.readdirSync(src);
|
|
58
|
+
|
|
59
|
+
items.forEach(item => {
|
|
60
|
+
const srcPath = path.join(src, item);
|
|
61
|
+
const destPath = path.join(dest, item);
|
|
62
|
+
const relativePath = basePath ? `${basePath}/${item}` : item;
|
|
63
|
+
const stat = fs.statSync(srcPath);
|
|
64
|
+
|
|
65
|
+
if (stat.isDirectory()) {
|
|
66
|
+
// Check if this directory contains core files
|
|
67
|
+
if (isCoreFile(relativePath)) {
|
|
68
|
+
if (!fs.existsSync(destPath)) {
|
|
69
|
+
fs.mkdirSync(destPath, { recursive: true });
|
|
70
|
+
}
|
|
71
|
+
copyDirFiltered(srcPath, destPath, relativePath);
|
|
72
|
+
}
|
|
73
|
+
} else if (isCoreFile(relativePath)) {
|
|
74
|
+
fs.copyFileSync(srcPath, destPath);
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
copyDirFiltered(srcDir, destDir);
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Make shell scripts executable
|
|
84
|
+
*/
|
|
85
|
+
function makeExecutable(dirPath) {
|
|
86
|
+
try {
|
|
87
|
+
const toolsDir = path.join(dirPath, '.am-tools');
|
|
88
|
+
if (fs.existsSync(toolsDir)) {
|
|
89
|
+
const files = fs.readdirSync(toolsDir);
|
|
90
|
+
files.forEach(file => {
|
|
91
|
+
if (file.endsWith('.sh')) {
|
|
92
|
+
const filePath = path.join(toolsDir, file);
|
|
93
|
+
try {
|
|
94
|
+
const { execSync } = require('child_process');
|
|
95
|
+
execSync(`chmod +x "${filePath}"`, { stdio: 'pipe' });
|
|
96
|
+
} catch {
|
|
97
|
+
// Silently fail - Windows doesn't support chmod
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
} catch (error) {
|
|
103
|
+
// Silently handle errors
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async function upgrade() {
|
|
108
|
+
const cwd = process.cwd();
|
|
109
|
+
const agentMindPath = path.join(cwd, '.agent-mind');
|
|
110
|
+
|
|
111
|
+
console.log('\n⬆️ Agent Mind Upgrade\n');
|
|
112
|
+
|
|
113
|
+
// Check if Agent Mind exists
|
|
114
|
+
if (!fs.existsSync(agentMindPath)) {
|
|
115
|
+
throw new Error('Agent Mind not found. Run "agent-mind init" first.');
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
// Get versions
|
|
119
|
+
const currentVersion = getCurrentVersion(agentMindPath);
|
|
120
|
+
const packageVersion = getPackageVersion();
|
|
121
|
+
|
|
122
|
+
console.log(`Current version: ${currentVersion}`);
|
|
123
|
+
console.log(`Package version: ${packageVersion}\n`);
|
|
124
|
+
|
|
125
|
+
if (currentVersion === packageVersion) {
|
|
126
|
+
console.log('✅ Already up to date!');
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Show what will be updated
|
|
131
|
+
console.log('Files that will be updated:');
|
|
132
|
+
CORE_FILES.forEach(file => {
|
|
133
|
+
console.log(` • ${file}`);
|
|
134
|
+
});
|
|
135
|
+
console.log('\nNote: Your user files (config.md, knowledge/, workspace/, history/) are never touched.\n');
|
|
136
|
+
|
|
137
|
+
// Ask for confirmation
|
|
138
|
+
const shouldProceed = await confirm('Continue upgrade?');
|
|
139
|
+
if (!shouldProceed) {
|
|
140
|
+
console.log('Upgrade cancelled.');
|
|
141
|
+
return;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Perform upgrade
|
|
145
|
+
console.log('\n📦 Upgrading...');
|
|
146
|
+
|
|
147
|
+
const templatePath = path.join(__dirname, '../../template');
|
|
148
|
+
if (!fs.existsSync(templatePath)) {
|
|
149
|
+
throw new Error('Template directory not found');
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Copy only core files
|
|
153
|
+
copyOnlyCore(templatePath, agentMindPath);
|
|
154
|
+
|
|
155
|
+
// Update VERSION.md
|
|
156
|
+
const versionPath = path.join(agentMindPath, 'VERSION.md');
|
|
157
|
+
const now = new Date().toISOString().split('T')[0];
|
|
158
|
+
|
|
159
|
+
replaceVars(versionPath, {
|
|
160
|
+
VERSION: packageVersion,
|
|
161
|
+
DATE: now
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
// Make scripts executable
|
|
165
|
+
makeExecutable(agentMindPath);
|
|
166
|
+
|
|
167
|
+
console.log(`✅ Upgraded to ${packageVersion}\n`);
|
|
168
|
+
console.log('Updated files:');
|
|
169
|
+
CORE_FILES.forEach(file => {
|
|
170
|
+
console.log(` ✓ ${file}`);
|
|
171
|
+
});
|
|
172
|
+
console.log('\nYour project data is safe and unchanged.\n');
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
module.exports = {
|
|
176
|
+
upgrade
|
|
177
|
+
};
|
package/src/index.js
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
// Agent Mind CLI main router
|
|
2
|
+
// Exports command modules for dispatch
|
|
3
|
+
|
|
4
|
+
module.exports = {
|
|
5
|
+
commands: {
|
|
6
|
+
init: require('./commands/init').init,
|
|
7
|
+
upgrade: require('./commands/upgrade').upgrade,
|
|
8
|
+
doctor: require('./commands/doctor').doctor,
|
|
9
|
+
version: require('./commands/meta').version,
|
|
10
|
+
help: require('./commands/meta').help
|
|
11
|
+
},
|
|
12
|
+
utils: {
|
|
13
|
+
detectTools: require('./utils/detect-tools'),
|
|
14
|
+
template: require('./utils/template'),
|
|
15
|
+
version: require('./utils/version'),
|
|
16
|
+
injectAdapter: require('./utils/inject-adapter')
|
|
17
|
+
}
|
|
18
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* Detect which LLM tool configuration files exist in the current directory
|
|
6
|
+
* @param {string} [cwd=process.cwd()] - Directory to search
|
|
7
|
+
* @returns {Array<{tool: string, path: string}>} - Detected tools with their config paths
|
|
8
|
+
*/
|
|
9
|
+
function detectTools(cwd = process.cwd()) {
|
|
10
|
+
const detected = [];
|
|
11
|
+
|
|
12
|
+
// Check for Claude Code
|
|
13
|
+
const claudePath = path.join(cwd, 'CLAUDE.md');
|
|
14
|
+
if (fs.existsSync(claudePath)) {
|
|
15
|
+
detected.push({
|
|
16
|
+
tool: 'Claude Code',
|
|
17
|
+
path: claudePath,
|
|
18
|
+
configFile: 'CLAUDE.md'
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
// Check for Codex
|
|
23
|
+
const agentsPath = path.join(cwd, 'AGENTS.md');
|
|
24
|
+
if (fs.existsSync(agentsPath)) {
|
|
25
|
+
detected.push({
|
|
26
|
+
tool: 'Codex',
|
|
27
|
+
path: agentsPath,
|
|
28
|
+
configFile: 'AGENTS.md'
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Check for Gemini CLI
|
|
33
|
+
const geminiPath = path.join(cwd, 'GEMINI.md');
|
|
34
|
+
if (fs.existsSync(geminiPath)) {
|
|
35
|
+
detected.push({
|
|
36
|
+
tool: 'Gemini CLI',
|
|
37
|
+
path: geminiPath,
|
|
38
|
+
configFile: 'GEMINI.md'
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
// Check for Cursor
|
|
43
|
+
const cursorRulesPath = path.join(cwd, '.cursorrules');
|
|
44
|
+
const cursorRulesDirPath = path.join(cwd, '.cursor', 'rules');
|
|
45
|
+
if (fs.existsSync(cursorRulesPath)) {
|
|
46
|
+
detected.push({
|
|
47
|
+
tool: 'Cursor',
|
|
48
|
+
path: cursorRulesPath,
|
|
49
|
+
configFile: '.cursorrules'
|
|
50
|
+
});
|
|
51
|
+
} else if (fs.existsSync(cursorRulesDirPath)) {
|
|
52
|
+
detected.push({
|
|
53
|
+
tool: 'Cursor',
|
|
54
|
+
path: cursorRulesDirPath,
|
|
55
|
+
configFile: '.cursor/rules/'
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
return detected;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
module.exports = detectTools;
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
|
|
3
|
+
const SNIPPET = `## Agent Mind Memory System
|
|
4
|
+
This project uses Agent Mind for structured memory management.
|
|
5
|
+
At the start of every session, read \`.agent-mind/BOOT.md\` and follow its protocols.
|
|
6
|
+
Use \`.agent-mind/workspace/\` as working memory for the current task.
|
|
7
|
+
After completing a task, follow \`.agent-mind/protocols/compaction.md\`.
|
|
8
|
+
When asked about memory health, follow \`.agent-mind/protocols/maintenance.md\`.`;
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Get the Agent Mind adapter snippet
|
|
12
|
+
*/
|
|
13
|
+
function getSnippet() {
|
|
14
|
+
return SNIPPET;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Check if the snippet is already present in the file
|
|
19
|
+
* @param {string} filePath - Path to config file
|
|
20
|
+
*/
|
|
21
|
+
function hasSnippet(filePath) {
|
|
22
|
+
try {
|
|
23
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
24
|
+
return content.includes('Agent Mind Memory System');
|
|
25
|
+
} catch (error) {
|
|
26
|
+
return false;
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Inject the Agent Mind snippet into a tool config file
|
|
32
|
+
* @param {string} filePath - Path to config file
|
|
33
|
+
*/
|
|
34
|
+
function injectSnippet(filePath) {
|
|
35
|
+
try {
|
|
36
|
+
if (hasSnippet(filePath)) {
|
|
37
|
+
return {
|
|
38
|
+
success: false,
|
|
39
|
+
reason: 'already-present',
|
|
40
|
+
message: 'Agent Mind snippet already present in file'
|
|
41
|
+
};
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const content = fs.readFileSync(filePath, 'utf8');
|
|
45
|
+
const newContent = content + '\n\n' + SNIPPET + '\n';
|
|
46
|
+
fs.writeFileSync(filePath, newContent, 'utf8');
|
|
47
|
+
|
|
48
|
+
return {
|
|
49
|
+
success: true,
|
|
50
|
+
message: 'Agent Mind snippet injected successfully'
|
|
51
|
+
};
|
|
52
|
+
} catch (error) {
|
|
53
|
+
return {
|
|
54
|
+
success: false,
|
|
55
|
+
reason: 'write-error',
|
|
56
|
+
message: `Failed to inject snippet: ${error.message}`
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
module.exports = {
|
|
62
|
+
getSnippet,
|
|
63
|
+
hasSnippet,
|
|
64
|
+
injectSnippet
|
|
65
|
+
};
|