winter-super-cli 2026.6.26 → 2026.6.28
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/CHANGELOG.md +28 -5
- package/README.md +66 -0
- package/package.json +5 -1
- package/resources/local/gsap-skills/.claude-plugin/marketplace.json +20 -0
- package/resources/local/gsap-skills/.claude-plugin/plugin.json +6 -0
- package/resources/local/gsap-skills/.cursor-plugin/marketplace.json +13 -0
- package/resources/local/gsap-skills/.cursor-plugin/plugin.json +22 -0
- package/resources/local/gsap-skills/.github/copilot-instructions.md +17 -0
- package/resources/local/gsap-skills/.github/instructions/react.instructions.md +15 -0
- package/resources/local/gsap-skills/.github/instructions/scrolltrigger.instructions.md +18 -0
- package/resources/local/gsap-skills/AGENTS.md +27 -0
- package/resources/local/gsap-skills/CLAUDE.md +1 -0
- package/resources/local/gsap-skills/GEMINI.md +1 -0
- package/resources/local/gsap-skills/LICENSE +21 -0
- package/resources/local/gsap-skills/README.md +163 -0
- package/resources/local/gsap-skills/assets/gsap-green.svg +7 -0
- package/resources/local/gsap-skills/assets/gsap-icon-inverted.svg +15 -0
- package/resources/local/gsap-skills/assets/gsap-icon-square.svg +1 -0
- package/resources/local/gsap-skills/assets/gsap-white.svg +7 -0
- package/resources/local/gsap-skills/examples/README.md +29 -0
- package/resources/local/gsap-skills/examples/nuxt/app/app.vue +3 -0
- package/resources/local/gsap-skills/examples/nuxt/app/composables/useGSAP.ts +91 -0
- package/resources/local/gsap-skills/examples/nuxt/app/pages/index.vue +55 -0
- package/resources/local/gsap-skills/examples/nuxt/nuxt.config.ts +4 -0
- package/resources/local/gsap-skills/examples/nuxt/package.json +18 -0
- package/resources/local/gsap-skills/examples/react/App.jsx +46 -0
- package/resources/local/gsap-skills/examples/react/index.html +12 -0
- package/resources/local/gsap-skills/examples/react/main.jsx +9 -0
- package/resources/local/gsap-skills/examples/react/package.json +21 -0
- package/resources/local/gsap-skills/examples/react/vite.config.js +7 -0
- package/resources/local/gsap-skills/examples/vanilla/index.html +33 -0
- package/resources/local/gsap-skills/examples/vanilla/main.js +36 -0
- package/resources/local/gsap-skills/examples/vue/app.vue +47 -0
- package/resources/local/gsap-skills/examples/vue/index.html +15 -0
- package/resources/local/gsap-skills/examples/vue/main.js +9 -0
- package/resources/local/gsap-skills/examples/vue/package.json +19 -0
- package/resources/local/gsap-skills/examples/vue/vite.config.js +7 -0
- package/resources/local/gsap-skills/skills/gsap-core/SKILL.md +254 -0
- package/resources/local/gsap-skills/skills/gsap-frameworks/SKILL.md +266 -0
- package/resources/local/gsap-skills/skills/gsap-performance/SKILL.md +79 -0
- package/resources/local/gsap-skills/skills/gsap-plugins/SKILL.md +433 -0
- package/resources/local/gsap-skills/skills/gsap-react/SKILL.md +136 -0
- package/resources/local/gsap-skills/skills/gsap-scrolltrigger/SKILL.md +296 -0
- package/resources/local/gsap-skills/skills/gsap-timeline/SKILL.md +107 -0
- package/resources/local/gsap-skills/skills/gsap-utils/SKILL.md +284 -0
- package/resources/local/gsap-skills/skills/llms.txt +39 -0
- package/resources/local/hermes-agent-core/AGENTS.md +1132 -0
- package/resources/local/hermes-agent-core/LICENSE +21 -0
- package/resources/local/hermes-agent-core/README.md +215 -0
- package/resources/local/hermes-agent-core/docs/2026-05-07-s6-overlay-dynamic-subagent-gateways.md +434 -0
- package/resources/local/hermes-agent-core/hermes-already-has-routines.md +160 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/DESCRIPTION.md +3 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/claude-code/SKILL.md +745 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/codex/SKILL.md +130 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/hermes-agent/SKILL.md +1021 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/kanban-codex-lane/SKILL.md +277 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/kanban-codex-lane/templates/pmb-codex-lane-prompt.md +57 -0
- package/resources/local/hermes-agent-core/skills/autonomous-ai-agents/opencode/SKILL.md +219 -0
- package/resources/local/hermes-agent-core/skills/github/DESCRIPTION.md +3 -0
- package/resources/local/hermes-agent-core/skills/github/codebase-inspection/SKILL.md +116 -0
- package/resources/local/hermes-agent-core/skills/github/github-auth/SKILL.md +247 -0
- package/resources/local/hermes-agent-core/skills/github/github-auth/scripts/gh-env.sh +66 -0
- package/resources/local/hermes-agent-core/skills/github/github-code-review/SKILL.md +481 -0
- package/resources/local/hermes-agent-core/skills/github/github-code-review/references/review-output-template.md +74 -0
- package/resources/local/hermes-agent-core/skills/github/github-issues/SKILL.md +370 -0
- package/resources/local/hermes-agent-core/skills/github/github-issues/templates/bug-report.md +35 -0
- package/resources/local/hermes-agent-core/skills/github/github-issues/templates/feature-request.md +31 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/SKILL.md +367 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/references/ci-troubleshooting.md +183 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/references/conventional-commits.md +71 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +35 -0
- package/resources/local/hermes-agent-core/skills/github/github-pr-workflow/templates/pr-body-feature.md +33 -0
- package/resources/local/hermes-agent-core/skills/github/github-repo-management/SKILL.md +516 -0
- package/resources/local/hermes-agent-core/skills/github/github-repo-management/references/github-api-cheatsheet.md +161 -0
- package/resources/local/hermes-agent-core/skills/mcp/DESCRIPTION.md +3 -0
- package/resources/local/hermes-agent-core/skills/mcp/native-mcp/SKILL.md +357 -0
- package/resources/local/hermes-agent-core/skills/software-development/debugging-hermes-tui-commands/SKILL.md +152 -0
- package/resources/local/hermes-agent-core/skills/software-development/hermes-agent-skill-authoring/SKILL.md +165 -0
- package/resources/local/hermes-agent-core/skills/software-development/hermes-s6-container-supervision/SKILL.md +176 -0
- package/resources/local/hermes-agent-core/skills/software-development/node-inspect-debugger/SKILL.md +319 -0
- package/resources/local/hermes-agent-core/skills/software-development/plan/SKILL.md +58 -0
- package/resources/local/hermes-agent-core/skills/software-development/python-debugpy/SKILL.md +375 -0
- package/resources/local/hermes-agent-core/skills/software-development/requesting-code-review/SKILL.md +280 -0
- package/resources/local/hermes-agent-core/skills/software-development/spike/SKILL.md +197 -0
- package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/SKILL.md +352 -0
- package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/references/context-budget-discipline.md +53 -0
- package/resources/local/hermes-agent-core/skills/software-development/subagent-driven-development/references/gates-taxonomy.md +93 -0
- package/resources/local/hermes-agent-core/skills/software-development/systematic-debugging/SKILL.md +367 -0
- package/resources/local/hermes-agent-core/skills/software-development/test-driven-development/SKILL.md +343 -0
- package/resources/local/hermes-agent-core/skills/software-development/writing-plans/SKILL.md +297 -0
- package/resources/local/manifest.json +12 -0
- package/rule.md +2 -0
- package/scripts/audit-pack.js +5 -0
- package/scripts/smoke-browser.js +53 -0
- package/scripts/smoke-package.js +38 -4
- package/skill.md +36 -4
- package/skills/gsap.md +26 -0
- package/skills/hermes-agent.md +17 -0
- package/src/agent/agent-definitions.js +4 -4
- package/src/agent/runtime.js +206 -5
- package/src/agent/subagent-child.js +44 -0
- package/src/ai/capability-scorecard.js +193 -14
- package/src/ai/hermes-core.js +77 -0
- package/src/ai/model-capabilities.js +42 -2
- package/src/ai/prompts/system-prompt.js +16 -2
- package/src/ai/small-model-amplifier.js +35 -7
- package/src/ai/workflow-selector.js +22 -1
- package/src/cli/commands.js +21 -1
- package/src/cli/config.js +42 -4
- package/src/cli/context-loader.js +253 -9
- package/src/cli/conversation-format.js +5 -0
- package/src/cli/input-controller.js +79 -10
- package/src/cli/prompt-builder.js +45 -8
- package/src/cli/repl-commands.js +123 -2
- package/src/cli/repl.js +183 -87
- package/src/cli/slash-commands.js +3 -1
- package/src/cli/tui.js +133 -37
- package/src/mcp/client.js +46 -5
- package/src/tools/agent.js +316 -25
- package/src/tools/executor.js +314 -11
- package/src/tools/permission.js +20 -17
- package/winter.d.ts +112 -10
package/src/cli/config.js
CHANGED
|
@@ -7,7 +7,7 @@ import { promises as fs } from 'fs';
|
|
|
7
7
|
import path from 'path';
|
|
8
8
|
import { homedir } from 'os';
|
|
9
9
|
import { loadEnvFile, stripInlineSecrets } from './secret-env.js';
|
|
10
|
-
import { CHROME_DEVTOOLS_MCP_NAME, createChromeDevtoolsMcpServer } from '../mcp/presets.js';
|
|
10
|
+
import { CHROME_DEVTOOLS_MCP_NAME, createChromeDevtoolsMcpServer, ensureMcpConfigShape } from '../mcp/presets.js';
|
|
11
11
|
|
|
12
12
|
export class ConfigLoader {
|
|
13
13
|
constructor() {
|
|
@@ -22,14 +22,29 @@ export class ConfigLoader {
|
|
|
22
22
|
await loadEnvFile(this.envFile);
|
|
23
23
|
const data = await fs.readFile(this.configFile, 'utf8');
|
|
24
24
|
const config = JSON.parse(data.replace(/^\uFEFF/, ''));
|
|
25
|
-
return this.applyEnv(config);
|
|
25
|
+
return this.applyEnv(this.ensureBundledDefaults(config));
|
|
26
26
|
} catch {
|
|
27
27
|
// Return defaults
|
|
28
28
|
await loadEnvFile(this.envFile);
|
|
29
|
-
return this.applyEnv(this.getDefaults());
|
|
29
|
+
return this.applyEnv(this.ensureBundledDefaults(this.getDefaults()));
|
|
30
30
|
}
|
|
31
31
|
}
|
|
32
32
|
|
|
33
|
+
ensureBundledDefaults(config = {}) {
|
|
34
|
+
const next = structuredClone(config || {});
|
|
35
|
+
ensureMcpConfigShape(next);
|
|
36
|
+
if (!next.mcp.servers.some(server => server?.name === CHROME_DEVTOOLS_MCP_NAME)) {
|
|
37
|
+
next.mcp.servers.push(createChromeDevtoolsMcpServer(['--isolated']));
|
|
38
|
+
}
|
|
39
|
+
next.permissions.allowlist.tools = [
|
|
40
|
+
...new Set([...(next.permissions.allowlist.tools || []), 'MCP']),
|
|
41
|
+
];
|
|
42
|
+
next.permissions.allowlist.mcpServers = [
|
|
43
|
+
...new Set([...(next.permissions.allowlist.mcpServers || []), CHROME_DEVTOOLS_MCP_NAME]),
|
|
44
|
+
];
|
|
45
|
+
return next;
|
|
46
|
+
}
|
|
47
|
+
|
|
33
48
|
applyEnv(config) {
|
|
34
49
|
const next = structuredClone(config || this.getDefaults());
|
|
35
50
|
for (const [provider, section] of Object.entries(next)) {
|
|
@@ -105,7 +120,13 @@ export class ConfigLoader {
|
|
|
105
120
|
sandbox: {
|
|
106
121
|
enabled: true,
|
|
107
122
|
restrictToWorkspace: true,
|
|
108
|
-
allowedCommands: [
|
|
123
|
+
allowedCommands: [
|
|
124
|
+
'git', 'npm', 'npx', 'node', 'python', 'powershell', 'pwsh', 'cmd',
|
|
125
|
+
'ping', 'test-connection', 'curl', 'wget', 'iwr', 'irm',
|
|
126
|
+
'invoke-webrequest', 'invoke-restmethod', 'nslookup', 'resolve-dnsname',
|
|
127
|
+
'tracert', 'traceroute', 'pathping', 'dig', 'ipconfig', 'ifconfig',
|
|
128
|
+
'ip', 'netstat', 'speedtest', 'speedtest-cli', 'measure-command', 'where',
|
|
129
|
+
],
|
|
109
130
|
},
|
|
110
131
|
session: {
|
|
111
132
|
autoSave: true,
|
|
@@ -240,6 +261,23 @@ export class ConfigLoader {
|
|
|
240
261
|
await this.save(config);
|
|
241
262
|
}
|
|
242
263
|
|
|
264
|
+
async setFullAccess(enabled = true) {
|
|
265
|
+
const config = await this.load();
|
|
266
|
+
const defaults = this.getDefaults();
|
|
267
|
+
const fullAccess = Boolean(enabled);
|
|
268
|
+
config.sandbox = {
|
|
269
|
+
...(config.sandbox || {}),
|
|
270
|
+
enabled: !fullAccess,
|
|
271
|
+
restrictToWorkspace: !fullAccess,
|
|
272
|
+
allowedCommands: config.sandbox?.allowedCommands || defaults.sandbox.allowedCommands,
|
|
273
|
+
};
|
|
274
|
+
config.permissions = config.permissions || { allowlist: {} };
|
|
275
|
+
config.permissions.allowlist = config.permissions.allowlist || { tools: [], commands: [], mcpServers: [] };
|
|
276
|
+
config.permissions.promptByDefault = !fullAccess;
|
|
277
|
+
await this.save(config);
|
|
278
|
+
return config;
|
|
279
|
+
}
|
|
280
|
+
|
|
243
281
|
async setMcpServers(servers = []) {
|
|
244
282
|
const config = await this.load();
|
|
245
283
|
config.mcp = config.mcp || {};
|
|
@@ -83,6 +83,11 @@ export class ContextLoader {
|
|
|
83
83
|
pageAgent: path.join(localRoot, 'page-agent'),
|
|
84
84
|
ecc: path.join(localRoot, 'ecc'),
|
|
85
85
|
eccReadme: path.join(localRoot, 'ecc', 'README.md'),
|
|
86
|
+
gsapSkills: path.join(localRoot, 'gsap-skills'),
|
|
87
|
+
gsapSkillsIndex: path.join(localRoot, 'gsap-skills', 'skills', 'llms.txt'),
|
|
88
|
+
hermesAgentCore: path.join(localRoot, 'hermes-agent-core'),
|
|
89
|
+
hermesAgentCoreAgents: path.join(localRoot, 'hermes-agent-core', 'AGENTS.md'),
|
|
90
|
+
hermesAgentCoreSkills: path.join(localRoot, 'hermes-agent-core', 'skills'),
|
|
86
91
|
designs: path.join(localRoot, 'awesome-design-md', 'design-md'),
|
|
87
92
|
agents: path.join(localRoot, 'agents.md'),
|
|
88
93
|
manifest: path.join(localRoot, 'manifest.json'),
|
|
@@ -101,6 +106,17 @@ export class ContextLoader {
|
|
|
101
106
|
}
|
|
102
107
|
}
|
|
103
108
|
|
|
109
|
+
async readFirstExisting(basePath, candidates = [], maxChars = 1800) {
|
|
110
|
+
for (const candidate of candidates) {
|
|
111
|
+
const filePath = path.join(basePath, candidate);
|
|
112
|
+
const content = await this.readTextIfExists(filePath, maxChars);
|
|
113
|
+
if (content) {
|
|
114
|
+
return { filePath, content };
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return { filePath: '', content: '' };
|
|
118
|
+
}
|
|
119
|
+
|
|
104
120
|
getUserResourcePaths() {
|
|
105
121
|
const home = homedir();
|
|
106
122
|
return {
|
|
@@ -175,7 +191,7 @@ export class ContextLoader {
|
|
|
175
191
|
}
|
|
176
192
|
|
|
177
193
|
lines.push('- Use Read/Grep/Glob to inspect any local resource when it matters for the task.');
|
|
178
|
-
lines.push('- Local resource families: winter skills, winter memories, agents.md, awesome-design-md, claude, codex, karpathy-tools, page-agent, ecc.');
|
|
194
|
+
lines.push('- Local resource families: winter skills, winter memories, agents.md, awesome-design-md, claude, codex, karpathy-tools, page-agent, ecc, gsap-skills, hermes-agent-core.');
|
|
179
195
|
lines.push(`- User resource roots: ${userPaths.codexRoot}, ${userPaths.claudeRoot}`);
|
|
180
196
|
|
|
181
197
|
if (winterSkills.length > 0) {
|
|
@@ -229,6 +245,11 @@ export class ContextLoader {
|
|
|
229
245
|
const pageAgentReadmePath = path.join(paths.pageAgent, 'README.md');
|
|
230
246
|
const pageAgentAgentsPath = path.join(paths.pageAgent, 'AGENTS.md');
|
|
231
247
|
const pageAgentWinterPath = path.join(paths.pageAgent, 'WINTER.md');
|
|
248
|
+
const gsapReadmePath = path.join(paths.gsapSkills, 'README.md');
|
|
249
|
+
const gsapIndexPath = paths.gsapSkillsIndex;
|
|
250
|
+
const hermesReadmePath = path.join(paths.hermesAgentCore, 'README.md');
|
|
251
|
+
const hermesAgentsPath = paths.hermesAgentCoreAgents;
|
|
252
|
+
const hermesRoutinesPath = path.join(paths.hermesAgentCore, 'hermes-already-has-routines.md');
|
|
232
253
|
const winterCodingSkillPath = path.join(paths.winter.skills, 'coding.md');
|
|
233
254
|
const winterDebugSkillPath = path.join(paths.winter.skills, 'debug.md');
|
|
234
255
|
const winterTestSkillPath = path.join(paths.winter.skills, 'test.md');
|
|
@@ -241,6 +262,12 @@ export class ContextLoader {
|
|
|
241
262
|
designBrands,
|
|
242
263
|
pageAgentWinter,
|
|
243
264
|
pageAgentAgents,
|
|
265
|
+
gsapReadme,
|
|
266
|
+
gsapIndex,
|
|
267
|
+
hermesReadme,
|
|
268
|
+
hermesAgents,
|
|
269
|
+
hermesRoutines,
|
|
270
|
+
hermesSkillFamilies,
|
|
244
271
|
winterCodingSkill,
|
|
245
272
|
winterDebugSkill,
|
|
246
273
|
winterTestSkill,
|
|
@@ -252,6 +279,12 @@ export class ContextLoader {
|
|
|
252
279
|
this.listPathEntries(paths.designs, 40),
|
|
253
280
|
this.readTextIfExists(pageAgentWinterPath, 1600),
|
|
254
281
|
this.readTextIfExists(pageAgentAgentsPath, 2200),
|
|
282
|
+
this.readTextIfExists(gsapReadmePath, 1600),
|
|
283
|
+
this.readTextIfExists(gsapIndexPath, 1800),
|
|
284
|
+
this.readTextIfExists(hermesReadmePath, 1800),
|
|
285
|
+
this.readTextIfExists(hermesAgentsPath, 2200),
|
|
286
|
+
this.readTextIfExists(hermesRoutinesPath, 1400),
|
|
287
|
+
this.listPathEntries(paths.hermesAgentCoreSkills, 40),
|
|
255
288
|
this.readTextIfExists(winterCodingSkillPath, 1200),
|
|
256
289
|
this.readTextIfExists(winterDebugSkillPath, 1000),
|
|
257
290
|
this.readTextIfExists(winterTestSkillPath, 1000),
|
|
@@ -265,6 +298,12 @@ export class ContextLoader {
|
|
|
265
298
|
designBrands.length > 0 ||
|
|
266
299
|
pageAgentWinter ||
|
|
267
300
|
pageAgentAgents ||
|
|
301
|
+
gsapReadme ||
|
|
302
|
+
gsapIndex ||
|
|
303
|
+
hermesReadme ||
|
|
304
|
+
hermesAgents ||
|
|
305
|
+
hermesRoutines ||
|
|
306
|
+
hermesSkillFamilies.length > 0 ||
|
|
268
307
|
winterCodingSkill ||
|
|
269
308
|
winterDebugSkill ||
|
|
270
309
|
winterTestSkill ||
|
|
@@ -282,6 +321,10 @@ export class ContextLoader {
|
|
|
282
321
|
lines.push(`- Page Agent (Alibaba GUI Agent): ${path.relative(this.projectPath, pageAgentReadmePath)}`);
|
|
283
322
|
lines.push(' Apply: for browser automation, smart form filling, SaaS AI copilot, accessibility, and multi-page agent tasks. PageAgent is an in-page JavaScript library that uses text-based DOM manipulation to control web interfaces with natural language. No browser extension or headless browser required.');
|
|
284
323
|
lines.push(` Internal architecture at: ${path.relative(this.projectPath, pageAgentAgentsPath)}`);
|
|
324
|
+
lines.push(`- GSAP skills: ${path.relative(this.projectPath, gsapIndexPath)}`);
|
|
325
|
+
lines.push(' Apply: for JavaScript animation, React/Vue/Svelte animation, timelines, ScrollTrigger, Flip, Draggable, SVG/motion-path work, and animation performance. Read the matching GSAP SKILL.md before implementing non-trivial animation.');
|
|
326
|
+
lines.push(`- Hermes Agent core: ${path.relative(this.projectPath, hermesAgentsPath)}`);
|
|
327
|
+
lines.push(' Apply directly in Winter core for self-improving skills, session search/compression, subagent delegation, tool gateway discipline, TUI/gateway separation, scheduled automation, and central command registry patterns.');
|
|
285
328
|
lines.push(`- Design system corpus: ${path.relative(this.projectPath, designReadmePath)} and ${path.relative(this.projectPath, paths.designs)}`);
|
|
286
329
|
lines.push(' Apply: for UI/brand/design tasks, search the design-md corpus first and follow the closest existing brand/design guidance instead of inventing style from scratch.');
|
|
287
330
|
lines.push(`- Winter packaged skills: ${path.relative(this.projectPath, paths.winter.skills)}`);
|
|
@@ -297,6 +340,13 @@ export class ContextLoader {
|
|
|
297
340
|
if (brandNames.length > 0) {
|
|
298
341
|
lines.push(`- Available design-md examples include: ${brandNames.join(', ')}${designBrands.length > brandNames.length ? ', ...' : ''}`);
|
|
299
342
|
}
|
|
343
|
+
const hermesFamilies = hermesSkillFamilies
|
|
344
|
+
.filter(item => item.isDirectory)
|
|
345
|
+
.map(item => item.name)
|
|
346
|
+
.slice(0, 12);
|
|
347
|
+
if (hermesFamilies.length > 0) {
|
|
348
|
+
lines.push(`- Hermes core skill families include: ${hermesFamilies.join(', ')}${hermesSkillFamilies.length > hermesFamilies.length ? ', ...' : ''}`);
|
|
349
|
+
}
|
|
300
350
|
|
|
301
351
|
const evidence = [];
|
|
302
352
|
if (/Think Before Coding|Simplicity First|Surgical Changes|Goal-Driven Execution/i.test(karpathy)) {
|
|
@@ -314,6 +364,12 @@ export class ContextLoader {
|
|
|
314
364
|
if (/Monorepo|PageAgentCore|PageController|DOM Pipeline|FlatDomTree/i.test(pageAgentAgents)) {
|
|
315
365
|
evidence.push('page-agent AGENTS.md confirms monorepo structure, DOM pipeline, and tool architecture.');
|
|
316
366
|
}
|
|
367
|
+
if (/gsap-core|gsap-scrolltrigger|gsap-react|ScrollTrigger|timeline/i.test(`${gsapReadme}\n${gsapIndex}`)) {
|
|
368
|
+
evidence.push('gsap-skills confirms core GSAP, timelines, ScrollTrigger, framework, plugin, and performance guidance.');
|
|
369
|
+
}
|
|
370
|
+
if (/self-improving|learning loop|subagents|TUI|gateway|Skill|MCP|cron|scheduled/i.test(`${hermesReadme}\n${hermesAgents}\n${hermesRoutines}`)) {
|
|
371
|
+
evidence.push('hermes-agent-core confirms self-improving agent loops, skills, memory/search, subagents, tool gateways, TUI separation, and automation routines.');
|
|
372
|
+
}
|
|
317
373
|
if (/Read code to understand|Read relevant project files|Verify|automatic tool usage/i.test(winterCodingSkill)) {
|
|
318
374
|
evidence.push('packaged coding skill confirms inspect-before-edit and verify-after-change behavior.');
|
|
319
375
|
}
|
|
@@ -333,26 +389,192 @@ export class ContextLoader {
|
|
|
333
389
|
return lines.join('\n');
|
|
334
390
|
}
|
|
335
391
|
|
|
392
|
+
async getResourceApplicationProfile({ projectInstructionFiles = [] } = {}) {
|
|
393
|
+
const paths = this.getResourcePaths();
|
|
394
|
+
const userPaths = this.getUserResourcePaths();
|
|
395
|
+
const [
|
|
396
|
+
karpathy,
|
|
397
|
+
agents,
|
|
398
|
+
pageAgent,
|
|
399
|
+
pageAgentAgents,
|
|
400
|
+
hermesAgents,
|
|
401
|
+
hermesReadme,
|
|
402
|
+
hermesRoutines,
|
|
403
|
+
gsapIndex,
|
|
404
|
+
gsapReadme,
|
|
405
|
+
eccReadme,
|
|
406
|
+
codexGuide,
|
|
407
|
+
claudeGuide,
|
|
408
|
+
codexRules,
|
|
409
|
+
codexSkills,
|
|
410
|
+
claudeSkills,
|
|
411
|
+
designBrands,
|
|
412
|
+
winterSkills,
|
|
413
|
+
winterMemories,
|
|
414
|
+
userCodexSkills,
|
|
415
|
+
userCodexRules,
|
|
416
|
+
userCodexMemories,
|
|
417
|
+
userClaudeSkills,
|
|
418
|
+
] = await Promise.all([
|
|
419
|
+
this.readTextIfExists(path.join(paths.karpathy, 'CLAUDE.md'), 1400),
|
|
420
|
+
this.readTextIfExists(path.join(paths.agents, 'AGENTS.md'), 1400),
|
|
421
|
+
this.readFirstExisting(paths.pageAgent, ['WINTER.md', 'README.md'], 1400),
|
|
422
|
+
this.readTextIfExists(path.join(paths.pageAgent, 'AGENTS.md'), 1400),
|
|
423
|
+
this.readTextIfExists(paths.hermesAgentCoreAgents, 1600),
|
|
424
|
+
this.readTextIfExists(path.join(paths.hermesAgentCore, 'README.md'), 1400),
|
|
425
|
+
this.readTextIfExists(path.join(paths.hermesAgentCore, 'hermes-already-has-routines.md'), 1200),
|
|
426
|
+
this.readTextIfExists(paths.gsapSkillsIndex, 1600),
|
|
427
|
+
this.readTextIfExists(path.join(paths.gsapSkills, 'README.md'), 1200),
|
|
428
|
+
this.readTextIfExists(paths.eccReadme, 1200),
|
|
429
|
+
this.readFirstExisting(paths.codex.root, ['AGENTS.md', 'README.md', 'CLAUDE.md'], 1400),
|
|
430
|
+
this.readFirstExisting(paths.claude.root, ['AGENTS.md', 'README.md', 'CLAUDE.md'], 1400),
|
|
431
|
+
this.listPathEntries(paths.codex.rules, 40),
|
|
432
|
+
this.listPathEntries(paths.codex.skills, 40),
|
|
433
|
+
this.listPathEntries(paths.claude.skills, 40),
|
|
434
|
+
this.listPathEntries(paths.designs, 40),
|
|
435
|
+
this.listPathEntries(paths.winter.skills, 40),
|
|
436
|
+
this.listPathEntries(paths.winter.memories, 40),
|
|
437
|
+
this.listPathEntries(userPaths.codexSkills, 40),
|
|
438
|
+
this.listPathEntries(userPaths.codexRules, 40),
|
|
439
|
+
this.listPathEntries(userPaths.codexMemories, 40),
|
|
440
|
+
this.listPathEntries(userPaths.claudeSkills, 40),
|
|
441
|
+
]);
|
|
442
|
+
|
|
443
|
+
const lines = [];
|
|
444
|
+
lines.push('[Auto-loaded Resource Application Profile]');
|
|
445
|
+
lines.push('- Winter must apply this profile automatically before task work. It is not optional and does not require a slash command.');
|
|
446
|
+
lines.push('- Default agent loop: read project rules + relevant resource profile -> inspect project state -> use tools -> verify -> final.');
|
|
447
|
+
lines.push('- If a task touches any listed domain, inspect the matching resource file in depth with Read/Grep before implementation.');
|
|
448
|
+
|
|
449
|
+
const projectRules = (projectInstructionFiles || [])
|
|
450
|
+
.map(file => `${file.relativePath}: ${this.compactText(String(file.content || '').replace(/\s+/g, ' ').trim(), 360, file.relativePath)}`)
|
|
451
|
+
.filter(Boolean);
|
|
452
|
+
if (projectRules.length > 0) {
|
|
453
|
+
lines.push('Project rules loaded:');
|
|
454
|
+
projectRules.slice(0, 8).forEach(rule => lines.push(`- ${rule}`));
|
|
455
|
+
}
|
|
456
|
+
|
|
457
|
+
const addSection = (name, apply, evidence, sourcePath = '') => {
|
|
458
|
+
lines.push(`${name}:`);
|
|
459
|
+
lines.push(`- Apply: ${apply}`);
|
|
460
|
+
if (sourcePath) lines.push(`- Source: ${sourcePath}`);
|
|
461
|
+
if (evidence) lines.push(`- Loaded evidence: ${this.compactText(evidence.replace(/\s+/g, ' ').trim(), 520, name)}`);
|
|
462
|
+
};
|
|
463
|
+
|
|
464
|
+
addSection(
|
|
465
|
+
'karpathy-tools',
|
|
466
|
+
'coding discipline, simplicity, surgical edits, verification criteria, and anti-overengineering.',
|
|
467
|
+
karpathy,
|
|
468
|
+
path.join(paths.karpathy, 'CLAUDE.md')
|
|
469
|
+
);
|
|
470
|
+
addSection(
|
|
471
|
+
'agents.md',
|
|
472
|
+
'agentic development workflow, dependency hygiene, project conventions, and repo-specific agent rules.',
|
|
473
|
+
agents,
|
|
474
|
+
path.join(paths.agents, 'AGENTS.md')
|
|
475
|
+
);
|
|
476
|
+
addSection(
|
|
477
|
+
'page-agent',
|
|
478
|
+
'visible/browser-like GUI automation, DOM reasoning, form workflows, multi-page interaction, and web agent tasks.',
|
|
479
|
+
`${pageAgent.content}\n${pageAgentAgents}`,
|
|
480
|
+
pageAgent.filePath || path.join(paths.pageAgent, 'README.md')
|
|
481
|
+
);
|
|
482
|
+
addSection(
|
|
483
|
+
'hermes-agent-core',
|
|
484
|
+
'self-improving loops, skill lifecycle, memory/search compression, subagents, TUI separation, tool gateways, and automation routines.',
|
|
485
|
+
`${hermesAgents}\n${hermesReadme}\n${hermesRoutines}`,
|
|
486
|
+
paths.hermesAgentCore
|
|
487
|
+
);
|
|
488
|
+
addSection(
|
|
489
|
+
'gsap-skills',
|
|
490
|
+
'GSAP animation, timelines, ScrollTrigger, framework bindings, SVG/motion-path work, and animation performance.',
|
|
491
|
+
`${gsapIndex}\n${gsapReadme}`,
|
|
492
|
+
paths.gsapSkills
|
|
493
|
+
);
|
|
494
|
+
addSection(
|
|
495
|
+
'ecc',
|
|
496
|
+
'ECC knowledge/resource browsing and bundled ecosystem context when a task references ECC or encoded/corpus resources.',
|
|
497
|
+
eccReadme,
|
|
498
|
+
paths.ecc
|
|
499
|
+
);
|
|
500
|
+
addSection(
|
|
501
|
+
'codex',
|
|
502
|
+
'Codex-style skills, plugins, rules, memories, provider/tool reliability, and coding-agent behavior.',
|
|
503
|
+
codexGuide.content,
|
|
504
|
+
paths.codex.root
|
|
505
|
+
);
|
|
506
|
+
addSection(
|
|
507
|
+
'claude',
|
|
508
|
+
'Claude-style skills, plugins, project rules, subagent conventions, hooks, and MCP workflows.',
|
|
509
|
+
claudeGuide.content,
|
|
510
|
+
paths.claude.root
|
|
511
|
+
);
|
|
512
|
+
addSection(
|
|
513
|
+
'awesome-design-md',
|
|
514
|
+
'brand/design/UI work. Search design-md before inventing visual direction; use the closest matching design guide.',
|
|
515
|
+
designBrands.length ? `Design libraries available: ${designBrands.filter(item => item.isDirectory).slice(0, 24).map(item => item.name).join(', ')}` : '',
|
|
516
|
+
paths.designs
|
|
517
|
+
);
|
|
518
|
+
addSection(
|
|
519
|
+
'winter packaged skills and memories',
|
|
520
|
+
'baseline coding/debug/test/memory behavior that survives global installs and external project roots.',
|
|
521
|
+
[
|
|
522
|
+
winterSkills.length ? `Skills: ${winterSkills.slice(0, 24).map(item => item.name).join(', ')}` : '',
|
|
523
|
+
winterMemories.length ? `Memories: ${winterMemories.slice(0, 16).map(item => item.name).join(', ')}` : '',
|
|
524
|
+
].filter(Boolean).join(' '),
|
|
525
|
+
paths.winter.root
|
|
526
|
+
);
|
|
527
|
+
|
|
528
|
+
const catalogLines = [];
|
|
529
|
+
if (codexRules.length > 0) catalogLines.push(`Codex rules: ${codexRules.slice(0, 16).map(item => item.name).join(', ')}`);
|
|
530
|
+
if (codexSkills.length > 0) catalogLines.push(`Codex skills: ${codexSkills.slice(0, 16).map(item => item.name).join(', ')}`);
|
|
531
|
+
if (claudeSkills.length > 0) catalogLines.push(`Claude skills: ${claudeSkills.slice(0, 16).map(item => item.name).join(', ')}`);
|
|
532
|
+
if (userCodexSkills.length > 0) catalogLines.push(`Home Codex skills: ${userCodexSkills.slice(0, 16).map(item => item.name).join(', ')}`);
|
|
533
|
+
if (userCodexRules.length > 0) catalogLines.push(`Home Codex rules: ${userCodexRules.slice(0, 16).map(item => item.name).join(', ')}`);
|
|
534
|
+
if (userCodexMemories.length > 0) catalogLines.push(`Home Codex memories: ${userCodexMemories.slice(0, 16).map(item => item.name).join(', ')}`);
|
|
535
|
+
if (userClaudeSkills.length > 0) catalogLines.push(`Home Claude skills: ${userClaudeSkills.slice(0, 16).map(item => item.name).join(', ')}`);
|
|
536
|
+
if (catalogLines.length > 0) {
|
|
537
|
+
lines.push('Discovered rule/skill/memory catalogs:');
|
|
538
|
+
catalogLines.forEach(item => lines.push(`- ${item}`));
|
|
539
|
+
}
|
|
540
|
+
|
|
541
|
+
return lines.join('\n');
|
|
542
|
+
}
|
|
543
|
+
|
|
336
544
|
async getProjectSignals() {
|
|
337
545
|
const signals = [];
|
|
546
|
+
const addSignal = (value) => {
|
|
547
|
+
const raw = String(value || '').toLowerCase();
|
|
548
|
+
if (!raw) return;
|
|
549
|
+
signals.push(raw);
|
|
550
|
+
for (const part of raw.split(/[^a-z0-9.+#-]+/i).filter(Boolean)) {
|
|
551
|
+
signals.push(part);
|
|
552
|
+
}
|
|
553
|
+
for (const part of raw.split(/[^a-z0-9]+/i).filter(Boolean)) {
|
|
554
|
+
signals.push(part);
|
|
555
|
+
}
|
|
556
|
+
};
|
|
338
557
|
|
|
339
558
|
try {
|
|
340
559
|
const packageJsonPath = path.join(this.projectPath, 'package.json');
|
|
341
560
|
const raw = await fs.readFile(packageJsonPath, 'utf8');
|
|
342
561
|
const pkg = JSON.parse(raw);
|
|
343
562
|
|
|
344
|
-
|
|
345
|
-
|
|
563
|
+
addSignal(pkg.name);
|
|
564
|
+
addSignal(pkg.description);
|
|
565
|
+
for (const keyword of pkg.keywords || []) {
|
|
566
|
+
addSignal(keyword);
|
|
567
|
+
}
|
|
346
568
|
|
|
347
569
|
for (const key of ['dependencies', 'devDependencies', 'peerDependencies']) {
|
|
348
570
|
const deps = pkg[key] || {};
|
|
349
571
|
for (const depName of Object.keys(deps)) {
|
|
350
|
-
|
|
572
|
+
addSignal(depName);
|
|
351
573
|
}
|
|
352
574
|
}
|
|
353
575
|
|
|
354
576
|
for (const script of Object.values(pkg.scripts || {})) {
|
|
355
|
-
|
|
577
|
+
addSignal(script);
|
|
356
578
|
}
|
|
357
579
|
} catch {
|
|
358
580
|
// Ignore package.json parsing issues.
|
|
@@ -362,8 +584,8 @@ export class ContextLoader {
|
|
|
362
584
|
const entries = await fs.readdir(this.projectPath, { withFileTypes: true });
|
|
363
585
|
for (const entry of entries) {
|
|
364
586
|
if (!entry.isFile()) continue;
|
|
365
|
-
|
|
366
|
-
|
|
587
|
+
addSignal(path.extname(entry.name).toLowerCase().slice(1));
|
|
588
|
+
addSignal(entry.name);
|
|
367
589
|
}
|
|
368
590
|
} catch {
|
|
369
591
|
// Ignore directory scan issues.
|
|
@@ -376,12 +598,23 @@ export class ContextLoader {
|
|
|
376
598
|
const catalog = new Set(['coding', 'design', 'debug', 'refactor', 'test', 'security', 'performance']);
|
|
377
599
|
const resourcePaths = this.getResourcePaths();
|
|
378
600
|
const userPaths = this.getUserResourcePaths();
|
|
379
|
-
const folders = [
|
|
601
|
+
const folders = [
|
|
602
|
+
resourcePaths.winter.skills,
|
|
603
|
+
resourcePaths.claude.skills,
|
|
604
|
+
resourcePaths.codex.skills,
|
|
605
|
+
path.join(resourcePaths.gsapSkills, 'skills'),
|
|
606
|
+
path.join(resourcePaths.hermesAgentCoreSkills, 'software-development'),
|
|
607
|
+
path.join(resourcePaths.hermesAgentCoreSkills, 'autonomous-ai-agents'),
|
|
608
|
+
path.join(resourcePaths.hermesAgentCoreSkills, 'mcp'),
|
|
609
|
+
path.join(resourcePaths.hermesAgentCoreSkills, 'github'),
|
|
610
|
+
userPaths.claudeSkills,
|
|
611
|
+
userPaths.codexSkills,
|
|
612
|
+
];
|
|
380
613
|
|
|
381
614
|
for (const folder of folders) {
|
|
382
615
|
const entries = await this.listPathEntries(folder, 200);
|
|
383
616
|
for (const entry of entries) {
|
|
384
|
-
catalog.add(entry.name);
|
|
617
|
+
catalog.add(entry.name.replace(/\.md$/i, ''));
|
|
385
618
|
}
|
|
386
619
|
}
|
|
387
620
|
|
|
@@ -406,6 +639,11 @@ export class ContextLoader {
|
|
|
406
639
|
|
|
407
640
|
if (hasAny('claude', 'agent', 'mcp', 'plugin', 'skill', 'automation', 'workflow')) {
|
|
408
641
|
['skill-creator', 'claude-automation-recommender', 'claude-md-improver', 'agent-development', 'hook-development', 'command-development', 'plugin-dev'].forEach(skill => activeSkills.add(skill));
|
|
642
|
+
['hermes-agent', 'subagent-driven-development', 'native-mcp', 'hermes-agent-skill-authoring'].forEach(skill => activeSkills.add(skill));
|
|
643
|
+
}
|
|
644
|
+
|
|
645
|
+
if (hasAny('tui', 'terminal', 'gateway', 'cron', 'schedule', 'webhook', 'memory', 'session', 'subagent')) {
|
|
646
|
+
['hermes-agent', 'subagent-driven-development', 'systematic-debugging', 'test-driven-development'].forEach(skill => activeSkills.add(skill));
|
|
409
647
|
}
|
|
410
648
|
|
|
411
649
|
if (hasAny('docs', 'markdown', 'md', 'readme', 'documentation')) {
|
|
@@ -416,6 +654,12 @@ export class ContextLoader {
|
|
|
416
654
|
['vibefigma', 'web-design-guidelines'].forEach(skill => activeSkills.add(skill));
|
|
417
655
|
}
|
|
418
656
|
|
|
657
|
+
if (hasAny('gsap', 'animation', 'animate', 'motion', 'scrolltrigger', 'scroll-trigger', 'greensock')) {
|
|
658
|
+
['gsap', 'gsap-core', 'gsap-timeline', 'gsap-scrolltrigger', 'gsap-performance'].forEach(skill => activeSkills.add(skill));
|
|
659
|
+
if (hasAny('react', 'next', 'nextjs', 'tsx', 'jsx')) activeSkills.add('gsap-react');
|
|
660
|
+
if (hasAny('vue', 'svelte', 'nuxt', 'sveltekit')) activeSkills.add('gsap-frameworks');
|
|
661
|
+
}
|
|
662
|
+
|
|
419
663
|
const filtered = [...activeSkills].filter(skill => catalog.has(skill));
|
|
420
664
|
return {
|
|
421
665
|
availableSkills: [...catalog],
|
|
@@ -93,6 +93,11 @@ export function formatToolResultForConsole(toolName, result) {
|
|
|
93
93
|
return `Fetched ${result.url} (${result.length} chars)`;
|
|
94
94
|
case 'WebSearch':
|
|
95
95
|
return `Found ${result.count} result(s)`;
|
|
96
|
+
case 'Agent':
|
|
97
|
+
case 'DelegateTask':
|
|
98
|
+
return `${result.status || 'completed'} ${result.agentId || ''}: ${result.summary || result.finalContent || ''}`.trim();
|
|
99
|
+
case 'ParallelAgent':
|
|
100
|
+
return `${result.status || 'completed'} ${result.count || 0} agent(s): ${result.summary || ''}`.trim();
|
|
96
101
|
default:
|
|
97
102
|
return result.message || '';
|
|
98
103
|
}
|
|
@@ -141,8 +141,16 @@ export class WinterInputController {
|
|
|
141
141
|
if (repl._handlingDirectClipboardPaste || repl.readlineClosed || !repl.running) return false;
|
|
142
142
|
repl._handlingDirectClipboardPaste = true;
|
|
143
143
|
try {
|
|
144
|
-
const
|
|
145
|
-
|
|
144
|
+
const payload = typeof repl.getClipboardPayload === 'function'
|
|
145
|
+
? await repl.getClipboardPayload()
|
|
146
|
+
: { type: 'image', image: await repl.getClipboardImage() };
|
|
147
|
+
if (!payload) return false;
|
|
148
|
+
|
|
149
|
+
if (payload.type === 'text') {
|
|
150
|
+
return await this.handleClipboardText(payload.text);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (!payload.image) return false;
|
|
146
154
|
|
|
147
155
|
const prompt = (repl.rl?.line || '').trim() || 'Analyze this pasted clipboard image.';
|
|
148
156
|
this.closeSlashMenu();
|
|
@@ -153,7 +161,7 @@ export class WinterInputController {
|
|
|
153
161
|
repl.inputQueue = repl.inputQueue
|
|
154
162
|
.then(async () => {
|
|
155
163
|
repl.closeInputBox?.();
|
|
156
|
-
await this.processPastedImageTask(prompt, image);
|
|
164
|
+
await this.processPastedImageTask(prompt, payload.image);
|
|
157
165
|
})
|
|
158
166
|
.catch((error) => {
|
|
159
167
|
repl.closeInputBox?.();
|
|
@@ -166,6 +174,52 @@ export class WinterInputController {
|
|
|
166
174
|
}
|
|
167
175
|
}
|
|
168
176
|
|
|
177
|
+
async handleClipboardText(text = '') {
|
|
178
|
+
const repl = this.repl;
|
|
179
|
+
const normalized = repl.normalizePastedText ? repl.normalizePastedText(text) : String(text || '');
|
|
180
|
+
if (!normalized.trim()) return false;
|
|
181
|
+
|
|
182
|
+
const currentLine = String(repl.rl?.line || '');
|
|
183
|
+
const isLargeOrMultiline = /\r|\n/.test(normalized) || repl.shouldPersistPastedText?.(normalized);
|
|
184
|
+
|
|
185
|
+
if (!isLargeOrMultiline) {
|
|
186
|
+
repl.rl?.write?.(normalized);
|
|
187
|
+
return true;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
this.closeSlashMenu();
|
|
191
|
+
repl.rl?.write?.(null, { ctrl: true, name: 'u' });
|
|
192
|
+
|
|
193
|
+
const combined = [currentLine, normalized].filter(value => String(value || '').trim()).join('\n').trim();
|
|
194
|
+
repl.inputQueue = repl.inputQueue
|
|
195
|
+
.then(async () => {
|
|
196
|
+
repl.closeInputBox?.();
|
|
197
|
+
await this.processPastedTextTask(combined);
|
|
198
|
+
})
|
|
199
|
+
.catch((error) => {
|
|
200
|
+
repl.closeInputBox?.();
|
|
201
|
+
console.log(`\n${colors.red}✖ Paste text error: ${error.message}${colors.reset}\n`);
|
|
202
|
+
if (repl.running && !repl.readlineClosed) repl.showInputPrompt?.();
|
|
203
|
+
});
|
|
204
|
+
return true;
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
async processPastedTextTask(text = '') {
|
|
208
|
+
const repl = this.repl;
|
|
209
|
+
const content = repl.normalizePastedText ? repl.normalizePastedText(text).trim() : String(text || '').trim();
|
|
210
|
+
if (!content) return;
|
|
211
|
+
|
|
212
|
+
if (repl.shouldPersistPastedText?.(content)) {
|
|
213
|
+
const paste = await repl.persistPastedText(content);
|
|
214
|
+
const reference = repl.formatPastedTextReference(paste);
|
|
215
|
+
console.log(`${colors.cyan}│ ${colors.dim}${reference}${colors.reset}`);
|
|
216
|
+
await repl.handleInput(reference);
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
await repl.handleInput(content);
|
|
221
|
+
}
|
|
222
|
+
|
|
169
223
|
async processPastedImageTask(prompt, image) {
|
|
170
224
|
const repl = this.repl;
|
|
171
225
|
repl.isProcessing = true;
|
|
@@ -195,14 +249,14 @@ export class WinterInputController {
|
|
|
195
249
|
}
|
|
196
250
|
if (repl.slashMenu.open && repl.slashMenu.line === line) return;
|
|
197
251
|
|
|
198
|
-
repl.slashMenu = { open: true, line, items: matches, selected: 0, printedLines: repl.slashMenu?.printedLines || 0 };
|
|
252
|
+
repl.slashMenu = { open: true, line, items: matches, selected: 0, offset: 0, printedLines: repl.slashMenu?.printedLines || 0 };
|
|
199
253
|
this.renderSlashMenu();
|
|
200
254
|
}
|
|
201
255
|
|
|
202
256
|
closeSlashMenu() {
|
|
203
257
|
const repl = this.repl;
|
|
204
258
|
this.clearSlashMenuRender();
|
|
205
|
-
repl.slashMenu = { open: false, line: '', items: [], selected: 0, printedLines: 0 };
|
|
259
|
+
repl.slashMenu = { open: false, line: '', items: [], selected: 0, offset: 0, printedLines: 0 };
|
|
206
260
|
}
|
|
207
261
|
|
|
208
262
|
clearSlashMenuRender() {
|
|
@@ -242,9 +296,21 @@ export class WinterInputController {
|
|
|
242
296
|
if (!repl.slashMenu.items.length) return;
|
|
243
297
|
const count = repl.slashMenu.items.length;
|
|
244
298
|
repl.slashMenu.selected = (repl.slashMenu.selected + delta + count) % count;
|
|
299
|
+
this.ensureSlashSelectionVisible();
|
|
245
300
|
this.renderSlashMenu();
|
|
246
301
|
}
|
|
247
302
|
|
|
303
|
+
ensureSlashSelectionVisible(maxDisplay = 7) {
|
|
304
|
+
const menu = this.repl.slashMenu;
|
|
305
|
+
if (!menu?.items?.length) return;
|
|
306
|
+
const selected = Math.max(0, Math.min(menu.selected || 0, menu.items.length - 1));
|
|
307
|
+
let offset = Math.max(0, Number(menu.offset || 0));
|
|
308
|
+
if (selected < offset) offset = selected;
|
|
309
|
+
if (selected >= offset + maxDisplay) offset = selected - maxDisplay + 1;
|
|
310
|
+
const maxOffset = Math.max(0, menu.items.length - maxDisplay);
|
|
311
|
+
menu.offset = Math.max(0, Math.min(offset, maxOffset));
|
|
312
|
+
}
|
|
313
|
+
|
|
248
314
|
acceptSlashSelection() {
|
|
249
315
|
const repl = this.repl;
|
|
250
316
|
const item = repl.slashMenu.items[repl.slashMenu.selected];
|
|
@@ -268,20 +334,23 @@ export class WinterInputController {
|
|
|
268
334
|
const matches = repl.slashMenu.items;
|
|
269
335
|
if (!matches.length) return;
|
|
270
336
|
|
|
271
|
-
const maxDisplay =
|
|
272
|
-
|
|
337
|
+
const maxDisplay = 7;
|
|
338
|
+
this.ensureSlashSelectionVisible(maxDisplay);
|
|
339
|
+
const offset = Math.max(0, Number(repl.slashMenu.offset || 0));
|
|
340
|
+
const displayedMatches = matches.slice(offset, offset + maxDisplay);
|
|
273
341
|
const body = [
|
|
274
|
-
`${colors.dim}Tab selects. Esc closes. Enter sends the current line.${colors.reset}`,
|
|
342
|
+
`${colors.dim}Tab selects. Esc closes. Enter sends the current line. Up/Down scroll.${colors.reset}`,
|
|
275
343
|
'',
|
|
276
344
|
...displayedMatches.map((item, index) => {
|
|
277
345
|
const usage = item.usage ? ` ${colors.dim}${item.usage}${colors.reset}` : '';
|
|
278
|
-
const
|
|
346
|
+
const absoluteIndex = offset + index;
|
|
347
|
+
const pointer = absoluteIndex === repl.slashMenu.selected ? `${colors.green}>${colors.reset}` : ' ';
|
|
279
348
|
return `${pointer} ${colors.cyan}${padVisible(item.cmd, 16)}${colors.reset} ${colors.dim}${item.desc}${colors.reset}${usage}`;
|
|
280
349
|
}),
|
|
281
350
|
];
|
|
282
351
|
|
|
283
352
|
if (matches.length > maxDisplay) {
|
|
284
|
-
body.push(` ${colors.dim}
|
|
353
|
+
body.push(` ${colors.dim}${offset + 1}-${Math.min(offset + maxDisplay, matches.length)} / ${matches.length}${colors.reset}`);
|
|
285
354
|
}
|
|
286
355
|
|
|
287
356
|
this.clearSlashMenuRender();
|