chainlesschain 0.47.8 → 0.49.0
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/bin/chainlesschain.js +0 -0
- package/package.json +10 -8
- package/src/assets/web-panel/.build-hash +1 -1
- package/src/assets/web-panel/assets/{AppLayout-6SPt_8Y_.js → AppLayout-Rvi759IS.js} +1 -1
- package/src/assets/web-panel/assets/Dashboard-BS-tzGNj.css +1 -0
- package/src/assets/web-panel/assets/{Dashboard-Br7kCwKJ.js → Dashboard-DBhFxXYQ.js} +2 -2
- package/src/assets/web-panel/assets/{index-tN-8TosE.js → index-uL0cZ8N_.js} +2 -2
- package/src/assets/web-panel/index.html +2 -2
- package/src/commands/activitypub.js +533 -0
- package/src/commands/codegen.js +303 -0
- package/src/commands/collab.js +482 -0
- package/src/commands/compliance.js +597 -6
- package/src/commands/crosschain.js +382 -0
- package/src/commands/dbevo.js +388 -0
- package/src/commands/dev.js +411 -0
- package/src/commands/federation.js +427 -0
- package/src/commands/fusion.js +332 -0
- package/src/commands/governance.js +505 -0
- package/src/commands/hardening.js +110 -0
- package/src/commands/incentive.js +373 -0
- package/src/commands/inference.js +304 -0
- package/src/commands/infra.js +361 -0
- package/src/commands/kg.js +371 -0
- package/src/commands/marketplace.js +326 -0
- package/src/commands/matrix.js +283 -0
- package/src/commands/mcp.js +441 -18
- package/src/commands/nlprog.js +329 -0
- package/src/commands/nostr.js +196 -7
- package/src/commands/ops.js +408 -0
- package/src/commands/perception.js +385 -0
- package/src/commands/pqc.js +34 -0
- package/src/commands/privacy.js +345 -0
- package/src/commands/quantization.js +280 -0
- package/src/commands/recommend.js +336 -0
- package/src/commands/reputation.js +349 -0
- package/src/commands/runtime.js +500 -0
- package/src/commands/sla.js +352 -0
- package/src/commands/social.js +265 -0
- package/src/commands/stress.js +252 -0
- package/src/commands/tech.js +268 -0
- package/src/commands/tenant.js +576 -0
- package/src/commands/trust.js +366 -0
- package/src/harness/mcp-client.js +330 -54
- package/src/index.js +114 -0
- package/src/lib/activitypub-bridge.js +623 -0
- package/src/lib/aiops.js +523 -0
- package/src/lib/autonomous-developer.js +524 -0
- package/src/lib/code-agent.js +442 -0
- package/src/lib/collaboration-governance.js +556 -0
- package/src/lib/community-governance.js +649 -0
- package/src/lib/compliance-framework-reporter.js +600 -0
- package/src/lib/content-recommendation.js +600 -0
- package/src/lib/cross-chain.js +669 -0
- package/src/lib/dbevo.js +669 -0
- package/src/lib/decentral-infra.js +445 -0
- package/src/lib/federation-hardening.js +587 -0
- package/src/lib/hardening-manager.js +409 -0
- package/src/lib/inference-network.js +407 -0
- package/src/lib/knowledge-graph.js +530 -0
- package/src/lib/matrix-bridge.js +252 -0
- package/src/lib/mcp-client.js +3 -0
- package/src/lib/mcp-registry.js +347 -0
- package/src/lib/mcp-scaffold.js +385 -0
- package/src/lib/multimodal.js +698 -0
- package/src/lib/nl-programming.js +595 -0
- package/src/lib/nostr-bridge.js +214 -38
- package/src/lib/perception.js +500 -0
- package/src/lib/pqc-manager.js +141 -9
- package/src/lib/privacy-computing.js +575 -0
- package/src/lib/protocol-fusion.js +535 -0
- package/src/lib/quantization.js +362 -0
- package/src/lib/reputation-optimizer.js +509 -0
- package/src/lib/skill-marketplace.js +397 -0
- package/src/lib/sla-manager.js +484 -0
- package/src/lib/social-graph.js +408 -0
- package/src/lib/stix-parser.js +167 -0
- package/src/lib/stress-tester.js +383 -0
- package/src/lib/tech-learning-engine.js +651 -0
- package/src/lib/tenant-saas.js +831 -0
- package/src/lib/threat-intel.js +268 -0
- package/src/lib/token-incentive.js +513 -0
- package/src/lib/topic-classifier.js +400 -0
- package/src/lib/trust-security.js +473 -0
- package/src/lib/ueba.js +403 -0
- package/src/lib/universal-runtime.js +771 -0
- package/src/assets/web-panel/assets/Dashboard-CKeMmCoT.css +0 -1
|
@@ -0,0 +1,595 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Natural Language Programming — CLI port of Phase 28
|
|
3
|
+
* (docs/design/modules/28_自然语言编程.md).
|
|
4
|
+
*
|
|
5
|
+
* Desktop uses SpecTranslator (9-step pipeline with LLM enhancement),
|
|
6
|
+
* RequirementParser (intent classification + entity extraction),
|
|
7
|
+
* and ProjectStyleAnalyzer (6-category code style learning).
|
|
8
|
+
* CLI port ships:
|
|
9
|
+
*
|
|
10
|
+
* - Translation recording with intent classification and spec
|
|
11
|
+
* - Heuristic intent classification (keyword-based)
|
|
12
|
+
* - Heuristic entity extraction (noun phrases)
|
|
13
|
+
* - Convention/style recording and retrieval
|
|
14
|
+
* - Completeness scoring and ambiguity tracking
|
|
15
|
+
*
|
|
16
|
+
* What does NOT port: LLM-driven translation, real AST analysis,
|
|
17
|
+
* KG context integration, instinct memory patterns, live project
|
|
18
|
+
* style analysis, spec refinement with feedback loops.
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import crypto from "crypto";
|
|
22
|
+
|
|
23
|
+
/* ── Constants ──────────────────────────────────────────── */
|
|
24
|
+
|
|
25
|
+
export const INTENT = Object.freeze({
|
|
26
|
+
CREATE_COMPONENT: "create_component",
|
|
27
|
+
ADD_FEATURE: "add_feature",
|
|
28
|
+
FIX_BUG: "fix_bug",
|
|
29
|
+
REFACTOR: "refactor",
|
|
30
|
+
ADD_API: "add_api",
|
|
31
|
+
ADD_TEST: "add_test",
|
|
32
|
+
UPDATE_STYLE: "update_style",
|
|
33
|
+
CONFIGURE: "configure",
|
|
34
|
+
GENERAL: "general",
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
export const TRANSLATION_STATUS = Object.freeze({
|
|
38
|
+
DRAFT: "draft",
|
|
39
|
+
COMPLETE: "complete",
|
|
40
|
+
REFINED: "refined",
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
export const STYLE_CATEGORY = Object.freeze({
|
|
44
|
+
NAMING: "naming",
|
|
45
|
+
ARCHITECTURE: "architecture",
|
|
46
|
+
TESTING: "testing",
|
|
47
|
+
STYLE: "style",
|
|
48
|
+
IMPORTS: "imports",
|
|
49
|
+
COMPONENTS: "components",
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
/* ── State ──────────────────────────────────────────────── */
|
|
53
|
+
|
|
54
|
+
let _translations = new Map();
|
|
55
|
+
let _conventions = new Map();
|
|
56
|
+
|
|
57
|
+
function _id() {
|
|
58
|
+
return crypto.randomUUID();
|
|
59
|
+
}
|
|
60
|
+
function _now() {
|
|
61
|
+
return Date.now();
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
function _strip(row) {
|
|
65
|
+
if (!row) return null;
|
|
66
|
+
const out = {};
|
|
67
|
+
for (const [k, v] of Object.entries(row)) {
|
|
68
|
+
if (k !== "_rowid_" && k !== "rowid") out[k] = v;
|
|
69
|
+
}
|
|
70
|
+
return out;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/* ── Schema ─────────────────────────────────────────────── */
|
|
74
|
+
|
|
75
|
+
export function ensureNlProgrammingTables(db) {
|
|
76
|
+
db.exec(`CREATE TABLE IF NOT EXISTS nl_programs (
|
|
77
|
+
id TEXT PRIMARY KEY,
|
|
78
|
+
input_text TEXT NOT NULL,
|
|
79
|
+
intent TEXT,
|
|
80
|
+
entities TEXT,
|
|
81
|
+
tech_stack TEXT,
|
|
82
|
+
spec TEXT,
|
|
83
|
+
completeness_score REAL DEFAULT 0.0,
|
|
84
|
+
ambiguities TEXT,
|
|
85
|
+
status TEXT DEFAULT 'draft',
|
|
86
|
+
created_at INTEGER NOT NULL,
|
|
87
|
+
updated_at INTEGER NOT NULL
|
|
88
|
+
)`);
|
|
89
|
+
|
|
90
|
+
db.exec(`CREATE TABLE IF NOT EXISTS nl_program_conventions (
|
|
91
|
+
id TEXT PRIMARY KEY,
|
|
92
|
+
category TEXT NOT NULL,
|
|
93
|
+
pattern TEXT NOT NULL,
|
|
94
|
+
examples TEXT,
|
|
95
|
+
confidence REAL DEFAULT 0.5,
|
|
96
|
+
source_files TEXT,
|
|
97
|
+
created_at INTEGER NOT NULL,
|
|
98
|
+
updated_at INTEGER NOT NULL
|
|
99
|
+
)`);
|
|
100
|
+
|
|
101
|
+
_loadAll(db);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
function _loadAll(db) {
|
|
105
|
+
_translations.clear();
|
|
106
|
+
_conventions.clear();
|
|
107
|
+
|
|
108
|
+
const tables = [
|
|
109
|
+
["nl_programs", _translations, "id"],
|
|
110
|
+
["nl_program_conventions", _conventions, "id"],
|
|
111
|
+
];
|
|
112
|
+
for (const [table, map, key] of tables) {
|
|
113
|
+
try {
|
|
114
|
+
for (const row of db.prepare(`SELECT * FROM ${table}`).all()) {
|
|
115
|
+
const r = _strip(row);
|
|
116
|
+
map.set(r[key], r);
|
|
117
|
+
}
|
|
118
|
+
} catch (_e) {
|
|
119
|
+
/* table may not exist */
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
/* ── Intent Classification (heuristic) ──────────────────── */
|
|
125
|
+
|
|
126
|
+
const INTENT_KEYWORDS = {
|
|
127
|
+
create_component: [
|
|
128
|
+
"create",
|
|
129
|
+
"new",
|
|
130
|
+
"build",
|
|
131
|
+
"make",
|
|
132
|
+
"scaffold",
|
|
133
|
+
"generate",
|
|
134
|
+
"创建",
|
|
135
|
+
"新建",
|
|
136
|
+
"生成",
|
|
137
|
+
],
|
|
138
|
+
add_feature: [
|
|
139
|
+
"add",
|
|
140
|
+
"implement",
|
|
141
|
+
"include",
|
|
142
|
+
"extend",
|
|
143
|
+
"添加",
|
|
144
|
+
"实现",
|
|
145
|
+
"增加",
|
|
146
|
+
"给",
|
|
147
|
+
],
|
|
148
|
+
fix_bug: [
|
|
149
|
+
"fix",
|
|
150
|
+
"repair",
|
|
151
|
+
"resolve",
|
|
152
|
+
"debug",
|
|
153
|
+
"patch",
|
|
154
|
+
"修复",
|
|
155
|
+
"修改",
|
|
156
|
+
"解决",
|
|
157
|
+
"bug",
|
|
158
|
+
],
|
|
159
|
+
refactor: [
|
|
160
|
+
"refactor",
|
|
161
|
+
"restructure",
|
|
162
|
+
"reorganize",
|
|
163
|
+
"clean",
|
|
164
|
+
"重构",
|
|
165
|
+
"整理",
|
|
166
|
+
"优化",
|
|
167
|
+
],
|
|
168
|
+
add_api: ["api", "endpoint", "route", "接口", "端点"],
|
|
169
|
+
add_test: ["test", "spec", "测试", "单元测试"],
|
|
170
|
+
update_style: [
|
|
171
|
+
"style",
|
|
172
|
+
"css",
|
|
173
|
+
"theme",
|
|
174
|
+
"color",
|
|
175
|
+
"样式",
|
|
176
|
+
"主题",
|
|
177
|
+
"颜色",
|
|
178
|
+
"UI",
|
|
179
|
+
],
|
|
180
|
+
configure: ["config", "setting", "setup", "配置", "设置", "安装"],
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
export function classifyIntent(text) {
|
|
184
|
+
if (!text) return { intent: "general", confidence: 0 };
|
|
185
|
+
|
|
186
|
+
const lower = text.toLowerCase();
|
|
187
|
+
let bestIntent = "general";
|
|
188
|
+
let bestScore = 0;
|
|
189
|
+
|
|
190
|
+
for (const [intent, keywords] of Object.entries(INTENT_KEYWORDS)) {
|
|
191
|
+
let score = 0;
|
|
192
|
+
for (const kw of keywords) {
|
|
193
|
+
if (lower.includes(kw)) score += 1;
|
|
194
|
+
}
|
|
195
|
+
if (score > bestScore) {
|
|
196
|
+
bestScore = score;
|
|
197
|
+
bestIntent = intent;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
const confidence = bestScore > 0 ? Math.min(1, bestScore * 0.3) : 0;
|
|
202
|
+
return {
|
|
203
|
+
intent: bestIntent,
|
|
204
|
+
confidence: Math.round(confidence * 1000) / 1000,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/* ── Entity Extraction (heuristic) ──────────────────────── */
|
|
209
|
+
|
|
210
|
+
export function extractEntities(text) {
|
|
211
|
+
if (!text) return { entities: [], count: 0 };
|
|
212
|
+
|
|
213
|
+
// Simple heuristic: extract quoted strings and capitalized phrases
|
|
214
|
+
const entities = [];
|
|
215
|
+
|
|
216
|
+
// Quoted strings
|
|
217
|
+
const quotedMatches = text.match(/["'`]([^"'`]+)["'`]/g);
|
|
218
|
+
if (quotedMatches) {
|
|
219
|
+
for (const m of quotedMatches) {
|
|
220
|
+
entities.push({ type: "quoted", value: m.slice(1, -1) });
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Chinese noun phrases (after keywords like 给/的/个)
|
|
225
|
+
const cnMatches = text.match(/(?:给|的|个|一个)([^\s,,。、]+)/g);
|
|
226
|
+
if (cnMatches) {
|
|
227
|
+
for (const m of cnMatches) {
|
|
228
|
+
const value = m.replace(/^(?:给|的|个|一个)/, "").trim();
|
|
229
|
+
if (value.length > 1) entities.push({ type: "noun_phrase", value });
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
// Technical terms (PascalCase, kebab-case patterns)
|
|
234
|
+
const techMatches = text.match(/\b[A-Z][a-zA-Z]+(?:[A-Z][a-z]+)+\b/g);
|
|
235
|
+
if (techMatches) {
|
|
236
|
+
for (const m of techMatches) {
|
|
237
|
+
entities.push({ type: "technical", value: m });
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// Deduplicate
|
|
242
|
+
const seen = new Set();
|
|
243
|
+
const unique = entities.filter((e) => {
|
|
244
|
+
const key = `${e.type}:${e.value}`;
|
|
245
|
+
if (seen.has(key)) return false;
|
|
246
|
+
seen.add(key);
|
|
247
|
+
return true;
|
|
248
|
+
});
|
|
249
|
+
|
|
250
|
+
return { entities: unique, count: unique.length };
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/* ── Tech Stack Detection (heuristic) ───────────────────── */
|
|
254
|
+
|
|
255
|
+
const TECH_PATTERNS = {
|
|
256
|
+
vue: ["vue", "Vue", "组件", "component", "composable"],
|
|
257
|
+
react: ["react", "React", "jsx", "tsx", "hook"],
|
|
258
|
+
typescript: ["typescript", "TypeScript", "ts", "类型"],
|
|
259
|
+
javascript: ["javascript", "js", "JavaScript"],
|
|
260
|
+
python: ["python", "Python", "pip", "django", "flask", "fastapi"],
|
|
261
|
+
java: ["java", "Java", "spring", "Spring"],
|
|
262
|
+
go: ["golang", "go", "Go"],
|
|
263
|
+
rust: ["rust", "Rust", "cargo"],
|
|
264
|
+
};
|
|
265
|
+
|
|
266
|
+
export function detectTechStack(text) {
|
|
267
|
+
if (!text) return { detected: [], primary: null };
|
|
268
|
+
|
|
269
|
+
const lower = text.toLowerCase();
|
|
270
|
+
const detected = [];
|
|
271
|
+
|
|
272
|
+
for (const [tech, patterns] of Object.entries(TECH_PATTERNS)) {
|
|
273
|
+
for (const p of patterns) {
|
|
274
|
+
if (lower.includes(p.toLowerCase())) {
|
|
275
|
+
detected.push(tech);
|
|
276
|
+
break;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
return { detected: [...new Set(detected)], primary: detected[0] || null };
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/* ── Completeness Scoring ───────────────────────────────── */
|
|
285
|
+
|
|
286
|
+
export function scoreCompleteness(spec) {
|
|
287
|
+
if (!spec) return { score: 0, missing: ["spec"] };
|
|
288
|
+
|
|
289
|
+
let score = 0;
|
|
290
|
+
const missing = [];
|
|
291
|
+
const total = 6;
|
|
292
|
+
|
|
293
|
+
if (spec.intent && spec.intent !== "general") score += 1;
|
|
294
|
+
else missing.push("intent");
|
|
295
|
+
|
|
296
|
+
if (spec.entities && spec.entities.length > 0) score += 1;
|
|
297
|
+
else missing.push("entities");
|
|
298
|
+
|
|
299
|
+
if (spec.techStack && spec.techStack.length > 0) score += 1;
|
|
300
|
+
else missing.push("tech_stack");
|
|
301
|
+
|
|
302
|
+
if (spec.inputText && spec.inputText.length > 10) score += 1;
|
|
303
|
+
else missing.push("detailed_input");
|
|
304
|
+
|
|
305
|
+
if (!spec.ambiguities || spec.ambiguities.length === 0) score += 1;
|
|
306
|
+
else missing.push("no_ambiguities");
|
|
307
|
+
|
|
308
|
+
if (spec.conventions && spec.conventions.length > 0) score += 1;
|
|
309
|
+
else missing.push("conventions");
|
|
310
|
+
|
|
311
|
+
return {
|
|
312
|
+
score: Math.round((score / total) * 1000) / 1000,
|
|
313
|
+
missing,
|
|
314
|
+
fulfilled: total - missing.length,
|
|
315
|
+
total,
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
/* ── Translation CRUD ───────────────────────────────────── */
|
|
320
|
+
|
|
321
|
+
const VALID_STATUSES = new Set(Object.values(TRANSLATION_STATUS));
|
|
322
|
+
|
|
323
|
+
export function translate(
|
|
324
|
+
db,
|
|
325
|
+
{ text, intent, entities, techStack, spec, ambiguities } = {},
|
|
326
|
+
) {
|
|
327
|
+
if (!text) return { translated: false, reason: "missing_text" };
|
|
328
|
+
|
|
329
|
+
const id = _id();
|
|
330
|
+
const now = _now();
|
|
331
|
+
|
|
332
|
+
// Auto-classify if not provided
|
|
333
|
+
const classified = intent || classifyIntent(text).intent;
|
|
334
|
+
const extracted = entities || JSON.stringify(extractEntities(text).entities);
|
|
335
|
+
const stack = techStack || JSON.stringify(detectTechStack(text).detected);
|
|
336
|
+
const entitiesJson =
|
|
337
|
+
typeof extracted === "string" ? extracted : JSON.stringify(extracted);
|
|
338
|
+
const stackJson = typeof stack === "string" ? stack : JSON.stringify(stack);
|
|
339
|
+
const specJson = spec
|
|
340
|
+
? typeof spec === "string"
|
|
341
|
+
? spec
|
|
342
|
+
: JSON.stringify(spec)
|
|
343
|
+
: null;
|
|
344
|
+
const ambigJson = ambiguities
|
|
345
|
+
? typeof ambiguities === "string"
|
|
346
|
+
? ambiguities
|
|
347
|
+
: JSON.stringify(ambiguities)
|
|
348
|
+
: null;
|
|
349
|
+
|
|
350
|
+
// Score completeness
|
|
351
|
+
const completeness = scoreCompleteness({
|
|
352
|
+
intent: classified,
|
|
353
|
+
entities: JSON.parse(entitiesJson),
|
|
354
|
+
techStack: JSON.parse(stackJson),
|
|
355
|
+
inputText: text,
|
|
356
|
+
ambiguities: ambigJson ? JSON.parse(ambigJson) : [],
|
|
357
|
+
});
|
|
358
|
+
|
|
359
|
+
const entry = {
|
|
360
|
+
id,
|
|
361
|
+
input_text: text,
|
|
362
|
+
intent: classified,
|
|
363
|
+
entities: entitiesJson,
|
|
364
|
+
tech_stack: stackJson,
|
|
365
|
+
spec: specJson,
|
|
366
|
+
completeness_score: completeness.score,
|
|
367
|
+
ambiguities: ambigJson,
|
|
368
|
+
status: "draft",
|
|
369
|
+
created_at: now,
|
|
370
|
+
updated_at: now,
|
|
371
|
+
};
|
|
372
|
+
|
|
373
|
+
db.prepare(
|
|
374
|
+
`INSERT INTO nl_programs (id, input_text, intent, entities, tech_stack, spec, completeness_score, ambiguities, status, created_at, updated_at)
|
|
375
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
376
|
+
).run(
|
|
377
|
+
id,
|
|
378
|
+
text,
|
|
379
|
+
entry.intent,
|
|
380
|
+
entry.entities,
|
|
381
|
+
entry.tech_stack,
|
|
382
|
+
entry.spec,
|
|
383
|
+
entry.completeness_score,
|
|
384
|
+
entry.ambiguities,
|
|
385
|
+
"draft",
|
|
386
|
+
now,
|
|
387
|
+
now,
|
|
388
|
+
);
|
|
389
|
+
|
|
390
|
+
_translations.set(id, entry);
|
|
391
|
+
return {
|
|
392
|
+
translated: true,
|
|
393
|
+
translationId: id,
|
|
394
|
+
intent: classified,
|
|
395
|
+
completeness: completeness.score,
|
|
396
|
+
};
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
export function getTranslation(db, id) {
|
|
400
|
+
const t = _translations.get(id);
|
|
401
|
+
return t ? { ...t } : null;
|
|
402
|
+
}
|
|
403
|
+
|
|
404
|
+
export function listTranslations(db, { intent, status, limit = 50 } = {}) {
|
|
405
|
+
let results = [..._translations.values()];
|
|
406
|
+
if (intent) results = results.filter((t) => t.intent === intent);
|
|
407
|
+
if (status) results = results.filter((t) => t.status === status);
|
|
408
|
+
return results
|
|
409
|
+
.sort((a, b) => b.created_at - a.created_at)
|
|
410
|
+
.slice(0, limit)
|
|
411
|
+
.map((t) => ({ ...t }));
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
export function updateTranslationStatus(db, id, status) {
|
|
415
|
+
const t = _translations.get(id);
|
|
416
|
+
if (!t) return { updated: false, reason: "not_found" };
|
|
417
|
+
if (!VALID_STATUSES.has(status))
|
|
418
|
+
return { updated: false, reason: "invalid_status" };
|
|
419
|
+
|
|
420
|
+
const now = _now();
|
|
421
|
+
t.status = status;
|
|
422
|
+
t.updated_at = now;
|
|
423
|
+
|
|
424
|
+
db.prepare(
|
|
425
|
+
"UPDATE nl_programs SET status = ?, updated_at = ? WHERE id = ?",
|
|
426
|
+
).run(status, now, id);
|
|
427
|
+
|
|
428
|
+
return { updated: true, status };
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export function refineTranslation(
|
|
432
|
+
db,
|
|
433
|
+
id,
|
|
434
|
+
{ spec, ambiguities, feedback } = {},
|
|
435
|
+
) {
|
|
436
|
+
const t = _translations.get(id);
|
|
437
|
+
if (!t) return { refined: false, reason: "not_found" };
|
|
438
|
+
|
|
439
|
+
const now = _now();
|
|
440
|
+
if (spec) t.spec = typeof spec === "string" ? spec : JSON.stringify(spec);
|
|
441
|
+
if (ambiguities)
|
|
442
|
+
t.ambiguities =
|
|
443
|
+
typeof ambiguities === "string"
|
|
444
|
+
? ambiguities
|
|
445
|
+
: JSON.stringify(ambiguities);
|
|
446
|
+
t.status = "refined";
|
|
447
|
+
t.updated_at = now;
|
|
448
|
+
|
|
449
|
+
// Rescore completeness
|
|
450
|
+
const completeness = scoreCompleteness({
|
|
451
|
+
intent: t.intent,
|
|
452
|
+
entities: JSON.parse(t.entities || "[]"),
|
|
453
|
+
techStack: JSON.parse(t.tech_stack || "[]"),
|
|
454
|
+
inputText: t.input_text,
|
|
455
|
+
ambiguities: t.ambiguities ? JSON.parse(t.ambiguities) : [],
|
|
456
|
+
});
|
|
457
|
+
t.completeness_score = completeness.score;
|
|
458
|
+
|
|
459
|
+
db.prepare(
|
|
460
|
+
"UPDATE nl_programs SET spec = ?, ambiguities = ?, completeness_score = ?, status = 'refined', updated_at = ? WHERE id = ?",
|
|
461
|
+
).run(t.spec, t.ambiguities, t.completeness_score, now, id);
|
|
462
|
+
|
|
463
|
+
return { refined: true, completeness: completeness.score };
|
|
464
|
+
}
|
|
465
|
+
|
|
466
|
+
export function removeTranslation(db, id) {
|
|
467
|
+
const t = _translations.get(id);
|
|
468
|
+
if (!t) return { removed: false, reason: "not_found" };
|
|
469
|
+
|
|
470
|
+
_translations.delete(id);
|
|
471
|
+
db.prepare("DELETE FROM nl_programs WHERE id = ?").run(id);
|
|
472
|
+
|
|
473
|
+
return { removed: true };
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
/* ── Conventions ────────────────────────────────────────── */
|
|
477
|
+
|
|
478
|
+
const VALID_CATEGORIES = new Set(Object.values(STYLE_CATEGORY));
|
|
479
|
+
|
|
480
|
+
export function addConvention(
|
|
481
|
+
db,
|
|
482
|
+
{ category, pattern, examples, confidence, sourceFiles } = {},
|
|
483
|
+
) {
|
|
484
|
+
if (!category || !VALID_CATEGORIES.has(category))
|
|
485
|
+
return { added: false, reason: "invalid_category" };
|
|
486
|
+
if (!pattern) return { added: false, reason: "missing_pattern" };
|
|
487
|
+
|
|
488
|
+
const id = _id();
|
|
489
|
+
const now = _now();
|
|
490
|
+
const examplesJson = examples
|
|
491
|
+
? typeof examples === "string"
|
|
492
|
+
? examples
|
|
493
|
+
: JSON.stringify(examples)
|
|
494
|
+
: null;
|
|
495
|
+
const sourceJson = sourceFiles
|
|
496
|
+
? typeof sourceFiles === "string"
|
|
497
|
+
? sourceFiles
|
|
498
|
+
: JSON.stringify(sourceFiles)
|
|
499
|
+
: null;
|
|
500
|
+
const conf = confidence != null ? Math.max(0, Math.min(1, confidence)) : 0.5;
|
|
501
|
+
|
|
502
|
+
const entry = {
|
|
503
|
+
id,
|
|
504
|
+
category,
|
|
505
|
+
pattern,
|
|
506
|
+
examples: examplesJson,
|
|
507
|
+
confidence: conf,
|
|
508
|
+
source_files: sourceJson,
|
|
509
|
+
created_at: now,
|
|
510
|
+
updated_at: now,
|
|
511
|
+
};
|
|
512
|
+
|
|
513
|
+
db.prepare(
|
|
514
|
+
`INSERT INTO nl_program_conventions (id, category, pattern, examples, confidence, source_files, created_at, updated_at)
|
|
515
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
516
|
+
).run(id, category, pattern, examplesJson, conf, sourceJson, now, now);
|
|
517
|
+
|
|
518
|
+
_conventions.set(id, entry);
|
|
519
|
+
return { added: true, conventionId: id };
|
|
520
|
+
}
|
|
521
|
+
|
|
522
|
+
export function getConvention(db, id) {
|
|
523
|
+
const c = _conventions.get(id);
|
|
524
|
+
return c ? { ...c } : null;
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
export function listConventions(db, { category, limit = 50 } = {}) {
|
|
528
|
+
let results = [..._conventions.values()];
|
|
529
|
+
if (category) results = results.filter((c) => c.category === category);
|
|
530
|
+
return results
|
|
531
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
532
|
+
.slice(0, limit)
|
|
533
|
+
.map((c) => ({ ...c }));
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
export function removeConvention(db, id) {
|
|
537
|
+
const c = _conventions.get(id);
|
|
538
|
+
if (!c) return { removed: false, reason: "not_found" };
|
|
539
|
+
|
|
540
|
+
_conventions.delete(id);
|
|
541
|
+
db.prepare("DELETE FROM nl_program_conventions WHERE id = ?").run(id);
|
|
542
|
+
|
|
543
|
+
return { removed: true };
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
/* ── Stats ──────────────────────────────────────────────── */
|
|
547
|
+
|
|
548
|
+
export function getNlProgrammingStats(db) {
|
|
549
|
+
const translations = [..._translations.values()];
|
|
550
|
+
const conventions = [..._conventions.values()];
|
|
551
|
+
|
|
552
|
+
const byIntent = {};
|
|
553
|
+
for (const i of Object.values(INTENT)) byIntent[i] = 0;
|
|
554
|
+
for (const t of translations)
|
|
555
|
+
byIntent[t.intent] = (byIntent[t.intent] || 0) + 1;
|
|
556
|
+
|
|
557
|
+
const byStatus = {};
|
|
558
|
+
for (const s of Object.values(TRANSLATION_STATUS)) byStatus[s] = 0;
|
|
559
|
+
for (const t of translations)
|
|
560
|
+
byStatus[t.status] = (byStatus[t.status] || 0) + 1;
|
|
561
|
+
|
|
562
|
+
const byCategory = {};
|
|
563
|
+
for (const c of Object.values(STYLE_CATEGORY)) byCategory[c] = 0;
|
|
564
|
+
for (const c of conventions)
|
|
565
|
+
byCategory[c.category] = (byCategory[c.category] || 0) + 1;
|
|
566
|
+
|
|
567
|
+
const avgCompleteness =
|
|
568
|
+
translations.length > 0
|
|
569
|
+
? Math.round(
|
|
570
|
+
(translations.reduce((s, t) => s + t.completeness_score, 0) /
|
|
571
|
+
translations.length) *
|
|
572
|
+
1000,
|
|
573
|
+
) / 1000
|
|
574
|
+
: 0;
|
|
575
|
+
|
|
576
|
+
return {
|
|
577
|
+
translations: {
|
|
578
|
+
total: translations.length,
|
|
579
|
+
byIntent,
|
|
580
|
+
byStatus,
|
|
581
|
+
avgCompleteness,
|
|
582
|
+
},
|
|
583
|
+
conventions: {
|
|
584
|
+
total: conventions.length,
|
|
585
|
+
byCategory,
|
|
586
|
+
},
|
|
587
|
+
};
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
/* ── Reset (tests) ──────────────────────────────────────── */
|
|
591
|
+
|
|
592
|
+
export function _resetState() {
|
|
593
|
+
_translations.clear();
|
|
594
|
+
_conventions.clear();
|
|
595
|
+
}
|