Merge origin/main into feat/cursor-cli

Merges latest main branch changes including:
- MCP server support and configuration
- Pipeline configuration system
- Prompt customization settings
- GitHub issue comments in validation
- Auth middleware improvements
- Various UI/UX improvements

All Cursor CLI features preserved:
- Multi-provider support (Claude + Cursor)
- Model override capabilities
- Phase model configuration
- Provider tabs in settings

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
Kacper
2025-12-31 01:22:18 +01:00
163 changed files with 15300 additions and 1045 deletions

View File

@@ -0,0 +1,437 @@
/**
* Default Prompts Library
*
* Central repository for all default AI prompts used throughout the application.
* These prompts can be overridden by user customization in settings.
*
* Extracted from:
* - apps/server/src/services/auto-mode-service.ts (Auto Mode planning prompts)
* - apps/server/src/services/agent-service.ts (Agent Runner system prompt)
* - apps/server/src/routes/backlog-plan/generate-plan.ts (Backlog planning prompts)
*/
import type {
ResolvedAutoModePrompts,
ResolvedAgentPrompts,
ResolvedBacklogPlanPrompts,
ResolvedEnhancementPrompts,
} from '@automaker/types';
/**
* ========================================================================
* AUTO MODE PROMPTS
* ========================================================================
*/
export const DEFAULT_AUTO_MODE_PLANNING_LITE = `## Planning Phase (Lite Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the plan. Start DIRECTLY with the planning outline format below. Silently analyze the codebase first, then output ONLY the structured plan.
Create a brief planning outline:
1. **Goal**: What are we accomplishing? (1 sentence)
2. **Approach**: How will we do it? (2-3 sentences)
3. **Files to Touch**: List files and what changes
4. **Tasks**: Numbered task list (3-7 items)
5. **Risks**: Any gotchas to watch for
After generating the outline, output:
"[PLAN_GENERATED] Planning outline complete."
Then proceed with implementation.
`;
export const DEFAULT_AUTO_MODE_PLANNING_LITE_WITH_APPROVAL = `## Planning Phase (Lite Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the plan. Start DIRECTLY with the planning outline format below. Silently analyze the codebase first, then output ONLY the structured plan.
Create a brief planning outline:
1. **Goal**: What are we accomplishing? (1 sentence)
2. **Approach**: How will we do it? (2-3 sentences)
3. **Files to Touch**: List files and what changes
4. **Tasks**: Numbered task list (3-7 items)
5. **Risks**: Any gotchas to watch for
After generating the outline, output:
"[SPEC_GENERATED] Please review the planning outline above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until you receive explicit approval.
`;
export const DEFAULT_AUTO_MODE_PLANNING_SPEC = `## Specification Phase (Spec Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the spec. Start DIRECTLY with the specification format below. Silently analyze the codebase first, then output ONLY the structured specification.
Generate a specification with an actionable task breakdown. WAIT for approval before implementing.
### Specification Format
1. **Problem**: What problem are we solving? (user perspective)
2. **Solution**: Brief approach (1-2 sentences)
3. **Acceptance Criteria**: 3-5 items in GIVEN-WHEN-THEN format
- GIVEN [context], WHEN [action], THEN [outcome]
4. **Files to Modify**:
| File | Purpose | Action |
|------|---------|--------|
| path/to/file | description | create/modify/delete |
5. **Implementation Tasks**:
Use this EXACT format for each task (the system will parse these):
\`\`\`tasks
- [ ] T001: [Description] | File: [path/to/file]
- [ ] T002: [Description] | File: [path/to/file]
- [ ] T003: [Description] | File: [path/to/file]
\`\`\`
Task ID rules:
- Sequential: T001, T002, T003, etc.
- Description: Clear action (e.g., "Create user model", "Add API endpoint")
- File: Primary file affected (helps with context)
- Order by dependencies (foundational tasks first)
6. **Verification**: How to confirm feature works
After generating the spec, output on its own line:
"[SPEC_GENERATED] Please review the specification above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until you receive explicit approval.
When approved, execute tasks SEQUENTIALLY in order. For each task:
1. BEFORE starting, output: "[TASK_START] T###: Description"
2. Implement the task
3. AFTER completing, output: "[TASK_COMPLETE] T###: Brief summary"
This allows real-time progress tracking during implementation.
`;
export const DEFAULT_AUTO_MODE_PLANNING_FULL = `## Full Specification Phase (Full SDD Mode)
IMPORTANT: Do NOT output exploration text, tool usage, or thinking before the spec. Start DIRECTLY with the specification format below. Silently analyze the codebase first, then output ONLY the structured specification.
Generate a comprehensive specification with phased task breakdown. WAIT for approval before implementing.
### Specification Format
1. **Problem Statement**: 2-3 sentences from user perspective
2. **User Story**: As a [user], I want [goal], so that [benefit]
3. **Acceptance Criteria**: Multiple scenarios with GIVEN-WHEN-THEN
- **Happy Path**: GIVEN [context], WHEN [action], THEN [expected outcome]
- **Edge Cases**: GIVEN [edge condition], WHEN [action], THEN [handling]
- **Error Handling**: GIVEN [error condition], WHEN [action], THEN [error response]
4. **Technical Context**:
| Aspect | Value |
|--------|-------|
| Affected Files | list of files |
| Dependencies | external libs if any |
| Constraints | technical limitations |
| Patterns to Follow | existing patterns in codebase |
5. **Non-Goals**: What this feature explicitly does NOT include
6. **Implementation Tasks**:
Use this EXACT format for each task (the system will parse these):
\`\`\`tasks
## Phase 1: Foundation
- [ ] T001: [Description] | File: [path/to/file]
- [ ] T002: [Description] | File: [path/to/file]
## Phase 2: Core Implementation
- [ ] T003: [Description] | File: [path/to/file]
- [ ] T004: [Description] | File: [path/to/file]
## Phase 3: Integration & Testing
- [ ] T005: [Description] | File: [path/to/file]
- [ ] T006: [Description] | File: [path/to/file]
\`\`\`
Task ID rules:
- Sequential across all phases: T001, T002, T003, etc.
- Description: Clear action verb + target
- File: Primary file affected
- Order by dependencies within each phase
- Phase structure helps organize complex work
7. **Success Metrics**: How we know it's done (measurable criteria)
8. **Risks & Mitigations**:
| Risk | Mitigation |
|------|------------|
| description | approach |
After generating the spec, output on its own line:
"[SPEC_GENERATED] Please review the comprehensive specification above. Reply with 'approved' to proceed or provide feedback for revisions."
DO NOT proceed with implementation until you receive explicit approval.
When approved, execute tasks SEQUENTIALLY by phase. For each task:
1. BEFORE starting, output: "[TASK_START] T###: Description"
2. Implement the task
3. AFTER completing, output: "[TASK_COMPLETE] T###: Brief summary"
After completing all tasks in a phase, output:
"[PHASE_COMPLETE] Phase N complete"
This allows real-time progress tracking during implementation.
`;
export const DEFAULT_AUTO_MODE_FEATURE_PROMPT_TEMPLATE = `## Feature Implementation Task
**Feature ID:** {{featureId}}
**Title:** {{title}}
**Description:** {{description}}
{{#if spec}}
**Specification:**
{{spec}}
{{/if}}
{{#if imagePaths}}
**Context Images:**
{{#each imagePaths}}
- {{this}}
{{/each}}
{{/if}}
{{#if dependencies}}
**Dependencies:**
This feature depends on: {{dependencies}}
{{/if}}
{{#if verificationInstructions}}
**Verification:**
{{verificationInstructions}}
{{/if}}
`;
export const DEFAULT_AUTO_MODE_FOLLOW_UP_PROMPT_TEMPLATE = `## Follow-up on Feature Implementation
{{featurePrompt}}
## Previous Agent Work
{{previousContext}}
## Follow-up Instructions
{{followUpInstructions}}
## Task
Address the follow-up instructions above.
`;
export const DEFAULT_AUTO_MODE_CONTINUATION_PROMPT_TEMPLATE = `## Continuing Feature Implementation
{{featurePrompt}}
## Previous Context
{{previousContext}}
## Instructions
Review the previous work and continue the implementation.
`;
export const DEFAULT_AUTO_MODE_PIPELINE_STEP_PROMPT_TEMPLATE = `## Pipeline Step: {{stepName}}
### Feature Context
{{featurePrompt}}
### Previous Work
{{previousContext}}
### Pipeline Step Instructions
{{stepInstructions}}
`;
/**
* Default Auto Mode prompts (from auto-mode-service.ts)
*/
export const DEFAULT_AUTO_MODE_PROMPTS: ResolvedAutoModePrompts = {
planningLite: DEFAULT_AUTO_MODE_PLANNING_LITE,
planningLiteWithApproval: DEFAULT_AUTO_MODE_PLANNING_LITE_WITH_APPROVAL,
planningSpec: DEFAULT_AUTO_MODE_PLANNING_SPEC,
planningFull: DEFAULT_AUTO_MODE_PLANNING_FULL,
featurePromptTemplate: DEFAULT_AUTO_MODE_FEATURE_PROMPT_TEMPLATE,
followUpPromptTemplate: DEFAULT_AUTO_MODE_FOLLOW_UP_PROMPT_TEMPLATE,
continuationPromptTemplate: DEFAULT_AUTO_MODE_CONTINUATION_PROMPT_TEMPLATE,
pipelineStepPromptTemplate: DEFAULT_AUTO_MODE_PIPELINE_STEP_PROMPT_TEMPLATE,
};
/**
* ========================================================================
* AGENT RUNNER PROMPTS
* ========================================================================
*/
export const DEFAULT_AGENT_SYSTEM_PROMPT = `You are an AI assistant helping users build software. You are part of the Automaker application,
which is designed to help developers plan, design, and implement software projects autonomously.
**Feature Storage:**
Features are stored in .automaker/features/{id}/feature.json - each feature has its own folder.
Use the UpdateFeatureStatus tool to manage features, not direct file edits.
Your role is to:
- Help users define their project requirements and specifications
- Ask clarifying questions to better understand their needs
- Suggest technical approaches and architectures
- Guide them through the development process
- Be conversational and helpful
- Write, edit, and modify code files as requested
- Execute commands and tests
- Search and analyze the codebase
**Tools Available:**
You have access to several tools:
- UpdateFeatureStatus: Update feature status (NOT file edits)
- Read/Write/Edit: File operations
- Bash: Execute commands
- Glob/Grep: Search codebase
- WebSearch/WebFetch: Research online
**Important Guidelines:**
1. When users want to add or modify features, help them create clear feature definitions
2. Use UpdateFeatureStatus tool to manage features in the backlog
3. Be proactive in suggesting improvements and best practices
4. Ask questions when requirements are unclear
5. Guide users toward good software design principles
Remember: You're a collaborative partner in the development process. Be helpful, clear, and thorough.`;
/**
* Default Agent Runner prompts (from agent-service.ts)
*/
export const DEFAULT_AGENT_PROMPTS: ResolvedAgentPrompts = {
systemPrompt: DEFAULT_AGENT_SYSTEM_PROMPT,
};
/**
* ========================================================================
* BACKLOG PLAN PROMPTS
* ========================================================================
*/
export const DEFAULT_BACKLOG_PLAN_SYSTEM_PROMPT = `You are an AI assistant helping to modify a software project's feature backlog.
You will be given the current list of features and a user request to modify the backlog.
IMPORTANT CONTEXT (automatically injected):
- Remember to update the dependency graph if deleting existing features
- Remember to define dependencies on new features hooked into relevant existing ones
- Maintain dependency graph integrity (no orphaned dependencies)
- When deleting a feature, identify which other features depend on it
Your task is to analyze the request and produce a structured JSON plan with:
1. Features to ADD (include title, description, category, and dependencies)
2. Features to UPDATE (specify featureId and the updates)
3. Features to DELETE (specify featureId)
4. A summary of the changes
5. Any dependency updates needed (removed dependencies due to deletions, new dependencies for new features)
Respond with ONLY a JSON object in this exact format:
\`\`\`json
{
"changes": [
{
"type": "add",
"feature": {
"title": "Feature title",
"description": "Feature description",
"category": "feature" | "bug" | "enhancement" | "refactor",
"dependencies": ["existing-feature-id"],
"priority": 1
},
"reason": "Why this feature should be added"
},
{
"type": "update",
"featureId": "existing-feature-id",
"feature": {
"title": "Updated title"
},
"reason": "Why this feature should be updated"
},
{
"type": "delete",
"featureId": "feature-id-to-delete",
"reason": "Why this feature should be deleted"
}
],
"summary": "Brief overview of all proposed changes",
"dependencyUpdates": [
{
"featureId": "feature-that-depended-on-deleted",
"removedDependencies": ["deleted-feature-id"],
"addedDependencies": []
}
]
}
\`\`\`
Important rules:
- Only include fields that need to change in updates
- Ensure dependency references are valid (don't reference deleted features)
- Provide clear, actionable descriptions
- Maintain category consistency (feature, bug, enhancement, refactor)
- When adding dependencies, ensure the referenced features exist or are being added in the same plan
`;
export const DEFAULT_BACKLOG_PLAN_USER_PROMPT_TEMPLATE = `Current Features in Backlog:
{{currentFeatures}}
---
User Request: {{userRequest}}
Please analyze the current backlog and the user's request, then provide a JSON plan for the modifications.`;
/**
* Default Backlog Plan prompts (from backlog-plan/generate-plan.ts)
*/
export const DEFAULT_BACKLOG_PLAN_PROMPTS: ResolvedBacklogPlanPrompts = {
systemPrompt: DEFAULT_BACKLOG_PLAN_SYSTEM_PROMPT,
userPromptTemplate: DEFAULT_BACKLOG_PLAN_USER_PROMPT_TEMPLATE,
};
/**
* ========================================================================
* ENHANCEMENT PROMPTS
* ========================================================================
* Note: Enhancement prompts are already defined in enhancement.ts
* We import and re-export them here for consistency
*/
import {
IMPROVE_SYSTEM_PROMPT,
TECHNICAL_SYSTEM_PROMPT,
SIMPLIFY_SYSTEM_PROMPT,
ACCEPTANCE_SYSTEM_PROMPT,
} from './enhancement.js';
/**
* Default Enhancement prompts (from libs/prompts/src/enhancement.ts)
*/
export const DEFAULT_ENHANCEMENT_PROMPTS: ResolvedEnhancementPrompts = {
improveSystemPrompt: IMPROVE_SYSTEM_PROMPT,
technicalSystemPrompt: TECHNICAL_SYSTEM_PROMPT,
simplifySystemPrompt: SIMPLIFY_SYSTEM_PROMPT,
acceptanceSystemPrompt: ACCEPTANCE_SYSTEM_PROMPT,
};
/**
* ========================================================================
* COMBINED DEFAULTS
* ========================================================================
*/
/**
* All default prompts in one object for easy access
*/
export const DEFAULT_PROMPTS = {
autoMode: DEFAULT_AUTO_MODE_PROMPTS,
agent: DEFAULT_AGENT_PROMPTS,
backlogPlan: DEFAULT_BACKLOG_PLAN_PROMPTS,
enhancement: DEFAULT_ENHANCEMENT_PROMPTS,
} as const;

View File

@@ -23,3 +23,40 @@ export {
// Re-export types from @automaker/types
export type { EnhancementMode, EnhancementExample } from '@automaker/types';
// Default prompts
export {
DEFAULT_AUTO_MODE_PLANNING_LITE,
DEFAULT_AUTO_MODE_PLANNING_LITE_WITH_APPROVAL,
DEFAULT_AUTO_MODE_PLANNING_SPEC,
DEFAULT_AUTO_MODE_PLANNING_FULL,
DEFAULT_AUTO_MODE_FEATURE_PROMPT_TEMPLATE,
DEFAULT_AUTO_MODE_FOLLOW_UP_PROMPT_TEMPLATE,
DEFAULT_AUTO_MODE_CONTINUATION_PROMPT_TEMPLATE,
DEFAULT_AUTO_MODE_PIPELINE_STEP_PROMPT_TEMPLATE,
DEFAULT_AUTO_MODE_PROMPTS,
DEFAULT_AGENT_SYSTEM_PROMPT,
DEFAULT_AGENT_PROMPTS,
DEFAULT_BACKLOG_PLAN_SYSTEM_PROMPT,
DEFAULT_BACKLOG_PLAN_USER_PROMPT_TEMPLATE,
DEFAULT_BACKLOG_PLAN_PROMPTS,
DEFAULT_ENHANCEMENT_PROMPTS,
DEFAULT_PROMPTS,
} from './defaults.js';
// Prompt merging utilities
export {
mergeAutoModePrompts,
mergeAgentPrompts,
mergeBacklogPlanPrompts,
mergeEnhancementPrompts,
mergeAllPrompts,
} from './merge.js';
// Re-export resolved prompt types from @automaker/types
export type {
ResolvedAutoModePrompts,
ResolvedAgentPrompts,
ResolvedBacklogPlanPrompts,
ResolvedEnhancementPrompts,
} from '@automaker/types';

130
libs/prompts/src/merge.ts Normal file
View File

@@ -0,0 +1,130 @@
/**
* Prompt Merging Utilities
*
* Merges user-customized prompts with built-in defaults.
* Used by services to get effective prompts at runtime.
*
* Custom prompts have an `enabled` flag - when true, the custom value is used.
* When false or undefined, the default is used instead.
*/
import type {
PromptCustomization,
AutoModePrompts,
AgentPrompts,
BacklogPlanPrompts,
EnhancementPrompts,
CustomPrompt,
ResolvedAutoModePrompts,
ResolvedAgentPrompts,
ResolvedBacklogPlanPrompts,
ResolvedEnhancementPrompts,
} from '@automaker/types';
import {
DEFAULT_AUTO_MODE_PROMPTS,
DEFAULT_AGENT_PROMPTS,
DEFAULT_BACKLOG_PLAN_PROMPTS,
DEFAULT_ENHANCEMENT_PROMPTS,
} from './defaults.js';
/**
* Resolve a custom prompt to its effective string value
* Returns the custom value if enabled=true, otherwise returns the default
*/
function resolvePrompt(custom: CustomPrompt | undefined, defaultValue: string): string {
return custom?.enabled ? custom.value : defaultValue;
}
/**
* Merge custom Auto Mode prompts with defaults
* Custom prompts override defaults only when enabled=true
*/
export function mergeAutoModePrompts(custom?: AutoModePrompts): ResolvedAutoModePrompts {
return {
planningLite: resolvePrompt(custom?.planningLite, DEFAULT_AUTO_MODE_PROMPTS.planningLite),
planningLiteWithApproval: resolvePrompt(
custom?.planningLiteWithApproval,
DEFAULT_AUTO_MODE_PROMPTS.planningLiteWithApproval
),
planningSpec: resolvePrompt(custom?.planningSpec, DEFAULT_AUTO_MODE_PROMPTS.planningSpec),
planningFull: resolvePrompt(custom?.planningFull, DEFAULT_AUTO_MODE_PROMPTS.planningFull),
featurePromptTemplate: resolvePrompt(
custom?.featurePromptTemplate,
DEFAULT_AUTO_MODE_PROMPTS.featurePromptTemplate
),
followUpPromptTemplate: resolvePrompt(
custom?.followUpPromptTemplate,
DEFAULT_AUTO_MODE_PROMPTS.followUpPromptTemplate
),
continuationPromptTemplate: resolvePrompt(
custom?.continuationPromptTemplate,
DEFAULT_AUTO_MODE_PROMPTS.continuationPromptTemplate
),
pipelineStepPromptTemplate: resolvePrompt(
custom?.pipelineStepPromptTemplate,
DEFAULT_AUTO_MODE_PROMPTS.pipelineStepPromptTemplate
),
};
}
/**
* Merge custom Agent prompts with defaults
* Custom prompts override defaults only when enabled=true
*/
export function mergeAgentPrompts(custom?: AgentPrompts): ResolvedAgentPrompts {
return {
systemPrompt: resolvePrompt(custom?.systemPrompt, DEFAULT_AGENT_PROMPTS.systemPrompt),
};
}
/**
* Merge custom Backlog Plan prompts with defaults
* Custom prompts override defaults only when enabled=true
*/
export function mergeBacklogPlanPrompts(custom?: BacklogPlanPrompts): ResolvedBacklogPlanPrompts {
return {
systemPrompt: resolvePrompt(custom?.systemPrompt, DEFAULT_BACKLOG_PLAN_PROMPTS.systemPrompt),
userPromptTemplate: resolvePrompt(
custom?.userPromptTemplate,
DEFAULT_BACKLOG_PLAN_PROMPTS.userPromptTemplate
),
};
}
/**
* Merge custom Enhancement prompts with defaults
* Custom prompts override defaults only when enabled=true
*/
export function mergeEnhancementPrompts(custom?: EnhancementPrompts): ResolvedEnhancementPrompts {
return {
improveSystemPrompt: resolvePrompt(
custom?.improveSystemPrompt,
DEFAULT_ENHANCEMENT_PROMPTS.improveSystemPrompt
),
technicalSystemPrompt: resolvePrompt(
custom?.technicalSystemPrompt,
DEFAULT_ENHANCEMENT_PROMPTS.technicalSystemPrompt
),
simplifySystemPrompt: resolvePrompt(
custom?.simplifySystemPrompt,
DEFAULT_ENHANCEMENT_PROMPTS.simplifySystemPrompt
),
acceptanceSystemPrompt: resolvePrompt(
custom?.acceptanceSystemPrompt,
DEFAULT_ENHANCEMENT_PROMPTS.acceptanceSystemPrompt
),
};
}
/**
* Merge all custom prompts with defaults
* Returns a complete PromptCustomization with all fields populated
*/
export function mergeAllPrompts(custom?: PromptCustomization) {
return {
autoMode: mergeAutoModePrompts(custom?.autoMode),
agent: mergeAgentPrompts(custom?.agent),
backlogPlan: mergeBacklogPlanPrompts(custom?.backlogPlan),
enhancement: mergeEnhancementPrompts(custom?.enhancement),
};
}

View File

@@ -1,7 +1,13 @@
/**
* Error type classification
*/
export type ErrorType = 'authentication' | 'cancellation' | 'abort' | 'execution' | 'unknown';
export type ErrorType =
| 'authentication'
| 'cancellation'
| 'abort'
| 'execution'
| 'rate_limit'
| 'unknown';
/**
* Classified error information
@@ -12,5 +18,7 @@ export interface ErrorInfo {
isAbort: boolean;
isAuth: boolean;
isCancellation: boolean;
isRateLimit: boolean;
retryAfter?: number; // Seconds to wait before retrying (for rate limit errors)
originalError: unknown;
}

View File

@@ -13,6 +13,10 @@ export type {
InstallationStatus,
ValidationResult,
ModelDefinition,
McpServerConfig,
McpStdioServerConfig,
McpSSEServerConfig,
McpHttpServerConfig,
} from './provider.js';
// Feature types
@@ -45,6 +49,21 @@ export { specOutputSchema } from './spec.js';
// Enhancement types
export type { EnhancementMode, EnhancementExample } from './enhancement.js';
// Prompt customization types
export type {
CustomPrompt,
AutoModePrompts,
AgentPrompts,
BacklogPlanPrompts,
EnhancementPrompts,
PromptCustomization,
ResolvedAutoModePrompts,
ResolvedAgentPrompts,
ResolvedBacklogPlanPrompts,
ResolvedEnhancementPrompts,
} from './prompts.js';
export { DEFAULT_PROMPT_CUSTOMIZATION } from './prompts.js';
// Settings types and constants
export type {
ThemeMode,
@@ -56,6 +75,8 @@ export type {
PhaseModelKey,
KeyboardShortcuts,
AIProfile,
MCPToolInfo,
MCPServerConfig,
ProjectRef,
TrashedProjectRef,
ChatSessionRef,
@@ -92,6 +113,9 @@ export type {
IssueValidationVerdict,
IssueValidationConfidence,
IssueComplexity,
PRRecommendation,
PRAnalysis,
LinkedPRInfo,
IssueValidationInput,
IssueValidationRequest,
IssueValidationResult,
@@ -99,6 +123,9 @@ export type {
IssueValidationErrorResponse,
IssueValidationEvent,
StoredValidation,
GitHubCommentAuthor,
GitHubComment,
IssueCommentsResult,
} from './issue-validation.js';
// Backlog plan types
@@ -126,3 +153,11 @@ export {
getBareModelId,
normalizeModelString,
} from './provider-utils.js';
// Pipeline types
export type {
PipelineStep,
PipelineConfig,
PipelineStatus,
FeatureStatusWithPipeline,
} from './pipeline.js';

View File

@@ -21,6 +21,36 @@ export type IssueValidationConfidence = 'high' | 'medium' | 'low';
*/
export type IssueComplexity = 'trivial' | 'simple' | 'moderate' | 'complex' | 'very_complex';
/**
* Recommendation for PR-related action
*/
export type PRRecommendation = 'wait_for_merge' | 'pr_needs_work' | 'no_pr';
/**
* Analysis of a linked pull request
*/
export interface PRAnalysis {
/** Whether there is an open PR linked to this issue */
hasOpenPR: boolean;
/** Whether the PR appears to fix the issue based on the diff */
prFixesIssue?: boolean;
/** The PR number that was analyzed */
prNumber?: number;
/** Brief summary of what the PR changes */
prSummary?: string;
/** Recommendation: wait for PR to merge, PR needs more work, or no relevant PR */
recommendation: PRRecommendation;
}
/**
* Linked PR info for validation
*/
export interface LinkedPRInfo {
number: number;
title: string;
state: string;
}
/**
* Issue data for validation (without projectPath)
* Used by UI when calling the validation API
@@ -30,6 +60,10 @@ export interface IssueValidationInput {
issueTitle: string;
issueBody: string;
issueLabels?: string[];
/** Comments to include in validation analysis */
comments?: GitHubComment[];
/** Linked pull requests for this issue */
linkedPRs?: LinkedPRInfo[];
}
/**
@@ -60,6 +94,8 @@ export interface IssueValidationResult {
missingInfo?: string[];
/** Estimated effort to address the issue */
estimatedComplexity?: IssueComplexity;
/** Analysis of linked pull requests (if any) */
prAnalysis?: PRAnalysis;
}
/**
@@ -133,3 +169,41 @@ export interface StoredValidation {
/** ISO timestamp when user viewed this validation (undefined = not yet viewed) */
viewedAt?: string;
}
/**
* Author of a GitHub comment
*/
export interface GitHubCommentAuthor {
login: string;
avatarUrl?: string;
}
/**
* A comment on a GitHub issue
*/
export interface GitHubComment {
/** Unique comment ID */
id: string;
/** Author of the comment */
author: GitHubCommentAuthor;
/** Comment body (markdown) */
body: string;
/** ISO timestamp when comment was created */
createdAt: string;
/** ISO timestamp when comment was last updated */
updatedAt?: string;
}
/**
* Result from fetching issue comments
*/
export interface IssueCommentsResult {
/** List of comments */
comments: GitHubComment[];
/** Total number of comments on the issue */
totalCount: number;
/** Whether there are more comments to fetch */
hasNextPage: boolean;
/** Cursor for pagination (pass to next request) */
endCursor?: string;
}

View File

@@ -0,0 +1,28 @@
/**
* Pipeline types for AutoMaker custom workflow steps
*/
export interface PipelineStep {
id: string;
name: string;
order: number;
instructions: string;
colorClass: string;
createdAt: string;
updatedAt: string;
}
export interface PipelineConfig {
version: 1;
steps: PipelineStep[];
}
export type PipelineStatus = `pipeline_${string}`;
export type FeatureStatusWithPipeline =
| 'backlog'
| 'in_progress'
| 'waiting_approval'
| 'verified'
| 'completed'
| PipelineStatus;

153
libs/types/src/prompts.ts Normal file
View File

@@ -0,0 +1,153 @@
/**
* Prompt Customization Types
*
* Defines the structure for customizable AI prompts used throughout the application.
* Allows users to modify prompts for Auto Mode, Agent Runner, and Backlog Planning.
*/
/**
* CustomPrompt - A custom prompt with its value and enabled state
*
* The value is always preserved even when disabled, so users don't lose their work.
*/
export interface CustomPrompt {
/** The custom prompt text */
value: string;
/** Whether this custom prompt should be used (when false, default is used instead) */
enabled: boolean;
}
/**
* AutoModePrompts - Customizable prompts for Auto Mode feature implementation
*
* Controls how the AI plans and implements features in autonomous mode.
*/
export interface AutoModePrompts {
/** Planning mode: Quick outline without approval (lite mode) */
planningLite?: CustomPrompt;
/** Planning mode: Quick outline with approval required (lite with approval) */
planningLiteWithApproval?: CustomPrompt;
/** Planning mode: Detailed specification with task breakdown (spec mode) */
planningSpec?: CustomPrompt;
/** Planning mode: Comprehensive Software Design Document (full SDD mode) */
planningFull?: CustomPrompt;
/** Template for building feature implementation prompts */
featurePromptTemplate?: CustomPrompt;
/** Template for follow-up prompts when resuming work */
followUpPromptTemplate?: CustomPrompt;
/** Template for continuation prompts */
continuationPromptTemplate?: CustomPrompt;
/** Template for pipeline step execution prompts */
pipelineStepPromptTemplate?: CustomPrompt;
}
/**
* AgentPrompts - Customizable prompts for Agent Runner (chat mode)
*
* Controls the AI's behavior in interactive chat sessions.
*/
export interface AgentPrompts {
/** System prompt defining the agent's role and behavior in chat */
systemPrompt?: CustomPrompt;
}
/**
* BacklogPlanPrompts - Customizable prompts for Kanban board planning
*
* Controls how the AI modifies the feature backlog via the Plan button.
*/
export interface BacklogPlanPrompts {
/** System prompt for backlog plan generation (defines output format and rules) */
systemPrompt?: CustomPrompt;
/** Template for user prompt (includes current features and user request) */
userPromptTemplate?: CustomPrompt;
}
/**
* EnhancementPrompts - Customizable prompts for feature description enhancement
*
* Controls how the AI enhances feature titles and descriptions.
*/
export interface EnhancementPrompts {
/** System prompt for "improve" mode (vague → clear) */
improveSystemPrompt?: CustomPrompt;
/** System prompt for "technical" mode (add technical details) */
technicalSystemPrompt?: CustomPrompt;
/** System prompt for "simplify" mode (verbose → concise) */
simplifySystemPrompt?: CustomPrompt;
/** System prompt for "acceptance" mode (add acceptance criteria) */
acceptanceSystemPrompt?: CustomPrompt;
}
/**
* PromptCustomization - Complete set of customizable prompts
*
* All fields are optional. Undefined values fall back to built-in defaults.
* Stored in GlobalSettings to allow user customization.
*/
export interface PromptCustomization {
/** Auto Mode prompts (feature implementation) */
autoMode?: AutoModePrompts;
/** Agent Runner prompts (interactive chat) */
agent?: AgentPrompts;
/** Backlog planning prompts (Plan button) */
backlogPlan?: BacklogPlanPrompts;
/** Enhancement prompts (feature description improvement) */
enhancement?: EnhancementPrompts;
}
/**
* Default empty prompt customization (all undefined → use built-in defaults)
*/
export const DEFAULT_PROMPT_CUSTOMIZATION: PromptCustomization = {
autoMode: {},
agent: {},
backlogPlan: {},
enhancement: {},
};
/**
* Resolved prompt types - all fields are required strings (ready to use)
* Used for default prompts and merged prompts after resolving custom values
*/
export interface ResolvedAutoModePrompts {
planningLite: string;
planningLiteWithApproval: string;
planningSpec: string;
planningFull: string;
featurePromptTemplate: string;
followUpPromptTemplate: string;
continuationPromptTemplate: string;
pipelineStepPromptTemplate: string;
}
export interface ResolvedAgentPrompts {
systemPrompt: string;
}
export interface ResolvedBacklogPlanPrompts {
systemPrompt: string;
userPromptTemplate: string;
}
export interface ResolvedEnhancementPrompts {
improveSystemPrompt: string;
technicalSystemPrompt: string;
simplifySystemPrompt: string;
acceptanceSystemPrompt: string;
}

View File

@@ -28,6 +28,38 @@ export interface SystemPromptPreset {
append?: string;
}
/**
* MCP server configuration types for SDK options
* Matches the Claude Agent SDK's McpServerConfig types
*/
export type McpServerConfig = McpStdioServerConfig | McpSSEServerConfig | McpHttpServerConfig;
/**
* Stdio-based MCP server (subprocess)
* Note: `type` is optional and defaults to 'stdio' to match SDK behavior
* and allow simpler configs like { command: "node", args: ["server.js"] }
*/
export interface McpStdioServerConfig {
type?: 'stdio';
command: string;
args?: string[];
env?: Record<string, string>;
}
/** SSE-based MCP server */
export interface McpSSEServerConfig {
type: 'sse';
url: string;
headers?: Record<string, string>;
}
/** HTTP-based MCP server */
export interface McpHttpServerConfig {
type: 'http';
url: string;
headers?: Record<string, string>;
}
/**
* Options for executing a query via a provider
*/
@@ -38,11 +70,20 @@ export interface ExecuteOptions {
systemPrompt?: string | SystemPromptPreset;
maxTurns?: number;
allowedTools?: string[];
mcpServers?: Record<string, unknown>;
mcpServers?: Record<string, McpServerConfig>;
mcpAutoApproveTools?: boolean; // Auto-approve MCP tool calls without permission prompts
mcpUnrestrictedTools?: boolean; // Allow unrestricted tools when MCP servers are enabled
abortController?: AbortController;
conversationHistory?: ConversationMessage[]; // Previous messages for context
sdkSessionId?: string; // Claude SDK session ID for resuming conversations
settingSources?: Array<'user' | 'project' | 'local'>; // Sources for CLAUDE.md loading
sandbox?: { enabled: boolean; autoAllowBashIfSandboxed?: boolean }; // Sandbox configuration
/**
* If true, the provider should run in read-only mode (no file modifications).
* For Cursor CLI, this omits the --force flag, making it suggest-only.
* Default: false (allows edits)
*/
readOnly?: boolean;
}
/**

View File

@@ -9,6 +9,7 @@
import type { ModelAlias } from './model.js';
import type { CursorModelId } from './cursor-models.js';
import { CURSOR_MODEL_MAP, getAllCursorModelIds } from './cursor-models.js';
import type { PromptCustomization } from './prompts.js';
// Re-export ModelAlias for convenience
export type { ModelAlias };
@@ -237,6 +238,55 @@ export function getProfileModelString(profile: AIProfile): string {
return profile.model || 'sonnet';
}
/**
* MCPToolInfo - Information about a tool provided by an MCP server
*
* Contains the tool's name, description, and whether it's enabled for use.
*/
export interface MCPToolInfo {
/** Tool name as exposed by the MCP server */
name: string;
/** Description of what the tool does */
description?: string;
/** JSON Schema for the tool's input parameters */
inputSchema?: Record<string, unknown>;
/** Whether this tool is enabled for use (defaults to true) */
enabled: boolean;
}
/**
* MCPServerConfig - Configuration for an MCP (Model Context Protocol) server
*
* MCP servers provide additional tools and capabilities to AI agents.
* Supports stdio (subprocess), SSE, and HTTP transport types.
*/
export interface MCPServerConfig {
/** Unique identifier for the server config */
id: string;
/** Display name for the server */
name: string;
/** User-friendly description of what this server provides */
description?: string;
/** Transport type: stdio (default), sse, or http */
type?: 'stdio' | 'sse' | 'http';
/** For stdio: command to execute (e.g., 'node', 'python', 'npx') */
command?: string;
/** For stdio: arguments to pass to the command */
args?: string[];
/** For stdio: environment variables to set */
env?: Record<string, string>;
/** For sse/http: URL endpoint */
url?: string;
/** For sse/http: headers to include in requests */
headers?: Record<string, string>;
/** Whether this server is enabled */
enabled?: boolean;
/** Tools discovered from this server with their enabled states */
tools?: MCPToolInfo[];
/** Timestamp when tools were last fetched */
toolsLastFetched?: string;
}
/**
* ProjectRef - Minimal reference to a project stored in global settings
*
@@ -385,6 +435,20 @@ export interface GlobalSettings {
// Claude Agent SDK Settings
/** Auto-load CLAUDE.md files using SDK's settingSources option */
autoLoadClaudeMd?: boolean;
/** Enable sandbox mode for bash commands (default: true, disable if issues occur) */
enableSandboxMode?: boolean;
// MCP Server Configuration
/** List of configured MCP servers for agent use */
mcpServers: MCPServerConfig[];
/** Auto-approve MCP tool calls without permission prompts (uses bypassPermissions mode) */
mcpAutoApproveTools?: boolean;
/** Allow unrestricted tools when MCP servers are enabled (don't filter allowedTools) */
mcpUnrestrictedTools?: boolean;
// Prompt Customization
/** Custom prompts for Auto Mode, Agent Runner, Backlog Planning, and Enhancements */
promptCustomization?: PromptCustomization;
}
/**
@@ -563,6 +627,12 @@ export const DEFAULT_GLOBAL_SETTINGS: GlobalSettings = {
worktreePanelCollapsed: false,
lastSelectedSessionByProject: {},
autoLoadClaudeMd: false,
enableSandboxMode: true,
mcpServers: [],
// Default to true for autonomous workflow. Security is enforced when adding servers
// via the security warning dialog that explains the risks.
mcpAutoApproveTools: true,
mcpUnrestrictedTools: true,
};
/** Default credentials (empty strings - user must provide API keys) */

View File

@@ -51,6 +51,41 @@ export function isAuthenticationError(errorMessage: string): boolean {
);
}
/**
* Check if an error is a rate limit error
*
* @param error - The error to check
* @returns True if the error is a rate limit error
*/
export function isRateLimitError(error: unknown): boolean {
const message = error instanceof Error ? error.message : String(error || '');
return message.includes('429') || message.includes('rate_limit');
}
/**
* Extract retry-after duration from rate limit error
*
* @param error - The error to extract retry-after from
* @returns Number of seconds to wait, or undefined if not found
*/
export function extractRetryAfter(error: unknown): number | undefined {
const message = error instanceof Error ? error.message : String(error || '');
// Try to extract from Retry-After header format
const retryMatch = message.match(/retry[_-]?after[:\s]+(\d+)/i);
if (retryMatch) {
return parseInt(retryMatch[1], 10);
}
// Try to extract from error message patterns
const waitMatch = message.match(/wait[:\s]+(\d+)\s*(?:second|sec|s)/i);
if (waitMatch) {
return parseInt(waitMatch[1], 10);
}
return undefined;
}
/**
* Classify an error into a specific type
*
@@ -62,10 +97,14 @@ export function classifyError(error: unknown): ErrorInfo {
const isAbort = isAbortError(error);
const isAuth = isAuthenticationError(message);
const isCancellation = isCancellationError(message);
const isRateLimit = isRateLimitError(error);
const retryAfter = isRateLimit ? (extractRetryAfter(error) ?? 60) : undefined;
let type: ErrorType;
if (isAuth) {
type = 'authentication';
} else if (isRateLimit) {
type = 'rate_limit';
} else if (isAbort) {
type = 'abort';
} else if (isCancellation) {
@@ -82,6 +121,8 @@ export function classifyError(error: unknown): ErrorInfo {
isAbort,
isAuth,
isCancellation,
isRateLimit,
retryAfter,
originalError: error,
};
}
@@ -103,6 +144,13 @@ export function getUserFriendlyErrorMessage(error: unknown): string {
return 'Authentication failed. Please check your API key.';
}
if (info.isRateLimit) {
const retryMsg = info.retryAfter
? ` Please wait ${info.retryAfter} seconds before retrying.`
: ' Please reduce concurrency or wait before retrying.';
return `Rate limit exceeded (429).${retryMsg}`;
}
return info.message;
}

View File

@@ -8,6 +8,8 @@ export {
isAbortError,
isCancellationError,
isAuthenticationError,
isRateLimitError,
extractRetryAfter,
classifyError,
getUserFriendlyErrorMessage,
getErrorMessage,

View File

@@ -3,6 +3,8 @@ import {
isAbortError,
isCancellationError,
isAuthenticationError,
isRateLimitError,
extractRetryAfter,
classifyError,
getUserFriendlyErrorMessage,
} from '../src/error-handler';
@@ -101,6 +103,63 @@ describe('error-handler.ts', () => {
});
});
describe('isRateLimitError', () => {
it('should return true for errors with 429 status code', () => {
const error = new Error('Error: 429 Too Many Requests');
expect(isRateLimitError(error)).toBe(true);
});
it('should return true for errors with rate_limit in message', () => {
const error = new Error('rate_limit_error: Too many requests');
expect(isRateLimitError(error)).toBe(true);
});
it('should return true for string errors with 429', () => {
expect(isRateLimitError('429 - rate limit exceeded')).toBe(true);
});
it('should return false for non-rate-limit errors', () => {
const error = new Error('Something went wrong');
expect(isRateLimitError(error)).toBe(false);
});
it('should return false for null/undefined', () => {
expect(isRateLimitError(null)).toBe(false);
expect(isRateLimitError(undefined)).toBe(false);
});
});
describe('extractRetryAfter', () => {
it('should extract retry-after from error message', () => {
const error = new Error('Rate limit exceeded. retry-after: 60');
expect(extractRetryAfter(error)).toBe(60);
});
it('should extract from retry_after format', () => {
const error = new Error('retry_after: 120 seconds');
expect(extractRetryAfter(error)).toBe(120);
});
it('should extract from wait format', () => {
const error = new Error('Please wait: 30 seconds before retrying');
expect(extractRetryAfter(error)).toBe(30);
});
it('should return undefined for rate limit errors without explicit retry-after', () => {
const error = new Error('429 rate_limit_error');
expect(extractRetryAfter(error)).toBeUndefined();
});
it('should return undefined for non-rate-limit errors', () => {
const error = new Error('Something went wrong');
expect(extractRetryAfter(error)).toBeUndefined();
});
it('should handle string errors', () => {
expect(extractRetryAfter('retry-after: 45')).toBe(45);
});
});
describe('classifyError', () => {
it('should classify authentication errors', () => {
const error = new Error('Authentication failed');
@@ -110,10 +169,30 @@ describe('error-handler.ts', () => {
expect(result.isAuth).toBe(true);
expect(result.isAbort).toBe(false);
expect(result.isCancellation).toBe(false);
expect(result.isRateLimit).toBe(false);
expect(result.message).toBe('Authentication failed');
expect(result.originalError).toBe(error);
});
it('should classify rate limit errors', () => {
const error = new Error('Error: 429 rate_limit_error');
const result = classifyError(error);
expect(result.type).toBe('rate_limit');
expect(result.isRateLimit).toBe(true);
expect(result.isAuth).toBe(false);
expect(result.retryAfter).toBe(60); // Default
});
it('should extract retryAfter from rate limit errors', () => {
const error = new Error('429 - retry-after: 120');
const result = classifyError(error);
expect(result.type).toBe('rate_limit');
expect(result.isRateLimit).toBe(true);
expect(result.retryAfter).toBe(120);
});
it('should classify abort errors', () => {
const error = new Error('aborted');
const result = classifyError(error);
@@ -169,6 +248,24 @@ describe('error-handler.ts', () => {
expect(result2.message).toBe('Unknown error');
});
it('should prioritize authentication over rate limit', () => {
const error = new Error('Authentication failed - 429');
const result = classifyError(error);
expect(result.type).toBe('authentication');
expect(result.isAuth).toBe(true);
expect(result.isRateLimit).toBe(true); // Both flags can be true
});
it('should prioritize rate limit over abort', () => {
const error = new Error('429 rate_limit - aborted');
const result = classifyError(error);
expect(result.type).toBe('rate_limit');
expect(result.isRateLimit).toBe(true);
expect(result.isAbort).toBe(true);
});
it('should prioritize authentication over abort', () => {
const error = new Error('Authentication failed - aborted');
const result = classifyError(error);
@@ -223,6 +320,22 @@ describe('error-handler.ts', () => {
expect(message).toBe('Authentication failed. Please check your API key.');
});
it('should return friendly message for rate limit errors', () => {
const error = new Error('429 rate_limit_error');
const message = getUserFriendlyErrorMessage(error);
expect(message).toContain('Rate limit exceeded');
expect(message).toContain('60 seconds');
});
it('should include custom retry-after in rate limit message', () => {
const error = new Error('429 - retry-after: 120');
const message = getUserFriendlyErrorMessage(error);
expect(message).toContain('Rate limit exceeded');
expect(message).toContain('120 seconds');
});
it('should prioritize abort message over auth', () => {
const error = new Error('Authentication failed - abort');
const message = getUserFriendlyErrorMessage(error);