lazy-gravity 0.10.0 → 0.11.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.
@@ -41,6 +41,7 @@ const cdpPorts_1 = require("../../utils/cdpPorts");
41
41
  const configLoader_1 = require("../../utils/configLoader");
42
42
  const pathUtils_1 = require("../../utils/pathUtils");
43
43
  const logger_1 = require("../../utils/logger");
44
+ const artifactService_1 = require("../../services/artifactService");
44
45
  const ok = (msg) => console.log(` ${logger_1.COLORS.green}[OK]${logger_1.COLORS.reset} ${msg}`);
45
46
  const warn = (msg) => console.log(` ${logger_1.COLORS.yellow}[--]${logger_1.COLORS.reset} ${msg}`);
46
47
  const fail = (msg) => console.log(` ${logger_1.COLORS.red}[!!]${logger_1.COLORS.reset} ${msg}`);
@@ -53,17 +54,22 @@ function checkPort(port) {
53
54
  res.on('end', () => {
54
55
  try {
55
56
  const parsed = JSON.parse(data);
56
- resolve(Array.isArray(parsed));
57
+ if (Array.isArray(parsed)) {
58
+ resolve({ alive: true, targets: parsed });
59
+ }
60
+ else {
61
+ resolve({ alive: false });
62
+ }
57
63
  }
58
64
  catch {
59
- resolve(false);
65
+ resolve({ alive: false });
60
66
  }
61
67
  });
62
68
  });
63
- req.on('error', () => resolve(false));
69
+ req.on('error', () => resolve({ alive: false }));
64
70
  req.setTimeout(2000, () => {
65
71
  req.destroy();
66
- resolve(false);
72
+ resolve({ alive: false });
67
73
  });
68
74
  });
69
75
  }
@@ -145,11 +151,15 @@ async function doctorAction() {
145
151
  // 5. CDP port check
146
152
  console.log(`\n ${logger_1.COLORS.dim}Checking CDP ports...${logger_1.COLORS.reset}`);
147
153
  let cdpOk = false;
154
+ const portResults = new Map();
148
155
  for (const port of cdpPorts_1.CDP_PORTS) {
149
- const alive = await checkPort(port);
150
- if (alive) {
156
+ const result = await checkPort(port);
157
+ if (result.alive) {
151
158
  ok(`CDP port ${port} is responding`);
152
159
  cdpOk = true;
160
+ if (result.targets) {
161
+ portResults.set(port, result.targets);
162
+ }
153
163
  }
154
164
  }
155
165
  if (!cdpOk) {
@@ -157,7 +167,35 @@ async function doctorAction() {
157
167
  hint(`Run: ${(0, pathUtils_1.getAntigravityCdpHint)(9222)}`);
158
168
  allOk = false;
159
169
  }
160
- // 6. Node.js version check
170
+ // 6. Path alignment check
171
+ console.log(`\n ${logger_1.COLORS.dim}Checking brain path alignment...${logger_1.COLORS.reset}`);
172
+ const artifactService = new artifactService_1.ArtifactService();
173
+ const resolvedPath = artifactService.getBrainBasePath();
174
+ ok(`Resolved brainBasePath: ${resolvedPath}`);
175
+ for (const port of cdpPorts_1.CDP_PORTS) {
176
+ const targets = portResults.get(port);
177
+ if (targets && Array.isArray(targets)) {
178
+ for (const t of targets) {
179
+ if (t.url?.includes('workbench')) {
180
+ const pathLower = t.url.toLowerCase();
181
+ const isIDE = pathLower.includes('antigravity%20ide') || pathLower.includes('antigravity-ide');
182
+ const resolvedLower = resolvedPath.toLowerCase();
183
+ if (isIDE && !resolvedLower.includes('antigravity-ide')) {
184
+ fail(`Path mismatch: Active IDE target is "Antigravity IDE" but brain basePath is resolved to: ${resolvedPath}`);
185
+ hint('Make sure to use .gemini/antigravity-ide/brain path.');
186
+ allOk = false;
187
+ }
188
+ else if (!isIDE && resolvedLower.includes('antigravity-ide')) {
189
+ warn(`Active IDE target does not appear to be "Antigravity IDE" but brain basePath is: ${resolvedPath}`);
190
+ }
191
+ else {
192
+ ok(`Target "${t.title}" aligns correctly with brain basePath`);
193
+ }
194
+ }
195
+ }
196
+ }
197
+ }
198
+ // 7. Node.js version check
161
199
  const nodeVersion = process.versions.node;
162
200
  const major = parseInt(nodeVersion.split('.')[0], 10);
163
201
  if (major >= 18) {
package/dist/bot/index.js CHANGED
@@ -191,11 +191,15 @@ function createSerialTaskQueueForTest(queueName, traceId) {
191
191
  async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService, modelService, inboundImages = [], options) {
192
192
  // Completion signal — called exactly once when the entire prompt lifecycle ends
193
193
  let completionSignaled = false;
194
+ let onApprovalRef = null;
194
195
  const signalCompletion = (exitPath) => {
195
196
  if (completionSignaled)
196
197
  return;
197
198
  completionSignaled = true;
198
199
  logger_1.logger.debug(`[sendPrompt:${message.channelId}] signalCompletion via ${exitPath}`);
200
+ if (onApprovalRef) {
201
+ cdp.off('approval_required', onApprovalRef);
202
+ }
199
203
  options?.onFullCompletion?.();
200
204
  };
201
205
  // Resolve output format once at the start (no mid-response switches)
@@ -587,6 +591,8 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
587
591
  return;
588
592
  }
589
593
  const startTime = Date.now();
594
+ let earlyConvIdResolved = false;
595
+ let earlyConvIdInFlight = false;
590
596
  await upsertLiveActivityEmbeds(`${PHASE_ICONS.thinking} Process Log`, '', PHASE_COLORS.thinking, (0, i18n_1.t)('⏱️ Elapsed: 0s | Process log'), { source: 'initial' });
591
597
  const monitor = new responseMonitor_1.ResponseMonitor({
592
598
  cdpService: cdp,
@@ -613,6 +619,30 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
613
619
  expectedVersion: activityVersion,
614
620
  skipWhenFinalized: true,
615
621
  }).catch(() => { });
622
+ // Try to resolve conversation_id early if not already bound
623
+ const { artifactService, chatSessionRepo, chatSessionService } = options || {};
624
+ if (!earlyConvIdResolved && !earlyConvIdInFlight && artifactService && chatSessionRepo && chatSessionService) {
625
+ earlyConvIdInFlight = true;
626
+ chatSessionService.getCurrentSessionInfo(cdp)
627
+ .then((sessionInfo) => {
628
+ if (sessionInfo && sessionInfo.title && sessionInfo.title !== (0, i18n_1.t)('(Untitled)')) {
629
+ const session = chatSessionRepo.findByChannelId(message.channelId);
630
+ const workspaceDirName = (session
631
+ ? bridge.pool.extractProjectName(session.workspacePath)
632
+ : cdp.getCurrentWorkspaceName()) ?? undefined;
633
+ const convId = artifactService.findConversationByTitle(sessionInfo.title, workspaceDirName);
634
+ if (convId) {
635
+ chatSessionRepo.setConversationId(message.channelId, convId);
636
+ earlyConvIdResolved = true;
637
+ logger_1.logger.debug(`[EarlyBinding] Saved conversation_id: "${convId}" for channel: ${message.channelId}`);
638
+ }
639
+ }
640
+ })
641
+ .catch(() => { })
642
+ .finally(() => {
643
+ earlyConvIdInFlight = false;
644
+ });
645
+ }
616
646
  },
617
647
  onProgress: (text) => {
618
648
  if (isFinalized)
@@ -706,6 +736,12 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
706
736
  const components = [];
707
737
  if (!citedFiles)
708
738
  citedFiles = [];
739
+ // Only pause when newly created conversation folders and files need to flush to disk
740
+ const existingConvId = options?.chatSessionRepo?.findByChannelId(message.channelId)?.conversationId;
741
+ if (!existingConvId) {
742
+ await new Promise((resolve) => setTimeout(resolve, 1000));
743
+ }
744
+ let activeConversationId = undefined;
709
745
  if (options && message.guild) {
710
746
  try {
711
747
  const sessionInfo = await options.chatSessionService.getCurrentSessionInfo(cdp);
@@ -733,6 +769,7 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
733
769
  const convId = options.artifactService.findConversationByTitle(sessionInfo.title, workspaceDirName);
734
770
  if (convId) {
735
771
  options.chatSessionRepo.setConversationId(message.channelId, convId);
772
+ activeConversationId = convId;
736
773
  }
737
774
  }
738
775
  }
@@ -741,6 +778,51 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
741
778
  logger_1.logger.error('[Rename] Failed to get title from Antigravity and rename:', e);
742
779
  }
743
780
  }
781
+ const classifiedForFiles = monitor.getLastClassified();
782
+ if (classifiedForFiles?.fileChanges && classifiedForFiles.fileChanges.length > 0) {
783
+ for (const fc of classifiedForFiles.fileChanges) {
784
+ if (!citedFiles.includes(fc.path)) {
785
+ citedFiles.push(fc.path);
786
+ }
787
+ }
788
+ }
789
+ if (!activeConversationId && options?.chatSessionRepo) {
790
+ activeConversationId = options.chatSessionRepo.findByChannelId(message.channelId)?.conversationId ?? undefined;
791
+ }
792
+ if (activeConversationId && options?.artifactService) {
793
+ const artifacts = options.artifactService.listArtifacts(activeConversationId);
794
+ const planArt = artifacts.find(art => art.artifactType === 'ARTIFACT_TYPE_IMPLEMENTATION_PLAN' ||
795
+ art.filename.toLowerCase() === 'implementation_plan.md');
796
+ let planTime = 0;
797
+ if (planArt) {
798
+ try {
799
+ planTime = fs_1.default.statSync(planArt.absolutePath).mtimeMs;
800
+ }
801
+ catch { /* ignore */ }
802
+ }
803
+ for (const art of artifacts) {
804
+ const nameWithExt = art.filename;
805
+ const nameWithoutExt = art.filename.replace(/\.[^/.]+$/, "");
806
+ const regex = new RegExp(`\\b${nameWithoutExt.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&')}\\b`, 'i');
807
+ const mentionsArtifact = finalOutputText.includes(nameWithExt) || finalOutputText.includes(`\`${nameWithoutExt}\``) || regex.test(finalOutputText);
808
+ const isTask = art.artifactType === 'ARTIFACT_TYPE_TASK' || nameWithExt.toLowerCase() === 'task.md';
809
+ const isWalkthrough = art.artifactType === 'ARTIFACT_TYPE_WALKTHROUGH' || nameWithExt.toLowerCase() === 'walkthrough.md';
810
+ let isSpecialArtifact = false;
811
+ if (isTask || isWalkthrough) {
812
+ let artTime = 0;
813
+ try {
814
+ artTime = fs_1.default.statSync(art.absolutePath).mtimeMs;
815
+ }
816
+ catch { /* ignore */ }
817
+ if (artTime > planTime) {
818
+ isSpecialArtifact = true;
819
+ }
820
+ }
821
+ if ((mentionsArtifact || isSpecialArtifact) && !citedFiles.includes(nameWithExt)) {
822
+ citedFiles.push(nameWithExt);
823
+ }
824
+ }
825
+ }
744
826
  if (citedFiles && citedFiles.length > 0) {
745
827
  // Strip trailing punctuation and deduplicate
746
828
  let uniqueFiles = Array.from(new Set(citedFiles.map(f => {
@@ -756,38 +838,82 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
756
838
  // Max 5 buttons per row in Discord
757
839
  for (let i = 0; i < Math.min(uniqueFiles.length, 5); i++) {
758
840
  let fileUrl = uniqueFiles[i];
841
+ let isArtifactMatch = false;
842
+ let artifactDisplayName = fileUrl;
843
+ if (activeConversationId && options?.artifactService) {
844
+ const artifacts = options.artifactService.listArtifacts(activeConversationId);
845
+ const rawName = fileUrl.replace(/^file:\/\/\//, '');
846
+ const matched = artifacts.find(a => a.filename.toLowerCase() === fileUrl.toLowerCase() ||
847
+ a.filename.toLowerCase() === rawName.toLowerCase());
848
+ if (matched) {
849
+ isArtifactMatch = true;
850
+ fileUrl = `file:///${matched.absolutePath.replace(/\\/g, '/')}`;
851
+ artifactDisplayName = matched.filename;
852
+ }
853
+ }
759
854
  const wsPath = cdp.getCurrentWorkspacePath();
760
855
  if (!wsPath) {
761
856
  continue;
762
857
  }
763
- if (!fileUrl.startsWith('file:///') && !path_1.default.isAbsolute(fileUrl)) {
764
- fileUrl = `file:///${path_1.default.resolve(wsPath, fileUrl).replace(/\\/g, '/')}`;
765
- }
766
- let fsPath = fileUrl.replace(/^file:\/\/\//, '');
767
- if (path_1.default.sep === '/' && !fsPath.startsWith('/')) {
768
- fsPath = '/' + fsPath;
769
- }
770
- else if (process.platform === 'win32' && fsPath.startsWith('/')) {
771
- fsPath = fsPath.substring(1);
772
- }
773
- const relative = path_1.default.relative(wsPath, fsPath);
774
- if (relative.startsWith('..') || path_1.default.isAbsolute(relative)) {
775
- continue;
776
- }
777
- if (!fs_1.default.existsSync(fsPath)) {
778
- continue;
858
+ if (!isArtifactMatch) {
859
+ if (!fileUrl.startsWith('file:///') && !path_1.default.isAbsolute(fileUrl)) {
860
+ fileUrl = `file:///${path_1.default.resolve(wsPath, fileUrl).replace(/\\/g, '/')}`;
861
+ }
862
+ let fsPath = fileUrl.replace(/^file:\/\/\//, '');
863
+ if (path_1.default.sep === '/' && !fsPath.startsWith('/')) {
864
+ fsPath = '/' + fsPath;
865
+ }
866
+ else if (process.platform === 'win32' && fsPath.startsWith('/')) {
867
+ fsPath = fsPath.substring(1);
868
+ }
869
+ const relative = path_1.default.relative(wsPath, fsPath);
870
+ const isArtifact = fsPath.includes('.gemini') && fsPath.includes('antigravity') && fsPath.includes('brain');
871
+ if (!isArtifact && (relative.startsWith('..') || path_1.default.isAbsolute(relative))) {
872
+ continue;
873
+ }
874
+ if (!fs_1.default.existsSync(fsPath)) {
875
+ continue;
876
+ }
779
877
  }
780
878
  let displayName = fileUrl;
781
- if (displayName.startsWith('file:///')) {
879
+ if (isArtifactMatch) {
880
+ displayName = artifactDisplayName;
881
+ }
882
+ else if (displayName.startsWith('file:///')) {
782
883
  const parts = displayName.split('/');
783
884
  displayName = parts[parts.length - 1];
784
885
  }
785
886
  const hashId = Math.random().toString(36).substring(2, 10);
786
887
  fileOpenCache_1.fileOpenCache.set(hashId, fileUrl);
888
+ let customId = `file_open:cache:${hashId}`;
889
+ if (isArtifactMatch && activeConversationId && displayName) {
890
+ const artId = `file_open:art:${activeConversationId}:${displayName}`;
891
+ if (artId.length < 100) {
892
+ customId = artId;
893
+ }
894
+ }
895
+ else if (wsPath) {
896
+ let fsPath = fileUrl.replace(/^file:\/\/\//, '');
897
+ if (path_1.default.sep === '/' && !fsPath.startsWith('/')) {
898
+ fsPath = '/' + fsPath;
899
+ }
900
+ else if (process.platform === 'win32' && fsPath.startsWith('/')) {
901
+ fsPath = fsPath.substring(1);
902
+ }
903
+ const resolvedFsPath = path_1.default.resolve(fsPath);
904
+ const relative = path_1.default.relative(wsPath, resolvedFsPath);
905
+ if (!relative.startsWith('..') && !path_1.default.isAbsolute(relative)) {
906
+ const relSlash = relative.replace(/\\/g, '/');
907
+ const relId = `file_open:rel:${relSlash}`;
908
+ if (relId.length < 100) {
909
+ customId = relId;
910
+ }
911
+ }
912
+ }
787
913
  row.addComponents(new discord_js_1.ButtonBuilder()
788
- .setCustomId(`file_open:${hashId}`)
789
- .setLabel(`Open ${displayName.substring(0, 20)}`)
790
- .setStyle(discord_js_1.ButtonStyle.Primary));
914
+ .setCustomId(customId)
915
+ .setLabel(isArtifactMatch ? `Review ${displayName.substring(0, 15)}` : `Open ${displayName.substring(0, 20)}`)
916
+ .setStyle(isArtifactMatch ? discord_js_1.ButtonStyle.Success : discord_js_1.ButtonStyle.Primary));
791
917
  }
792
918
  if (row.components.length > 0) {
793
919
  components.push(row);
@@ -895,6 +1021,32 @@ async function sendPromptToAntigravity(bridge, message, prompt, cdp, modeService
895
1021
  }
896
1022
  },
897
1023
  });
1024
+ onApprovalRef = async () => {
1025
+ if (isFinalized)
1026
+ return;
1027
+ const textToUse = lastProgressText || monitor.getLastText() || '';
1028
+ const separated = (0, discordFormatter_1.splitOutputAndLogs)(textToUse);
1029
+ const outputText = separated.output || textToUse;
1030
+ if (outputText && outputText.trim().length > 0) {
1031
+ liveResponseUpdateVersion += 1;
1032
+ const responseVersion = liveResponseUpdateVersion;
1033
+ const elapsed = Math.round((Date.now() - startTime) / 1000);
1034
+ await upsertLiveResponseEmbeds((0, i18n_1.t)('Response'), outputText, PHASE_COLORS.thinking, (0, i18n_1.t)(`⏱️ Time: ${elapsed}s | Awaiting Approval`), {
1035
+ source: 'approval-pause',
1036
+ expectedVersion: responseVersion,
1037
+ skipWhenFinalized: true,
1038
+ }).catch(() => { });
1039
+ }
1040
+ };
1041
+ cdp.on('approval_required', onApprovalRef);
1042
+ if (options?.onMonitorCreated) {
1043
+ options.onMonitorCreated({
1044
+ stop: async () => {
1045
+ isFinalized = true;
1046
+ await monitor.stop();
1047
+ }
1048
+ });
1049
+ }
898
1050
  await monitor.start();
899
1051
  // 1-second elapsed timer — updates footer independently of process log events
900
1052
  const elapsedTimer = setInterval(() => {
@@ -959,6 +1111,8 @@ const startBot = async (cliLogLevel) => {
959
1111
  .filter((account) => typeof account.userDataDir === 'string' && account.userDataDir.trim().length > 0)
960
1112
  .map((account) => [account.name, account.userDataDir.trim()]));
961
1113
  const bridge = (0, cdpBridgeManager_1.initCdpBridge)(config.autoApproveFileEdits, accountPorts, accountUserDataDirs);
1114
+ bridge.chatSessionRepo = chatSessionRepo;
1115
+ bridge.artifactService = artifactService;
962
1116
  // Initialize CDP-dependent services (constructor CDP dependency removed)
963
1117
  const chatSessionService = new chatSessionService_1.ChatSessionService();
964
1118
  const titleGenerator = new titleGeneratorService_1.TitleGeneratorService();
@@ -1009,7 +1163,20 @@ const startBot = async (cliLogLevel) => {
1009
1163
  accountPrefRepo,
1010
1164
  accounts: config.antigravityAccounts,
1011
1165
  });
1012
- await (0, antigravityLauncher_1.startAntigravity)(accountPorts[accountName] ?? 9222);
1166
+ const status = await (0, antigravityLauncher_1.startAntigravity)(accountPorts[accountName] ?? 9222);
1167
+ if (status === 'started') {
1168
+ const cdp = new cdpService_1.CdpService({ portsToScan: [accountPorts[accountName] ?? 9222] });
1169
+ try {
1170
+ await cdp.connect();
1171
+ await cdp.openChatPanel();
1172
+ }
1173
+ catch (e) {
1174
+ // ignore
1175
+ }
1176
+ finally {
1177
+ await cdp.disconnect().catch(() => { });
1178
+ }
1179
+ }
1013
1180
  });
1014
1181
  const chatHandler = new chatCommandHandler_1.ChatCommandHandler(chatSessionService, chatSessionRepo, workspaceBindingRepo, channelManager, workspaceService, bridge.pool, (channelId, userId) => (0, accountUtils_1.resolveScopedAccountName)({
1015
1182
  channelId,
@@ -2043,7 +2210,9 @@ async function handleSlashInteraction(interaction, handler, bridge, wsHandler, c
2043
2210
  const candidatePath = path_1.default.isAbsolute(rawPath) ? rawPath : path_1.default.join(wsPath, rawPath);
2044
2211
  const relative = path_1.default.relative(wsPath, candidatePath);
2045
2212
  if (!relative.startsWith('..') && !path_1.default.isAbsolute(relative)) {
2046
- resolvedPath = candidatePath;
2213
+ if (fs_1.default.existsSync(candidatePath)) {
2214
+ resolvedPath = candidatePath;
2215
+ }
2047
2216
  }
2048
2217
  }
2049
2218
  }
@@ -1,40 +1,8 @@
1
1
  "use strict";
2
- var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
- if (k2 === undefined) k2 = k;
4
- var desc = Object.getOwnPropertyDescriptor(m, k);
5
- if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
- desc = { enumerable: true, get: function() { return m[k]; } };
7
- }
8
- Object.defineProperty(o, k2, desc);
9
- }) : (function(o, m, k, k2) {
10
- if (k2 === undefined) k2 = k;
11
- o[k2] = m[k];
12
- }));
13
- var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
- Object.defineProperty(o, "default", { enumerable: true, value: v });
15
- }) : function(o, v) {
16
- o["default"] = v;
17
- });
18
- var __importStar = (this && this.__importStar) || (function () {
19
- var ownKeys = function(o) {
20
- ownKeys = Object.getOwnPropertyNames || function (o) {
21
- var ar = [];
22
- for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
- return ar;
24
- };
25
- return ownKeys(o);
26
- };
27
- return function (mod) {
28
- if (mod && mod.__esModule) return mod;
29
- var result = {};
30
- if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
- __setModuleDefault(result, mod);
32
- return result;
33
- };
34
- })();
35
2
  Object.defineProperty(exports, "__esModule", { value: true });
36
3
  exports.ChatCommandHandler = void 0;
37
4
  const i18n_1 = require("../utils/i18n");
5
+ const logger_1 = require("../utils/logger");
38
6
  const discord_js_1 = require("discord.js");
39
7
  /**
40
8
  * Handler for chat session related commands
@@ -111,9 +79,7 @@ class ChatCommandHandler {
111
79
  if (!newChatResult.ok) {
112
80
  // Log but don't fail the command, as the channel still needs to be created
113
81
  // and the user might just have to click it manually if the IDE state is strange.
114
- Promise.resolve().then(() => __importStar(require('../utils/logger'))).then(({ logger }) => {
115
- logger.warn(`[/new] Could not start new chat in IDE automatically: ${newChatResult.error}`);
116
- });
82
+ logger_1.logger.warn(`[/new] Could not start new chat in IDE automatically: ${newChatResult.error}`);
117
83
  }
118
84
  const customNameRaw = interaction.options.getString('name');
119
85
  let safeCustomName = null;
@@ -156,9 +122,7 @@ class ChatCommandHandler {
156
122
  if (workspaceCdp) {
157
123
  const renameResult = await this.chatSessionService.renameCurrentChatInUI(workspaceCdp, safeCustomName);
158
124
  if (!renameResult.ok) {
159
- Promise.resolve().then(() => __importStar(require('../utils/logger'))).then(({ logger }) => {
160
- logger.warn(`[/new] Could not rename chat in IDE automatically: ${renameResult.error}`);
161
- });
125
+ logger_1.logger.warn(`[/new] Could not rename chat in IDE automatically: ${renameResult.error}`);
162
126
  }
163
127
  }
164
128
  }
@@ -185,8 +149,14 @@ class ChatCommandHandler {
185
149
  const embed = new discord_js_1.EmbedBuilder()
186
150
  .setTitle((0, i18n_1.t)('💬 Chat Session Info'))
187
151
  .setColor(info.hasActiveChat ? 0x00CC88 : 0x888888)
188
- .addFields({ name: (0, i18n_1.t)('Title'), value: info.title, inline: true }, { name: (0, i18n_1.t)('Status'), value: info.hasActiveChat ? (0, i18n_1.t)('🟢 Active') : (0, i18n_1.t)('⚪ Inactive'), inline: true })
189
- .setDescription((0, i18n_1.t)('※ Non-session channel.\nUse `/project` to create a project first.'))
152
+ .addFields({ name: (0, i18n_1.t)('Title'), value: info.title, inline: true }, { name: (0, i18n_1.t)('Status'), value: info.hasActiveChat ? (0, i18n_1.t)('🟢 Active') : (0, i18n_1.t)('⚪ Inactive'), inline: true });
153
+ const channel = await interaction.client.channels.fetch(interaction.channelId).catch(() => null);
154
+ const categoryId = channel?.parentId;
155
+ const isProjectChannel = categoryId ? !!this.bindingRepo.findByChannelId(categoryId) : false;
156
+ const desc = isProjectChannel
157
+ ? (0, i18n_1.t)('※ No active session is bound to this channel.\n\n💡 **Tip**: If you recently deleted a session in the IDE, this channel was automatically unbound. Send a prompt or use `/new` to start a fresh chat.')
158
+ : (0, i18n_1.t)('※ Non-session channel.\nUse `/project` to create a project first.');
159
+ embed.setDescription(desc)
190
160
  .setTimestamp();
191
161
  await interaction.editReply({ embeds: [embed] });
192
162
  return;