-
-
Notifications
You must be signed in to change notification settings - Fork 1.9k
feat: JIRA integration, Vault support, and consolidated settings UI #1979
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: develop
Are you sure you want to change the base?
Changes from all commits
eff4d13
3363135
efec45f
f5ee693
912ddfd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -88,6 +88,25 @@ export const MEMORY_MCP_TOOLS = [ | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** @deprecated Use MEMORY_MCP_TOOLS instead */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const GRAPHITI_MCP_TOOLS = MEMORY_MCP_TOOLS; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** JIRA MCP tools for issue tracking (when JIRA is configured) */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const JIRA_TOOLS = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__jira__list_projects', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__jira__get_issue', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__jira__create_issue', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__jira__update_issue', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__jira__search_issues', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__jira__add_comment', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__jira__get_transitions', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__jira__transition_issue', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] as const; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| /** Vault MCP tools for external vault access */ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| export const VAULT_TOOLS = [ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__vault__read_file', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__vault__list_directory', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| 'mcp__vault__search_files', | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ] as const; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+91
to
+109
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Add You introduced new MCP integrations, but 🔧 Proposed fix const MCP_SERVER_NAME_MAP: Record<string, string> = {
context7: 'context7',
'graphiti-memory': 'memory',
graphiti: 'memory',
memory: 'memory',
linear: 'linear',
+ jira: 'jira',
+ vault: 'vault',
electron: 'electron',
puppeteer: 'puppeteer',
'auto-claude': 'auto-claude',
};📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================================= | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // Browser Automation MCP Tools (QA agents only) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================================= | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -145,11 +145,34 @@ export class TaskLogWriter { | |
| this.flushPendingText(); | ||
| break; | ||
|
|
||
| case 'error': | ||
| case 'error': { | ||
| this.flushPendingText(); | ||
| this.addEntry(logPhase, 'error', event.error.message); | ||
| // Extract meaningful error message - AI SDK sometimes just sends 'error' as the message | ||
| const err = event.error; | ||
| let errorContent = 'Unknown error'; | ||
| if (err) { | ||
| const msg = err.message || ''; | ||
| const code = err.code || ''; | ||
| const cause = err.cause; | ||
| // If message is just 'error' (unhelpful), try to extract from cause or code | ||
| if (msg && msg !== 'error') { | ||
| errorContent = msg; | ||
| } else if (cause instanceof Error) { | ||
| errorContent = cause.message || String(cause); | ||
| } else if (cause && typeof cause === 'object') { | ||
| errorContent = JSON.stringify(cause).slice(0, 500); | ||
| } else if (cause) { | ||
| errorContent = String(cause); | ||
| } else if (code && code !== 'error') { | ||
| errorContent = `Error code: ${code}`; | ||
| } else { | ||
| errorContent = `Error: ${JSON.stringify(err).slice(0, 500)}`; | ||
|
Comment on lines
+160
to
+169
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # Check if the file exists and get its structure
fd task-log-writer.tsRepository: AndyMik90/Aperant Length of output: 112 🏁 Script executed: # Read the full file to understand the processEvent context
cat -n apps/desktop/src/main/ai/logging/task-log-writer.ts | head -200Repository: AndyMik90/Aperant Length of output: 7978 🏁 Script executed: # Search for JSON.stringify usage patterns in similar logging/error contexts
rg "JSON.stringify" apps/desktop/src/main/ -B 2 -A 2 --max-count=20Repository: AndyMik90/Aperant Length of output: 50374 🏁 Script executed: # Find the StreamEvent type definition
fd -e ts -e tsx | xargs grep -l "type StreamEvent" | head -5Repository: AndyMik90/Aperant Length of output: 102 🏁 Script executed: # Look at the session/types.ts file to understand StreamEvent structure
rg "type StreamEvent|interface StreamEvent" -A 30 apps/desktop/src/main/ai/session/Repository: AndyMik90/Aperant Length of output: 2029 🏁 Script executed: # Get the full ErrorEvent definition
rg "interface ErrorEvent" -A 10 apps/desktop/src/main/ai/session/types.tsRepository: AndyMik90/Aperant Length of output: 248 🏁 Script executed: # Find the SessionError type definition
rg "type SessionError|interface SessionError" -A 15 apps/desktop/src/main/ai/session/types.tsRepository: AndyMik90/Aperant Length of output: 518 Wrap JSON.stringify calls in error fallback with try-catch. Lines 163 and 169 call 🔧 Suggested fix const err = event.error;
+ const safeSerialize = (value: unknown): string => {
+ try {
+ return JSON.stringify(value);
+ } catch {
+ return String(value);
+ }
+ };
let errorContent = 'Unknown error';
if (err) {
const msg = err.message || '';
const code = err.code || '';
const cause = err.cause;
if (msg && msg !== 'error') {
errorContent = msg;
} else if (cause instanceof Error) {
errorContent = cause.message || String(cause);
- } else if (cause && typeof cause === 'object') {
- errorContent = JSON.stringify(cause).slice(0, 500);
+ } else if (cause && typeof cause === 'object') {
+ errorContent = safeSerialize(cause).slice(0, 500);
} else if (cause) {
errorContent = String(cause);
} else if (code && code !== 'error') {
errorContent = `Error code: ${code}`;
} else {
- errorContent = `Error: ${JSON.stringify(err).slice(0, 500)}`;
+ errorContent = `Error: ${safeSerialize(err).slice(0, 500)}`;
}
}🤖 Prompt for AI Agents |
||
| } | ||
| } | ||
| this.addEntry(logPhase, 'error', errorContent); | ||
| this.save(); | ||
| break; | ||
| } | ||
|
|
||
| default: | ||
| // Ignore thinking-delta, usage-update | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -118,6 +118,55 @@ function createAutoClaudeServer(specDir: string): McpServerConfig { | |||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * JIRA MCP server - issue tracking integration. | ||||||||||||||||||||||||||||||||||||||||||||||
| * Conditionally enabled when project has JIRA configured. | ||||||||||||||||||||||||||||||||||||||||||||||
| * Requires JIRA_HOST, JIRA_EMAIL, JIRA_TOKEN environment variables. | ||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||
| * Uses the community @modelcontextprotocol/server-atlassian package. | ||||||||||||||||||||||||||||||||||||||||||||||
| * If no suitable MCP server is available, JIRA access is handled | ||||||||||||||||||||||||||||||||||||||||||||||
| * directly via the IPC handlers (jira/issue-handlers.ts) instead. | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| function createJiraServer(env: Record<string, string>): McpServerConfig { | ||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||
| id: 'jira', | ||||||||||||||||||||||||||||||||||||||||||||||
| name: 'JIRA', | ||||||||||||||||||||||||||||||||||||||||||||||
| description: 'Issue tracking integration for JIRA/Atlassian', | ||||||||||||||||||||||||||||||||||||||||||||||
| enabledByDefault: false, | ||||||||||||||||||||||||||||||||||||||||||||||
| transport: { | ||||||||||||||||||||||||||||||||||||||||||||||
| type: 'stdio', | ||||||||||||||||||||||||||||||||||||||||||||||
| command: 'npx', | ||||||||||||||||||||||||||||||||||||||||||||||
| args: ['-y', '@modelcontextprotocol/server-atlassian'], | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+138
to
+139
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: cat -n apps/desktop/src/main/ai/mcp/registry.ts | head -250 | tail -150Repository: AndyMik90/Aperant Length of output: 5816 🏁 Script executed: wc -l apps/desktop/src/main/ai/mcp/registry.tsRepository: AndyMik90/Aperant Length of output: 105 🏁 Script executed: cat -n apps/desktop/src/main/ai/mcp/registry.ts | sed -n '130,250p'Repository: AndyMik90/Aperant Length of output: 4700 🌐 Web query:
💡 Result: When running npx -y (unversioned package specifier) where the package is not found locally or globally, npx (npm exec) will install and execute the version associated with the 'latest' dist-tag from the npm registry. The -y (or --yes) flag bypasses the confirmation prompt for installation. If a matching package exists locally in the project's dependencies (exact name and version match if specifier provided, or any version for unversioned), it uses the local one first. Otherwise, it fetches from the registry using standard npm resolution, defaulting to 'latest' tag for unversioned names. This aligns with npm install behavior: unversioned installs the 'latest' tag (docs confirm npm install without specifier uses latest tag, and npx uses same package resolution). Citations:
🌐 Web query:
💡 Result: The Citations:
Address supply-chain risk and security issues in MCP server invocations. Three issues require fixing:
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| env: { | ||||||||||||||||||||||||||||||||||||||||||||||
| ATLASSIAN_SITE_URL: env.JIRA_HOST || '', | ||||||||||||||||||||||||||||||||||||||||||||||
| ATLASSIAN_USER_EMAIL: env.JIRA_EMAIL || '', | ||||||||||||||||||||||||||||||||||||||||||||||
| ATLASSIAN_API_TOKEN: env.JIRA_TOKEN || '', | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+130
to
+143
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧹 Nitpick | 🔵 Trivial Use a narrow
Also applies to: 184-185 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
| * Vault MCP server - external vault/Obsidian integration. | ||||||||||||||||||||||||||||||||||||||||||||||
| * Conditionally enabled when vault path is configured. | ||||||||||||||||||||||||||||||||||||||||||||||
| * Provides read-only file access to vault directory for agent context. | ||||||||||||||||||||||||||||||||||||||||||||||
| * | ||||||||||||||||||||||||||||||||||||||||||||||
| * Uses the official @modelcontextprotocol/server-filesystem package. | ||||||||||||||||||||||||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||||||||||||||||||||||||
| function createVaultServer(vaultPath: string): McpServerConfig { | ||||||||||||||||||||||||||||||||||||||||||||||
| return { | ||||||||||||||||||||||||||||||||||||||||||||||
| id: 'vault', | ||||||||||||||||||||||||||||||||||||||||||||||
| name: 'Vault', | ||||||||||||||||||||||||||||||||||||||||||||||
| description: 'External vault integration for context and learnings', | ||||||||||||||||||||||||||||||||||||||||||||||
| enabledByDefault: false, | ||||||||||||||||||||||||||||||||||||||||||||||
| transport: { | ||||||||||||||||||||||||||||||||||||||||||||||
| type: 'stdio', | ||||||||||||||||||||||||||||||||||||||||||||||
| command: 'npx', | ||||||||||||||||||||||||||||||||||||||||||||||
| args: ['-y', '@modelcontextprotocol/server-filesystem', vaultPath], | ||||||||||||||||||||||||||||||||||||||||||||||
| }, | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+149
to
+166
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: # First, let's examine the actual file to understand the current implementation
find . -name "registry.ts" -path "*apps/desktop/src/main/ai/mcp*" -type fRepository: AndyMik90/Aperant Length of output: 103 🏁 Script executed: # Check the specific file content around the mentioned lines
cat -n apps/desktop/src/main/ai/mcp/registry.ts | sed -n '140,175p'Repository: AndyMik90/Aperant Length of output: 1400 🏁 Script executed: # Look for any related MCP server configuration or tool handling
rg "createVaultServer|@modelcontextprotocol/server-filesystem" --type ts -A 5 -B 5Repository: AndyMik90/Aperant Length of output: 2334 🏁 Script executed: # Check package.json for version info and related dependencies
cat -n apps/desktop/package.json | grep -i "modelcontext\|mcp"Repository: AndyMik90/Aperant Length of output: 290 🌐 Web query:
💡 Result: The Citations:
🌐 Web query:
💡 Result:
Citations:
🏁 Script executed: # Check if `@modelcontextprotocol/server-filesystem` has any env vars or args for tool restriction
npm view `@modelcontextprotocol/server-filesystem` readme | head -100Repository: AndyMik90/Aperant Length of output: 3915 🌐 Web query:
💡 Result: The Citations:
Fix misleading documentation comment and remove write-enabled tools from Vault MCP. The comment claiming "read-only file access" (line 152) is false. The Use one of:
Update the misleading comment at line 152 to accurately describe the access level once the fix is in place. 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================================= | ||||||||||||||||||||||||||||||||||||||||||||||
| // Registry | ||||||||||||||||||||||||||||||||||||||||||||||
| // ============================================================================= | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -132,6 +181,10 @@ export interface McpRegistryOptions { | |||||||||||||||||||||||||||||||||||||||||||||
| linearApiKey?: string; | ||||||||||||||||||||||||||||||||||||||||||||||
| /** Environment variables for server processes */ | ||||||||||||||||||||||||||||||||||||||||||||||
| env?: Record<string, string>; | ||||||||||||||||||||||||||||||||||||||||||||||
| /** JIRA environment variables (JIRA_HOST, JIRA_EMAIL, JIRA_TOKEN) */ | ||||||||||||||||||||||||||||||||||||||||||||||
| jiraEnv?: Record<string, string>; | ||||||||||||||||||||||||||||||||||||||||||||||
| /** Vault path (if vault integration is enabled) */ | ||||||||||||||||||||||||||||||||||||||||||||||
| vaultPath?: string; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| /** | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
@@ -180,6 +233,23 @@ export function getMcpServerConfig( | |||||||||||||||||||||||||||||||||||||||||||||
| return createAutoClaudeServer(specDir); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| case 'jira': { | ||||||||||||||||||||||||||||||||||||||||||||||
| const jiraHost = options.jiraEnv?.JIRA_HOST ?? options.env?.JIRA_HOST; | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!jiraHost) return null; | ||||||||||||||||||||||||||||||||||||||||||||||
| const jiraEnv = options.jiraEnv ?? { | ||||||||||||||||||||||||||||||||||||||||||||||
| JIRA_HOST: options.env?.JIRA_HOST ?? '', | ||||||||||||||||||||||||||||||||||||||||||||||
| JIRA_EMAIL: options.env?.JIRA_EMAIL ?? '', | ||||||||||||||||||||||||||||||||||||||||||||||
| JIRA_TOKEN: options.env?.JIRA_TOKEN ?? '', | ||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||
| return createJiraServer(jiraEnv); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+236
to
+245
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Require full JIRA credentials before returning MCP config. Line 231 currently checks only 🛡️ Proposed fix case 'jira': {
- const jiraHost = options.jiraEnv?.JIRA_HOST ?? options.env?.JIRA_HOST;
- if (!jiraHost) return null;
- const jiraEnv = options.jiraEnv ?? {
- JIRA_HOST: options.env?.JIRA_HOST ?? '',
- JIRA_EMAIL: options.env?.JIRA_EMAIL ?? '',
- JIRA_TOKEN: options.env?.JIRA_TOKEN ?? '',
- };
+ const jiraHost = options.jiraEnv?.JIRA_HOST ?? options.env?.JIRA_HOST;
+ const jiraEmail = options.jiraEnv?.JIRA_EMAIL ?? options.env?.JIRA_EMAIL;
+ const jiraToken = options.jiraEnv?.JIRA_TOKEN ?? options.env?.JIRA_TOKEN;
+ if (!jiraHost || !jiraEmail || !jiraToken) return null;
+ const jiraEnv = {
+ JIRA_HOST: jiraHost,
+ JIRA_EMAIL: jiraEmail,
+ JIRA_TOKEN: jiraToken,
+ };
return createJiraServer(jiraEnv);
}📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| case 'vault': { | ||||||||||||||||||||||||||||||||||||||||||||||
| const vPath = options.vaultPath ?? options.env?.VAULT_PATH; | ||||||||||||||||||||||||||||||||||||||||||||||
| if (!vPath) return null; | ||||||||||||||||||||||||||||||||||||||||||||||
| return createVaultServer(vPath); | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| default: | ||||||||||||||||||||||||||||||||||||||||||||||
| return null; | ||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -266,7 +266,33 @@ export function createStreamHandler(onEvent: SessionEventCallback) { | |
| } | ||
|
|
||
| function handleError(part: ErrorPart): void { | ||
| const errorMessage = part.error instanceof Error ? part.error.message : String(part.error ?? 'Stream error'); | ||
| // DEBUG: Dump the raw error to console for diagnosis | ||
| console.warn('[StreamHandler] RAW ERROR:', JSON.stringify(part.error, Object.getOwnPropertyNames(part.error instanceof Error ? part.error : {}), 2).slice(0, 1000)); | ||
| console.warn('[StreamHandler] ERROR TYPE:', typeof part.error, part.error?.constructor?.name); | ||
| if (part.error instanceof Error) { | ||
| console.warn('[StreamHandler] ERROR CAUSE:', (part.error as { cause?: unknown }).cause); | ||
| console.warn('[StreamHandler] ERROR STACK:', part.error.stack?.slice(0, 500)); | ||
| } | ||
|
Comment on lines
+269
to
+275
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🧩 Analysis chain🏁 Script executed: head -300 apps/desktop/src/main/ai/session/stream-handler.ts | tail -60Repository: AndyMik90/Aperant Length of output: 2479 🏁 Script executed: sed -n '250,300p' apps/desktop/src/main/ai/session/stream-handler.tsRepository: AndyMik90/Aperant Length of output: 2102 🏁 Script executed: git ls-files apps/desktop/src/main/ | head -20Repository: AndyMik90/Aperant Length of output: 1199 🏁 Script executed: # Check for any dev-only conditionals or Sentry usage in the handler
sed -n '250,310p' apps/desktop/src/main/ai/session/stream-handler.tsRepository: AndyMik90/Aperant Length of output: 2383 🏁 Script executed: # Verify if classifyError sanitizes and how it works
rg -A 10 "function classifyError" apps/desktop/src/main/ai/session/stream-handler.tsRepository: AndyMik90/Aperant Length of output: 43 🏁 Script executed: # Check if there's any dev-only flag or environment check in this file
rg "isDev|process.env.NODE_ENV|__DEV__" apps/desktop/src/main/ai/session/stream-handler.tsRepository: AndyMik90/Aperant Length of output: 43 🏁 Script executed: # Check imports at the top of the file
head -50 apps/desktop/src/main/ai/session/stream-handler.tsRepository: AndyMik90/Aperant Length of output: 1869 🏁 Script executed: # Look for classifyError definition anywhere in the codebase
rg "classifyError" apps/desktop/src/main/ai/ -A 5Repository: AndyMik90/Aperant Length of output: 10800 🏁 Script executed: # Check the full handleError function and what follows
sed -n '260,300p' apps/desktop/src/main/ai/session/stream-handler.tsRepository: AndyMik90/Aperant Length of output: 1897 Remove unsanitized debug logs that leak sensitive error data. Lines 269-275 log raw |
||
|
|
||
| // Extract meaningful error message - AI SDK error objects may have nested cause | ||
| let errorMessage: string; | ||
| if (part.error instanceof Error) { | ||
| errorMessage = part.error.message; | ||
| // If message is just 'error', try cause | ||
| if (errorMessage === 'error' && (part.error as { cause?: unknown }).cause) { | ||
| const cause = (part.error as { cause?: unknown }).cause; | ||
| errorMessage = cause instanceof Error ? cause.message : String(cause); | ||
| } | ||
| // Also check stack for more context if message is unhelpful | ||
| if (errorMessage === 'error' && part.error.stack) { | ||
| errorMessage = part.error.stack.split('\n')[0] || 'Stream error'; | ||
| } | ||
| } else if (typeof part.error === 'object' && part.error !== null) { | ||
| const errObj = part.error as Record<string, unknown>; | ||
| errorMessage = (errObj.message as string) || (errObj.text as string) || JSON.stringify(part.error).slice(0, 500); | ||
| } else { | ||
| errorMessage = String(part.error ?? 'Stream error'); | ||
| } | ||
| const { sessionError } = classifyError(errorMessage); | ||
| emit({ type: 'error', error: sessionError }); | ||
| } | ||
|
|
||
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -89,6 +89,29 @@ export function registerEnvHandlers( | |||||||||||||||||||||||||||||||||
| if (config.gitlabAutoSync !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars[GITLAB_ENV_KEYS.AUTO_SYNC] = config.gitlabAutoSync ? 'true' : 'false'; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| // Independent issue tracking flags (separate from source control) | ||||||||||||||||||||||||||||||||||
| if (config.githubIssuesEnabled !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars['GITHUB_ISSUES_ENABLED'] = config.githubIssuesEnabled ? 'true' : 'false'; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (config.gitlabIssuesEnabled !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars['GITLAB_ISSUES_ENABLED'] = config.gitlabIssuesEnabled ? 'true' : 'false'; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+92
to
+98
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Persist
🐛 Proposed fix # =============================================================================
# GITHUB INTEGRATION (OPTIONAL)
# =============================================================================
${existingVars['GITHUB_TOKEN'] ? `GITHUB_TOKEN=${existingVars['GITHUB_TOKEN']}` : '# GITHUB_TOKEN='}
${existingVars['GITHUB_REPO'] ? `GITHUB_REPO=${existingVars['GITHUB_REPO']}` : '# GITHUB_REPO=owner/repo'}
${existingVars['GITHUB_AUTO_SYNC'] !== undefined ? `GITHUB_AUTO_SYNC=${existingVars['GITHUB_AUTO_SYNC']}` : '# GITHUB_AUTO_SYNC=false'}
+${existingVars['GITHUB_ISSUES_ENABLED'] !== undefined ? `GITHUB_ISSUES_ENABLED=${existingVars['GITHUB_ISSUES_ENABLED']}` : '# GITHUB_ISSUES_ENABLED=false'}Also applies to: 238-247 🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| // JIRA Integration | ||||||||||||||||||||||||||||||||||
| if (config.jiraEnabled !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars['JIRA_ENABLED'] = config.jiraEnabled ? 'true' : 'false'; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (config.jiraHost !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars['JIRA_HOST'] = config.jiraHost; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (config.jiraEmail !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars['JIRA_EMAIL'] = config.jiraEmail; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (config.jiraToken !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars['JIRA_TOKEN'] = config.jiraToken; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (config.jiraProjectKey !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars['JIRA_PROJECT_KEY'] = config.jiraProjectKey; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| // Git/Worktree Settings | ||||||||||||||||||||||||||||||||||
| if (config.defaultBranch !== undefined) { | ||||||||||||||||||||||||||||||||||
| existingVars['DEFAULT_BRANCH'] = config.defaultBranch; | ||||||||||||||||||||||||||||||||||
|
|
@@ -212,6 +235,16 @@ ${envLine(existingVars, GITLAB_ENV_KEYS.INSTANCE_URL, 'https://gitlab.com')} | |||||||||||||||||||||||||||||||||
| ${envLine(existingVars, GITLAB_ENV_KEYS.TOKEN)} | ||||||||||||||||||||||||||||||||||
| ${envLine(existingVars, GITLAB_ENV_KEYS.PROJECT, 'group/project')} | ||||||||||||||||||||||||||||||||||
| ${envLine(existingVars, GITLAB_ENV_KEYS.AUTO_SYNC, 'false')} | ||||||||||||||||||||||||||||||||||
| ${existingVars['GITLAB_ISSUES_ENABLED'] !== undefined ? `GITLAB_ISSUES_ENABLED=${existingVars['GITLAB_ISSUES_ENABLED']}` : '# GITLAB_ISSUES_ENABLED=false'} | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||||||||||||||||||
| # JIRA INTEGRATION (OPTIONAL) | ||||||||||||||||||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||||||||||||||||||
| ${existingVars['JIRA_ENABLED'] !== undefined ? `JIRA_ENABLED=${existingVars['JIRA_ENABLED']}` : '# JIRA_ENABLED=false'} | ||||||||||||||||||||||||||||||||||
| ${envLine(existingVars, 'JIRA_HOST', 'https://your-domain.atlassian.net')} | ||||||||||||||||||||||||||||||||||
| ${envLine(existingVars, 'JIRA_EMAIL')} | ||||||||||||||||||||||||||||||||||
| ${envLine(existingVars, 'JIRA_TOKEN')} | ||||||||||||||||||||||||||||||||||
| ${envLine(existingVars, 'JIRA_PROJECT_KEY', 'PROJ')} | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| # ============================================================================= | ||||||||||||||||||||||||||||||||||
| # GIT/WORKTREE SETTINGS (OPTIONAL) | ||||||||||||||||||||||||||||||||||
|
|
@@ -324,6 +357,7 @@ ${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_ | |||||||||||||||||||||||||||||||||
| linearEnabled: false, | ||||||||||||||||||||||||||||||||||
| githubEnabled: false, | ||||||||||||||||||||||||||||||||||
| gitlabEnabled: false, | ||||||||||||||||||||||||||||||||||
| jiraEnabled: false, | ||||||||||||||||||||||||||||||||||
| memoryEnabled: false, | ||||||||||||||||||||||||||||||||||
| enableFancyUi: true, | ||||||||||||||||||||||||||||||||||
| openaiKeyIsGlobal: false | ||||||||||||||||||||||||||||||||||
|
|
@@ -386,6 +420,30 @@ ${existingVars['GRAPHITI_DB_PATH'] ? `GRAPHITI_DB_PATH=${existingVars['GRAPHITI_ | |||||||||||||||||||||||||||||||||
| config.gitlabAutoSync = true; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Independent issue tracking flags | ||||||||||||||||||||||||||||||||||
| if (vars['GITHUB_ISSUES_ENABLED']?.toLowerCase() === 'true') { | ||||||||||||||||||||||||||||||||||
| config.githubIssuesEnabled = true; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (vars['GITLAB_ISSUES_ENABLED']?.toLowerCase() === 'true') { | ||||||||||||||||||||||||||||||||||
| config.gitlabIssuesEnabled = true; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // JIRA config | ||||||||||||||||||||||||||||||||||
| if (vars['JIRA_HOST']) { | ||||||||||||||||||||||||||||||||||
| config.jiraHost = vars['JIRA_HOST']; | ||||||||||||||||||||||||||||||||||
| // Enable by default if host exists and JIRA_ENABLED is not explicitly false | ||||||||||||||||||||||||||||||||||
| config.jiraEnabled = vars['JIRA_ENABLED']?.toLowerCase() !== 'false'; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
Comment on lines
+431
to
+436
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Honor
🐛 Proposed fix- if (vars['JIRA_HOST']) {
- config.jiraHost = vars['JIRA_HOST'];
- // Enable by default if host exists and JIRA_ENABLED is not explicitly false
- config.jiraEnabled = vars['JIRA_ENABLED']?.toLowerCase() !== 'false';
- }
+ if (vars['JIRA_ENABLED'] !== undefined) {
+ config.jiraEnabled = vars['JIRA_ENABLED'].toLowerCase() === 'true';
+ }
+ if (vars['JIRA_HOST']) {
+ config.jiraHost = vars['JIRA_HOST'];
+ if (vars['JIRA_ENABLED'] === undefined) {
+ config.jiraEnabled = true;
+ }
+ }📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||
| if (vars['JIRA_EMAIL']) { | ||||||||||||||||||||||||||||||||||
| config.jiraEmail = vars['JIRA_EMAIL']; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (vars['JIRA_TOKEN']) { | ||||||||||||||||||||||||||||||||||
| config.jiraToken = vars['JIRA_TOKEN']; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
| if (vars['JIRA_PROJECT_KEY']) { | ||||||||||||||||||||||||||||||||||
| config.jiraProjectKey = vars['JIRA_PROJECT_KEY']; | ||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| // Git/Worktree config | ||||||||||||||||||||||||||||||||||
| if (vars['DEFAULT_BRANCH']) { | ||||||||||||||||||||||||||||||||||
| config.defaultBranch = vars['DEFAULT_BRANCH']; | ||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,27 @@ | ||
| /** | ||
| * JIRA IPC Handlers Module | ||
| * | ||
| * This module exports the main registration function for all JIRA-related IPC handlers. | ||
| */ | ||
|
|
||
| import type { BrowserWindow } from 'electron'; | ||
| import type { AgentManager } from '../../agent'; | ||
|
|
||
| import { registerJiraIssueHandlers } from './issue-handlers'; | ||
| import { registerJiraInvestigationHandlers } from './investigation-handlers'; | ||
|
|
||
| /** | ||
| * Register all JIRA IPC handlers | ||
| */ | ||
| export function registerJiraHandlers( | ||
| agentManager: AgentManager, | ||
| getMainWindow: () => BrowserWindow | null | ||
| ): void { | ||
| console.warn('[JIRA] Registering JIRA handlers'); | ||
| registerJiraIssueHandlers(); | ||
| registerJiraInvestigationHandlers(agentManager, getMainWindow); | ||
| console.warn('[JIRA] JIRA handlers registered'); | ||
| } | ||
|
|
||
| // Re-export individual registration functions for custom usage | ||
| export { registerJiraIssueHandlers, registerJiraInvestigationHandlers }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Map orchestrator phases before logging them.
The cast on Line 685 is unsafe:
BuildOrchestratoremits phases likeqa_reviewandqa_fixing, butTaskLogWriter.logText()only understandsplanning/coding/qa/spec. Those QA failures will hittoLogPhase()'s default branch and be written undercoding. ReusemapExecutionPhaseToPhase()here instead of casting.🩹 Suggested fix
if (logWriter) { - logWriter.logText(error.message, phase as Phase, 'error'); + const logPhase = mapExecutionPhaseToPhase(phase as ExecutionPhase); + logWriter.logText(error.message, logPhase, 'error'); }📝 Committable suggestion
🤖 Prompt for AI Agents