Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
2c37a1e
feat: core-prompts phase01: prompt-manager infrastructure
noreply Apr 11, 2026
7c03c2f
feat: core-prompts phase02: IPC handlers and preload bridge
noreply Apr 11, 2026
5861b41
feat: core-prompts phase03: initialize prompts at startup
noreply Apr 11, 2026
241dd9f
feat: core-prompts phase04: migrate main process imports
noreply Apr 11, 2026
fd7f323
feat: core-prompts phase05: migrate renderer stores and hooks
noreply Apr 11, 2026
27be651
feat: core-prompts phase06: migrate renderer services
noreply Apr 11, 2026
1c15997
feat: core-prompts phase07: centralized renderer initialization
noreply Apr 11, 2026
9b08035
feat: core-prompts phase08: remove legacy build system
noreply Apr 11, 2026
185d352
feat: core-prompts phase09: Maestro Prompts settings tab
noreply Apr 11, 2026
a8f4a11
feat: core-prompts phase10: update tests
noreply Apr 11, 2026
8cfb3cb
feat: core-prompts phase11: update documentation
noreply Apr 11, 2026
6d787b3
feat: core-prompts phase13: remove Group Chat tab and migrate standin…
noreply Apr 11, 2026
17e2fd2
fix: core-prompts: fix 5 review issues with module-load defaults and …
noreply Apr 11, 2026
a897ef8
fix: use {{AUTORUN_FOLDER}} in speckit/openspec implement prompts
noreply Apr 11, 2026
cc791bd
fix: register prompts IPC handlers in main process startup
noreply Apr 11, 2026
ff41456
feat: Minor UX and added command palette for 4 of the prompts, adding…
noreply Apr 11, 2026
388ec2a
fix: core-prompts: refresh /commit prompt on save and load CLI custom…
noreply Apr 11, 2026
9acaa37
docs: add Maestro Prompts documentation to configuration and CLI pages
noreply Apr 11, 2026
d29ac17
fix: report prompt-init failures to Sentry instead of console
noreply Apr 11, 2026
73837c3
fix: address code review findings across core-prompts branch
noreply Apr 12, 2026
18c3c47
fix: address code review findings - mutex, migration safety, CLI empt…
noreply Apr 12, 2026
7189b06
fix: add no-op build:prompts script for CI compatibility
noreply Apr 12, 2026
6a8a1ff
style: run prettier on 5 files with formatting issues
noreply Apr 12, 2026
9abd4c4
fix: address code review findings - migration key and prompt ordering
noreply Apr 12, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,6 @@ node_modules/
# Build outputs
dist/
release/
src/generated/
*.log
tmp/
scratch/
Expand Down
4 changes: 3 additions & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -184,7 +184,9 @@ src/
| Add tab overlay menu | See Tab Hover Overlay Menu pattern in [[CLAUDE-PATTERNS.md]] |
| Add setting | `src/shared/settingsMetadata.ts` (metadata), `src/renderer/stores/settingsStore.ts`, `src/main/stores/defaults.ts` |
| Add template variable | `src/shared/templateVariables.ts`, `src/renderer/utils/templateVariables.ts` |
| Modify system prompts | `src/prompts/*.md` (wizard, Auto Run, etc.) |
| Modify system prompts | `src/prompts/*.md` (wizard, Auto Run, etc.) or edit via **Maestro Prompts** tab in Settings |
| Customize prompts | Use **Maestro Prompts** tab in Settings, or edit `userData/core-prompts-customizations.json` |
| Add new prompt | `src/prompts/*.md`, `src/shared/promptDefinitions.ts` (add to `CORE_PROMPTS` array and `PROMPT_IDS`) |
| Add Spec-Kit command | `src/prompts/speckit/`, `src/main/speckit-manager.ts` |
| Add OpenSpec command | `src/prompts/openspec/`, `src/main/openspec-manager.ts` |
| Add CLI command | `src/cli/commands/`, `src/cli/index.ts` |
Expand Down
41 changes: 41 additions & 0 deletions docs/cli.md
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,47 @@ maestro-cli clean playbooks
maestro-cli clean playbooks --dry-run
```

### Prompt Customization

The CLI uses the same core system prompts as the desktop app. When you customize prompts via Settings → **Maestro Prompts**, those customizations are stored in `core-prompts-customizations.json` in the Maestro data directory and are automatically picked up by the CLI during playbook runs.

The prompts most relevant to CLI playbook execution are:

| Prompt ID | Controls |
| ----------------------- | --------------------------------------------- |
| `autorun-default` | Default Auto Run task execution behavior |
| `autorun-synopsis` | Synopsis generation after task completion |
| `commit-command` | `/commit` command behavior |
| `maestro-system-prompt` | Maestro system context injected into sessions |
| `context-grooming` | Context grooming during transfers |

To customize these prompts, either use the desktop app's **Maestro Prompts** tab or edit the JSON file directly:

```text
# macOS
~/Library/Application Support/Maestro/core-prompts-customizations.json

# Linux
~/.config/Maestro/core-prompts-customizations.json

# Windows
%APPDATA%\Maestro\core-prompts-customizations.json
```

The file format is:

```json
{
"prompts": {
"autorun-default": {
"content": "Your customized prompt content...",
"isModified": true,
"modifiedAt": "2026-04-11T..."
}
}
}
```

### Managing Settings

View and modify any Maestro configuration setting directly from the CLI. Changes take effect immediately in the running desktop app — no restart required.
Expand Down
18 changes: 18 additions & 0 deletions docs/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,28 @@ Settings are organized into tabs:
| **Themes** | Dark, light, and vibe mode themes, custom theme builder with import/export |
| **Notifications** | OS notifications, custom command notifications, toast notification duration |
| **AI Commands** | View and edit slash commands, [Spec-Kit](./speckit-commands), [OpenSpec](./openspec-commands), and [BMAD](./bmad-commands) prompts |
| **Maestro Prompts** | Browse and edit the 23 core system prompts (wizard, Auto Run, group chat, context, etc.). Changes take effect immediately; reset to bundled defaults at any time |
| **SSH Hosts** | Configure remote hosts for [SSH agent execution](./ssh-remote-execution) |
| **Environment** | Global environment variables that cascade to all agents and terminal sessions |
| **WakaTime** _(in General tab)_ | WakaTime integration toggle, API key, detailed file tracking |

## Maestro Prompts

Maestro ships with 23 core system prompts that control wizard conversations, Auto Run behavior, group chat moderation, context management, and more. You can customize any of them via the **Maestro Prompts** tab in Settings.

**To edit a prompt:**

1. Open **Settings** (`Cmd+,` / `Ctrl+,`) → **Maestro Prompts** tab
2. Select a prompt from the category list on the left
3. Edit the content in the editor
4. Click **Save** — changes take effect immediately (no restart needed)

**To reset a prompt:**

Click **Reset to Default** to restore the bundled version. This also takes effect immediately.

Customizations are stored separately from bundled prompts and survive app updates. You can also access the four most common prompts directly from **Quick Actions** (`Cmd+K` / `Ctrl+K`): Maestro System Prompt, Auto Run Default, Commit Command, and Group Chat Moderator.

## Conductor Profile

The **Conductor Profile** (Settings → General → **About Me**) is a short description of yourself that gets injected into every AI agent's system prompt. This helps agents understand your background, preferences, and communication style so they can tailor responses accordingly.
Expand Down
31 changes: 26 additions & 5 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,13 @@
"dev": "node scripts/dev.mjs",
"dev:prod-data": "USE_PROD_DATA=1 node scripts/dev.mjs",
"dev:demo": "MAESTRO_DEMO_DIR=/tmp/maestro-demo npm run dev",
"dev:main": "npm run build:prompts && tsc -p tsconfig.main.json && npm run build:preload && NODE_ENV=development electron .",
"dev:main:prod-data": "npm run build:prompts && tsc -p tsconfig.main.json && npm run build:preload && NODE_ENV=development USE_PROD_DATA=1 electron .",
"dev:main": "tsc -p tsconfig.main.json && npm run build:preload && NODE_ENV=development electron .",
"dev:main:prod-data": "tsc -p tsconfig.main.json && npm run build:preload && NODE_ENV=development USE_PROD_DATA=1 electron .",
"dev:renderer": "vite",
"dev:web": "vite --config vite.config.web.mts",
"dev:win": "powershell -NoProfile -ExecutionPolicy Bypass -File ./scripts/start-dev.ps1",
"build": "npm run build:prompts && npm run build:main && npm run build:preload && npm run build:renderer && npm run build:web && npm run build:cli",
"build:prompts": "node scripts/generate-prompts.mjs",
"build": "npm run build:main && npm run build:preload && npm run build:renderer && npm run build:web && npm run build:cli",
"build:prompts": "echo 'No-op: prompts are now loaded from disk at runtime (see prompt-manager.ts)'",
"build:main": "tsc -p tsconfig.main.json",
"build:preload": "node scripts/build-preload.mjs",
"build:cli": "node scripts/build-cli.mjs",
Expand All @@ -45,7 +45,7 @@
"format:all": "prettier --write .",
"format:check": "prettier --check \"src/**/*.{ts,tsx}\"",
"format:check:all": "prettier --check .",
"validate:push": "npm run build:prompts && npm run format:check:all && npm run lint && npm run lint:eslint && npm run test",
"validate:push": "npm run format:check:all && npm run lint && npm run lint:eslint && npm run test",
"test": "NODE_OPTIONS=--max-old-space-size=8192 vitest run",
"test:watch": "NODE_OPTIONS=--max-old-space-size=8192 vitest",
"test:coverage": "NODE_OPTIONS=--max-old-space-size=8192 vitest run --coverage",
Expand Down Expand Up @@ -106,6 +106,13 @@
"from": "dist/cli/maestro-cli.js",
"to": "maestro-cli.js"
},
{
"from": "src/prompts",
"to": "prompts/core",
"filter": [
"*.md"
]
},
{
"from": "src/prompts/speckit",
"to": "prompts/speckit"
Expand Down Expand Up @@ -142,6 +149,13 @@
"from": "dist/cli/maestro-cli.js",
"to": "maestro-cli.js"
},
{
"from": "src/prompts",
"to": "prompts/core",
"filter": [
"*.md"
]
},
{
"from": "src/prompts/speckit",
"to": "prompts/speckit"
Expand Down Expand Up @@ -170,6 +184,13 @@
"from": "dist/cli/maestro-cli.js",
"to": "maestro-cli.js"
},
{
"from": "src/prompts",
"to": "prompts/core",
"filter": [
"*.md"
]
},
{
"from": "src/prompts/speckit",
"to": "prompts/speckit"
Expand Down
98 changes: 0 additions & 98 deletions scripts/generate-prompts.mjs

This file was deleted.

17 changes: 17 additions & 0 deletions src/__tests__/main/group-chat/group-chat-agent.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,23 @@ vi.mock('electron-store', () => {
};
});

vi.mock('../../../main/prompt-manager', () => ({
getPrompt: vi.fn((id: string) => {
const fs = require('fs');
const path = require('path');
const promptsDir = path.resolve(__dirname, '..', '..', '..', '..', 'src', 'prompts');
const filenameMap: Record<string, string> = {
'group-chat-participant': 'group-chat-participant.md',
'group-chat-participant-request': 'group-chat-participant-request.md',
'group-chat-moderator-system': 'group-chat-moderator-system.md',
'group-chat-moderator-synthesis': 'group-chat-moderator-synthesis.md',
};
const filename = filenameMap[id];
if (!filename) throw new Error(`Unknown prompt ID in test mock: ${id}`);
return fs.readFileSync(path.join(promptsDir, filename), 'utf-8');
}),
}));

import {
addParticipant,
sendToParticipant,
Expand Down
12 changes: 12 additions & 0 deletions src/__tests__/main/group-chat/group-chat-moderator.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,18 @@ vi.mock('electron-store', () => {
};
});

vi.mock('../../../main/prompt-manager', () => ({
getPrompt: vi.fn((id: string) => {
const prompts: Record<string, string> = {
'group-chat-moderator-system':
'You are a Group Chat Moderator.\n\n{{CONDUCTOR_PROFILE}}\n\nCoordinate multiple AI agents using @mentions.',
'group-chat-moderator-synthesis':
'Review the agents responses and synthesize a coherent answer.',
};
return prompts[id] ?? `mock prompt for ${id}`;
}),
}));

import {
spawnModerator,
sendToModerator,
Expand Down
17 changes: 17 additions & 0 deletions src/__tests__/main/group-chat/group-chat-router.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,23 @@ vi.mock('../../../main/utils/ssh-spawn-wrapper', () => ({
wrapSpawnWithSsh: (...args: unknown[]) => mockWrapSpawnWithSsh(...args),
}));

vi.mock('../../../main/prompt-manager', () => ({
getPrompt: vi.fn((id: string) => {
const fs = require('fs');
const path = require('path');
const promptsDir = path.resolve(__dirname, '..', '..', '..', '..', 'src', 'prompts');
const filenameMap: Record<string, string> = {
'group-chat-participant': 'group-chat-participant.md',
'group-chat-participant-request': 'group-chat-participant-request.md',
'group-chat-moderator-system': 'group-chat-moderator-system.md',
'group-chat-moderator-synthesis': 'group-chat-moderator-synthesis.md',
};
const filename = filenameMap[id];
if (!filename) throw new Error(`Unknown prompt ID in test mock: ${id}`);
return fs.readFileSync(path.join(promptsDir, filename), 'utf-8');
}),
}));

import {
extractMentions,
extractAllMentions,
Expand Down
9 changes: 6 additions & 3 deletions src/__tests__/main/ipc/handlers/director-notes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,12 @@ vi.mock('../../../../main/utils/context-groomer', () => ({
groomContext: vi.fn(),
}));

// Mock the prompts module
vi.mock('../../../../../prompts', () => ({
directorNotesPrompt: 'Mock director notes prompt',
// Mock prompt-manager so getPrompt() returns mock content without needing disk I/O
vi.mock('../../../../main/prompt-manager', () => ({
getPrompt: vi.fn((id: string) => {
if (id === 'director-notes') return 'Mock director notes prompt';
return `mock prompt for ${id}`;
}),
}));

describe('director-notes IPC handlers', () => {
Expand Down
9 changes: 6 additions & 3 deletions src/__tests__/main/ipc/handlers/tabNaming.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,12 @@ vi.mock('uuid', () => ({
v4: vi.fn(() => 'mock-uuid-1234'),
}));

// Mock the prompts
vi.mock('../../../../prompts', () => ({
tabNamingPrompt: 'You are a tab naming assistant. Generate a concise tab name.',
// Mock prompt-manager so getPrompt() returns mock content without needing disk I/O
vi.mock('../../../../main/prompt-manager', () => ({
getPrompt: vi.fn((id: string) => {
if (id === 'tab-naming') return 'You are a tab naming assistant. Generate a concise tab name.';
return `mock prompt for ${id}`;
}),
}));

// Mock the agent args utilities
Expand Down
16 changes: 16 additions & 0 deletions src/__tests__/renderer/components/BatchRunnerModal.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,22 @@
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
import { render, screen, fireEvent, waitFor, within, act } from '@testing-library/react';
import React from 'react';

// Mock batchUtils to provide loaded DEFAULT_BATCH_PROMPT and real validation functions
vi.mock('../../../renderer/hooks/batch/batchUtils', async () => {
const actual = await vi.importActual('../../../renderer/hooks/batch/batchUtils');
const fs = await import('fs');
const path = await import('path');
const content = fs.readFileSync(
path.resolve(__dirname, '..', '..', '..', '..', 'src', 'prompts', 'autorun-default.md'),
'utf-8'
);
return {
...actual,
DEFAULT_BATCH_PROMPT: content,
};
});

import {
BatchRunnerModal,
DEFAULT_BATCH_PROMPT,
Expand Down
Loading