@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.
Files changed (32) hide show
  1. package/CHANGELOG.md +22 -1
  2. package/README.md +1 -5
  3. package/dist/core/slash-commands.d.ts.map +1 -1
  4. package/dist/core/slash-commands.js +2 -0
  5. package/dist/core/slash-commands.js.map +1 -1
  6. package/dist/core/tools/edit-diff.d.ts +0 -8
  7. package/dist/core/tools/edit-diff.d.ts.map +1 -1
  8. package/dist/core/tools/edit-diff.js +5 -2
  9. package/dist/core/tools/edit-diff.js.map +1 -1
  10. package/dist/core/tools/edit.d.ts.map +1 -1
  11. package/dist/core/tools/edit.js +33 -1
  12. package/dist/core/tools/edit.js.map +1 -1
  13. package/dist/core/tools/read.d.ts +7 -0
  14. package/dist/core/tools/read.d.ts.map +1 -1
  15. package/dist/core/tools/read.js +271 -46
  16. package/dist/core/tools/read.js.map +1 -1
  17. package/dist/modes/interactive/interactive-mode.d.ts +5 -0
  18. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  19. package/dist/modes/interactive/interactive-mode.js +143 -3
  20. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  21. package/docs/extensions.md +5 -0
  22. package/docs/index.md +1 -7
  23. package/docs/sessions.md +10 -0
  24. package/docs/usage.md +7 -0
  25. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  26. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  27. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  28. package/examples/extensions/sandbox/package-lock.json +2 -2
  29. package/examples/extensions/sandbox/package.json +1 -1
  30. package/examples/extensions/with-deps/package-lock.json +2 -2
  31. package/examples/extensions/with-deps/package.json +1 -1
  32. 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
- const updateCmd = theme.fg("accent", "elyra update");
3497
- const message = `${theme.fg("dim", `Update available: ${current} \u2192 ${latest}. Run`)} ${updateCmd}`;
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");