codemini-cli 0.5.10 → 0.5.11
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/OPERATIONS.md +242 -242
- package/README.md +588 -588
- package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-7HL7yft8.js → highlighted-body-OFNGDK62-CANOG7Xg.js} +1 -1
- package/codemini-web/dist/assets/{index-BK75hMb2.js → index-B71xykPM.js} +108 -108
- package/codemini-web/dist/assets/index-Dkq1DdDX.css +2 -0
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-Z_w7M93P.js +1 -0
- package/codemini-web/dist/index.html +23 -23
- package/codemini-web/lib/approval-manager.js +32 -32
- package/codemini-web/lib/runtime-bridge.js +17 -11
- package/codemini-web/server.js +534 -205
- package/deployment.md +212 -212
- package/package.json +1 -1
- package/skills/brainstorm/SKILL.md +77 -77
- package/skills/codemini.skills.json +40 -40
- package/skills/grill-me/SKILL.md +30 -30
- package/skills/superpowers-lite/SKILL.md +82 -82
- package/src/cli.js +74 -74
- package/src/commands/chat.js +210 -210
- package/src/commands/run.js +313 -313
- package/src/commands/skill.js +438 -304
- package/src/commands/web.js +57 -57
- package/src/core/agent-loop.js +980 -980
- package/src/core/ast.js +309 -307
- package/src/core/chat-runtime.js +6261 -6253
- package/src/core/command-evaluator.js +72 -72
- package/src/core/command-loader.js +311 -311
- package/src/core/command-policy.js +301 -301
- package/src/core/command-risk.js +156 -156
- package/src/core/config-store.js +289 -289
- package/src/core/constants.js +18 -1
- package/src/core/context-compact.js +365 -365
- package/src/core/default-system-prompt.js +114 -107
- package/src/core/dream-audit.js +105 -105
- package/src/core/dream-consolidate.js +229 -229
- package/src/core/dream-evaluator.js +185 -185
- package/src/core/fff-adapter.js +383 -383
- package/src/core/memory-store.js +543 -543
- package/src/core/project-index.js +737 -548
- package/src/core/project-instructions.js +98 -98
- package/src/core/provider/anthropic.js +514 -514
- package/src/core/provider/openai-compatible.js +501 -501
- package/src/core/reflect-skill.js +178 -178
- package/src/core/reply-language.js +40 -40
- package/src/core/session-store.js +474 -474
- package/src/core/shell-profile.js +237 -237
- package/src/core/shell.js +323 -323
- package/src/core/soul.js +69 -69
- package/src/core/system-prompt-composer.js +52 -52
- package/src/core/tool-args.js +199 -154
- package/src/core/tool-output.js +184 -184
- package/src/core/tool-result-store.js +206 -206
- package/src/core/tools.js +3024 -2893
- package/src/core/version.js +11 -11
- package/src/tui/chat-app.js +5171 -5171
- package/src/tui/tool-activity/presenters/misc.js +30 -30
- package/src/tui/tool-activity/presenters/system.js +20 -20
- package/templates/project-requirements/report-shell.html +582 -582
- package/codemini-web/dist/assets/index-BSdIdn3L.css +0 -2
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-Dg9qh8mg.js +0 -1
|
@@ -1,178 +1,178 @@
|
|
|
1
|
-
import fs from 'node:fs/promises';
|
|
2
|
-
import path from 'node:path';
|
|
3
|
-
import { getProjectSkillsDir, getSkillsDir } from './paths.js';
|
|
4
|
-
import { createChatCompletion } from './provider/index.js';
|
|
5
|
-
|
|
6
|
-
const REFLECT_TIMEOUT_MS = 45000;
|
|
7
|
-
|
|
8
|
-
function slugifySkillName(value) {
|
|
9
|
-
const slug = String(value || '')
|
|
10
|
-
.trim()
|
|
11
|
-
.toLowerCase()
|
|
12
|
-
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
|
|
13
|
-
.replace(/^-+|-+$/g, '');
|
|
14
|
-
return slug || 'reflected-success-workflow';
|
|
15
|
-
}
|
|
16
|
-
|
|
17
|
-
function escapeFrontmatter(value) {
|
|
18
|
-
return String(value || '').replace(/\r?\n/g, ' ').replace(/"/g, '\\"').trim();
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function hasFrontmatter(content) {
|
|
22
|
-
return /^---\r?\n[\s\S]*?\r?\n---\r?\n/.test(String(content || '').trimStart());
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function renderSkillContent({ name, description, content }) {
|
|
26
|
-
const body = String(content || '').trim() || [
|
|
27
|
-
'## Workflow',
|
|
28
|
-
'',
|
|
29
|
-
'1. Recreate the successful chain from the recent task.',
|
|
30
|
-
'2. Preserve the key decision that made it work.',
|
|
31
|
-
'3. Verify with the narrowest relevant check.',
|
|
32
|
-
'',
|
|
33
|
-
'## Boundaries',
|
|
34
|
-
'',
|
|
35
|
-
'Use this only when the current task matches the preserved workflow.'
|
|
36
|
-
].join('\n');
|
|
37
|
-
if (hasFrontmatter(body)) return `${body.trim()}\n`;
|
|
38
|
-
return [
|
|
39
|
-
'---',
|
|
40
|
-
`name: ${name}`,
|
|
41
|
-
`description: ${escapeFrontmatter(description) || `Use when this reflected workflow applies.`}`,
|
|
42
|
-
'---',
|
|
43
|
-
'',
|
|
44
|
-
body
|
|
45
|
-
].join('\n').trimEnd() + '\n';
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
export function normalizeReflectDraft(raw = {}) {
|
|
49
|
-
const name = slugifySkillName(raw.name || raw.skillName || raw.title);
|
|
50
|
-
const description = String(raw.description || raw.summary || `Use when the ${name} workflow applies.`).trim();
|
|
51
|
-
const confidence = Math.min(1, Math.max(0, Number(raw.confidence ?? 0.75)));
|
|
52
|
-
return {
|
|
53
|
-
id: Number(raw.id || 1),
|
|
54
|
-
name,
|
|
55
|
-
description,
|
|
56
|
-
confidence,
|
|
57
|
-
content: renderSkillContent({ name, description, content: raw.content || raw.markdown || raw.body })
|
|
58
|
-
};
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
export function buildReflectTargetPath({ scope = 'project', name, workspaceRoot = process.cwd() } = {}) {
|
|
62
|
-
const safeName = slugifySkillName(name);
|
|
63
|
-
const baseDir = String(scope || '').toLowerCase() === 'global'
|
|
64
|
-
? getSkillsDir()
|
|
65
|
-
: getProjectSkillsDir(workspaceRoot);
|
|
66
|
-
return path.join(baseDir, safeName, 'SKILL.md');
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
export function parseReflectScope(args = []) {
|
|
70
|
-
let scope = 'project';
|
|
71
|
-
const requestParts = [];
|
|
72
|
-
for (let index = 0; index < args.length; index += 1) {
|
|
73
|
-
const arg = String(args[index] || '');
|
|
74
|
-
if (arg === '--scope') {
|
|
75
|
-
const next = String(args[index + 1] || '').toLowerCase();
|
|
76
|
-
if (next === 'global' || next === 'project') {
|
|
77
|
-
scope = next;
|
|
78
|
-
index += 1;
|
|
79
|
-
}
|
|
80
|
-
continue;
|
|
81
|
-
}
|
|
82
|
-
if (arg.startsWith('--scope=')) {
|
|
83
|
-
const value = arg.slice('--scope='.length).toLowerCase();
|
|
84
|
-
if (value === 'global' || value === 'project') scope = value;
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
requestParts.push(arg);
|
|
88
|
-
}
|
|
89
|
-
return { scope, request: requestParts.join(' ').trim() };
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function parseModelDrafts(text) {
|
|
93
|
-
const raw = String(text || '').trim();
|
|
94
|
-
if (!raw) return [];
|
|
95
|
-
const unfenced = raw.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '').trim();
|
|
96
|
-
try {
|
|
97
|
-
const parsed = JSON.parse(unfenced);
|
|
98
|
-
if (Array.isArray(parsed?.candidates)) return parsed.candidates.map((item, index) => normalizeReflectDraft({ id: index + 1, ...item }));
|
|
99
|
-
if (Array.isArray(parsed)) return parsed.map((item, index) => normalizeReflectDraft({ id: index + 1, ...item }));
|
|
100
|
-
if (parsed && typeof parsed === 'object') return [normalizeReflectDraft(parsed)];
|
|
101
|
-
} catch {
|
|
102
|
-
// Fall back to wrapping plain markdown below.
|
|
103
|
-
}
|
|
104
|
-
return [normalizeReflectDraft({
|
|
105
|
-
name: 'reflected-success-workflow',
|
|
106
|
-
description: 'Use when the reflected successful workflow applies.',
|
|
107
|
-
content: raw
|
|
108
|
-
})];
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
function recentContext(session, limit = 10) {
|
|
112
|
-
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
113
|
-
return messages
|
|
114
|
-
.slice(-limit)
|
|
115
|
-
.map((message) => `${message.role}: ${String(message.content || '').slice(0, 1200)}`)
|
|
116
|
-
.join('\n\n');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
export async function buildReflectSkillDraft({
|
|
120
|
-
request = '',
|
|
121
|
-
scope = 'project',
|
|
122
|
-
session,
|
|
123
|
-
config = {},
|
|
124
|
-
model,
|
|
125
|
-
systemPrompt = '',
|
|
126
|
-
previousDraft = null,
|
|
127
|
-
feedback = ''
|
|
128
|
-
} = {}) {
|
|
129
|
-
const mode = String(request || '').trim() ? 'directed' : 'exploratory';
|
|
130
|
-
const prompt = [
|
|
131
|
-
'Create a reusable Codex/Codemini SKILL.md draft from a successful workflow.',
|
|
132
|
-
`Mode: ${mode}`,
|
|
133
|
-
`Target scope: ${scope}`,
|
|
134
|
-
request ? `User reflection request:\n${request}` : 'No explicit request was supplied. Be conservative and return no candidates if the recent context does not show a reusable success pattern.',
|
|
135
|
-
previousDraft ? `Existing draft to revise:\n${previousDraft.content || ''}` : '',
|
|
136
|
-
feedback ? `User edit feedback:\n${feedback}` : '',
|
|
137
|
-
'Recent session context:',
|
|
138
|
-
recentContext(session),
|
|
139
|
-
'Return valid JSON only, no markdown fences.',
|
|
140
|
-
'Shape: {"candidates":[{"name":"kebab-case-name","description":"when to use this skill","confidence":0.0,"content":"full SKILL.md body or markdown body"}]}',
|
|
141
|
-
'The content must include trigger conditions, workflow/toolchain, key decisions, pitfalls, verification, and boundaries.',
|
|
142
|
-
'Do not write memory or inbox content. This is only a skill draft.'
|
|
143
|
-
].filter(Boolean).join('\n\n');
|
|
144
|
-
|
|
145
|
-
const result = await createChatCompletion({
|
|
146
|
-
sdkProvider: config?.sdk?.provider,
|
|
147
|
-
baseUrl: config?.gateway?.base_url,
|
|
148
|
-
apiKey: config?.gateway?.api_key,
|
|
149
|
-
model: model || config?.model?.name,
|
|
150
|
-
messages: [
|
|
151
|
-
{ role: 'system', content: systemPrompt || 'You draft concise, reusable coding workflow skills.' },
|
|
152
|
-
{ role: 'user', content: prompt }
|
|
153
|
-
],
|
|
154
|
-
temperature: 0,
|
|
155
|
-
timeoutMs: REFLECT_TIMEOUT_MS
|
|
156
|
-
});
|
|
157
|
-
|
|
158
|
-
return parseModelDrafts(result?.text || '');
|
|
159
|
-
}
|
|
160
|
-
|
|
161
|
-
export function attachReflectTargets({ candidates = [], scope = 'project', workspaceRoot = process.cwd() } = {}) {
|
|
162
|
-
return candidates.map((candidate, index) => {
|
|
163
|
-
const draft = normalizeReflectDraft({ id: index + 1, ...candidate });
|
|
164
|
-
return {
|
|
165
|
-
...draft,
|
|
166
|
-
targetPath: buildReflectTargetPath({ scope, name: draft.name, workspaceRoot })
|
|
167
|
-
};
|
|
168
|
-
});
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
export async function writeReflectSkillDraft({ draft, scope = 'project', workspaceRoot = process.cwd() } = {}) {
|
|
172
|
-
const normalized = normalizeReflectDraft(draft);
|
|
173
|
-
const filePath = buildReflectTargetPath({ scope, name: normalized.name, workspaceRoot });
|
|
174
|
-
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
175
|
-
await fs.writeFile(filePath, normalized.content, 'utf8');
|
|
176
|
-
return { filePath, draft: normalized };
|
|
177
|
-
}
|
|
178
|
-
|
|
1
|
+
import fs from 'node:fs/promises';
|
|
2
|
+
import path from 'node:path';
|
|
3
|
+
import { getProjectSkillsDir, getSkillsDir } from './paths.js';
|
|
4
|
+
import { createChatCompletion } from './provider/index.js';
|
|
5
|
+
|
|
6
|
+
const REFLECT_TIMEOUT_MS = 45000;
|
|
7
|
+
|
|
8
|
+
function slugifySkillName(value) {
|
|
9
|
+
const slug = String(value || '')
|
|
10
|
+
.trim()
|
|
11
|
+
.toLowerCase()
|
|
12
|
+
.replace(/[^a-z0-9\u4e00-\u9fa5]+/g, '-')
|
|
13
|
+
.replace(/^-+|-+$/g, '');
|
|
14
|
+
return slug || 'reflected-success-workflow';
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function escapeFrontmatter(value) {
|
|
18
|
+
return String(value || '').replace(/\r?\n/g, ' ').replace(/"/g, '\\"').trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function hasFrontmatter(content) {
|
|
22
|
+
return /^---\r?\n[\s\S]*?\r?\n---\r?\n/.test(String(content || '').trimStart());
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function renderSkillContent({ name, description, content }) {
|
|
26
|
+
const body = String(content || '').trim() || [
|
|
27
|
+
'## Workflow',
|
|
28
|
+
'',
|
|
29
|
+
'1. Recreate the successful chain from the recent task.',
|
|
30
|
+
'2. Preserve the key decision that made it work.',
|
|
31
|
+
'3. Verify with the narrowest relevant check.',
|
|
32
|
+
'',
|
|
33
|
+
'## Boundaries',
|
|
34
|
+
'',
|
|
35
|
+
'Use this only when the current task matches the preserved workflow.'
|
|
36
|
+
].join('\n');
|
|
37
|
+
if (hasFrontmatter(body)) return `${body.trim()}\n`;
|
|
38
|
+
return [
|
|
39
|
+
'---',
|
|
40
|
+
`name: ${name}`,
|
|
41
|
+
`description: ${escapeFrontmatter(description) || `Use when this reflected workflow applies.`}`,
|
|
42
|
+
'---',
|
|
43
|
+
'',
|
|
44
|
+
body
|
|
45
|
+
].join('\n').trimEnd() + '\n';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
export function normalizeReflectDraft(raw = {}) {
|
|
49
|
+
const name = slugifySkillName(raw.name || raw.skillName || raw.title);
|
|
50
|
+
const description = String(raw.description || raw.summary || `Use when the ${name} workflow applies.`).trim();
|
|
51
|
+
const confidence = Math.min(1, Math.max(0, Number(raw.confidence ?? 0.75)));
|
|
52
|
+
return {
|
|
53
|
+
id: Number(raw.id || 1),
|
|
54
|
+
name,
|
|
55
|
+
description,
|
|
56
|
+
confidence,
|
|
57
|
+
content: renderSkillContent({ name, description, content: raw.content || raw.markdown || raw.body })
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function buildReflectTargetPath({ scope = 'project', name, workspaceRoot = process.cwd() } = {}) {
|
|
62
|
+
const safeName = slugifySkillName(name);
|
|
63
|
+
const baseDir = String(scope || '').toLowerCase() === 'global'
|
|
64
|
+
? getSkillsDir()
|
|
65
|
+
: getProjectSkillsDir(workspaceRoot);
|
|
66
|
+
return path.join(baseDir, safeName, 'SKILL.md');
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export function parseReflectScope(args = []) {
|
|
70
|
+
let scope = 'project';
|
|
71
|
+
const requestParts = [];
|
|
72
|
+
for (let index = 0; index < args.length; index += 1) {
|
|
73
|
+
const arg = String(args[index] || '');
|
|
74
|
+
if (arg === '--scope') {
|
|
75
|
+
const next = String(args[index + 1] || '').toLowerCase();
|
|
76
|
+
if (next === 'global' || next === 'project') {
|
|
77
|
+
scope = next;
|
|
78
|
+
index += 1;
|
|
79
|
+
}
|
|
80
|
+
continue;
|
|
81
|
+
}
|
|
82
|
+
if (arg.startsWith('--scope=')) {
|
|
83
|
+
const value = arg.slice('--scope='.length).toLowerCase();
|
|
84
|
+
if (value === 'global' || value === 'project') scope = value;
|
|
85
|
+
continue;
|
|
86
|
+
}
|
|
87
|
+
requestParts.push(arg);
|
|
88
|
+
}
|
|
89
|
+
return { scope, request: requestParts.join(' ').trim() };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function parseModelDrafts(text) {
|
|
93
|
+
const raw = String(text || '').trim();
|
|
94
|
+
if (!raw) return [];
|
|
95
|
+
const unfenced = raw.replace(/^```(?:json)?\s*/i, '').replace(/\s*```$/i, '').trim();
|
|
96
|
+
try {
|
|
97
|
+
const parsed = JSON.parse(unfenced);
|
|
98
|
+
if (Array.isArray(parsed?.candidates)) return parsed.candidates.map((item, index) => normalizeReflectDraft({ id: index + 1, ...item }));
|
|
99
|
+
if (Array.isArray(parsed)) return parsed.map((item, index) => normalizeReflectDraft({ id: index + 1, ...item }));
|
|
100
|
+
if (parsed && typeof parsed === 'object') return [normalizeReflectDraft(parsed)];
|
|
101
|
+
} catch {
|
|
102
|
+
// Fall back to wrapping plain markdown below.
|
|
103
|
+
}
|
|
104
|
+
return [normalizeReflectDraft({
|
|
105
|
+
name: 'reflected-success-workflow',
|
|
106
|
+
description: 'Use when the reflected successful workflow applies.',
|
|
107
|
+
content: raw
|
|
108
|
+
})];
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function recentContext(session, limit = 10) {
|
|
112
|
+
const messages = Array.isArray(session?.messages) ? session.messages : [];
|
|
113
|
+
return messages
|
|
114
|
+
.slice(-limit)
|
|
115
|
+
.map((message) => `${message.role}: ${String(message.content || '').slice(0, 1200)}`)
|
|
116
|
+
.join('\n\n');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function buildReflectSkillDraft({
|
|
120
|
+
request = '',
|
|
121
|
+
scope = 'project',
|
|
122
|
+
session,
|
|
123
|
+
config = {},
|
|
124
|
+
model,
|
|
125
|
+
systemPrompt = '',
|
|
126
|
+
previousDraft = null,
|
|
127
|
+
feedback = ''
|
|
128
|
+
} = {}) {
|
|
129
|
+
const mode = String(request || '').trim() ? 'directed' : 'exploratory';
|
|
130
|
+
const prompt = [
|
|
131
|
+
'Create a reusable Codex/Codemini SKILL.md draft from a successful workflow.',
|
|
132
|
+
`Mode: ${mode}`,
|
|
133
|
+
`Target scope: ${scope}`,
|
|
134
|
+
request ? `User reflection request:\n${request}` : 'No explicit request was supplied. Be conservative and return no candidates if the recent context does not show a reusable success pattern.',
|
|
135
|
+
previousDraft ? `Existing draft to revise:\n${previousDraft.content || ''}` : '',
|
|
136
|
+
feedback ? `User edit feedback:\n${feedback}` : '',
|
|
137
|
+
'Recent session context:',
|
|
138
|
+
recentContext(session),
|
|
139
|
+
'Return valid JSON only, no markdown fences.',
|
|
140
|
+
'Shape: {"candidates":[{"name":"kebab-case-name","description":"when to use this skill","confidence":0.0,"content":"full SKILL.md body or markdown body"}]}',
|
|
141
|
+
'The content must include trigger conditions, workflow/toolchain, key decisions, pitfalls, verification, and boundaries.',
|
|
142
|
+
'Do not write memory or inbox content. This is only a skill draft.'
|
|
143
|
+
].filter(Boolean).join('\n\n');
|
|
144
|
+
|
|
145
|
+
const result = await createChatCompletion({
|
|
146
|
+
sdkProvider: config?.sdk?.provider,
|
|
147
|
+
baseUrl: config?.gateway?.base_url,
|
|
148
|
+
apiKey: config?.gateway?.api_key,
|
|
149
|
+
model: model || config?.model?.name,
|
|
150
|
+
messages: [
|
|
151
|
+
{ role: 'system', content: systemPrompt || 'You draft concise, reusable coding workflow skills.' },
|
|
152
|
+
{ role: 'user', content: prompt }
|
|
153
|
+
],
|
|
154
|
+
temperature: 0,
|
|
155
|
+
timeoutMs: REFLECT_TIMEOUT_MS
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
return parseModelDrafts(result?.text || '');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function attachReflectTargets({ candidates = [], scope = 'project', workspaceRoot = process.cwd() } = {}) {
|
|
162
|
+
return candidates.map((candidate, index) => {
|
|
163
|
+
const draft = normalizeReflectDraft({ id: index + 1, ...candidate });
|
|
164
|
+
return {
|
|
165
|
+
...draft,
|
|
166
|
+
targetPath: buildReflectTargetPath({ scope, name: draft.name, workspaceRoot })
|
|
167
|
+
};
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export async function writeReflectSkillDraft({ draft, scope = 'project', workspaceRoot = process.cwd() } = {}) {
|
|
172
|
+
const normalized = normalizeReflectDraft(draft);
|
|
173
|
+
const filePath = buildReflectTargetPath({ scope, name: normalized.name, workspaceRoot });
|
|
174
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
175
|
+
await fs.writeFile(filePath, normalized.content, 'utf8');
|
|
176
|
+
return { filePath, draft: normalized };
|
|
177
|
+
}
|
|
178
|
+
|
|
@@ -1,40 +1,40 @@
|
|
|
1
|
-
export function normalizeReplyLanguage(value) {
|
|
2
|
-
const raw = String(value || '').trim().toLowerCase();
|
|
3
|
-
if (!raw) return 'zh';
|
|
4
|
-
if (['en', 'en-us', 'en_us', 'english'].includes(raw)) return 'en';
|
|
5
|
-
if (['zh', 'zh-cn', 'zh_cn', 'cn', 'chinese', '中文', '简体中文'].includes(raw)) return 'zh';
|
|
6
|
-
return 'zh';
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export function getReplyLanguage(config = {}) {
|
|
10
|
-
if (typeof config === 'string') return normalizeReplyLanguage(config);
|
|
11
|
-
return normalizeReplyLanguage(config?.ui?.reply_language);
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
export function getReplyLanguageName(config = {}) {
|
|
15
|
-
return getReplyLanguage(config) === 'en' ? 'English' : 'Simplified Chinese';
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
export function buildSystemPromptWithReplyLanguage(baseSystemPrompt, config = {}) {
|
|
19
|
-
const replyLanguage = getReplyLanguage(config);
|
|
20
|
-
const directive =
|
|
21
|
-
replyLanguage === 'en'
|
|
22
|
-
? [
|
|
23
|
-
'[Reply language]',
|
|
24
|
-
'Respond in English.',
|
|
25
|
-
'Write generated documentation, user-facing text, and code comments in English unless the user explicitly asks for a different language.'
|
|
26
|
-
].join('\n')
|
|
27
|
-
: [
|
|
28
|
-
'[Reply language]',
|
|
29
|
-
'Respond in Simplified Chinese.',
|
|
30
|
-
'Write generated documentation, user-facing text, and code comments in Simplified Chinese unless the user explicitly asks for a different language.'
|
|
31
|
-
].join('\n');
|
|
32
|
-
|
|
33
|
-
return `${String(baseSystemPrompt || '').trim()}\n\n${directive}`.trim();
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
export function stripReplyLanguageDirective(systemPrompt) {
|
|
37
|
-
return String(systemPrompt || '')
|
|
38
|
-
.replace(/\n{0,2}\[Reply language\]\n(?:Respond in (?:English|Simplified Chinese)\.\n)?Write generated documentation, user-facing text, and code comments in (?:English|Simplified Chinese) unless the user explicitly asks for a different language\./g, '')
|
|
39
|
-
.trim();
|
|
40
|
-
}
|
|
1
|
+
export function normalizeReplyLanguage(value) {
|
|
2
|
+
const raw = String(value || '').trim().toLowerCase();
|
|
3
|
+
if (!raw) return 'zh';
|
|
4
|
+
if (['en', 'en-us', 'en_us', 'english'].includes(raw)) return 'en';
|
|
5
|
+
if (['zh', 'zh-cn', 'zh_cn', 'cn', 'chinese', '中文', '简体中文'].includes(raw)) return 'zh';
|
|
6
|
+
return 'zh';
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
export function getReplyLanguage(config = {}) {
|
|
10
|
+
if (typeof config === 'string') return normalizeReplyLanguage(config);
|
|
11
|
+
return normalizeReplyLanguage(config?.ui?.reply_language);
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function getReplyLanguageName(config = {}) {
|
|
15
|
+
return getReplyLanguage(config) === 'en' ? 'English' : 'Simplified Chinese';
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function buildSystemPromptWithReplyLanguage(baseSystemPrompt, config = {}) {
|
|
19
|
+
const replyLanguage = getReplyLanguage(config);
|
|
20
|
+
const directive =
|
|
21
|
+
replyLanguage === 'en'
|
|
22
|
+
? [
|
|
23
|
+
'[Reply language]',
|
|
24
|
+
'Respond in English.',
|
|
25
|
+
'Write generated documentation, user-facing text, and code comments in English unless the user explicitly asks for a different language.'
|
|
26
|
+
].join('\n')
|
|
27
|
+
: [
|
|
28
|
+
'[Reply language]',
|
|
29
|
+
'Respond in Simplified Chinese.',
|
|
30
|
+
'Write generated documentation, user-facing text, and code comments in Simplified Chinese unless the user explicitly asks for a different language.'
|
|
31
|
+
].join('\n');
|
|
32
|
+
|
|
33
|
+
return `${String(baseSystemPrompt || '').trim()}\n\n${directive}`.trim();
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export function stripReplyLanguageDirective(systemPrompt) {
|
|
37
|
+
return String(systemPrompt || '')
|
|
38
|
+
.replace(/\n{0,2}\[Reply language\]\n(?:Respond in (?:English|Simplified Chinese)\.\n)?Write generated documentation, user-facing text, and code comments in (?:English|Simplified Chinese) unless the user explicitly asks for a different language\./g, '')
|
|
39
|
+
.trim();
|
|
40
|
+
}
|