panopticon-cli 0.5.3 → 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.
- package/dist/{agents-DMPT32H7.js → agents-5HWTDR4S.js} +12 -9
- package/dist/archive-planning-U3AZAKWI.js +16 -0
- package/dist/{chunk-KBHRXV5T.js → chunk-43F4LDZ4.js} +3 -3
- package/dist/chunk-6OYUJ4AJ.js +146 -0
- package/dist/chunk-6OYUJ4AJ.js.map +1 -0
- package/dist/{chunk-MOPGR3CL.js → chunk-AAP4G6U7.js} +1 -1
- package/dist/chunk-AAP4G6U7.js.map +1 -0
- package/dist/{chunk-4HST45MO.js → chunk-BYWVPPAZ.js} +19 -12
- package/dist/chunk-BYWVPPAZ.js.map +1 -0
- package/dist/{chunk-CFCUOV3Q.js → chunk-DMRTN432.js} +4 -1
- package/dist/chunk-DMRTN432.js.map +1 -0
- package/dist/{chunk-HOGYHJ2G.js → chunk-DW3PKGIS.js} +2 -2
- package/dist/{chunk-D67AQTHF.js → chunk-FUUP55PE.js} +108 -46
- package/dist/chunk-FUUP55PE.js.map +1 -0
- package/dist/chunk-GUV2EPBG.js +692 -0
- package/dist/chunk-GUV2EPBG.js.map +1 -0
- package/dist/{chunk-44EOY2ZL.js → chunk-HHL3AWXA.js} +46 -2
- package/dist/chunk-HHL3AWXA.js.map +1 -0
- package/dist/{chunk-6N2KBSJA.js → chunk-IZIXJYXZ.js} +40 -6
- package/dist/chunk-IZIXJYXZ.js.map +1 -0
- package/dist/chunk-MJXYTGK5.js +64 -0
- package/dist/chunk-MJXYTGK5.js.map +1 -0
- package/dist/chunk-OJF4QS3S.js +269 -0
- package/dist/chunk-OJF4QS3S.js.map +1 -0
- package/dist/{chunk-FQ66DECN.js → chunk-QAJAJBFW.js} +1 -1
- package/dist/chunk-QAJAJBFW.js.map +1 -0
- package/dist/chunk-R4KPLLRB.js +36 -0
- package/dist/chunk-R4KPLLRB.js.map +1 -0
- package/dist/{chunk-DFNVHK3N.js → chunk-SUM2WVPF.js} +4 -4
- package/dist/{chunk-T7BBPDEJ.js → chunk-UKSGE6RH.js} +45 -15
- package/dist/chunk-UKSGE6RH.js.map +1 -0
- package/dist/chunk-W2OTF6OS.js +201 -0
- package/dist/chunk-W2OTF6OS.js.map +1 -0
- package/dist/chunk-WEQW3EAT.js +78 -0
- package/dist/chunk-WEQW3EAT.js.map +1 -0
- package/dist/{chunk-2V2DQ3IX.js → chunk-WJJ3ZIQ6.js} +112 -45
- package/dist/chunk-WJJ3ZIQ6.js.map +1 -0
- package/dist/chunk-YAAT66RT.js +70 -0
- package/dist/chunk-YAAT66RT.js.map +1 -0
- package/dist/{chunk-RLZQB7HS.js → chunk-ZMJFEHGF.js} +13 -1
- package/dist/chunk-ZMJFEHGF.js.map +1 -0
- package/dist/{chunk-HRU7S4TA.js → chunk-ZN5RHWGR.js} +18 -208
- package/dist/{chunk-HRU7S4TA.js.map → chunk-ZN5RHWGR.js.map} +1 -1
- package/dist/{chunk-ZTYHZMEC.js → chunk-ZWZNEA26.js} +2 -2
- package/dist/clean-planning-7Z5YY64X.js +9 -0
- package/dist/cli/index.js +1314 -2146
- package/dist/cli/index.js.map +1 -1
- package/dist/close-issue-CTZK777I.js +9 -0
- package/dist/compact-beads-72SHALOL.js +9 -0
- package/dist/{config-4CJNUE3O.js → config-FFTMBVHM.js} +2 -2
- package/dist/dashboard/public/assets/{index-BJKEp64j.css → index-Bx4NCn9A.css} +1 -1
- package/dist/dashboard/public/assets/index-Db9NOz4z.js +756 -0
- package/dist/dashboard/public/index.html +3 -2
- package/dist/dashboard/server.js +34785 -34330
- package/dist/{feedback-writer-T43PI5S2.js → feedback-writer-T2WCT6EZ.js} +2 -2
- package/dist/{hume-CKJJ3OUU.js → hume-GVTB5BKW.js} +3 -3
- package/dist/index.d.ts +24 -16
- package/dist/index.js +4 -4
- package/dist/label-cleanup-4HJVX6NP.js +103 -0
- package/dist/label-cleanup-4HJVX6NP.js.map +1 -0
- package/dist/merge-agent-WM7ZKUET.js +1725 -0
- package/dist/merge-agent-WM7ZKUET.js.map +1 -0
- package/dist/{projects-KVM3MN3Y.js → projects-3CRF57ZU.js} +2 -2
- package/dist/{rally-RKFSWC7E.js → rally-LBY24P4C.js} +2 -2
- package/dist/{remote-agents-ULPD6C5U.js → remote-agents-3NZPSHYG.js} +2 -3
- package/dist/{remote-workspace-XX6ARE6I.js → remote-workspace-M4IULGFZ.js} +24 -49
- package/dist/remote-workspace-M4IULGFZ.js.map +1 -0
- package/dist/{review-status-XKUKZF6J.js → review-status-J2YJGL3E.js} +2 -2
- package/dist/{specialist-context-53AWO6AE.js → specialist-context-74RQF5SR.js} +7 -5
- package/dist/{specialist-context-53AWO6AE.js.map → specialist-context-74RQF5SR.js.map} +1 -1
- package/dist/{specialist-logs-QREUJ4HN.js → specialist-logs-T5GW7CSU.js} +6 -4
- package/dist/{specialists-2DBBXRCK.js → specialists-HTYYFXHQ.js} +6 -4
- package/dist/specialists-HTYYFXHQ.js.map +1 -0
- package/dist/tmux-X2I5SAIJ.js +31 -0
- package/dist/tmux-X2I5SAIJ.js.map +1 -0
- package/dist/{traefik-5GL3Q7DJ.js → traefik-QXLZ4PO2.js} +4 -4
- package/dist/traefik-QXLZ4PO2.js.map +1 -0
- package/dist/{tunnel-BKC7KLBX.js → tunnel-7IOSRZVH.js} +3 -3
- package/dist/tunnel-7IOSRZVH.js.map +1 -0
- package/dist/{workspace-manager-ALBR62AS.js → workspace-manager-G6TTBPC3.js} +6 -6
- package/dist/workspace-manager-G6TTBPC3.js.map +1 -0
- package/package.json +2 -2
- package/scripts/build-cost-script.mjs +17 -0
- package/scripts/heartbeat-hook +28 -8
- package/scripts/record-cost-event.js +46 -7
- package/scripts/record-cost-event.ts +2 -1
- package/scripts/recover-costs-deep.mjs +209 -0
- package/scripts/recover-costs-proportional.mjs +206 -0
- package/scripts/recover-costs.mjs +169 -0
- package/dist/chunk-2V2DQ3IX.js.map +0 -1
- package/dist/chunk-44EOY2ZL.js.map +0 -1
- package/dist/chunk-4HST45MO.js.map +0 -1
- package/dist/chunk-565HZ6VV.js +0 -159
- package/dist/chunk-565HZ6VV.js.map +0 -1
- package/dist/chunk-6N2KBSJA.js.map +0 -1
- package/dist/chunk-CFCUOV3Q.js.map +0 -1
- package/dist/chunk-D67AQTHF.js.map +0 -1
- package/dist/chunk-FQ66DECN.js.map +0 -1
- package/dist/chunk-MOPGR3CL.js.map +0 -1
- package/dist/chunk-RLZQB7HS.js.map +0 -1
- package/dist/chunk-T7BBPDEJ.js.map +0 -1
- package/dist/chunk-ZDNQFWR5.js +0 -650
- package/dist/chunk-ZDNQFWR5.js.map +0 -1
- package/dist/dashboard/public/assets/index-CgJjqjAV.js +0 -767
- package/dist/remote-workspace-XX6ARE6I.js.map +0 -1
- /package/dist/{agents-DMPT32H7.js.map → agents-5HWTDR4S.js.map} +0 -0
- /package/dist/{config-4CJNUE3O.js.map → archive-planning-U3AZAKWI.js.map} +0 -0
- /package/dist/{chunk-KBHRXV5T.js.map → chunk-43F4LDZ4.js.map} +0 -0
- /package/dist/{chunk-HOGYHJ2G.js.map → chunk-DW3PKGIS.js.map} +0 -0
- /package/dist/{chunk-DFNVHK3N.js.map → chunk-SUM2WVPF.js.map} +0 -0
- /package/dist/{chunk-ZTYHZMEC.js.map → chunk-ZWZNEA26.js.map} +0 -0
- /package/dist/{hume-CKJJ3OUU.js.map → clean-planning-7Z5YY64X.js.map} +0 -0
- /package/dist/{projects-KVM3MN3Y.js.map → close-issue-CTZK777I.js.map} +0 -0
- /package/dist/{rally-RKFSWC7E.js.map → compact-beads-72SHALOL.js.map} +0 -0
- /package/dist/{remote-agents-ULPD6C5U.js.map → config-FFTMBVHM.js.map} +0 -0
- /package/dist/{feedback-writer-T43PI5S2.js.map → feedback-writer-T2WCT6EZ.js.map} +0 -0
- /package/dist/{review-status-XKUKZF6J.js.map → hume-GVTB5BKW.js.map} +0 -0
- /package/dist/{specialist-logs-QREUJ4HN.js.map → projects-3CRF57ZU.js.map} +0 -0
- /package/dist/{specialists-2DBBXRCK.js.map → rally-LBY24P4C.js.map} +0 -0
- /package/dist/{traefik-5GL3Q7DJ.js.map → remote-agents-3NZPSHYG.js.map} +0 -0
- /package/dist/{tunnel-BKC7KLBX.js.map → review-status-J2YJGL3E.js.map} +0 -0
- /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-
|
|
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
|
|
198
|
-
import { join
|
|
18
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, readdirSync, unlinkSync } from "fs";
|
|
19
|
+
import { join } from "path";
|
|
199
20
|
function getHookDir(agentId) {
|
|
200
|
-
return
|
|
21
|
+
return join(AGENTS_DIR, agentId);
|
|
201
22
|
}
|
|
202
23
|
function getHookFile(agentId) {
|
|
203
|
-
return
|
|
24
|
+
return join(getHookDir(agentId), "hook.json");
|
|
204
25
|
}
|
|
205
26
|
function getMailDir(agentId) {
|
|
206
|
-
return
|
|
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
|
-
|
|
212
|
-
|
|
32
|
+
mkdirSync(hookDir, { recursive: true });
|
|
33
|
+
mkdirSync(mailDir, { recursive: true });
|
|
213
34
|
const hookFile = getHookFile(agentId);
|
|
214
|
-
if (!
|
|
35
|
+
if (!existsSync(hookFile)) {
|
|
215
36
|
const hook = {
|
|
216
37
|
agentId,
|
|
217
38
|
items: []
|
|
218
39
|
};
|
|
219
|
-
|
|
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 (!
|
|
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
|
-
|
|
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 (
|
|
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(
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
314
|
-
|
|
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-
|
|
835
|
+
//# sourceMappingURL=chunk-ZN5RHWGR.js.map
|