codemini-cli 0.5.7 → 0.5.9
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 +30 -0
- package/codemini-web/dist/assets/{highlighted-body-OFNGDK62-Dp1CwQdI.js → highlighted-body-OFNGDK62-HgeDi9HJ.js} +1 -1
- package/codemini-web/dist/assets/index-BSdIdn3L.css +2 -0
- package/codemini-web/dist/assets/{index-Bvd2jj3t.js → index-C4tKT3v4.js} +95 -93
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-CDgkkDBg.js +1 -0
- package/codemini-web/dist/index.html +2 -2
- package/codemini-web/lib/runtime-bridge.js +550 -549
- package/codemini-web/server.js +314 -187
- package/package.json +67 -67
- package/skills/codemini.skills.json +40 -0
- package/src/commands/skill.js +16 -5
- package/src/core/chat-runtime.js +93 -14
- package/src/core/command-loader.js +120 -25
- package/src/core/command-policy.js +34 -10
- package/src/core/config-store.js +11 -0
- package/src/core/provider/anthropic.js +137 -24
- package/src/core/tools.js +114 -65
- package/codemini-web/dist/assets/index-Csjkc1MY.css +0 -2
- package/codemini-web/dist/assets/mermaid-GHXKKRXX-DSVp--w4.js +0 -1
package/package.json
CHANGED
|
@@ -1,67 +1,67 @@
|
|
|
1
|
-
{
|
|
2
|
-
"name": "codemini-cli",
|
|
3
|
-
"version": "0.5.
|
|
4
|
-
"description": "Coding CLI optimized for small-model workflows and Windows PowerShell",
|
|
5
|
-
"keywords": [
|
|
6
|
-
"cli",
|
|
7
|
-
"ai",
|
|
8
|
-
"coding-assistant",
|
|
9
|
-
"developer-tools",
|
|
10
|
-
"terminal",
|
|
11
|
-
"windows",
|
|
12
|
-
"powershell"
|
|
13
|
-
],
|
|
14
|
-
"type": "module",
|
|
15
|
-
"homepage": "https://github.com/havingautism/Codemini-CLI#readme",
|
|
16
|
-
"bugs": {
|
|
17
|
-
"url": "https://github.com/havingautism/Codemini-CLI/issues"
|
|
18
|
-
},
|
|
19
|
-
"repository": {
|
|
20
|
-
"type": "git",
|
|
21
|
-
"url": "git+https://github.com/havingautism/Codemini-CLI.git"
|
|
22
|
-
},
|
|
23
|
-
"bin": {
|
|
24
|
-
"codemini": "bin/coder.js",
|
|
25
|
-
"coder": "bin/coder.js"
|
|
26
|
-
},
|
|
27
|
-
"scripts": {
|
|
28
|
-
"start": "node bin/coder.js",
|
|
29
|
-
"test": "node --test tests/*.test.js",
|
|
30
|
-
"build:web": "npm install --prefix codemini-web && npm run build --prefix codemini-web",
|
|
31
|
-
"prepack": "npm run build:web",
|
|
32
|
-
"pack:offline": "npm pack",
|
|
33
|
-
"bump:patch": "npm version patch --no-git-tag-version",
|
|
34
|
-
"bump:minor": "npm version minor --no-git-tag-version",
|
|
35
|
-
"bump:major": "npm version major --no-git-tag-version"
|
|
36
|
-
},
|
|
37
|
-
"files": [
|
|
38
|
-
"bin",
|
|
39
|
-
"src",
|
|
40
|
-
"codemini-web/server.js",
|
|
41
|
-
"codemini-web/lib",
|
|
42
|
-
"codemini-web/dist",
|
|
43
|
-
"codemini-web/codemini_logo.png",
|
|
44
|
-
"souls",
|
|
45
|
-
"templates",
|
|
46
|
-
"skills",
|
|
47
|
-
"README.md",
|
|
48
|
-
"OPERATIONS.md",
|
|
49
|
-
"deployment.md"
|
|
50
|
-
],
|
|
51
|
-
"engines": {
|
|
52
|
-
"node": ">=22"
|
|
53
|
-
},
|
|
54
|
-
"publishConfig": {
|
|
55
|
-
"access": "public"
|
|
56
|
-
},
|
|
57
|
-
"dependencies": {
|
|
58
|
-
"@cursorless/tree-sitter-wasms": "^0.8.1",
|
|
59
|
-
"cheerio": "^1.1.2",
|
|
60
|
-
"cli-truncate": "^6.0.0",
|
|
61
|
-
"ink": "^7.0.0",
|
|
62
|
-
"react": "^19.2.5",
|
|
63
|
-
"strip-ansi": "^7.2.0",
|
|
64
|
-
"web-tree-sitter": "^0.26.8"
|
|
65
|
-
},
|
|
66
|
-
"license": "MIT"
|
|
67
|
-
}
|
|
1
|
+
{
|
|
2
|
+
"name": "codemini-cli",
|
|
3
|
+
"version": "0.5.9",
|
|
4
|
+
"description": "Coding CLI optimized for small-model workflows and Windows PowerShell",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"cli",
|
|
7
|
+
"ai",
|
|
8
|
+
"coding-assistant",
|
|
9
|
+
"developer-tools",
|
|
10
|
+
"terminal",
|
|
11
|
+
"windows",
|
|
12
|
+
"powershell"
|
|
13
|
+
],
|
|
14
|
+
"type": "module",
|
|
15
|
+
"homepage": "https://github.com/havingautism/Codemini-CLI#readme",
|
|
16
|
+
"bugs": {
|
|
17
|
+
"url": "https://github.com/havingautism/Codemini-CLI/issues"
|
|
18
|
+
},
|
|
19
|
+
"repository": {
|
|
20
|
+
"type": "git",
|
|
21
|
+
"url": "git+https://github.com/havingautism/Codemini-CLI.git"
|
|
22
|
+
},
|
|
23
|
+
"bin": {
|
|
24
|
+
"codemini": "bin/coder.js",
|
|
25
|
+
"coder": "bin/coder.js"
|
|
26
|
+
},
|
|
27
|
+
"scripts": {
|
|
28
|
+
"start": "node bin/coder.js",
|
|
29
|
+
"test": "node --test tests/*.test.js",
|
|
30
|
+
"build:web": "npm install --prefix codemini-web && npm run build --prefix codemini-web",
|
|
31
|
+
"prepack": "npm run build:web",
|
|
32
|
+
"pack:offline": "npm pack",
|
|
33
|
+
"bump:patch": "npm version patch --no-git-tag-version",
|
|
34
|
+
"bump:minor": "npm version minor --no-git-tag-version",
|
|
35
|
+
"bump:major": "npm version major --no-git-tag-version"
|
|
36
|
+
},
|
|
37
|
+
"files": [
|
|
38
|
+
"bin",
|
|
39
|
+
"src",
|
|
40
|
+
"codemini-web/server.js",
|
|
41
|
+
"codemini-web/lib",
|
|
42
|
+
"codemini-web/dist",
|
|
43
|
+
"codemini-web/codemini_logo.png",
|
|
44
|
+
"souls",
|
|
45
|
+
"templates",
|
|
46
|
+
"skills",
|
|
47
|
+
"README.md",
|
|
48
|
+
"OPERATIONS.md",
|
|
49
|
+
"deployment.md"
|
|
50
|
+
],
|
|
51
|
+
"engines": {
|
|
52
|
+
"node": ">=22"
|
|
53
|
+
},
|
|
54
|
+
"publishConfig": {
|
|
55
|
+
"access": "public"
|
|
56
|
+
},
|
|
57
|
+
"dependencies": {
|
|
58
|
+
"@cursorless/tree-sitter-wasms": "^0.8.1",
|
|
59
|
+
"cheerio": "^1.1.2",
|
|
60
|
+
"cli-truncate": "^6.0.0",
|
|
61
|
+
"ink": "^7.0.0",
|
|
62
|
+
"react": "^19.2.5",
|
|
63
|
+
"strip-ansi": "^7.2.0",
|
|
64
|
+
"web-tree-sitter": "^0.26.8"
|
|
65
|
+
},
|
|
66
|
+
"license": "MIT"
|
|
67
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"skills": {
|
|
4
|
+
"superpowers-lite": {
|
|
5
|
+
"description": "Default concise workflow for coding tasks; keep context tight, choose the right route, and verify before claiming success.",
|
|
6
|
+
"mode": "always",
|
|
7
|
+
"triggers": [],
|
|
8
|
+
"enabled": true,
|
|
9
|
+
"priority": 100
|
|
10
|
+
},
|
|
11
|
+
"brainstorm": {
|
|
12
|
+
"description": "Use when a feature or behavior request has multiple reasonable approaches and the missing piece is user preference, tradeoff choice, or key constraint.",
|
|
13
|
+
"mode": "agent_requested",
|
|
14
|
+
"triggers": [],
|
|
15
|
+
"enabled": true,
|
|
16
|
+
"priority": 70
|
|
17
|
+
},
|
|
18
|
+
"writing-plans": {
|
|
19
|
+
"description": "Use when you have a clear goal or approved design for a non-trivial task, before touching code.",
|
|
20
|
+
"mode": "agent_requested",
|
|
21
|
+
"triggers": [],
|
|
22
|
+
"enabled": true,
|
|
23
|
+
"priority": 75
|
|
24
|
+
},
|
|
25
|
+
"grill-me": {
|
|
26
|
+
"description": "Use when the user explicitly asks to pressure-test a plan, architecture choice, PR, launch, or product idea.",
|
|
27
|
+
"mode": "agent_requested",
|
|
28
|
+
"triggers": [],
|
|
29
|
+
"enabled": true,
|
|
30
|
+
"priority": 65
|
|
31
|
+
},
|
|
32
|
+
"project-requirements": {
|
|
33
|
+
"description": "Create a project requirements report with repository inspection and structured output artifacts.",
|
|
34
|
+
"mode": "manual",
|
|
35
|
+
"triggers": [],
|
|
36
|
+
"enabled": true,
|
|
37
|
+
"priority": 50
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
package/src/commands/skill.js
CHANGED
|
@@ -77,9 +77,15 @@ export async function listSkillEntries({ scope = 'all', cwd = process.cwd() } =
|
|
|
77
77
|
name: command.name,
|
|
78
78
|
version: command.metadata?.version || '0.0.0',
|
|
79
79
|
description: command.metadata?.description || '',
|
|
80
|
+
mode: command.metadata?.mode || '',
|
|
81
|
+
triggers: Array.isArray(command.metadata?.triggers) ? command.metadata.triggers : [],
|
|
80
82
|
scope: itemScope,
|
|
81
83
|
path: command.path,
|
|
82
|
-
enabled:
|
|
84
|
+
enabled: command.metadata?.enabled === false
|
|
85
|
+
? false
|
|
86
|
+
: itemScope === 'builtin'
|
|
87
|
+
? true
|
|
88
|
+
: config.skills?.enabled?.[command.name] !== false
|
|
83
89
|
});
|
|
84
90
|
}
|
|
85
91
|
return entries.sort((a, b) => `${a.scope}:${a.name}`.localeCompare(`${b.scope}:${b.name}`));
|
|
@@ -93,14 +99,19 @@ async function readSkillMeta(name, { scope = 'all', cwd = process.cwd() } = {})
|
|
|
93
99
|
}
|
|
94
100
|
const dir = path.dirname(found.path);
|
|
95
101
|
const manifestPath = path.join(dir, 'manifest.json');
|
|
102
|
+
const catalogPath = path.join(path.dirname(dir), 'codemini.skills.json');
|
|
96
103
|
let manifest = null;
|
|
97
104
|
try {
|
|
98
|
-
|
|
105
|
+
const catalog = JSON.parse(await fs.readFile(catalogPath, 'utf8'));
|
|
106
|
+
manifest = catalog?.skills?.[found.name] || null;
|
|
99
107
|
} catch {
|
|
100
|
-
|
|
108
|
+
try {
|
|
109
|
+
manifest = JSON.parse(await fs.readFile(manifestPath, 'utf8'));
|
|
110
|
+
} catch {
|
|
111
|
+
manifest = null;
|
|
112
|
+
}
|
|
101
113
|
}
|
|
102
|
-
const
|
|
103
|
-
const skillPath = found.path || path.join(dir, entryFile);
|
|
114
|
+
const skillPath = found.path || path.join(dir, 'SKILL.md');
|
|
104
115
|
try {
|
|
105
116
|
const content = await fs.readFile(skillPath, 'utf8');
|
|
106
117
|
const firstLines = content.split('\n').slice(0, 20).join('\n');
|
package/src/core/chat-runtime.js
CHANGED
|
@@ -136,6 +136,7 @@ function getCompletionCopy(language = 'zh') {
|
|
|
136
136
|
'soul.preset': 'soul 预设',
|
|
137
137
|
'soul.custom_path': '自定义 soul 路径',
|
|
138
138
|
'policy.safe_mode': '安全模式开关',
|
|
139
|
+
'policy.allowed_paths': '安全模式目录白名单',
|
|
139
140
|
'policy.allow_dangerous_commands': '危险命令开关'
|
|
140
141
|
},
|
|
141
142
|
optionHints: {
|
|
@@ -145,6 +146,7 @@ function getCompletionCopy(language = 'zh') {
|
|
|
145
146
|
'execution.mode': '可选:auto | normal | plan',
|
|
146
147
|
'shell.default': '常用:bash | powershell',
|
|
147
148
|
'policy.safe_mode': '可选:true | false',
|
|
149
|
+
'policy.allowed_paths': 'JSON 数组,例如 ["D:\\\\shared"]',
|
|
148
150
|
'policy.allow_dangerous_commands': '可选:true | false',
|
|
149
151
|
'context.prompt_budget_audit': '可选:true | false'
|
|
150
152
|
},
|
|
@@ -243,6 +245,7 @@ function getCompletionCopy(language = 'zh') {
|
|
|
243
245
|
'soul.preset': 'soul preset',
|
|
244
246
|
'soul.custom_path': 'custom soul prompt path',
|
|
245
247
|
'policy.safe_mode': 'safe mode switch',
|
|
248
|
+
'policy.allowed_paths': 'safe-mode allowed path roots',
|
|
246
249
|
'policy.allow_dangerous_commands': 'dangerous command allowance'
|
|
247
250
|
},
|
|
248
251
|
optionHints: {
|
|
@@ -252,6 +255,7 @@ function getCompletionCopy(language = 'zh') {
|
|
|
252
255
|
'execution.mode': 'options: auto | normal | plan',
|
|
253
256
|
'shell.default': 'common: bash | powershell',
|
|
254
257
|
'policy.safe_mode': 'options: true | false',
|
|
258
|
+
'policy.allowed_paths': 'JSON array, for example ["D:\\\\shared"]',
|
|
255
259
|
'policy.allow_dangerous_commands': 'options: true | false',
|
|
256
260
|
'context.prompt_budget_audit': 'options: true | false'
|
|
257
261
|
},
|
|
@@ -1265,6 +1269,7 @@ function isBundledSkillCommand(command) {
|
|
|
1265
1269
|
}
|
|
1266
1270
|
|
|
1267
1271
|
function isSkillEnabled(config, name, command = null) {
|
|
1272
|
+
if (command?.metadata?.enabled === false) return false;
|
|
1268
1273
|
if (isBundledSkillCommand(command)) return true;
|
|
1269
1274
|
return config.skills?.enabled?.[name] !== false;
|
|
1270
1275
|
}
|
|
@@ -2301,6 +2306,9 @@ function buildRuntimeStateSnapshot({ currentSession, config, model, executionMod
|
|
|
2301
2306
|
maxContextTokens,
|
|
2302
2307
|
pendingPlanApproval: planState?.status === 'pending_approval'
|
|
2303
2308
|
? { goal: planState.goal, summary: planState.finalSummary || planState.summary, filePath: planState.filePath, steps: planState.steps || [] }
|
|
2309
|
+
: null,
|
|
2310
|
+
pendingReflectSkill: planState?.status === 'pending_reflect_skill'
|
|
2311
|
+
? buildPendingReflectSkillSnapshot(planState)
|
|
2304
2312
|
: null
|
|
2305
2313
|
};
|
|
2306
2314
|
Object.defineProperties(snapshot, {
|
|
@@ -2314,11 +2322,6 @@ function buildRuntimeStateSnapshot({ currentSession, config, model, executionMod
|
|
|
2314
2322
|
enumerable: false,
|
|
2315
2323
|
writable: false
|
|
2316
2324
|
},
|
|
2317
|
-
pendingReflectSkill: {
|
|
2318
|
-
value: currentSession?.planState?.status === 'pending_reflect_skill',
|
|
2319
|
-
enumerable: false,
|
|
2320
|
-
writable: false
|
|
2321
|
-
},
|
|
2322
2325
|
replyLanguage: {
|
|
2323
2326
|
value: getReplyLanguage(config),
|
|
2324
2327
|
enumerable: false,
|
|
@@ -2329,7 +2332,6 @@ function buildRuntimeStateSnapshot({ currentSession, config, model, executionMod
|
|
|
2329
2332
|
...snapshot,
|
|
2330
2333
|
currentContextTokens,
|
|
2331
2334
|
contextUsagePct,
|
|
2332
|
-
pendingReflectSkill: currentSession?.planState?.status === 'pending_reflect_skill',
|
|
2333
2335
|
replyLanguage: getReplyLanguage(config)
|
|
2334
2336
|
}),
|
|
2335
2337
|
enumerable: false,
|
|
@@ -2513,6 +2515,21 @@ function buildPendingReflectSkillMessage(reflectState) {
|
|
|
2513
2515
|
return lines.join('\n');
|
|
2514
2516
|
}
|
|
2515
2517
|
|
|
2518
|
+
function buildPendingReflectSkillSnapshot(reflectState) {
|
|
2519
|
+
const candidates = Array.isArray(reflectState?.candidates) ? reflectState.candidates : [];
|
|
2520
|
+
const candidate = candidates[0] || null;
|
|
2521
|
+
if (!candidate) return null;
|
|
2522
|
+
return {
|
|
2523
|
+
scope: reflectState?.targetScope || 'project',
|
|
2524
|
+
request: reflectState?.request || '',
|
|
2525
|
+
name: candidate.name || '',
|
|
2526
|
+
description: candidate.description || '',
|
|
2527
|
+
confidence: Number(candidate.confidence ?? 0.75),
|
|
2528
|
+
targetPath: candidate.targetPath || '',
|
|
2529
|
+
content: candidate.content || ''
|
|
2530
|
+
};
|
|
2531
|
+
}
|
|
2532
|
+
|
|
2516
2533
|
function buildApprovedPlanExecutionPrompt(planState, approvalText = '') {
|
|
2517
2534
|
const requirementPacket = buildGoalRequirementPacket(planState?.goal || '', 'coder');
|
|
2518
2535
|
const lines = [
|
|
@@ -2740,8 +2757,10 @@ async function askModel({
|
|
|
2740
2757
|
}
|
|
2741
2758
|
}
|
|
2742
2759
|
|
|
2760
|
+
const shouldGenerateTitle = text
|
|
2761
|
+
? !session.messages.some((msg) => msg?.role === 'user')
|
|
2762
|
+
: false;
|
|
2743
2763
|
if (text) {
|
|
2744
|
-
const shouldGenerateTitle = !session.messages.some((msg) => msg?.role === 'user');
|
|
2745
2764
|
const modelExtra =
|
|
2746
2765
|
typeof modelText === 'string' && modelText && modelText !== text ? { model_content: modelText } : {};
|
|
2747
2766
|
const userMessage = stampedMessage('user', text, modelExtra);
|
|
@@ -2773,7 +2792,16 @@ async function askModel({
|
|
|
2773
2792
|
|
|
2774
2793
|
const { definitions, handlers, formatters, deferredDefinitions, dispose: disposeTools } = getBuiltinTools({
|
|
2775
2794
|
workspaceRoot: process.cwd(),
|
|
2776
|
-
config
|
|
2795
|
+
config: {
|
|
2796
|
+
...config,
|
|
2797
|
+
policy: {
|
|
2798
|
+
...(config.policy || {}),
|
|
2799
|
+
allowed_paths: [
|
|
2800
|
+
...(Array.isArray(config.policy?.allowed_paths) ? config.policy.allowed_paths : []),
|
|
2801
|
+
path.join(getSessionsDir(), String(session.id))
|
|
2802
|
+
]
|
|
2803
|
+
}
|
|
2804
|
+
},
|
|
2777
2805
|
sessionId: session.id,
|
|
2778
2806
|
onSystemEvent: onAgentEvent,
|
|
2779
2807
|
getTodos: () => normalizeTodos(session.todos),
|
|
@@ -3004,7 +3032,7 @@ async function askModel({
|
|
|
3004
3032
|
await flushScheduledSave();
|
|
3005
3033
|
await saveSession(session);
|
|
3006
3034
|
// Generate a better title asynchronously after saving
|
|
3007
|
-
if (
|
|
3035
|
+
if (shouldGenerateTitle) {
|
|
3008
3036
|
const titleSessionId = session.id;
|
|
3009
3037
|
generateSessionTitle({
|
|
3010
3038
|
userText: text,
|
|
@@ -4242,6 +4270,32 @@ export async function createChatRuntime({
|
|
|
4242
4270
|
if (hasPendingPlanApproval(currentSession)) {
|
|
4243
4271
|
executionMode = 'plan';
|
|
4244
4272
|
}
|
|
4273
|
+
let compactState = null;
|
|
4274
|
+
const normalizeCompactThreshold = (value, fallback = 60) => {
|
|
4275
|
+
const num = Number(value);
|
|
4276
|
+
if (!Number.isFinite(num)) return fallback;
|
|
4277
|
+
return Math.min(95, Math.max(50, num));
|
|
4278
|
+
};
|
|
4279
|
+
const syncCompactStateFromConfig = () => {
|
|
4280
|
+
if (!compactState) return;
|
|
4281
|
+
compactState.threshold = normalizeCompactThreshold(config.context?.preflight_trigger_pct, 60);
|
|
4282
|
+
};
|
|
4283
|
+
const syncRuntimeFromConfig = async ({ model: nextModel } = {}) => {
|
|
4284
|
+
const configuredMode = String(config.execution?.mode || 'normal');
|
|
4285
|
+
executionMode = hasPendingPlanApproval(currentSession)
|
|
4286
|
+
? 'plan'
|
|
4287
|
+
: (['normal', 'auto', 'plan'].includes(configuredMode) ? configuredMode : 'normal');
|
|
4288
|
+
syncCompactStateFromConfig();
|
|
4289
|
+
|
|
4290
|
+
const resolvedModel = String(nextModel || '').trim();
|
|
4291
|
+
if (resolvedModel) {
|
|
4292
|
+
model = resolvedModel;
|
|
4293
|
+
if (currentSession && typeof currentSession === 'object') {
|
|
4294
|
+
currentSession.model = model;
|
|
4295
|
+
await saveSession(currentSession).catch(() => {});
|
|
4296
|
+
}
|
|
4297
|
+
}
|
|
4298
|
+
};
|
|
4245
4299
|
const commands = await loadCommandsAndSkills();
|
|
4246
4300
|
const reloadCommandsAndSkills = async () => {
|
|
4247
4301
|
const next = await loadCommandsAndSkills();
|
|
@@ -4254,10 +4308,10 @@ export async function createChatRuntime({
|
|
|
4254
4308
|
// Set up tool result store under session directory
|
|
4255
4309
|
const sessionResultsDir = path.join(getSessionsDir(), String(currentSession.id));
|
|
4256
4310
|
setResultDir(sessionResultsDir);
|
|
4257
|
-
|
|
4311
|
+
compactState = {
|
|
4258
4312
|
backupMessages: null,
|
|
4259
4313
|
autoEnabled: true,
|
|
4260
|
-
threshold: 60,
|
|
4314
|
+
threshold: normalizeCompactThreshold(config.context?.preflight_trigger_pct, 60),
|
|
4261
4315
|
mode: 'conservative'
|
|
4262
4316
|
};
|
|
4263
4317
|
let compactedForModel = currentSession.compact?.view || null;
|
|
@@ -4341,6 +4395,7 @@ export async function createChatRuntime({
|
|
|
4341
4395
|
'soul.preset',
|
|
4342
4396
|
'soul.custom_path',
|
|
4343
4397
|
'policy.safe_mode',
|
|
4398
|
+
'policy.allowed_paths',
|
|
4344
4399
|
'policy.allow_dangerous_commands'
|
|
4345
4400
|
];
|
|
4346
4401
|
|
|
@@ -4762,6 +4817,11 @@ export async function createChatRuntime({
|
|
|
4762
4817
|
};
|
|
4763
4818
|
|
|
4764
4819
|
const persistAssistantExchange = async (userText, assistantText, { includeUser = true, extra = {} } = {}) => {
|
|
4820
|
+
const priorUserCount = currentSession.messages.filter((msg) => msg?.role === 'user').length;
|
|
4821
|
+
const priorAssistantCount = currentSession.messages.filter((msg) => msg?.role === 'assistant').length;
|
|
4822
|
+
const shouldGenerateTitle =
|
|
4823
|
+
(includeUser && userText && priorUserCount === 0) ||
|
|
4824
|
+
(!includeUser && userText && priorUserCount === 1 && priorAssistantCount === 0);
|
|
4765
4825
|
if (includeUser && userText) {
|
|
4766
4826
|
appendSessionMessage(stampedMessage('user', userText));
|
|
4767
4827
|
}
|
|
@@ -4772,7 +4832,7 @@ export async function createChatRuntime({
|
|
|
4772
4832
|
currentSession.mode = executionMode || config.execution?.mode || 'normal';
|
|
4773
4833
|
await saveSession(currentSession);
|
|
4774
4834
|
// Generate a better title asynchronously after saving
|
|
4775
|
-
if (shouldReplaceSessionTitle(currentSession.title)) {
|
|
4835
|
+
if (shouldGenerateTitle || shouldReplaceSessionTitle(currentSession.title)) {
|
|
4776
4836
|
const titleSessionId = currentSession.id;
|
|
4777
4837
|
generateSessionTitle({
|
|
4778
4838
|
userText,
|
|
@@ -5100,6 +5160,7 @@ export async function createChatRuntime({
|
|
|
5100
5160
|
});
|
|
5101
5161
|
currentSession.planState = null;
|
|
5102
5162
|
executionMode = 'auto';
|
|
5163
|
+
if (onAgentEvent) onAgentEvent({ type: 'reflect:approval_cleared' });
|
|
5103
5164
|
await reloadCommandsAndSkills();
|
|
5104
5165
|
const text = `Reflect skill written and loaded: /${written.draft.name}\nPath: ${written.filePath}`;
|
|
5105
5166
|
await persistLocalExchange(line, text, { includeUser: false });
|
|
@@ -5157,6 +5218,12 @@ export async function createChatRuntime({
|
|
|
5157
5218
|
workspaceRoot: process.cwd()
|
|
5158
5219
|
})
|
|
5159
5220
|
};
|
|
5221
|
+
if (onAgentEvent) {
|
|
5222
|
+
onAgentEvent({
|
|
5223
|
+
type: 'reflect:pending_approval',
|
|
5224
|
+
draft: buildPendingReflectSkillSnapshot(currentSession.planState)
|
|
5225
|
+
});
|
|
5226
|
+
}
|
|
5160
5227
|
const text = `Reflect skill draft revised.\n${buildPendingReflectSkillMessage(currentSession.planState)}`;
|
|
5161
5228
|
await persistLocalExchange(line, text);
|
|
5162
5229
|
return { type: 'system', text };
|
|
@@ -5194,6 +5261,7 @@ export async function createChatRuntime({
|
|
|
5194
5261
|
if (hasPendingReflectSkill(currentSession)) {
|
|
5195
5262
|
currentSession.planState = null;
|
|
5196
5263
|
executionMode = 'auto';
|
|
5264
|
+
if (onAgentEvent) onAgentEvent({ type: 'reflect:approval_cleared' });
|
|
5197
5265
|
const text = 'Reflect skill draft discarded.';
|
|
5198
5266
|
await persistLocalExchange(line, text, { includeUser: false });
|
|
5199
5267
|
return { type: 'system', text };
|
|
@@ -5653,6 +5721,12 @@ export async function createChatRuntime({
|
|
|
5653
5721
|
request: parsedReflect.request,
|
|
5654
5722
|
candidates
|
|
5655
5723
|
};
|
|
5724
|
+
if (onAgentEvent) {
|
|
5725
|
+
onAgentEvent({
|
|
5726
|
+
type: 'reflect:pending_approval',
|
|
5727
|
+
draft: buildPendingReflectSkillSnapshot(currentSession.planState)
|
|
5728
|
+
});
|
|
5729
|
+
}
|
|
5656
5730
|
const text = buildPendingReflectSkillMessage(currentSession.planState);
|
|
5657
5731
|
await persistLocalExchange(line, text);
|
|
5658
5732
|
return { type: 'system', text };
|
|
@@ -5708,6 +5782,9 @@ export async function createChatRuntime({
|
|
|
5708
5782
|
if (!key || !value) return { type: 'system', text: 'Usage: /config set <key> <value>' };
|
|
5709
5783
|
await setConfigValue(key, value);
|
|
5710
5784
|
config = await loadConfig();
|
|
5785
|
+
await syncRuntimeFromConfig(
|
|
5786
|
+
key === 'model.name' ? { model: config.model?.name } : {}
|
|
5787
|
+
);
|
|
5711
5788
|
const text = `Set ${key}=${value}`;
|
|
5712
5789
|
await persistLocalExchange(line, text);
|
|
5713
5790
|
return { type: 'system', text };
|
|
@@ -5716,7 +5793,8 @@ export async function createChatRuntime({
|
|
|
5716
5793
|
if (sub === 'reset') {
|
|
5717
5794
|
await resetConfig();
|
|
5718
5795
|
config = await loadConfig();
|
|
5719
|
-
|
|
5796
|
+
await syncRuntimeFromConfig({ model: resolveDefaultModel(config) });
|
|
5797
|
+
syncCompactStateFromConfig();
|
|
5720
5798
|
compactState.mode = 'conservative';
|
|
5721
5799
|
compactState.autoEnabled = true;
|
|
5722
5800
|
const text = 'Config reset complete';
|
|
@@ -6135,8 +6213,9 @@ export async function createChatRuntime({
|
|
|
6135
6213
|
getCurrentSessionId: () => currentSession.id,
|
|
6136
6214
|
getSessionMessages: () => currentSession.messages || [],
|
|
6137
6215
|
getSessionCompact: () => currentSession.compact || null,
|
|
6138
|
-
reloadConfig: async () => {
|
|
6216
|
+
reloadConfig: async (options = {}) => {
|
|
6139
6217
|
config = await loadConfig();
|
|
6218
|
+
await syncRuntimeFromConfig(options);
|
|
6140
6219
|
return config;
|
|
6141
6220
|
},
|
|
6142
6221
|
setExecutionMode: async (next) => {
|