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 +6 -0
- package/bin/delimit-cli.js +224 -1
- package/lib/cross-model-hooks.js +1 -1
- package/package.json +1 -1
- package/server.json +4 -4
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
|
```
|
package/bin/delimit-cli.js
CHANGED
|
@@ -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]);
|
package/lib/cross-model-hooks.js
CHANGED
|
@@ -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
|
|
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.
|
|
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
|
|
5
|
-
"description": "
|
|
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": "
|
|
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": "
|
|
16
|
+
"version": "4.1.34",
|
|
17
17
|
"transport": {
|
|
18
18
|
"type": "stdio"
|
|
19
19
|
}
|