winter-super-cli 2026.5.24 → 2026.5.26
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 +1 -1
- package/WINTER.md +6 -0
- package/bin/winter.js +77 -220
- package/package.json +1 -1
- package/resources/local/manifest.json +60 -57
- package/src/ai/providers.js +64 -13
- package/src/ai/providers.test.js +35 -0
- package/src/cli/commands.js +61 -3
- package/src/cli/commands.test.js +179 -0
- package/src/cli/config.js +12 -0
- package/src/cli/repl.js +475 -150
- package/src/cli/repl.test.js +234 -2
- package/src/cli/snowflake-logo.js +15 -7
- package/src/cli/terminal-ui.js +125 -0
- package/src/cli/terminal-ui.test.js +33 -0
- package/src/plugins/manager.js +3 -1
- package/src/session/manager.js +44 -0
- package/src/session/manager.test.js +72 -0
- package/src/tools/executor.js +1 -1
- package/src/tools/executor.test.js +110 -0
- package/resources/local/claude/settings.json +0 -33
- package/resources/local/claude/todos/022bdc3c-e2c0-4a20-a74f-b348ed022c75-agent-022bdc3c-e2c0-4a20-a74f-b348ed022c75.json +0 -1
- package/resources/local/claude/todos/316f0e7d-5512-49fa-8c7f-edc75b777612-agent-316f0e7d-5512-49fa-8c7f-edc75b777612.json +0 -1
- package/resources/local/claude/todos/3676dc17-fca1-4692-934b-ce35e1965af6-agent-3676dc17-fca1-4692-934b-ce35e1965af6.json +0 -1
- package/resources/local/claude/todos/464493de-7f2a-45cf-93e8-ad73214afa10-agent-464493de-7f2a-45cf-93e8-ad73214afa10.json +0 -1
- package/resources/local/claude/todos/51f2e7a7-3f31-4692-a9b2-d3f3906aafea-agent-51f2e7a7-3f31-4692-a9b2-d3f3906aafea.json +0 -1
- package/resources/local/claude/todos/64a67dce-3d62-4a98-a548-b9c91a8e87e8-agent-64a67dce-3d62-4a98-a548-b9c91a8e87e8.json +0 -1
- package/resources/local/claude/todos/727a06e6-0ac2-41ca-8b81-2c14e4d40182-agent-727a06e6-0ac2-41ca-8b81-2c14e4d40182.json +0 -1
- package/resources/local/claude/todos/7d34d296-9b5a-4525-9b68-600d2ae20b59-agent-7d34d296-9b5a-4525-9b68-600d2ae20b59.json +0 -1
- package/resources/local/claude/todos/8c0606f1-5bcc-4176-8125-c5174fd69002-agent-8c0606f1-5bcc-4176-8125-c5174fd69002.json +0 -1
- package/resources/local/claude/todos/905aab16-5225-43f6-8ae4-c94491fd3a6f-agent-905aab16-5225-43f6-8ae4-c94491fd3a6f.json +0 -1
- package/resources/local/claude/todos/9dbe93f0-d62c-4c12-b4eb-0eecc437d625-agent-9dbe93f0-d62c-4c12-b4eb-0eecc437d625.json +0 -1
- package/resources/local/claude/todos/ad48500f-02a5-4f18-970b-82fb595d171f-agent-ad48500f-02a5-4f18-970b-82fb595d171f.json +0 -1
- package/resources/local/claude/todos/af86ea71-9907-4066-907c-68055e6c0081-agent-af86ea71-9907-4066-907c-68055e6c0081.json +0 -1
- package/resources/local/claude/todos/dbb0dc16-5d71-4f1d-a56c-db0741b3d485-agent-dbb0dc16-5d71-4f1d-a56c-db0741b3d485.json +0 -1
- package/resources/local/claude/todos/ff1ac487-eb0f-4c63-9360-fbb0a81bb5ae-agent-ff1ac487-eb0f-4c63-9360-fbb0a81bb5ae.json +0 -1
- package/resources/local/codex/config.toml +0 -84
- package/resources/local/codex/memories/MEMORY.md +0 -972
- package/resources/local/codex/memories/extensions/ad_hoc/instructions.md +0 -13
- package/resources/local/codex/memories/memory_summary.md +0 -188
- package/resources/local/codex/memories/raw_memories.md +0 -1488
- package/resources/local/codex/memories/rollout_summaries/2026-03-27T04-05-14-Iirb-nsis_full_installer_build_cpp_ocr_translator.md +0 -46
- package/resources/local/codex/memories/rollout_summaries/2026-03-28T06-18-17-Si3U-my_translator_overlay_lockfix_portable_nsis.md +0 -112
- package/resources/local/codex/memories/rollout_summaries/2026-04-15T06-42-11-2JMi-qelasy_timeout_and_watch_control_stability.md +0 -90
- package/resources/local/codex/memories/rollout_summaries/2026-04-16T03-12-59-z6Wi-request_all_row_click_detail_navigation.md +0 -42
- package/resources/local/codex/memories/rollout_summaries/2026-04-17T05-49-03-tNBk-my_translator_project_readability_audio_latency_clear_button.md +0 -75
- package/resources/local/codex/memories/rollout_summaries/2026-04-21T04-05-04-EXnh-nsis_packaging_harfbuzz_dll_qml_runtime_debug.md +0 -108
- package/resources/local/codex/memories/rollout_summaries/2026-04-22T03-48-40-VnNG-openclaw_opencode_sync_and_runtime_repair.md +0 -86
- package/resources/local/codex/memories/rollout_summaries/2026-04-22T06-49-49-R8yZ-web_book_user_portal_and_lint_fixes.md +0 -82
- package/resources/local/codex/memories/rollout_summaries/2026-04-22T06-50-35-ZaS1-smoke_admin_rbac_refund_connection_refused.md +0 -35
- package/resources/local/codex/memories/rollout_summaries/2026-04-22T11-05-04-aotT-nextjs_build_fix_statswidget_leaflet_ssr.md +0 -78
- package/resources/local/codex/memories/rollout_summaries/2026-04-23T03-22-24-a5q4-ui_still_looks_cloudflare_only.md +0 -41
- package/resources/local/codex/memories/rollout_summaries/2026-04-23T04-35-47-amlb-bayre247_hero_slide_above_search_form.md +0 -49
- package/resources/local/codex/memories/rollout_summaries/2026-04-23T04-59-21-lZWv-ocr_backend_parity_easyocr_tesseract_paddle_fallback.md +0 -92
- package/resources/local/codex/memories/rollout_summaries/2026-04-23T07-36-22-tPuo-request_workflow_editor_drag_edge_smaller_arrows_roadmap.md +0 -72
- package/resources/local/codex/memories/rollout_summaries/2026-04-24T08-01-05-Gb3B-checkin_shifts_workdays_assignments_and_checkout_overhaul.md +0 -90
- package/resources/local/codex/memories/rollout_summaries/2026-04-25T03-39-02-mbDr-web_book_refund_admin_popup_pagination_responsiveness.md +0 -151
- package/resources/local/codex/memories/rollout_summaries/2026-04-25T09-20-30-4usS-tool_scv_9router_custom_provider_and_paddle_ocr.md +0 -130
- package/resources/local/codex/memories/rollout_summaries/2026-05-06T10-19-38-mt2X-find_db_config_in_web_book_app_env.md +0 -40
- package/resources/local/codex/memories/rollout_summaries/2026-05-06T11-10-23-TkwP-goirong_backend_title_crash_and_client_audio_tcp_tunnel_debu.md +0 -85
- package/resources/local/codex/memories/rollout_summaries/2026-05-09T07-52-18-On1F-chakra_git_cleanup_readme_bilingual_publish_config.md +0 -88
- package/resources/local/codex/memories/rollout_summaries/2026-05-11T08-05-34-oMEl-check_crack_gui_logo_onefile_build.md +0 -68
- package/resources/local/codex/memories/skills/windows-packaged-app-smoke-check/SKILL.md +0 -72
package/src/cli/commands.js
CHANGED
|
@@ -10,6 +10,25 @@ import { DesignCommands } from '../design/commands.js';
|
|
|
10
10
|
import { SkillManager } from '../skills/manager.js';
|
|
11
11
|
import { PluginManager } from '../plugins/manager.js';
|
|
12
12
|
|
|
13
|
+
const SECRET_KEY_PATTERN = /(api[-_]?key|auth[-_]?token|access[-_]?token|refresh[-_]?token|secret|password)/i;
|
|
14
|
+
|
|
15
|
+
export function redactSecrets(value) {
|
|
16
|
+
if (Array.isArray(value)) {
|
|
17
|
+
return value.map(item => redactSecrets(item));
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
if (!value || typeof value !== 'object') {
|
|
21
|
+
return value;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return Object.fromEntries(
|
|
25
|
+
Object.entries(value).map(([key, entry]) => [
|
|
26
|
+
key,
|
|
27
|
+
SECRET_KEY_PATTERN.test(key) ? '[redacted]' : redactSecrets(entry),
|
|
28
|
+
])
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
13
32
|
export class CommandParser {
|
|
14
33
|
constructor({ session, ai, config }) {
|
|
15
34
|
this.session = session;
|
|
@@ -67,6 +86,8 @@ export class CommandParser {
|
|
|
67
86
|
'/memories': () => this.showMemories(),
|
|
68
87
|
'/plans': () => this.showPlans(),
|
|
69
88
|
'/cache': () => this.handleCache(args),
|
|
89
|
+
'/provider': () => this.handleProvider(args),
|
|
90
|
+
'/providers': () => this.showProviders(),
|
|
70
91
|
'/exit': () => process.exit(0),
|
|
71
92
|
};
|
|
72
93
|
|
|
@@ -177,6 +198,7 @@ export class CommandParser {
|
|
|
177
198
|
const [action, ...rest] = args;
|
|
178
199
|
|
|
179
200
|
switch (action) {
|
|
201
|
+
case undefined:
|
|
180
202
|
case 'list':
|
|
181
203
|
const skills = await this.skills.listSkills();
|
|
182
204
|
console.log(`\n${colors.cyan}Available Skills:${colors.reset}`);
|
|
@@ -199,16 +221,17 @@ export class CommandParser {
|
|
|
199
221
|
const [action, ...rest] = args;
|
|
200
222
|
|
|
201
223
|
switch (action) {
|
|
224
|
+
case undefined:
|
|
202
225
|
case 'list':
|
|
203
226
|
const plugins = await this.plugins.listPlugins();
|
|
204
227
|
console.log(`\n${colors.cyan}Installed Plugins:${colors.reset}`);
|
|
205
228
|
plugins.forEach(p => console.log(` ${p.icon} ${p.name} v${p.version}`));
|
|
206
229
|
break;
|
|
207
230
|
case 'install':
|
|
208
|
-
|
|
231
|
+
await this.plugins.installPlugin(rest[0]);
|
|
209
232
|
break;
|
|
210
233
|
case 'remove':
|
|
211
|
-
|
|
234
|
+
await this.plugins.removePlugin(rest[0]);
|
|
212
235
|
break;
|
|
213
236
|
default:
|
|
214
237
|
console.log(`${colors.yellow}Usage: winter plugin <list|install|remove>${colors.reset}`);
|
|
@@ -231,7 +254,7 @@ export class CommandParser {
|
|
|
231
254
|
async handleConfig(args) {
|
|
232
255
|
const config = await this.config.load();
|
|
233
256
|
console.log(`\n${colors.cyan}Current Configuration:${colors.reset}`);
|
|
234
|
-
console.log(JSON.stringify(config, null, 2));
|
|
257
|
+
console.log(JSON.stringify(redactSecrets(config), null, 2));
|
|
235
258
|
}
|
|
236
259
|
|
|
237
260
|
async handleInit(args) {
|
|
@@ -259,6 +282,41 @@ export class CommandParser {
|
|
|
259
282
|
}
|
|
260
283
|
}
|
|
261
284
|
|
|
285
|
+
async handleProvider(args) {
|
|
286
|
+
const providerName = args[0]?.trim().toLowerCase();
|
|
287
|
+
|
|
288
|
+
if (!providerName) {
|
|
289
|
+
await this.ai.init?.();
|
|
290
|
+
console.log(`${colors.cyan}Provider: ${this.ai.getActiveProvider()}${colors.reset}`);
|
|
291
|
+
return;
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
const switched = typeof this.ai.switchProvider === 'function'
|
|
295
|
+
? await this.ai.switchProvider(providerName)
|
|
296
|
+
: (this.ai.setProvider(providerName) ? providerName : null);
|
|
297
|
+
|
|
298
|
+
if (switched) {
|
|
299
|
+
await this.config.setDefaultProvider(switched);
|
|
300
|
+
console.log(`${statusIcons.success} Provider: ${switched}`);
|
|
301
|
+
return;
|
|
302
|
+
}
|
|
303
|
+
|
|
304
|
+
const available = this.ai.listProviders?.().map(p => p.name).join(', ') || 'none';
|
|
305
|
+
console.log(`${colors.red}${statusIcons.error} Unknown provider: ${providerName}${colors.reset}`);
|
|
306
|
+
console.log(`${colors.dim}Available providers: ${available}${colors.reset}`);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
async showProviders() {
|
|
310
|
+
await this.ai.init?.();
|
|
311
|
+
const providers = this.ai.listProviders?.() || [];
|
|
312
|
+
console.log(`\n${colors.cyan}Providers:${colors.reset}`);
|
|
313
|
+
providers.forEach(p => {
|
|
314
|
+
const status = p.ready ? statusIcons.online : statusIcons.offline;
|
|
315
|
+
const active = p.name === this.ai.getActiveProvider?.() ? ` ${colors.green}< active${colors.reset}` : '';
|
|
316
|
+
console.log(` ${status} ${p.name} (${p.model})${active}`);
|
|
317
|
+
});
|
|
318
|
+
}
|
|
319
|
+
|
|
262
320
|
async showMemories() {
|
|
263
321
|
const memories = this.session.getMemory();
|
|
264
322
|
console.log(`\n${colors.cyan}Memories:${colors.reset}`);
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import test from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import { mkdtemp, mkdir, writeFile } from 'node:fs/promises';
|
|
4
|
+
import { tmpdir } from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
|
|
7
|
+
import { CommandParser, redactSecrets } from './commands.js';
|
|
8
|
+
import { PluginManager } from '../plugins/manager.js';
|
|
9
|
+
|
|
10
|
+
function createParser() {
|
|
11
|
+
const session = {
|
|
12
|
+
getSessionId: () => '12345678-1234-1234-1234-123456789abc',
|
|
13
|
+
updateContext: async () => {},
|
|
14
|
+
addToMemory: async () => {},
|
|
15
|
+
getMemory: () => [],
|
|
16
|
+
getPlans: () => [],
|
|
17
|
+
newSession: async () => ({ id: '12345678-1234-1234-1234-123456789abc' }),
|
|
18
|
+
saveSession: async () => {},
|
|
19
|
+
listSessions: async () => [],
|
|
20
|
+
switchSession: async () => true,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const ai = {
|
|
24
|
+
chat: async () => ({ content: 'ok' }),
|
|
25
|
+
callAllProviders: async () => ({}),
|
|
26
|
+
clearCache: () => {},
|
|
27
|
+
getCacheStats: () => ({ size: 0, activeProvider: 'ollama' }),
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const config = { load: async () => ({}) };
|
|
31
|
+
const parser = new CommandParser({ session, ai, config });
|
|
32
|
+
|
|
33
|
+
parser.skills = {
|
|
34
|
+
listSkills: async () => [
|
|
35
|
+
{ icon: '💻', name: 'coding', description: 'Code analysis, generation, and review' },
|
|
36
|
+
],
|
|
37
|
+
enableSkill: async () => true,
|
|
38
|
+
createSkill: async () => {},
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
parser.plugins = {
|
|
42
|
+
listPlugins: async () => [
|
|
43
|
+
{ icon: '❄️', name: 'winter-core', version: '1.0.0' },
|
|
44
|
+
],
|
|
45
|
+
installPlugin: async () => {},
|
|
46
|
+
removePlugin: async () => {},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
return parser;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
test('config output redacts provider secrets recursively', async () => {
|
|
53
|
+
const config = {
|
|
54
|
+
defaultProvider: 'custom',
|
|
55
|
+
custom: {
|
|
56
|
+
baseURL: 'http://localhost:4000/v1',
|
|
57
|
+
apiKey: 'sk-live-secret',
|
|
58
|
+
nested: {
|
|
59
|
+
authToken: 'npm-secret-token',
|
|
60
|
+
},
|
|
61
|
+
},
|
|
62
|
+
project: {
|
|
63
|
+
current: 'E:\\dev\\app\\winter',
|
|
64
|
+
},
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
const redacted = redactSecrets(config);
|
|
68
|
+
|
|
69
|
+
assert.equal(redacted.custom.apiKey, '[redacted]');
|
|
70
|
+
assert.equal(redacted.custom.nested.authToken, '[redacted]');
|
|
71
|
+
assert.equal(redacted.custom.baseURL, config.custom.baseURL);
|
|
72
|
+
assert.equal(redacted.project.current, config.project.current);
|
|
73
|
+
assert.equal(config.custom.apiKey, 'sk-live-secret');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('skill and plugin commands default to list output', async () => {
|
|
77
|
+
const parser = createParser();
|
|
78
|
+
const logs = [];
|
|
79
|
+
const originalLog = console.log;
|
|
80
|
+
|
|
81
|
+
console.log = (...args) => {
|
|
82
|
+
logs.push(args.join(' '));
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
await parser.parse(['skill']);
|
|
87
|
+
await parser.parse(['plugin']);
|
|
88
|
+
} finally {
|
|
89
|
+
console.log = originalLog;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
assert(logs.some(line => line.includes('Available Skills')));
|
|
93
|
+
assert(logs.some(line => line.includes('Installed Plugins')));
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('slash provider command switches and persists provider', async () => {
|
|
97
|
+
const session = {
|
|
98
|
+
getSessionId: () => '12345678-1234-1234-1234-123456789abc',
|
|
99
|
+
addToMemory: async () => {},
|
|
100
|
+
getMemory: () => [],
|
|
101
|
+
getPlans: () => [],
|
|
102
|
+
};
|
|
103
|
+
const saved = [];
|
|
104
|
+
const ai = {
|
|
105
|
+
active: 'ollama',
|
|
106
|
+
providers: {
|
|
107
|
+
custom: { model: 'custom-model', ready: true },
|
|
108
|
+
ollama: { model: 'llama3', ready: true },
|
|
109
|
+
},
|
|
110
|
+
async switchProvider(name) {
|
|
111
|
+
if (!this.providers[name]) return null;
|
|
112
|
+
this.active = name;
|
|
113
|
+
return name;
|
|
114
|
+
},
|
|
115
|
+
getActiveProvider() {
|
|
116
|
+
return this.active;
|
|
117
|
+
},
|
|
118
|
+
listProviders() {
|
|
119
|
+
return Object.entries(this.providers).map(([name, provider]) => ({ name, ...provider }));
|
|
120
|
+
},
|
|
121
|
+
};
|
|
122
|
+
const parser = new CommandParser({
|
|
123
|
+
session,
|
|
124
|
+
ai,
|
|
125
|
+
config: {
|
|
126
|
+
setDefaultProvider: async provider => saved.push(provider),
|
|
127
|
+
},
|
|
128
|
+
});
|
|
129
|
+
|
|
130
|
+
await parser.parse(['/provider', 'custom']);
|
|
131
|
+
|
|
132
|
+
assert.equal(ai.getActiveProvider(), 'custom');
|
|
133
|
+
assert.deepEqual(saved, ['custom']);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
test('slash providers command lists available providers', async () => {
|
|
137
|
+
const parser = createParser();
|
|
138
|
+
parser.ai = {
|
|
139
|
+
init: async () => {},
|
|
140
|
+
getActiveProvider: () => 'custom',
|
|
141
|
+
listProviders: () => [
|
|
142
|
+
{ name: 'custom', ready: true, model: 'custom-model' },
|
|
143
|
+
],
|
|
144
|
+
};
|
|
145
|
+
const logs = [];
|
|
146
|
+
const originalLog = console.log;
|
|
147
|
+
|
|
148
|
+
console.log = (...args) => logs.push(args.join(' '));
|
|
149
|
+
try {
|
|
150
|
+
await parser.parse(['/providers']);
|
|
151
|
+
} finally {
|
|
152
|
+
console.log = originalLog;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
assert(logs.some(line => line.includes('custom')));
|
|
156
|
+
assert(logs.some(line => line.includes('custom-model')));
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('plugin manager loads local plugin files via file URLs', async () => {
|
|
160
|
+
const root = await mkdtemp(path.join(tmpdir(), 'winter-plugin-load-'));
|
|
161
|
+
const pluginsDir = path.join(root, '.winter', 'plugins');
|
|
162
|
+
await mkdir(pluginsDir, { recursive: true });
|
|
163
|
+
await writeFile(
|
|
164
|
+
path.join(pluginsDir, 'example.js'),
|
|
165
|
+
'export default { name: "example", version: "2.0.0", icon: "✨", description: "Example plugin" };\n'
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
const manager = new PluginManager({
|
|
169
|
+
addToMemory: async () => {},
|
|
170
|
+
});
|
|
171
|
+
manager.pluginsDir = pluginsDir;
|
|
172
|
+
|
|
173
|
+
const plugins = await manager.listPlugins();
|
|
174
|
+
const loaded = plugins.find(plugin => plugin.name === 'example');
|
|
175
|
+
|
|
176
|
+
assert(loaded);
|
|
177
|
+
assert.equal(loaded.version, '2.0.0');
|
|
178
|
+
assert.equal(loaded.icon, '✨');
|
|
179
|
+
});
|
package/src/cli/config.js
CHANGED
|
@@ -27,6 +27,10 @@ export class ConfigLoader {
|
|
|
27
27
|
getDefaults() {
|
|
28
28
|
return {
|
|
29
29
|
defaultProvider: 'ollama',
|
|
30
|
+
project: {
|
|
31
|
+
current: '',
|
|
32
|
+
lastOpenedAt: '',
|
|
33
|
+
},
|
|
30
34
|
anthropic: {
|
|
31
35
|
apiKey: '',
|
|
32
36
|
model: 'claude-sonnet-4-20250514',
|
|
@@ -91,4 +95,12 @@ export class ConfigLoader {
|
|
|
91
95
|
config[provider].model = model;
|
|
92
96
|
await this.save(config);
|
|
93
97
|
}
|
|
98
|
+
|
|
99
|
+
async setProjectCurrent(projectPath) {
|
|
100
|
+
const config = await this.load();
|
|
101
|
+
config.project = config.project || {};
|
|
102
|
+
config.project.current = projectPath;
|
|
103
|
+
config.project.lastOpenedAt = new Date().toISOString();
|
|
104
|
+
await this.save(config);
|
|
105
|
+
}
|
|
94
106
|
}
|