panopticon-cli 0.6.0 → 0.6.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 (57) hide show
  1. package/dist/cli/index.js +36 -27
  2. package/dist/cli/index.js.map +1 -1
  3. package/dist/dashboard/{event-store-BtuZCLHu.js → event-store-D7kLBd07.js} +1 -1
  4. package/dist/dashboard/{event-store-OS5jH3Eu.js → event-store-O9q0Gweh.js} +2 -2
  5. package/dist/dashboard/{event-store-OS5jH3Eu.js.map → event-store-O9q0Gweh.js.map} +1 -1
  6. package/dist/dashboard/{inspect-agent-CwT4mrvV.js → inspect-agent-B57kGDUV.js} +3 -3
  7. package/dist/dashboard/{inspect-agent-CwT4mrvV.js.map → inspect-agent-B57kGDUV.js.map} +1 -1
  8. package/dist/dashboard/{issue-service-singleton-z78bbRiO.js → issue-service-singleton-DQK42EqH.js} +1 -1
  9. package/dist/dashboard/{issue-service-singleton-0n9hcF71.js → issue-service-singleton-sb2HkB9f.js} +2 -2
  10. package/dist/dashboard/{issue-service-singleton-0n9hcF71.js.map → issue-service-singleton-sb2HkB9f.js.map} +1 -1
  11. package/dist/dashboard/{lifecycle-B6d3AE3n.js → lifecycle-ZTYdrr2O.js} +1 -1
  12. package/dist/dashboard/{merge-agent-DaIEvGJG.js → merge-agent-GLtMEsTu.js} +1 -1
  13. package/dist/dashboard/{merge-agent-CmqR1MFf.js → merge-agent-twroFuAh.js} +2 -2
  14. package/dist/dashboard/{merge-agent-CmqR1MFf.js.map → merge-agent-twroFuAh.js.map} +1 -1
  15. package/dist/dashboard/{projection-cache-Bkzs_90o.js → projection-cache-DQ9zegkK.js} +10 -10
  16. package/dist/dashboard/projection-cache-DQ9zegkK.js.map +1 -0
  17. package/dist/dashboard/public/assets/{dist-D-q87oB4.js → dist-C2sRcZJv.js} +1 -1
  18. package/dist/dashboard/public/assets/{index--G6_upSx.js → index-BCLmEMRf.js} +41 -41
  19. package/dist/dashboard/public/assets/index-BEdq7CFf.css +1 -0
  20. package/dist/dashboard/public/index.html +2 -2
  21. package/dist/dashboard/{review-status-DqJZDthU.js → review-status-CK3eBGyb.js} +1 -1
  22. package/dist/dashboard/{review-status-LQATWF6L.js → review-status-CV55Tl-n.js} +2 -2
  23. package/dist/dashboard/{review-status-LQATWF6L.js.map → review-status-CV55Tl-n.js.map} +1 -1
  24. package/dist/dashboard/server.js +85 -85
  25. package/dist/dashboard/server.js.map +1 -1
  26. package/dist/dashboard/{specialist-context-IX8ZZBxy.js → specialist-context-ColzlmGE.js} +2 -2
  27. package/dist/dashboard/{specialist-context-IX8ZZBxy.js.map → specialist-context-ColzlmGE.js.map} +1 -1
  28. package/dist/dashboard/{specialist-logs-BvOQ3XPt.js → specialist-logs-BhmDpFIq.js} +1 -1
  29. package/dist/dashboard/{specialists-C7Fyhq_j.js → specialists-C6s3U6tX.js} +21 -7
  30. package/dist/dashboard/specialists-C6s3U6tX.js.map +1 -0
  31. package/dist/dashboard/{specialists-B4aDa5xP.js → specialists-Cny632-T.js} +1 -1
  32. package/dist/dashboard/{test-agent-queue-C0WrVdrJ.js → test-agent-queue-tqI4VDsu.js} +3 -3
  33. package/dist/dashboard/{test-agent-queue-C0WrVdrJ.js.map → test-agent-queue-tqI4VDsu.js.map} +1 -1
  34. package/dist/dashboard/workflows-B2ARUpOa.js +2 -0
  35. package/dist/dashboard/{workflows-Cj6tzch6.js → workflows-N1UTipYl.js} +3 -3
  36. package/dist/dashboard/{workflows-Cj6tzch6.js.map → workflows-N1UTipYl.js.map} +1 -1
  37. package/dist/{merge-agent-BCPyotWG.js → merge-agent-VQH9z9t8.js} +2 -2
  38. package/dist/{merge-agent-BCPyotWG.js.map → merge-agent-VQH9z9t8.js.map} +1 -1
  39. package/dist/{review-status-p_HOugvo.js → review-status-2TdtHNcs.js} +1 -1
  40. package/dist/{review-status-BbY22dtx.js → review-status-Bm1bWNEa.js} +2 -2
  41. package/dist/{review-status-BbY22dtx.js.map → review-status-Bm1bWNEa.js.map} +1 -1
  42. package/dist/{specialist-context-CRBBW-z5.js → specialist-context-BdNFsfMG.js} +2 -2
  43. package/dist/{specialist-context-CRBBW-z5.js.map → specialist-context-BdNFsfMG.js.map} +1 -1
  44. package/dist/{specialist-logs-m0UvPm3F.js → specialist-logs-CLztE_bE.js} +1 -1
  45. package/dist/{specialists-ldNesMhg.js → specialists-DEKqgkxp.js} +21 -7
  46. package/dist/specialists-DEKqgkxp.js.map +1 -0
  47. package/dist/{specialists-DXDDLqoY.js → specialists-aUoUVWsN.js} +1 -1
  48. package/package.json +1 -1
  49. package/scripts/record-cost-event.js +15 -0
  50. package/scripts/record-cost-event.js.map +1 -1
  51. package/scripts/record-cost-event.ts +2 -0
  52. package/scripts/work-agent-stop-hook +26 -0
  53. package/dist/dashboard/projection-cache-Bkzs_90o.js.map +0 -1
  54. package/dist/dashboard/public/assets/index-CjpnhB4Q.css +0 -1
  55. package/dist/dashboard/specialists-C7Fyhq_j.js.map +0 -1
  56. package/dist/dashboard/workflows-BsUDQntr.js +0 -2
  57. package/dist/specialists-ldNesMhg.js.map +0 -1
@@ -1,7 +1,7 @@
1
1
  import { n as __esmMin } from "./chunk-DORXReHP.js";
2
2
  import { g as init_paths, h as getPanopticonHome } from "./paths-COdEvoXR.js";
3
3
  import { c as getProject, p as init_projects } from "./projects-Cq3TWdPS.js";
4
- import { mt as getRecentRunLogs, yt as init_specialist_logs } from "./specialists-C7Fyhq_j.js";
4
+ import { mt as getRecentRunLogs, yt as init_specialist_logs } from "./specialists-C6s3U6tX.js";
5
5
  import { a as getModelId, s as init_work_type_router } from "./work-type-router-Cxp8_ur2.js";
6
6
  import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from "fs";
7
7
  import { join } from "path";
@@ -274,4 +274,4 @@ __esmMin((() => {
274
274
  }))();
275
275
  export { deleteContextDigest, generateContextDigest, getContextDigestPath, getContextDirectory, hasContextDigest, loadContextDigest, regenerateContextDigest, scheduleDigestGeneration };
276
276
 
277
- //# sourceMappingURL=specialist-context-IX8ZZBxy.js.map
277
+ //# sourceMappingURL=specialist-context-ColzlmGE.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"specialist-context-IX8ZZBxy.js","names":[],"sources":["../../src/lib/cloister/specialist-context.ts"],"sourcesContent":["/**\n * Specialist Context Management\n *\n * Generates and manages AI-powered context digests from recent specialist runs.\n * These digests seed new specialist sessions with learned patterns and expertise.\n *\n * Directory structure:\n * ~/.panopticon/specialists/{projectKey}/{specialistType}/context/latest-digest.md\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { getPanopticonHome } from '../paths.js';\nimport { getRecentRunLogs, type RunLogEntry } from './specialist-logs.js';\nimport { getProject } from '../projects.js';\nimport { getModelId } from '../work-type-router.js';\n\nconst execAsync = promisify(exec);\n\n/** Get specialists directory (lazy to support test env overrides) */\nfunction getSpecialistsDir(): string {\n return join(getPanopticonHome(), 'specialists');\n}\n\n/**\n * Get the context directory for a project's specialist\n */\nexport function getContextDirectory(projectKey: string, specialistType: string): string {\n return join(getSpecialistsDir(), projectKey, specialistType, 'context');\n}\n\n/**\n * Get the path to the latest context digest file\n */\nexport function getContextDigestPath(projectKey: string, specialistType: string): string {\n const contextDir = getContextDirectory(projectKey, specialistType);\n return join(contextDir, 'latest-digest.md');\n}\n\n/**\n * Ensure context directory exists for a project's specialist\n */\nfunction ensureContextDirectory(projectKey: string, specialistType: string): void {\n const contextDir = getContextDirectory(projectKey, specialistType);\n if (!existsSync(contextDir)) {\n mkdirSync(contextDir, { recursive: true });\n }\n}\n\n/**\n * Load the context digest for a specialist\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns Context digest content or null if not found\n */\nexport function loadContextDigest(projectKey: string, specialistType: string): string | null {\n const digestPath = getContextDigestPath(projectKey, specialistType);\n\n if (!existsSync(digestPath)) {\n return null;\n }\n\n try {\n return readFileSync(digestPath, 'utf-8');\n } catch (error) {\n console.error(`[specialist-context] Failed to load digest for ${projectKey}/${specialistType}:`, error);\n return null;\n }\n}\n\n/**\n * Get the number of recent runs to include in context\n *\n * Reads from project config or uses default.\n *\n * @param projectKey - Project identifier\n * @returns Number of runs to include (default: 5)\n */\nfunction getContextRunsCount(projectKey: string): number {\n const project = getProject(projectKey);\n return project?.specialists?.context_runs ?? 5;\n}\n\n/**\n * Get the model to use for digest generation\n *\n * Reads from project config or uses the same model as the specialist.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns Model ID to use\n */\nfunction getDigestModel(projectKey: string, specialistType: string): string {\n const project = getProject(projectKey);\n\n // Check for explicit digest model in project config\n if (project?.specialists?.digest_model) {\n return project.specialists.digest_model;\n }\n\n // Fall back to specialist's model\n try {\n const workTypeId = `specialist-${specialistType}` as any;\n return getModelId(workTypeId);\n } catch (error) {\n // Default to Sonnet if can't resolve\n return 'claude-sonnet-4-6';\n }\n}\n\n/**\n * Generate a context digest from recent runs using AI\n *\n * Creates an AI-generated summary of recent specialist runs to provide\n * context for the next run. This includes patterns, learnings, and common issues.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @param options - Generation options\n * @returns Generated digest or null if generation failed\n */\nexport async function generateContextDigest(\n projectKey: string,\n specialistType: string,\n options: {\n runCount?: number;\n model?: string;\n force?: boolean; // Generate even if no recent runs\n } = {}\n): Promise<string | null> {\n ensureContextDirectory(projectKey, specialistType);\n\n // Get recent runs\n const runCount = options.runCount ?? getContextRunsCount(projectKey);\n const recentRuns = getRecentRunLogs(projectKey, specialistType, runCount);\n\n if (recentRuns.length === 0 && !options.force) {\n console.log(`[specialist-context] No recent runs for ${projectKey}/${specialistType}, skipping digest generation`);\n return null;\n }\n\n // Build prompt for digest generation\n const prompt = buildDigestPrompt(projectKey, specialistType, recentRuns);\n const model = options.model ?? getDigestModel(projectKey, specialistType);\n\n try {\n console.log(`[specialist-context] Generating digest for ${projectKey}/${specialistType} using ${model}...`);\n\n // Use Claude Code CLI to generate digest\n // Write prompt to temp file to avoid shell escaping issues\n const tempDir = join(getPanopticonHome(), 'tmp');\n if (!existsSync(tempDir)) {\n mkdirSync(tempDir, { recursive: true });\n }\n\n const promptFile = join(tempDir, `digest-prompt-${Date.now()}.md`);\n writeFileSync(promptFile, prompt, 'utf-8');\n\n // Run Claude Code with the prompt (include provider env vars for non-Anthropic models)\n const { getProviderEnvForModel } = await import('../agents.js');\n const providerEnv = getProviderEnvForModel(model);\n const envPrefix = Object.entries(providerEnv).map(([k, v]) => `${k}=\"${v}\"`).join(' ');\n const { stdout, stderr } = await execAsync(\n `${envPrefix ? envPrefix + ' ' : ''}claude --dangerously-skip-permissions --model ${model} \"$(cat '${promptFile}')\"`,\n {\n encoding: 'utf-8',\n maxBuffer: 10 * 1024 * 1024, // 10MB buffer\n timeout: 60000, // 60 second timeout\n }\n );\n\n // Clean up temp file\n try {\n unlinkSync(promptFile);\n } catch {\n // Ignore cleanup errors\n }\n\n if (stderr && !stderr.includes('warning')) {\n console.error(`[specialist-context] Claude stderr:`, stderr);\n }\n\n const digest = stdout.trim();\n\n if (!digest) {\n console.error(`[specialist-context] Empty digest generated`);\n return null;\n }\n\n // Save digest\n const digestPath = getContextDigestPath(projectKey, specialistType);\n writeFileSync(digestPath, digest, 'utf-8');\n\n console.log(`[specialist-context] Generated digest (${digest.length} chars)`);\n return digest;\n } catch (error: any) {\n console.error(`[specialist-context] Failed to generate digest:`, error.message);\n // Degrade gracefully - return null so specialist can continue without context\n return null;\n }\n}\n\n/**\n * Build the prompt for digest generation\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @param recentRuns - Recent run logs\n * @returns Prompt for Claude\n */\nfunction buildDigestPrompt(\n projectKey: string,\n specialistType: string,\n recentRuns: RunLogEntry[]\n): string {\n const project = getProject(projectKey);\n const projectName = project?.name || projectKey;\n\n let prompt = `You are analyzing the recent history of a ${specialistType} specialist for the ${projectName} project.\n\nYour task is to generate a concise context digest that will be provided to the specialist at the start of their next run. This digest should help them understand:\n- Common patterns and practices observed in recent runs\n- Recurring issues or failure modes\n- Successful approaches and best practices\n- Any project-specific context that would be helpful\n\nGenerate a digest in markdown format. Keep it focused and actionable - aim for 200-400 words total.\n\n## Recent Runs\n\n`;\n\n if (recentRuns.length === 0) {\n prompt += `No recent runs available yet. This is the specialist's first run.\\n\\n`;\n prompt += `Generate a brief introduction for the specialist explaining their role and what to expect.\\n`;\n } else {\n recentRuns.forEach((run, index) => {\n prompt += `### Run ${index + 1}: ${run.metadata.issueId} (${run.metadata.status || 'unknown'})\\n`;\n prompt += `Started: ${run.metadata.startedAt}\\n`;\n if (run.metadata.finishedAt) {\n prompt += `Finished: ${run.metadata.finishedAt}\\n`;\n }\n if (run.metadata.duration) {\n const durationSec = Math.floor(run.metadata.duration / 1000);\n const minutes = Math.floor(durationSec / 60);\n const seconds = durationSec % 60;\n prompt += `Duration: ${minutes}m ${seconds}s\\n`;\n }\n if (run.metadata.notes) {\n prompt += `Notes: ${run.metadata.notes}\\n`;\n }\n\n // Include snippets from the log if available\n try {\n const logContent = readFileSync(run.filePath, 'utf-8');\n // Extract key sections (limit to avoid overwhelming the prompt)\n const maxChars = 500;\n const transcriptMatch = logContent.match(/## Session Transcript\\n([\\s\\S]+?)(?=\\n## |$)/);\n if (transcriptMatch) {\n let transcript = transcriptMatch[1].trim();\n if (transcript.length > maxChars) {\n transcript = transcript.substring(0, maxChars) + '... [truncated]';\n }\n prompt += `\\nTranscript excerpt:\\n${transcript}\\n`;\n }\n } catch (error) {\n // If we can't read the log, skip the excerpt\n }\n\n prompt += `\\n`;\n });\n }\n\n prompt += `\\n## Your Task\n\nGenerate a context digest that summarizes the key insights from these runs. Format it as:\n\n# Recent ${specialistType} History for ${projectName}\n\n## Summary\n[2-3 sentence overview of patterns and trends]\n\n## Common Patterns\n[Bulleted list of observed patterns]\n\n## Recent Notable Runs\n[Brief highlights of 2-3 most interesting runs]\n\n## Recommendations\n[Specific guidance for the next run based on this history]\n\nKeep it concise, actionable, and focused on helping the specialist be more effective.`;\n\n return prompt;\n}\n\n/**\n * Regenerate the context digest\n *\n * Forces regeneration even if a digest already exists.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns Generated digest or null if generation failed\n */\nexport async function regenerateContextDigest(\n projectKey: string,\n specialistType: string\n): Promise<string | null> {\n return generateContextDigest(projectKey, specialistType, { force: true });\n}\n\n/**\n * Generate digest after a run completes (async, fire-and-forget)\n *\n * This is called after a specialist finishes a run to update the context\n * for the next run. It runs asynchronously and failures are logged but not thrown.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n */\nexport function scheduleDigestGeneration(projectKey: string, specialistType: string): void {\n // Run async without awaiting\n generateContextDigest(projectKey, specialistType).catch((error) => {\n console.error(\n `[specialist-context] Background digest generation failed for ${projectKey}/${specialistType}:`,\n error\n );\n });\n}\n\n/**\n * Check if a context digest exists\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns True if digest file exists\n */\nexport function hasContextDigest(projectKey: string, specialistType: string): boolean {\n const digestPath = getContextDigestPath(projectKey, specialistType);\n return existsSync(digestPath);\n}\n\n/**\n * Delete the context digest\n *\n * Useful for forcing a fresh start or clearing stale context.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns True if digest was deleted, false if it didn't exist\n */\nexport function deleteContextDigest(projectKey: string, specialistType: string): boolean {\n const digestPath = getContextDigestPath(projectKey, specialistType);\n\n if (!existsSync(digestPath)) {\n return false;\n }\n\n try {\n unlinkSync(digestPath);\n return true;\n } catch (error) {\n console.error(`[specialist-context] Failed to delete digest:`, error);\n return false;\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"specialist-context-ColzlmGE.js","names":[],"sources":["../../src/lib/cloister/specialist-context.ts"],"sourcesContent":["/**\n * Specialist Context Management\n *\n * Generates and manages AI-powered context digests from recent specialist runs.\n * These digests seed new specialist sessions with learned patterns and expertise.\n *\n * Directory structure:\n * ~/.panopticon/specialists/{projectKey}/{specialistType}/context/latest-digest.md\n */\n\nimport { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from 'fs';\nimport { join } from 'path';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport { getPanopticonHome } from '../paths.js';\nimport { getRecentRunLogs, type RunLogEntry } from './specialist-logs.js';\nimport { getProject } from '../projects.js';\nimport { getModelId } from '../work-type-router.js';\n\nconst execAsync = promisify(exec);\n\n/** Get specialists directory (lazy to support test env overrides) */\nfunction getSpecialistsDir(): string {\n return join(getPanopticonHome(), 'specialists');\n}\n\n/**\n * Get the context directory for a project's specialist\n */\nexport function getContextDirectory(projectKey: string, specialistType: string): string {\n return join(getSpecialistsDir(), projectKey, specialistType, 'context');\n}\n\n/**\n * Get the path to the latest context digest file\n */\nexport function getContextDigestPath(projectKey: string, specialistType: string): string {\n const contextDir = getContextDirectory(projectKey, specialistType);\n return join(contextDir, 'latest-digest.md');\n}\n\n/**\n * Ensure context directory exists for a project's specialist\n */\nfunction ensureContextDirectory(projectKey: string, specialistType: string): void {\n const contextDir = getContextDirectory(projectKey, specialistType);\n if (!existsSync(contextDir)) {\n mkdirSync(contextDir, { recursive: true });\n }\n}\n\n/**\n * Load the context digest for a specialist\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns Context digest content or null if not found\n */\nexport function loadContextDigest(projectKey: string, specialistType: string): string | null {\n const digestPath = getContextDigestPath(projectKey, specialistType);\n\n if (!existsSync(digestPath)) {\n return null;\n }\n\n try {\n return readFileSync(digestPath, 'utf-8');\n } catch (error) {\n console.error(`[specialist-context] Failed to load digest for ${projectKey}/${specialistType}:`, error);\n return null;\n }\n}\n\n/**\n * Get the number of recent runs to include in context\n *\n * Reads from project config or uses default.\n *\n * @param projectKey - Project identifier\n * @returns Number of runs to include (default: 5)\n */\nfunction getContextRunsCount(projectKey: string): number {\n const project = getProject(projectKey);\n return project?.specialists?.context_runs ?? 5;\n}\n\n/**\n * Get the model to use for digest generation\n *\n * Reads from project config or uses the same model as the specialist.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns Model ID to use\n */\nfunction getDigestModel(projectKey: string, specialistType: string): string {\n const project = getProject(projectKey);\n\n // Check for explicit digest model in project config\n if (project?.specialists?.digest_model) {\n return project.specialists.digest_model;\n }\n\n // Fall back to specialist's model\n try {\n const workTypeId = `specialist-${specialistType}` as any;\n return getModelId(workTypeId);\n } catch (error) {\n // Default to Sonnet if can't resolve\n return 'claude-sonnet-4-6';\n }\n}\n\n/**\n * Generate a context digest from recent runs using AI\n *\n * Creates an AI-generated summary of recent specialist runs to provide\n * context for the next run. This includes patterns, learnings, and common issues.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @param options - Generation options\n * @returns Generated digest or null if generation failed\n */\nexport async function generateContextDigest(\n projectKey: string,\n specialistType: string,\n options: {\n runCount?: number;\n model?: string;\n force?: boolean; // Generate even if no recent runs\n } = {}\n): Promise<string | null> {\n ensureContextDirectory(projectKey, specialistType);\n\n // Get recent runs\n const runCount = options.runCount ?? getContextRunsCount(projectKey);\n const recentRuns = getRecentRunLogs(projectKey, specialistType, runCount);\n\n if (recentRuns.length === 0 && !options.force) {\n console.log(`[specialist-context] No recent runs for ${projectKey}/${specialistType}, skipping digest generation`);\n return null;\n }\n\n // Build prompt for digest generation\n const prompt = buildDigestPrompt(projectKey, specialistType, recentRuns);\n const model = options.model ?? getDigestModel(projectKey, specialistType);\n\n try {\n console.log(`[specialist-context] Generating digest for ${projectKey}/${specialistType} using ${model}...`);\n\n // Use Claude Code CLI to generate digest\n // Write prompt to temp file to avoid shell escaping issues\n const tempDir = join(getPanopticonHome(), 'tmp');\n if (!existsSync(tempDir)) {\n mkdirSync(tempDir, { recursive: true });\n }\n\n const promptFile = join(tempDir, `digest-prompt-${Date.now()}.md`);\n writeFileSync(promptFile, prompt, 'utf-8');\n\n // Run Claude Code with the prompt (include provider env vars for non-Anthropic models)\n const { getProviderEnvForModel } = await import('../agents.js');\n const providerEnv = getProviderEnvForModel(model);\n const envPrefix = Object.entries(providerEnv).map(([k, v]) => `${k}=\"${v}\"`).join(' ');\n const { stdout, stderr } = await execAsync(\n `${envPrefix ? envPrefix + ' ' : ''}claude --dangerously-skip-permissions --model ${model} \"$(cat '${promptFile}')\"`,\n {\n encoding: 'utf-8',\n maxBuffer: 10 * 1024 * 1024, // 10MB buffer\n timeout: 60000, // 60 second timeout\n }\n );\n\n // Clean up temp file\n try {\n unlinkSync(promptFile);\n } catch {\n // Ignore cleanup errors\n }\n\n if (stderr && !stderr.includes('warning')) {\n console.error(`[specialist-context] Claude stderr:`, stderr);\n }\n\n const digest = stdout.trim();\n\n if (!digest) {\n console.error(`[specialist-context] Empty digest generated`);\n return null;\n }\n\n // Save digest\n const digestPath = getContextDigestPath(projectKey, specialistType);\n writeFileSync(digestPath, digest, 'utf-8');\n\n console.log(`[specialist-context] Generated digest (${digest.length} chars)`);\n return digest;\n } catch (error: any) {\n console.error(`[specialist-context] Failed to generate digest:`, error.message);\n // Degrade gracefully - return null so specialist can continue without context\n return null;\n }\n}\n\n/**\n * Build the prompt for digest generation\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @param recentRuns - Recent run logs\n * @returns Prompt for Claude\n */\nfunction buildDigestPrompt(\n projectKey: string,\n specialistType: string,\n recentRuns: RunLogEntry[]\n): string {\n const project = getProject(projectKey);\n const projectName = project?.name || projectKey;\n\n let prompt = `You are analyzing the recent history of a ${specialistType} specialist for the ${projectName} project.\n\nYour task is to generate a concise context digest that will be provided to the specialist at the start of their next run. This digest should help them understand:\n- Common patterns and practices observed in recent runs\n- Recurring issues or failure modes\n- Successful approaches and best practices\n- Any project-specific context that would be helpful\n\nGenerate a digest in markdown format. Keep it focused and actionable - aim for 200-400 words total.\n\n## Recent Runs\n\n`;\n\n if (recentRuns.length === 0) {\n prompt += `No recent runs available yet. This is the specialist's first run.\\n\\n`;\n prompt += `Generate a brief introduction for the specialist explaining their role and what to expect.\\n`;\n } else {\n recentRuns.forEach((run, index) => {\n prompt += `### Run ${index + 1}: ${run.metadata.issueId} (${run.metadata.status || 'unknown'})\\n`;\n prompt += `Started: ${run.metadata.startedAt}\\n`;\n if (run.metadata.finishedAt) {\n prompt += `Finished: ${run.metadata.finishedAt}\\n`;\n }\n if (run.metadata.duration) {\n const durationSec = Math.floor(run.metadata.duration / 1000);\n const minutes = Math.floor(durationSec / 60);\n const seconds = durationSec % 60;\n prompt += `Duration: ${minutes}m ${seconds}s\\n`;\n }\n if (run.metadata.notes) {\n prompt += `Notes: ${run.metadata.notes}\\n`;\n }\n\n // Include snippets from the log if available\n try {\n const logContent = readFileSync(run.filePath, 'utf-8');\n // Extract key sections (limit to avoid overwhelming the prompt)\n const maxChars = 500;\n const transcriptMatch = logContent.match(/## Session Transcript\\n([\\s\\S]+?)(?=\\n## |$)/);\n if (transcriptMatch) {\n let transcript = transcriptMatch[1].trim();\n if (transcript.length > maxChars) {\n transcript = transcript.substring(0, maxChars) + '... [truncated]';\n }\n prompt += `\\nTranscript excerpt:\\n${transcript}\\n`;\n }\n } catch (error) {\n // If we can't read the log, skip the excerpt\n }\n\n prompt += `\\n`;\n });\n }\n\n prompt += `\\n## Your Task\n\nGenerate a context digest that summarizes the key insights from these runs. Format it as:\n\n# Recent ${specialistType} History for ${projectName}\n\n## Summary\n[2-3 sentence overview of patterns and trends]\n\n## Common Patterns\n[Bulleted list of observed patterns]\n\n## Recent Notable Runs\n[Brief highlights of 2-3 most interesting runs]\n\n## Recommendations\n[Specific guidance for the next run based on this history]\n\nKeep it concise, actionable, and focused on helping the specialist be more effective.`;\n\n return prompt;\n}\n\n/**\n * Regenerate the context digest\n *\n * Forces regeneration even if a digest already exists.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns Generated digest or null if generation failed\n */\nexport async function regenerateContextDigest(\n projectKey: string,\n specialistType: string\n): Promise<string | null> {\n return generateContextDigest(projectKey, specialistType, { force: true });\n}\n\n/**\n * Generate digest after a run completes (async, fire-and-forget)\n *\n * This is called after a specialist finishes a run to update the context\n * for the next run. It runs asynchronously and failures are logged but not thrown.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n */\nexport function scheduleDigestGeneration(projectKey: string, specialistType: string): void {\n // Run async without awaiting\n generateContextDigest(projectKey, specialistType).catch((error) => {\n console.error(\n `[specialist-context] Background digest generation failed for ${projectKey}/${specialistType}:`,\n error\n );\n });\n}\n\n/**\n * Check if a context digest exists\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns True if digest file exists\n */\nexport function hasContextDigest(projectKey: string, specialistType: string): boolean {\n const digestPath = getContextDigestPath(projectKey, specialistType);\n return existsSync(digestPath);\n}\n\n/**\n * Delete the context digest\n *\n * Useful for forcing a fresh start or clearing stale context.\n *\n * @param projectKey - Project identifier\n * @param specialistType - Specialist type\n * @returns True if digest was deleted, false if it didn't exist\n */\nexport function deleteContextDigest(projectKey: string, specialistType: string): boolean {\n const digestPath = getContextDigestPath(projectKey, specialistType);\n\n if (!existsSync(digestPath)) {\n return false;\n }\n\n try {\n unlinkSync(digestPath);\n return true;\n } catch (error) {\n console.error(`[specialist-context] Failed to delete digest:`, error);\n return false;\n }\n}\n"],"mappings":"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"}
@@ -1,3 +1,3 @@
1
- import { St as parseLogMetadata, _t as getRunLogSize, bt as isRunLogActive, ct as checkLogSizeLimit, dt as createRunLog, ft as finalizeRunLog, gt as getRunLogPath, ht as getRunLog, lt as cleanupAllLogs, mt as getRecentRunLogs, ot as MAX_LOG_SIZE, pt as generateRunId, st as appendToRunLog, ut as cleanupOldLogs, vt as getRunsDirectory, xt as listRunLogs, yt as init_specialist_logs } from "./specialists-C7Fyhq_j.js";
1
+ import { St as parseLogMetadata, _t as getRunLogSize, bt as isRunLogActive, ct as checkLogSizeLimit, dt as createRunLog, ft as finalizeRunLog, gt as getRunLogPath, ht as getRunLog, lt as cleanupAllLogs, mt as getRecentRunLogs, ot as MAX_LOG_SIZE, pt as generateRunId, st as appendToRunLog, ut as cleanupOldLogs, vt as getRunsDirectory, xt as listRunLogs, yt as init_specialist_logs } from "./specialists-C6s3U6tX.js";
2
2
  init_specialist_logs();
3
3
  export { MAX_LOG_SIZE, appendToRunLog, checkLogSizeLimit, cleanupAllLogs, cleanupOldLogs, createRunLog, finalizeRunLog, generateRunId, getRecentRunLogs, getRunLog, getRunLogPath, getRunLogSize, getRunsDirectory, isRunLogActive, listRunLogs, parseLogMetadata };
@@ -185,6 +185,20 @@ var init_cost = __esmMin((() => {
185
185
  inputPer1k: 3e-4,
186
186
  outputPer1k: .0012,
187
187
  currency: "USD"
188
+ },
189
+ {
190
+ provider: "custom",
191
+ model: "MiniMax-M2.7",
192
+ inputPer1k: 3e-4,
193
+ outputPer1k: .0012,
194
+ currency: "USD"
195
+ },
196
+ {
197
+ provider: "custom",
198
+ model: "MiniMax-M2.7-highspeed",
199
+ inputPer1k: 3e-4,
200
+ outputPer1k: .0012,
201
+ currency: "USD"
188
202
  }
189
203
  ];
190
204
  join(COSTS_DIR, "budgets.json");
@@ -1344,9 +1358,9 @@ function recordWake(name, sessionId) {
1344
1358
  */
1345
1359
  async function spawnEphemeralSpecialist(projectKey, specialistType, task) {
1346
1360
  ensureProjectSpecialistDir(projectKey, specialistType);
1347
- const { loadContextDigest } = await import("./specialist-context-IX8ZZBxy.js");
1361
+ const { loadContextDigest } = await import("./specialist-context-ColzlmGE.js");
1348
1362
  const contextDigest = loadContextDigest(projectKey, specialistType);
1349
- const { createRunLog } = await import("./specialist-logs-BvOQ3XPt.js");
1363
+ const { createRunLog } = await import("./specialist-logs-BhmDpFIq.js");
1350
1364
  const { runId, filePath: logFilePath } = createRunLog(projectKey, specialistType, task.issueId, contextDigest || void 0);
1351
1365
  setCurrentRun(projectKey, specialistType, runId);
1352
1366
  incrementProjectRunCount(projectKey, specialistType);
@@ -1767,7 +1781,7 @@ async function terminateSpecialist(projectKey, specialistType) {
1767
1781
  console.error(`[specialist] Failed to kill tmux session ${tmuxSession}:`, error);
1768
1782
  }
1769
1783
  if (metadata.currentRun) {
1770
- const { finalizeRunLog } = await import("./specialist-logs-BvOQ3XPt.js");
1784
+ const { finalizeRunLog } = await import("./specialist-logs-BhmDpFIq.js");
1771
1785
  try {
1772
1786
  finalizeRunLog(projectKey, specialistType, metadata.currentRun, {
1773
1787
  status: metadata.lastRunStatus || "incomplete",
@@ -1785,7 +1799,7 @@ async function terminateSpecialist(projectKey, specialistType) {
1785
1799
  state: "suspended",
1786
1800
  lastActivity: (/* @__PURE__ */ new Date()).toISOString()
1787
1801
  });
1788
- const { scheduleDigestGeneration } = await import("./specialist-context-IX8ZZBxy.js");
1802
+ const { scheduleDigestGeneration } = await import("./specialist-context-ColzlmGE.js");
1789
1803
  scheduleDigestGeneration(projectKey, specialistType);
1790
1804
  scheduleLogCleanup(projectKey, specialistType);
1791
1805
  }
@@ -1798,7 +1812,7 @@ async function terminateSpecialist(projectKey, specialistType) {
1798
1812
  function scheduleLogCleanup(projectKey, specialistType) {
1799
1813
  Promise.resolve().then(async () => {
1800
1814
  try {
1801
- const { cleanupOldLogs } = await import("./specialist-logs-BvOQ3XPt.js");
1815
+ const { cleanupOldLogs } = await import("./specialist-logs-BhmDpFIq.js");
1802
1816
  const { getSpecialistRetention } = await import("./projects-DyT3vSy-.js");
1803
1817
  const retention = getSpecialistRetention(projectKey);
1804
1818
  const deleted = cleanupOldLogs(projectKey, specialistType, {
@@ -2432,7 +2446,7 @@ CRITICAL: Do NOT delete the feature branch.`;
2432
2446
  }
2433
2447
  if (totalChangedFiles === 0) {
2434
2448
  console.log(`[specialist] review-agent: stale branch detected for ${task.issueId} — 0 files changed vs main`);
2435
- const { setReviewStatus } = await import("./review-status-DqJZDthU.js");
2449
+ const { setReviewStatus } = await import("./review-status-CK3eBGyb.js");
2436
2450
  setReviewStatus(task.issueId.toUpperCase(), {
2437
2451
  reviewStatus: "passed",
2438
2452
  reviewNotes: "No changes to review — branch identical to main (already merged or stale)"
@@ -3039,4 +3053,4 @@ var init_specialists = __esmMin((() => {
3039
3053
  //#endregion
3040
3054
  export { updateContextTokens as $, initSpecialistsDirectory as A, getSessionFiles as At, loadRegistry as B, getSessionGeneration as C, getUnblockedItems as Ct, getSpecialistStatus as D, readSpecialistHandoffs as Dt, getSpecialistState as E, init_specialist_handoff_logger as Et, isInitialized as F, getPricing as Ft, sendFeedbackToAgent as G, recordWake as H, isRunning as I, init_cost as It, signalSpecialistCompletion as J, setCurrentRun as K, listProjectsWithSpecialists as L, initializeEnabledSpecialists as M, normalizeModelName as Mt, initializeSpecialist as N, parseClaudeSession as Nt, getTmuxSessionName as O, getActiveSessionModel as Ot, isEnabled as P, calculateCost as Pt, terminateSpecialist as Q, listSessionFiles as R, getSessionFilePath as S, parseLogMetadata as St, getSpecialistMetadata as T, getSpecialistHandoffStats as Tt, resumeGracePeriod as U, pauseGracePeriod as V, saveRegistry as W, startGracePeriod as X, spawnEphemeralSpecialist as Y, submitToSpecialistQueue as Z, getGracePeriodState as _, getRunLogSize as _t, completeSpecialistTask as a, wakeSpecialistWithTask as at, getProjectSpecialistDir as b, isRunLogActive as bt, enableSpecialist as c, checkLogSizeLimit as ct, findSessionFile as d, createRunLog as dt, updateProjectSpecialistMetadata as et, getAllProjectSpecialistStatuses as f, finalizeRunLog as ft, getFeedbackStats as g, getRunLogPath as gt, getEnabledSpecialists as h, getRunLog as ht, clearSessionId as i, wakeSpecialistOrQueue as it, init_specialists as j, init_jsonl_parser as jt, incrementProjectRunCount as k, getProjectDirs as kt, ensureProjectSpecialistDir as l, cleanupAllLogs as lt, getAllSpecialists as m, getRecentRunLogs as mt, bumpSessionGeneration as n, updateSpecialistMetadata as nt, countContextTokens as o, MAX_LOG_SIZE as ot, getAllSpecialistStatus as p, generateRunId as pt, setSessionId as q, checkSpecialistQueue as r, wakeSpecialist as rt, disableSpecialist as s, appendToRunLog as st, buildTestAgentPromptContent as t, updateRunStatus as tt, exitGracePeriod as u, cleanupOldLogs as ut, getNextSpecialistTask as v, getRunsDirectory as vt, getSessionId as w, init_task_readiness as wt, getProjectSpecialistMetadata as x, listRunLogs as xt, getPendingFeedback as y, init_specialist_logs as yt, listSpecialistsForProject as z };
3041
3055
 
3042
- //# sourceMappingURL=specialists-C7Fyhq_j.js.map
3056
+ //# sourceMappingURL=specialists-C6s3U6tX.js.map