Skip to content

Core prompts#798

Open
scriptease wants to merge 24 commits intoRunMaestro:rcfrom
scriptease:core-prompts
Open

Core prompts#798
scriptease wants to merge 24 commits intoRunMaestro:rcfrom
scriptease:core-prompts

Conversation

@scriptease
Copy link
Copy Markdown

@scriptease scriptease commented Apr 11, 2026

Implementation of #197, after merging this look at #804


Maestro Prompts: Full Control Over Your AI System Prompts

All 23 core system prompts are now fully customizable from a new Maestro Prompts tab in Settings. Edit wizard, auto run, group chat, context management, commit command, and other prompts — changes take effect immediately, no restart needed.

What's new

Settings Tab — A split-view browser/editor organized by category (Wizard, Inline Wizard, Auto Run, Group Chat, Context, Commands, System). Modified prompts show an orange indicator dot. Save and Reset to Default with instant effect.

Bildschirmfoto 2026-04-11 um 21 20 44

Command Palette — The 4 most-used prompts are accessible directly from Quick Actions (Cmd+K): Maestro System Prompt, Auto Run Default, Commit Command, and Group Chat Moderator. Configurable via QUICK_ACTION_PROMPTS in promptDefinitions.ts.

Bildschirmfoto 2026-04-11 um 21 20 33

CLI Supportmaestro-cli playbook runs now respect user prompt customizations. The CLI reads the same core-prompts-customizations.json that the desktop app writes, so edits made in the UI carry over to batch execution.

Architecture — Prompts are loaded from disk at startup instead of being compiled into the app bundle. User customizations are stored separately and survive app updates. Bundled defaults can always be restored with one click.

Migration notes

  • The Group Chat settings tab has been removed. Existing moderator standing instructions are automatically migrated into a customization of the group-chat-moderator-system prompt on first launch.
  • The build:prompts step and scripts/generate-prompts.mjs are removed. The src/generated/ directory no longer exists.
  • src/prompts/index.ts now re-exports shared definitions from src/shared/promptDefinitions.ts instead of compiled constants.

Files created

  • src/shared/promptDefinitions.ts — single source of truth for 23 prompt IDs, filenames, categories
  • src/main/prompt-manager.ts — disk loader with customization overlay
  • src/main/ipc/handlers/prompts.ts — IPC handlers (get, getAll, save, reset)
  • src/main/preload/prompts.ts — preload API factory
  • src/renderer/services/promptInit.ts — centralized renderer init (13 parallel loaders)
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.tsx + .css

Testing

  • All 25,018 tests pass
  • Full production build succeeds
  • Lint clean across all 3 tsconfig targets

Summary by CodeRabbit

  • New Features

    • Maestro Prompts settings tab: browse, edit, save, and reset core system prompts with immediate effect.
    • Quick Actions to open the prompt editor for common prompts.
    • Prompts are exposed to the UI and CLI so customizations apply at runtime.
  • Behavior Changes

    • Group Chat standing-instructions removed from the standalone settings UI and managed via editable prompts.
    • Prompts are initialized at startup (migrations applied); failures are surfaced and logged.
  • Documentation

    • Added docs describing prompt customization and CLI integration.

noreply and others added 18 commits April 11, 2026 18:40
Add shared prompt definitions (23 IDs, filenames, categories) and the
prompt-manager that loads prompts from disk at startup with user
customization support. Bundle core prompts as extraResources for all
three platforms.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add prompts IPC handlers (get, getAll, save, reset) and preload API
factory following the existing modular pattern. Register in both
handler and preload index files.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Call initializePrompts() early in app.whenReady() before any features
that use prompts. Fail-safe with dialog and quit on load failure.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace compiled prompt constant imports with getPrompt() calls in
group-chat, director-notes, tab-naming, and feedback handlers. Add
CLI-specific async prompt loader for batch-processor.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace compiled prompt imports with IPC-based cache+loader pattern
in settingsStore, agentStore, useInputProcessing, useWizardHandlers,
useAgentListeners, useMergeTransferHandlers, and batchUtils. Add
prompts namespace to MaestroAPI type in global.d.ts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace compiled prompt imports with IPC-based cache+loader pattern
in contextGroomer, contextSummarizer, inlineWizardConversation,
inlineWizardDocumentGeneration, wizardPrompts, and phaseGenerator.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Create promptInit.ts that loads all 13 renderer prompt caches in
parallel via IPC. Gate MaestroConsole rendering on promptsReady
state. Add refreshRendererPrompts() for Settings UI cache invalidation.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Delete src/generated/prompts.ts and scripts/generate-prompts.mjs.
Remove build:prompts from dev:main, dev:main:prod-data, build, and
validate:push scripts. Update src/prompts/index.ts to re-export
shared definitions. Remove src/generated/ from .gitignore.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add MaestroPromptsTab component with split-view browser/editor for
all 23 core prompts grouped by category. Supports save (immediate
effect via cache refresh), reset to default, modified indicators,
and unsaved-changes guard. Integrated into SettingsModal sidebar
and settings search.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Update 11 test files to mock prompt-manager (main process) or
window.maestro.prompts (renderer) instead of old compiled imports.
Add prompts mock to global test setup. Fix DEFAULT_BATCH_PROMPT
to use let with refresh on load. All 25018 tests pass.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add prompt customization entries to CLAUDE.md key files table:
Customize prompts, Add new prompt, and updated Modify system prompts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…g instructions

Add one-time migration that bakes existing moderator standing
instructions into a prompt customization. Remove Group Chat settings
tab, moderatorStandingInstructions state/setter, and standing
instructions injection from group-chat-router. Conductor profile
remains in General tab unchanged.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…error recovery

Address all issues found during code review of the core-prompts branch:

- HIGH: /commit prompt empty for first-run users. DEFAULT_AI_COMMANDS
  captured the empty cache at module load; loadSettingsStorePrompts()
  now rebuilds the array AND patches the live Zustand store when the
  commit command prompt is still blank.
- HIGH: Image-only messages default to empty prompt. DEFAULT_IMAGE_ONLY_PROMPT
  was a const evaluated at module load; converted to let and updated
  after loadInputProcessingPrompts() completes.
- HIGH: CLI prompt loading broken outside dev/Electron. Replaced the
  Electron-only process.resourcesPath fallback with multi-candidate
  path resolution (dev, Electron, script-relative, __dirname-relative).
- MEDIUM: Renderer prompt init cannot recover after failure. Cleared
  initPromise on rejection so subsequent calls retry instead of
  returning the same rejected promise.
- HIGH (DEFAULT_BATCH_PROMPT): Same module-load-time const issue;
  converted to let with refresh-on-load (fixed in phase 10).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Replace hardcoded .maestro/playbooks/ paths with the {{AUTORUN_FOLDER}}
template variable so generated Auto Run documents are saved to the
agent's configured Auto Run folder.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
registerPromptsHandlers() was only added to registerAllHandlers() in
the handlers index, but main/index.ts registers handlers individually
and never calls registerAllHandlers(). Import and call it directly
alongside the other no-dependency handlers.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
… 23 seemed excessive and once the user finds the four he finds the others
…izations

- HIGH: Zustand store patch guard changed from !commitCmd.prompt to
  commitCmd.prompt !== cachedCommitCommandPrompt so save/reset in
  Maestro Prompts tab updates the live /commit command mid-session
- MEDIUM: CLI getCliPrompt() now reads core-prompts-customizations.json
  from userData before falling back to bundled files, matching the
  same customization precedence as the Electron prompt-manager

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add Maestro Prompts row to Settings tabs table and new section in
docs/configuration.md covering editing, resetting, and Quick Actions
access. Add Prompt Customization section to docs/cli.md with the 5
CLI-relevant prompts, customizations JSON file paths per platform,
and file format for direct editing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 11, 2026

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Walkthrough

Moves prompts from build-time generated constants to a runtime, disk-backed system: adds a main-process prompt manager, IPC + preload prompt API, renderer initialization and per-module async loaders, a Maestro Prompts settings UI (save/reset), packaging of markdown prompts, and updates across main/renderer/CLI consumers and tests.

Changes

Cohort / File(s) Summary
Build & Packaging
/.gitignore, package.json, scripts/generate-prompts.mjs
Stop embedding generated prompt constants: remove generation script and build steps, include src/prompts/*.md in packaged extraResources, and delete scripts/generate-prompts.mjs.
Prompt Definitions
src/shared/promptDefinitions.ts, src/prompts/index.ts
Introduce centralized CORE_PROMPTS, PROMPT_IDS, getPromptFilename; switch exports away from generated string constants to prompt metadata and utilities.
Main: Prompt Manager & Startup
src/main/prompt-manager.ts, src/main/index.ts
Add prompt-manager with initialize/get/getAll/save/reset/getAllIds/arePromptsInitialized; await initialization at startup, register prompts IPC handlers, run one-time moderator-standing-instructions migration, and fail-fast on init errors.
IPC & Preload
src/main/ipc/handlers/prompts.ts, src/main/ipc/handlers/index.ts, src/main/preload/prompts.ts, src/main/preload/index.ts
New IPC handlers and preload API exposing maestro.prompts methods: get, getAll, getAllIds, save, reset for renderer access.
Main: Consumer Updates
src/main/group-chat/..., src/main/ipc/handlers/*, src/cli/services/batch-processor.ts
Replace static prompt imports with getPrompt(id); CLI batch processor adds runtime prompt resolution, customization overlay loading/saving (core-prompts-customizations.json) and multi-path file probing.
Renderer: Init & Centralization
src/renderer/App.tsx, src/renderer/services/promptInit.ts, src/renderer/global.d.ts
Add renderer prompt initialization gating, centralized initialize/refresh/areInitialized APIs, and maestro.prompts type declarations.
Renderer: Lazy Prompt Loaders
src/renderer/services/*, src/renderer/hooks/*, src/renderer/stores/*
Many modules replace static prompt constants with per-module async loaders (load*Prompts) and cached getters; some exported consts become mutable to be updated post-load.
Renderer: Settings UI & Integration
src/renderer/components/Settings/..., src/renderer/stores/modalStore.ts, src/renderer/types/index.ts, src/renderer/components/QuickActionsModal.tsx
Add "Maestro Prompts" settings tab component/editor, searchable settings entry, modal wiring for initial prompt selection, quick actions to open prompt editors, and associated CSS.
Settings Store & API Surface
src/renderer/stores/settingsStore.ts, src/renderer/hooks/settings/useSettings.ts, src/renderer/components/Settings/SettingsModal.tsx
Remove moderatorStandingInstructions from settings surface and UI; make commit-command prompt dynamic and reconcile store defaults with loaded prompts.
Tests: Mocks & Setup
src/__tests__/**, src/__tests__/setup.ts
Update tests to mock prompt-manager or window.maestro.prompts, load prompt markdown from src/prompts where required, and add maestro.prompts mocks in test setup.
Docs & Prompt Content Edits
CLAUDE.md, docs/cli.md, docs/configuration.md, src/prompts/...
Document prompt customization workflows (Settings UI and core-prompts-customizations.json), CLI behavior, and update autorun folder placeholders in prompt MD files.
UI Styling
src/renderer/components/Settings/tabs/MaestroPromptsTab.css
Add stylesheet for the Maestro Prompts editor tab.

Sequence Diagram(s)

sequenceDiagram
    participant Renderer as "Renderer"
    participant Preload as "Preload (ipcRenderer)"
    participant Main as "Main (ipcMain / prompt-manager)"
    participant FS as "FileSystem"

    Renderer->>Preload: maestro.prompts.getAll() / get(id)
    Preload->>Main: ipc.invoke('prompts:getAll' / 'prompts:get', id)
    Main->>Main: if !initialized -> initializePrompts()
    Main->>FS: read bundled prompt files (resources/src/prompts/*.md)
    Main->>FS: read userData/core-prompts-customizations.json (optional)
    Main->>Main: merge defaults + customizations -> promptCache
    Main-->>Preload: return prompts / content
    Preload-->>Renderer: resolve Promise with { success, prompts|content }
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related issues

Possibly related PRs

Suggested labels

approved

Poem

🐰 I used to hide prompts deep in a build,

now I tuck them on disk, fresh and unsealed.
A Maestro tab to tweak each guiding line,
save, reset, and watch the system refine. 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 48.75% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'Core prompts' is vague and generic, failing to convey the specific nature of this substantial architectural change that removes prompt generation, adds a settings UI, and implements runtime prompt loading. Revise to a more specific title such as 'Add Maestro Prompts Settings tab with runtime prompt loading' or 'Migrate prompts from build-time generation to runtime disk loading' to clearly communicate the primary changes.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@greptile-apps
Copy link
Copy Markdown

greptile-apps bot commented Apr 11, 2026

Greptile Summary

This PR implements full user customization of all 23 core system prompts via a new Maestro Prompts tab in Settings, along with CLI support for reading those customizations. The previous review findings (CLI not loading customizations, missing Sentry reporting, bare console.* calls) have all been addressed in the latest commits. The architecture is clean: promptDefinitions.ts is the single source of truth, prompt-manager.ts handles disk I/O in the main process, and promptInit.ts orchestrates 13 parallel IPC loaders in the renderer at startup.

Confidence Score: 5/5

Safe to merge — all P0/P1 findings from the previous review are resolved; only minor P2 style issues remain.

All three prior review concerns (CLI not loading customizations, missing Sentry capture on prompt-init failure, bare console.* calls) have been addressed in follow-up commits. The remaining findings are P2: an unawaited IPC save in the one-time commit-command migration (retries on next startup, no data loss) and a redundant double-call to unregisterCliActivity (idempotent, harmless).

src/renderer/stores/settingsStore.ts (unawaited migration save, line 78)

Important Files Changed

Filename Overview
src/shared/promptDefinitions.ts Clean single source of truth for 23 prompt IDs, filenames, categories, and QUICK_ACTION_PROMPTS; well-structured with type-safe PROMPT_IDS constants.
src/main/prompt-manager.ts Disk loader with customization overlay; handles ENOENT gracefully and reads bundle before deleting customization to prevent data loss.
src/main/ipc/handlers/prompts.ts IPC handlers for get/getAll/getAllIds/save/reset; all handlers guard against uninitialized state and return structured error objects rather than throwing.
src/renderer/services/promptInit.ts Centralized init with correct concurrency guard (promise deduplication), Sentry capture on failure, and force-refresh path for post-save updates.
src/renderer/stores/settingsStore.ts Adds commit-command prompt migration path; the IPC save call on line 78 is fire-and-forget (unawaited), silently losing failures during one-time migration.
src/cli/services/batch-processor.ts Added getCustomizedPrompt() to honour user customizations in CLI; try/finally for unregisterCliActivity causes benign double-calls on early-exit paths.
src/renderer/components/Settings/tabs/MaestroPromptsTab.tsx Split-view browse/edit UI with Sentry capture on all error paths, auto-dismiss success toast, and post-save renderer cache refresh.
src/main/index.ts One-time migration of moderatorStandingInstructions into prompt customization on startup; guarded by a migration flag and idempotency check on content.

Sequence Diagram

sequenceDiagram
    participant Renderer as Renderer (App.tsx)
    participant PromptInit as promptInit.ts
    participant IPC as IPC (preload)
    participant Main as Main Process
    participant PM as prompt-manager.ts
    participant Disk as Disk

    Renderer->>PromptInit: initializeRendererPrompts()
    PromptInit->>IPC: prompts.get(id) x13 parallel
    IPC->>Main: prompts:get / prompts:getAll
    Main->>PM: getPrompt(id) / getAllPrompts()
    PM-->>Main: cached content
    Main-->>IPC: success, content
    IPC-->>PromptInit: resolved
    PromptInit-->>Renderer: promptsReady = true

    Note over Renderer,Disk: User edits a prompt in Settings
    Renderer->>IPC: prompts.save(id, content)
    IPC->>Main: prompts:save
    Main->>PM: savePrompt(id, content)
    PM->>Disk: read customizations.json
    PM->>Disk: write customizations.json
    PM->>PM: update in-memory cache
    Main-->>IPC: success true
    IPC-->>Renderer: result
    Renderer->>PromptInit: refreshRendererPrompts()
    PromptInit->>IPC: reload all 13 loaders force=true
Loading

Reviews (2): Last reviewed commit: "fix: address code review findings across..." | Re-trigger Greptile

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 15

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/group-chat/group-chat-router.ts (1)

769-781: ⚠️ Potential issue | 🟡 Minor

Replace all {{CONDUCTOR_PROFILE}} placeholders in editable moderator prompts.

Now that these templates are user-customizable, {{CONDUCTOR_PROFILE}} can reasonably appear more than once. The single replace(...) used just above these prompt bodies only resolves the first token, so later copies leak through verbatim.

Suggested fix
-			const baseSystemPrompt = getModeratorSystemPrompt().replace(
-				'{{CONDUCTOR_PROFILE}}',
-				moderatorSettings.conductorProfile || '(No conductor profile set)'
-			);
+			const baseSystemPrompt = getModeratorSystemPrompt().replace(
+				/\{\{CONDUCTOR_PROFILE\}\}/g,
+				moderatorSettings.conductorProfile || '(No conductor profile set)'
+			);
...
-	const synthBasePrompt = getModeratorSystemPrompt().replace(
-		'{{CONDUCTOR_PROFILE}}',
-		synthModeratorSettings.conductorProfile || '(No conductor profile set)'
-	);
+	const synthBasePrompt = getModeratorSystemPrompt().replace(
+		/\{\{CONDUCTOR_PROFILE\}\}/g,
+		synthModeratorSettings.conductorProfile || '(No conductor profile set)'
+	);

Also applies to: 1701-1716

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/group-chat/group-chat-router.ts` around lines 769 - 781, The
moderator prompt construction is vulnerable because a single replace call only
replaces the first '{{CONDUCTOR_PROFILE}}' token, leaving later occurrences
unchanged; update the code that prepares editable moderator prompt templates
(e.g., the strings used to build fullPrompt and the other prompt around lines
1701-1716) to perform a global replacement of '{{CONDUCTOR_PROFILE}}' (use a
global regex or split/join) on the template(s) before assembling fullPrompt so
every placeholder instance is resolved.
🧹 Nitpick comments (5)
src/__tests__/main/group-chat/group-chat-agent.test.ts (1)

53-58: Make unknown prompt IDs fail this mock instead of returning placeholder text.

Line 53 and Line 57 currently hide prompt wiring mistakes. If a production ID changes or is misspelled, these tests can still pass.

Suggested fix
 		const filename = filenameMap[id];
-		if (!filename) return `mock prompt for ${id}`;
+		if (!filename) throw new Error(`Unexpected prompt id in test: ${id}`);
 		try {
 			return fs.readFileSync(path.join(promptsDir, filename), 'utf-8');
-		} catch {
-			return `mock prompt for ${id}`;
+		} catch (error) {
+			throw new Error(`Failed to load prompt fixture for ${id}: ${String(error)}`);
 		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/__tests__/main/group-chat/group-chat-agent.test.ts` around lines 53 - 58,
The mock currently returns a placeholder string when a prompt filename is
missing or read fails, which masks wiring errors; update the mock so that when
filename is falsy or fs.readFileSync(path.join(promptsDir, filename), 'utf-8')
throws, the mock throws a descriptive Error instead (including the prompt id and
filename) rather than returning `mock prompt for ${id}`, so missing/unknown
prompt IDs fail the test; locate the logic using variables filename, promptsDir
and the fs.readFileSync call and replace the fallback return with throwing an
Error that mentions the id and filename.
src/renderer/services/inlineWizardConversation.ts (1)

228-238: Consider adding a guard for empty prompts.

If loadInlineWizardConversationPrompts() hasn't completed, the getters return empty strings, and generateInlineWizardPrompt() would return a mostly empty prompt after template substitution. While the inline wizard is user-initiated (so prompts should be loaded by then), a defensive check could prevent confusing agent behavior if prompts somehow aren't loaded.

💡 Optional defensive check
 export function generateInlineWizardPrompt(config: InlineWizardConversationConfig): string {
 	const { mode, projectName, directoryPath, goal, existingDocs, autoRunFolderPath } = config;

 	// Select the base prompt based on mode
 	let basePrompt: string;
 	if (mode === 'iterate') {
 		basePrompt = getWizardInlineIteratePrompt();
 	} else {
 		// 'new' mode uses the new plan prompt
 		basePrompt = getWizardInlineNewPrompt();
 	}

+	if (!basePrompt) {
+		throw new Error(`Inline wizard prompt not loaded for mode: ${mode}`);
+	}
+
 	// Handle wizard-specific variables...
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/services/inlineWizardConversation.ts` around lines 228 - 238,
generateInlineWizardPrompt currently assumes
getWizardInlineIteratePrompt/getWizardInlineNewPrompt return non-empty strings;
add a defensive guard after selecting basePrompt (e.g., after calling
getWizardInlineIteratePrompt or getWizardInlineNewPrompt) to detect an empty
string (which can happen if loadInlineWizardConversationPrompts() hasn't run)
and handle it by returning a clear fallback prompt or throwing/logging a
descriptive error. Reference generateInlineWizardPrompt,
getWizardInlineIteratePrompt, getWizardInlineNewPrompt and
loadInlineWizardConversationPrompts when adding the check so the code fails fast
with an informative message instead of producing a mostly empty prompt.
src/renderer/services/contextGroomer.ts (1)

197-197: Consider defensive checks for empty prompts.

Unlike agentStore.ts which checks getMaestroSystemPrompt() for truthiness before use, these prompt usages don't guard against empty strings. If loadContextGroomerPrompts() hasn't completed, getContextTransferPrompt() and getContextGroomingPrompt() return empty strings.

Given context grooming is user-initiated after app startup, this is likely fine in practice, but consider adding a guard or logging a warning if the prompt is unexpectedly empty.

Also applies to: 392-392

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/services/contextGroomer.ts` at line 197, The prompt-returning
functions getContextTransferPrompt() and getContextGroomingPrompt() can return
empty strings if loadContextGroomerPrompts() hasn't finished; add a defensive
guard inside the callers (or within these functions) to detect an empty/falsey
prompt and either log a warning via the existing logger or return a sensible
fallback before proceeding; specifically update the return site that currently
just returns getContextTransferPrompt() (and the analogous site calling
getContextGroomingPrompt()) to check the prompt value, log a clear warning
mentioning getContextTransferPrompt/getContextGroomingPrompt, and avoid using an
empty prompt (e.g., abort the grooming flow or use a default message).
src/shared/promptDefinitions.ts (1)

192-208: Minor: Duplicate "System" section comments.

Lines 192-193 and 203-204 both have // System comments, making the grouping slightly confusing. Consider renaming one to // System (misc) or similar for clarity.

📝 Suggested comment clarification
 	// System
 	MAESTRO_SYSTEM_PROMPT: 'maestro-system-prompt',
 	// Group Chat
 	GROUP_CHAT_MODERATOR_SYSTEM: 'group-chat-moderator-system',
 	GROUP_CHAT_MODERATOR_SYNTHESIS: 'group-chat-moderator-synthesis',
 	GROUP_CHAT_PARTICIPANT: 'group-chat-participant',
 	GROUP_CHAT_PARTICIPANT_REQUEST: 'group-chat-participant-request',
 	// Context
 	CONTEXT_GROOMING: 'context-grooming',
 	CONTEXT_TRANSFER: 'context-transfer',
 	CONTEXT_SUMMARIZE: 'context-summarize',
-	// System
+	// System (utilities)
 	TAB_NAMING: 'tab-naming',
 	DIRECTOR_NOTES: 'director-notes',
 	FEEDBACK: 'feedback',
 	FEEDBACK_CONVERSATION: 'feedback-conversation',
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/shared/promptDefinitions.ts` around lines 192 - 208, The comment header
"System" is duplicated and causes confusion; change the second occurrence above
TAB_NAMING/DIRECTOR_NOTES/FEEDBACK (locate around the constants TAB_NAMING,
DIRECTOR_NOTES, FEEDBACK, FEEDBACK_CONVERSATION) to a clearer label such as "//
System (misc)" or "// System - UI/meta" so the grouping is distinct from the
earlier "System" header near MAESTRO_SYSTEM_PROMPT; update only the comment
text—no code logic changes.
src/main/prompt-manager.ts (1)

72-87: Consider using unknown instead of any for caught error.

Minor typing improvement for stricter type safety.

📝 Suggested type refinement
-async function loadUserCustomizations(): Promise<StoredData | null> {
-	const filePath = getCustomizationsPath();
-	try {
-		const content = await fs.readFile(filePath, 'utf-8');
-		return JSON.parse(content) as StoredData;
-	} catch (error: any) {
-		// File not existing is expected (no customizations yet)
-		if (error?.code === 'ENOENT') {
-			return null;
-		}
+async function loadUserCustomizations(): Promise<StoredData | null> {
+	const filePath = getCustomizationsPath();
+	try {
+		const content = await fs.readFile(filePath, 'utf-8');
+		return JSON.parse(content) as StoredData;
+	} catch (error: unknown) {
+		// File not existing is expected (no customizations yet)
+		if (error instanceof Error && 'code' in error && error.code === 'ENOENT') {
+			return null;
+		}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/main/prompt-manager.ts` around lines 72 - 87, In
loadUserCustomizations(), change the catch parameter type from any to unknown
for stricter typing; then narrow it before accessing .code by asserting or
guarding (e.g., const err = error as NodeJS.ErrnoException | undefined and check
err?.code === 'ENOENT') and use a safe string conversion when logging (e.g.,
String(error) or util.inspect) so you don't access properties on an unknown
value directly; update the logger.error call to use that converted value.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/__tests__/renderer/stores/agentStore.test.ts`:
- Around line 110-119: The prompts.get mock currently returns Promise.resolve({
success: true, content: prompts[id] ?? '' }) which treats unknown IDs the same
as intentionally empty prompts; change the mock in the test (the vi.fn for
prompts.get) so it checks whether id exists in the prompts map and returns
Promise.resolve({ success: true, content: prompts[id] }) for known keys and
Promise.resolve({ success: false, content: '' }) (or similar error payload) for
unknown keys — update the vi.fn implementation used in the test to explicit
existence-check the prompts map rather than using the nullish coalescing
fallback.

In `@src/main/ipc/handlers/prompts.ts`:
- Around line 65-73: The prompts:save IPC handler is missing the initialization
guard; add the same arePromptsInitialized() check used in prompts:get,
prompts:getAll, and prompts:getAllIds at the start of the
ipcMain.handle('prompts:save', ...) callback and immediately return a failure
response (e.g., { success: false, error: 'Prompts not initialized' }) if it
returns false, before calling savePrompt(id, content); keep the existing
try/catch and logger.error usage around savePrompt unchanged.
- Around line 76-84: The 'prompts:reset' IPC handler lacks the initialization
guard present in 'prompts:save'; update the ipcMain.handle('prompts:reset', ...)
handler to first perform the same initialization check used in the
'prompts:save' handler (reuse the same condition/utility), return the same
failure shape when not initialized, and log the event via the same logger
pattern before calling resetPrompt(id) so resetPrompt and logger are only
invoked when initialization is confirmed.

In `@src/renderer/App.tsx`:
- Around line 3102-3105: The catch block that currently only console.errors
prompt initialization failures should also report the failure to Sentry: import
and call captureException(err, { extra: { stage: 'prompt-init' } }) (or
captureMessage for a short event) from the Sentry utilities, include contextual
metadata (e.g., userId, environment, or any available state), then continue to
call setPromptsReady(true) so the app degrades gracefully; update the error
handler around the promise rejection to use captureException/captureMessage
before setting prompts ready.

In `@src/renderer/components/Settings/SettingsModal.tsx`:
- Line 635: The prompts tab mounts MaestroPromptsTab when activeTab ===
'prompts' but doesn't render the expected search anchor/data attribute that
PROMPTS_SETTINGS registers (data-setting-id="prompts-editor"), so
handleSearchNavigate can't find or scroll to the target; update the tab render
to include a wrapper element with the matching data-setting-id (e.g., add a
container around MaestroPromptsTab with data-setting-id="prompts-editor") so
that when activeTab switches to 'prompts' the search can locate, scroll to, and
highlight the correct element.

In `@src/renderer/components/Settings/tabs/MaestroPromptsTab.css`:
- Line 115: The CSS font-family declaration is failing stylelint's
font-family-name-quotes rule; update the font-family property (the line
containing font-family: 'Monaco', 'Menlo', 'Consolas', monospace;) to remove the
unnecessary single quotes around Monaco, Menlo and Consolas so it becomes:
font-family: Monaco, Menlo, Consolas, monospace; (keep quotes only for family
names that require them).

In `@src/renderer/components/Settings/tabs/MaestroPromptsTab.tsx`:
- Around line 55-72: The load/save/reset flows in MaestroPromptsTab currently
swallow failures by only calling setError; import and use the Sentry helpers
(captureException and optionally captureMessage) from src/utils/sentry.ts and
report errors alongside setting UI state: in the useEffect load block catch
clause call captureException(err) before setError, and when
window.maestro.prompts.getAll() or save/reset returns result.success === false
call captureMessage or captureException with contextual info (include operation
name and result.error) before setError; apply the same pattern inside the save
handler (savePrompt) and the reset handler (resetPrompt) so all unexpected
exceptions or failed IPC results are sent to Sentry while preserving the
existing setError UI behavior.
- Around line 55-72: The initial load in MaestroPromptsTab's useEffect swallows
getAll() failures by leaving selectedPrompt null so the error UI never renders;
update the loader logic to always surface load errors regardless of
selectedPrompt by setting an explicit load state or forcing selectedPrompt to a
sentinel when getAll() fails: in the async block around
window.maestro.prompts.getAll() ensure you call setError(...) and also set a
"loaded" flag (e.g., setPromptsLoaded(true)) or setSelectedPrompt to a sentinel
value, then update the component render path to check that error/loaded state
(instead of only checking selectedPrompt) so the error message displays on
initial failure; apply the same change to the similar logic in the other range
(lines 220-299) that fetches prompts.

In `@src/renderer/components/Wizard/services/phaseGenerator.ts`:
- Around line 17-33: The code currently uses cachedPhaseGenDocPrompt = '' as a
sentinel which conflates an intentionally blank prompt with "not loaded"; change
cachedPhaseGenDocPrompt to be nullable (string | null) and initialize to null,
update loadPhaseGeneratorPrompts to set it to the fetched string, and update
getWizardDocumentGenerationPrompt to either throw or assert when
phaseGeneratorPromptsLoaded is false (or return null) so callers (e.g.,
generateDocumentGenerationPrompt) cannot silently use an empty prompt; also
update any other usages that read cachedPhaseGenDocPrompt (the similar block
referenced at lines around 370-374) to handle the nullable value or call
loadPhaseGeneratorPrompts before accessing the getter.

In `@src/renderer/components/Wizard/services/wizardPrompts.ts`:
- Around line 15-46: The caches cachedWizardSystemPrompt and
cachedWizardSystemContinuationPrompt are initialized to empty strings so callers
(generateSystemPrompt) can't tell "not loaded" from intentionally blank; change
their types to string | null (initialize to null), and in loadWizardPrompts
assign the loaded content as before and set wizardPromptsLoaded = true; update
getWizardSystemPrompt and getWizardSystemContinuationPrompt to either throw an
Error when wizardPromptsLoaded is false (or return null consistently) so callers
can detect the unloaded state; adjust any callers that assume non-null to handle
null/throw accordingly.

In `@src/renderer/hooks/agent/useAgentListeners.ts`:
- Around line 53-69: The cachedAutorunSynopsisPrompt uses an empty string as the
unloaded sentinel and getAutorunSynopsisPrompt returns it without verifying load
state; change the logic so the unloaded state is explicit and guarded: keep
agentListenersPromptsLoaded boolean and modify getAutorunSynopsisPrompt (and any
consumers like SYNOPSIS_PROMPT usage) to throw or return null if
agentListenersPromptsLoaded is false, or alternatively initialize
cachedAutorunSynopsisPrompt to undefined and make getAutorunSynopsisPrompt
validate agentListenersPromptsLoaded (or undefined) before returning the prompt;
ensure loadAgentListenersPrompts still sets cachedAutorunSynopsisPrompt and
flips agentListenersPromptsLoaded to true so callers cannot treat an
uninitialized cache as a valid empty prompt.

In `@src/renderer/hooks/agent/useMergeTransferHandlers.ts`:
- Around line 28-44: The code uses an empty-string sentinel
(cachedMergeTransferSystemPrompt = '') which conflates "loaded-but-empty" with
"not-loaded" and can cause appendSystemPrompt to be skipped; change
cachedMergeTransferSystemPrompt to a nullable/undefined initial value (e.g., let
cachedMergeTransferSystemPrompt: string | null = null), keep using
mergeTransferPromptsLoaded as the explicit loaded flag in
loadMergeTransferPrompts (set cachedMergeTransferSystemPrompt = result.content!
which may be '' and set mergeTransferPromptsLoaded = true), and update
getMaestroSystemPrompt to return the nullable value (or undefined) instead of ''
so callers (e.g., where appendSystemPrompt is invoked) can distinguish an
unloaded state from a deliberately blank prompt and only skip appendSystemPrompt
when mergeTransferPromptsLoaded is true and the prompt is truly empty.

In `@src/renderer/hooks/wizard/useWizardHandlers.ts`:
- Around line 40-56: The cached prompt should not default to an empty string
because that makes "not loaded" indistinguishable from "intentionally blank";
change cachedAutorunSynopsisPrompt to be nullable (e.g. string | null) and
update loadWizardHandlersPrompts to set it to result.content (which may be an
empty string) and wizardHandlersPromptsLoaded to true, then modify
getAutorunSynopsisPrompt to first check wizardHandlersPromptsLoaded (or that
cachedAutorunSynopsisPrompt !== null) and either throw/return undefined when not
loaded so callers (e.g. the /history composition) can detect and avoid using an
ambiguous value; update any other usages that call getAutorunSynopsisPrompt
(including the other referenced occurrences) to handle the not-loaded case
accordingly.

In `@src/renderer/services/contextSummarizer.ts`:
- Around line 52-54: The function getContextSummarizePrompt currently returns
cachedContextSummarizePrompt even when it's empty, which yields a misleading
prompt; update getContextSummarizePrompt (and the similar code path around where
the cached prompt is read at the other spot) to check that
cachedContextSummarizePrompt is initialized/non-empty and throw an explicit
Error (or assert) if not, so calling code fails fast instead of proceeding with
an invalid prompt; reference the cachedContextSummarizePrompt variable and the
getContextSummarizePrompt function when making the change.

In `@src/renderer/services/inlineWizardDocumentGeneration.ts`:
- Around line 19-50: The cached prompt variables
(cachedWizardDocumentGenerationPrompt,
cachedWizardInlineIterateGenerationPrompt) currently default to '' which makes
"unloaded" indistinguishable from intentionally blank; update the implementation
so caches are nullable (e.g. string | null) or have callers guard on
inlineWizardDocGenPromptsLoaded: change loadInlineWizardDocGenPrompts to set the
caches to non-null values and inlineWizardDocGenPromptsLoaded=true on success,
and modify getWizardDocumentGenerationPrompt and
getWizardInlineIterateGenerationPrompt (and any call sites like
generateDocumentPrompt) to either throw/return a safe fallback when
inlineWizardDocGenPromptsLoaded is false or check for null before using the
prompt so an uninitialized state cannot be treated as an empty template.

---

Outside diff comments:
In `@src/main/group-chat/group-chat-router.ts`:
- Around line 769-781: The moderator prompt construction is vulnerable because a
single replace call only replaces the first '{{CONDUCTOR_PROFILE}}' token,
leaving later occurrences unchanged; update the code that prepares editable
moderator prompt templates (e.g., the strings used to build fullPrompt and the
other prompt around lines 1701-1716) to perform a global replacement of
'{{CONDUCTOR_PROFILE}}' (use a global regex or split/join) on the template(s)
before assembling fullPrompt so every placeholder instance is resolved.

---

Nitpick comments:
In `@src/__tests__/main/group-chat/group-chat-agent.test.ts`:
- Around line 53-58: The mock currently returns a placeholder string when a
prompt filename is missing or read fails, which masks wiring errors; update the
mock so that when filename is falsy or fs.readFileSync(path.join(promptsDir,
filename), 'utf-8') throws, the mock throws a descriptive Error instead
(including the prompt id and filename) rather than returning `mock prompt for
${id}`, so missing/unknown prompt IDs fail the test; locate the logic using
variables filename, promptsDir and the fs.readFileSync call and replace the
fallback return with throwing an Error that mentions the id and filename.

In `@src/main/prompt-manager.ts`:
- Around line 72-87: In loadUserCustomizations(), change the catch parameter
type from any to unknown for stricter typing; then narrow it before accessing
.code by asserting or guarding (e.g., const err = error as NodeJS.ErrnoException
| undefined and check err?.code === 'ENOENT') and use a safe string conversion
when logging (e.g., String(error) or util.inspect) so you don't access
properties on an unknown value directly; update the logger.error call to use
that converted value.

In `@src/renderer/services/contextGroomer.ts`:
- Line 197: The prompt-returning functions getContextTransferPrompt() and
getContextGroomingPrompt() can return empty strings if
loadContextGroomerPrompts() hasn't finished; add a defensive guard inside the
callers (or within these functions) to detect an empty/falsey prompt and either
log a warning via the existing logger or return a sensible fallback before
proceeding; specifically update the return site that currently just returns
getContextTransferPrompt() (and the analogous site calling
getContextGroomingPrompt()) to check the prompt value, log a clear warning
mentioning getContextTransferPrompt/getContextGroomingPrompt, and avoid using an
empty prompt (e.g., abort the grooming flow or use a default message).

In `@src/renderer/services/inlineWizardConversation.ts`:
- Around line 228-238: generateInlineWizardPrompt currently assumes
getWizardInlineIteratePrompt/getWizardInlineNewPrompt return non-empty strings;
add a defensive guard after selecting basePrompt (e.g., after calling
getWizardInlineIteratePrompt or getWizardInlineNewPrompt) to detect an empty
string (which can happen if loadInlineWizardConversationPrompts() hasn't run)
and handle it by returning a clear fallback prompt or throwing/logging a
descriptive error. Reference generateInlineWizardPrompt,
getWizardInlineIteratePrompt, getWizardInlineNewPrompt and
loadInlineWizardConversationPrompts when adding the check so the code fails fast
with an informative message instead of producing a mostly empty prompt.

In `@src/shared/promptDefinitions.ts`:
- Around line 192-208: The comment header "System" is duplicated and causes
confusion; change the second occurrence above TAB_NAMING/DIRECTOR_NOTES/FEEDBACK
(locate around the constants TAB_NAMING, DIRECTOR_NOTES, FEEDBACK,
FEEDBACK_CONVERSATION) to a clearer label such as "// System (misc)" or "//
System - UI/meta" so the grouping is distinct from the earlier "System" header
near MAESTRO_SYSTEM_PROMPT; update only the comment text—no code logic changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 58de1a76-60e6-43bb-95e4-bd9ee7729b31

📥 Commits

Reviewing files that changed from the base of the PR and between b34f55a and a897ef8.

📒 Files selected for processing (54)
  • .gitignore
  • CLAUDE.md
  • package.json
  • scripts/generate-prompts.mjs
  • src/__tests__/main/group-chat/group-chat-agent.test.ts
  • src/__tests__/main/group-chat/group-chat-moderator.test.ts
  • src/__tests__/main/group-chat/group-chat-router.test.ts
  • src/__tests__/main/ipc/handlers/director-notes.test.ts
  • src/__tests__/main/ipc/handlers/tabNaming.test.ts
  • src/__tests__/renderer/components/BatchRunnerModal.test.tsx
  • src/__tests__/renderer/components/Wizard/services/wizardPrompts.test.ts
  • src/__tests__/renderer/hooks/useWizardHandlers.test.ts
  • src/__tests__/renderer/services/contextGroomer.test.ts
  • src/__tests__/renderer/services/inlineWizardDocumentGeneration.test.ts
  • src/__tests__/renderer/stores/agentStore.test.ts
  • src/__tests__/setup.ts
  • src/cli/services/batch-processor.ts
  • src/main/group-chat/group-chat-agent.ts
  • src/main/group-chat/group-chat-moderator.ts
  • src/main/group-chat/group-chat-router.ts
  • src/main/index.ts
  • src/main/ipc/handlers/director-notes.ts
  • src/main/ipc/handlers/feedback.ts
  • src/main/ipc/handlers/index.ts
  • src/main/ipc/handlers/prompts.ts
  • src/main/ipc/handlers/tabNaming.ts
  • src/main/preload/index.ts
  • src/main/preload/prompts.ts
  • src/main/prompt-manager.ts
  • src/prompts/index.ts
  • src/prompts/openspec/openspec.implement.md
  • src/prompts/speckit/speckit.implement.md
  • src/renderer/App.tsx
  • src/renderer/components/Settings/SettingsModal.tsx
  • src/renderer/components/Settings/searchableSettings.ts
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.css
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.tsx
  • src/renderer/components/Wizard/services/phaseGenerator.ts
  • src/renderer/components/Wizard/services/wizardPrompts.ts
  • src/renderer/global.d.ts
  • src/renderer/hooks/agent/useAgentListeners.ts
  • src/renderer/hooks/agent/useMergeTransferHandlers.ts
  • src/renderer/hooks/batch/batchUtils.ts
  • src/renderer/hooks/input/useInputProcessing.ts
  • src/renderer/hooks/settings/useSettings.ts
  • src/renderer/hooks/wizard/useWizardHandlers.ts
  • src/renderer/services/contextGroomer.ts
  • src/renderer/services/contextSummarizer.ts
  • src/renderer/services/inlineWizardConversation.ts
  • src/renderer/services/inlineWizardDocumentGeneration.ts
  • src/renderer/services/promptInit.ts
  • src/renderer/stores/agentStore.ts
  • src/renderer/stores/settingsStore.ts
  • src/shared/promptDefinitions.ts
💤 Files with no reviewable changes (3)
  • .gitignore
  • src/renderer/hooks/settings/useSettings.ts
  • scripts/generate-prompts.mjs

Replace bare console.log/console.error in promptInit.ts and App.tsx
with captureException() from renderer/utils/sentry so prompt
initialization failures surface in production error tracking.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

♻️ Duplicate comments (1)
src/renderer/components/Settings/SettingsModal.tsx (1)

637-639: ⚠️ Potential issue | 🟡 Minor

Add a searchable anchor wrapper for the Prompts tab content.

handleSearchNavigate depends on data-setting-id targets in the rendered tab content; this branch mounts MaestroPromptsTab without one, so search-driven scroll/highlight can miss this tab target.

Suggested fix
-						{activeTab === 'prompts' && (
-							<MaestroPromptsTab theme={theme} initialSelectedPromptId={initialSelectedPromptId} />
-						)}
+						{activeTab === 'prompts' && (
+							<div data-setting-id="prompts-editor">
+								<MaestroPromptsTab
+									theme={theme}
+									initialSelectedPromptId={initialSelectedPromptId}
+								/>
+							</div>
+						)}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/Settings/SettingsModal.tsx` around lines 637 - 639,
The Prompts tab mounts MaestroPromptsTab without the searchable anchor wrapper
that handleSearchNavigate expects; wrap the rendered content for the 'prompts'
branch in the same searchable anchor element (the container that includes the
data-setting-id attribute used elsewhere) so that handleSearchNavigate can find
the tab target; locate the conditional rendering where activeTab === 'prompts'
and ensure the outer element provides the correct data-setting-id (matching the
pattern other tabs use) and any CSS/role attributes required for
scroll/highlight behavior.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@docs/cli.md`:
- Around line 212-221: Update the fenced code block showing OS-specific paths to
include a language identifier (use "text") so markdownlint stops flagging it;
locate the triple-backtick block containing the macOS/Linux/Windows paths (the
block starting with "# macOS" and ending with
"%APPDATA%\\Maestro\\core-prompts-customizations.json") and change the opening
fence from ``` to ```text (ensure the closing fence remains ```).

In `@docs/configuration.md`:
- Line 21: The phrase "auto run" in the table row under "Maestro Prompts" should
use the product styling "Auto Run" consistently; update the text "Browse and
edit the 23 core system prompts (wizard, auto run, group chat, context, etc.)"
to "Browse and edit the 23 core system prompts (wizard, Auto Run, group chat,
context, etc.)" and make the same change for the matching occurrence mentioned
(the other instance at the same section). Locate the string in the docs and
replace the lowercase "auto run" with the styled "Auto Run" wherever it appears
in that table/descriptive list.

In `@src/cli/services/batch-processor.ts`:
- Around line 91-93: The thrown Error in the prompt-load failure path can leave
the CLI activity registered; update the prompt-loading code (the branch that
throws `Failed to load prompt "${id}"...`) so that after `registerCliActivity()`
has been called it always clears/unregisters the activity before propagating the
error—either by wrapping the load/register sequence in try/finally and calling
`unregisterCliActivity()` (or the existing CLI activity-clear function) in the
finally, or by calling that unregister function immediately before rethrowing
the Error; ensure this change covers the `runPlaybook()` flow so the desktop app
won't remain stuck as busy.

In `@src/main/index.ts`:
- Around line 395-405: The migration currently only checks for the header "##
Standing Instructions" and sets store.set(migratedKey, true) even when the
actual standingInstructions text wasn't copied; update the logic around
getPrompt('group-chat-moderator-system') and savePrompt(...) so you detect
whether the currentPrompt already contains the exact standingInstructions
content (not just the header) and only set the migratedKey after you
successfully insert or merge the standingInstructions; if the header exists but
the content differs, merge/replace the section with the legacy
standingInstructions and call await savePrompt('group-chat-moderator-system',
migratedPrompt) before calling store.set(migratedKey, true), using getPrompt,
savePrompt, standingInstructions, migratedKey and logger to locate and modify
the code paths.
- Around line 378-389: The catch block handling initializePrompts currently logs
and shows a dialog then quits without reporting to Sentry; import or require the
Sentry helper (captureException) and call await captureException(error, {
operation: 'startup:initializePrompts' }) inside that catch block before calling
app.quit() so the fatal error is sent to Sentry; update the catch that wraps
await initializePrompts() (the block that calls logger.error,
dialog.showErrorBox and app.quit) to await captureException(error, { operation:
'startup:initializePrompts' }) using the caught error variable.

In `@src/renderer/stores/settingsStore.ts`:
- Around line 71-79: The current logic in useSettingsStore (customAICommands /
commitCmd) overwrites a user's edited /commit prompt; before updating
customAICommands you must detect an existing override and migrate it into the
new prompt store (the "commit-command" prompt key) instead of clobbering it.
Concretely: in the block that reads useSettingsStore.getState().customAICommands
and finds commitCmd, if commitCmd.prompt differs from cachedCommitCommandPrompt
then write that existing commitCmd.prompt into the new prompt store (e.g., the
"commit-command" entry) and only then update customAICommands to ensure no
silent data loss; use the existing symbols commitCmd, cachedCommitCommandPrompt,
and useSettingsStore.setState to implement the migration atomically.

---

Duplicate comments:
In `@src/renderer/components/Settings/SettingsModal.tsx`:
- Around line 637-639: The Prompts tab mounts MaestroPromptsTab without the
searchable anchor wrapper that handleSearchNavigate expects; wrap the rendered
content for the 'prompts' branch in the same searchable anchor element (the
container that includes the data-setting-id attribute used elsewhere) so that
handleSearchNavigate can find the tab target; locate the conditional rendering
where activeTab === 'prompts' and ensure the outer element provides the correct
data-setting-id (matching the pattern other tabs use) and any CSS/role
attributes required for scroll/highlight behavior.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 63469149-3d03-4cae-8350-d0f9ec2b37b9

📥 Commits

Reviewing files that changed from the base of the PR and between a897ef8 and 9acaa37.

📒 Files selected for processing (12)
  • docs/cli.md
  • docs/configuration.md
  • src/cli/services/batch-processor.ts
  • src/main/index.ts
  • src/renderer/components/AppStandaloneModals.tsx
  • src/renderer/components/QuickActionsModal.tsx
  • src/renderer/components/Settings/SettingsModal.tsx
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.tsx
  • src/renderer/stores/modalStore.ts
  • src/renderer/stores/settingsStore.ts
  • src/renderer/types/index.ts
  • src/shared/promptDefinitions.ts
🚧 Files skipped from review as they are similar to previous changes (2)
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.tsx
  • src/shared/promptDefinitions.ts

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🧹 Nitpick comments (1)
src/renderer/App.tsx (1)

3102-3105: Avoid double-reporting the same prompt-init exception.

initializeRendererPrompts() already reports to Sentry in src/renderer/services/promptInit.ts:56-84 before rethrowing. Capturing again here can duplicate alerts/events.

Proposed adjustment
 		.catch((err) => {
-			captureException(err instanceof Error ? err : new Error(String(err)), {
-				extra: { context: 'MaestroConsole.initializeRendererPrompts' },
-			});
+			// Error is already captured in initializeRendererPrompts().
 			setPromptsReady(true); // Allow app to render; features degrade gracefully
 		});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/App.tsx` around lines 3102 - 3105, The catch in App.tsx that
calls captureException around initializeRendererPrompts is causing duplicate
Sentry reports because initializeRendererPrompts (in
src/renderer/services/promptInit.ts) already captures and rethrows the error;
remove the captureException call in the catch block (or guard it so it only
captures when the error wasn't already reported) and simply rethrow or handle
the error to avoid double-reporting.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Nitpick comments:
In `@src/renderer/App.tsx`:
- Around line 3102-3105: The catch in App.tsx that calls captureException around
initializeRendererPrompts is causing duplicate Sentry reports because
initializeRendererPrompts (in src/renderer/services/promptInit.ts) already
captures and rethrows the error; remove the captureException call in the catch
block (or guard it so it only captures when the error wasn't already reported)
and simply rethrow or handle the error to avoid double-reporting.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: f74353da-5fe2-4d34-92c1-263d04fdf102

📥 Commits

Reviewing files that changed from the base of the PR and between 9acaa37 and d29ac17.

📒 Files selected for processing (2)
  • src/renderer/App.tsx
  • src/renderer/services/promptInit.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/renderer/services/promptInit.ts

- Add arePromptsInitialized() guards to IPC save/reset handlers
- Null sentinel pattern (string | null = null) across 9 renderer caches
- Sentry reporting in MaestroPromptsTab, promptInit, App.tsx, main index
- CONDUCTOR_PROFILE global regex at both router locations
- CLI try/finally for unregisterCliActivity on generator throw
- Standing instructions migration checks exact content not just header
- /commit prompt migration preserves AI Commands user overrides
- Test mock improvements: explicit error on unknown IDs
- prompt-manager catch type narrowing (any → unknown)
- CSS font-family quote removal, docs styling fixes
- data-setting-id wrapper for settings search navigation

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@scriptease
Copy link
Copy Markdown
Author

@greptile please review again

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (6)
src/renderer/services/contextSummarizer.ts (1)

52-57: ⚠️ Potential issue | 🟠 Major

Fail fast when the summarization prompt cache is uninitialized.

At Line 54, returning '' produces a valid-looking prompt with missing system instructions (used at Line 402), causing silent behavior drift instead of a clear failure.

Proposed fix
 function getContextSummarizePrompt(): string {
-	if (!contextSummarizerPromptsLoaded || cachedContextSummarizePrompt === null) {
-		return '';
+	if (!contextSummarizerPromptsLoaded || !cachedContextSummarizePrompt?.trim()) {
+		throw new Error('Context summarizer prompts not loaded');
 	}
 	return cachedContextSummarizePrompt;
 }

Also applies to: 402-405

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/services/contextSummarizer.ts` around lines 52 - 57, The
getContextSummarizePrompt function should fail fast instead of returning an
empty string: replace the current conditional return with a thrown Error when
contextSummarizerPromptsLoaded is false or cachedContextSummarizePrompt is null,
e.g. throw new Error('Context summarization prompts not initialized'); update
callers that expect a string (the consumer that uses getContextSummarizePrompt)
to let the error propagate so missing system instructions cause an explicit
failure rather than producing a silent empty prompt. Ensure you reference
getContextSummarizePrompt, contextSummarizerPromptsLoaded and
cachedContextSummarizePrompt when making the change.
src/renderer/services/inlineWizardDocumentGeneration.ts (1)

44-56: ⚠️ Potential issue | 🟠 Major

Fail fast when prompts are not initialized; don’t silently return ''.

Returning '' here still makes “unloaded” indistinguishable from an intentionally blank customization. That propagates into prompt selection at Line 406 and can run generation with no base template.

Proposed fix
 function getWizardDocumentGenerationPrompt(): string {
-	if (!inlineWizardDocGenPromptsLoaded || cachedWizardDocumentGenerationPrompt === null) {
-		return '';
-	}
+	if (!inlineWizardDocGenPromptsLoaded || cachedWizardDocumentGenerationPrompt === null) {
+		throw new Error(
+			'Inline wizard prompts not initialized. Call loadInlineWizardDocGenPrompts() before generation.'
+		);
+	}
 	return cachedWizardDocumentGenerationPrompt;
 }
 
 function getWizardInlineIterateGenerationPrompt(): string {
-	if (!inlineWizardDocGenPromptsLoaded || cachedWizardInlineIterateGenerationPrompt === null) {
-		return '';
-	}
+	if (!inlineWizardDocGenPromptsLoaded || cachedWizardInlineIterateGenerationPrompt === null) {
+		throw new Error(
+			'Inline wizard prompts not initialized. Call loadInlineWizardDocGenPrompts() before generation.'
+		);
+	}
 	return cachedWizardInlineIterateGenerationPrompt;
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/services/inlineWizardDocumentGeneration.ts` around lines 44 -
56, The current getters silently return an empty string when prompts aren't
loaded; change getWizardDocumentGenerationPrompt and
getWizardInlineIterateGenerationPrompt to fail fast by throwing a clear Error
(e.g., "Wizard prompts not initialized") when inlineWizardDocGenPromptsLoaded is
false or the cached prompt is null, so callers cannot proceed with an unintended
blank template; update any callers that expect a string to either catch the
Error or ensure initialization runs before calling these getters.
src/renderer/components/Wizard/services/wizardPrompts.ts (1)

40-52: ⚠️ Potential issue | 🟠 Major

Don't collapse "unloaded" into an empty wizard template.

Both getters still return '' before load completes, so generateSystemPrompt() can silently drop the main wizard prompt and continuation block instead of surfacing the initialization problem.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/components/Wizard/services/wizardPrompts.ts` around lines 40 -
52, The getters getWizardSystemPrompt and getWizardSystemContinuationPrompt
currently return an empty string when wizardPromptsLoaded is false, which allows
generateSystemPrompt to silently proceed without the real prompts; change these
functions to surface the uninitialized state instead of returning '' — for
example return null (or throw a clear Error) when !wizardPromptsLoaded or the
cached values are null, e.g. check wizardPromptsLoaded and
cachedWizardSystemPrompt / cachedWizardSystemContinuationPrompt and return null
(or throw) so callers like generateSystemPrompt can detect initialization
failure and handle/log it appropriately.
src/renderer/hooks/agent/useAgentListeners.ts (1)

67-71: ⚠️ Potential issue | 🟠 Major

Keep the unloaded state observable.

Returning '' here still makes “prompt not loaded yet” indistinguishable from an intentional blank customization. The onExit synopsis flow later consumes this as a real base prompt, so a missed init silently generates history with no template instead of failing fast.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/agent/useAgentListeners.ts` around lines 67 - 71,
getAutorunSynopsisPrompt currently hides the "not loaded" state by returning ''
which is indistinguishable from an intentional empty prompt; change its contract
to return null when prompts aren't loaded (use the existing
agentListenersPromptsLoaded and cachedAutorunSynopsisPrompt to return null if
not loaded or cache is null), update the function signature to string | null,
and update all consumers (notably the onExit synopsis flow) to explicitly check
for null and fail fast or log/throw instead of treating it as a real base
prompt; ensure variables referenced (agentListenersPromptsLoaded,
cachedAutorunSynopsisPrompt, getAutorunSynopsisPrompt, and the onExit synopsis
consumer) are updated accordingly.
src/renderer/hooks/wizard/useWizardHandlers.ts (1)

54-59: ⚠️ Potential issue | 🟠 Major

Don't build /history from an ambiguous cache value.

This getter still returns '' when the synopsis prompt has not been loaded. handleHistoryCommand() then composes a real synopsis request from that ambiguous value, which turns a missed init into a silent blank prompt.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/hooks/wizard/useWizardHandlers.ts` around lines 54 - 59,
getAutorunSynopsisPrompt currently returns an ambiguous empty string when the
synopsis hasn't been loaded; change it to return null (or undefined) to signal
"not loaded" (update the function getAutorunSynopsisPrompt to return null
instead of '' when wizardHandlersPromptsLoaded is false or
cachedAutorunSynopsisPrompt is null) and then update callers (notably
handleHistoryCommand) to check for null and abort/trigger init rather than
composing a synopsis from an empty string. Ensure the unique symbol
getAutorunSynopsisPrompt is modified and all usages (handleHistoryCommand)
explicitly guard against a null return before building the /history prompt.
src/renderer/stores/settingsStore.ts (1)

76-79: ⚠️ Potential issue | 🟠 Major

Await the /commit migration save.

This fire-and-forget IPC can still be in flight, or fail, after loadSettingsStorePrompts() resolves. That makes the migration non-deterministic and turns a save failure into silent data loss for the old /commit override. Await it and validate the result before considering prompt init complete.

As per coding guidelines, "Let exceptions bubble up to Sentry for unhandled errors; only explicitly catch and handle expected/recoverable errors".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/stores/settingsStore.ts` around lines 76 - 79, The code
currently fire-and-forgets window.maestro.prompts.save('commit-command',
commitCmd.prompt) which can fail after loadSettingsStorePrompts() resolves;
change it to await the promise from window.maestro.prompts.save inside the
branch where commitCmd.prompt && !force, and only set cachedCommitCommandPrompt
= commitCmd.prompt after the awaited save returns a successful result (validate
the truthy/success value returned). Do not swallow errors—do not add
try/catch—so any exception will bubble up (per guidelines); ensure the change is
applied where commitCmd.prompt, force, window.maestro.prompts.save and
cachedCommitCommandPrompt are referenced and that this update makes prompt
initialization deterministic.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/cli/services/batch-processor.ts`:
- Around line 33-58: getCustomizedPrompt and getCliPrompt currently treat an
empty string as “no customization” because they use truthy checks; update them
so that getCustomizedPrompt returns entry.content whenever entry?.isModified is
true (even if entry.content === ''), and change the caller in getCliPrompt to
check for a non-null/undefined value (e.g., customized !== null && customized
!== undefined) instead of if (customized) so an intentional blank override from
core-prompts-customizations.json is honored by the CLI.

In `@src/main/index.ts`:
- Around line 395-423: Wrap the migration disk write in a try/catch so a failing
savePrompt('group-chat-moderator-system', ...) does not block startup: call
getPrompt and build migratedPrompt as-is, then inside try { await
savePrompt(...); logger.info(...) } catch (err) { captureException(err, { extra:
{ migratedKey, standingInstructionsSlice: standingInstructions.slice(0,200) }
}); logger.warn('Failed to persist migrated moderator standing instructions,
continuing startup', 'Startup'); } finally ensure store.set(migratedKey, true)
still runs so we don't retry repeatedly; reference functions/idents: savePrompt,
getPrompt, store.set, migratedKey, standingInstructions, logger, and
captureException from your Sentry utils.

In `@src/main/prompt-manager.ts`:
- Around line 182-195: Concurrent read-modify-write in savePrompt and
resetPrompt can clobber changes to core-prompts-customizations.json; serialize
these mutations by adding a module-level mutex/queue and acquiring it around the
entire loadUserCustomizations -> modify -> saveUserCustomizations sequence.
Specifically, wrap the body of savePrompt (function savePrompt) and the
corresponding resetPrompt function (and any other functions that call
loadUserCustomizations/saveUserCustomizations) with lock acquisition and release
(or push operations to a single async FIFO) so only one IPC handler performs the
read-modify-write at a time, ensuring each modification is persisted without
races.

---

Duplicate comments:
In `@src/renderer/components/Wizard/services/wizardPrompts.ts`:
- Around line 40-52: The getters getWizardSystemPrompt and
getWizardSystemContinuationPrompt currently return an empty string when
wizardPromptsLoaded is false, which allows generateSystemPrompt to silently
proceed without the real prompts; change these functions to surface the
uninitialized state instead of returning '' — for example return null (or throw
a clear Error) when !wizardPromptsLoaded or the cached values are null, e.g.
check wizardPromptsLoaded and cachedWizardSystemPrompt /
cachedWizardSystemContinuationPrompt and return null (or throw) so callers like
generateSystemPrompt can detect initialization failure and handle/log it
appropriately.

In `@src/renderer/hooks/agent/useAgentListeners.ts`:
- Around line 67-71: getAutorunSynopsisPrompt currently hides the "not loaded"
state by returning '' which is indistinguishable from an intentional empty
prompt; change its contract to return null when prompts aren't loaded (use the
existing agentListenersPromptsLoaded and cachedAutorunSynopsisPrompt to return
null if not loaded or cache is null), update the function signature to string |
null, and update all consumers (notably the onExit synopsis flow) to explicitly
check for null and fail fast or log/throw instead of treating it as a real base
prompt; ensure variables referenced (agentListenersPromptsLoaded,
cachedAutorunSynopsisPrompt, getAutorunSynopsisPrompt, and the onExit synopsis
consumer) are updated accordingly.

In `@src/renderer/hooks/wizard/useWizardHandlers.ts`:
- Around line 54-59: getAutorunSynopsisPrompt currently returns an ambiguous
empty string when the synopsis hasn't been loaded; change it to return null (or
undefined) to signal "not loaded" (update the function getAutorunSynopsisPrompt
to return null instead of '' when wizardHandlersPromptsLoaded is false or
cachedAutorunSynopsisPrompt is null) and then update callers (notably
handleHistoryCommand) to check for null and abort/trigger init rather than
composing a synopsis from an empty string. Ensure the unique symbol
getAutorunSynopsisPrompt is modified and all usages (handleHistoryCommand)
explicitly guard against a null return before building the /history prompt.

In `@src/renderer/services/contextSummarizer.ts`:
- Around line 52-57: The getContextSummarizePrompt function should fail fast
instead of returning an empty string: replace the current conditional return
with a thrown Error when contextSummarizerPromptsLoaded is false or
cachedContextSummarizePrompt is null, e.g. throw new Error('Context
summarization prompts not initialized'); update callers that expect a string
(the consumer that uses getContextSummarizePrompt) to let the error propagate so
missing system instructions cause an explicit failure rather than producing a
silent empty prompt. Ensure you reference getContextSummarizePrompt,
contextSummarizerPromptsLoaded and cachedContextSummarizePrompt when making the
change.

In `@src/renderer/services/inlineWizardDocumentGeneration.ts`:
- Around line 44-56: The current getters silently return an empty string when
prompts aren't loaded; change getWizardDocumentGenerationPrompt and
getWizardInlineIterateGenerationPrompt to fail fast by throwing a clear Error
(e.g., "Wizard prompts not initialized") when inlineWizardDocGenPromptsLoaded is
false or the cached prompt is null, so callers cannot proceed with an unintended
blank template; update any callers that expect a string to either catch the
Error or ensure initialization runs before calling these getters.

In `@src/renderer/stores/settingsStore.ts`:
- Around line 76-79: The code currently fire-and-forgets
window.maestro.prompts.save('commit-command', commitCmd.prompt) which can fail
after loadSettingsStorePrompts() resolves; change it to await the promise from
window.maestro.prompts.save inside the branch where commitCmd.prompt && !force,
and only set cachedCommitCommandPrompt = commitCmd.prompt after the awaited save
returns a successful result (validate the truthy/success value returned). Do not
swallow errors—do not add try/catch—so any exception will bubble up (per
guidelines); ensure the change is applied where commitCmd.prompt, force,
window.maestro.prompts.save and cachedCommitCommandPrompt are referenced and
that this update makes prompt initialization deterministic.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 467073e6-f14d-4864-aedb-6380d6d65abf

📥 Commits

Reviewing files that changed from the base of the PR and between d29ac17 and 73837c3.

📒 Files selected for processing (24)
  • docs/cli.md
  • docs/configuration.md
  • src/__tests__/main/group-chat/group-chat-agent.test.ts
  • src/__tests__/main/group-chat/group-chat-router.test.ts
  • src/__tests__/renderer/stores/agentStore.test.ts
  • src/cli/services/batch-processor.ts
  • src/main/group-chat/group-chat-router.ts
  • src/main/index.ts
  • src/main/ipc/handlers/prompts.ts
  • src/main/prompt-manager.ts
  • src/renderer/components/Settings/SettingsModal.tsx
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.css
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.tsx
  • src/renderer/components/Wizard/services/phaseGenerator.ts
  • src/renderer/components/Wizard/services/wizardPrompts.ts
  • src/renderer/hooks/agent/useAgentListeners.ts
  • src/renderer/hooks/agent/useMergeTransferHandlers.ts
  • src/renderer/hooks/wizard/useWizardHandlers.ts
  • src/renderer/services/contextGroomer.ts
  • src/renderer/services/contextSummarizer.ts
  • src/renderer/services/inlineWizardConversation.ts
  • src/renderer/services/inlineWizardDocumentGeneration.ts
  • src/renderer/stores/settingsStore.ts
  • src/shared/promptDefinitions.ts
✅ Files skipped from review due to trivial changes (4)
  • src/tests/main/group-chat/group-chat-agent.test.ts
  • docs/configuration.md
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.css
  • src/shared/promptDefinitions.ts
🚧 Files skipped from review as they are similar to previous changes (7)
  • src/tests/main/group-chat/group-chat-router.test.ts
  • src/renderer/components/Wizard/services/phaseGenerator.ts
  • src/renderer/services/contextGroomer.ts
  • src/main/ipc/handlers/prompts.ts
  • src/renderer/components/Settings/tabs/MaestroPromptsTab.tsx
  • src/main/group-chat/group-chat-router.ts
  • docs/cli.md

noreply and others added 2 commits April 12, 2026 06:17
…y override

- Serialize prompt-manager disk writes with a write lock to prevent
  concurrent read-modify-write races on customizations JSON
- Wrap standing instructions migration in try/catch with Sentry
  reporting so a failing savePrompt does not block startup
- Honor intentionally blank prompt customizations in CLI (typeof check
  instead of truthy, null check instead of truthy on caller)
- Await prompts.save in settingsStore commit migration and only update
  cache on success

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
External CI pipelines may still reference build:prompts. Keep it as a
no-op echo so those pipelines don't fail after the generate-prompts
build step was removed.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

♻️ Duplicate comments (1)
src/renderer/stores/settingsStore.ts (1)

70-88: ⚠️ Potential issue | 🟠 Major

This still drops a legitimately blank legacy /commit override.

The migration decision is based on the live Zustand value, but this file also documents that the bootstrap /commit prompt is '' before settings load. That means an intentionally blank saved override is indistinguishable from the bootstrap placeholder here, so Lines 76-88 overwrite it with the bundled prompt instead of preserving it. This migration needs to read the persisted customAICommands source directly, or wait until settingsLoaded is true before deciding whether blank means “custom” or “not loaded yet.”

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/renderer/stores/settingsStore.ts` around lines 70 - 88, The migration
currently runs against the in-memory Zustand placeholder and can overwrite an
intentionally blank legacy commit prompt; change the logic in the block
referencing currentCommands, commitCmd and cachedCommitCommandPrompt so it only
performs migration after settings have been loaded (check
useSettingsStore.getState().settingsLoaded) or if the live commitCmd.prompt is
non-empty, thereby treating an empty string as “not ready” until settingsLoaded
is true; if you prefer reading persisted state directly, fetch the persisted
customAICommands from the persistence API before deciding to call
window.maestro.prompts.save and updating useSettingsStore.setState so blank
saved overrides are preserved.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/main/index.ts`:
- Around line 399-428: The code currently calls store.set(migratedKey, true)
unconditionally, marking moderatorStandingInstructionsMigrated even on failures;
change this so store.set(migratedKey, true) is only executed after a successful
migration path (i.e., after savePrompt completes or after detecting that no
change is needed because currentPrompt already contains standingInstructions).
Move the store.set(...) inside the try block just after await savePrompt(...) or
right after the branch that skips migration, and do not set it in the catch
handler; reference getPrompt, savePrompt, standingInstructions, migratedKey, and
store.set to locate and update the logic.

In `@src/main/prompt-manager.ts`:
- Around line 80-100: loadUserCustomizations currently throws on malformed JSON
which blocks startup; change it so that when read/parse fails for reasons other
than ENOENT (e.g., JSON.parse error, permission issues) you quarantine the bad
file (rename getCustomizationsPath() to a .broken.TIMESTAMP or similar using
fs.rename) and log the error with logger.error and LOG_CONTEXT, then return null
so initializePrompts can fall back to defaults; for saveUserCustomizations make
writes atomic by writing JSON to a temporary path (e.g., getCustomizationsPath()
+ '.tmp.' + timestamp or PID) and then fs.rename that temp file to the real path
(ensuring the rename replaces the target atomically), and surface real write
failures but avoid in-place truncation that can leave a corrupt file.

In `@src/renderer/stores/settingsStore.ts`:
- Around line 55-81: The code sets settingsStorePromptsLoaded and assigns
DEFAULT_AI_COMMANDS (using cachedCommitCommandPrompt) before performing the
legacy migration that can change cachedCommitCommandPrompt; move the
creation/assignment of DEFAULT_AI_COMMANDS and the settingsStorePromptsLoaded
flag to after the migration completes and succeeds so the exported default
reflects the final prompt, and when calling
window.maestro.prompts.save('commit-command', ...) propagate unexpected errors
(don’t swallow them) while only handling expected/recoverable failures
explicitly; update references to useSettingsStore.getState().customAICommands
and the commitCmd migration logic to run before finalizing DEFAULT_AI_COMMANDS
and flipping settingsStorePromptsLoaded.

---

Duplicate comments:
In `@src/renderer/stores/settingsStore.ts`:
- Around line 70-88: The migration currently runs against the in-memory Zustand
placeholder and can overwrite an intentionally blank legacy commit prompt;
change the logic in the block referencing currentCommands, commitCmd and
cachedCommitCommandPrompt so it only performs migration after settings have been
loaded (check useSettingsStore.getState().settingsLoaded) or if the live
commitCmd.prompt is non-empty, thereby treating an empty string as “not ready”
until settingsLoaded is true; if you prefer reading persisted state directly,
fetch the persisted customAICommands from the persistence API before deciding to
call window.maestro.prompts.save and updating useSettingsStore.setState so blank
saved overrides are preserved.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 5647fcef-cb29-4f30-b0a5-2e621fce98c3

📥 Commits

Reviewing files that changed from the base of the PR and between 73837c3 and 7189b06.

📒 Files selected for processing (5)
  • package.json
  • src/cli/services/batch-processor.ts
  • src/main/index.ts
  • src/main/prompt-manager.ts
  • src/renderer/stores/settingsStore.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/cli/services/batch-processor.ts

- Move store.set(migratedKey, true) inside try block so standing
  instructions migration is retried on next launch if savePrompt fails
- Move DEFAULT_AI_COMMANDS assignment and settingsStorePromptsLoaded
  flag after legacy migration completes so the exported default
  reflects the final prompt value post-migration

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants