panopticon-cli 0.5.4 → 0.5.6

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 (119) hide show
  1. package/dist/{agents-HNMF52RM.js → agents-5HWTDR4S.js} +12 -9
  2. package/dist/archive-planning-U3AZAKWI.js +16 -0
  3. package/dist/{chunk-KBHRXV5T.js → chunk-43F4LDZ4.js} +3 -3
  4. package/dist/chunk-6OYUJ4AJ.js +146 -0
  5. package/dist/chunk-6OYUJ4AJ.js.map +1 -0
  6. package/dist/{chunk-MOPGR3CL.js → chunk-AAP4G6U7.js} +1 -1
  7. package/dist/chunk-AAP4G6U7.js.map +1 -0
  8. package/dist/{chunk-4HST45MO.js → chunk-BYWVPPAZ.js} +19 -12
  9. package/dist/chunk-BYWVPPAZ.js.map +1 -0
  10. package/dist/{chunk-CFCUOV3Q.js → chunk-DMRTN432.js} +4 -1
  11. package/dist/chunk-DMRTN432.js.map +1 -0
  12. package/dist/{chunk-HOGYHJ2G.js → chunk-DW3PKGIS.js} +2 -2
  13. package/dist/{chunk-KY2E2Q3T.js → chunk-FUUP55PE.js} +104 -46
  14. package/dist/chunk-FUUP55PE.js.map +1 -0
  15. package/dist/chunk-GUV2EPBG.js +692 -0
  16. package/dist/chunk-GUV2EPBG.js.map +1 -0
  17. package/dist/{chunk-44EOY2ZL.js → chunk-HHL3AWXA.js} +46 -2
  18. package/dist/chunk-HHL3AWXA.js.map +1 -0
  19. package/dist/{chunk-6N2KBSJA.js → chunk-IZIXJYXZ.js} +40 -6
  20. package/dist/chunk-IZIXJYXZ.js.map +1 -0
  21. package/dist/chunk-MJXYTGK5.js +64 -0
  22. package/dist/chunk-MJXYTGK5.js.map +1 -0
  23. package/dist/chunk-OJF4QS3S.js +269 -0
  24. package/dist/chunk-OJF4QS3S.js.map +1 -0
  25. package/dist/{chunk-FQ66DECN.js → chunk-QAJAJBFW.js} +1 -1
  26. package/dist/chunk-QAJAJBFW.js.map +1 -0
  27. package/dist/chunk-R4KPLLRB.js +36 -0
  28. package/dist/chunk-R4KPLLRB.js.map +1 -0
  29. package/dist/{chunk-DFNVHK3N.js → chunk-SUM2WVPF.js} +4 -4
  30. package/dist/{chunk-T7BBPDEJ.js → chunk-UKSGE6RH.js} +45 -15
  31. package/dist/chunk-UKSGE6RH.js.map +1 -0
  32. package/dist/chunk-W2OTF6OS.js +201 -0
  33. package/dist/chunk-W2OTF6OS.js.map +1 -0
  34. package/dist/chunk-WEQW3EAT.js +78 -0
  35. package/dist/chunk-WEQW3EAT.js.map +1 -0
  36. package/dist/{chunk-ID4OYXVH.js → chunk-WJJ3ZIQ6.js} +112 -45
  37. package/dist/chunk-WJJ3ZIQ6.js.map +1 -0
  38. package/dist/chunk-YAAT66RT.js +70 -0
  39. package/dist/chunk-YAAT66RT.js.map +1 -0
  40. package/dist/{chunk-RLZQB7HS.js → chunk-ZMJFEHGF.js} +13 -1
  41. package/dist/chunk-ZMJFEHGF.js.map +1 -0
  42. package/dist/{chunk-HRU7S4TA.js → chunk-ZN5RHWGR.js} +18 -208
  43. package/dist/{chunk-HRU7S4TA.js.map → chunk-ZN5RHWGR.js.map} +1 -1
  44. package/dist/{chunk-ZTYHZMEC.js → chunk-ZWZNEA26.js} +2 -2
  45. package/dist/clean-planning-7Z5YY64X.js +9 -0
  46. package/dist/cli/index.js +1301 -2142
  47. package/dist/cli/index.js.map +1 -1
  48. package/dist/close-issue-CTZK777I.js +9 -0
  49. package/dist/compact-beads-72SHALOL.js +9 -0
  50. package/dist/{config-4CJNUE3O.js → config-FFTMBVHM.js} +2 -2
  51. package/dist/dashboard/public/assets/{index-DSvt5pPn.css → index-Bx4NCn9A.css} +1 -1
  52. package/dist/dashboard/public/assets/index-Db9NOz4z.js +756 -0
  53. package/dist/dashboard/public/index.html +3 -2
  54. package/dist/dashboard/server.js +34714 -34296
  55. package/dist/{feedback-writer-T43PI5S2.js → feedback-writer-T2WCT6EZ.js} +2 -2
  56. package/dist/{hume-CKJJ3OUU.js → hume-GVTB5BKW.js} +3 -3
  57. package/dist/index.d.ts +24 -16
  58. package/dist/index.js +4 -4
  59. package/dist/label-cleanup-4HJVX6NP.js +103 -0
  60. package/dist/label-cleanup-4HJVX6NP.js.map +1 -0
  61. package/dist/merge-agent-WM7ZKUET.js +1725 -0
  62. package/dist/merge-agent-WM7ZKUET.js.map +1 -0
  63. package/dist/{projects-KVM3MN3Y.js → projects-3CRF57ZU.js} +2 -2
  64. package/dist/{rally-RKFSWC7E.js → rally-LBY24P4C.js} +2 -2
  65. package/dist/{remote-agents-ULPD6C5U.js → remote-agents-3NZPSHYG.js} +2 -3
  66. package/dist/{remote-workspace-XX6ARE6I.js → remote-workspace-M4IULGFZ.js} +24 -49
  67. package/dist/remote-workspace-M4IULGFZ.js.map +1 -0
  68. package/dist/{review-status-XKUKZF6J.js → review-status-J2YJGL3E.js} +2 -2
  69. package/dist/{specialist-context-C66TEMXS.js → specialist-context-74RQF5SR.js} +7 -5
  70. package/dist/{specialist-context-C66TEMXS.js.map → specialist-context-74RQF5SR.js.map} +1 -1
  71. package/dist/{specialist-logs-CJKXM3SR.js → specialist-logs-T5GW7CSU.js} +6 -4
  72. package/dist/{specialists-NXYD4Z62.js → specialists-HTYYFXHQ.js} +6 -4
  73. package/dist/specialists-HTYYFXHQ.js.map +1 -0
  74. package/dist/tmux-X2I5SAIJ.js +31 -0
  75. package/dist/tmux-X2I5SAIJ.js.map +1 -0
  76. package/dist/{traefik-5GL3Q7DJ.js → traefik-QXLZ4PO2.js} +4 -4
  77. package/dist/traefik-QXLZ4PO2.js.map +1 -0
  78. package/dist/{tunnel-BKC7KLBX.js → tunnel-7IOSRZVH.js} +3 -3
  79. package/dist/tunnel-7IOSRZVH.js.map +1 -0
  80. package/dist/{workspace-manager-ALBR62AS.js → workspace-manager-G6TTBPC3.js} +6 -6
  81. package/dist/workspace-manager-G6TTBPC3.js.map +1 -0
  82. package/package.json +2 -2
  83. package/scripts/build-cost-script.mjs +17 -0
  84. package/scripts/heartbeat-hook +28 -8
  85. package/scripts/record-cost-event.js +46 -7
  86. package/scripts/record-cost-event.ts +2 -1
  87. package/dist/chunk-44EOY2ZL.js.map +0 -1
  88. package/dist/chunk-4HST45MO.js.map +0 -1
  89. package/dist/chunk-565HZ6VV.js +0 -159
  90. package/dist/chunk-565HZ6VV.js.map +0 -1
  91. package/dist/chunk-6N2KBSJA.js.map +0 -1
  92. package/dist/chunk-CFCUOV3Q.js.map +0 -1
  93. package/dist/chunk-FQ66DECN.js.map +0 -1
  94. package/dist/chunk-ID4OYXVH.js.map +0 -1
  95. package/dist/chunk-KY2E2Q3T.js.map +0 -1
  96. package/dist/chunk-MOPGR3CL.js.map +0 -1
  97. package/dist/chunk-RLZQB7HS.js.map +0 -1
  98. package/dist/chunk-T7BBPDEJ.js.map +0 -1
  99. package/dist/chunk-ZDNQFWR5.js +0 -650
  100. package/dist/chunk-ZDNQFWR5.js.map +0 -1
  101. package/dist/dashboard/public/assets/index-DA6pnizT.js +0 -767
  102. package/dist/remote-workspace-XX6ARE6I.js.map +0 -1
  103. /package/dist/{agents-HNMF52RM.js.map → agents-5HWTDR4S.js.map} +0 -0
  104. /package/dist/{config-4CJNUE3O.js.map → archive-planning-U3AZAKWI.js.map} +0 -0
  105. /package/dist/{chunk-KBHRXV5T.js.map → chunk-43F4LDZ4.js.map} +0 -0
  106. /package/dist/{chunk-HOGYHJ2G.js.map → chunk-DW3PKGIS.js.map} +0 -0
  107. /package/dist/{chunk-DFNVHK3N.js.map → chunk-SUM2WVPF.js.map} +0 -0
  108. /package/dist/{chunk-ZTYHZMEC.js.map → chunk-ZWZNEA26.js.map} +0 -0
  109. /package/dist/{hume-CKJJ3OUU.js.map → clean-planning-7Z5YY64X.js.map} +0 -0
  110. /package/dist/{projects-KVM3MN3Y.js.map → close-issue-CTZK777I.js.map} +0 -0
  111. /package/dist/{rally-RKFSWC7E.js.map → compact-beads-72SHALOL.js.map} +0 -0
  112. /package/dist/{remote-agents-ULPD6C5U.js.map → config-FFTMBVHM.js.map} +0 -0
  113. /package/dist/{feedback-writer-T43PI5S2.js.map → feedback-writer-T2WCT6EZ.js.map} +0 -0
  114. /package/dist/{review-status-XKUKZF6J.js.map → hume-GVTB5BKW.js.map} +0 -0
  115. /package/dist/{specialist-logs-CJKXM3SR.js.map → projects-3CRF57ZU.js.map} +0 -0
  116. /package/dist/{specialists-NXYD4Z62.js.map → rally-LBY24P4C.js.map} +0 -0
  117. /package/dist/{traefik-5GL3Q7DJ.js.map → remote-agents-3NZPSHYG.js.map} +0 -0
  118. /package/dist/{tunnel-BKC7KLBX.js.map → review-status-J2YJGL3E.js.map} +0 -0
  119. /package/dist/{workspace-manager-ALBR62AS.js.map → specialist-logs-T5GW7CSU.js.map} +0 -0
@@ -137,6 +137,18 @@ function resolveProjectFromIssue(issueId, labels = []) {
137
137
  linearTeam: projectConfig.linear_team
138
138
  };
139
139
  }
140
+ if (!projectConfig.linear_team && projectConfig.github_repo) {
141
+ const derivedPrefix = key.toUpperCase().replace(/-/g, "");
142
+ if (derivedPrefix === teamPrefix) {
143
+ const resolvedPath = resolveProjectPath(projectConfig, labels);
144
+ return {
145
+ projectKey: key,
146
+ projectName: projectConfig.name,
147
+ projectPath: resolvedPath,
148
+ linearTeam: void 0
149
+ };
150
+ }
151
+ }
140
152
  }
141
153
  return null;
142
154
  }
@@ -270,4 +282,4 @@ export {
270
282
  projects_exports,
271
283
  init_projects
272
284
  };
273
- //# sourceMappingURL=chunk-RLZQB7HS.js.map
285
+ //# sourceMappingURL=chunk-ZMJFEHGF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/lib/projects.ts"],"sourcesContent":["/**\n * Project Registry - Multi-project support for Panopticon\n *\n * Maps Linear team prefixes and labels to project paths for workspace creation.\n */\n\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join, resolve } from 'path';\nimport { parse as parseYaml, stringify as stringifyYaml } from 'yaml';\nimport { PANOPTICON_HOME } from './paths.js';\nimport type { QualityGateConfig } from './workspace-config.js';\n\nexport const PROJECTS_CONFIG_FILE = join(PANOPTICON_HOME, 'projects.yaml');\n\n/**\n * Issue routing rule - routes issues with certain labels to specific paths\n */\nexport interface IssueRoutingRule {\n labels?: string[];\n default?: boolean;\n path: string;\n}\n\n/**\n * Workspace configuration (imported from workspace-config.ts for full details)\n */\nexport interface WorkspaceConfig {\n type?: 'polyrepo' | 'monorepo';\n workspaces_dir?: string;\n repos?: Array<{ name: string; path: string; branch_prefix?: string }>;\n dns?: { domain: string; entries: string[]; sync_method?: 'wsl2hosts' | 'hosts_file' | 'dnsmasq' };\n ports?: Record<string, { range: [number, number] }>;\n docker?: { traefik?: string; compose_template?: string };\n database?: { seed_file?: string; container_name?: string; [key: string]: any };\n agent?: { template_dir: string; templates?: Array<{ source: string; target: string }>; copy_dirs?: string[]; symlinks?: string[] };\n env?: { template?: string; secrets_file?: string };\n services?: Array<{ name: string; path: string; start_command: string; docker_command?: string; health_url?: string; port?: number }>;\n}\n\n/**\n * Test configuration\n */\nexport interface TestConfig {\n type: string;\n path: string;\n command: string;\n container?: boolean;\n container_name?: string;\n env?: Record<string, string>;\n}\n\n/**\n * Specialist configuration for per-project specialists\n */\nexport interface SpecialistConfig {\n /** Number of recent runs to include in context digest (default: 5) */\n context_runs?: number;\n /** Model to use for generating context digests (null = same as specialist) */\n digest_model?: string | null;\n /** Log retention policy */\n retention?: {\n /** Maximum days to keep logs */\n max_days: number;\n /** Maximum number of runs to keep (whichever is more permissive) */\n max_runs: number;\n };\n /** Per-specialist prompt overrides */\n prompts?: {\n 'review-agent'?: string;\n 'test-agent'?: string;\n 'merge-agent'?: string;\n };\n}\n\n/**\n * Project configuration\n */\nexport interface ProjectConfig {\n name: string;\n path: string;\n linear_team?: string;\n github_repo?: string; // e.g. \"owner/repo\"\n gitlab_repo?: string; // e.g. \"group/repo\"\n issue_routing?: IssueRoutingRule[];\n /** Workspace configuration */\n workspace?: WorkspaceConfig;\n /** Test configuration by name */\n tests?: Record<string, TestConfig>;\n /** Custom command to create workspaces (e.g., infra/new-feature for MYN) */\n workspace_command?: string;\n /** Custom command to remove workspaces */\n workspace_remove_command?: string;\n /** Rally project OID (e.g., \"/project/822404704163\") for per-project Rally scoping */\n rally_project?: string;\n /** Specialist agent configuration */\n specialists?: SpecialistConfig;\n /** Quality gates run by merge-agent before pushing (lint, typecheck, prod build, etc.) */\n quality_gates?: Record<string, QualityGateConfig>;\n /**\n * Path to the repo where per-project cost WAL files live.\n * Defaults to `path` (the project repo itself).\n * For polyrepo setups, point this at the docs/shared repo.\n */\n events_repo?: string;\n /**\n * Subdirectory within events_repo where cost JSONL files are stored.\n * Defaults to \".panopticon/events\".\n */\n events_path?: string;\n}\n\n/**\n * Full projects configuration file\n */\nexport interface ProjectsConfig {\n projects: Record<string, ProjectConfig>;\n}\n\n/**\n * Resolved project info for workspace creation\n */\nexport interface ResolvedProject {\n projectKey: string;\n projectName: string;\n projectPath: string;\n linearTeam?: string;\n}\n\n/**\n * Load projects configuration from ~/.panopticon/projects.yaml\n */\nexport function loadProjectsConfig(): ProjectsConfig {\n if (!existsSync(PROJECTS_CONFIG_FILE)) {\n return { projects: {} };\n }\n\n try {\n const content = readFileSync(PROJECTS_CONFIG_FILE, 'utf-8');\n const config = parseYaml(content) as ProjectsConfig;\n return config || { projects: {} };\n } catch (error: any) {\n console.error(`Failed to parse projects.yaml: ${error.message}`);\n return { projects: {} };\n }\n}\n\n/**\n * Save projects configuration\n */\nexport function saveProjectsConfig(config: ProjectsConfig): void {\n const dir = PANOPTICON_HOME;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const yaml = stringifyYaml(config, { indent: 2 });\n writeFileSync(PROJECTS_CONFIG_FILE, yaml, 'utf-8');\n}\n\n/**\n * Get a list of all registered projects\n */\nexport function listProjects(): Array<{ key: string; config: ProjectConfig }> {\n const config = loadProjectsConfig();\n return Object.entries(config.projects).map(([key, projectConfig]) => ({\n key,\n config: projectConfig,\n }));\n}\n\n/**\n * Add or update a project in the registry\n */\nexport function registerProject(key: string, projectConfig: ProjectConfig): void {\n const config = loadProjectsConfig();\n config.projects[key] = projectConfig;\n saveProjectsConfig(config);\n}\n\n/**\n * Remove a project from the registry\n */\nexport function unregisterProject(key: string): boolean {\n const config = loadProjectsConfig();\n if (config.projects[key]) {\n delete config.projects[key];\n saveProjectsConfig(config);\n return true;\n }\n return false;\n}\n\n/**\n * Extract Linear team prefix from an issue ID\n * E.g., \"MIN-123\" -> \"MIN\", \"PAN-456\" -> \"PAN\"\n */\nexport function extractTeamPrefix(issueId: string): string | null {\n const match = issueId.match(/^([A-Z]+)-\\d+$/i);\n return match ? match[1].toUpperCase() : null;\n}\n\n/**\n * Find project by Linear team prefix\n */\nexport function findProjectByTeam(teamPrefix: string): ProjectConfig | null {\n const config = loadProjectsConfig();\n\n for (const [, projectConfig] of Object.entries(config.projects)) {\n if (projectConfig.linear_team?.toUpperCase() === teamPrefix.toUpperCase()) {\n return projectConfig;\n }\n }\n\n return null;\n}\n\n/**\n * Find project by workspace path.\n * Matches any project whose root path is an ancestor of the given path.\n * Used to resolve the tracker (GitHub/GitLab) from a workspace directory.\n */\nexport function findProjectByPath(workspacePath: string): ProjectConfig | null {\n const config = loadProjectsConfig();\n const normalizedTarget = resolve(workspacePath);\n\n for (const [, projectConfig] of Object.entries(config.projects)) {\n const normalizedProject = resolve(projectConfig.path);\n if (normalizedTarget === normalizedProject || normalizedTarget.startsWith(normalizedProject + '/')) {\n return projectConfig;\n }\n }\n\n return null;\n}\n\n\n/**\n * Resolve the correct project path for an issue based on labels\n *\n * @param project - The project config\n * @param labels - Array of label names from the Linear issue\n * @returns The resolved path (may differ from project.path based on routing rules)\n */\nexport function resolveProjectPath(project: ProjectConfig, labels: string[] = []): string {\n if (!project.issue_routing || project.issue_routing.length === 0) {\n return project.path;\n }\n\n // Normalize labels to lowercase for comparison\n const normalizedLabels = labels.map(l => l.toLowerCase());\n\n // First, check label-based routing rules\n for (const rule of project.issue_routing) {\n if (rule.labels && rule.labels.length > 0) {\n const ruleLabels = rule.labels.map(l => l.toLowerCase());\n const hasMatch = ruleLabels.some(label => normalizedLabels.includes(label));\n if (hasMatch) {\n return rule.path;\n }\n }\n }\n\n // Then, find default rule\n for (const rule of project.issue_routing) {\n if (rule.default) {\n return rule.path;\n }\n }\n\n // Fall back to project path\n return project.path;\n}\n\n/**\n * Resolve project from an issue ID (and optional labels)\n *\n * @param issueId - Linear issue ID (e.g., \"MIN-123\")\n * @param labels - Optional array of label names\n * @returns Resolved project info or null if not found\n */\nexport function resolveProjectFromIssue(\n issueId: string,\n labels: string[] = []\n): ResolvedProject | null {\n const teamPrefix = extractTeamPrefix(issueId);\n if (!teamPrefix) {\n return null;\n }\n\n const config = loadProjectsConfig();\n\n // Find project by team prefix (check linear_team first, then derive from project key)\n for (const [key, projectConfig] of Object.entries(config.projects)) {\n if (projectConfig.linear_team?.toUpperCase() === teamPrefix) {\n const resolvedPath = resolveProjectPath(projectConfig, labels);\n return {\n projectKey: key,\n projectName: projectConfig.name,\n projectPath: resolvedPath,\n linearTeam: projectConfig.linear_team,\n };\n }\n // For GitHub-only projects without linear_team, derive prefix from project key\n if (!projectConfig.linear_team && projectConfig.github_repo) {\n const derivedPrefix = key.toUpperCase().replace(/-/g, '');\n if (derivedPrefix === teamPrefix) {\n const resolvedPath = resolveProjectPath(projectConfig, labels);\n return {\n projectKey: key,\n projectName: projectConfig.name,\n projectPath: resolvedPath,\n linearTeam: undefined,\n };\n }\n }\n }\n\n return null;\n}\n\n/**\n * Get a project by key\n */\nexport function getProject(key: string): ProjectConfig | null {\n const config = loadProjectsConfig();\n return config.projects[key] || null;\n}\n\n/**\n * Check if projects.yaml exists and has any projects\n */\nexport function hasProjects(): boolean {\n const config = loadProjectsConfig();\n return Object.keys(config.projects).length > 0;\n}\n\n/**\n * Create a default projects.yaml with example structure\n */\nexport function createDefaultProjectsConfig(): ProjectsConfig {\n const defaultConfig: ProjectsConfig = {\n projects: {\n // Example project - commented out in actual file\n },\n };\n\n return defaultConfig;\n}\n\n/**\n * Initialize projects.yaml with example configuration\n */\nexport function initializeProjectsConfig(): void {\n if (existsSync(PROJECTS_CONFIG_FILE)) {\n console.log(`Projects config already exists at ${PROJECTS_CONFIG_FILE}`);\n return;\n }\n\n const exampleYaml = `# Panopticon Project Registry\n# Maps Linear teams to project paths for workspace creation\n\nprojects:\n # Example: Mind Your Now project\n # myn:\n # name: \"Mind Your Now\"\n # path: /home/user/projects/myn\n # linear_team: MIN\n # issue_routing:\n # # Route docs/marketing issues to docs repo\n # - labels: [docs, marketing, seo, landing-pages]\n # path: /home/user/projects/myn/docs\n # # Default: main repo\n # - default: true\n # path: /home/user/projects/myn\n # specialists:\n # context_runs: 5\n # digest_model: null # Use same model as specialist\n # retention:\n # max_days: 30\n # max_runs: 50\n # prompts:\n # review-agent: |\n # Pay special attention to:\n # - Database migration safety\n # - API backward compatibility\n\n # Example: Panopticon itself\n # panopticon:\n # name: \"Panopticon\"\n # path: /home/user/projects/panopticon\n # linear_team: PAN\n`;\n\n const dir = PANOPTICON_HOME;\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n writeFileSync(PROJECTS_CONFIG_FILE, exampleYaml, 'utf-8');\n console.log(`Created example projects config at ${PROJECTS_CONFIG_FILE}`);\n}\n\n/**\n * Default specialist configuration values\n */\nconst DEFAULT_SPECIALIST_CONFIG: Required<SpecialistConfig> = {\n context_runs: 5,\n digest_model: null,\n retention: {\n max_days: 30,\n max_runs: 50,\n },\n prompts: {},\n};\n\n/**\n * Get specialist configuration for a project with defaults\n *\n * @param projectKey - Project key\n * @returns Specialist config with defaults applied\n */\nexport function getSpecialistConfig(projectKey: string): Required<SpecialistConfig> {\n const project = getProject(projectKey);\n\n if (!project || !project.specialists) {\n return DEFAULT_SPECIALIST_CONFIG;\n }\n\n return {\n context_runs: project.specialists.context_runs ?? DEFAULT_SPECIALIST_CONFIG.context_runs,\n digest_model: project.specialists.digest_model ?? DEFAULT_SPECIALIST_CONFIG.digest_model,\n retention: {\n max_days: project.specialists.retention?.max_days ?? DEFAULT_SPECIALIST_CONFIG.retention.max_days,\n max_runs: project.specialists.retention?.max_runs ?? DEFAULT_SPECIALIST_CONFIG.retention.max_runs,\n },\n prompts: project.specialists.prompts ?? DEFAULT_SPECIALIST_CONFIG.prompts,\n };\n}\n\n/**\n * Get retention policy for a project's specialists\n *\n * @param projectKey - Project key\n * @returns Retention policy\n */\nexport function getSpecialistRetention(projectKey: string): { max_days: number; max_runs: number } {\n const config = getSpecialistConfig(projectKey);\n return config.retention;\n}\n\n/**\n * Find all projects that have a rally_project configured.\n * Returns array of { key, config } for projects with Rally project OIDs.\n */\nexport function findProjectsByRallyProject(): Array<{ key: string; config: ProjectConfig }> {\n const config = loadProjectsConfig();\n return Object.entries(config.projects)\n .filter(([, projectConfig]) => !!projectConfig.rally_project)\n .map(([key, projectConfig]) => ({ key, config: projectConfig }));\n}\n\n/**\n * Get custom prompt override for a specialist (if configured)\n *\n * @param projectKey - Project key\n * @param specialistType - Specialist type\n * @returns Custom prompt or null if not configured\n */\nexport function getSpecialistPromptOverride(\n projectKey: string,\n specialistType: 'review-agent' | 'test-agent' | 'merge-agent'\n): string | null {\n const config = getSpecialistConfig(projectKey);\n return config.prompts[specialistType] || null;\n}\n"],"mappings":"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","names":[]}
@@ -7,7 +7,6 @@ import {
7
7
  } from "./chunk-ZP6EWSZV.js";
8
8
  import {
9
9
  AGENTS_DIR,
10
- PANOPTICON_HOME,
11
10
  init_paths
12
11
  } from "./chunk-ZTFNYOC7.js";
13
12
  import {
@@ -15,213 +14,35 @@ import {
15
14
  init_esm_shims
16
15
  } from "./chunk-ZHC57RCV.js";
17
16
 
18
- // src/lib/tmux.ts
19
- import { execSync, exec } from "child_process";
20
- import { promisify } from "util";
21
- import { writeFileSync, chmodSync, appendFileSync, mkdirSync, existsSync, unlinkSync } from "fs";
22
- import { join } from "path";
23
- function ensureLogDir() {
24
- const logDir = join(PANOPTICON_HOME, "logs");
25
- if (!existsSync(logDir)) {
26
- mkdirSync(logDir, { recursive: true });
27
- }
28
- }
29
- function logSendKeys(sessionName, keys, caller) {
30
- try {
31
- ensureLogDir();
32
- const stack = new Error().stack || "";
33
- const stackLines = stack.split("\n").slice(3, 6);
34
- const callerInfo = caller || stackLines.map((l) => l.trim()).join(" <- ");
35
- const entry = {
36
- timestamp: (/* @__PURE__ */ new Date()).toISOString(),
37
- sessionName,
38
- keysLength: keys.length,
39
- keysPreview: keys.length > 200 ? keys.slice(0, 200) + "..." : keys,
40
- caller: callerInfo,
41
- pid: process.pid
42
- };
43
- appendFileSync(SENDKEYS_LOG_FILE, JSON.stringify(entry) + "\n", "utf-8");
44
- } catch {
45
- }
46
- }
47
- function listSessions() {
48
- try {
49
- const output = execSync('tmux list-sessions -F "#{session_name}|#{session_created}|#{session_attached}|#{session_windows}"', {
50
- encoding: "utf8"
51
- });
52
- return output.trim().split("\n").filter(Boolean).map((line) => {
53
- const [name, created, attached, windows] = line.split("|");
54
- return {
55
- name,
56
- created: new Date(parseInt(created) * 1e3),
57
- attached: attached === "1",
58
- windows: parseInt(windows)
59
- };
60
- });
61
- } catch {
62
- return [];
63
- }
64
- }
65
- function sessionExists(name) {
66
- try {
67
- execSync(`tmux has-session -t ${name} 2>/dev/null`);
68
- return true;
69
- } catch {
70
- return false;
71
- }
72
- }
73
- function createSession(name, cwd, initialCommand, options) {
74
- const escapedCwd = cwd.replace(/"/g, '\\"');
75
- let envFlags = "";
76
- if (options?.env) {
77
- for (const [key, value] of Object.entries(options.env)) {
78
- envFlags += ` -e ${key}="${value.replace(/"/g, '\\"')}"`;
79
- }
80
- }
81
- if (initialCommand && (initialCommand.includes("`") || initialCommand.includes("\n") || initialCommand.length > 500)) {
82
- execSync(`tmux new-session -d -s ${name} -c "${escapedCwd}"${envFlags}`);
83
- execSync("sleep 0.5");
84
- const tmpFile = `/tmp/pan-cmd-${name}.sh`;
85
- writeFileSync(tmpFile, initialCommand);
86
- chmodSync(tmpFile, "755");
87
- execSync(`tmux send-keys -t ${name} "bash ${tmpFile}"`);
88
- execSync(`tmux send-keys -t ${name} C-m`);
89
- } else if (initialCommand) {
90
- const cmd = `tmux new-session -d -s ${name} -c "${escapedCwd}"${envFlags} "${initialCommand.replace(/"/g, '\\"')}"`;
91
- execSync(cmd);
92
- } else {
93
- execSync(`tmux new-session -d -s ${name} -c "${escapedCwd}"${envFlags}`);
94
- }
95
- }
96
- function killSession(name) {
97
- execSync(`tmux kill-session -t ${name}`);
98
- }
99
- async function sendKeysAsync(sessionName, keys, caller) {
100
- logSendKeys(sessionName, keys, caller);
101
- const bufferName = `pan-${process.pid}-${Date.now()}`;
102
- const tmpFile = `/tmp/pan-sendkeys-${bufferName}.txt`;
103
- try {
104
- writeFileSync(tmpFile, keys);
105
- await execAsync(`tmux load-buffer -b ${bufferName} ${tmpFile}`);
106
- await execAsync(`tmux paste-buffer -b ${bufferName} -t ${sessionName} -d`);
107
- await new Promise((r) => setTimeout(r, 300));
108
- await execAsync(`tmux send-keys -t ${sessionName} C-m`);
109
- } finally {
110
- try {
111
- unlinkSync(tmpFile);
112
- } catch {
113
- }
114
- try {
115
- await execAsync(`tmux delete-buffer -b ${bufferName} 2>/dev/null`);
116
- } catch {
117
- }
118
- }
119
- }
120
- function sendKeys(sessionName, keys, caller) {
121
- logSendKeys(sessionName, keys, caller);
122
- const tmpFile = `/tmp/pan-sendkeys-${process.pid}-${Date.now()}.txt`;
123
- try {
124
- writeFileSync(tmpFile, keys);
125
- execSync(`tmux load-buffer ${tmpFile}`);
126
- execSync(`tmux paste-buffer -t ${sessionName}`);
127
- execSync(`sleep 0.3`);
128
- execSync(`tmux send-keys -t ${sessionName} C-m`);
129
- } finally {
130
- try {
131
- unlinkSync(tmpFile);
132
- } catch {
133
- }
134
- }
135
- }
136
- function capturePane(sessionName, lines = 50) {
137
- try {
138
- return execSync(`tmux capture-pane -t ${sessionName} -p -S -${lines}`, {
139
- encoding: "utf8"
140
- });
141
- } catch {
142
- return "";
143
- }
144
- }
145
- async function capturePaneAsync(sessionName, lines = 50) {
146
- try {
147
- const { stdout } = await execAsync(`tmux capture-pane -t ${sessionName} -p -S -${lines}`, {
148
- encoding: "utf-8"
149
- });
150
- return stdout;
151
- } catch {
152
- return "";
153
- }
154
- }
155
- async function waitForClaudePrompt(sessionName, timeoutMs = 15e3) {
156
- const start = Date.now();
157
- const POLL = 500;
158
- while (Date.now() - start < timeoutMs) {
159
- const output = await capturePaneAsync(sessionName, 10);
160
- const lines = output.split("\n").filter((l) => l.trim());
161
- const lastLine = lines[lines.length - 1] || "";
162
- if (lastLine.includes("\u276F")) return true;
163
- await new Promise((r) => setTimeout(r, POLL));
164
- }
165
- return false;
166
- }
167
- async function confirmDelivery(sessionName, outputBefore, timeoutMs = 1e4) {
168
- const start = Date.now();
169
- const POLL = 1e3;
170
- const beforeLineCount = outputBefore.split("\n").filter((l) => l.trim()).length;
171
- while (Date.now() - start < timeoutMs) {
172
- await new Promise((r) => setTimeout(r, POLL));
173
- const after = await capturePaneAsync(sessionName, 50);
174
- const afterLines = after.split("\n").filter((l) => l.trim());
175
- const afterLineCount = afterLines.length;
176
- if (afterLineCount > beforeLineCount + 1) return true;
177
- const newOutput = afterLines.slice(beforeLineCount).join("\n");
178
- if (newOutput.includes("\u25CF") || newOutput.includes("\u23BF") || newOutput.includes("Read") || newOutput.includes("\u273B") || newOutput.includes("\xB7") || newOutput.includes("\u2736") || newOutput.includes("\u273D") || newOutput.includes("\u2722") || newOutput.includes("Generating") || newOutput.includes("thinking") || newOutput.includes("thought for")) return true;
179
- }
180
- return false;
181
- }
182
- function getAgentSessions() {
183
- return listSessions().filter((s) => s.name.startsWith("agent-"));
184
- }
185
- var SENDKEYS_LOG_FILE, execAsync;
186
- var init_tmux = __esm({
187
- "src/lib/tmux.ts"() {
188
- "use strict";
189
- init_esm_shims();
190
- init_paths();
191
- SENDKEYS_LOG_FILE = join(PANOPTICON_HOME, "logs", "sendkeys.jsonl");
192
- execAsync = promisify(exec);
193
- }
194
- });
195
-
196
17
  // src/lib/hooks.ts
197
- import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2, readdirSync, unlinkSync as unlinkSync2 } from "fs";
198
- import { join as join2 } from "path";
18
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync } from "fs";
19
+ import { join } from "path";
199
20
  function getHookDir(agentId) {
200
- return join2(AGENTS_DIR, agentId);
21
+ return join(AGENTS_DIR, agentId);
201
22
  }
202
23
  function getHookFile(agentId) {
203
- return join2(getHookDir(agentId), "hook.json");
24
+ return join(getHookDir(agentId), "hook.json");
204
25
  }
205
26
  function getMailDir(agentId) {
206
- return join2(getHookDir(agentId), "mail");
27
+ return join(getHookDir(agentId), "mail");
207
28
  }
208
29
  function initHook(agentId) {
209
30
  const hookDir = getHookDir(agentId);
210
31
  const mailDir = getMailDir(agentId);
211
- mkdirSync2(hookDir, { recursive: true });
212
- mkdirSync2(mailDir, { recursive: true });
32
+ mkdirSync(hookDir, { recursive: true });
33
+ mkdirSync(mailDir, { recursive: true });
213
34
  const hookFile = getHookFile(agentId);
214
- if (!existsSync2(hookFile)) {
35
+ if (!existsSync(hookFile)) {
215
36
  const hook = {
216
37
  agentId,
217
38
  items: []
218
39
  };
219
- writeFileSync2(hookFile, JSON.stringify(hook, null, 2));
40
+ writeFileSync(hookFile, JSON.stringify(hook, null, 2));
220
41
  }
221
42
  }
222
43
  function getHook(agentId) {
223
44
  const hookFile = getHookFile(agentId);
224
- if (!existsSync2(hookFile)) {
45
+ if (!existsSync(hookFile)) {
225
46
  return null;
226
47
  }
227
48
  try {
@@ -240,19 +61,19 @@ function pushToHook(agentId, item) {
240
61
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
241
62
  };
242
63
  hook.items.push(newItem);
243
- writeFileSync2(getHookFile(agentId), JSON.stringify(hook, null, 2));
64
+ writeFileSync(getHookFile(agentId), JSON.stringify(hook, null, 2));
244
65
  return newItem;
245
66
  }
246
67
  function checkHook(agentId) {
247
68
  const hook = getHook(agentId);
248
69
  if (!hook || hook.items.length === 0) {
249
70
  const mailDir = getMailDir(agentId);
250
- if (existsSync2(mailDir)) {
71
+ if (existsSync(mailDir)) {
251
72
  const mails = readdirSync(mailDir).filter((f) => f.endsWith(".json"));
252
73
  if (mails.length > 0) {
253
74
  const mailItems = mails.map((file) => {
254
75
  try {
255
- const content = readFileSync(join2(mailDir, file), "utf-8");
76
+ const content = readFileSync(join(mailDir, file), "utf-8");
256
77
  return JSON.parse(content);
257
78
  } catch {
258
79
  return null;
@@ -289,7 +110,7 @@ function popFromHook(agentId, itemId) {
289
110
  if (index === -1) return false;
290
111
  hook.items.splice(index, 1);
291
112
  hook.lastChecked = (/* @__PURE__ */ new Date()).toISOString();
292
- writeFileSync2(getHookFile(agentId), JSON.stringify(hook, null, 2));
113
+ writeFileSync(getHookFile(agentId), JSON.stringify(hook, null, 2));
293
114
  return true;
294
115
  }
295
116
  function clearHook(agentId) {
@@ -297,7 +118,7 @@ function clearHook(agentId) {
297
118
  if (!hook) return;
298
119
  hook.items = [];
299
120
  hook.lastChecked = (/* @__PURE__ */ new Date()).toISOString();
300
- writeFileSync2(getHookFile(agentId), JSON.stringify(hook, null, 2));
121
+ writeFileSync(getHookFile(agentId), JSON.stringify(hook, null, 2));
301
122
  }
302
123
  function sendMail(toAgentId, from, message, priority = "normal") {
303
124
  initHook(toAgentId);
@@ -310,8 +131,8 @@ function sendMail(toAgentId, from, message, priority = "normal") {
310
131
  payload: { message },
311
132
  createdAt: (/* @__PURE__ */ new Date()).toISOString()
312
133
  };
313
- writeFileSync2(
314
- join2(mailDir, `${mailItem.id}.json`),
134
+ writeFileSync(
135
+ join(mailDir, `${mailItem.id}.json`),
315
136
  JSON.stringify(mailItem, null, 2)
316
137
  );
317
138
  }
@@ -1000,17 +821,6 @@ var init_work_type_router = __esm({
1000
821
  });
1001
822
 
1002
823
  export {
1003
- sessionExists,
1004
- createSession,
1005
- killSession,
1006
- sendKeysAsync,
1007
- sendKeys,
1008
- capturePane,
1009
- capturePaneAsync,
1010
- waitForClaudePrompt,
1011
- confirmDelivery,
1012
- getAgentSessions,
1013
- init_tmux,
1014
824
  initHook,
1015
825
  pushToHook,
1016
826
  checkHook,
@@ -1022,4 +832,4 @@ export {
1022
832
  getModelId,
1023
833
  init_work_type_router
1024
834
  };
1025
- //# sourceMappingURL=chunk-HRU7S4TA.js.map
835
+ //# sourceMappingURL=chunk-ZN5RHWGR.js.map