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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "namnam-skills",
3
- "version": "1.0.1",
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
- .action(async () => {
883
- console.log(banner);
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();