Skip to content

Conversation

@sestinj
Copy link
Contributor

@sestinj sestinj commented Aug 27, 2025

Description

Generate message titles for the CLI. Reuses existing core logic. Also addresses a few session history-related bugs


Summary by cubic

Generates session titles in the CLI using core logic, updating the title after the first assistant reply. Also fixes session history ordering, improves org switching, and makes /clear start a fresh session.

  • New Features

    • Auto-generate session titles after the first assistant response (headless and TUI).
    • New startNewSession API; /clear now starts a new session and keeps the system message if present.
    • Cleaner tool call labels: multi‑line string args show the first line with an ellipsis.
  • Bug Fixes

    • Session history shows in correct chronological order when using /resume.
    • Fixed assistant loading for org-visible assistants by passing the access token during slug load.
    • Smoother org switching by invoking clear flow to reset state without artifacts.

@sestinj sestinj requested a review from a team as a code owner August 27, 2025 18:28
@sestinj sestinj requested review from tomasz-stefaniak and removed request for a team August 27, 2025 18:28
@dosubot dosubot bot added the size:L This PR changes 100-499 lines, ignoring generated files. label Aug 27, 2025
Copy link
Contributor

@cubic-dev-ai cubic-dev-ai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

6 issues found across 16 files

React with 👍 or 👎 to teach cubic. You can also tag @cubic-dev-ai to give feedback, ask questions, or re-run the review.

import { processSlashCommandResult } from "./useChat.helpers.js";

// Mock the session module
vi.mock("../../session.js", () => ({
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Clear mocks in beforeEach to prevent call history leaking between tests and reduce flakiness.

Prompt for AI agents
Address the following comment on extensions/cli/src/ui/hooks/useChat.clear.test.ts at line 10:

<comment>Clear mocks in beforeEach to prevent call history leaking between tests and reduce flakiness.</comment>

<file context>
@@ -2,9 +2,17 @@ import { ChatCompletionMessageParam } from &quot;openai/resources.mjs&quot;;
 import { processSlashCommandResult } from &quot;./useChat.helpers.js&quot;;
 
+// Mock the session module
+vi.mock(&quot;../../session.js&quot;, () =&gt; ({
+  loadSession: vi.fn(),
+  updateSessionHistory: vi.fn(),
</file context>

try {
// Try to import CLI dependencies dynamically
const { convertFromUnifiedHistory } = await import(
"../../extensions/cli/src/messageConversion.js"
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The core module is dynamically importing from extensions/cli, violating the architectural principle that core should be independent of any extensions. This creates tight coupling and reduces reusability. Dependencies should be inverted, e.g., by passing required functions as arguments.

Prompt for AI agents
Address the following comment on core/util/chatDescriber.ts at line 80:

<comment>The `core` module is dynamically importing from `extensions/cli`, violating the architectural principle that `core` should be independent of any `extensions`. This creates tight coupling and reduces reusability. Dependencies should be inverted, e.g., by passing required functions as arguments.</comment>

<file context>
@@ -46,6 +46,91 @@ export class ChatDescriber {
+      try {
+        // Try to import CLI dependencies dynamically
+        const { convertFromUnifiedHistory } = await import(
+          &quot;../../extensions/cli/src/messageConversion.js&quot;
+        );
+        const { getDefaultCompletionOptions } = await import(
</file context>

currentSessionTitle?: string,
): Promise<string | undefined> {
// Only generate title for untitled sessions
if (currentSessionTitle && currentSessionTitle !== "Untitled Session") {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Avoid hard-coded "Untitled Session" string; reference a shared constant to keep session title checks consistent across modules.

Prompt for AI agents
Address the following comment on extensions/cli/src/ui/hooks/useChat.helpers.ts at line 429:

<comment>Avoid hard-coded &quot;Untitled Session&quot; string; reference a shared constant to keep session title checks consistent across modules.</comment>

<file context>
@@ -407,3 +415,41 @@ export async function handleAutoCompaction({
+  currentSessionTitle?: string,
+): Promise&lt;string | undefined&gt; {
+  // Only generate title for untitled sessions
+  if (currentSessionTitle &amp;&amp; currentSessionTitle !== &quot;Untitled Session&quot;) {
+    return undefined;
+  }
</file context>

format: options.format,
silent: options.silent,
compactionIndex,
firstAssistantResponse: isFirstMessage && !options.resume, // Only generate title for new conversations
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

firstAssistantResponse is computed after isFirstMessage is set to false, so the title generation path never triggers; compute it without depending on the mutated flag or capture it before toggling

Prompt for AI agents
Address the following comment on extensions/cli/src/commands/chat.ts at line 475:

<comment>firstAssistantResponse is computed after isFirstMessage is set to false, so the title generation path never triggers; compute it without depending on the mutated flag or capture it before toggling</comment>

<file context>
@@ -433,6 +472,7 @@ async function runHeadlessMode(
       format: options.format,
       silent: options.silent,
       compactionIndex,
+      firstAssistantResponse: isFirstMessage &amp;&amp; !options.resume, // Only generate title for new conversations
     });
 
</file context>
Suggested change
firstAssistantResponse: isFirstMessage && !options.resume, // Only generate title for new conversations
firstAssistantResponse: !options.resume && chatHistory.every(item => item.message.role === "system"), // Only generate title for new conversations

currentSession.title,
);

if (generatedTitle) {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Possible race: title can be overwritten if it changes while awaiting generateSessionTitle; recheck the session is still untitled before updating.

(Based on your team's feedback about session history-related bugs, I reviewed title updates for race conditions.)

Prompt for AI agents
Address the following comment on extensions/cli/src/ui/hooks/useChat.stream.helpers.ts at line 48:

<comment>Possible race: title can be overwritten if it changes while awaiting generateSessionTitle; recheck the session is still untitled before updating.

(Based on your team&#39;s feedback about session history-related bugs, I reviewed title updates for race conditions.)</comment>

<file context>
@@ -28,6 +34,21 @@ export function createStreamCallbacks(
+          currentSession.title,
+        );
+
+        if (generatedTitle) {
+          updateSessionTitle(generatedTitle);
+        }
</file context>

@dosubot dosubot bot added size:XL This PR changes 500-999 lines, ignoring generated files. and removed size:L This PR changes 100-499 lines, ignoring generated files. labels Aug 27, 2025
@dosubot dosubot bot added size:XXL This PR changes 1000+ lines, ignoring generated files. and removed size:XL This PR changes 500-999 lines, ignoring generated files. labels Aug 27, 2025
sestinj and others added 3 commits August 27, 2025 15:58
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
@sestinj sestinj merged commit 7ad698c into main Aug 28, 2025
55 checks passed
@sestinj sestinj deleted the generate-message-titles-in-cli branch August 28, 2025 05:29
@github-project-automation github-project-automation bot moved this from Todo to Done in Issues and PRs Aug 28, 2025
@github-actions github-actions bot locked and limited conversation to collaborators Aug 28, 2025
@github-actions github-actions bot added the tier 1 Big feature that took multiple weeks to launch and represents a big milestone for the product label Aug 28, 2025
@sestinj
Copy link
Contributor Author

sestinj commented Sep 1, 2025

🎉 This PR is included in version 1.12.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

@sestinj
Copy link
Contributor Author

sestinj commented Sep 3, 2025

🎉 This PR is included in version 1.12.0 🎉

The release is available on:

Your semantic-release bot 📦🚀

Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

released size:XXL This PR changes 1000+ lines, ignoring generated files. tier 1 Big feature that took multiple weeks to launch and represents a big milestone for the product

Projects

Status: Done

Development

Successfully merging this pull request may close these issues.

2 participants