namnam-skills 1.0.1 → 1.0.3

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.3",
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",
@@ -47,7 +47,7 @@
47
47
  "commander": "^12.1.0",
48
48
  "fs-extra": "^11.2.0",
49
49
  "inquirer": "^9.2.15",
50
- "namnam-skills": "^1.0.0",
50
+ "namnam-skills": "^1.0.2",
51
51
  "ora": "^8.0.1"
52
52
  },
53
53
  "engines": {
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,
@@ -46,7 +58,7 @@ ${chalk.cyan('╚═════════════════════
46
58
  const SKILL_CATEGORIES = {
47
59
  core: {
48
60
  name: 'Core Skills',
49
- description: 'Essential: /namnam orchestrator, git, code-review, validate-and-fix',
61
+ description: 'Essential: /namnam mega-command (orchestration, git, review, memory)',
50
62
  folder: 'core'
51
63
  },
52
64
  platforms: {
@@ -201,9 +213,17 @@ program
201
213
  const templatesDir = path.join(__dirname, 'templates');
202
214
  let copiedCount = 0;
203
215
 
216
+ // Always copy namnam.md to root (for /namnam command)
217
+ const namnamSrc = path.join(templatesDir, 'namnam.md');
218
+ const namnamDest = path.join(claudeDir, 'namnam.md');
219
+ if (await fs.pathExists(namnamSrc)) {
220
+ await fs.copy(namnamSrc, namnamDest, { overwrite: options.force });
221
+ copiedCount += 1;
222
+ }
223
+
204
224
  if (selectedCategories.includes('all')) {
205
225
  // Copy everything
206
- const folders = ['core', 'platforms', 'official-plugins', 'vercel-skills', 'claudekit', 'bmad', 'cursor-rules', 'system-prompts'];
226
+ const folders = ['platforms', 'official-plugins', 'vercel-skills', 'claudekit', 'bmad', 'cursor-rules', 'system-prompts'];
207
227
  for (const folder of folders) {
208
228
  const srcDir = path.join(templatesDir, folder);
209
229
  if (await fs.pathExists(srcDir)) {
@@ -214,6 +234,9 @@ program
214
234
  } else {
215
235
  // Copy selected categories
216
236
  for (const category of selectedCategories) {
237
+ // Skip 'core' as namnam.md is already copied to root
238
+ if (category === 'core') continue;
239
+
217
240
  const catConfig = SKILL_CATEGORIES[category];
218
241
  if (!catConfig) continue;
219
242
 
@@ -809,11 +832,36 @@ index
809
832
  .command('build')
810
833
  .description('Build or rebuild the codebase index')
811
834
  .option('-f, --force', 'Force rebuild even if index exists')
835
+ .option('--json', 'Output results as JSON (for programmatic use)')
812
836
  .action(async (options) => {
813
- console.log(banner);
814
-
815
837
  const cwd = process.cwd();
816
838
 
839
+ // JSON mode - no banner or colors
840
+ if (options.json) {
841
+ try {
842
+ const meta = await buildIndex(cwd);
843
+ console.log(JSON.stringify({
844
+ success: true,
845
+ stats: {
846
+ files: meta.stats.totalFiles,
847
+ lines: meta.stats.totalLines,
848
+ functions: meta.stats.totalFunctions,
849
+ classes: meta.stats.totalClasses
850
+ },
851
+ patterns: meta.patterns
852
+ }));
853
+ } catch (error) {
854
+ console.log(JSON.stringify({
855
+ success: false,
856
+ error: error.message
857
+ }));
858
+ process.exit(1);
859
+ }
860
+ return;
861
+ }
862
+
863
+ console.log(banner);
864
+
817
865
  // Check if index exists
818
866
  if (!options.force && await hasIndex(cwd)) {
819
867
  const changes = await checkIndexChanges(cwd);
@@ -879,11 +927,38 @@ index
879
927
  index
880
928
  .command('status')
881
929
  .description('Show index status and statistics')
882
- .action(async () => {
883
- console.log(banner);
884
-
930
+ .option('--json', 'Output results as JSON (for programmatic use)')
931
+ .action(async (options) => {
885
932
  const cwd = process.cwd();
886
933
 
934
+ // JSON mode
935
+ if (options.json) {
936
+ if (!(await hasIndex(cwd))) {
937
+ console.log(JSON.stringify({ exists: false }));
938
+ return;
939
+ }
940
+ const stats = await getIndexStats(cwd);
941
+ const changes = await checkIndexChanges(cwd);
942
+ console.log(JSON.stringify({
943
+ exists: true,
944
+ totalFiles: stats.totalFiles,
945
+ totalLines: stats.totalLines,
946
+ totalFunctions: stats.totalFunctions,
947
+ totalClasses: stats.totalClasses,
948
+ lastUpdated: stats.lastUpdated,
949
+ patterns: stats.patterns,
950
+ hasChanges: changes.hasChanges,
951
+ changes: changes.hasChanges ? {
952
+ newFiles: changes.newFiles?.length || 0,
953
+ modifiedFiles: changes.modifiedFiles?.length || 0,
954
+ deletedFiles: changes.deletedFiles?.length || 0
955
+ } : null
956
+ }));
957
+ return;
958
+ }
959
+
960
+ console.log(banner);
961
+
887
962
  if (!(await hasIndex(cwd))) {
888
963
  console.log(chalk.yellow('\n⚠️ No index found.'));
889
964
  console.log(chalk.gray('Run `namnam index build` to create one.'));
@@ -1001,4 +1076,408 @@ index
1001
1076
  }
1002
1077
  });
1003
1078
 
1079
+ index
1080
+ .command('watch')
1081
+ .description('Watch for file changes and auto-update index (live indexing)')
1082
+ .option('-v, --verbose', 'Show detailed logs')
1083
+ .option('--no-auto-rebuild', 'Only notify on changes, do not auto-rebuild')
1084
+ .action(async (options) => {
1085
+ console.log(banner);
1086
+ console.log(chalk.cyan('\n👁️ Live Index Watcher\n'));
1087
+
1088
+ const cwd = process.cwd();
1089
+
1090
+ // Dynamic import to avoid loading watcher on every CLI call
1091
+ const { IndexWatcher, writeWatcherStatus, clearWatcherStatus } = await import('./watcher.js');
1092
+
1093
+ const watcher = new IndexWatcher(cwd, {
1094
+ verbose: options.verbose,
1095
+ autoRebuild: options.autoRebuild !== false
1096
+ });
1097
+
1098
+ // Event handlers
1099
+ watcher.on('started', () => {
1100
+ console.log(chalk.green('✓ Watcher started'));
1101
+ console.log(chalk.gray(` Watching: ${cwd}`));
1102
+ console.log(chalk.gray(` Auto-rebuild: ${options.autoRebuild !== false ? 'enabled' : 'disabled'}`));
1103
+ console.log(chalk.yellow('\n Press Ctrl+C to stop\n'));
1104
+ });
1105
+
1106
+ watcher.on('change', ({ type, path: filePath }) => {
1107
+ const icon = type === 'rename' ? '~' : type === 'change' ? '±' : '+';
1108
+ console.log(chalk.gray(` ${icon} ${filePath}`));
1109
+ });
1110
+
1111
+ watcher.on('indexing', ({ type }) => {
1112
+ if (type === 'initial') {
1113
+ console.log(chalk.yellow(' Building initial index...'));
1114
+ } else {
1115
+ console.log(chalk.yellow(' Updating index...'));
1116
+ }
1117
+ });
1118
+
1119
+ watcher.on('indexed', ({ type, changesProcessed }) => {
1120
+ if (type === 'initial') {
1121
+ console.log(chalk.green(' ✓ Initial index complete'));
1122
+ } else {
1123
+ console.log(chalk.green(` ✓ Index updated (${changesProcessed} files)`));
1124
+ }
1125
+ writeWatcherStatus(cwd, watcher.getStatus());
1126
+ });
1127
+
1128
+ watcher.on('error', (err) => {
1129
+ console.log(chalk.red(` ✗ Error: ${err.message}`));
1130
+ });
1131
+
1132
+ // Graceful shutdown
1133
+ const shutdown = async () => {
1134
+ console.log(chalk.yellow('\n\nStopping watcher...'));
1135
+ watcher.stop();
1136
+ await clearWatcherStatus(cwd);
1137
+ console.log(chalk.green('Watcher stopped.'));
1138
+ process.exit(0);
1139
+ };
1140
+
1141
+ process.on('SIGINT', shutdown);
1142
+ process.on('SIGTERM', shutdown);
1143
+
1144
+ // Start watching
1145
+ try {
1146
+ await watcher.start();
1147
+ await writeWatcherStatus(cwd, watcher.getStatus());
1148
+ } catch (error) {
1149
+ console.error(chalk.red('Failed to start watcher:'), error.message);
1150
+ process.exit(1);
1151
+ }
1152
+ });
1153
+
1154
+ // ========================================
1155
+ // MEMORY COMMANDS (Auto-Memory System)
1156
+ // ========================================
1157
+
1158
+ const memory = program
1159
+ .command('memory')
1160
+ .alias('mem')
1161
+ .description('Auto-memory system for persistent context across sessions');
1162
+
1163
+ memory
1164
+ .command('remember <content>')
1165
+ .description('Save a memory (decision, pattern, or context)')
1166
+ .option('-t, --type <type>', 'Memory type: decision, pattern, context, learning', 'context')
1167
+ .option('-i, --importance <level>', 'Importance: low, normal, high, critical', 'normal')
1168
+ .option('-g, --tags <tags>', 'Comma-separated tags')
1169
+ .option('-f, --files <files>', 'Related files (comma-separated)')
1170
+ .option('-s, --summary <summary>', 'Short summary of the memory')
1171
+ .action(async (content, options) => {
1172
+ console.log(banner);
1173
+
1174
+ try {
1175
+ const cwd = process.cwd();
1176
+ const spinner = ora('Saving memory...').start();
1177
+
1178
+ const memory = await remember(content, {
1179
+ type: options.type,
1180
+ importance: options.importance,
1181
+ tags: options.tags ? options.tags.split(',').map(t => t.trim()) : [],
1182
+ files: options.files ? options.files.split(',').map(f => f.trim()) : [],
1183
+ summary: options.summary,
1184
+ source: 'user'
1185
+ }, cwd);
1186
+
1187
+ spinner.succeed(chalk.green('Memory saved!'));
1188
+
1189
+ console.log(chalk.cyan('\n📝 Memory Details:'));
1190
+ console.log(chalk.gray(` ID: ${memory.id}`));
1191
+ console.log(chalk.gray(` Type: ${memory.type}`));
1192
+ console.log(chalk.gray(` Importance: ${memory.importance}`));
1193
+ if (memory.tags.length) {
1194
+ console.log(chalk.gray(` Tags: ${memory.tags.join(', ')}`));
1195
+ }
1196
+ } catch (error) {
1197
+ console.error(chalk.red('Failed to save memory:'), error.message);
1198
+ process.exit(1);
1199
+ }
1200
+ });
1201
+
1202
+ memory
1203
+ .command('recall [query]')
1204
+ .description('Recall relevant memories')
1205
+ .option('-l, --limit <n>', 'Number of memories to return', '10')
1206
+ .option('-t, --types <types>', 'Filter by types (comma-separated)', 'decision,pattern,context,learning')
1207
+ .option('-i, --min-importance <level>', 'Minimum importance level', 'low')
1208
+ .option('--json', 'Output as JSON')
1209
+ .action(async (query, options) => {
1210
+ const cwd = process.cwd();
1211
+
1212
+ try {
1213
+ const memories = await recall(query || '', {
1214
+ limit: parseInt(options.limit),
1215
+ types: options.types.split(',').map(t => t.trim()),
1216
+ minImportance: options.minImportance
1217
+ }, cwd);
1218
+
1219
+ if (options.json) {
1220
+ console.log(JSON.stringify(memories, null, 2));
1221
+ return;
1222
+ }
1223
+
1224
+ console.log(banner);
1225
+
1226
+ if (memories.length === 0) {
1227
+ console.log(chalk.yellow('\n⚠️ No memories found.'));
1228
+ if (query) {
1229
+ console.log(chalk.gray(` Query: "${query}"`));
1230
+ }
1231
+ console.log(chalk.gray('\n Use `namnam memory remember` to save memories.'));
1232
+ return;
1233
+ }
1234
+
1235
+ console.log(chalk.cyan(`\n🧠 Memories${query ? ` matching "${query}"` : ''} (${memories.length}):\n`));
1236
+
1237
+ const typeIcons = {
1238
+ decision: '⚖️',
1239
+ pattern: '🔄',
1240
+ context: '📝',
1241
+ learning: '💡'
1242
+ };
1243
+
1244
+ const importanceColors = {
1245
+ low: chalk.gray,
1246
+ normal: chalk.white,
1247
+ high: chalk.yellow,
1248
+ critical: chalk.red
1249
+ };
1250
+
1251
+ for (const mem of memories) {
1252
+ const icon = typeIcons[mem.type] || '📝';
1253
+ const colorFn = importanceColors[mem.importance] || chalk.white;
1254
+ const age = getTimeAgo(new Date(mem.createdAt));
1255
+
1256
+ console.log(colorFn(` ${icon} ${mem.summary || mem.content.substring(0, 60)}...`));
1257
+ console.log(chalk.gray(` ${mem.type} | ${mem.importance} | ${age}`));
1258
+ if (mem.tags.length) {
1259
+ console.log(chalk.gray(` Tags: ${mem.tags.join(', ')}`));
1260
+ }
1261
+ console.log();
1262
+ }
1263
+ } catch (error) {
1264
+ console.error(chalk.red('Failed to recall memories:'), error.message);
1265
+ process.exit(1);
1266
+ }
1267
+ });
1268
+
1269
+ memory
1270
+ .command('list')
1271
+ .description('List all memories')
1272
+ .option('-t, --type <type>', 'Filter by type')
1273
+ .option('-l, --limit <n>', 'Limit results', '20')
1274
+ .action(async (options) => {
1275
+ console.log(banner);
1276
+
1277
+ const cwd = process.cwd();
1278
+
1279
+ try {
1280
+ const index = await loadAutoMemoryIndex(cwd);
1281
+
1282
+ let allMemories = [
1283
+ ...index.memories.map(m => ({ ...m, _category: 'context' })),
1284
+ ...index.decisions.map(m => ({ ...m, _category: 'decision' })),
1285
+ ...index.patterns.map(m => ({ ...m, _category: 'pattern' }))
1286
+ ];
1287
+
1288
+ // Filter by type
1289
+ if (options.type) {
1290
+ allMemories = allMemories.filter(m => m.type === options.type);
1291
+ }
1292
+
1293
+ // Sort by date (newest first)
1294
+ allMemories.sort((a, b) => new Date(b.createdAt) - new Date(a.createdAt));
1295
+
1296
+ // Limit
1297
+ const limit = parseInt(options.limit);
1298
+ allMemories = allMemories.slice(0, limit);
1299
+
1300
+ if (allMemories.length === 0) {
1301
+ console.log(chalk.yellow('\n⚠️ No memories found.'));
1302
+ console.log(chalk.gray('\n Use `namnam memory remember` to save memories.'));
1303
+ return;
1304
+ }
1305
+
1306
+ console.log(chalk.cyan(`\n🧠 All Memories (${allMemories.length}):\n`));
1307
+
1308
+ console.log(chalk.gray(' ID | Type | Importance | Content'));
1309
+ console.log(chalk.gray(' ' + '-'.repeat(70)));
1310
+
1311
+ for (const mem of allMemories) {
1312
+ const content = (mem.summary || mem.content).substring(0, 35);
1313
+ console.log(
1314
+ chalk.gray(` ${mem.id.padEnd(20)} | `) +
1315
+ chalk.cyan(`${mem.type.padEnd(9)} | `) +
1316
+ chalk.yellow(`${mem.importance.padEnd(10)} | `) +
1317
+ chalk.white(content)
1318
+ );
1319
+ }
1320
+
1321
+ console.log(chalk.gray('\n Total: ' + allMemories.length + ' memories'));
1322
+ } catch (error) {
1323
+ console.error(chalk.red('Failed to list memories:'), error.message);
1324
+ process.exit(1);
1325
+ }
1326
+ });
1327
+
1328
+ memory
1329
+ .command('session')
1330
+ .description('Manage current session')
1331
+ .option('-s, --start [task]', 'Start a new session')
1332
+ .option('-e, --end', 'End current session')
1333
+ .option('-u, --update', 'Update session with a decision or note')
1334
+ .option('-d, --decision <text>', 'Record a decision')
1335
+ .option('-n, --note <text>', 'Add a note/memory')
1336
+ .option('--status', 'Show current session status')
1337
+ .action(async (options) => {
1338
+ console.log(banner);
1339
+
1340
+ const cwd = process.cwd();
1341
+
1342
+ try {
1343
+ if (options.start !== undefined) {
1344
+ const task = typeof options.start === 'string' ? options.start : null;
1345
+ const session = await startSession({ task }, cwd);
1346
+ console.log(chalk.green('\n✅ Session started!'));
1347
+ console.log(chalk.gray(` ID: ${session.id}`));
1348
+ if (task) {
1349
+ console.log(chalk.gray(` Task: ${task}`));
1350
+ }
1351
+ console.log(chalk.cyan('\n Use `namnam memory session --end` when done.'));
1352
+ return;
1353
+ }
1354
+
1355
+ if (options.end) {
1356
+ const session = await endSession({}, cwd);
1357
+ if (!session) {
1358
+ console.log(chalk.yellow('\n⚠️ No active session to end.'));
1359
+ return;
1360
+ }
1361
+ console.log(chalk.green('\n✅ Session ended!'));
1362
+ console.log(chalk.gray(` ID: ${session.id}`));
1363
+ console.log(chalk.gray(` Duration: ${getTimeAgo(new Date(session.startedAt))}`));
1364
+ console.log(chalk.gray(` Decisions saved: ${session.decisions.length}`));
1365
+ console.log(chalk.gray(` Memories saved: ${session.memories.length}`));
1366
+ return;
1367
+ }
1368
+
1369
+ if (options.decision) {
1370
+ await updateSession({ decision: options.decision }, cwd);
1371
+ console.log(chalk.green('\n✅ Decision recorded!'));
1372
+ console.log(chalk.gray(` "${options.decision}"`));
1373
+ return;
1374
+ }
1375
+
1376
+ if (options.note) {
1377
+ await updateSession({ memory: options.note }, cwd);
1378
+ console.log(chalk.green('\n✅ Note added!'));
1379
+ console.log(chalk.gray(` "${options.note}"`));
1380
+ return;
1381
+ }
1382
+
1383
+ // Default: show status
1384
+ const session = await getCurrentSession(cwd);
1385
+ if (!session) {
1386
+ console.log(chalk.yellow('\n⚠️ No active session.'));
1387
+ console.log(chalk.gray('\n Use `namnam memory session --start` to begin.'));
1388
+ return;
1389
+ }
1390
+
1391
+ console.log(chalk.cyan('\n📋 Current Session:\n'));
1392
+ console.log(chalk.gray(` ID: ${session.id}`));
1393
+ console.log(chalk.gray(` Started: ${new Date(session.startedAt).toLocaleString()}`));
1394
+ console.log(chalk.gray(` Task: ${session.task || '(none)'}`));
1395
+ console.log(chalk.gray(` Files modified: ${session.filesModified.length}`));
1396
+ console.log(chalk.gray(` Decisions: ${session.decisions.length}`));
1397
+ console.log(chalk.gray(` Notes: ${session.memories.length}`));
1398
+
1399
+ if (session.decisions.length > 0) {
1400
+ console.log(chalk.cyan('\n Recent Decisions:'));
1401
+ for (const d of session.decisions.slice(-3)) {
1402
+ console.log(chalk.white(` • ${d.content}`));
1403
+ }
1404
+ }
1405
+ } catch (error) {
1406
+ console.error(chalk.red('Session error:'), error.message);
1407
+ process.exit(1);
1408
+ }
1409
+ });
1410
+
1411
+ memory
1412
+ .command('context')
1413
+ .description('Generate auto-memory context for AI consumption')
1414
+ .option('-q, --query <query>', 'Query to find relevant memories')
1415
+ .option('-l, --limit <n>', 'Max memories to include', '10')
1416
+ .option('--json', 'Output as JSON')
1417
+ .action(async (options) => {
1418
+ const cwd = process.cwd();
1419
+
1420
+ try {
1421
+ const context = await generateAutoMemoryContext({
1422
+ query: options.query,
1423
+ limit: parseInt(options.limit)
1424
+ }, cwd);
1425
+
1426
+ if (!context) {
1427
+ if (!options.json) {
1428
+ console.log(chalk.yellow('No relevant memories found.'));
1429
+ } else {
1430
+ console.log(JSON.stringify({ context: null }));
1431
+ }
1432
+ return;
1433
+ }
1434
+
1435
+ if (options.json) {
1436
+ console.log(JSON.stringify({ context }));
1437
+ } else {
1438
+ console.log(context);
1439
+ }
1440
+ } catch (error) {
1441
+ console.error(chalk.red('Failed to generate context:'), error.message);
1442
+ process.exit(1);
1443
+ }
1444
+ });
1445
+
1446
+ memory
1447
+ .command('init')
1448
+ .description('Initialize auto-memory system for this project')
1449
+ .action(async () => {
1450
+ console.log(banner);
1451
+
1452
+ const cwd = process.cwd();
1453
+
1454
+ try {
1455
+ const spinner = ora('Initializing auto-memory system...').start();
1456
+ await initAutoMemory(cwd);
1457
+ spinner.succeed(chalk.green('Auto-memory system initialized!'));
1458
+
1459
+ console.log(chalk.cyan('\n📝 Memory directory created at:'));
1460
+ console.log(chalk.gray(` .claude/auto-memories/`));
1461
+
1462
+ console.log(chalk.cyan('\n🚀 Quick Start:'));
1463
+ console.log(chalk.gray(' namnam memory remember "User prefers TypeScript" -t pattern'));
1464
+ console.log(chalk.gray(' namnam memory recall "preferences"'));
1465
+ console.log(chalk.gray(' namnam memory session --start "Implement feature X"'));
1466
+ } catch (error) {
1467
+ console.error(chalk.red('Failed to initialize:'), error.message);
1468
+ process.exit(1);
1469
+ }
1470
+ });
1471
+
1472
+ // Helper function for time ago
1473
+ function getTimeAgo(date) {
1474
+ const seconds = Math.floor((new Date() - date) / 1000);
1475
+
1476
+ if (seconds < 60) return 'just now';
1477
+ if (seconds < 3600) return `${Math.floor(seconds / 60)}m ago`;
1478
+ if (seconds < 86400) return `${Math.floor(seconds / 3600)}h ago`;
1479
+ if (seconds < 604800) return `${Math.floor(seconds / 86400)}d ago`;
1480
+ return date.toLocaleDateString();
1481
+ }
1482
+
1004
1483
  program.parse();