namnam-skills 1.0.1 → 1.0.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/cli.js +474 -6
- package/src/conversation.js +467 -0
- package/src/indexer.js +151 -0
- package/src/templates/{core/namnam.md → namnam.md} +118 -5
- package/src/watcher.js +356 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "namnam-skills",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
4
4
|
"description": "Ultimate AI Skills Installer - Universal support for Claude, Codex, Cursor, Windsurf, Cline, Aider, and more. 150+ agents, workflows, and skills.",
|
|
5
5
|
"author": "NamNam",
|
|
6
6
|
"license": "MIT",
|
package/src/cli.js
CHANGED
|
@@ -15,7 +15,19 @@ import {
|
|
|
15
15
|
deleteConversation,
|
|
16
16
|
exportConversation,
|
|
17
17
|
getConversationContext,
|
|
18
|
-
updateConversation
|
|
18
|
+
updateConversation,
|
|
19
|
+
// Auto-memory functions
|
|
20
|
+
initAutoMemory,
|
|
21
|
+
loadAutoMemoryIndex,
|
|
22
|
+
autoSaveMemory,
|
|
23
|
+
getRelevantMemories,
|
|
24
|
+
generateAutoMemoryContext,
|
|
25
|
+
startSession,
|
|
26
|
+
getCurrentSession,
|
|
27
|
+
updateSession,
|
|
28
|
+
endSession,
|
|
29
|
+
remember,
|
|
30
|
+
recall
|
|
19
31
|
} from './conversation.js';
|
|
20
32
|
import {
|
|
21
33
|
buildIndex,
|
|
@@ -809,11 +821,36 @@ index
|
|
|
809
821
|
.command('build')
|
|
810
822
|
.description('Build or rebuild the codebase index')
|
|
811
823
|
.option('-f, --force', 'Force rebuild even if index exists')
|
|
824
|
+
.option('--json', 'Output results as JSON (for programmatic use)')
|
|
812
825
|
.action(async (options) => {
|
|
813
|
-
console.log(banner);
|
|
814
|
-
|
|
815
826
|
const cwd = process.cwd();
|
|
816
827
|
|
|
828
|
+
// JSON mode - no banner or colors
|
|
829
|
+
if (options.json) {
|
|
830
|
+
try {
|
|
831
|
+
const meta = await buildIndex(cwd);
|
|
832
|
+
console.log(JSON.stringify({
|
|
833
|
+
success: true,
|
|
834
|
+
stats: {
|
|
835
|
+
files: meta.stats.totalFiles,
|
|
836
|
+
lines: meta.stats.totalLines,
|
|
837
|
+
functions: meta.stats.totalFunctions,
|
|
838
|
+
classes: meta.stats.totalClasses
|
|
839
|
+
},
|
|
840
|
+
patterns: meta.patterns
|
|
841
|
+
}));
|
|
842
|
+
} catch (error) {
|
|
843
|
+
console.log(JSON.stringify({
|
|
844
|
+
success: false,
|
|
845
|
+
error: error.message
|
|
846
|
+
}));
|
|
847
|
+
process.exit(1);
|
|
848
|
+
}
|
|
849
|
+
return;
|
|
850
|
+
}
|
|
851
|
+
|
|
852
|
+
console.log(banner);
|
|
853
|
+
|
|
817
854
|
// Check if index exists
|
|
818
855
|
if (!options.force && await hasIndex(cwd)) {
|
|
819
856
|
const changes = await checkIndexChanges(cwd);
|
|
@@ -879,11 +916,38 @@ index
|
|
|
879
916
|
index
|
|
880
917
|
.command('status')
|
|
881
918
|
.description('Show index status and statistics')
|
|
882
|
-
.
|
|
883
|
-
|
|
884
|
-
|
|
919
|
+
.option('--json', 'Output results as JSON (for programmatic use)')
|
|
920
|
+
.action(async (options) => {
|
|
885
921
|
const cwd = process.cwd();
|
|
886
922
|
|
|
923
|
+
// JSON mode
|
|
924
|
+
if (options.json) {
|
|
925
|
+
if (!(await hasIndex(cwd))) {
|
|
926
|
+
console.log(JSON.stringify({ exists: false }));
|
|
927
|
+
return;
|
|
928
|
+
}
|
|
929
|
+
const stats = await getIndexStats(cwd);
|
|
930
|
+
const changes = await checkIndexChanges(cwd);
|
|
931
|
+
console.log(JSON.stringify({
|
|
932
|
+
exists: true,
|
|
933
|
+
totalFiles: stats.totalFiles,
|
|
934
|
+
totalLines: stats.totalLines,
|
|
935
|
+
totalFunctions: stats.totalFunctions,
|
|
936
|
+
totalClasses: stats.totalClasses,
|
|
937
|
+
lastUpdated: stats.lastUpdated,
|
|
938
|
+
patterns: stats.patterns,
|
|
939
|
+
hasChanges: changes.hasChanges,
|
|
940
|
+
changes: changes.hasChanges ? {
|
|
941
|
+
newFiles: changes.newFiles?.length || 0,
|
|
942
|
+
modifiedFiles: changes.modifiedFiles?.length || 0,
|
|
943
|
+
deletedFiles: changes.deletedFiles?.length || 0
|
|
944
|
+
} : null
|
|
945
|
+
}));
|
|
946
|
+
return;
|
|
947
|
+
}
|
|
948
|
+
|
|
949
|
+
console.log(banner);
|
|
950
|
+
|
|
887
951
|
if (!(await hasIndex(cwd))) {
|
|
888
952
|
console.log(chalk.yellow('\n⚠️ No index found.'));
|
|
889
953
|
console.log(chalk.gray('Run `namnam index build` to create one.'));
|
|
@@ -1001,4 +1065,408 @@ index
|
|
|
1001
1065
|
}
|
|
1002
1066
|
});
|
|
1003
1067
|
|
|
1068
|
+
index
|
|
1069
|
+
.command('watch')
|
|
1070
|
+
.description('Watch for file changes and auto-update index (live indexing)')
|
|
1071
|
+
.option('-v, --verbose', 'Show detailed logs')
|
|
1072
|
+
.option('--no-auto-rebuild', 'Only notify on changes, do not auto-rebuild')
|
|
1073
|
+
.action(async (options) => {
|
|
1074
|
+
console.log(banner);
|
|
1075
|
+
console.log(chalk.cyan('\n👁️ Live Index Watcher\n'));
|
|
1076
|
+
|
|
1077
|
+
const cwd = process.cwd();
|
|
1078
|
+
|
|
1079
|
+
// Dynamic import to avoid loading watcher on every CLI call
|
|
1080
|
+
const { IndexWatcher, writeWatcherStatus, clearWatcherStatus } = await import('./watcher.js');
|
|
1081
|
+
|
|
1082
|
+
const watcher = new IndexWatcher(cwd, {
|
|
1083
|
+
verbose: options.verbose,
|
|
1084
|
+
autoRebuild: options.autoRebuild !== false
|
|
1085
|
+
});
|
|
1086
|
+
|
|
1087
|
+
// Event handlers
|
|
1088
|
+
watcher.on('started', () => {
|
|
1089
|
+
console.log(chalk.green('✓ Watcher started'));
|
|
1090
|
+
console.log(chalk.gray(` Watching: ${cwd}`));
|
|
1091
|
+
console.log(chalk.gray(` Auto-rebuild: ${options.autoRebuild !== false ? 'enabled' : 'disabled'}`));
|
|
1092
|
+
console.log(chalk.yellow('\n Press Ctrl+C to stop\n'));
|
|
1093
|
+
});
|
|
1094
|
+
|
|
1095
|
+
watcher.on('change', ({ type, path: filePath }) => {
|
|
1096
|
+
const icon = type === 'rename' ? '~' : type === 'change' ? '±' : '+';
|
|
1097
|
+
console.log(chalk.gray(` ${icon} ${filePath}`));
|
|
1098
|
+
});
|
|
1099
|
+
|
|
1100
|
+
watcher.on('indexing', ({ type }) => {
|
|
1101
|
+
if (type === 'initial') {
|
|
1102
|
+
console.log(chalk.yellow(' Building initial index...'));
|
|
1103
|
+
} else {
|
|
1104
|
+
console.log(chalk.yellow(' Updating index...'));
|
|
1105
|
+
}
|
|
1106
|
+
});
|
|
1107
|
+
|
|
1108
|
+
watcher.on('indexed', ({ type, changesProcessed }) => {
|
|
1109
|
+
if (type === 'initial') {
|
|
1110
|
+
console.log(chalk.green(' ✓ Initial index complete'));
|
|
1111
|
+
} else {
|
|
1112
|
+
console.log(chalk.green(` ✓ Index updated (${changesProcessed} files)`));
|
|
1113
|
+
}
|
|
1114
|
+
writeWatcherStatus(cwd, watcher.getStatus());
|
|
1115
|
+
});
|
|
1116
|
+
|
|
1117
|
+
watcher.on('error', (err) => {
|
|
1118
|
+
console.log(chalk.red(` ✗ Error: ${err.message}`));
|
|
1119
|
+
});
|
|
1120
|
+
|
|
1121
|
+
// Graceful shutdown
|
|
1122
|
+
const shutdown = async () => {
|
|
1123
|
+
console.log(chalk.yellow('\n\nStopping watcher...'));
|
|
1124
|
+
watcher.stop();
|
|
1125
|
+
await clearWatcherStatus(cwd);
|
|
1126
|
+
console.log(chalk.green('Watcher stopped.'));
|
|
1127
|
+
process.exit(0);
|
|
1128
|
+
};
|
|
1129
|
+
|
|
1130
|
+
process.on('SIGINT', shutdown);
|
|
1131
|
+
process.on('SIGTERM', shutdown);
|
|
1132
|
+
|
|
1133
|
+
// Start watching
|
|
1134
|
+
try {
|
|
1135
|
+
await watcher.start();
|
|
1136
|
+
await writeWatcherStatus(cwd, watcher.getStatus());
|
|
1137
|
+
} catch (error) {
|
|
1138
|
+
console.error(chalk.red('Failed to start watcher:'), error.message);
|
|
1139
|
+
process.exit(1);
|
|
1140
|
+
}
|
|
1141
|
+
});
|
|
1142
|
+
|
|
1143
|
+
// ========================================
|
|
1144
|
+
// MEMORY COMMANDS (Auto-Memory System)
|
|
1145
|
+
// ========================================
|
|
1146
|
+
|
|
1147
|
+
const memory = program
|
|
1148
|
+
.command('memory')
|
|
1149
|
+
.alias('mem')
|
|
1150
|
+
.description('Auto-memory system for persistent context across sessions');
|
|
1151
|
+
|
|
1152
|
+
memory
|
|
1153
|
+
.command('remember <content>')
|
|
1154
|
+
.description('Save a memory (decision, pattern, or context)')
|
|
1155
|
+
.option('-t, --type <type>', 'Memory type: decision, pattern, context, learning', 'context')
|
|
1156
|
+
.option('-i, --importance <level>', 'Importance: low, normal, high, critical', 'normal')
|
|
1157
|
+
.option('-g, --tags <tags>', 'Comma-separated tags')
|
|
1158
|
+
.option('-f, --files <files>', 'Related files (comma-separated)')
|
|
1159
|
+
.option('-s, --summary <summary>', 'Short summary of the memory')
|
|
1160
|
+
.action(async (content, options) => {
|
|
1161
|
+
console.log(banner);
|
|
1162
|
+
|
|
1163
|
+
try {
|
|
1164
|
+
const cwd = process.cwd();
|
|
1165
|
+
const spinner = ora('Saving memory...').start();
|
|
1166
|
+
|
|
1167
|
+
const memory = await remember(content, {
|
|
1168
|
+
type: options.type,
|
|
1169
|
+
importance: options.importance,
|
|
1170
|
+
tags: options.tags ? options.tags.split(',').map(t => t.trim()) : [],
|
|
1171
|
+
files: options.files ? options.files.split(',').map(f => f.trim()) : [],
|
|
1172
|
+
summary: options.summary,
|
|
1173
|
+
source: 'user'
|
|
1174
|
+
}, cwd);
|
|
1175
|
+
|
|
1176
|
+
spinner.succeed(chalk.green('Memory saved!'));
|
|
1177
|
+
|
|
1178
|
+
console.log(chalk.cyan('\n📝 Memory Details:'));
|
|
1179
|
+
console.log(chalk.gray(` ID: ${memory.id}`));
|
|
1180
|
+
console.log(chalk.gray(` Type: ${memory.type}`));
|
|
1181
|
+
console.log(chalk.gray(` Importance: ${memory.importance}`));
|
|
1182
|
+
if (memory.tags.length) {
|
|
1183
|
+
console.log(chalk.gray(` Tags: ${memory.tags.join(', ')}`));
|
|
1184
|
+
}
|
|
1185
|
+
} catch (error) {
|
|
1186
|
+
console.error(chalk.red('Failed to save memory:'), error.message);
|
|
1187
|
+
process.exit(1);
|
|
1188
|
+
}
|
|
1189
|
+
});
|
|
1190
|
+
|
|
1191
|
+
memory
|
|
1192
|
+
.command('recall [query]')
|
|
1193
|
+
.description('Recall relevant memories')
|
|
1194
|
+
.option('-l, --limit <n>', 'Number of memories to return', '10')
|
|
1195
|
+
.option('-t, --types <types>', 'Filter by types (comma-separated)', 'decision,pattern,context,learning')
|
|
1196
|
+
.option('-i, --min-importance <level>', 'Minimum importance level', 'low')
|
|
1197
|
+
.option('--json', 'Output as JSON')
|
|
1198
|
+
.action(async (query, options) => {
|
|
1199
|
+
const cwd = process.cwd();
|
|
1200
|
+
|
|
1201
|
+
try {
|
|
1202
|
+
const memories = await recall(query || '', {
|
|
1203
|
+
limit: parseInt(options.limit),
|
|
1204
|
+
types: options.types.split(',').map(t => t.trim()),
|
|
1205
|
+
minImportance: options.minImportance
|
|
1206
|
+
}, cwd);
|
|
1207
|
+
|
|
1208
|
+
if (options.json) {
|
|
1209
|
+
console.log(JSON.stringify(memories, null, 2));
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1212
|
+
|
|
1213
|
+
console.log(banner);
|
|
1214
|
+
|
|
1215
|
+
if (memories.length === 0) {
|
|
1216
|
+
console.log(chalk.yellow('\n⚠️ No memories found.'));
|
|
1217
|
+
if (query) {
|
|
1218
|
+
console.log(chalk.gray(` Query: "${query}"`));
|
|
1219
|
+
}
|
|
1220
|
+
console.log(chalk.gray('\n Use `namnam memory remember` to save memories.'));
|
|
1221
|
+
return;
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
console.log(chalk.cyan(`\n🧠 Memories${query ? ` matching "${query}"` : ''} (${memories.length}):\n`));
|
|
1225
|
+
|
|
1226
|
+
const typeIcons = {
|
|
1227
|
+
decision: '⚖️',
|
|
1228
|
+
pattern: '🔄',
|
|
1229
|
+
context: '📝',
|
|
1230
|
+
learning: '💡'
|
|
1231
|
+
};
|
|
1232
|
+
|
|
1233
|
+
const importanceColors = {
|
|
1234
|
+
low: chalk.gray,
|
|
1235
|
+
normal: chalk.white,
|
|
1236
|
+
high: chalk.yellow,
|
|
1237
|
+
critical: chalk.red
|
|
1238
|
+
};
|
|
1239
|
+
|
|
1240
|
+
for (const mem of memories) {
|
|
1241
|
+
const icon = typeIcons[mem.type] || '📝';
|
|
1242
|
+
const colorFn = importanceColors[mem.importance] || chalk.white;
|
|
1243
|
+
const age = getTimeAgo(new Date(mem.createdAt));
|
|
1244
|
+
|
|
1245
|
+
console.log(colorFn(` ${icon} ${mem.summary || mem.content.substring(0, 60)}...`));
|
|
1246
|
+
console.log(chalk.gray(` ${mem.type} | ${mem.importance} | ${age}`));
|
|
1247
|
+
if (mem.tags.length) {
|
|
1248
|
+
console.log(chalk.gray(` Tags: ${mem.tags.join(', ')}`));
|
|
1249
|
+
}
|
|
1250
|
+
console.log();
|
|
1251
|
+
}
|
|
1252
|
+
} catch (error) {
|
|
1253
|
+
console.error(chalk.red('Failed to recall memories:'), error.message);
|
|
1254
|
+
process.exit(1);
|
|
1255
|
+
}
|
|
1256
|
+
});
|
|
1257
|
+
|
|
1258
|
+
memory
|
|
1259
|
+
.command('list')
|
|
1260
|
+
.description('List all memories')
|
|
1261
|
+
.option('-t, --type <type>', 'Filter by type')
|
|
1262
|
+
.option('-l, --limit <n>', 'Limit results', '20')
|
|
1263
|
+
.action(async (options) => {
|
|
1264
|
+
console.log(banner);
|
|
1265
|
+
|
|
1266
|
+
const cwd = process.cwd();
|
|
1267
|
+
|
|
1268
|
+
try {
|
|
1269
|
+
const index = await loadAutoMemoryIndex(cwd);
|
|
1270
|
+
|
|
1271
|
+
let allMemories = [
|
|
1272
|
+
...index.memories.map(m => ({ ...m, _category: 'context' })),
|
|
1273
|
+
...index.decisions.map(m => ({ ...m, _category: 'decision' })),
|
|
1274
|
+
...index.patterns.map(m => ({ ...m, _category: 'pattern' }))
|
|
1275
|
+
];
|
|
1276
|
+
|
|
1277
|
+
// Filter by type
|
|
1278
|
+
if (options.type) {
|
|
1279
|
+
allMemories = allMemories.filter(m => m.type === options.type);
|
|
1280
|
+
}
|
|
1281
|
+
|
|
1282
|
+
// Sort by date (newest first)
|
|
1283
|
+
allMemories.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
|
|
1284
|
+
|
|
1285
|
+
// Limit
|
|
1286
|
+
const limit = parseInt(options.limit);
|
|
1287
|
+
allMemories = allMemories.slice(0, limit);
|
|
1288
|
+
|
|
1289
|
+
if (allMemories.length === 0) {
|
|
1290
|
+
console.log(chalk.yellow('\n⚠️ No memories found.'));
|
|
1291
|
+
console.log(chalk.gray('\n Use `namnam memory remember` to save memories.'));
|
|
1292
|
+
return;
|
|
1293
|
+
}
|
|
1294
|
+
|
|
1295
|
+
console.log(chalk.cyan(`\n🧠 All Memories (${allMemories.length}):\n`));
|
|
1296
|
+
|
|
1297
|
+
console.log(chalk.gray(' ID | Type | Importance | Content'));
|
|
1298
|
+
console.log(chalk.gray(' ' + '-'.repeat(70)));
|
|
1299
|
+
|
|
1300
|
+
for (const mem of allMemories) {
|
|
1301
|
+
const content = (mem.summary || mem.content).substring(0, 35);
|
|
1302
|
+
console.log(
|
|
1303
|
+
chalk.gray(` ${mem.id.padEnd(20)} | `) +
|
|
1304
|
+
chalk.cyan(`${mem.type.padEnd(9)} | `) +
|
|
1305
|
+
chalk.yellow(`${mem.importance.padEnd(10)} | `) +
|
|
1306
|
+
chalk.white(content)
|
|
1307
|
+
);
|
|
1308
|
+
}
|
|
1309
|
+
|
|
1310
|
+
console.log(chalk.gray('\n Total: ' + allMemories.length + ' memories'));
|
|
1311
|
+
} catch (error) {
|
|
1312
|
+
console.error(chalk.red('Failed to list memories:'), error.message);
|
|
1313
|
+
process.exit(1);
|
|
1314
|
+
}
|
|
1315
|
+
});
|
|
1316
|
+
|
|
1317
|
+
memory
|
|
1318
|
+
.command('session')
|
|
1319
|
+
.description('Manage current session')
|
|
1320
|
+
.option('-s, --start [task]', 'Start a new session')
|
|
1321
|
+
.option('-e, --end', 'End current session')
|
|
1322
|
+
.option('-u, --update', 'Update session with a decision or note')
|
|
1323
|
+
.option('-d, --decision <text>', 'Record a decision')
|
|
1324
|
+
.option('-n, --note <text>', 'Add a note/memory')
|
|
1325
|
+
.option('--status', 'Show current session status')
|
|
1326
|
+
.action(async (options) => {
|
|
1327
|
+
console.log(banner);
|
|
1328
|
+
|
|
1329
|
+
const cwd = process.cwd();
|
|
1330
|
+
|
|
1331
|
+
try {
|
|
1332
|
+
if (options.start !== undefined) {
|
|
1333
|
+
const task = typeof options.start === 'string' ? options.start : null;
|
|
1334
|
+
const session = await startSession({ task }, cwd);
|
|
1335
|
+
console.log(chalk.green('\n✅ Session started!'));
|
|
1336
|
+
console.log(chalk.gray(` ID: ${session.id}`));
|
|
1337
|
+
if (task) {
|
|
1338
|
+
console.log(chalk.gray(` Task: ${task}`));
|
|
1339
|
+
}
|
|
1340
|
+
console.log(chalk.cyan('\n Use `namnam memory session --end` when done.'));
|
|
1341
|
+
return;
|
|
1342
|
+
}
|
|
1343
|
+
|
|
1344
|
+
if (options.end) {
|
|
1345
|
+
const session = await endSession({}, cwd);
|
|
1346
|
+
if (!session) {
|
|
1347
|
+
console.log(chalk.yellow('\n⚠️ No active session to end.'));
|
|
1348
|
+
return;
|
|
1349
|
+
}
|
|
1350
|
+
console.log(chalk.green('\n✅ Session ended!'));
|
|
1351
|
+
console.log(chalk.gray(` ID: ${session.id}`));
|
|
1352
|
+
console.log(chalk.gray(` Duration: ${getTimeAgo(new Date(session.startedAt))}`));
|
|
1353
|
+
console.log(chalk.gray(` Decisions saved: ${session.decisions.length}`));
|
|
1354
|
+
console.log(chalk.gray(` Memories saved: ${session.memories.length}`));
|
|
1355
|
+
return;
|
|
1356
|
+
}
|
|
1357
|
+
|
|
1358
|
+
if (options.decision) {
|
|
1359
|
+
await updateSession({ decision: options.decision }, cwd);
|
|
1360
|
+
console.log(chalk.green('\n✅ Decision recorded!'));
|
|
1361
|
+
console.log(chalk.gray(` "${options.decision}"`));
|
|
1362
|
+
return;
|
|
1363
|
+
}
|
|
1364
|
+
|
|
1365
|
+
if (options.note) {
|
|
1366
|
+
await updateSession({ memory: options.note }, cwd);
|
|
1367
|
+
console.log(chalk.green('\n✅ Note added!'));
|
|
1368
|
+
console.log(chalk.gray(` "${options.note}"`));
|
|
1369
|
+
return;
|
|
1370
|
+
}
|
|
1371
|
+
|
|
1372
|
+
// Default: show status
|
|
1373
|
+
const session = await getCurrentSession(cwd);
|
|
1374
|
+
if (!session) {
|
|
1375
|
+
console.log(chalk.yellow('\n⚠️ No active session.'));
|
|
1376
|
+
console.log(chalk.gray('\n Use `namnam memory session --start` to begin.'));
|
|
1377
|
+
return;
|
|
1378
|
+
}
|
|
1379
|
+
|
|
1380
|
+
console.log(chalk.cyan('\n📋 Current Session:\n'));
|
|
1381
|
+
console.log(chalk.gray(` ID: ${session.id}`));
|
|
1382
|
+
console.log(chalk.gray(` Started: ${new Date(session.startedAt).toLocaleString()}`));
|
|
1383
|
+
console.log(chalk.gray(` Task: ${session.task || '(none)'}`));
|
|
1384
|
+
console.log(chalk.gray(` Files modified: ${session.filesModified.length}`));
|
|
1385
|
+
console.log(chalk.gray(` Decisions: ${session.decisions.length}`));
|
|
1386
|
+
console.log(chalk.gray(` Notes: ${session.memories.length}`));
|
|
1387
|
+
|
|
1388
|
+
if (session.decisions.length > 0) {
|
|
1389
|
+
console.log(chalk.cyan('\n Recent Decisions:'));
|
|
1390
|
+
for (const d of session.decisions.slice(-3)) {
|
|
1391
|
+
console.log(chalk.white(` • ${d.content}`));
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
} catch (error) {
|
|
1395
|
+
console.error(chalk.red('Session error:'), error.message);
|
|
1396
|
+
process.exit(1);
|
|
1397
|
+
}
|
|
1398
|
+
});
|
|
1399
|
+
|
|
1400
|
+
memory
|
|
1401
|
+
.command('context')
|
|
1402
|
+
.description('Generate auto-memory context for AI consumption')
|
|
1403
|
+
.option('-q, --query <query>', 'Query to find relevant memories')
|
|
1404
|
+
.option('-l, --limit <n>', 'Max memories to include', '10')
|
|
1405
|
+
.option('--json', 'Output as JSON')
|
|
1406
|
+
.action(async (options) => {
|
|
1407
|
+
const cwd = process.cwd();
|
|
1408
|
+
|
|
1409
|
+
try {
|
|
1410
|
+
const context = await generateAutoMemoryContext({
|
|
1411
|
+
query: options.query,
|
|
1412
|
+
limit: parseInt(options.limit)
|
|
1413
|
+
}, cwd);
|
|
1414
|
+
|
|
1415
|
+
if (!context) {
|
|
1416
|
+
if (!options.json) {
|
|
1417
|
+
console.log(chalk.yellow('No relevant memories found.'));
|
|
1418
|
+
} else {
|
|
1419
|
+
console.log(JSON.stringify({ context: null }));
|
|
1420
|
+
}
|
|
1421
|
+
return;
|
|
1422
|
+
}
|
|
1423
|
+
|
|
1424
|
+
if (options.json) {
|
|
1425
|
+
console.log(JSON.stringify({ context }));
|
|
1426
|
+
} else {
|
|
1427
|
+
console.log(context);
|
|
1428
|
+
}
|
|
1429
|
+
} catch (error) {
|
|
1430
|
+
console.error(chalk.red('Failed to generate context:'), error.message);
|
|
1431
|
+
process.exit(1);
|
|
1432
|
+
}
|
|
1433
|
+
});
|
|
1434
|
+
|
|
1435
|
+
memory
|
|
1436
|
+
.command('init')
|
|
1437
|
+
.description('Initialize auto-memory system for this project')
|
|
1438
|
+
.action(async () => {
|
|
1439
|
+
console.log(banner);
|
|
1440
|
+
|
|
1441
|
+
const cwd = process.cwd();
|
|
1442
|
+
|
|
1443
|
+
try {
|
|
1444
|
+
const spinner = ora('Initializing auto-memory system...').start();
|
|
1445
|
+
await initAutoMemory(cwd);
|
|
1446
|
+
spinner.succeed(chalk.green('Auto-memory system initialized!'));
|
|
1447
|
+
|
|
1448
|
+
console.log(chalk.cyan('\n📝 Memory directory created at:'));
|
|
1449
|
+
console.log(chalk.gray(` .claude/auto-memories/`));
|
|
1450
|
+
|
|
1451
|
+
console.log(chalk.cyan('\n🚀 Quick Start:'));
|
|
1452
|
+
console.log(chalk.gray(' namnam memory remember "User prefers TypeScript" -t pattern'));
|
|
1453
|
+
console.log(chalk.gray(' namnam memory recall "preferences"'));
|
|
1454
|
+
console.log(chalk.gray(' namnam memory session --start "Implement feature X"'));
|
|
1455
|
+
} catch (error) {
|
|
1456
|
+
console.error(chalk.red('Failed to initialize:'), error.message);
|
|
1457
|
+
process.exit(1);
|
|
1458
|
+
}
|
|
1459
|
+
});
|
|
1460
|
+
|
|
1461
|
+
// Helper function for time ago
|
|
1462
|
+
function getTimeAgo(date) {
|
|
1463
|
+
const seconds = Math.floor((new Date() - date) / 1000);
|
|
1464
|
+
|
|
1465
|
+
if (seconds < 60) return 'just now';
|
|
1466
|
+
if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
|
|
1467
|
+
if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
|
|
1468
|
+
if (seconds < 604800) return `${Math.floor(seconds / 86400)}d ago`;
|
|
1469
|
+
return date.toLocaleDateString();
|
|
1470
|
+
}
|
|
1471
|
+
|
|
1004
1472
|
program.parse();
|