profclaw 0.0.1-beta.1 → 0.0.1-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (151) hide show
  1. package/README.md +1 -1
  2. package/dist/adapters/openclaw.js +1 -1
  3. package/dist/agents/executor.js +38 -6
  4. package/dist/auth/auth-service.js +8 -6
  5. package/dist/auth/device-identity.js +7 -5
  6. package/dist/auth/github-oauth.js +3 -1
  7. package/dist/auth/pairing-codes.js +6 -4
  8. package/dist/browser/service.js +5 -3
  9. package/dist/chat/execution/guardrails.js +3 -1
  10. package/dist/chat/execution/workflows/executor.js +4 -2
  11. package/dist/chat/memory.js +3 -1
  12. package/dist/chat/proactive/index.js +13 -11
  13. package/dist/chat/tools.js +8 -7
  14. package/dist/cli/commands/auth.js +22 -13
  15. package/dist/cli/commands/onboard.js +248 -59
  16. package/dist/cli/commands/setup.js +180 -104
  17. package/dist/cron/issue-poller.js +1 -1
  18. package/dist/hooks/agent-webhook.js +10 -7
  19. package/dist/hooks/prompt-submit.js +5 -6
  20. package/dist/hooks/session-end.js +10 -6
  21. package/dist/hooks/tool-use.js +9 -6
  22. package/dist/integrations/github-ticket-sync.js +8 -6
  23. package/dist/integrations/github.js +8 -6
  24. package/dist/integrations/jira.js +5 -3
  25. package/dist/mcp/server.js +12 -9
  26. package/dist/memory/experience-store.js +15 -14
  27. package/dist/notifications/slack.js +4 -2
  28. package/dist/providers/types.d.ts +2 -2
  29. package/dist/queue/failure-handler.js +15 -13
  30. package/dist/queue/index.js +6 -4
  31. package/dist/queue/memory-queue.js +23 -27
  32. package/dist/queue/task-queue.js +18 -22
  33. package/dist/routes/agents.js +3 -1
  34. package/dist/routes/auth.js +11 -9
  35. package/dist/routes/devices.js +17 -15
  36. package/dist/routes/gateway.js +3 -1
  37. package/dist/routes/hooks.js +7 -5
  38. package/dist/routes/import.js +10 -8
  39. package/dist/routes/labels.js +11 -9
  40. package/dist/routes/oobe.js +8 -6
  41. package/dist/routes/openapi.js +1 -1
  42. package/dist/routes/projects.js +27 -25
  43. package/dist/routes/settings.js +7 -5
  44. package/dist/routes/setup.js +12 -11
  45. package/dist/routes/slack.js +28 -27
  46. package/dist/routes/stats.js +6 -4
  47. package/dist/routes/summaries.js +4 -3
  48. package/dist/routes/sync.js +7 -5
  49. package/dist/routes/tasks.js +10 -8
  50. package/dist/routes/tickets.js +26 -26
  51. package/dist/routes/tokens.js +5 -3
  52. package/dist/routes/users.js +14 -12
  53. package/dist/routes/webhooks.js +5 -3
  54. package/dist/settings/index.js +3 -1
  55. package/dist/storage/index.d.ts +2 -1
  56. package/dist/storage/index.js +9 -4
  57. package/dist/storage/libsql.js +29 -21
  58. package/dist/storage/schema.d.ts +107 -46
  59. package/dist/summaries/storage.js +3 -1
  60. package/dist/tickets/index.js +10 -8
  61. package/dist/utils/circuit-breaker.js +10 -5
  62. package/dist/utils/logger.d.ts +5 -1
  63. package/dist/utils/logger.js +23 -7
  64. package/package.json +4 -3
  65. package/ui/dist/assets/{ActivityView-CkPgKhwm.js → ActivityView-DIPQSJ_d.js} +1 -1
  66. package/ui/dist/assets/{AgentList-C3SVdeEz.js → AgentList-CqyaYrcq.js} +1 -1
  67. package/ui/dist/assets/{AnalyticsDashboard-DVU--vlP.js → AnalyticsDashboard-C-mjuGHT.js} +1 -1
  68. package/ui/dist/assets/{AreaChart-B5qMEGWj.js → AreaChart-CfYWmbtr.js} +1 -1
  69. package/ui/dist/assets/{CartesianChart-B50bcUJi.js → CartesianChart-BaQ3It0c.js} +1 -1
  70. package/ui/dist/assets/{ChatView-BZFJs7w_.js → ChatView-DxxQy6pl.js} +1 -1
  71. package/ui/dist/assets/CostsDashboard-CDBBcGkg.js +1 -0
  72. package/ui/dist/assets/{EstimateSelect-CLpB4NQO.js → EstimateSelect-CDjf5qKl.js} +1 -1
  73. package/ui/dist/assets/{FloatingChatbot-CBzBY9a0.js → FloatingChatbot-CZHjvJIe.js} +1 -1
  74. package/ui/dist/assets/{InviteCodeManagement-DsbdPa6W.js → InviteCodeManagement-DTm-IPTm.js} +1 -1
  75. package/ui/dist/assets/{ProjectDetail-BEM3tFXg.js → ProjectDetail-DlYUcOss.js} +1 -1
  76. package/ui/dist/assets/{ProjectIcon-CN1FmFAk.js → ProjectIcon-BMRpnM04.js} +1 -1
  77. package/ui/dist/assets/{ProjectList-DSDaNhxQ.js → ProjectList-D85TN0i0.js} +1 -1
  78. package/ui/dist/assets/{Settings-dPVcqC_d.js → Settings-CgPqzUvj.js} +3 -3
  79. package/ui/dist/assets/{StatusIndicator-DA3NAo_b.js → StatusIndicator-yig0uZEA.js} +1 -1
  80. package/ui/dist/assets/{SummaryDetail-Bjnf9lT8.js → SummaryDetail-CwLAfHgJ.js} +1 -1
  81. package/ui/dist/assets/{SummaryList-ENwKDBKW.js → SummaryList-BvS5Idnu.js} +1 -1
  82. package/ui/dist/assets/{TaskDetail-DnwG6MzB.js → TaskDetail-zarC1fOt.js} +1 -1
  83. package/ui/dist/assets/{TaskList-D9Ug_HiV.js → TaskList-CyJq3ANE.js} +1 -1
  84. package/ui/dist/assets/{TicketBoard-CviH1571.js → TicketBoard-BlWr3-iH.js} +1 -1
  85. package/ui/dist/assets/{TicketDetail-8Jk8O-33.js → TicketDetail-DmqWKj2W.js} +1 -1
  86. package/ui/dist/assets/{TicketList-B-iV4rRA.js → TicketList-Scj4pMWa.js} +1 -1
  87. package/ui/dist/assets/{UserManagement-CPZ8s7s2.js → UserManagement-BH7uG7dw.js} +1 -1
  88. package/ui/dist/assets/{ViewSwitcher-BUyOOWDI.js → ViewSwitcher-3HzZUdWn.js} +1 -1
  89. package/ui/dist/assets/{alert-dialog-BOmnTkN-.js → alert-dialog-DWPb2IfQ.js} +1 -1
  90. package/ui/dist/assets/{archive-D5my9zOG.js → archive-CGck7gxR.js} +1 -1
  91. package/ui/dist/assets/{badge-B4ov81_m.js → badge-DEmc3Z_l.js} +1 -1
  92. package/ui/dist/assets/{brain-C53ElFLD.js → brain-CfFdLVqb.js} +1 -1
  93. package/ui/dist/assets/{bug-D0TJoCYn.js → bug-DqSGPUUE.js} +1 -1
  94. package/ui/dist/assets/{calendar-BkWIhKa1.js → calendar-BuZ4zbPW.js} +1 -1
  95. package/ui/dist/assets/{chevron-up-BRTtndUZ.js → chevron-up-DcJ4LL_N.js} +1 -1
  96. package/ui/dist/assets/{circle-x-BN89YhbS.js → circle-x-C78PNZjF.js} +1 -1
  97. package/ui/dist/assets/{constants-fc7TFI7u.js → constants-DhDjZe4P.js} +1 -1
  98. package/ui/dist/assets/{cpu-B41x1-vV.js → cpu-Ck6AoG8c.js} +1 -1
  99. package/ui/dist/assets/{database-D9Cnfmzj.js → database-CA2w8Lvt.js} +1 -1
  100. package/ui/dist/assets/{dialog-DrWB4EYD.js → dialog-BOdGWhgE.js} +1 -1
  101. package/ui/dist/assets/{ellipsis-NuUWekvu.js → ellipsis-FhHKRbfl.js} +1 -1
  102. package/ui/dist/assets/{ellipsis-vertical-BueMfuCw.js → ellipsis-vertical-CO0UqE4-.js} +1 -1
  103. package/ui/dist/assets/{file-code-HR9829Jc.js → file-code-ChnhYYj1.js} +1 -1
  104. package/ui/dist/assets/{folder-open-C_7oYEqe.js → folder-open-CHwA1-ve.js} +1 -1
  105. package/ui/dist/assets/{funnel-B5gmNXGf.js → funnel-ByEjRFNB.js} +1 -1
  106. package/ui/dist/assets/{git-branch-BRMiJmV3.js → git-branch-GTOPQgU4.js} +1 -1
  107. package/ui/dist/assets/{git-commit-horizontal-CXOUQeJI.js → git-commit-horizontal-F5M84Kut.js} +1 -1
  108. package/ui/dist/assets/{globe-CCspjvus.js → globe-CQWfr7hT.js} +1 -1
  109. package/ui/dist/assets/{hash-g3102kPC.js → hash-nKC9gtLg.js} +1 -1
  110. package/ui/dist/assets/{history-CNqcBBrV.js → history-DfFSA9xJ.js} +1 -1
  111. package/ui/dist/assets/{index-kb4AJXUB.js → index--JkIxVXb.js} +3 -3
  112. package/ui/dist/assets/index-Ca3vk7Gq.js +1 -0
  113. package/ui/dist/assets/{index-C9jNQXzL.js → index-DHGvP9mp.js} +1 -1
  114. package/ui/dist/assets/index-DeI8lKRR.js +1 -0
  115. package/ui/dist/assets/{index-DURTouJI.js → index-Z4-RtZcs.js} +1 -1
  116. package/ui/dist/assets/{index-CEZ6R2Uw.js → index-a4Fxq0Hb.js} +1 -1
  117. package/ui/dist/assets/{layers-CLXiHN6o.js → layers-DFeLExSa.js} +1 -1
  118. package/ui/dist/assets/{layout-dashboard-B07MFhBs.js → layout-dashboard-WmgjMhWF.js} +1 -1
  119. package/ui/dist/assets/{lightbulb-B2dgG5ks.js → lightbulb-Bo8hEr7B.js} +1 -1
  120. package/ui/dist/assets/{link-2-BxNmr9hL.js → link-2-YqrpQXBH.js} +1 -1
  121. package/ui/dist/assets/{markdown-renderer-CjU6hBcW.js → markdown-renderer-Croje6Xe.js} +1 -1
  122. package/ui/dist/assets/{message-square-Bf2-CVbt.js → message-square-CD0H5hot.js} +1 -1
  123. package/ui/dist/assets/{pencil-D096Ey62.js → pencil-DpFnj5dn.js} +1 -1
  124. package/ui/dist/assets/{play-DfdcSI3x.js → play-DVgea_jT.js} +1 -1
  125. package/ui/dist/assets/{popover-BV0QJDQq.js → popover-JXecfWG5.js} +1 -1
  126. package/ui/dist/assets/{progress-DjPEDJfI.js → progress-C1ghf1Fu.js} +1 -1
  127. package/ui/dist/assets/{rocket-CPsTc-5G.js → rocket-BFxPYGx9.js} +1 -1
  128. package/ui/dist/assets/{select-LsO16slr.js → select-MADP8m6Q.js} +1 -1
  129. package/ui/dist/assets/{send-BDYBZDUZ.js → send-eYnW_7n-.js} +1 -1
  130. package/ui/dist/assets/{server-B3k6noce.js → server-CPMaysH8.js} +1 -1
  131. package/ui/dist/assets/{settings-2-Cu4xTjhG.js → settings-2-scXUOuE_.js} +1 -1
  132. package/ui/dist/assets/{sheet-BScLnPKX.js → sheet-r6NSOxi-.js} +1 -1
  133. package/ui/dist/assets/{shield-check-CQqkwufI.js → shield-check-PEr7nmVw.js} +1 -1
  134. package/ui/dist/assets/{shield-off-ZFruH9KO.js → shield-off-CLtXio62.js} +1 -1
  135. package/ui/dist/assets/{skeleton-CtkIA_MU.js → skeleton-C9oEzNlU.js} +1 -1
  136. package/ui/dist/assets/{smartphone-DnoksVkc.js → smartphone-DusFfY0U.js} +1 -1
  137. package/ui/dist/assets/{square-uD9ZHMfW.js → square-e1yxB2eR.js} +1 -1
  138. package/ui/dist/assets/{switch-MQPnwGJK.js → switch-tNs0n-kz.js} +1 -1
  139. package/ui/dist/assets/{table-B8cqlywA.js → table-CXxtLicg.js} +1 -1
  140. package/ui/dist/assets/{tag-wEJQRWlV.js → tag-tAwMmQ9G.js} +1 -1
  141. package/ui/dist/assets/{textarea-D5LJCOGl.js → textarea-BjiEeIWh.js} +1 -1
  142. package/ui/dist/assets/{timer-mbGHegmr.js → timer-Vel1N3RP.js} +1 -1
  143. package/ui/dist/assets/{tooltip-Df1CunSp.js → tooltip-ZMwtfr1J.js} +1 -1
  144. package/ui/dist/assets/{trash-2-B4oAXsX-.js → trash-2-eM-CNwsZ.js} +1 -1
  145. package/ui/dist/assets/{trending-up-BGWxvlmZ.js → trending-up-BGafmjRo.js} +1 -1
  146. package/ui/dist/assets/{wand-sparkles-CZy4xvL3.js → wand-sparkles-DPvcd9Pi.js} +1 -1
  147. package/ui/dist/assets/{wrench-ByZkjO6m.js → wrench-B8s3zYVV.js} +1 -1
  148. package/ui/dist/index.html +1 -1
  149. package/ui/dist/assets/CostsDashboard-ChfH8BlG.js +0 -1
  150. package/ui/dist/assets/index-27mTPKuh.js +0 -1
  151. package/ui/dist/assets/index-BMx3fH-e.js +0 -1
@@ -9,6 +9,8 @@ import { randomUUID } from 'crypto';
9
9
  import { getDb } from '../storage/index.js';
10
10
  import { tickets, ticketExternalLinks, ticketComments, ticketHistory, githubCommentSyncs, } from '../storage/schema.js';
11
11
  import { findOrCreateLabel, setTicketLabels } from '../labels/index.js';
12
+ import { createContextualLogger } from '../utils/logger.js';
13
+ const log = createContextualLogger('GitHubSync');
12
14
  function isGitHubSyncWebhookPayload(payload) {
13
15
  return typeof payload === 'object' && payload !== null;
14
16
  }
@@ -181,7 +183,7 @@ export async function handleIssueEvent(action, issue, repository) {
181
183
  await setTicketLabels(ticketId, labelIds);
182
184
  }
183
185
  catch (e) {
184
- console.warn('[GitHub Sync] Failed to sync labels:', e);
186
+ log.warn('Failed to sync labels', { error: e instanceof Error ? e.message : String(e) });
185
187
  }
186
188
  }
187
189
  // Update last synced timestamp
@@ -189,7 +191,7 @@ export async function handleIssueEvent(action, issue, repository) {
189
191
  lastSyncedAt: now,
190
192
  syncError: null,
191
193
  }).where(eq(ticketExternalLinks.ticketId, ticketId));
192
- console.log(`[GitHub Sync] Updated ticket ${ticketId} from issue #${issue.number}: ${changes.map(c => c.field).join(', ')}`);
194
+ log.info('Updated ticket from issue', { ticketId, issueNumber: issue.number, fields: changes.map(c => c.field) });
193
195
  return { action: 'updated', ticketId };
194
196
  }
195
197
  /**
@@ -250,7 +252,7 @@ export async function handleCommentCreated(issue, comment, repository) {
250
252
  githubCommentId: comment.id,
251
253
  createdAt: now,
252
254
  });
253
- console.log(`[GitHub Sync] Created comment ${commentId} from GitHub comment #${comment.id}`);
255
+ log.info('Created comment from GitHub', { commentId, githubCommentId: comment.id });
254
256
  return { action: 'comment_created', ticketId, commentId };
255
257
  }
256
258
  /**
@@ -279,7 +281,7 @@ export async function handleCommentEdited(issue, comment, repository) {
279
281
  content: comment.body,
280
282
  updatedAt: new Date(),
281
283
  }).where(eq(ticketComments.id, syncRecord[0].commentId));
282
- console.log(`[GitHub Sync] Updated comment ${syncRecord[0].commentId} from GitHub comment #${comment.id}`);
284
+ log.info('Updated comment from GitHub', { commentId: syncRecord[0].commentId, githubCommentId: comment.id });
283
285
  return { action: 'comment_updated', ticketId: synced.ticketId, commentId: syncRecord[0].commentId };
284
286
  }
285
287
  /**
@@ -305,7 +307,7 @@ export async function handleCommentDeleted(issue, comment, repository) {
305
307
  // Delete sync record and comment
306
308
  await db.delete(githubCommentSyncs).where(eq(githubCommentSyncs.id, syncRecord[0].id));
307
309
  await db.delete(ticketComments).where(eq(ticketComments.id, syncRecord[0].commentId));
308
- console.log(`[GitHub Sync] Deleted comment ${syncRecord[0].commentId}`);
310
+ log.info('Deleted comment', { commentId: syncRecord[0].commentId });
309
311
  return { action: 'comment_deleted', ticketId: synced.ticketId };
310
312
  }
311
313
  // MAIN WEBHOOK HANDLER
@@ -345,7 +347,7 @@ export async function processGitHubWebhookForTicketSync(event, payload) {
345
347
  return { action: 'ignored', reason: `unhandled_event: ${event}` };
346
348
  }
347
349
  catch (error) {
348
- console.error('[GitHub Sync] Error processing webhook:', error);
350
+ log.error('Error processing webhook', error instanceof Error ? error : new Error(String(error)));
349
351
  throw error;
350
352
  }
351
353
  }
@@ -1,4 +1,6 @@
1
1
  import { createHmac, timingSafeEqual } from 'crypto';
2
+ import { createContextualLogger } from '../utils/logger.js';
3
+ const log = createContextualLogger('GitHub');
2
4
  /**
3
5
  * GitHub Webhook Integration
4
6
  *
@@ -20,7 +22,7 @@ const WEBHOOK_SECRET = process.env.GITHUB_WEBHOOK_SECRET || '';
20
22
  */
21
23
  export function verifyGitHubSignature(payload, signature) {
22
24
  if (!WEBHOOK_SECRET) {
23
- console.warn('GITHUB_WEBHOOK_SECRET not set - skipping signature verification');
25
+ log.warn('GITHUB_WEBHOOK_SECRET not set - skipping signature verification');
24
26
  return true;
25
27
  }
26
28
  if (!signature) {
@@ -49,10 +51,10 @@ export async function handleGitHubWebhook(c) {
49
51
  throw new Error('Invalid webhook signature');
50
52
  }
51
53
  const payload = JSON.parse(rawBody);
52
- console.log(`[GitHub Webhook] Event: ${event}, Delivery: ${deliveryId}`);
54
+ log.info('GitHub webhook received', { event, deliveryId });
53
55
  switch (event) {
54
56
  case 'ping':
55
- console.log('[GitHub Webhook] Ping received');
57
+ log.info('GitHub ping received');
56
58
  return null;
57
59
  case 'issues':
58
60
  return handleIssueEvent(payload);
@@ -61,7 +63,7 @@ export async function handleGitHubWebhook(c) {
61
63
  case 'pull_request':
62
64
  return handlePullRequestEvent(payload);
63
65
  default:
64
- console.log(`[GitHub Webhook] Unhandled event: ${event}`);
66
+ log.info('Unhandled event', { event });
65
67
  return null;
66
68
  }
67
69
  }
@@ -75,7 +77,7 @@ function handleIssueEvent(payload) {
75
77
  // Check if issue has the AI task label
76
78
  const hasAiLabel = issue.labels?.some((l) => l.name === AI_TASK_LABEL || l.name === AI_REVIEW_LABEL);
77
79
  if (!hasAiLabel) {
78
- console.log('[GitHub] Issue opened without AI label, skipping');
80
+ log.info('Issue opened without AI label, skipping', { issueNumber: issue.number });
79
81
  return null;
80
82
  }
81
83
  }
@@ -121,7 +123,7 @@ function handleIssueCommentEvent(payload) {
121
123
  return null;
122
124
  }
123
125
  const prompt = triggerMatch[1].trim();
124
- console.log(`[GitHub] AI trigger command in issue #${issue.number}: ${prompt.substring(0, 50)}...`);
126
+ log.info('AI trigger command received', { issueNumber: issue.number, promptPreview: prompt.substring(0, 50) });
125
127
  return {
126
128
  title: `AI Task: ${issue.title}`,
127
129
  description: `Triggered by @${comment.user.login} in issue #${issue.number}`,
@@ -1,3 +1,5 @@
1
+ import { createContextualLogger } from '../utils/logger.js';
2
+ const log = createContextualLogger('Jira');
1
3
  /**
2
4
  * Jira Webhook Integration
3
5
  *
@@ -16,7 +18,7 @@ const WEBHOOK_SECRET = process.env.JIRA_WEBHOOK_SECRET || '';
16
18
  */
17
19
  export function verifyJiraToken(c) {
18
20
  if (!WEBHOOK_SECRET) {
19
- console.warn('JIRA_WEBHOOK_SECRET not set - skipping verification');
21
+ log.warn('JIRA_WEBHOOK_SECRET not set - skipping verification');
20
22
  return true;
21
23
  }
22
24
  const token = c.req.query('token');
@@ -33,7 +35,7 @@ export async function handleJiraWebhook(c) {
33
35
  const rawBody = await c.req.text();
34
36
  const payload = JSON.parse(rawBody);
35
37
  const { webhookEvent, issue } = payload;
36
- console.log(`[Jira Webhook] Event: ${webhookEvent}, Issue: ${issue.key}`);
38
+ log.info('Jira webhook received', { webhookEvent, issueKey: issue.key });
37
39
  // Only handle created or updated events
38
40
  if (webhookEvent !== 'jira:issue_created' && webhookEvent !== 'jira:issue_updated') {
39
41
  return null;
@@ -41,7 +43,7 @@ export async function handleJiraWebhook(c) {
41
43
  // Check for AI label
42
44
  const hasAiLabel = issue.fields.labels?.includes(JIRA_AI_TASK_LABEL);
43
45
  if (!hasAiLabel) {
44
- console.log('[Jira] Issue does not have AI label, skipping');
46
+ log.info('Issue does not have AI label, skipping', { issueKey: issue.key });
45
47
  return null;
46
48
  }
47
49
  // Construct source URL (convert API URL to browser URL)
@@ -24,6 +24,8 @@ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
24
24
  import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
25
25
  import { ListToolsRequestSchema, CallToolRequestSchema, } from '@modelcontextprotocol/sdk/types.js';
26
26
  import { BROWSER_TOOLS, handleBrowserTool } from './browser-tools.js';
27
+ import { createContextualLogger } from '../utils/logger.js';
28
+ const log = createContextualLogger('MCPServer', { stream: 'stderr' });
27
29
  // MCP Server configuration
28
30
  const PROFCLAW_API_URL = process.env.PROFCLAW_API_URL || process.env.PROFCLAW_API_URL || 'http://localhost:3000';
29
31
  const sessionState = {
@@ -405,12 +407,12 @@ export async function handleLogTask(args) {
405
407
  }),
406
408
  });
407
409
  if (!response.ok) {
408
- console.error('[MCP] Failed to report to profClaw:', response.statusText);
410
+ log.error('[MCP] Failed to report to profClaw', { statusText: response.statusText });
409
411
  }
410
412
  }
411
413
  catch (error) {
412
414
  // Silently fail - don't block the agent
413
- console.error('[MCP] Failed to connect to profClaw:', error);
415
+ log.error('[MCP] Failed to connect to profClaw', error instanceof Error ? error : { error });
414
416
  }
415
417
  return {
416
418
  content: [
@@ -447,11 +449,11 @@ export async function handleCompleteTask(args) {
447
449
  }),
448
450
  });
449
451
  if (!response.ok) {
450
- console.error('[MCP] Failed to report completion to profClaw:', response.statusText);
452
+ log.error('[MCP] Failed to report completion to profClaw', { statusText: response.statusText });
451
453
  }
452
454
  }
453
455
  catch (error) {
454
- console.error('[MCP] Failed to connect to profClaw:', error);
456
+ log.error('[MCP] Failed to connect to profClaw', error instanceof Error ? error : { error });
455
457
  }
456
458
  // Clear current task
457
459
  sessionState.currentTask = undefined;
@@ -510,7 +512,7 @@ async function handleGetContext(args) {
510
512
  }
511
513
  }
512
514
  catch (error) {
513
- console.error('[MCP] Failed to fetch context from profClaw:', error);
515
+ log.error('[MCP] Failed to fetch context from profClaw', error instanceof Error ? error : { error });
514
516
  }
515
517
  // Fallback if API unavailable
516
518
  return {
@@ -815,12 +817,13 @@ async function main() {
815
817
  const transport = new StdioServerTransport();
816
818
  await server.connect(transport);
817
819
  // Log startup to stderr (stdout is for MCP protocol)
818
- console.error('[profClaw MCP] Server started');
819
- console.error(`[profClaw MCP] Session ID: ${sessionState.sessionId}`);
820
- console.error(`[profClaw MCP] API URL: ${PROFCLAW_API_URL}`);
820
+ log.info('[profClaw MCP] Server started', {
821
+ apiUrl: PROFCLAW_API_URL,
822
+ sessionId: sessionState.sessionId,
823
+ });
821
824
  }
822
825
  main().catch((error) => {
823
- console.error('[profClaw MCP] Fatal error:', error);
826
+ log.error('[profClaw MCP] Fatal error', error instanceof Error ? error : { error });
824
827
  process.exit(1);
825
828
  });
826
829
  //# sourceMappingURL=server.js.map
@@ -10,7 +10,8 @@
10
10
  */
11
11
  import { randomUUID } from 'node:crypto';
12
12
  import { getClient } from '../storage/index.js';
13
- import { logger } from '../utils/logger.js';
13
+ import { createContextualLogger } from '../utils/logger.js';
14
+ const log = createContextualLogger('ExperienceStore');
14
15
  // Table Initialization
15
16
  /**
16
17
  * Initialize the experiences table and indexes.
@@ -90,7 +91,7 @@ export async function recordExperience(exp) {
90
91
  return id;
91
92
  }
92
93
  catch (error) {
93
- console.error('[experience-store] Failed to record experience:', error);
94
+ log.error('Failed to record experience', error instanceof Error ? error : new Error(String(error)));
94
95
  throw error;
95
96
  }
96
97
  }
@@ -109,7 +110,7 @@ export async function getExperience(id) {
109
110
  return rowToExperience(result.rows[0]);
110
111
  }
111
112
  catch (error) {
112
- console.error('[experience-store] Failed to get experience:', error);
113
+ log.error('Failed to get experience', error instanceof Error ? error : new Error(String(error)));
113
114
  return null;
114
115
  }
115
116
  }
@@ -129,7 +130,7 @@ export async function markUsed(id) {
129
130
  });
130
131
  }
131
132
  catch (error) {
132
- console.error('[experience-store] Failed to mark experience used:', error);
133
+ log.error('Failed to mark experience used', error instanceof Error ? error : new Error(String(error)));
133
134
  }
134
135
  }
135
136
  /**
@@ -145,7 +146,7 @@ export async function deleteExperience(id) {
145
146
  return true;
146
147
  }
147
148
  catch (error) {
148
- console.error('[experience-store] Failed to delete experience:', error);
149
+ log.error('Failed to delete experience', error instanceof Error ? error : new Error(String(error)));
149
150
  return false;
150
151
  }
151
152
  }
@@ -190,7 +191,7 @@ export async function listExperiences(options = {}) {
190
191
  };
191
192
  }
192
193
  catch (error) {
193
- console.error('[experience-store] Failed to list experiences:', error);
194
+ log.error('Failed to list experiences', error instanceof Error ? error : new Error(String(error)));
194
195
  return { experiences: [], total: 0 };
195
196
  }
196
197
  }
@@ -246,7 +247,7 @@ export async function findSimilarExperiences(intent, tags, limit = 10) {
246
247
  return results;
247
248
  }
248
249
  catch (error) {
249
- console.error('[experience-store] Similarity search failed:', error);
250
+ log.error('Similarity search failed', error instanceof Error ? error : new Error(String(error)));
250
251
  return [];
251
252
  }
252
253
  }
@@ -306,7 +307,7 @@ export async function trackPreference(userId, category, value) {
306
307
  }
307
308
  }
308
309
  catch (error) {
309
- console.error('[experience-store] Failed to track preference:', error);
310
+ log.error('Failed to track preference', error instanceof Error ? error : new Error(String(error)));
310
311
  }
311
312
  }
312
313
  /**
@@ -334,7 +335,7 @@ export async function getUserPreferences(userId) {
334
335
  return grouped;
335
336
  }
336
337
  catch (error) {
337
- console.error('[experience-store] Failed to get user preferences:', error);
338
+ log.error('Failed to get user preferences', error instanceof Error ? error : new Error(String(error)));
338
339
  return {};
339
340
  }
340
341
  }
@@ -369,11 +370,11 @@ export async function applyDecay(halfLifeDays = DEFAULT_HALF_LIFE_DAYS) {
369
370
  });
370
371
  updated++;
371
372
  }
372
- logger.info(`[experience-store] Decay applied to ${updated} experiences (halfLife=${halfLifeDays}d)`);
373
+ log.info('Decay applied', { count: updated, halfLifeDays });
373
374
  return updated;
374
375
  }
375
376
  catch (error) {
376
- console.error('[experience-store] Decay calculation failed:', error);
377
+ log.error('Decay calculation failed', error instanceof Error ? error : new Error(String(error)));
377
378
  return 0;
378
379
  }
379
380
  }
@@ -394,12 +395,12 @@ export async function pruneExpired(minWeight = 0.05) {
394
395
  sql: `DELETE FROM experiences WHERE weight < ?`,
395
396
  args: [minWeight],
396
397
  });
397
- logger.info(`[experience-store] Pruned ${count} expired experiences (minWeight=${minWeight})`);
398
+ log.info('Pruned expired experiences', { count, minWeight });
398
399
  }
399
400
  return count;
400
401
  }
401
402
  catch (error) {
402
- console.error('[experience-store] Prune failed:', error);
403
+ log.error('Prune failed', error instanceof Error ? error : new Error(String(error)));
403
404
  return 0;
404
405
  }
405
406
  }
@@ -432,7 +433,7 @@ export async function getStats() {
432
433
  return { total, byType, avgAge: Math.round(avgAge * 10) / 10 };
433
434
  }
434
435
  catch (error) {
435
- console.error('[experience-store] Failed to get stats:', error);
436
+ log.error('Failed to get stats', error instanceof Error ? error : new Error(String(error)));
436
437
  return { total: 0, byType: {}, avgAge: 0 };
437
438
  }
438
439
  }
@@ -3,6 +3,8 @@
3
3
  *
4
4
  * Sends notifications to Slack via incoming webhooks.
5
5
  */
6
+ import { createContextualLogger } from '../utils/logger.js';
7
+ const log = createContextualLogger('SlackNotify');
6
8
  const SLACK_WEBHOOK_URL = process.env.SLACK_WEBHOOK_URL || '';
7
9
  const SLACK_CHANNEL = process.env.SLACK_CHANNEL || '';
8
10
  const NOTIFY_ON_START = process.env.SLACK_NOTIFY_ON_START === 'true';
@@ -84,11 +86,11 @@ export async function sendSlackNotification(notification) {
84
86
  body: JSON.stringify(payload),
85
87
  });
86
88
  if (!response.ok) {
87
- console.error(`[Slack] Failed to send notification: ${response.status}`);
89
+ log.error('Failed to send notification', new Error(`HTTP ${response.status}`));
88
90
  }
89
91
  }
90
92
  catch (error) {
91
- console.error('[Slack] Error sending notification:', error);
93
+ log.error('Error sending notification', error instanceof Error ? error : new Error(String(error)));
92
94
  }
93
95
  }
94
96
  /**
@@ -306,6 +306,7 @@ export declare const ChatCompletionRequestSchema: z.ZodObject<{
306
306
  ticketId: z.ZodOptional<z.ZodString>;
307
307
  taskId: z.ZodOptional<z.ZodString>;
308
308
  }, "strip", z.ZodTypeAny, {
309
+ stream: boolean;
309
310
  messages: {
310
311
  id: string;
311
312
  timestamp: string;
@@ -317,7 +318,6 @@ export declare const ChatCompletionRequestSchema: z.ZodObject<{
317
318
  imageUrls?: string[] | undefined;
318
319
  }[];
319
320
  temperature: number;
320
- stream: boolean;
321
321
  taskId?: string | undefined;
322
322
  model?: string | undefined;
323
323
  ticketId?: string | undefined;
@@ -342,6 +342,7 @@ export declare const ChatCompletionRequestSchema: z.ZodObject<{
342
342
  imageUrls?: string[] | undefined;
343
343
  }[];
344
344
  taskId?: string | undefined;
345
+ stream?: boolean | undefined;
345
346
  model?: string | undefined;
346
347
  ticketId?: string | undefined;
347
348
  provider?: "custom" | "anthropic" | "openai" | "google" | "azure" | "ollama" | "openrouter" | "bedrock" | undefined;
@@ -354,7 +355,6 @@ export declare const ChatCompletionRequestSchema: z.ZodObject<{
354
355
  systemPrompt?: string | undefined;
355
356
  temperature?: number | undefined;
356
357
  maxTokens?: number | undefined;
357
- stream?: boolean | undefined;
358
358
  }>;
359
359
  export type ChatCompletionRequest = z.infer<typeof ChatCompletionRequestSchema>;
360
360
  export declare const ChatCompletionResponseSchema: z.ZodObject<{
@@ -9,6 +9,8 @@
9
9
  import { sendSlackNotification } from '../notifications/slack.js';
10
10
  import { getStorage } from '../storage/index.js';
11
11
  import { randomUUID } from 'crypto';
12
+ import { createContextualLogger } from '../utils/logger.js';
13
+ const log = createContextualLogger('FailureHandler');
12
14
  // Reference to the task queue (set during init)
13
15
  let taskQueueRef = null;
14
16
  /**
@@ -69,12 +71,12 @@ export async function initDeadLetterQueue() {
69
71
  * Handle a failed task
70
72
  */
71
73
  export async function handleTaskFailure(task, error) {
72
- console.error(`[FailureHandler] Task ${task.id} failed: ${error.message}`);
74
+ log.error(`Task ${task.id} failed`, new Error(error.message));
73
75
  task.attempts = (task.attempts || 0) + 1;
74
76
  // Check if we should retry
75
77
  if (task.attempts < task.maxAttempts) {
76
78
  const delay = calculateBackoffWithJitter(task.attempts);
77
- console.log(`[FailureHandler] Scheduling retry ${task.attempts}/${task.maxAttempts} in ${Math.round(delay / 1000)}s`);
79
+ log.info('Scheduling retry', { taskId: task.id, attempt: task.attempts, maxAttempts: task.maxAttempts, delaySeconds: Math.round(delay / 1000) });
78
80
  // Actually retry with BullMQ delay
79
81
  if (taskQueueRef) {
80
82
  await taskQueueRef.add(`retry-${task.id}-${task.attempts}`, { ...task, status: 'pending' }, {
@@ -82,10 +84,10 @@ export async function handleTaskFailure(task, error) {
82
84
  priority: task.priority,
83
85
  jobId: `${task.id}-retry-${task.attempts}`,
84
86
  });
85
- console.log(`[FailureHandler] Task ${task.id} requeued with ${delay}ms delay`);
87
+ log.info('Task requeued with delay', { taskId: task.id, delayMs: delay });
86
88
  }
87
89
  else {
88
- console.warn('[FailureHandler] Task queue not available, cannot retry');
90
+ log.warn('Task queue not available, cannot retry');
89
91
  }
90
92
  await sendSlackNotification({
91
93
  type: 'task_retry',
@@ -93,11 +95,11 @@ export async function handleTaskFailure(task, error) {
93
95
  message: `Attempt ${task.attempts}/${task.maxAttempts} failed. Retrying in ${Math.round(delay / 60000)} minutes.\nError: ${error.message}`,
94
96
  task,
95
97
  severity: 'info',
96
- }).catch(console.error);
98
+ }).catch((err) => log.error('Slack notification failed', err instanceof Error ? err : new Error(String(err))));
97
99
  return;
98
100
  }
99
101
  // Max retries reached - move to dead letter queue
100
- console.error(`[FailureHandler] Task ${task.id} exceeded max retries, moving to dead letter queue`);
102
+ log.error(`Task ${task.id} exceeded max retries, moving to dead letter queue`, new Error('MAX_RETRIES_EXCEEDED'));
101
103
  task.status = 'failed';
102
104
  task.result = {
103
105
  success: false,
@@ -119,7 +121,7 @@ export async function handleTaskFailure(task, error) {
119
121
  message: `Task failed after ${task.maxAttempts} attempts and moved to dead letter queue.\n\nLast error: ${error.message}\n\nSource: ${task.sourceUrl || 'N/A'}`,
120
122
  task,
121
123
  severity: 'error',
122
- }).catch(console.error);
124
+ }).catch((err) => log.error('Slack notification failed', err instanceof Error ? err : new Error(String(err))));
123
125
  }
124
126
  /**
125
127
  * Calculate exponential backoff with jitter
@@ -167,7 +169,7 @@ async function addToDeadLetterQueue(task, error) {
167
169
  toUnixSeconds(task.createdAt),
168
170
  toUnixSeconds(task.startedAt),
169
171
  ]);
170
- console.log(`[FailureHandler] Task ${task.id} persisted to DLQ with ID ${id}`);
172
+ log.info('Task persisted to DLQ', { taskId: task.id, dlqId: id });
171
173
  }
172
174
  /**
173
175
  * Post failure comment on GitHub issue
@@ -216,7 +218,7 @@ This task has been moved to the dead letter queue for manual review. Please:
216
218
  });
217
219
  }
218
220
  catch (err) {
219
- console.error('[FailureHandler] Failed to post GitHub comment:', err);
221
+ log.error('Failed to post GitHub comment', err instanceof Error ? err : new Error(String(err)));
220
222
  }
221
223
  }
222
224
  /**
@@ -289,14 +291,14 @@ export async function discardFromDeadLetterQueue(dlqId, resolvedBy, resolutionNo
289
291
  */
290
292
  export async function retryDeadLetterTask(dlqId) {
291
293
  if (!taskQueueRef) {
292
- console.error('[FailureHandler] Task queue not available, cannot retry');
294
+ log.error('Task queue not available, cannot retry', new Error('QUEUE_NOT_INITIALIZED'));
293
295
  return false;
294
296
  }
295
297
  const storage = getStorage();
296
298
  // Get the DLQ entry
297
299
  const rows = await storage.query('SELECT * FROM dead_letter_tasks WHERE id = ? AND status = ?', [dlqId, 'pending']);
298
300
  if (rows.length === 0) {
299
- console.error(`[FailureHandler] DLQ entry ${dlqId} not found or not pending`);
301
+ log.error(`DLQ entry not found or not pending`, new Error(`DLQ entry ${dlqId} not found`));
300
302
  return false;
301
303
  }
302
304
  const dlqEntry = rows[0];
@@ -334,7 +336,7 @@ export async function retryDeadLetterTask(dlqId) {
334
336
  await storage.execute(`UPDATE dead_letter_tasks
335
337
  SET retry_count = retry_count + 1, last_retry_at = strftime('%s', 'now'), status = 'resolved', resolution_note = 'Retried'
336
338
  WHERE id = ?`, [dlqId]);
337
- console.log(`[FailureHandler] Task ${task.id} moved from DLQ back to main queue`);
339
+ log.info('Task moved from DLQ back to main queue', { taskId: task.id, dlqId });
338
340
  return true;
339
341
  }
340
342
  /**
@@ -350,7 +352,7 @@ export async function cleanupDeadLetterQueue(retentionDays = parseInt(process.en
350
352
  // SQLite execute doesn't reliably return affected rows, so count separately
351
353
  const remaining = await storage.query(`SELECT COUNT(*) as count FROM dead_letter_tasks WHERE status IN ('resolved', 'discarded')`);
352
354
  const remainingCount = remaining[0]?.count ?? 0;
353
- console.log(`[FailureHandler] DLQ cleanup: removed entries older than ${retentionDays} days (${remainingCount} resolved/discarded remaining)`);
355
+ log.info('DLQ cleanup complete', { retentionDays, remainingResolved: remainingCount });
354
356
  return remainingCount;
355
357
  }
356
358
  /**
@@ -5,6 +5,8 @@
5
5
  * All consumers import from this module instead of task-queue or memory-queue directly.
6
6
  */
7
7
  import { shouldUseRedis } from '../core/deployment.js';
8
+ import { createContextualLogger } from '../utils/logger.js';
9
+ const log = createContextualLogger('Queue');
8
10
  let backend = null;
9
11
  let backendType = null;
10
12
  async function loadBackend() {
@@ -25,21 +27,21 @@ async function loadBackend() {
25
27
  await testRedis.disconnect();
26
28
  backend = await import('./task-queue.js');
27
29
  backendType = 'redis';
28
- console.log('[Queue] Using Redis-backed BullMQ queue');
30
+ log.info('Using Redis-backed BullMQ queue');
29
31
  return backend;
30
32
  }
31
33
  catch {
32
34
  if (process.env.PROFCLAW_REQUIRE_REDIS === 'true') {
33
- console.error('[FATAL] PROFCLAW_REQUIRE_REDIS=true but Redis is unreachable');
35
+ log.error('PROFCLAW_REQUIRE_REDIS=true but Redis is unreachable', new Error('REDIS_UNREACHABLE'));
34
36
  process.exit(1);
35
37
  }
36
- console.log('[Queue] Redis unavailable, falling back to in-memory queue');
38
+ log.warn('Redis unavailable, falling back to in-memory queue');
37
39
  }
38
40
  }
39
41
  // Fallback: in-memory queue
40
42
  backend = await import('./memory-queue.js');
41
43
  backendType = 'memory';
42
- console.log('[Queue] Using in-memory queue (no Redis)');
44
+ log.info('Using in-memory queue (no Redis)');
43
45
  return backend;
44
46
  }
45
47
  function getBackend() {