claude-mem-lite 2.3.3 → 2.5.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/.claude-plugin/plugin.json +1 -1
- package/dispatch-feedback.mjs +45 -0
- package/dispatch-inject.mjs +4 -1
- package/dispatch-workflow.mjs +20 -5
- package/dispatch.mjs +108 -18
- package/haiku-client.mjs +1 -0
- package/hook-context.mjs +32 -1
- package/hook-handoff.mjs +27 -0
- package/hook-llm.mjs +35 -12
- package/hook-memory.mjs +44 -7
- package/hook-shared.mjs +2 -1
- package/hook.mjs +65 -35
- package/install.mjs +430 -1
- package/package.json +1 -1
- package/registry-indexer.mjs +4 -1
- package/registry.mjs +12 -2
- package/schema.mjs +4 -0
- package/server-internals.mjs +68 -1
- package/server.mjs +22 -30
- package/utils.mjs +64 -0
package/package.json
CHANGED
package/registry-indexer.mjs
CHANGED
|
@@ -24,7 +24,7 @@ ${content}
|
|
|
24
24
|
</content>
|
|
25
25
|
|
|
26
26
|
JSON format:
|
|
27
|
-
{"intent_tags":"comma-separated intent keywords (e.g. test,debug,deploy,review)","domain_tags":"comma-separated tech/language tags (e.g. javascript,react,python)","action_type":"analyze|generate|transform|validate|deploy|configure|review","trigger_patterns":"natural language describing when to use this (e.g. when user needs to write tests; when debugging errors)","capability_summary":"50-100 char description of what it does","input_type":"code|file|directory|url|text","output_type":"report|file|diff|terminal_output|analysis"}`;
|
|
27
|
+
{"intent_tags":"comma-separated intent keywords (e.g. test,debug,deploy,review)","domain_tags":"comma-separated tech/language tags (e.g. javascript,react,python)","action_type":"analyze|generate|transform|validate|deploy|configure|review","trigger_patterns":"natural language describing when to use this (e.g. when user needs to write tests; when debugging errors)","capability_summary":"50-100 char description of what it does","keywords":"specific technical terms, framework names, method names not duplicating intent_tags (e.g. jest,vitest,red-green-refactor,pytest)","tech_stack":"specific frameworks and tools this works with (e.g. jest,vitest,mocha,pytest)","use_cases":"3-5 specific usage scenarios separated by semicolons","input_type":"code|file|directory|url|text","output_type":"report|file|diff|terminal_output|analysis"}`;
|
|
28
28
|
}
|
|
29
29
|
|
|
30
30
|
// ─── Fallback Extraction ─────────────────────────────────────────────────────
|
|
@@ -127,6 +127,9 @@ export async function indexResource(db, resource) {
|
|
|
127
127
|
capability_summary: metadata.capability_summary || '',
|
|
128
128
|
input_type: metadata.input_type || '',
|
|
129
129
|
output_type: metadata.output_type || '',
|
|
130
|
+
keywords: metadata.keywords || '',
|
|
131
|
+
tech_stack: metadata.tech_stack || '',
|
|
132
|
+
use_cases: metadata.use_cases || '',
|
|
130
133
|
prerequisites: typeof metadata.prerequisites === 'object'
|
|
131
134
|
? JSON.stringify(metadata.prerequisites) : '{}',
|
|
132
135
|
indexed_at: now,
|
package/registry.mjs
CHANGED
|
@@ -106,6 +106,7 @@ const INVOCATIONS_SCHEMA = `
|
|
|
106
106
|
adopted INTEGER DEFAULT 0,
|
|
107
107
|
outcome TEXT CHECK(outcome IN ('success','partial','failure','skipped','ignored') OR outcome IS NULL),
|
|
108
108
|
score REAL,
|
|
109
|
+
rejection_reason TEXT CHECK(rejection_reason IN ('alternative','manual','context_switch','session_end','unknown') OR rejection_reason IS NULL),
|
|
109
110
|
created_at TEXT DEFAULT (datetime('now'))
|
|
110
111
|
);
|
|
111
112
|
|
|
@@ -210,6 +211,14 @@ export function ensureRegistryDb(dbPath) {
|
|
|
210
211
|
}
|
|
211
212
|
} catch (e) { debugCatch(e, 'ensureRegistryDb-ignored-migration'); }
|
|
212
213
|
|
|
214
|
+
// Migrate: add rejection_reason column if missing
|
|
215
|
+
try {
|
|
216
|
+
const cols = db.prepare("PRAGMA table_info(invocations)").all();
|
|
217
|
+
if (!cols.some(c => c.name === 'rejection_reason')) {
|
|
218
|
+
db.exec("ALTER TABLE invocations ADD COLUMN rejection_reason TEXT");
|
|
219
|
+
}
|
|
220
|
+
} catch (e) { debugCatch(e, 'rejection_reason-migration'); }
|
|
221
|
+
|
|
213
222
|
db.exec(PREINSTALLED_SCHEMA);
|
|
214
223
|
|
|
215
224
|
return db;
|
|
@@ -229,7 +238,8 @@ const UPSERT_SQL = `
|
|
|
229
238
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, datetime('now'))
|
|
230
239
|
ON CONFLICT(type, name) DO UPDATE SET
|
|
231
240
|
status=excluded.status, source=excluded.source, repo_url=excluded.repo_url,
|
|
232
|
-
repo_stars=excluded.repo_stars
|
|
241
|
+
repo_stars=CASE WHEN excluded.repo_stars > 0 THEN excluded.repo_stars ELSE repo_stars END,
|
|
242
|
+
local_path=excluded.local_path, file_hash=excluded.file_hash,
|
|
233
243
|
invocation_name=CASE WHEN excluded.invocation_name != '' THEN excluded.invocation_name ELSE invocation_name END,
|
|
234
244
|
intent_tags=excluded.intent_tags, domain_tags=excluded.domain_tags,
|
|
235
245
|
action_type=excluded.action_type, trigger_patterns=excluded.trigger_patterns,
|
|
@@ -369,7 +379,7 @@ export function getSessionInvocations(db, sessionId) {
|
|
|
369
379
|
* @param {object} update Fields to update
|
|
370
380
|
*/
|
|
371
381
|
export function updateInvocation(db, id, update) {
|
|
372
|
-
const allowed = new Set(['adopted', 'outcome', 'score']);
|
|
382
|
+
const allowed = new Set(['adopted', 'outcome', 'score', 'rejection_reason']);
|
|
373
383
|
const sets = [];
|
|
374
384
|
const vals = [];
|
|
375
385
|
for (const [key, val] of Object.entries(update)) {
|
package/schema.mjs
CHANGED
|
@@ -99,6 +99,10 @@ const MIGRATIONS = [
|
|
|
99
99
|
'ALTER TABLE observations ADD COLUMN access_count INTEGER DEFAULT 0',
|
|
100
100
|
'ALTER TABLE observations ADD COLUMN compressed_into INTEGER DEFAULT NULL',
|
|
101
101
|
'ALTER TABLE session_summaries ADD COLUMN remaining_items TEXT',
|
|
102
|
+
'ALTER TABLE observations ADD COLUMN lesson_learned TEXT DEFAULT NULL',
|
|
103
|
+
'ALTER TABLE observations ADD COLUMN search_aliases TEXT DEFAULT NULL',
|
|
104
|
+
'ALTER TABLE session_summaries ADD COLUMN lessons TEXT DEFAULT NULL',
|
|
105
|
+
'ALTER TABLE session_summaries ADD COLUMN key_decisions TEXT DEFAULT NULL',
|
|
102
106
|
];
|
|
103
107
|
|
|
104
108
|
/**
|
package/server-internals.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// claude-mem-lite server internal functions
|
|
2
2
|
// Extracted from server.mjs for testability (server.mjs has top-level side effects)
|
|
3
3
|
|
|
4
|
-
import { debugCatch } from './utils.mjs';
|
|
4
|
+
import { debugCatch, COMPRESSED_AUTO, COMPRESSED_PENDING_PURGE } from './utils.mjs';
|
|
5
5
|
|
|
6
6
|
// ─── Search Re-ranking Helpers ────────────────────────────────────────────
|
|
7
7
|
|
|
@@ -193,3 +193,70 @@ export function expandQueryByConcepts(db, ftsQuery, project) {
|
|
|
193
193
|
.slice(0, 3)
|
|
194
194
|
.map(([concept]) => concept);
|
|
195
195
|
}
|
|
196
|
+
|
|
197
|
+
// ─── Auto-boost ─────────────────────────────────────────────────────────────
|
|
198
|
+
|
|
199
|
+
/**
|
|
200
|
+
* Boost importance to 2 for observations that have been accessed multiple times
|
|
201
|
+
* (access_count >= 2) but still have default importance (1).
|
|
202
|
+
* Called after incrementing access_count in mem_get.
|
|
203
|
+
* @param {object} db better-sqlite3 database handle
|
|
204
|
+
* @param {number[]} ids Array of observation IDs to check
|
|
205
|
+
*/
|
|
206
|
+
export function autoBoostIfNeeded(db, ids) {
|
|
207
|
+
if (!ids || ids.length === 0) return;
|
|
208
|
+
const placeholders = ids.map(() => '?').join(',');
|
|
209
|
+
db.prepare(`
|
|
210
|
+
UPDATE observations SET importance = 2
|
|
211
|
+
WHERE id IN (${placeholders})
|
|
212
|
+
AND COALESCE(importance, 1) = 1
|
|
213
|
+
AND COALESCE(access_count, 0) >= 2
|
|
214
|
+
`).run(...ids);
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
// ─── Idle Cleanup ────────────────────────────────────────────────────────────
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Run type-differentiated idle cleanup on stale observations.
|
|
221
|
+
* Higher-value types (decision, discovery) survive longer than ephemeral types (change).
|
|
222
|
+
* - Marks low-quality (importance<=1, never accessed) as pending-purge (COMPRESSED_PENDING_PURGE).
|
|
223
|
+
* - Marks importance=1 accessed observations as auto-compressed (COMPRESSED_AUTO).
|
|
224
|
+
* @param {object} db better-sqlite3 database handle
|
|
225
|
+
* @returns {{ marked: number, compressed: number }}
|
|
226
|
+
*/
|
|
227
|
+
export function runIdleCleanup(db) {
|
|
228
|
+
// SAFETY: type values are hardcoded constants, not user input
|
|
229
|
+
const staleThresholds = [
|
|
230
|
+
{ types: "'decision','discovery'", days: 90 },
|
|
231
|
+
{ types: "'feature'", days: 60 },
|
|
232
|
+
{ types: "'bugfix','refactor'", days: 30 },
|
|
233
|
+
{ types: "'change'", days: 14 },
|
|
234
|
+
];
|
|
235
|
+
|
|
236
|
+
let totalMarked = 0;
|
|
237
|
+
let totalCompressed = 0;
|
|
238
|
+
|
|
239
|
+
db.transaction(() => {
|
|
240
|
+
for (const { types, days } of staleThresholds) {
|
|
241
|
+
const cutoff = Date.now() - days * 86400000;
|
|
242
|
+
|
|
243
|
+
const marked = db.prepare(`
|
|
244
|
+
UPDATE observations SET compressed_into = ${COMPRESSED_PENDING_PURGE}
|
|
245
|
+
WHERE importance <= 1 AND COALESCE(access_count, 0) = 0
|
|
246
|
+
AND type IN (${types})
|
|
247
|
+
AND created_at_epoch < ? AND COALESCE(compressed_into, 0) = 0
|
|
248
|
+
`).run(cutoff);
|
|
249
|
+
totalMarked += marked.changes;
|
|
250
|
+
|
|
251
|
+
const compressed = db.prepare(`
|
|
252
|
+
UPDATE observations SET compressed_into = ${COMPRESSED_AUTO}
|
|
253
|
+
WHERE COALESCE(compressed_into, 0) = 0 AND importance = 1
|
|
254
|
+
AND type IN (${types})
|
|
255
|
+
AND created_at_epoch < ?
|
|
256
|
+
`).run(cutoff);
|
|
257
|
+
totalCompressed += compressed.changes;
|
|
258
|
+
}
|
|
259
|
+
})();
|
|
260
|
+
|
|
261
|
+
return { marked: totalMarked, compressed: totalCompressed };
|
|
262
|
+
}
|
package/server.mjs
CHANGED
|
@@ -6,7 +6,7 @@ import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
|
|
6
6
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
|
7
7
|
import { jaccardSimilarity, truncate, typeIcon, sanitizeFtsQuery, relaxFtsQueryToOr, inferProject, computeMinHash, estimateJaccardFromMinHash, scrubSecrets, fmtDate, isoWeekKey, debugLog, debugCatch, COMPRESSED_AUTO, COMPRESSED_PENDING_PURGE } from './utils.mjs';
|
|
8
8
|
import { ensureDb, DB_PATH, REGISTRY_DB_PATH } from './schema.mjs';
|
|
9
|
-
import { reRankWithContext, markSuperseded, extractPRFTerms, expandQueryByConcepts } from './server-internals.mjs';
|
|
9
|
+
import { reRankWithContext, markSuperseded, extractPRFTerms, expandQueryByConcepts, autoBoostIfNeeded, runIdleCleanup } from './server-internals.mjs';
|
|
10
10
|
import { memSearchSchema, memTimelineSchema, memGetSchema, memDeleteSchema, memSaveSchema, memStatsSchema, memCompressSchema, memMaintainSchema, memRegistrySchema } from './tool-schemas.mjs';
|
|
11
11
|
import { ensureRegistryDb, upsertResource } from './registry.mjs';
|
|
12
12
|
import { createRequire } from 'module';
|
|
@@ -121,15 +121,28 @@ function safeHandler(fn) {
|
|
|
121
121
|
|
|
122
122
|
// ─── Tool: mem_search — helper functions ────────────────────────────────────
|
|
123
123
|
|
|
124
|
+
// Type-differentiated recency decay: decisions persist longer, routine changes fade fast
|
|
125
|
+
const TYPE_DECAY_CASE = `(
|
|
126
|
+
CASE o.type
|
|
127
|
+
WHEN 'decision' THEN 7776000000.0
|
|
128
|
+
WHEN 'discovery' THEN 5184000000.0
|
|
129
|
+
WHEN 'feature' THEN 2592000000.0
|
|
130
|
+
WHEN 'bugfix' THEN 1209600000.0
|
|
131
|
+
WHEN 'refactor' THEN 1209600000.0
|
|
132
|
+
WHEN 'change' THEN 604800000.0
|
|
133
|
+
ELSE 1209600000.0
|
|
134
|
+
END
|
|
135
|
+
)`;
|
|
136
|
+
|
|
124
137
|
// Score expression variants for FTS5 queries (see Scoring Model Constants above)
|
|
125
138
|
const FULL_SCORE = `${OBS_BM25}
|
|
126
|
-
* (1.0 + EXP(-0.693 * (? - o.created_at_epoch) / ${
|
|
139
|
+
* (1.0 + EXP(-0.693 * (? - o.created_at_epoch) / ${TYPE_DECAY_CASE}))
|
|
127
140
|
* (CASE WHEN ? IS NOT NULL AND o.project = ? THEN 2.0 ELSE 1.0 END)
|
|
128
141
|
* (0.5 + 0.5 * COALESCE(o.importance, 1))
|
|
129
142
|
* (1.0 + 0.1 * LN(1 + COALESCE(o.access_count, 0)))`;
|
|
130
143
|
|
|
131
144
|
const SIMPLE_SCORE = `${OBS_BM25}
|
|
132
|
-
* (1.0 + EXP(-0.693 * (? - o.created_at_epoch) / ${
|
|
145
|
+
* (1.0 + EXP(-0.693 * (? - o.created_at_epoch) / ${TYPE_DECAY_CASE}))
|
|
133
146
|
* (0.5 + 0.5 * COALESCE(o.importance, 1))`;
|
|
134
147
|
|
|
135
148
|
/**
|
|
@@ -607,6 +620,8 @@ server.registerTool(
|
|
|
607
620
|
db.prepare(
|
|
608
621
|
`UPDATE observations SET access_count = COALESCE(access_count, 0) + 1 WHERE id IN (${placeholders})`
|
|
609
622
|
).run(...args.ids);
|
|
623
|
+
// Auto-boost importance for frequently accessed observations
|
|
624
|
+
autoBoostIfNeeded(db, args.ids);
|
|
610
625
|
rows = db.prepare(`SELECT * FROM observations WHERE id IN (${placeholders}) ORDER BY created_at_epoch ASC`).all(...args.ids);
|
|
611
626
|
allFields = ['id', 'type', 'title', 'subtitle', 'narrative', 'text', 'facts', 'concepts', 'files_read', 'files_modified', 'project', 'created_at', 'memory_session_id', 'prompt_number', 'importance', 'related_ids', 'access_count'];
|
|
612
627
|
prefix = '#';
|
|
@@ -1249,33 +1264,10 @@ const idleTimer = setInterval(() => {
|
|
|
1249
1264
|
idleCleanupRan = true;
|
|
1250
1265
|
|
|
1251
1266
|
try {
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
// Actual deletion only happens when user confirms via mem_maintain execute purge_stale.
|
|
1257
|
-
// NOTE: no project filter — MCP server is global, operates across all projects.
|
|
1258
|
-
const marked = db.prepare(`
|
|
1259
|
-
UPDATE observations SET compressed_into = ${COMPRESSED_PENDING_PURGE}
|
|
1260
|
-
WHERE importance <= 1 AND COALESCE(access_count, 0) = 0
|
|
1261
|
-
AND created_at_epoch < ? AND COALESCE(compressed_into, 0) = 0
|
|
1262
|
-
`).run(thirtyDaysAgo);
|
|
1263
|
-
if (marked.changes > 0) {
|
|
1264
|
-
debugLog('INFO', 'idle-cleanup', `Marked ${marked.changes} stale observations as pending-purge`);
|
|
1265
|
-
}
|
|
1266
|
-
|
|
1267
|
-
// Mark old importance=1 with access_count>0 as compressed (30+ days).
|
|
1268
|
-
// Note: importance=1, access_count=0 rows were already marked pending-purge above,
|
|
1269
|
-
// so this only catches importance=1 rows that HAVE been accessed.
|
|
1270
|
-
const compressed = db.prepare(`
|
|
1271
|
-
UPDATE observations SET compressed_into = ${COMPRESSED_AUTO}
|
|
1272
|
-
WHERE COALESCE(compressed_into, 0) = 0 AND importance = 1
|
|
1273
|
-
AND created_at_epoch < ?
|
|
1274
|
-
`).run(thirtyDaysAgo);
|
|
1275
|
-
if (compressed.changes > 0) {
|
|
1276
|
-
debugLog('INFO', 'idle-cleanup', `Compressed ${compressed.changes} old observations`);
|
|
1277
|
-
}
|
|
1278
|
-
})();
|
|
1267
|
+
// Type-differentiated cleanup: higher-value types survive longer
|
|
1268
|
+
const { marked, compressed } = runIdleCleanup(db);
|
|
1269
|
+
if (marked > 0) debugLog('INFO', 'idle-cleanup', `Marked ${marked} stale observations as pending-purge`);
|
|
1270
|
+
if (compressed > 0) debugLog('INFO', 'idle-cleanup', `Compressed ${compressed} old observations`);
|
|
1279
1271
|
|
|
1280
1272
|
// FTS5 index optimization (outside transaction — WAL-friendly)
|
|
1281
1273
|
db.exec("INSERT INTO observations_fts(observations_fts) VALUES('optimize')");
|
package/utils.mjs
CHANGED
|
@@ -10,6 +10,19 @@ export const COMPRESSED_AUTO = -1;
|
|
|
10
10
|
/** compressed_into sentinel: pending user-confirmed purge (marked by idle cleanup) */
|
|
11
11
|
export const COMPRESSED_PENDING_PURGE = -2;
|
|
12
12
|
|
|
13
|
+
// ─── Type-Differentiated Recency Decay ──────────────────────────────────────
|
|
14
|
+
|
|
15
|
+
/** Recency half-life per observation type (in milliseconds) */
|
|
16
|
+
export const DECAY_HALF_LIFE_BY_TYPE = {
|
|
17
|
+
decision: 90 * 86400000, // 90 days — architectural decisions persist
|
|
18
|
+
discovery: 60 * 86400000, // 60 days — learned patterns last
|
|
19
|
+
feature: 30 * 86400000, // 30 days — feature work is mid-range
|
|
20
|
+
bugfix: 14 * 86400000, // 14 days — bugs are usually one-off
|
|
21
|
+
refactor: 14 * 86400000, // 14 days — code cleanup
|
|
22
|
+
change: 7 * 86400000, // 7 days — routine changes decay fast
|
|
23
|
+
};
|
|
24
|
+
export const DEFAULT_DECAY_HALF_LIFE_MS = 14 * 86400000;
|
|
25
|
+
|
|
13
26
|
// ─── String Utilities ────────────────────────────────────────────────────────
|
|
14
27
|
|
|
15
28
|
/**
|
|
@@ -255,6 +268,57 @@ const SYNONYM_PAIRS = [
|
|
|
255
268
|
['debug', 'troubleshoot'],
|
|
256
269
|
['error', 'failure'],
|
|
257
270
|
['migrate', 'migration'],
|
|
271
|
+
// ─── CJK ↔ EN cross-language synonyms ───
|
|
272
|
+
// Authentication & Authorization
|
|
273
|
+
['认证', 'auth'], ['认证', 'authentication'], ['登录', 'login'], ['登录', 'auth'],
|
|
274
|
+
['授权', 'authorization'], ['权限', 'permission'],
|
|
275
|
+
// Deployment & Operations
|
|
276
|
+
['部署', 'deploy'], ['部署', 'deployment'], ['发布', 'release'], ['发布', 'publish'],
|
|
277
|
+
// Data & Storage
|
|
278
|
+
['缓存', 'cache'], ['缓存', 'caching'],
|
|
279
|
+
['数据库', 'database'], ['数据库', 'db'],
|
|
280
|
+
// Testing & Debugging
|
|
281
|
+
['测试', 'test'], ['测试', 'testing'],
|
|
282
|
+
['调试', 'debug'], ['调试', 'debugging'],
|
|
283
|
+
['修复', 'fix'], ['修复', 'bugfix'],
|
|
284
|
+
// Code Quality
|
|
285
|
+
['重构', 'refactor'], ['重构', 'refactoring'],
|
|
286
|
+
['配置', 'config'], ['配置', 'configuration'],
|
|
287
|
+
// API & Networking
|
|
288
|
+
['接口', 'api'], ['接口', 'endpoint'],
|
|
289
|
+
['路由', 'route'], ['路由', 'routing'],
|
|
290
|
+
['中间件', 'middleware'],
|
|
291
|
+
// UI & Components
|
|
292
|
+
['组件', 'component'], ['模板', 'template'],
|
|
293
|
+
// Database Operations
|
|
294
|
+
['迁移', 'migration'], ['迁移', 'migrate'],
|
|
295
|
+
['索引', 'index'], ['查询', 'query'], ['查询', 'search'],
|
|
296
|
+
['排序', 'sort'], ['分页', 'pagination'],
|
|
297
|
+
// Validation & Security
|
|
298
|
+
['验证', 'validate'], ['验证', 'validation'],
|
|
299
|
+
['加密', 'encrypt'], ['加密', 'encryption'],
|
|
300
|
+
['会话', 'session'], ['令牌', 'token'],
|
|
301
|
+
// Patterns & Architecture
|
|
302
|
+
['钩子', 'hook'], ['回调', 'callback'],
|
|
303
|
+
['异步', 'async'], ['同步', 'sync'],
|
|
304
|
+
['并发', 'concurrent'], ['线程', 'thread'],
|
|
305
|
+
// Performance
|
|
306
|
+
['性能', 'performance'], ['性能', 'perf'],
|
|
307
|
+
['内存', 'memory'], ['泄漏', 'leak'],
|
|
308
|
+
['超时', 'timeout'], ['重试', 'retry'],
|
|
309
|
+
// Observability
|
|
310
|
+
['日志', 'log'], ['日志', 'logging'],
|
|
311
|
+
['监控', 'monitor'], ['告警', 'alert'],
|
|
312
|
+
// Build & Dependencies
|
|
313
|
+
['依赖', 'dependency'], ['构建', 'build'], ['构建', 'compile'],
|
|
314
|
+
['打包', 'bundle'], ['类型', 'type'], ['类型', 'typescript'],
|
|
315
|
+
// Errors
|
|
316
|
+
['错误', 'error'], ['异常', 'exception'],
|
|
317
|
+
// Infrastructure
|
|
318
|
+
['容器', 'container'], ['容器', 'docker'],
|
|
319
|
+
['集群', 'cluster'], ['集群', 'kubernetes'],
|
|
320
|
+
['网关', 'gateway'], ['负载', 'load balancing'],
|
|
321
|
+
['队列', 'queue'], ['序列化', 'serialize'],
|
|
258
322
|
];
|
|
259
323
|
// Build bidirectional lookup (case-insensitive)
|
|
260
324
|
for (const [abbr, full] of SYNONYM_PAIRS) {
|