winter-super-cli 2026.5.27 → 2026.5.29

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.
@@ -0,0 +1,136 @@
1
+ /**
2
+ * Resource Loader - Auto-discovers and indexes local resources
3
+ * (design systems, agent instructions, skills) for contextual injection.
4
+ */
5
+
6
+ import { promises as fs } from 'fs';
7
+ import path from 'path';
8
+ import { homedir } from 'os';
9
+ import { fileURLToPath } from 'url';
10
+
11
+ const __dirname = path.dirname(fileURLToPath(import.meta.url));
12
+ const PROJECT_ROOT = path.resolve(__dirname, '..', '..');
13
+ const LOCAL_ROOT = path.join(PROJECT_ROOT, 'resources', 'local');
14
+
15
+ // ── Design Systems ──────────────────────────────────────────────────────────
16
+
17
+ const DESIGN_MD_DIR = path.join(LOCAL_ROOT, 'awesome-design-md', 'design-md');
18
+
19
+ /**
20
+ * Discover all available design system brands.
21
+ */
22
+ export async function discoverDesignBrands() {
23
+ try {
24
+ const entries = await fs.readdir(DESIGN_MD_DIR, { withFileTypes: true });
25
+ return entries
26
+ .filter(e => e.isDirectory())
27
+ .map(e => e.name)
28
+ .sort();
29
+ } catch {
30
+ return [];
31
+ }
32
+ }
33
+
34
+ /**
35
+ * Load a DESIGN.md file for a specific brand.
36
+ */
37
+ export async function loadDesignMd(brand) {
38
+ try {
39
+ const dir = path.join(DESIGN_MD_DIR, brand);
40
+ const files = ['DESIGN.md', 'README.md'];
41
+ for (const file of files) {
42
+ const filePath = path.join(dir, file);
43
+ await fs.access(filePath);
44
+ return { brand, file, content: await fs.readFile(filePath, 'utf8') };
45
+ }
46
+ } catch {}
47
+ return null;
48
+ }
49
+
50
+ /**
51
+ * Search design systems by keyword (brand name or description).
52
+ */
53
+ export async function searchDesignSystems(query) {
54
+ const brands = await discoverDesignBrands();
55
+ const q = query.toLowerCase();
56
+ const matched = brands.filter(b => b.includes(q));
57
+ return matched.slice(0, 10);
58
+ }
59
+
60
+ // ── Resource Manifest ──────────────────────────────────────────────────────
61
+
62
+ const MANIFEST_PATH = path.join(LOCAL_ROOT, 'manifest.json');
63
+
64
+ export async function loadResourceManifest() {
65
+ try {
66
+ const raw = await fs.readFile(MANIFEST_PATH, 'utf8');
67
+ return JSON.parse(raw);
68
+ } catch {
69
+ return { localResources: [] };
70
+ }
71
+ }
72
+
73
+ // ── Context Builder ─────────────────────────────────────────────────────────
74
+
75
+ /**
76
+ * Build a concise context summary of all available local resources.
77
+ * Used for automatic injection into system prompts.
78
+ */
79
+ export async function buildResourceContext() {
80
+ const manifest = await loadResourceManifest();
81
+ const resources = manifest.localResources || [];
82
+ const designBrands = await discoverDesignBrands();
83
+
84
+ const parts = [];
85
+
86
+ if (resources.length > 0) {
87
+ parts.push('## Local Resources');
88
+ resources.forEach(r => {
89
+ parts.push(` - ${r.name}: ${r.fileCount} files, ${(r.size / 1024).toFixed(0)}KB`);
90
+ });
91
+ parts.push('');
92
+ }
93
+
94
+ if (designBrands.length > 0) {
95
+ const brandsStr = designBrands.slice(0, 40).join(', ');
96
+ const leftover = designBrands.length - 40;
97
+ parts.push(`## Design Systems (${designBrands.length} available)`);
98
+ parts.push(` ${brandsStr}${leftover > 0 ? `, +${leftover} more` : ''}`);
99
+ parts.push('');
100
+ }
101
+
102
+ return parts.join('\n');
103
+ }
104
+
105
+ /**
106
+ * Get a relevant DESIGN.md content based on the task description.
107
+ * Uses keyword matching between the task text and design system brand names.
108
+ */
109
+ export async function getRelevantDesignGuide(taskText) {
110
+ if (!taskText) return null;
111
+
112
+ const brands = await discoverDesignBrands();
113
+ const text = taskText.toLowerCase();
114
+
115
+ // Match by brand name in task text
116
+ for (const brand of brands) {
117
+ if (text.includes(brand)) {
118
+ const design = await loadDesignMd(brand);
119
+ if (design) return design;
120
+ }
121
+ }
122
+
123
+ // Match by context clues (e.g., "design", "ui", "looks like", "brand")
124
+ const designHint = /\b(design|ui|looks? like|brand guide|style guide|make it look)\b/i.test(text);
125
+ if (designHint && brands.length > 0) {
126
+ // Return the first few brands as options
127
+ return {
128
+ brand: null,
129
+ type: 'design_hint',
130
+ brands: brands.slice(0, 5),
131
+ note: 'Design-related task detected. Available design systems listed above.',
132
+ };
133
+ }
134
+
135
+ return null;
136
+ }
@@ -11,26 +11,83 @@ function flattenMessageText(messages) {
11
11
  : String(messages || '').toLowerCase();
12
12
  }
13
13
 
14
- export function selectExecutionProfile({ messages = [], activeProvider = null, providers = {}, options = {} } = {}) {
15
- const text = flattenMessageText(messages);
16
- const providerNames = Object.keys(providers).filter(name => providers[name]?.ready || providers[name]?.model);
17
- const hasProvider = name => providerNames.includes(name);
14
+ import { ReasoningConfig, REASONING_LEVELS } from '../ai/reasoning.js';
15
+ import { classifyModelTier, isSmallModel, getReasoningBump, MODEL_TIERS } from '../ai/model-capabilities.js';
18
16
 
19
- const explicitProvider = options.provider && hasProvider(options.provider) ? options.provider : null;
20
- let provider = explicitProvider || (activeProvider && hasProvider(activeProvider) ? activeProvider : providerNames[0] || null);
21
-
22
- if (explicitProvider) {
23
- provider = explicitProvider;
24
- } else if (/\b(review|refactor|debug|fix|bug|error|stack trace|test|tool|patch|code)\b/.test(text) && hasProvider('claude')) {
25
- provider = 'claude';
26
- } else if (/\b(summary|summarize|commit message|changelog|docs|explain|rewrite)\b/.test(text) && hasProvider('openai')) {
27
- provider = 'openai';
28
- } else if (/\b(local|offline|privacy|private|on-device)\b/.test(text) && hasProvider('ollama')) {
29
- provider = 'ollama';
30
- } else if (/\b(quick|brief|short|fast)\b/.test(text) && hasProvider('groq')) {
31
- provider = 'groq';
32
- }
17
+ /**
18
+ * Bump reasoning level by N steps.
19
+ */
20
+ function bumpReasoningLevel(level, steps) {
21
+ const order = [REASONING_LEVELS.NONE, REASONING_LEVELS.LOW, REASONING_LEVELS.MEDIUM, REASONING_LEVELS.HIGH, REASONING_LEVELS.MAX];
22
+ const idx = order.indexOf(level);
23
+ if (idx === -1) return level;
24
+ const newIdx = Math.min(idx + steps, order.length - 1);
25
+ return order[newIdx];
26
+ } export function selectExecutionProfile({ messages = [], activeProvider = null, providers = {}, options = {} } = {}) {
27
+ const text = flattenMessageText(messages);
28
+ const providerNames = Object.keys(providers).filter(name => providers[name]?.ready || providers[name]?.model);
29
+ const hasProvider = name => providerNames.includes(name);
30
+
31
+ const explicitProvider = options.provider && hasProvider(options.provider) ? options.provider : null;
32
+ let provider = explicitProvider || (activeProvider && hasProvider(activeProvider) ? activeProvider : providerNames[0] || null);
33
+
34
+ if (explicitProvider) {
35
+ provider = explicitProvider;
36
+ } else if (/\b(review|refactor|debug|fix|bug|error|stack trace|test|tool|patch|code)\b/.test(text) && hasProvider('claude')) {
37
+ provider = 'claude';
38
+ } else if (/\b(summary|summarize|commit message|changelog|docs|explain|rewrite)\b/.test(text) && hasProvider('openai')) {
39
+ provider = 'openai';
40
+ } else if (/\b(local|offline|privacy|private|on-device)\b/.test(text) && hasProvider('ollama')) {
41
+ provider = 'ollama';
42
+ } else if (/\b(quick|brief|short|fast)\b/.test(text) && hasProvider('groq')) {
43
+ provider = 'groq';
44
+ }
45
+
46
+ const providerConfig = providers[provider] || providers[activeProvider] || {};
47
+ const model = options.model || providerConfig.model || providers[activeProvider]?.model || null;
48
+
49
+ // Detect model capability tier
50
+ const modelTier = classifyModelTier(model, provider);
51
+ const isSmall = isSmallModel(modelTier);
52
+ const reasoningBump = getReasoningBump(modelTier);
53
+
54
+ // Determine reasoning level based on task complexity signals
55
+ // Default: HIGH for coding — all models must think deeply
56
+ let reasoningLevel = options.reasoningLevel || REASONING_LEVELS.HIGH;
57
+ if (!options.reasoningLevel) {
58
+ const hasDeepSignals = /\b(refactor|architecture|redesign|migrate|complex|full stack|e2e|end to end|security|optimize|performance|implement|build|create)\b/.test(text);
59
+ const hasComplexSignals = /\b(debug|fix|test|multiple|integrate|design|plan|review|analyze)\b/.test(text);
60
+
61
+ if (hasDeepSignals && text.length > 30) {
62
+ reasoningLevel = REASONING_LEVELS.MAX;
63
+ } else if (hasComplexSignals && text.length > 20) {
64
+ reasoningLevel = REASONING_LEVELS.MAX;
65
+ } else if (text.split(/\s+/).length > 10) {
66
+ reasoningLevel = REASONING_LEVELS.HIGH;
67
+ } else if (text.split(/\s+/).length < 3) {
68
+ reasoningLevel = REASONING_LEVELS.MEDIUM;
69
+ } else {
70
+ reasoningLevel = REASONING_LEVELS.HIGH;
71
+ }
72
+
73
+ // If small model, bump reasoning level even more to compensate
74
+ if (isSmall && reasoningBump > 0) {
75
+ reasoningLevel = bumpReasoningLevel(reasoningLevel, reasoningBump);
76
+ }
77
+ }
33
78
 
34
- const model = options.model || providers[provider]?.model || providers[activeProvider]?.model || null;
35
- return { provider, model };
79
+ const reasoning = new ReasoningConfig({
80
+ level: reasoningLevel,
81
+ provider: provider || activeProvider,
82
+ modelTier,
83
+ });
84
+
85
+ return {
86
+ provider,
87
+ model,
88
+ modelTier,
89
+ reasoningLevel,
90
+ reasoningParam: reasoning.getApiReasoningParam(),
91
+ reasoningPrompt: reasoning.getPromptInstructions(),
92
+ };
36
93
  }
@@ -470,26 +470,26 @@ export class ToolExecutor {
470
470
  }
471
471
 
472
472
  async executeInternal(toolName, input, context = {}) {
473
- input = input && typeof input === 'object' ? input : {};
474
473
  toolName = this.normalizeToolName(toolName);
474
+ input = this.normalizeToolInput(toolName, input);
475
475
  const cwd = context.cwd || this.projectPath;
476
476
  const resolvedPath = (p) => this.resolveInputPath(p, cwd);
477
477
 
478
478
  switch (toolName) {
479
479
  case 'Read':
480
- return await this.readFile(this.resolveInputPath(input.file_path ?? input.path ?? input.file, cwd));
480
+ return await this.readFile(this.resolveInputPath(input.file_path ?? input.filePath ?? input.filepath ?? input.path ?? input.file ?? input.filename ?? input.target_file ?? input.targetFile, cwd));
481
481
  case 'Write':
482
- return await this.writeFile(this.resolveInputPath(input.file_path ?? input.path ?? input.file, cwd), input.content);
482
+ return await this.writeFile(this.resolveInputPath(input.file_path ?? input.filePath ?? input.filepath ?? input.path ?? input.file ?? input.filename ?? input.target_file ?? input.targetFile, cwd), input.content ?? input.text ?? input.data ?? input.value ?? input.body);
483
483
  case 'Edit':
484
484
  return await this.executeEdit(input, cwd);
485
485
  case 'Bash':
486
- return await this.bash(input.command ?? input.cmd, input.cwd || cwd, input.timeout, input.shell);
486
+ return await this.bash(input.command ?? input.cmd ?? input.script ?? input.code ?? input.input, input.cwd || input.path || cwd, input.timeout, input.shell);
487
487
  case 'Glob':
488
488
  return await this.glob(input.pattern ?? input.glob ?? '**/*', input.cwd || input.path || cwd);
489
489
  case 'Grep':
490
490
  return await this.grep(
491
491
  input.pattern ?? input.query ?? input.q,
492
- input.path || cwd,
492
+ input.path || input.cwd || cwd,
493
493
  input.glob,
494
494
  input.output_mode,
495
495
  {
@@ -519,9 +519,9 @@ export class ToolExecutor {
519
519
  case 'BrowserDebug':
520
520
  return await this.browserDebug(input.url ?? input.uri, input.action);
521
521
  case 'WebFetch':
522
- return await this.webFetch(input.url ?? input.uri, input.prompt);
522
+ return await this.webFetch(input.url ?? input.uri ?? input.href, input.prompt ?? input.query ?? input.extract);
523
523
  case 'WebSearch':
524
- return await this.webSearch(input.query ?? input.q);
524
+ return await this.webSearch(input.query ?? input.q ?? input.search ?? input.search_query ?? input.searchQuery);
525
525
  case 'NotebookRead':
526
526
  return await this.notebookTool.read(this.resolveInputPath(input.notebook_path ?? input.path ?? input.file, cwd));
527
527
  case 'NotebookEdit':
@@ -651,41 +651,67 @@ export class ToolExecutor {
651
651
  }
652
652
 
653
653
  normalizeToolName(toolName) {
654
- const normalized = String(toolName || '').replace(/[-_\s]/g, '').toLowerCase();
654
+ const raw = String(toolName || '').trim();
655
+ const normalized = raw
656
+ .replace(/^functions[._-]/i, '')
657
+ .replace(/^tools?[._-]/i, '')
658
+ .replace(/^winter[._-]/i, '')
659
+ .replace(/^[\w-]+[.:/](?=[A-Za-z])/i, '')
660
+ .replace(/[-_\s]/g, '')
661
+ .toLowerCase();
655
662
  const aliases = {
656
663
  read: 'Read',
657
664
  readfile: 'Read',
665
+ fileread: 'Read',
658
666
  openfile: 'Read',
659
667
  viewfile: 'Read',
668
+ view: 'Read',
660
669
  cat: 'Read',
670
+ getfile: 'Read',
671
+ readfilecontent: 'Read',
661
672
  write: 'Write',
662
673
  writefile: 'Write',
674
+ filewrite: 'Write',
663
675
  writetofile: 'Write',
664
676
  createfile: 'Write',
665
677
  savefile: 'Write',
678
+ create: 'Write',
679
+ overwritefile: 'Write',
666
680
  edit: 'Edit',
667
681
  editfile: 'Edit',
682
+ fileedit: 'Edit',
668
683
  replaceinfile: 'Edit',
669
684
  strreplace: 'Edit',
670
685
  strreplaceeditor: 'Edit',
686
+ strreplaceedit: 'Edit',
671
687
  applydiff: 'Edit',
688
+ applypatch: 'Edit',
672
689
  patch: 'Edit',
673
690
  bash: 'Bash',
674
691
  shell: 'Bash',
675
692
  command: 'Bash',
676
693
  commandexecutor: 'Bash',
677
694
  executecommand: 'Bash',
695
+ runterminalcmd: 'Bash',
696
+ runterminalcommand: 'Bash',
678
697
  runcommand: 'Bash',
698
+ runcmd: 'Bash',
699
+ exec: 'Bash',
679
700
  terminal: 'Bash',
680
701
  powershell: 'Bash',
702
+ cmd: 'Bash',
681
703
  glob: 'Glob',
682
704
  listfiles: 'Glob',
705
+ list: 'Glob',
683
706
  ls: 'Glob',
684
707
  findfiles: 'Glob',
708
+ find: 'Glob',
685
709
  grep: 'Grep',
686
710
  search: 'Grep',
687
711
  searchfiles: 'Grep',
712
+ grepsearch: 'Grep',
688
713
  searchtext: 'Grep',
714
+ searchcode: 'Grep',
689
715
  rg: 'Grep',
690
716
  rgfull: 'Grep',
691
717
  searchadvanced: 'Grep',
@@ -707,12 +733,17 @@ export class ToolExecutor {
707
733
  webfetch: 'WebFetch',
708
734
  fetch: 'WebFetch',
709
735
  fetchurl: 'WebFetch',
736
+ geturl: 'WebFetch',
710
737
  websearch: 'WebSearch',
711
738
  searchweb: 'WebSearch',
739
+ internetsearch: 'WebSearch',
740
+ googlesearch: 'WebSearch',
712
741
  browserdebug: 'BrowserDebug',
713
742
  browser: 'BrowserDebug',
743
+ browserinspect: 'BrowserDebug',
714
744
  parallel: 'Parallel',
715
745
  parallelexecute: 'Parallel',
746
+ paralleltools: 'Parallel',
716
747
  batch: 'Parallel',
717
748
  // New tools
718
749
  webarchive: 'WebArchive',
@@ -746,7 +777,45 @@ export class ToolExecutor {
746
777
  subagent: 'Agent',
747
778
  agentrun: 'Agent',
748
779
  };
749
- return aliases[normalized] || toolName;
780
+ return aliases[normalized] || raw;
781
+ }
782
+
783
+ normalizeToolInput(toolName, input) {
784
+ if (input && typeof input === 'object' && !Array.isArray(input)) {
785
+ return this.unwrapToolInput(input);
786
+ }
787
+
788
+ if (typeof input !== 'string') return {};
789
+ const value = input.trim();
790
+ if (!value) return {};
791
+
792
+ switch (toolName) {
793
+ case 'Read':
794
+ case 'NotebookRead':
795
+ return { file_path: value };
796
+ case 'Write':
797
+ return { content: value };
798
+ case 'Bash':
799
+ return { command: value };
800
+ case 'Glob':
801
+ return { pattern: value };
802
+ case 'Grep':
803
+ case 'WebSearch':
804
+ return { query: value };
805
+ case 'WebFetch':
806
+ case 'WebArchive':
807
+ case 'BrowserDebug':
808
+ return { url: value };
809
+ case 'TaskCreate':
810
+ case 'TodoWrite':
811
+ return { title: value };
812
+ case 'ScheduleWakeup':
813
+ return { prompt: value };
814
+ case 'Agent':
815
+ return { task: value };
816
+ default:
817
+ return { input: value };
818
+ }
750
819
  }
751
820
 
752
821
  resolveInputPath(filePath, cwd) {