erosolar-cli 1.7.13 → 1.7.15
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/dist/core/responseVerifier.d.ts +79 -0
- package/dist/core/responseVerifier.d.ts.map +1 -0
- package/dist/core/responseVerifier.js +443 -0
- package/dist/core/responseVerifier.js.map +1 -0
- package/dist/shell/interactiveShell.d.ts +5 -0
- package/dist/shell/interactiveShell.d.ts.map +1 -1
- package/dist/shell/interactiveShell.js +45 -14
- package/dist/shell/interactiveShell.js.map +1 -1
- package/dist/ui/ShellUIAdapter.d.ts +3 -0
- package/dist/ui/ShellUIAdapter.d.ts.map +1 -1
- package/dist/ui/ShellUIAdapter.js +4 -10
- package/dist/ui/ShellUIAdapter.js.map +1 -1
- package/dist/ui/display.d.ts +15 -0
- package/dist/ui/display.d.ts.map +1 -1
- package/dist/ui/display.js +57 -0
- package/dist/ui/display.js.map +1 -1
- package/dist/ui/persistentPrompt.d.ts +4 -0
- package/dist/ui/persistentPrompt.d.ts.map +1 -1
- package/dist/ui/persistentPrompt.js +10 -11
- package/dist/ui/persistentPrompt.js.map +1 -1
- package/package.json +1 -1
- package/dist/bin/core/agent.js +0 -362
- package/dist/bin/core/agentProfileManifest.js +0 -187
- package/dist/bin/core/agentProfiles.js +0 -34
- package/dist/bin/core/agentRulebook.js +0 -135
- package/dist/bin/core/agentSchemaLoader.js +0 -233
- package/dist/bin/core/contextManager.js +0 -412
- package/dist/bin/core/contextWindow.js +0 -122
- package/dist/bin/core/customCommands.js +0 -80
- package/dist/bin/core/errors/apiKeyErrors.js +0 -114
- package/dist/bin/core/errors/errorTypes.js +0 -340
- package/dist/bin/core/errors/safetyValidator.js +0 -304
- package/dist/bin/core/errors.js +0 -32
- package/dist/bin/core/modelDiscovery.js +0 -755
- package/dist/bin/core/preferences.js +0 -224
- package/dist/bin/core/schemaValidator.js +0 -92
- package/dist/bin/core/secretStore.js +0 -199
- package/dist/bin/core/sessionStore.js +0 -187
- package/dist/bin/core/toolRuntime.js +0 -290
- package/dist/bin/core/types.js +0 -1
- package/dist/bin/shell/bracketedPasteManager.js +0 -350
- package/dist/bin/shell/fileChangeTracker.js +0 -65
- package/dist/bin/shell/interactiveShell.js +0 -2908
- package/dist/bin/shell/liveStatus.js +0 -78
- package/dist/bin/shell/shellApp.js +0 -290
- package/dist/bin/shell/systemPrompt.js +0 -60
- package/dist/bin/shell/updateManager.js +0 -108
- package/dist/bin/ui/ShellUIAdapter.js +0 -459
- package/dist/bin/ui/UnifiedUIController.js +0 -183
- package/dist/bin/ui/animation/AnimationScheduler.js +0 -430
- package/dist/bin/ui/codeHighlighter.js +0 -854
- package/dist/bin/ui/designSystem.js +0 -121
- package/dist/bin/ui/display.js +0 -1222
- package/dist/bin/ui/interrupts/InterruptManager.js +0 -437
- package/dist/bin/ui/layout.js +0 -139
- package/dist/bin/ui/orchestration/StatusOrchestrator.js +0 -403
- package/dist/bin/ui/outputMode.js +0 -38
- package/dist/bin/ui/persistentPrompt.js +0 -183
- package/dist/bin/ui/richText.js +0 -338
- package/dist/bin/ui/shortcutsHelp.js +0 -87
- package/dist/bin/ui/telemetry/UITelemetry.js +0 -443
- package/dist/bin/ui/textHighlighter.js +0 -210
- package/dist/bin/ui/theme.js +0 -116
- package/dist/bin/ui/toolDisplay.js +0 -423
- package/dist/bin/ui/toolDisplayAdapter.js +0 -357
|
@@ -1,78 +0,0 @@
|
|
|
1
|
-
export class LiveStatusTracker {
|
|
2
|
-
constructor() {
|
|
3
|
-
this.base = null;
|
|
4
|
-
this.overrides = new Map();
|
|
5
|
-
this.overrideOrder = [];
|
|
6
|
-
this.listeners = new Set();
|
|
7
|
-
}
|
|
8
|
-
subscribe(listener) {
|
|
9
|
-
this.listeners.add(listener);
|
|
10
|
-
listener(this.currentState());
|
|
11
|
-
return () => {
|
|
12
|
-
this.listeners.delete(listener);
|
|
13
|
-
};
|
|
14
|
-
}
|
|
15
|
-
setBase(text, options = {}) {
|
|
16
|
-
if (!text?.trim()) {
|
|
17
|
-
this.base = null;
|
|
18
|
-
this.emit();
|
|
19
|
-
return;
|
|
20
|
-
}
|
|
21
|
-
this.base = this.buildState(text, options);
|
|
22
|
-
this.emit();
|
|
23
|
-
}
|
|
24
|
-
pushOverride(id, text, options = {}) {
|
|
25
|
-
if (!id?.trim() || !text?.trim()) {
|
|
26
|
-
return;
|
|
27
|
-
}
|
|
28
|
-
this.overrides.set(id, this.buildState(text, options));
|
|
29
|
-
if (!this.overrideOrder.includes(id)) {
|
|
30
|
-
this.overrideOrder.push(id);
|
|
31
|
-
}
|
|
32
|
-
this.emit();
|
|
33
|
-
}
|
|
34
|
-
clearOverride(id) {
|
|
35
|
-
if (!id?.trim()) {
|
|
36
|
-
return;
|
|
37
|
-
}
|
|
38
|
-
this.overrides.delete(id);
|
|
39
|
-
const index = this.overrideOrder.indexOf(id);
|
|
40
|
-
if (index >= 0) {
|
|
41
|
-
this.overrideOrder.splice(index, 1);
|
|
42
|
-
}
|
|
43
|
-
this.emit();
|
|
44
|
-
}
|
|
45
|
-
clearOverrides() {
|
|
46
|
-
this.overrides.clear();
|
|
47
|
-
this.overrideOrder.length = 0;
|
|
48
|
-
this.emit();
|
|
49
|
-
}
|
|
50
|
-
reset() {
|
|
51
|
-
this.base = null;
|
|
52
|
-
this.clearOverrides();
|
|
53
|
-
}
|
|
54
|
-
currentState() {
|
|
55
|
-
for (let index = this.overrideOrder.length - 1; index >= 0; index -= 1) {
|
|
56
|
-
const id = this.overrideOrder[index];
|
|
57
|
-
const state = this.overrides.get(id);
|
|
58
|
-
if (state) {
|
|
59
|
-
return state;
|
|
60
|
-
}
|
|
61
|
-
}
|
|
62
|
-
return this.base;
|
|
63
|
-
}
|
|
64
|
-
buildState(text, options) {
|
|
65
|
-
return {
|
|
66
|
-
text: text.trim(),
|
|
67
|
-
detail: options.detail?.trim() || undefined,
|
|
68
|
-
tone: options.tone,
|
|
69
|
-
startedAt: Date.now(),
|
|
70
|
-
};
|
|
71
|
-
}
|
|
72
|
-
emit() {
|
|
73
|
-
const state = this.currentState();
|
|
74
|
-
for (const listener of this.listeners) {
|
|
75
|
-
listener(state);
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
@@ -1,290 +0,0 @@
|
|
|
1
|
-
import { exit } from 'node:process';
|
|
2
|
-
import { readFileSync } from 'node:fs';
|
|
3
|
-
import { dirname, resolve } from 'node:path';
|
|
4
|
-
import { fileURLToPath } from 'node:url';
|
|
5
|
-
import '../config.js';
|
|
6
|
-
import { buildWorkspaceContext, resolveWorkspaceCaptureOptions } from '../workspace.js';
|
|
7
|
-
import { InteractiveShell } from './interactiveShell.js';
|
|
8
|
-
import { display } from '../ui/display.js';
|
|
9
|
-
import { loadActiveProfilePreference, loadModelPreference, loadToolSettings, } from '../core/preferences.js';
|
|
10
|
-
import { createNodeRuntime } from '../runtime/node.js';
|
|
11
|
-
import { buildEnabledToolSet, evaluateToolPermissions, isPluginEnabled, } from '../capabilities/toolRegistry.js';
|
|
12
|
-
import { listAgentProfiles, hasAgentProfile } from '../core/agentProfiles.js';
|
|
13
|
-
import { maybeOfferCliUpdate } from './updateManager.js';
|
|
14
|
-
import { LiveStatusTracker } from './liveStatus.js';
|
|
15
|
-
import { buildInteractiveSystemPrompt } from './systemPrompt.js';
|
|
16
|
-
import { ShellUIAdapter } from '../ui/ShellUIAdapter.js';
|
|
17
|
-
import { stdout } from 'node:process';
|
|
18
|
-
import { setPlainOutputMode } from '../ui/outputMode.js';
|
|
19
|
-
import { getPluginIdsFromFlags, loadPlugin } from '../plugins/index.js';
|
|
20
|
-
import { quickCheckProviders } from '../core/modelDiscovery.js';
|
|
21
|
-
/**
|
|
22
|
-
* Launch the interactive shell with full capability awareness for the selected profile.
|
|
23
|
-
*/
|
|
24
|
-
export async function launchShell(defaultProfile, options = {}) {
|
|
25
|
-
try {
|
|
26
|
-
const { profileOverride, promptArgs, sessionRestore, plainOutput, pluginFlags } = parseLaunchArguments(process.argv.slice(2));
|
|
27
|
-
if (plainOutput) {
|
|
28
|
-
setPlainOutputMode(true);
|
|
29
|
-
}
|
|
30
|
-
// Load enabled plugins based on CLI flags
|
|
31
|
-
const enabledPluginIds = getPluginIdsFromFlags(pluginFlags);
|
|
32
|
-
const envProfileOverride = process.env['EROSOLAR_PROFILE']?.trim() || null;
|
|
33
|
-
const allowProfileSelection = Boolean(options.enableProfileSelection);
|
|
34
|
-
const availableProfiles = listAgentProfiles();
|
|
35
|
-
const rawSavedProfile = allowProfileSelection ? loadActiveProfilePreference() : null;
|
|
36
|
-
const savedProfile = rawSavedProfile && hasAgentProfile(rawSavedProfile) ? rawSavedProfile : null;
|
|
37
|
-
const profile = resolveLaunchProfile({
|
|
38
|
-
defaultProfile,
|
|
39
|
-
availableProfiles,
|
|
40
|
-
cliOverride: profileOverride,
|
|
41
|
-
envOverride: envProfileOverride,
|
|
42
|
-
savedProfile,
|
|
43
|
-
allowSavedProfile: allowProfileSelection,
|
|
44
|
-
});
|
|
45
|
-
const workingDir = process.cwd();
|
|
46
|
-
const workspaceOptions = resolveWorkspaceCaptureOptions(process.env);
|
|
47
|
-
const workspaceContext = buildWorkspaceContext(workingDir, workspaceOptions);
|
|
48
|
-
const statusTracker = new LiveStatusTracker();
|
|
49
|
-
// Create unified UI adapter early to get the tool observer
|
|
50
|
-
// We'll add the file change callback after creating the shell
|
|
51
|
-
const uiAdapter = new ShellUIAdapter(stdout, display, {
|
|
52
|
-
useUnifiedUI: true,
|
|
53
|
-
preserveCompatibility: true,
|
|
54
|
-
enableTelemetry: true,
|
|
55
|
-
debugMode: false,
|
|
56
|
-
});
|
|
57
|
-
// Use the unified UI adapter's tool observer instead of the legacy one
|
|
58
|
-
const toolObserver = uiAdapter.createToolObserver();
|
|
59
|
-
const toolSettings = loadToolSettings();
|
|
60
|
-
const toolSelection = buildEnabledToolSet(toolSettings);
|
|
61
|
-
const permissionSummary = evaluateToolPermissions(toolSelection);
|
|
62
|
-
const pluginFilter = (plugin) => isPluginEnabled(plugin.id, permissionSummary.allowedPluginIds);
|
|
63
|
-
const runtime = await createNodeRuntime({
|
|
64
|
-
profile,
|
|
65
|
-
workspaceContext,
|
|
66
|
-
workingDir,
|
|
67
|
-
toolObserver,
|
|
68
|
-
adapterOptions: {
|
|
69
|
-
filter: pluginFilter,
|
|
70
|
-
},
|
|
71
|
-
});
|
|
72
|
-
const session = runtime.session;
|
|
73
|
-
const profileConfig = session.profileConfig;
|
|
74
|
-
const providerTools = session.toolRuntime.listProviderTools();
|
|
75
|
-
const persistedSelection = profileConfig.modelLocked || profileConfig.providerLocked
|
|
76
|
-
? null
|
|
77
|
-
: loadModelPreference(profile);
|
|
78
|
-
const initialModel = persistedSelection ?? {
|
|
79
|
-
provider: profileConfig.provider,
|
|
80
|
-
model: profileConfig.model,
|
|
81
|
-
temperature: profileConfig.temperature,
|
|
82
|
-
maxTokens: profileConfig.maxTokens,
|
|
83
|
-
};
|
|
84
|
-
const enhancedSystemPrompt = buildInteractiveSystemPrompt(profileConfig.systemPrompt, profileConfig.label, providerTools);
|
|
85
|
-
const version = readPackageVersion();
|
|
86
|
-
display.showWelcome(profileConfig.label, profile, initialModel.model, initialModel.provider, workingDir, version);
|
|
87
|
-
display.showAvailableTools(providerTools);
|
|
88
|
-
// Show available providers and models on launch
|
|
89
|
-
try {
|
|
90
|
-
const providerStatus = await quickCheckProviders();
|
|
91
|
-
display.showProvidersStatus(providerStatus);
|
|
92
|
-
}
|
|
93
|
-
catch {
|
|
94
|
-
// Silently ignore provider check errors
|
|
95
|
-
}
|
|
96
|
-
const continueLaunch = await maybeOfferCliUpdate(version);
|
|
97
|
-
if (!continueLaunch) {
|
|
98
|
-
return;
|
|
99
|
-
}
|
|
100
|
-
if (permissionSummary.warnings.length) {
|
|
101
|
-
reportSkippedTools(permissionSummary.warnings);
|
|
102
|
-
}
|
|
103
|
-
const agentSelection = allowProfileSelection
|
|
104
|
-
? {
|
|
105
|
-
defaultProfile,
|
|
106
|
-
persistedProfile: savedProfile,
|
|
107
|
-
options: availableProfiles,
|
|
108
|
-
}
|
|
109
|
-
: undefined;
|
|
110
|
-
// Load optional plugins if enabled via CLI flags
|
|
111
|
-
for (const pluginId of enabledPluginIds) {
|
|
112
|
-
await loadPlugin(pluginId);
|
|
113
|
-
}
|
|
114
|
-
const shell = new InteractiveShell({
|
|
115
|
-
profile,
|
|
116
|
-
profileLabel: profileConfig.label,
|
|
117
|
-
workingDir,
|
|
118
|
-
session,
|
|
119
|
-
baseSystemPrompt: enhancedSystemPrompt,
|
|
120
|
-
initialModel,
|
|
121
|
-
agentSelection,
|
|
122
|
-
statusTracker,
|
|
123
|
-
uiAdapter,
|
|
124
|
-
workspaceOptions,
|
|
125
|
-
sessionRestore,
|
|
126
|
-
enabledPlugins: enabledPluginIds,
|
|
127
|
-
});
|
|
128
|
-
const initialPrompt = promptArgs.join(' ').trim();
|
|
129
|
-
await shell.start(initialPrompt || undefined);
|
|
130
|
-
}
|
|
131
|
-
catch (error) {
|
|
132
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
133
|
-
display.showError(message);
|
|
134
|
-
exit(1);
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
function parseLaunchArguments(argv) {
|
|
138
|
-
const promptArgs = [];
|
|
139
|
-
let override = null;
|
|
140
|
-
let plainOutput = false;
|
|
141
|
-
let sessionRestore = undefined;
|
|
142
|
-
const pluginFlags = {};
|
|
143
|
-
const expectValue = (flag, value) => {
|
|
144
|
-
if (value && value.trim()) {
|
|
145
|
-
return value.trim();
|
|
146
|
-
}
|
|
147
|
-
throw new Error(`Missing value for ${flag}.`);
|
|
148
|
-
};
|
|
149
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
150
|
-
const token = argv[index];
|
|
151
|
-
if (!token) {
|
|
152
|
-
continue;
|
|
153
|
-
}
|
|
154
|
-
if (token === '--profile' || token === '-p') {
|
|
155
|
-
const value = expectValue(token, argv[index + 1]);
|
|
156
|
-
override = value;
|
|
157
|
-
index += 1;
|
|
158
|
-
continue;
|
|
159
|
-
}
|
|
160
|
-
if (token.startsWith('--profile=')) {
|
|
161
|
-
const [, candidate] = token.split('=');
|
|
162
|
-
if (!candidate?.trim()) {
|
|
163
|
-
throw new Error('Missing value for --profile.');
|
|
164
|
-
}
|
|
165
|
-
override = candidate.trim();
|
|
166
|
-
continue;
|
|
167
|
-
}
|
|
168
|
-
if (token === '--plain' || token === '--plain-output') {
|
|
169
|
-
plainOutput = true;
|
|
170
|
-
continue;
|
|
171
|
-
}
|
|
172
|
-
if (token === '--restore-session') {
|
|
173
|
-
sessionRestore = { mode: 'autosave' };
|
|
174
|
-
continue;
|
|
175
|
-
}
|
|
176
|
-
if (token === '--session' || token === '-s') {
|
|
177
|
-
const value = expectValue(token, argv[index + 1]);
|
|
178
|
-
sessionRestore = { mode: 'session-id', sessionId: value };
|
|
179
|
-
index += 1;
|
|
180
|
-
continue;
|
|
181
|
-
}
|
|
182
|
-
if (token.startsWith('--session=')) {
|
|
183
|
-
const [, candidate] = token.split('=');
|
|
184
|
-
if (!candidate?.trim()) {
|
|
185
|
-
throw new Error('Missing value for --session.');
|
|
186
|
-
}
|
|
187
|
-
sessionRestore = { mode: 'session-id', sessionId: candidate.trim() };
|
|
188
|
-
continue;
|
|
189
|
-
}
|
|
190
|
-
// Plugin flags
|
|
191
|
-
if (token === '--alpha-zero') {
|
|
192
|
-
pluginFlags.alphaZero = true;
|
|
193
|
-
continue;
|
|
194
|
-
}
|
|
195
|
-
if (token === '--no-alpha-zero') {
|
|
196
|
-
pluginFlags.alphaZero = false;
|
|
197
|
-
continue;
|
|
198
|
-
}
|
|
199
|
-
if (token === '--coding') {
|
|
200
|
-
pluginFlags.coding = true;
|
|
201
|
-
continue;
|
|
202
|
-
}
|
|
203
|
-
if (token === '--no-coding') {
|
|
204
|
-
pluginFlags.coding = false;
|
|
205
|
-
continue;
|
|
206
|
-
}
|
|
207
|
-
if (token === '--security') {
|
|
208
|
-
pluginFlags.security = true;
|
|
209
|
-
continue;
|
|
210
|
-
}
|
|
211
|
-
if (token === '--no-security') {
|
|
212
|
-
pluginFlags.security = false;
|
|
213
|
-
continue;
|
|
214
|
-
}
|
|
215
|
-
if (token === '--all-plugins') {
|
|
216
|
-
pluginFlags.allPlugins = true;
|
|
217
|
-
continue;
|
|
218
|
-
}
|
|
219
|
-
promptArgs.push(token);
|
|
220
|
-
}
|
|
221
|
-
return {
|
|
222
|
-
profileOverride: override,
|
|
223
|
-
promptArgs,
|
|
224
|
-
plainOutput,
|
|
225
|
-
sessionRestore,
|
|
226
|
-
pluginFlags,
|
|
227
|
-
};
|
|
228
|
-
}
|
|
229
|
-
function resolveLaunchProfile(input) {
|
|
230
|
-
if (input.cliOverride) {
|
|
231
|
-
const resolved = matchProfile(input.cliOverride, input.availableProfiles);
|
|
232
|
-
if (!resolved) {
|
|
233
|
-
throw new Error(`Unknown agent profile "${input.cliOverride}". Run "/agents" to view available options.`);
|
|
234
|
-
}
|
|
235
|
-
return resolved;
|
|
236
|
-
}
|
|
237
|
-
if (input.envOverride?.trim()) {
|
|
238
|
-
const resolved = matchProfile(input.envOverride, input.availableProfiles);
|
|
239
|
-
if (!resolved) {
|
|
240
|
-
throw new Error(`Unknown agent profile "${input.envOverride}" provided via EROSOLAR_PROFILE.`);
|
|
241
|
-
}
|
|
242
|
-
return resolved;
|
|
243
|
-
}
|
|
244
|
-
if (input.allowSavedProfile) {
|
|
245
|
-
const saved = matchProfile(input.savedProfile, input.availableProfiles);
|
|
246
|
-
if (saved) {
|
|
247
|
-
return saved;
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
const fallback = matchProfile(input.defaultProfile, input.availableProfiles);
|
|
251
|
-
if (fallback) {
|
|
252
|
-
return fallback;
|
|
253
|
-
}
|
|
254
|
-
throw new Error('No registered CLI profile is available.');
|
|
255
|
-
}
|
|
256
|
-
function matchProfile(candidate, availableProfiles) {
|
|
257
|
-
if (!candidate) {
|
|
258
|
-
return null;
|
|
259
|
-
}
|
|
260
|
-
const trimmed = candidate.trim();
|
|
261
|
-
if (!trimmed) {
|
|
262
|
-
return null;
|
|
263
|
-
}
|
|
264
|
-
if (hasAgentProfile(trimmed)) {
|
|
265
|
-
return trimmed;
|
|
266
|
-
}
|
|
267
|
-
const lower = trimmed.toLowerCase();
|
|
268
|
-
const match = availableProfiles.find((profile) => profile.name.toLowerCase() === lower);
|
|
269
|
-
return match ? match.name : null;
|
|
270
|
-
}
|
|
271
|
-
function reportSkippedTools(entries) {
|
|
272
|
-
for (const warning of entries) {
|
|
273
|
-
const details = warning.reason === 'missing-secret' && warning.secretId
|
|
274
|
-
? `missing ${warning.secretId}`
|
|
275
|
-
: warning.reason;
|
|
276
|
-
const suffix = warning.secretId ? ' (use /secrets to configure it)' : '';
|
|
277
|
-
display.showWarning(`Skipped ${warning.label} — ${details}${suffix}`);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
function readPackageVersion() {
|
|
281
|
-
try {
|
|
282
|
-
const filePath = fileURLToPath(import.meta.url);
|
|
283
|
-
const packagePath = resolve(dirname(filePath), '../../package.json');
|
|
284
|
-
const payload = JSON.parse(readFileSync(packagePath, 'utf8'));
|
|
285
|
-
return typeof payload.version === 'string' ? payload.version : '0.0.0';
|
|
286
|
-
}
|
|
287
|
-
catch {
|
|
288
|
-
return '0.0.0';
|
|
289
|
-
}
|
|
290
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
export function buildInteractiveSystemPrompt(basePrompt, profileLabel, tools) {
|
|
2
|
-
const name = profileLabel || 'Active Agent';
|
|
3
|
-
const toolSummary = formatToolSummary(tools, { maxDescriptionLength: 80 });
|
|
4
|
-
const capabilityLines = [
|
|
5
|
-
'✓ Full file system read/write access',
|
|
6
|
-
'✓ Bash command execution (including git commands)',
|
|
7
|
-
'✓ Git operations: status, diff, add, commit, push, branch management',
|
|
8
|
-
'✓ Advanced code search and analysis',
|
|
9
|
-
'✓ Workspace snapshot is guaranteed and immutable',
|
|
10
|
-
'✓ Tool usage is narrated to the operator in real time',
|
|
11
|
-
];
|
|
12
|
-
const behaviorGuidelines = [
|
|
13
|
-
'Narrate intent before using tools',
|
|
14
|
-
'Follow rulebook guardrails; cite rule IDs when referencing',
|
|
15
|
-
'Use README, package.json, and workspace context as evidence',
|
|
16
|
-
'Read before editing; re-read after to confirm',
|
|
17
|
-
'Keep responses concise; reference actual commands/files',
|
|
18
|
-
'Request missing info explicitly',
|
|
19
|
-
'For self-improvement tasks: make changes, commit frequently, and push when complete',
|
|
20
|
-
];
|
|
21
|
-
const behaviorSection = behaviorGuidelines
|
|
22
|
-
.map((line, index) => `${index + 1}. ${line}`)
|
|
23
|
-
.join('\n');
|
|
24
|
-
return `${basePrompt}
|
|
25
|
-
|
|
26
|
-
You are ${name}, running in an interactive shell with full capabilities:
|
|
27
|
-
|
|
28
|
-
AVAILABLE TOOLS:
|
|
29
|
-
${toolSummary}
|
|
30
|
-
|
|
31
|
-
CAPABILITIES:
|
|
32
|
-
${capabilityLines.join('\n')}
|
|
33
|
-
|
|
34
|
-
BEHAVIOR GUIDELINES:
|
|
35
|
-
${behaviorSection}
|
|
36
|
-
|
|
37
|
-
Remember: answer truthfully, ground everything in the workspace, and let the logs show what you actually did.`;
|
|
38
|
-
}
|
|
39
|
-
function formatToolSummary(tools, options = {}) {
|
|
40
|
-
if (!tools.length) {
|
|
41
|
-
return '- (no tools are registered in this session)';
|
|
42
|
-
}
|
|
43
|
-
return tools
|
|
44
|
-
.map((tool) => {
|
|
45
|
-
const description = tool.description ? sanitizeWhitespace(tool.description) : 'No description provided.';
|
|
46
|
-
const summary = truncate(description, options.maxDescriptionLength);
|
|
47
|
-
return `- ${tool.name}: ${summary}`;
|
|
48
|
-
})
|
|
49
|
-
.join('\n');
|
|
50
|
-
}
|
|
51
|
-
function sanitizeWhitespace(value) {
|
|
52
|
-
return value.replace(/\s+/g, ' ').trim();
|
|
53
|
-
}
|
|
54
|
-
function truncate(value, maxLength) {
|
|
55
|
-
if (!maxLength || value.length <= maxLength) {
|
|
56
|
-
return value;
|
|
57
|
-
}
|
|
58
|
-
const safeLength = Math.max(0, maxLength - 3);
|
|
59
|
-
return `${value.slice(0, safeLength).trimEnd()}...`;
|
|
60
|
-
}
|
|
@@ -1,108 +0,0 @@
|
|
|
1
|
-
import { spawn } from 'node:child_process';
|
|
2
|
-
import { stdin as input, stdout as output } from 'node:process';
|
|
3
|
-
import { createInterface } from 'node:readline/promises';
|
|
4
|
-
import { display } from '../ui/display.js';
|
|
5
|
-
const PACKAGE_NAME = 'erosolar-cli';
|
|
6
|
-
const REGISTRY_URL = `https://registry.npmjs.org/${PACKAGE_NAME}/latest`;
|
|
7
|
-
const FETCH_TIMEOUT_MS = 4000;
|
|
8
|
-
export async function maybeOfferCliUpdate(currentVersion) {
|
|
9
|
-
try {
|
|
10
|
-
const latestVersion = await fetchLatestVersion();
|
|
11
|
-
if (!latestVersion) {
|
|
12
|
-
return true;
|
|
13
|
-
}
|
|
14
|
-
if (!isNewerVersion(latestVersion, currentVersion)) {
|
|
15
|
-
return true;
|
|
16
|
-
}
|
|
17
|
-
display.showInfo([
|
|
18
|
-
`A new Erosolar CLI release is available.`,
|
|
19
|
-
`Current version: ${currentVersion}`,
|
|
20
|
-
`Latest version: ${latestVersion}`,
|
|
21
|
-
].join('\n'));
|
|
22
|
-
if (!input.isTTY || !output.isTTY) {
|
|
23
|
-
display.showInfo(`Run "npm install -g ${PACKAGE_NAME}@latest" when you're ready to upgrade.`);
|
|
24
|
-
return true;
|
|
25
|
-
}
|
|
26
|
-
const shouldUpdate = await promptForUpdate();
|
|
27
|
-
if (!shouldUpdate) {
|
|
28
|
-
display.showInfo(`Continuing with ${currentVersion}. You can upgrade later via "npm install -g ${PACKAGE_NAME}@latest".`);
|
|
29
|
-
return true;
|
|
30
|
-
}
|
|
31
|
-
const success = await installLatestVersion();
|
|
32
|
-
if (success) {
|
|
33
|
-
display.showInfo(`Update complete. Relaunch the CLI to start using ${PACKAGE_NAME}@${latestVersion}.`);
|
|
34
|
-
return false;
|
|
35
|
-
}
|
|
36
|
-
display.showWarning(`Failed to install ${PACKAGE_NAME}@latest. Please run "npm install -g ${PACKAGE_NAME}@latest" manually.`);
|
|
37
|
-
return true;
|
|
38
|
-
}
|
|
39
|
-
catch {
|
|
40
|
-
return true;
|
|
41
|
-
}
|
|
42
|
-
}
|
|
43
|
-
async function fetchLatestVersion() {
|
|
44
|
-
const controller = new AbortController();
|
|
45
|
-
const timeout = setTimeout(() => controller.abort(), FETCH_TIMEOUT_MS);
|
|
46
|
-
timeout.unref?.();
|
|
47
|
-
try {
|
|
48
|
-
const response = await fetch(REGISTRY_URL, {
|
|
49
|
-
headers: { 'user-agent': `${PACKAGE_NAME}/update-check` },
|
|
50
|
-
signal: controller.signal,
|
|
51
|
-
});
|
|
52
|
-
if (!response.ok) {
|
|
53
|
-
return null;
|
|
54
|
-
}
|
|
55
|
-
const payload = (await response.json());
|
|
56
|
-
return typeof payload.version === 'string' ? payload.version : null;
|
|
57
|
-
}
|
|
58
|
-
catch {
|
|
59
|
-
return null;
|
|
60
|
-
}
|
|
61
|
-
finally {
|
|
62
|
-
clearTimeout(timeout);
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
function isNewerVersion(latest, current) {
|
|
66
|
-
const latestParts = normalizeVersion(latest);
|
|
67
|
-
const currentParts = normalizeVersion(current);
|
|
68
|
-
const length = Math.max(latestParts.length, currentParts.length);
|
|
69
|
-
for (let index = 0; index < length; index += 1) {
|
|
70
|
-
const nextLatest = latestParts[index] ?? 0;
|
|
71
|
-
const nextCurrent = currentParts[index] ?? 0;
|
|
72
|
-
if (nextLatest > nextCurrent) {
|
|
73
|
-
return true;
|
|
74
|
-
}
|
|
75
|
-
if (nextLatest < nextCurrent) {
|
|
76
|
-
return false;
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
return false;
|
|
80
|
-
}
|
|
81
|
-
function normalizeVersion(value) {
|
|
82
|
-
const sanitized = value.split('-')[0] ?? '';
|
|
83
|
-
return sanitized
|
|
84
|
-
.split('.')
|
|
85
|
-
.map((segment) => Number.parseInt(segment, 10))
|
|
86
|
-
.filter((segment) => Number.isFinite(segment) && segment >= 0);
|
|
87
|
-
}
|
|
88
|
-
async function promptForUpdate() {
|
|
89
|
-
const prompt = createInterface({ input, output });
|
|
90
|
-
try {
|
|
91
|
-
const answer = await prompt.question('Update now? (Y/n): ');
|
|
92
|
-
const normalized = answer.trim().toLowerCase();
|
|
93
|
-
return normalized === '' || normalized === 'y' || normalized === 'yes';
|
|
94
|
-
}
|
|
95
|
-
finally {
|
|
96
|
-
prompt.close();
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
async function installLatestVersion() {
|
|
100
|
-
const binary = process.platform === 'win32' ? 'npm.cmd' : 'npm';
|
|
101
|
-
return new Promise((resolve) => {
|
|
102
|
-
const child = spawn(binary, ['install', '-g', `${PACKAGE_NAME}@latest`], {
|
|
103
|
-
stdio: 'inherit',
|
|
104
|
-
});
|
|
105
|
-
child.on('error', () => resolve(false));
|
|
106
|
-
child.on('close', (code) => resolve(code === 0));
|
|
107
|
-
});
|
|
108
|
-
}
|