delimit-cli 4.1.34 → 4.1.36

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/README.md CHANGED
@@ -161,6 +161,12 @@ npx delimit-cli hooks install # Install git pre-commit hook
161
161
  npx delimit-cli hooks install --pre-push # Also add pre-push hook
162
162
  npx delimit-cli ci # Generate GitHub Action workflow
163
163
  npx delimit-cli ci --strict --dry-run # Preview strict workflow
164
+ npx delimit-cli remember "Redis uses JWT 15min" # Save a persistent memory
165
+ npx delimit-cli recall redis # Search memories
166
+ npx delimit-cli recall # Show recent memories
167
+ npx delimit-cli recall --tag deploy --all # Filter by tag, show all
168
+ npx delimit-cli recall --export # Export as markdown
169
+ npx delimit-cli forget abc123 # Delete a memory by ID
164
170
  npx delimit-cli doctor # Check setup health
165
171
  npx delimit-cli uninstall --dry-run # Preview removal
166
172
  ```
@@ -67,7 +67,8 @@ function normalizeNaturalLanguageArgs(argv) {
67
67
 
68
68
  const explicitCommands = new Set([
69
69
  'install', 'mode', 'status', 'session', 'build', 'ask', 'policy', 'auth', 'audit',
70
- 'explain-decision', 'uninstall', 'proxy', 'hook', 'version', 'vault', 'deliberate'
70
+ 'explain-decision', 'uninstall', 'proxy', 'hook', 'version', 'vault', 'deliberate',
71
+ 'remember', 'recall', 'forget'
71
72
  ]);
72
73
  if (explicitCommands.has((raw[0] || '').toLowerCase())) {
73
74
  return raw;
@@ -4228,5 +4229,227 @@ program
4228
4229
  console.log(chalk.gray(' The badge links to Delimit so visitors can learn more.\n'));
4229
4230
  });
4230
4231
 
4232
+ // ---------------------------------------------------------------------------
4233
+ // Memory commands: remember, recall, forget
4234
+ // ---------------------------------------------------------------------------
4235
+
4236
+ const MEMORY_DIR = path.join(os.homedir(), '.delimit', 'memory');
4237
+ const MEMORY_FILE = path.join(MEMORY_DIR, 'memories.jsonl');
4238
+
4239
+ const KNOWN_TECH_TERMS = new Set([
4240
+ 'redis', 'jwt', 'docker', 'k8s', 'kubernetes', 'aws', 'gcp', 'azure', 'api',
4241
+ 'graphql', 'rest', 'grpc', 'postgres', 'mysql', 'mongo', 'mongodb', 'nginx',
4242
+ 'kafka', 'rabbitmq', 'terraform', 'ansible', 'helm', 'react', 'vue', 'angular',
4243
+ 'node', 'python', 'rust', 'go', 'java', 'typescript', 'webpack', 'vite',
4244
+ 'supabase', 'firebase', 'vercel', 'netlify', 'cloudflare', 'lambda', 'sqs',
4245
+ 'sns', 's3', 'ec2', 'ecs', 'eks', 'fargate', 'dynamodb', 'elasticsearch',
4246
+ 'kibana', 'prometheus', 'grafana', 'datadog', 'sentry', 'pagerduty',
4247
+ 'github', 'gitlab', 'bitbucket', 'jira', 'linear', 'notion', 'slack',
4248
+ 'openapi', 'swagger', 'oauth', 'saml', 'sso', 'cicd', 'ci', 'cd',
4249
+ 'cdn', 'dns', 'ssl', 'tls', 'http', 'https', 'websocket', 'ssh',
4250
+ 'cron', 'celery', 'sidekiq', 'redis', 'memcached', 'clickhouse',
4251
+ ]);
4252
+
4253
+ function generateShortId() {
4254
+ const chars = 'abcdefghijklmnopqrstuvwxyz0123456789';
4255
+ let id = '';
4256
+ for (let i = 0; i < 6; i++) {
4257
+ id += chars[Math.floor(Math.random() * chars.length)];
4258
+ }
4259
+ return id;
4260
+ }
4261
+
4262
+ function extractTags(text) {
4263
+ const tags = new Set();
4264
+ const words = text.split(/[\s,;:.!?()\[\]{}"']+/).filter(Boolean);
4265
+ for (const word of words) {
4266
+ // @mentions
4267
+ if (word.startsWith('@') && word.length > 1) {
4268
+ tags.add(word.toLowerCase());
4269
+ continue;
4270
+ }
4271
+ // ALL_CAPS words (at least 2 chars, allow underscores)
4272
+ if (/^[A-Z][A-Z0-9_]{1,}$/.test(word)) {
4273
+ tags.add(word.toLowerCase());
4274
+ continue;
4275
+ }
4276
+ // Known tech terms
4277
+ const lower = word.toLowerCase().replace(/[^a-z0-9]/g, '');
4278
+ if (lower.length >= 2 && KNOWN_TECH_TERMS.has(lower)) {
4279
+ tags.add(lower);
4280
+ }
4281
+ }
4282
+ return [...tags];
4283
+ }
4284
+
4285
+ function readMemories() {
4286
+ if (!fs.existsSync(MEMORY_FILE)) return [];
4287
+ const lines = fs.readFileSync(MEMORY_FILE, 'utf-8').split('\n').filter(l => l.trim());
4288
+ const memories = [];
4289
+ for (const line of lines) {
4290
+ try { memories.push(JSON.parse(line)); } catch {}
4291
+ }
4292
+ return memories;
4293
+ }
4294
+
4295
+ function writeMemories(memories) {
4296
+ fs.mkdirSync(MEMORY_DIR, { recursive: true });
4297
+ fs.writeFileSync(MEMORY_FILE, memories.map(m => JSON.stringify(m)).join('\n') + (memories.length ? '\n' : ''));
4298
+ }
4299
+
4300
+ function relativeTime(isoDate) {
4301
+ const now = Date.now();
4302
+ const then = new Date(isoDate).getTime();
4303
+ const diffSec = Math.floor((now - then) / 1000);
4304
+ if (diffSec < 60) return 'just now';
4305
+ const diffMin = Math.floor(diffSec / 60);
4306
+ if (diffMin < 60) return `${diffMin} minute${diffMin === 1 ? '' : 's'} ago`;
4307
+ const diffHr = Math.floor(diffMin / 60);
4308
+ if (diffHr < 24) return `${diffHr} hour${diffHr === 1 ? '' : 's'} ago`;
4309
+ const diffDay = Math.floor(diffHr / 24);
4310
+ if (diffDay < 7) return `${diffDay} day${diffDay === 1 ? '' : 's'} ago`;
4311
+ const diffWeek = Math.floor(diffDay / 7);
4312
+ if (diffWeek < 5) return `${diffWeek} week${diffWeek === 1 ? '' : 's'} ago`;
4313
+ const diffMonth = Math.floor(diffDay / 30);
4314
+ if (diffMonth < 12) return `${diffMonth} month${diffMonth === 1 ? '' : 's'} ago`;
4315
+ const diffYear = Math.floor(diffDay / 365);
4316
+ return `${diffYear} year${diffYear === 1 ? '' : 's'} ago`;
4317
+ }
4318
+
4319
+ function displayMemory(mem) {
4320
+ console.log(` ${chalk.gray('[' + mem.id + ']')} ${chalk.gray(relativeTime(mem.created))}`);
4321
+ console.log(` ${mem.text}`);
4322
+ if (mem.tags && mem.tags.length > 0) {
4323
+ console.log(` ${chalk.blue(mem.tags.map(t => '#' + t).join(' '))}`);
4324
+ }
4325
+ console.log('');
4326
+ }
4327
+
4328
+ program
4329
+ .command('remember <text...>')
4330
+ .description('Save a memory that persists across all AI assistants')
4331
+ .option('--tag <tag>', 'Add a manual tag (repeatable)', (val, prev) => prev ? [...prev, val] : [val])
4332
+ .action((textParts, options) => {
4333
+ const text = textParts.join(' ');
4334
+ const autoTags = extractTags(text);
4335
+ const manualTags = (options.tag || []).map(t => t.toLowerCase());
4336
+ const allTags = [...new Set([...autoTags, ...manualTags])];
4337
+
4338
+ const memories = readMemories();
4339
+ const entry = {
4340
+ id: generateShortId(),
4341
+ text,
4342
+ tags: allTags,
4343
+ created: new Date().toISOString(),
4344
+ source: 'cli',
4345
+ };
4346
+ memories.push(entry);
4347
+ writeMemories(memories);
4348
+
4349
+ console.log(chalk.green(`\n Remembered.`) + chalk.gray(` (${memories.length} memor${memories.length === 1 ? 'y' : 'ies'} total)\n`));
4350
+ });
4351
+
4352
+ program
4353
+ .command('recall [query...]')
4354
+ .description('Search your memories — with no query, shows the most recent')
4355
+ .option('--tag <tag>', 'Filter by tag')
4356
+ .option('--all', 'Show all memories')
4357
+ .option('--forget <id>', 'Delete a memory by ID')
4358
+ .option('--export', 'Export memories as markdown')
4359
+ .action((queryParts, options) => {
4360
+ const memories = readMemories();
4361
+
4362
+ // --forget mode
4363
+ if (options.forget) {
4364
+ const idx = memories.findIndex(m => m.id === options.forget);
4365
+ if (idx === -1) {
4366
+ console.log(chalk.red(`\n No memory found with ID: ${options.forget}\n`));
4367
+ process.exit(1);
4368
+ }
4369
+ memories.splice(idx, 1);
4370
+ writeMemories(memories);
4371
+ console.log(chalk.green(`\n Forgotten.`) + chalk.gray(` (${memories.length} memor${memories.length === 1 ? 'y' : 'ies'} remaining)\n`));
4372
+ return;
4373
+ }
4374
+
4375
+ if (memories.length === 0) {
4376
+ console.log(chalk.gray('\n No memories yet. Save one with:\n'));
4377
+ console.log(` ${chalk.green('delimit remember')} "Your first memory"\n`);
4378
+ return;
4379
+ }
4380
+
4381
+ // --export mode
4382
+ if (options.export) {
4383
+ console.log('# Delimit Memories\n');
4384
+ for (const mem of memories) {
4385
+ const date = new Date(mem.created).toISOString().split('T')[0];
4386
+ console.log(`- **${date}** — ${mem.text}`);
4387
+ if (mem.tags && mem.tags.length > 0) {
4388
+ console.log(` Tags: ${mem.tags.map(t => '`' + t + '`').join(', ')}`);
4389
+ }
4390
+ }
4391
+ console.log(`\n_${memories.length} memories exported._`);
4392
+ return;
4393
+ }
4394
+
4395
+ let results = [...memories];
4396
+ const query = (queryParts || []).join(' ').trim().toLowerCase();
4397
+
4398
+ // Filter by tag
4399
+ if (options.tag) {
4400
+ const tagFilter = options.tag.toLowerCase();
4401
+ results = results.filter(m => m.tags && m.tags.some(t => t.includes(tagFilter)));
4402
+ }
4403
+
4404
+ // Filter by query (case-insensitive substring on text + tags)
4405
+ if (query) {
4406
+ results = results.filter(m => {
4407
+ const haystack = (m.text + ' ' + (m.tags || []).join(' ')).toLowerCase();
4408
+ return haystack.includes(query);
4409
+ });
4410
+ }
4411
+
4412
+ // Unless --all, limit to last 10
4413
+ const total = results.length;
4414
+ if (!options.all && !query && !options.tag) {
4415
+ results = results.slice(-10);
4416
+ }
4417
+
4418
+ // Display newest first
4419
+ results.reverse();
4420
+
4421
+ console.log(chalk.bold('\n Delimit Memories\n'));
4422
+
4423
+ if (results.length === 0) {
4424
+ console.log(chalk.gray(' No matching memories found.\n'));
4425
+ return;
4426
+ }
4427
+
4428
+ for (const mem of results) {
4429
+ displayMemory(mem);
4430
+ }
4431
+
4432
+ const shownCount = results.length;
4433
+ const label = query || options.tag
4434
+ ? `${shownCount} memor${shownCount === 1 ? 'y' : 'ies'} found`
4435
+ : `${shownCount} shown`;
4436
+ console.log(chalk.gray(` ${label} (${memories.length} total)\n`));
4437
+ });
4438
+
4439
+ program
4440
+ .command('forget <id>')
4441
+ .description('Delete a memory by ID (alias for recall --forget)')
4442
+ .action((id) => {
4443
+ const memories = readMemories();
4444
+ const idx = memories.findIndex(m => m.id === id);
4445
+ if (idx === -1) {
4446
+ console.log(chalk.red(`\n No memory found with ID: ${id}\n`));
4447
+ process.exit(1);
4448
+ }
4449
+ memories.splice(idx, 1);
4450
+ writeMemories(memories);
4451
+ console.log(chalk.green(`\n Forgotten.`) + chalk.gray(` (${memories.length} memor${memories.length === 1 ? 'y' : 'ies'} remaining)\n`));
4452
+ });
4453
+
4231
4454
  const normalizedArgs = normalizeNaturalLanguageArgs(process.argv);
4232
4455
  program.parse([process.argv[0], process.argv[1], ...normalizedArgs]);
@@ -482,7 +482,7 @@ fi
482
482
  if (!existingSpecLint) {
483
483
  config.hooks.PostToolUse.push({
484
484
  matcher: 'Edit|Write',
485
- if: "path_matches('**/openapi*') || path_matches('**/swagger*') || path_matches('**/specs/**')",
485
+ if: "path_matches('**/openapi*.yaml') || path_matches('**/openapi*.yml') || path_matches('**/openapi*.json') || path_matches('**/swagger*.yaml') || path_matches('**/swagger*.yml') || path_matches('**/swagger*.json')",
486
486
  hooks: [{
487
487
  type: 'command',
488
488
  command: `${npxCmd} lint "$DELIMIT_FILE_PATH"`,
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "delimit-cli",
3
3
  "mcpName": "io.github.delimit-ai/delimit-mcp-server",
4
- "version": "4.1.34",
4
+ "version": "4.1.36",
5
5
  "description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
6
6
  "main": "index.js",
7
7
  "files": [
package/server.json CHANGED
@@ -1,19 +1,19 @@
1
1
  {
2
2
  "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
3
  "name": "io.github.delimit-ai/delimit-mcp-server",
4
- "title": "Delimit MCP Server",
5
- "description": "Unify Claude Code, Codex, Cursor, and Gemini CLI with persistent context, governance, and multi-model debate.",
4
+ "title": "Delimit API Governance for AI Coding Assistants",
5
+ "description": "API governance for AI coding assistants. Breaking changes, policies, cross-model context.",
6
6
  "repository": {
7
7
  "url": "https://github.com/delimit-ai/delimit-mcp-server",
8
8
  "source": "github"
9
9
  },
10
- "version": "3.11.10",
10
+ "version": "4.1.34",
11
11
  "websiteUrl": "https://delimit.ai",
12
12
  "packages": [
13
13
  {
14
14
  "registryType": "npm",
15
15
  "identifier": "delimit-cli",
16
- "version": "3.11.10",
16
+ "version": "4.1.34",
17
17
  "transport": {
18
18
  "type": "stdio"
19
19
  }