@dreb/coding-agent 2.5.2 → 2.6.0

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.
@@ -13,6 +13,7 @@ import { parseSkillBlock } from "../../core/agent-session.js";
13
13
  import { BuddyManager, checkOllama } from "../../core/buddy/buddy-manager.js";
14
14
  import { Rarity, Stat } from "../../core/buddy/buddy-types.js";
15
15
  import { BuddyController } from "../../core/buddy/index.js";
16
+ import { acquireDreamLock, buildDreamPrompt, cleanupDreamTmpDirs, parseDreamCommand, performDreamBackup, pruneOldBackups, resolveDreamContext, validateArchivePath, validateMemoryLinks, } from "../../core/dream.js";
16
17
  import { FooterDataProvider } from "../../core/footer-data-provider.js";
17
18
  import { KeybindingsManager } from "../../core/keybindings.js";
18
19
  import { createCompactionSummaryMessage } from "../../core/messages.js";
@@ -20,6 +21,7 @@ import { findExactModelReferenceMatch, resolveModelScope } from "../../core/mode
20
21
  import { DefaultPackageManager } from "../../core/package-manager.js";
21
22
  import { SessionManager } from "../../core/session-manager.js";
22
23
  import { BUILTIN_SLASH_COMMANDS } from "../../core/slash-commands.js";
24
+ import { resolveToCwd } from "../../core/tools/path-utils.js";
23
25
  import { abortBackgroundAgents, getRunningBackgroundAgents } from "../../core/tools/subagent.js";
24
26
  import { getChangelogPath, getNewEntries, parseChangelog } from "../../utils/changelog.js";
25
27
  import { copyToClipboard } from "../../utils/clipboard.js";
@@ -280,6 +282,16 @@ export class InteractiveMode {
280
282
  }));
281
283
  };
282
284
  }
285
+ const dreamCommand = slashCommands.find((command) => command.name === "dream");
286
+ if (dreamCommand) {
287
+ dreamCommand.getArgumentCompletions = (prefix) => {
288
+ const subcommands = [
289
+ { value: "backup", label: "backup", description: "Show or set the backup archive path" },
290
+ ];
291
+ const filtered = prefix ? subcommands.filter((s) => s.value.startsWith(prefix.toLowerCase())) : subcommands;
292
+ return filtered.length > 0 ? filtered : null;
293
+ };
294
+ }
283
295
  const buddyCommand = slashCommands.find((command) => command.name === "buddy");
284
296
  if (buddyCommand) {
285
297
  buddyCommand.getArgumentCompletions = (prefix) => {
@@ -1786,6 +1798,11 @@ export class InteractiveMode {
1786
1798
  await this.handleCompactCommand(customInstructions);
1787
1799
  return;
1788
1800
  }
1801
+ if (text === "/dream" || text.startsWith("/dream ")) {
1802
+ this.editor.setText("");
1803
+ await this.handleDreamCommand(text);
1804
+ return;
1805
+ }
1789
1806
  if (text === "/reload") {
1790
1807
  this.editor.setText("");
1791
1808
  await this.handleReloadCommand();
@@ -3878,6 +3895,138 @@ ${cycleModelForward || cycleModelBackward ? `| \`${cycleModelForward}\` / \`${cy
3878
3895
  this.bashComponent = undefined;
3879
3896
  this.ui.requestRender();
3880
3897
  }
3898
+ // =========================================================================
3899
+ // Dream (memory consolidation) handler
3900
+ // =========================================================================
3901
+ async handleDreamCommand(text) {
3902
+ const command = parseDreamCommand(text);
3903
+ switch (command.type) {
3904
+ case "showBackup": {
3905
+ const archivePath = this.settingsManager.getDreamArchivePath();
3906
+ this.showStatus(`Dream backup path: ${archivePath}`);
3907
+ return;
3908
+ }
3909
+ case "setBackup": {
3910
+ try {
3911
+ const absolutePath = resolveToCwd(command.path, process.cwd());
3912
+ const context = await resolveDreamContext(this.settingsManager);
3913
+ const allMemoryDirs = [
3914
+ context.globalMemoryDir,
3915
+ ...context.projectMemoryDirs,
3916
+ ...context.claudeMemoryDirs,
3917
+ ];
3918
+ validateArchivePath(absolutePath, allMemoryDirs);
3919
+ this.settingsManager.setDreamArchivePath(absolutePath);
3920
+ this.showStatus(`Dream backup path set to: ${absolutePath}`);
3921
+ }
3922
+ catch (error) {
3923
+ this.showError(`Invalid backup path: ${error instanceof Error ? error.message : String(error)}`);
3924
+ }
3925
+ return;
3926
+ }
3927
+ case "run": {
3928
+ await this.executeDream();
3929
+ return;
3930
+ }
3931
+ }
3932
+ }
3933
+ async executeDream() {
3934
+ // Stop any existing loading animation
3935
+ if (this.loadingAnimation) {
3936
+ this.loadingAnimation.stop();
3937
+ this.loadingAnimation = undefined;
3938
+ }
3939
+ this.statusContainer.clear();
3940
+ let releaseLock;
3941
+ let dreamContext;
3942
+ // Override escape to cancel
3943
+ const originalOnEscape = this.defaultEditor.onEscape;
3944
+ this.defaultEditor.onEscape = () => {
3945
+ // User pressed escape — abort the dream
3946
+ this.session.abort();
3947
+ };
3948
+ // Show loading spinner
3949
+ this.chatContainer.addChild(new Spacer(1));
3950
+ const cancelHint = `(${keyText("app.interrupt")} to cancel)`;
3951
+ const dreamLoader = new Loader(this.ui, (spinner) => theme.fg("accent", spinner), (text) => theme.fg("muted", text), `Dreaming... ${cancelHint}`);
3952
+ this.statusContainer.addChild(dreamLoader);
3953
+ this.ui.requestRender();
3954
+ try {
3955
+ // Acquire lock
3956
+ try {
3957
+ releaseLock = await acquireDreamLock();
3958
+ }
3959
+ catch (error) {
3960
+ this.showError(`Cannot start dream: ${error instanceof Error ? error.message : String(error)}`);
3961
+ return;
3962
+ }
3963
+ // Resolve context
3964
+ dreamContext = await resolveDreamContext(this.settingsManager);
3965
+ // Validate archive path
3966
+ try {
3967
+ const allMemoryDirs = [
3968
+ dreamContext.globalMemoryDir,
3969
+ ...dreamContext.projectMemoryDirs,
3970
+ ...dreamContext.claudeMemoryDirs,
3971
+ ];
3972
+ validateArchivePath(dreamContext.archivePath, allMemoryDirs);
3973
+ }
3974
+ catch (error) {
3975
+ this.showError(`Invalid archive path: ${error instanceof Error ? error.message : String(error)}`);
3976
+ return;
3977
+ }
3978
+ // Perform backup
3979
+ let backupResult;
3980
+ try {
3981
+ backupResult = await performDreamBackup(dreamContext);
3982
+ if (!backupResult.verified) {
3983
+ this.showError(`Backup verification failed — aborting to protect your data. Check backup at: ${backupResult.backupPath}`);
3984
+ return;
3985
+ }
3986
+ }
3987
+ catch (error) {
3988
+ this.showError(`Backup failed: ${error instanceof Error ? error.message : String(error)}`);
3989
+ return;
3990
+ }
3991
+ // Update loader text
3992
+ dreamLoader.setText(`Consolidating memories... ${cancelHint}`);
3993
+ this.ui.requestRender();
3994
+ // Build prompt and inject into session
3995
+ const prompt = buildDreamPrompt(dreamContext, backupResult);
3996
+ await this.session.prompt(prompt);
3997
+ // Post-consolidation: validate links
3998
+ const allMemoryDirs = [dreamContext.globalMemoryDir, ...dreamContext.projectMemoryDirs];
3999
+ const linkResult = validateMemoryLinks(allMemoryDirs);
4000
+ if (!linkResult.valid) {
4001
+ const broken = linkResult.brokenLinks.map((l) => ` ${l.indexFile}: ${l.pointer} → ${l.target}`).join("\n");
4002
+ this.showWarning(`Broken memory links detected:\n${broken}`);
4003
+ }
4004
+ // Prune old backups
4005
+ await pruneOldBackups(dreamContext.archivePath);
4006
+ }
4007
+ catch (error) {
4008
+ const message = error instanceof Error ? error.message : String(error);
4009
+ this.showError(`Dream failed: ${message}`);
4010
+ }
4011
+ finally {
4012
+ // Always clean up
4013
+ dreamLoader.stop();
4014
+ this.statusContainer.clear();
4015
+ this.defaultEditor.onEscape = originalOnEscape;
4016
+ if (releaseLock) {
4017
+ try {
4018
+ releaseLock();
4019
+ }
4020
+ catch {
4021
+ /* ignore release errors */
4022
+ }
4023
+ }
4024
+ // Clean up temp dirs on abort/crash too
4025
+ if (dreamContext) {
4026
+ cleanupDreamTmpDirs([dreamContext.globalMemoryDir, ...dreamContext.projectMemoryDirs]);
4027
+ }
4028
+ }
4029
+ }
3881
4030
  async handleCompactCommand(customInstructions) {
3882
4031
  const entries = this.sessionManager.getEntries();
3883
4032
  const messageCount = entries.filter((e) => e.type === "message").length;