winter-super-cli 2026.6.5 → 2026.6.7
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/winter.js +1 -0
- package/package.json +3 -3
- package/src/agent/runtime.js +13 -16
- package/src/ai/model-capabilities.js +17 -1
- package/src/ai/prompts/system-prompt.js +33 -52
- package/src/ai/providers.js +179 -62
- package/src/ai/small-model-amplifier.js +7 -19
- package/src/cli/commands.js +162 -0
- package/src/cli/context-loader.js +1 -1
- package/src/cli/input-controller.js +55 -44
- package/src/cli/prompt-builder.js +20 -11
- package/src/cli/repl-commands.js +3 -0
- package/src/cli/repl.js +318 -444
- package/src/cli/slash-commands.js +1 -0
- package/src/cli/snowflake-logo.js +64 -86
- package/src/cli/terminal-ui.js +139 -85
- package/src/cli/tool-runtime.js +8 -3
- package/src/cli/tui.js +181 -0
- package/src/codebase-index/codegraph-adapter.js +154 -0
- package/src/codebase-index/indexer.js +1 -1
- package/src/codebase-index/search.js +31 -2
- package/src/context/router.js +4 -41
- package/src/context/token-juice.js +37 -10
- package/src/tools/executor.js +78 -3
|
@@ -1,30 +1,19 @@
|
|
|
1
|
-
import { isSmallModel } from './model-capabilities.js';
|
|
2
|
-
|
|
3
1
|
export function isWeakTier(modelTier = '') {
|
|
4
|
-
return
|
|
2
|
+
return true;
|
|
5
3
|
}
|
|
6
4
|
|
|
7
5
|
export function buildSmallModelAmplification({ modelTier = '', workflowProfile = 'general', depth = 'standard' } = {}) {
|
|
8
|
-
const weak = isWeakTier(modelTier);
|
|
9
|
-
if (!weak) {
|
|
10
|
-
return {
|
|
11
|
-
weak: false,
|
|
12
|
-
maxToolTurns: 8,
|
|
13
|
-
enforceSelfCritique: false,
|
|
14
|
-
hint: '',
|
|
15
|
-
};
|
|
16
|
-
}
|
|
17
|
-
|
|
18
6
|
const deepLike = depth === 'deep' || /debug|backend|data|devops|ai/.test(workflowProfile);
|
|
19
|
-
const maxToolTurns = deepLike ?
|
|
7
|
+
const maxToolTurns = deepLike ? 18 : 14;
|
|
20
8
|
|
|
21
9
|
const hint = [
|
|
22
|
-
'[
|
|
23
|
-
'-
|
|
24
|
-
'- Mandatory loop: PLAN (requirements + files + risks) -> TOOL ACTIONS -> VERIFY -> SELF-CHECK -> FINAL.',
|
|
10
|
+
'[Winter Strength Amplifier]',
|
|
11
|
+
'- Every model, including tiny/local/free models, must run at Winter maximum capability.',
|
|
12
|
+
'- Mandatory internal loop: PLAN (requirements + files + risks) -> TOOL ACTIONS -> VERIFY -> PRIVATE SELF-CHECK -> FINAL.',
|
|
25
13
|
'- Do not skip verification. If verification fails, iterate until max loops.',
|
|
26
|
-
'- Before final answer, run a private self-critique
|
|
14
|
+
'- Before final answer, run a private self-critique silently; do not print the self-check or an improved-answer preface.',
|
|
27
15
|
'- Prefer concrete evidence from tool outputs over reasoning guesses.',
|
|
16
|
+
'- Use CodeGraph/codebase index context before broad file reads when available.',
|
|
28
17
|
].join('\n');
|
|
29
18
|
|
|
30
19
|
return {
|
|
@@ -34,4 +23,3 @@ export function buildSmallModelAmplification({ modelTier = '', workflowProfile =
|
|
|
34
23
|
hint,
|
|
35
24
|
};
|
|
36
25
|
}
|
|
37
|
-
|
package/src/cli/commands.js
CHANGED
|
@@ -15,6 +15,7 @@ import { redactSecrets } from './secret-env.js';
|
|
|
15
15
|
import { formatRuntimeEnvironmentSummary, getRuntimeEnvironment } from './runtime-env.js';
|
|
16
16
|
import { ContextLoader } from './context-loader.js';
|
|
17
17
|
import { ECCManager } from './ecc.js';
|
|
18
|
+
import { buildTuiSnapshot, renderLandingTui } from './tui.js';
|
|
18
19
|
import { HtmlFxManager } from '../integrations/htmlfx-manager.js';
|
|
19
20
|
import { selectWorkflow } from '../ai/workflow-selector.js';
|
|
20
21
|
import { getProfileBlueprint } from '../ai/profile-blueprints.js';
|
|
@@ -73,6 +74,7 @@ export class CommandParser {
|
|
|
73
74
|
resources: this.handleResources.bind(this),
|
|
74
75
|
htmlfx: this.handleHtmlFx.bind(this),
|
|
75
76
|
'memory-vault': this.handleMemoryVault.bind(this),
|
|
77
|
+
tui: this.handleTui.bind(this),
|
|
76
78
|
provider: this.handleProvider.bind(this),
|
|
77
79
|
providers: this.showProviders.bind(this),
|
|
78
80
|
model: this.handleModel.bind(this),
|
|
@@ -121,6 +123,7 @@ export class CommandParser {
|
|
|
121
123
|
'/forget': (args) => this.session.clearMemory(args.length > 0 ? args.join(' ') : null),
|
|
122
124
|
'/memories': () => this.showMemories(),
|
|
123
125
|
'/memory-vault': () => this.handleMemoryVault(args),
|
|
126
|
+
'/tui': () => this.handleTui(args),
|
|
124
127
|
'/plans': () => this.showPlans(),
|
|
125
128
|
'/plan': () => this.handlePlan(args),
|
|
126
129
|
'/cache': () => this.handleCache(args),
|
|
@@ -220,6 +223,15 @@ export class CommandParser {
|
|
|
220
223
|
}
|
|
221
224
|
}
|
|
222
225
|
|
|
226
|
+
async handleTui() {
|
|
227
|
+
await this.ai.init?.();
|
|
228
|
+
const snapshot = buildTuiSnapshot(this);
|
|
229
|
+
console.log(`\n${renderLandingTui(snapshot, {
|
|
230
|
+
colors,
|
|
231
|
+
title: 'Winter Dashboard',
|
|
232
|
+
})}\n`);
|
|
233
|
+
}
|
|
234
|
+
|
|
223
235
|
async searchResourceFiles(root, query, limit = 30) {
|
|
224
236
|
const matches = [];
|
|
225
237
|
const needle = String(query || '').toLowerCase();
|
|
@@ -962,6 +974,150 @@ EXECUTION CONTRACT:
|
|
|
962
974
|
);
|
|
963
975
|
}
|
|
964
976
|
|
|
977
|
+
buildPlanSlug(task) {
|
|
978
|
+
return String(task || 'plan')
|
|
979
|
+
.toLowerCase()
|
|
980
|
+
.replace(/[^a-z0-9]+/g, '-')
|
|
981
|
+
.replace(/^-+|-+$/g, '')
|
|
982
|
+
.slice(0, 48) || 'plan';
|
|
983
|
+
}
|
|
984
|
+
|
|
985
|
+
getPlanScaffoldSpec(task, workflow = {}) {
|
|
986
|
+
const slug = this.buildPlanSlug(task);
|
|
987
|
+
const featureName = slug.replace(/-/g, ' ');
|
|
988
|
+
const profile = String(workflow.profile || 'general');
|
|
989
|
+
const family = profile.split('-')[0] || 'general';
|
|
990
|
+
|
|
991
|
+
const common = {
|
|
992
|
+
dirs: ['docs', 'tests'],
|
|
993
|
+
files: {
|
|
994
|
+
'docs/plan-notes.md': [
|
|
995
|
+
`# ${featureName}`,
|
|
996
|
+
'',
|
|
997
|
+
`- Profile: ${profile}`,
|
|
998
|
+
'- Status: scaffolded by Winter plan apply',
|
|
999
|
+
'',
|
|
1000
|
+
].join('\n'),
|
|
1001
|
+
'tests/README.md': [
|
|
1002
|
+
'# Test Plan',
|
|
1003
|
+
'',
|
|
1004
|
+
'- Add smoke tests for the generated plan.',
|
|
1005
|
+
'- Keep verification commands close to the final implementation stack.',
|
|
1006
|
+
'',
|
|
1007
|
+
].join('\n'),
|
|
1008
|
+
},
|
|
1009
|
+
};
|
|
1010
|
+
|
|
1011
|
+
const specs = {
|
|
1012
|
+
webapp: {
|
|
1013
|
+
dirs: ['src/app', `src/features/${slug}`, 'src/components', 'src/lib', 'tests/e2e'],
|
|
1014
|
+
files: {
|
|
1015
|
+
[`src/features/${slug}/README.md`]: `# ${featureName}\n\nFeature slice for the plan.\n`,
|
|
1016
|
+
[`src/features/${slug}/tasks.md`]: '',
|
|
1017
|
+
'tests/e2e/README.md': '# E2E Tests\n\nAdd Playwright or browser smoke tests here.\n',
|
|
1018
|
+
},
|
|
1019
|
+
},
|
|
1020
|
+
mobile: {
|
|
1021
|
+
dirs: ['src/navigation', `src/features/${slug}`, 'src/screens', 'src/services', 'src/components', 'src/state', 'tests'],
|
|
1022
|
+
files: {
|
|
1023
|
+
[`src/features/${slug}/README.md`]: `# ${featureName}\n\nMobile feature module for screens, hooks, API calls, and state.\n`,
|
|
1024
|
+
[`src/features/${slug}/tasks.md`]: '',
|
|
1025
|
+
'src/navigation/README.md': '# Navigation\n\nDefine app navigation graph and route ownership here.\n',
|
|
1026
|
+
'src/services/README.md': '# Services\n\nPlace API clients, storage adapters, and platform services here.\n',
|
|
1027
|
+
},
|
|
1028
|
+
},
|
|
1029
|
+
backend: {
|
|
1030
|
+
dirs: [`src/modules/${slug}`, `src/modules/${slug}/dto`, `src/modules/${slug}/tests`, 'src/config', 'src/common'],
|
|
1031
|
+
files: {
|
|
1032
|
+
[`src/modules/${slug}/README.md`]: `# ${featureName}\n\nBackend module scaffold for routes/controllers/services/tests.\n`,
|
|
1033
|
+
[`src/modules/${slug}/tasks.md`]: '',
|
|
1034
|
+
'src/config/README.md': '# Config\n\nDocument environment variables and runtime config here.\n',
|
|
1035
|
+
},
|
|
1036
|
+
},
|
|
1037
|
+
desktop: {
|
|
1038
|
+
dirs: ['src/main', 'src/preload', 'src/renderer', `src/features/${slug}`, 'tests'],
|
|
1039
|
+
files: {
|
|
1040
|
+
[`src/features/${slug}/README.md`]: `# ${featureName}\n\nDesktop feature module scaffold.\n`,
|
|
1041
|
+
[`src/features/${slug}/tasks.md`]: '',
|
|
1042
|
+
'src/main/README.md': '# Main Process\n\nKeep privileged runtime code here.\n',
|
|
1043
|
+
'src/preload/README.md': '# Preload\n\nExpose minimal validated IPC APIs here.\n',
|
|
1044
|
+
},
|
|
1045
|
+
},
|
|
1046
|
+
ai: {
|
|
1047
|
+
dirs: ['src/ingestion', 'src/retrieval', 'src/generation', 'src/evals', `src/features/${slug}`],
|
|
1048
|
+
files: {
|
|
1049
|
+
[`src/features/${slug}/README.md`]: `# ${featureName}\n\nAI feature scaffold for pipeline wiring and evals.\n`,
|
|
1050
|
+
[`src/features/${slug}/tasks.md`]: '',
|
|
1051
|
+
'src/evals/README.md': '# Evals\n\nTrack regression prompts, datasets, and quality thresholds here.\n',
|
|
1052
|
+
},
|
|
1053
|
+
},
|
|
1054
|
+
};
|
|
1055
|
+
|
|
1056
|
+
const spec = specs[family] || {
|
|
1057
|
+
dirs: [`src/features/${slug}`, 'src/lib', 'tests'],
|
|
1058
|
+
files: {
|
|
1059
|
+
[`src/features/${slug}/README.md`]: `# ${featureName}\n\nFeature scaffold generated from Winter plan apply.\n`,
|
|
1060
|
+
[`src/features/${slug}/tasks.md`]: '',
|
|
1061
|
+
},
|
|
1062
|
+
};
|
|
1063
|
+
|
|
1064
|
+
return {
|
|
1065
|
+
slug,
|
|
1066
|
+
family,
|
|
1067
|
+
dirs: [...common.dirs, ...spec.dirs],
|
|
1068
|
+
files: { ...common.files, ...spec.files },
|
|
1069
|
+
};
|
|
1070
|
+
}
|
|
1071
|
+
|
|
1072
|
+
async writeFileIfMissing(filePath, content) {
|
|
1073
|
+
try {
|
|
1074
|
+
await fs.writeFile(filePath, content, { encoding: 'utf8', flag: 'wx' });
|
|
1075
|
+
return true;
|
|
1076
|
+
} catch (error) {
|
|
1077
|
+
if (error?.code === 'EEXIST') return false;
|
|
1078
|
+
throw error;
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
async applyPlanProfileScaffold({ task, selected, workflow }) {
|
|
1083
|
+
const spec = this.getPlanScaffoldSpec(task, workflow);
|
|
1084
|
+
const created = [];
|
|
1085
|
+
const skipped = [];
|
|
1086
|
+
|
|
1087
|
+
for (const dir of spec.dirs) {
|
|
1088
|
+
const dirPath = path.join(this.projectPath, dir);
|
|
1089
|
+
await fs.mkdir(dirPath, { recursive: true });
|
|
1090
|
+
created.push(dir);
|
|
1091
|
+
}
|
|
1092
|
+
|
|
1093
|
+
const taskListContent = [
|
|
1094
|
+
`# ${selected.title}`,
|
|
1095
|
+
'',
|
|
1096
|
+
`- Task: ${task}`,
|
|
1097
|
+
`- Profile: ${workflow.profile}`,
|
|
1098
|
+
'',
|
|
1099
|
+
'## Steps',
|
|
1100
|
+
...selected.steps.map(step => `- [ ] ${step}`),
|
|
1101
|
+
'',
|
|
1102
|
+
].join('\n');
|
|
1103
|
+
|
|
1104
|
+
const files = { ...spec.files };
|
|
1105
|
+
for (const fileName of Object.keys(files)) {
|
|
1106
|
+
if (fileName.endsWith('/tasks.md')) {
|
|
1107
|
+
files[fileName] = taskListContent;
|
|
1108
|
+
}
|
|
1109
|
+
}
|
|
1110
|
+
|
|
1111
|
+
for (const [relativePath, content] of Object.entries(files)) {
|
|
1112
|
+
const filePath = path.join(this.projectPath, relativePath);
|
|
1113
|
+
await fs.mkdir(path.dirname(filePath), { recursive: true });
|
|
1114
|
+
const didCreate = await this.writeFileIfMissing(filePath, content);
|
|
1115
|
+
(didCreate ? created : skipped).push(relativePath);
|
|
1116
|
+
}
|
|
1117
|
+
|
|
1118
|
+
return { ...spec, created, skipped };
|
|
1119
|
+
}
|
|
1120
|
+
|
|
965
1121
|
async exportPlanArtifact({ task, workflow, selected, outputPath, format = 'md' }) {
|
|
966
1122
|
const normalizedFormat = format === 'json' ? 'json' : 'md';
|
|
967
1123
|
const finalPath = this.resolvePlanOutputPath(task, normalizedFormat, outputPath);
|
|
@@ -1004,6 +1160,7 @@ EXECUTION CONTRACT:
|
|
|
1004
1160
|
}
|
|
1005
1161
|
|
|
1006
1162
|
async applyPlanSkeleton({ task, selected, workflow, exportPath = null }) {
|
|
1163
|
+
const scaffold = await this.applyPlanProfileScaffold({ task, selected, workflow });
|
|
1007
1164
|
const targetPath = path.join(this.projectPath, '.winter', 'plan-task-list.md');
|
|
1008
1165
|
const skeleton = [
|
|
1009
1166
|
'# Plan Task List',
|
|
@@ -1012,10 +1169,15 @@ EXECUTION CONTRACT:
|
|
|
1012
1169
|
`- Profile: ${workflow.profile}`,
|
|
1013
1170
|
`- Plan: ${selected.title}`,
|
|
1014
1171
|
...(exportPath ? [`- Plan File: ${path.relative(this.projectPath, exportPath) || exportPath}`] : []),
|
|
1172
|
+
`- Scaffold Profile: ${scaffold.family}`,
|
|
1015
1173
|
'',
|
|
1016
1174
|
'## TODO',
|
|
1017
1175
|
...selected.steps.map(step => `- [ ] ${step}`),
|
|
1018
1176
|
'',
|
|
1177
|
+
'## Scaffold Created',
|
|
1178
|
+
...scaffold.created.map(item => `- ${item}`),
|
|
1179
|
+
...(scaffold.skipped.length ? ['', '## Existing Files Kept', ...scaffold.skipped.map(item => `- ${item}`)] : []),
|
|
1180
|
+
'',
|
|
1019
1181
|
].join('\n');
|
|
1020
1182
|
await fs.mkdir(path.dirname(targetPath), { recursive: true });
|
|
1021
1183
|
await fs.writeFile(targetPath, skeleton, 'utf8');
|
|
@@ -226,7 +226,7 @@ export class ContextLoader {
|
|
|
226
226
|
this.readTextIfExists(pageAgentAgentsPath, 2200),
|
|
227
227
|
]);
|
|
228
228
|
|
|
229
|
-
const hasRequired = Boolean(karpathy || agents || designReadme || designBrands.length > 0 || pageAgentWinter);
|
|
229
|
+
const hasRequired = Boolean(karpathy || agents || designReadme || designBrands.length > 0 || pageAgentWinter || pageAgentAgents);
|
|
230
230
|
if (!hasRequired) return '';
|
|
231
231
|
|
|
232
232
|
const lines = [];
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import readline from 'readline';
|
|
2
2
|
import { colors } from './snowflake-logo.js';
|
|
3
|
-
import { padVisible, renderBox, terminalWidth
|
|
3
|
+
import { drawInFixedArea, enableFixedPanel, moveToPromptRow, moveToScrollRegion, padVisible, renderBox, terminalWidth } from './terminal-ui.js';
|
|
4
|
+
import { buildTuiSnapshot, renderInputPanel } from './tui.js';
|
|
4
5
|
|
|
5
6
|
export class WinterInputController {
|
|
6
7
|
constructor(repl) {
|
|
@@ -11,9 +12,20 @@ export class WinterInputController {
|
|
|
11
12
|
const repl = this.repl;
|
|
12
13
|
if (!repl.running || repl.readlineClosed) return;
|
|
13
14
|
const panel = this.buildInputPanel();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
repl.
|
|
15
|
+
|
|
16
|
+
// Queue indicator
|
|
17
|
+
const queueCount = repl.taskQueue?.length || 0;
|
|
18
|
+
const queueTag = queueCount > 0
|
|
19
|
+
? ` ${colors.yellow}⧗ ${queueCount} pending${colors.reset}`
|
|
20
|
+
: '';
|
|
21
|
+
|
|
22
|
+
const lines = [panel.top + queueTag, panel.status, panel.hint].filter(l => l && l.trim() !== '');
|
|
23
|
+
process.stdout.write('\n' + lines.join('\n') + '\n');
|
|
24
|
+
|
|
25
|
+
if (typeof repl.rl?.setPrompt === 'function') {
|
|
26
|
+
repl.rl.setPrompt(panel.prompt);
|
|
27
|
+
}
|
|
28
|
+
repl.rl?.prompt?.();
|
|
17
29
|
}
|
|
18
30
|
|
|
19
31
|
closeInputBox() {
|
|
@@ -25,38 +37,10 @@ export class WinterInputController {
|
|
|
25
37
|
|
|
26
38
|
buildInputPanel() {
|
|
27
39
|
const repl = this.repl;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
const provider = repl.ai?.getActiveProvider?.() || 'provider';
|
|
33
|
-
const model = repl.ai?.providers?.[provider]?.model || 'model';
|
|
34
|
-
const sessionId = repl.session?.getSessionId?.()?.slice(0, 8) || 'session';
|
|
35
|
-
const projectName = repl.projectPath ? repl.projectPath.split(/[\\/]/).filter(Boolean).pop() : 'project';
|
|
36
|
-
const queueText = repl.taskQueue?.length > 0 ? `queue:${repl.taskQueue.length}` : 'ready';
|
|
37
|
-
const title = ' Winter CLI ';
|
|
38
|
-
const titleWidth = visibleWidth(title);
|
|
39
|
-
const topFill = Math.max(0, width - titleWidth);
|
|
40
|
-
const leftFill = Math.floor(topFill / 2);
|
|
41
|
-
const rightFill = topFill - leftFill;
|
|
42
|
-
const statusText = [
|
|
43
|
-
`model ${provider}/${model}`,
|
|
44
|
-
`project ${projectName}`,
|
|
45
|
-
`session ${sessionId}`,
|
|
46
|
-
queueText,
|
|
47
|
-
].join(' ');
|
|
48
|
-
const hintText = '@file @Agent task !cmd Ctrl+V image /context /doctor full';
|
|
49
|
-
const hintInnerWidth = Math.max(20, width - 2);
|
|
50
|
-
const status = `${colors.magenta}${box.vertical}${colors.reset} ${colors.dim}${padVisible(statusText, hintInnerWidth)}${colors.reset} ${colors.magenta}${box.vertical}${colors.reset}`;
|
|
51
|
-
const hint = `${colors.magenta}${box.vertical}${colors.reset} ${colors.dim}${padVisible(hintText, hintInnerWidth)}${colors.reset} ${colors.magenta}${box.vertical}${colors.reset}`;
|
|
52
|
-
const prompt = `${colors.magenta}${box.vertical}${colors.reset} ${colors.bright}${colors.cyan}winter${colors.reset}${colors.dim} > ${colors.reset}`;
|
|
53
|
-
return {
|
|
54
|
-
top: `${colors.magenta}${box.topLeft}${box.horizontal.repeat(leftFill)}${title}${box.horizontal.repeat(rightFill)}${box.topRight}${colors.reset}`,
|
|
55
|
-
status,
|
|
56
|
-
hint,
|
|
57
|
-
prompt,
|
|
58
|
-
bottom: `${colors.magenta}${box.bottomLeft}${box.horizontal.repeat(width)}${box.bottomRight}${colors.reset}`,
|
|
59
|
-
};
|
|
40
|
+
return renderInputPanel(buildTuiSnapshot(repl), {
|
|
41
|
+
colors,
|
|
42
|
+
width: terminalWidth(66, 124),
|
|
43
|
+
});
|
|
60
44
|
}
|
|
61
45
|
|
|
62
46
|
installSlashSuggestions() {
|
|
@@ -80,10 +64,24 @@ export class WinterInputController {
|
|
|
80
64
|
return;
|
|
81
65
|
}
|
|
82
66
|
|
|
83
|
-
if (key.name === 'escape'
|
|
84
|
-
repl.
|
|
85
|
-
|
|
86
|
-
|
|
67
|
+
if (key.name === 'escape') {
|
|
68
|
+
if (repl.isProcessing) {
|
|
69
|
+
// Cancel current AI turn
|
|
70
|
+
repl.isCancelled = true;
|
|
71
|
+
if (repl.spinner) repl.spinner.stop();
|
|
72
|
+
console.log(`\n${colors.red}[ Đã nhận lệnh HỦY... AI sẽ kết thúc ở thao tác tiếp theo ]${colors.reset}`);
|
|
73
|
+
} else {
|
|
74
|
+
// Double-ESC to end session
|
|
75
|
+
const now = Date.now();
|
|
76
|
+
if (this._lastEscTime && (now - this._lastEscTime) < 500) {
|
|
77
|
+
console.log(`\n\n${colors.cyan}Cảm ơn đã sử dụng Winter!${colors.reset}`);
|
|
78
|
+
console.log(`${colors.yellow}Tiếp tục phiên làm việc:${colors.reset}`);
|
|
79
|
+
console.log(`${colors.bright}${colors.green}winter --session ${repl.session?.getSessionId?.() || ''}${colors.reset}\n`);
|
|
80
|
+
process.exit(0);
|
|
81
|
+
}
|
|
82
|
+
this._lastEscTime = now;
|
|
83
|
+
console.log(`${colors.dim}Press ESC again to end session${colors.reset}`);
|
|
84
|
+
}
|
|
87
85
|
return;
|
|
88
86
|
}
|
|
89
87
|
|
|
@@ -116,13 +114,13 @@ export class WinterInputController {
|
|
|
116
114
|
|
|
117
115
|
repl.inputQueue = repl.inputQueue
|
|
118
116
|
.then(async () => {
|
|
119
|
-
|
|
117
|
+
repl.closeInputBox?.();
|
|
120
118
|
await this.processPastedImageTask(prompt, image);
|
|
121
119
|
})
|
|
122
120
|
.catch((error) => {
|
|
123
|
-
|
|
121
|
+
repl.closeInputBox?.();
|
|
124
122
|
console.log(`\n${colors.red}✖ Paste image error: ${error.message}${colors.reset}\n`);
|
|
125
|
-
if (repl.running && !repl.readlineClosed)
|
|
123
|
+
if (repl.running && !repl.readlineClosed) repl.showInputPrompt?.();
|
|
126
124
|
});
|
|
127
125
|
return true;
|
|
128
126
|
} finally {
|
|
@@ -134,15 +132,17 @@ export class WinterInputController {
|
|
|
134
132
|
const repl = this.repl;
|
|
135
133
|
repl.isProcessing = true;
|
|
136
134
|
repl.isCancelled = false;
|
|
135
|
+
repl.currentAbortController = new AbortController();
|
|
137
136
|
try {
|
|
138
137
|
await repl.chat(prompt, [image]);
|
|
139
138
|
} finally {
|
|
140
139
|
repl.isProcessing = false;
|
|
140
|
+
repl.currentAbortController = null;
|
|
141
141
|
if (repl.taskQueue.length > 0) {
|
|
142
142
|
const nextTask = repl.taskQueue.shift();
|
|
143
143
|
setTimeout(() => repl.processInputTask(nextTask), 0);
|
|
144
144
|
} else if (!repl.readlineClosed) {
|
|
145
|
-
|
|
145
|
+
repl.showInputPrompt?.();
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
}
|
|
@@ -247,12 +247,23 @@ export class WinterInputController {
|
|
|
247
247
|
|
|
248
248
|
this.clearSlashMenuRender();
|
|
249
249
|
|
|
250
|
+
const ASCII_BOX = {
|
|
251
|
+
topLeft: '+',
|
|
252
|
+
topRight: '+',
|
|
253
|
+
bottomLeft: '+',
|
|
254
|
+
bottomRight: '+',
|
|
255
|
+
horizontal: '-',
|
|
256
|
+
vertical: '|',
|
|
257
|
+
teeLeft: '+',
|
|
258
|
+
teeRight: '+',
|
|
259
|
+
};
|
|
250
260
|
const rendered = renderBox({
|
|
251
261
|
title: 'Command Palette',
|
|
252
262
|
width: terminalWidth(66, 110, 88),
|
|
253
263
|
borderColor: colors.magenta,
|
|
254
264
|
titleColor: colors.cyan,
|
|
255
265
|
body,
|
|
266
|
+
boxChars: ASCII_BOX,
|
|
256
267
|
});
|
|
257
268
|
|
|
258
269
|
process.stdout.write(`\n${rendered}\n`);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { formatRuntimeEnvironmentSummary, getRuntimeEnvironment } from './runtime-env.js';
|
|
2
|
+
import { getModelBudgetMultiplier } from '../ai/model-capabilities.js';
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* PromptBuilder — Builds system prompts for Winter CLI agents.
|
|
@@ -49,26 +50,32 @@ export class PromptBuilder {
|
|
|
49
50
|
const sessionContext = this.session?.getContext?.() || {};
|
|
50
51
|
const environmentSummary = this.tools?.getRuntimeEnvironmentSummary?.() || this._defaultEnvironmentSummary();
|
|
51
52
|
const requiredLocalResources = this.getRequiredLocalResources();
|
|
52
|
-
const
|
|
53
|
-
const
|
|
53
|
+
const scale = getModelBudgetMultiplier(options.modelTier);
|
|
54
|
+
const projectContextBudget = options.projectContextBudget || Math.round(3200 * scale);
|
|
55
|
+
const compactSystemPrompt = options.compactSystemPrompt ?? (scale <= 0.75);
|
|
56
|
+
const memoryBudget = Math.round(1200 * scale);
|
|
57
|
+
const planBudget = Math.round(1200 * scale);
|
|
58
|
+
const requiredResourcesBudget = Math.round((compactSystemPrompt ? 1200 : 1600) * scale);
|
|
59
|
+
const workflowBudget = Math.round(900 * scale);
|
|
60
|
+
const blueprintBudget = Math.round(700 * scale);
|
|
54
61
|
|
|
55
62
|
const memoryStr = memories.length > 0
|
|
56
|
-
? this._formatMemories(memories)
|
|
63
|
+
? this._formatMemories(memories, { maxTotalChars: memoryBudget })
|
|
57
64
|
: '';
|
|
58
65
|
const requiredResourcesStr = requiredLocalResources
|
|
59
|
-
? `\n## Required Local Resource Rules\n${this._compactText(requiredLocalResources,
|
|
66
|
+
? `\n## Required Local Resource Rules\n${this._compactText(requiredLocalResources, requiredResourcesBudget, 'required local resources')}`
|
|
60
67
|
: '';
|
|
61
68
|
const plansStr = plans.length > 0
|
|
62
|
-
? this._formatPlans(plans)
|
|
69
|
+
? this._formatPlans(plans, { maxTotalChars: planBudget })
|
|
63
70
|
: '';
|
|
64
71
|
const skillsStr = Array.isArray(sessionContext.activeSkills) && sessionContext.activeSkills.length > 0
|
|
65
72
|
? `\n## Auto-applied Skills\n${sessionContext.activeSkills.slice(0, 12).map(skill => `- ${skill}`).join('\n')}${sessionContext.activeSkills.length > 12 ? '\n- ...' : ''}`
|
|
66
73
|
: '';
|
|
67
74
|
const workflowStr = sessionContext.workflowHints
|
|
68
|
-
? `\n## Workflow Auto-Selection\n${this._compactText(sessionContext.workflowHints,
|
|
75
|
+
? `\n## Workflow Auto-Selection\n${this._compactText(sessionContext.workflowHints, workflowBudget, 'workflow hints')}`
|
|
69
76
|
: '';
|
|
70
77
|
const blueprintStr = sessionContext.workflowBlueprint
|
|
71
|
-
? `\n## Profile Blueprint\n${this._compactText(sessionContext.workflowBlueprint,
|
|
78
|
+
? `\n## Profile Blueprint\n${this._compactText(sessionContext.workflowBlueprint, blueprintBudget, 'workflow blueprint')}`
|
|
72
79
|
: '';
|
|
73
80
|
const startupPlanStr = sessionContext.bootstrapPlan?.title
|
|
74
81
|
? `\n## Startup Plan\n- ${sessionContext.bootstrapPlan.title}: ${sessionContext.bootstrapPlan.description}`
|
|
@@ -164,12 +171,14 @@ export class PromptBuilder {
|
|
|
164
171
|
const plans = this.session?.getPlans?.() || [];
|
|
165
172
|
const sessionContext = this.session?.getContext?.() || {};
|
|
166
173
|
const requiredLocalResources = this.getRequiredLocalResources();
|
|
174
|
+
const scale = getModelBudgetMultiplier(this.ai?._modelTier || '');
|
|
175
|
+
const projectContextBudget = Math.round(3200 * (scale || 1));
|
|
167
176
|
|
|
168
|
-
const memoryStr = memories.length > 0 ? this._formatMemories(memories, { maxTotalChars: 900 }) : '';
|
|
177
|
+
const memoryStr = memories.length > 0 ? this._formatMemories(memories, { maxTotalChars: Math.round(900 * (scale || 1)) }) : '';
|
|
169
178
|
const requiredResourcesStr = requiredLocalResources
|
|
170
|
-
? `\n## Required Local Resource Rules\n${this._compactText(requiredLocalResources, 1600, 'required local resources')}`
|
|
179
|
+
? `\n## Required Local Resource Rules\n${this._compactText(requiredLocalResources, Math.round(1600 * (scale || 1)), 'required local resources')}`
|
|
171
180
|
: '';
|
|
172
|
-
const plansStr = plans.length > 0 ? this._formatPlans(plans, { maxTotalChars: 900 }) : '';
|
|
181
|
+
const plansStr = plans.length > 0 ? this._formatPlans(plans, { maxTotalChars: Math.round(900 * (scale || 1)) }) : '';
|
|
173
182
|
const skillsStr = Array.isArray(sessionContext.activeSkills) && sessionContext.activeSkills.length > 0
|
|
174
183
|
? `\n## Auto-applied Skills\n${sessionContext.activeSkills.slice(0, 12).map(skill => `- ${skill}`).join('\n')}${sessionContext.activeSkills.length > 12 ? '\n- ...' : ''}`
|
|
175
184
|
: '';
|
|
@@ -217,7 +226,7 @@ export class PromptBuilder {
|
|
|
217
226
|
`Working directory: ${this.projectPath}`,
|
|
218
227
|
`Current session: ${this.session?.getSessionId?.()?.substring(0, 8) || 'unknown'}`,
|
|
219
228
|
`${requiredResourcesStr}${memoryStr}${plansStr}${skillsStr}${startupPlanStr}`,
|
|
220
|
-
context ? `\n## Project Context\n${this._compactText(context,
|
|
229
|
+
context ? `\n## Project Context\n${this._compactText(context, projectContextBudget, 'project context')}` : '',
|
|
221
230
|
].join('\n');
|
|
222
231
|
}
|
|
223
232
|
|
package/src/cli/repl-commands.js
CHANGED