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.
Files changed (63) hide show
  1. package/README.md +1 -1
  2. package/WINTER.md +6 -0
  3. package/bin/winter.js +77 -220
  4. package/package.json +1 -1
  5. package/resources/local/manifest.json +60 -57
  6. package/src/ai/providers.js +64 -13
  7. package/src/ai/providers.test.js +35 -0
  8. package/src/cli/commands.js +61 -3
  9. package/src/cli/commands.test.js +179 -0
  10. package/src/cli/config.js +12 -0
  11. package/src/cli/repl.js +475 -150
  12. package/src/cli/repl.test.js +234 -2
  13. package/src/cli/snowflake-logo.js +15 -7
  14. package/src/cli/terminal-ui.js +125 -0
  15. package/src/cli/terminal-ui.test.js +33 -0
  16. package/src/plugins/manager.js +3 -1
  17. package/src/session/manager.js +44 -0
  18. package/src/session/manager.test.js +72 -0
  19. package/src/tools/executor.js +1 -1
  20. package/src/tools/executor.test.js +110 -0
  21. package/resources/local/claude/settings.json +0 -33
  22. package/resources/local/claude/todos/022bdc3c-e2c0-4a20-a74f-b348ed022c75-agent-022bdc3c-e2c0-4a20-a74f-b348ed022c75.json +0 -1
  23. package/resources/local/claude/todos/316f0e7d-5512-49fa-8c7f-edc75b777612-agent-316f0e7d-5512-49fa-8c7f-edc75b777612.json +0 -1
  24. package/resources/local/claude/todos/3676dc17-fca1-4692-934b-ce35e1965af6-agent-3676dc17-fca1-4692-934b-ce35e1965af6.json +0 -1
  25. package/resources/local/claude/todos/464493de-7f2a-45cf-93e8-ad73214afa10-agent-464493de-7f2a-45cf-93e8-ad73214afa10.json +0 -1
  26. package/resources/local/claude/todos/51f2e7a7-3f31-4692-a9b2-d3f3906aafea-agent-51f2e7a7-3f31-4692-a9b2-d3f3906aafea.json +0 -1
  27. package/resources/local/claude/todos/64a67dce-3d62-4a98-a548-b9c91a8e87e8-agent-64a67dce-3d62-4a98-a548-b9c91a8e87e8.json +0 -1
  28. package/resources/local/claude/todos/727a06e6-0ac2-41ca-8b81-2c14e4d40182-agent-727a06e6-0ac2-41ca-8b81-2c14e4d40182.json +0 -1
  29. package/resources/local/claude/todos/7d34d296-9b5a-4525-9b68-600d2ae20b59-agent-7d34d296-9b5a-4525-9b68-600d2ae20b59.json +0 -1
  30. package/resources/local/claude/todos/8c0606f1-5bcc-4176-8125-c5174fd69002-agent-8c0606f1-5bcc-4176-8125-c5174fd69002.json +0 -1
  31. package/resources/local/claude/todos/905aab16-5225-43f6-8ae4-c94491fd3a6f-agent-905aab16-5225-43f6-8ae4-c94491fd3a6f.json +0 -1
  32. package/resources/local/claude/todos/9dbe93f0-d62c-4c12-b4eb-0eecc437d625-agent-9dbe93f0-d62c-4c12-b4eb-0eecc437d625.json +0 -1
  33. package/resources/local/claude/todos/ad48500f-02a5-4f18-970b-82fb595d171f-agent-ad48500f-02a5-4f18-970b-82fb595d171f.json +0 -1
  34. package/resources/local/claude/todos/af86ea71-9907-4066-907c-68055e6c0081-agent-af86ea71-9907-4066-907c-68055e6c0081.json +0 -1
  35. package/resources/local/claude/todos/dbb0dc16-5d71-4f1d-a56c-db0741b3d485-agent-dbb0dc16-5d71-4f1d-a56c-db0741b3d485.json +0 -1
  36. package/resources/local/claude/todos/ff1ac487-eb0f-4c63-9360-fbb0a81bb5ae-agent-ff1ac487-eb0f-4c63-9360-fbb0a81bb5ae.json +0 -1
  37. package/resources/local/codex/config.toml +0 -84
  38. package/resources/local/codex/memories/MEMORY.md +0 -972
  39. package/resources/local/codex/memories/extensions/ad_hoc/instructions.md +0 -13
  40. package/resources/local/codex/memories/memory_summary.md +0 -188
  41. package/resources/local/codex/memories/raw_memories.md +0 -1488
  42. package/resources/local/codex/memories/rollout_summaries/2026-03-27T04-05-14-Iirb-nsis_full_installer_build_cpp_ocr_translator.md +0 -46
  43. package/resources/local/codex/memories/rollout_summaries/2026-03-28T06-18-17-Si3U-my_translator_overlay_lockfix_portable_nsis.md +0 -112
  44. package/resources/local/codex/memories/rollout_summaries/2026-04-15T06-42-11-2JMi-qelasy_timeout_and_watch_control_stability.md +0 -90
  45. package/resources/local/codex/memories/rollout_summaries/2026-04-16T03-12-59-z6Wi-request_all_row_click_detail_navigation.md +0 -42
  46. package/resources/local/codex/memories/rollout_summaries/2026-04-17T05-49-03-tNBk-my_translator_project_readability_audio_latency_clear_button.md +0 -75
  47. package/resources/local/codex/memories/rollout_summaries/2026-04-21T04-05-04-EXnh-nsis_packaging_harfbuzz_dll_qml_runtime_debug.md +0 -108
  48. package/resources/local/codex/memories/rollout_summaries/2026-04-22T03-48-40-VnNG-openclaw_opencode_sync_and_runtime_repair.md +0 -86
  49. package/resources/local/codex/memories/rollout_summaries/2026-04-22T06-49-49-R8yZ-web_book_user_portal_and_lint_fixes.md +0 -82
  50. package/resources/local/codex/memories/rollout_summaries/2026-04-22T06-50-35-ZaS1-smoke_admin_rbac_refund_connection_refused.md +0 -35
  51. package/resources/local/codex/memories/rollout_summaries/2026-04-22T11-05-04-aotT-nextjs_build_fix_statswidget_leaflet_ssr.md +0 -78
  52. package/resources/local/codex/memories/rollout_summaries/2026-04-23T03-22-24-a5q4-ui_still_looks_cloudflare_only.md +0 -41
  53. package/resources/local/codex/memories/rollout_summaries/2026-04-23T04-35-47-amlb-bayre247_hero_slide_above_search_form.md +0 -49
  54. package/resources/local/codex/memories/rollout_summaries/2026-04-23T04-59-21-lZWv-ocr_backend_parity_easyocr_tesseract_paddle_fallback.md +0 -92
  55. package/resources/local/codex/memories/rollout_summaries/2026-04-23T07-36-22-tPuo-request_workflow_editor_drag_edge_smaller_arrows_roadmap.md +0 -72
  56. package/resources/local/codex/memories/rollout_summaries/2026-04-24T08-01-05-Gb3B-checkin_shifts_workdays_assignments_and_checkout_overhaul.md +0 -90
  57. package/resources/local/codex/memories/rollout_summaries/2026-04-25T03-39-02-mbDr-web_book_refund_admin_popup_pagination_responsiveness.md +0 -151
  58. package/resources/local/codex/memories/rollout_summaries/2026-04-25T09-20-30-4usS-tool_scv_9router_custom_provider_and_paddle_ocr.md +0 -130
  59. package/resources/local/codex/memories/rollout_summaries/2026-05-06T10-19-38-mt2X-find_db_config_in_web_book_app_env.md +0 -40
  60. 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
  61. package/resources/local/codex/memories/rollout_summaries/2026-05-09T07-52-18-On1F-chakra_git_cleanup_readme_bilingual_publish_config.md +0 -88
  62. package/resources/local/codex/memories/rollout_summaries/2026-05-11T08-05-34-oMEl-check_crack_gui_logo_onefile_build.md +0 -68
  63. package/resources/local/codex/memories/skills/windows-packaged-app-smoke-check/SKILL.md +0 -72
@@ -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
- console.log(`${statusIcons.success} Installing plugin: ${rest[0]}`);
231
+ await this.plugins.installPlugin(rest[0]);
209
232
  break;
210
233
  case 'remove':
211
- console.log(`${statusIcons.success} Removed plugin: ${rest[0]}`);
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
  }