@elyracode/coding-agent 0.7.9 → 0.7.11
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 +22 -1
- package/README.md +1 -5
- package/dist/core/slash-commands.d.ts.map +1 -1
- package/dist/core/slash-commands.js +2 -0
- package/dist/core/slash-commands.js.map +1 -1
- package/dist/core/tools/edit-diff.d.ts +0 -8
- package/dist/core/tools/edit-diff.d.ts.map +1 -1
- package/dist/core/tools/edit-diff.js +5 -2
- package/dist/core/tools/edit-diff.js.map +1 -1
- package/dist/core/tools/edit.d.ts.map +1 -1
- package/dist/core/tools/edit.js +33 -1
- package/dist/core/tools/edit.js.map +1 -1
- package/dist/core/tools/read.d.ts +7 -0
- package/dist/core/tools/read.d.ts.map +1 -1
- package/dist/core/tools/read.js +271 -46
- package/dist/core/tools/read.js.map +1 -1
- package/dist/modes/interactive/interactive-mode.d.ts +5 -0
- package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
- package/dist/modes/interactive/interactive-mode.js +143 -3
- package/dist/modes/interactive/interactive-mode.js.map +1 -1
- package/docs/extensions.md +5 -0
- package/docs/index.md +1 -7
- package/docs/sessions.md +10 -0
- package/docs/usage.md +7 -0
- package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
- package/examples/extensions/custom-provider-anthropic/package.json +1 -1
- package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
- package/examples/extensions/sandbox/package-lock.json +2 -2
- package/examples/extensions/sandbox/package.json +1 -1
- package/examples/extensions/with-deps/package-lock.json +2 -2
- package/examples/extensions/with-deps/package.json +1 -1
- package/package.json +4 -4
|
@@ -9,7 +9,7 @@ import * as path from "node:path";
|
|
|
9
9
|
import { getProviders, } from "@elyracode/ai";
|
|
10
10
|
import { CombinedAutocompleteProvider, Container, fuzzyFilter, Loader, Markdown, matchesKey, ProcessTerminal, Spacer, setKeybindings, Text, TruncatedText, TUI, visibleWidth, } from "@elyracode/tui";
|
|
11
11
|
import { spawn, spawnSync } from "child_process";
|
|
12
|
-
import { APP_NAME, APP_TITLE, getAgentDir, getAuthPath, getBlueprintsDir, getDebugLogPath, getDocsPath, getProjectBlueprintsDir, getProjectSnippetsDir, getShareViewerUrl, getSnippetsDir, VERSION, } from "../../config.js";
|
|
12
|
+
import { APP_NAME, APP_TITLE, getAgentDir, getAuthPath, getBlueprintsDir, getDebugLogPath, getDocsPath, getProjectBlueprintsDir, getProjectSnippetsDir, getSelfUpdateCommand, getShareViewerUrl, getSnippetsDir, PACKAGE_NAME, VERSION, } from "../../config.js";
|
|
13
13
|
import { parseSkillBlock } from "../../core/agent-session.js";
|
|
14
14
|
import { SessionImportFileNotFoundError } from "../../core/agent-session-runtime.js";
|
|
15
15
|
import { FooterDataProvider } from "../../core/footer-data-provider.js";
|
|
@@ -131,6 +131,8 @@ export class InteractiveMode {
|
|
|
131
131
|
this.hiddenThinkingLabel = this.defaultHiddenThinkingLabel;
|
|
132
132
|
this.lastSigintTime = 0;
|
|
133
133
|
this.lastEscapeTime = 0;
|
|
134
|
+
this._availableUpdate = undefined;
|
|
135
|
+
this._activeGoal = undefined;
|
|
134
136
|
this.changelogMarkdown = undefined;
|
|
135
137
|
this.startupNoticesShown = false;
|
|
136
138
|
this.anthropicSubscriptionWarningShown = false;
|
|
@@ -2198,6 +2200,16 @@ export class InteractiveMode {
|
|
|
2198
2200
|
await this.handlePackageBrowser();
|
|
2199
2201
|
return;
|
|
2200
2202
|
}
|
|
2203
|
+
if (text === "/update") {
|
|
2204
|
+
this.editor.setText("");
|
|
2205
|
+
await this.handleInAppUpdate();
|
|
2206
|
+
return;
|
|
2207
|
+
}
|
|
2208
|
+
if (text.startsWith("/goal")) {
|
|
2209
|
+
this.editor.setText("");
|
|
2210
|
+
this.handleGoalCommand(text.slice(5).trim());
|
|
2211
|
+
return;
|
|
2212
|
+
}
|
|
2201
2213
|
// Handle bash command (! for normal, !! for excluded from context)
|
|
2202
2214
|
if (text.startsWith("!")) {
|
|
2203
2215
|
const isExcluded = text.startsWith("!!");
|
|
@@ -2427,6 +2439,10 @@ export class InteractiveMode {
|
|
|
2427
2439
|
}
|
|
2428
2440
|
this.pendingTools.clear();
|
|
2429
2441
|
await this.checkShutdownRequested();
|
|
2442
|
+
// Check if an active goal has been met
|
|
2443
|
+
if (this._activeGoal) {
|
|
2444
|
+
void this.checkGoalCondition();
|
|
2445
|
+
}
|
|
2430
2446
|
this.ui.requestRender();
|
|
2431
2447
|
break;
|
|
2432
2448
|
case "compaction_start": {
|
|
@@ -3493,14 +3509,138 @@ export class InteractiveMode {
|
|
|
3493
3509
|
(lMajor === cMajor && lMinor < cMinor) ||
|
|
3494
3510
|
(lMajor === cMajor && lMinor === cMinor && lPatch <= cPatch))
|
|
3495
3511
|
return;
|
|
3496
|
-
|
|
3497
|
-
const
|
|
3512
|
+
this._availableUpdate = { latest, current };
|
|
3513
|
+
const updateKey = theme.fg("accent", "/update");
|
|
3514
|
+
const message = `${theme.fg("dim", `Update available: ${current} \u2192 ${latest}. Type`)} ${updateKey} ${theme.fg("dim", "to update and restart")}`;
|
|
3498
3515
|
this.chatContainer.addChild(new Text(message, 1, 0));
|
|
3499
3516
|
}
|
|
3500
3517
|
catch {
|
|
3501
3518
|
// Never block startup on version check failure
|
|
3502
3519
|
}
|
|
3503
3520
|
}
|
|
3521
|
+
async handleInAppUpdate() {
|
|
3522
|
+
// If no update detected yet, check now
|
|
3523
|
+
if (!this._availableUpdate) {
|
|
3524
|
+
await this.checkForVersionUpdate();
|
|
3525
|
+
}
|
|
3526
|
+
if (!this._availableUpdate) {
|
|
3527
|
+
this.showStatus(`Already on latest version (${this.version})`);
|
|
3528
|
+
return;
|
|
3529
|
+
}
|
|
3530
|
+
const { latest, current } = this._availableUpdate;
|
|
3531
|
+
// Get the self-update command for this installation
|
|
3532
|
+
const settingsManager = this.settingsManager;
|
|
3533
|
+
const npmCommand = settingsManager.getGlobalSettings().npmCommand;
|
|
3534
|
+
const selfUpdateCommand = getSelfUpdateCommand(PACKAGE_NAME, npmCommand);
|
|
3535
|
+
if (!selfUpdateCommand) {
|
|
3536
|
+
this.showError(`Cannot self-update this installation. Run manually:\n ${theme.fg("accent", `npm install -g ${PACKAGE_NAME}`)}`);
|
|
3537
|
+
return;
|
|
3538
|
+
}
|
|
3539
|
+
// Show progress
|
|
3540
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3541
|
+
const updateText = new Text(theme.fg("accent", `Updating ${current} \u2192 ${latest}...`), 1, 0);
|
|
3542
|
+
this.chatContainer.addChild(updateText);
|
|
3543
|
+
this.ui.requestRender();
|
|
3544
|
+
try {
|
|
3545
|
+
// Run each step of the update command
|
|
3546
|
+
for (const step of selfUpdateCommand.steps ?? [selfUpdateCommand]) {
|
|
3547
|
+
await new Promise((resolve, reject) => {
|
|
3548
|
+
const child = spawn(step.command, step.args, {
|
|
3549
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3550
|
+
shell: process.platform === "win32",
|
|
3551
|
+
});
|
|
3552
|
+
let stderr = "";
|
|
3553
|
+
child.stderr?.on("data", (data) => {
|
|
3554
|
+
stderr += data.toString();
|
|
3555
|
+
});
|
|
3556
|
+
child.on("close", (code) => {
|
|
3557
|
+
if (code === 0)
|
|
3558
|
+
resolve();
|
|
3559
|
+
else
|
|
3560
|
+
reject(new Error(stderr.trim() || `Exit code ${code}`));
|
|
3561
|
+
});
|
|
3562
|
+
child.on("error", reject);
|
|
3563
|
+
});
|
|
3564
|
+
}
|
|
3565
|
+
// Success — show message and restart
|
|
3566
|
+
updateText.setText(theme.fg("accent", `Updated to ${latest}. Restarting...`));
|
|
3567
|
+
this.ui.requestRender();
|
|
3568
|
+
// Brief pause so the user sees the message
|
|
3569
|
+
await new Promise((r) => setTimeout(r, 500));
|
|
3570
|
+
// Restart: stop TUI, then exec the same command
|
|
3571
|
+
this.stop();
|
|
3572
|
+
await this.runtimeHost.dispose();
|
|
3573
|
+
// Re-exec elyra with the same arguments
|
|
3574
|
+
const args = process.argv.slice(1);
|
|
3575
|
+
const child = spawn(process.argv[0], args, {
|
|
3576
|
+
stdio: "inherit",
|
|
3577
|
+
shell: false,
|
|
3578
|
+
});
|
|
3579
|
+
child.on("close", (code) => {
|
|
3580
|
+
process.exit(code ?? 0);
|
|
3581
|
+
});
|
|
3582
|
+
}
|
|
3583
|
+
catch (error) {
|
|
3584
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
3585
|
+
this.showError(`Update failed: ${msg}\n\nRun manually: ${selfUpdateCommand.display}`);
|
|
3586
|
+
}
|
|
3587
|
+
}
|
|
3588
|
+
handleGoalCommand(args) {
|
|
3589
|
+
// No args = show or clear goal
|
|
3590
|
+
if (!args) {
|
|
3591
|
+
if (this._activeGoal) {
|
|
3592
|
+
this._activeGoal = undefined;
|
|
3593
|
+
this.showStatus("Goal cleared");
|
|
3594
|
+
}
|
|
3595
|
+
else {
|
|
3596
|
+
this.showStatus("No active goal. Set one with: /goal <command>\n\nExample: /goal npm run check");
|
|
3597
|
+
}
|
|
3598
|
+
return;
|
|
3599
|
+
}
|
|
3600
|
+
this._activeGoal = { condition: args, checkCommand: args };
|
|
3601
|
+
const goalDisplay = theme.fg("accent", args);
|
|
3602
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3603
|
+
this.chatContainer.addChild(new Text(`${theme.bold(theme.fg("accent", "Goal set:"))} ${goalDisplay}\n${theme.fg("muted", "The agent will keep working until this command exits with code 0. Type /goal to clear.")}`, 1, 0));
|
|
3604
|
+
this.ui.requestRender();
|
|
3605
|
+
}
|
|
3606
|
+
async checkGoalCondition() {
|
|
3607
|
+
if (!this._activeGoal)
|
|
3608
|
+
return;
|
|
3609
|
+
if (this.session.isStreaming)
|
|
3610
|
+
return;
|
|
3611
|
+
const { checkCommand } = this._activeGoal;
|
|
3612
|
+
try {
|
|
3613
|
+
const result = spawnSync("sh", ["-c", checkCommand], {
|
|
3614
|
+
cwd: this.sessionManager.getCwd(),
|
|
3615
|
+
encoding: "utf-8",
|
|
3616
|
+
timeout: 30_000,
|
|
3617
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
3618
|
+
});
|
|
3619
|
+
if (result.status === 0) {
|
|
3620
|
+
// Goal met!
|
|
3621
|
+
const goalText = this._activeGoal.condition;
|
|
3622
|
+
this._activeGoal = undefined;
|
|
3623
|
+
this.chatContainer.addChild(new Spacer(1));
|
|
3624
|
+
this.chatContainer.addChild(new Text(theme.bold(theme.fg("accent", `Goal met: ${goalText}`)), 1, 0));
|
|
3625
|
+
this.ui.requestRender();
|
|
3626
|
+
}
|
|
3627
|
+
else {
|
|
3628
|
+
// Goal not met — send the output back to the agent to continue
|
|
3629
|
+
const output = (result.stdout || "").trim();
|
|
3630
|
+
const stderr = (result.stderr || "").trim();
|
|
3631
|
+
const combined = [output, stderr].filter(Boolean).join("\n");
|
|
3632
|
+
const truncated = combined.length > 2000 ? `${combined.slice(0, 2000)}... (truncated)` : combined;
|
|
3633
|
+
const prompt = `Goal not yet met. The check command \`${checkCommand}\` exited with code ${result.status}.${truncated ? `\n\nOutput:\n${truncated}` : ""}\n\nContinue working to fix the remaining issues.`;
|
|
3634
|
+
this.showStatus(`Goal check failed (exit ${result.status}), continuing...`);
|
|
3635
|
+
await this.session.prompt(prompt);
|
|
3636
|
+
}
|
|
3637
|
+
}
|
|
3638
|
+
catch {
|
|
3639
|
+
// Timeout or error running the check — don't loop
|
|
3640
|
+
this._activeGoal = undefined;
|
|
3641
|
+
this.showStatus("Goal check timed out, goal cleared");
|
|
3642
|
+
}
|
|
3643
|
+
}
|
|
3504
3644
|
async handleSkillsInstallCommand(args) {
|
|
3505
3645
|
if (!args) {
|
|
3506
3646
|
this.showError("Usage: /skills install <name> [name2 ...]\n\nBrowse available skills at github.com/kwhorne/elyra-skills");
|