panopticon-cli 0.6.8 → 0.6.9

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 (89) hide show
  1. package/dist/{agents-D_2oRFVf.js → agents-BQOqo27C.js} +1 -1
  2. package/dist/{agents-CfFDs52G.js → agents-DezveQ1x.js} +4 -4
  3. package/dist/{agents-CfFDs52G.js.map → agents-DezveQ1x.js.map} +1 -1
  4. package/dist/cli/index.js +34 -34
  5. package/dist/{config-yaml-DGbLSMCa.js → config-yaml-BHD2Qdd8.js} +22 -1
  6. package/dist/config-yaml-BHD2Qdd8.js.map +1 -0
  7. package/dist/{config-yaml-Dqt4FWQH.js → config-yaml-IlSnFzJQ.js} +1 -1
  8. package/dist/dashboard/{agent-enrichment-DdO7ZqjI.js → agent-enrichment-BKZjVvlL.js} +3 -3
  9. package/dist/dashboard/{agent-enrichment-DdO7ZqjI.js.map → agent-enrichment-BKZjVvlL.js.map} +1 -1
  10. package/dist/dashboard/{agent-enrichment-dLeGE1fX.js → agent-enrichment-iY3_PylI.js} +1 -1
  11. package/dist/dashboard/{agents-DCpQQ_W5.js → agents-BQWA-Vps.js} +4 -4
  12. package/dist/dashboard/{agents-DCpQQ_W5.js.map → agents-BQWA-Vps.js.map} +1 -1
  13. package/dist/dashboard/{agents-Dgh2TjSp.js → agents-Dinc9j_8.js} +1 -1
  14. package/dist/dashboard/{config-yaml-DkresmrS.js → config-yaml-CNNnB4Mu.js} +1 -1
  15. package/dist/dashboard/{config-yaml-DSfYpzN6.js → config-yaml-DUu0JI25.js} +22 -1
  16. package/dist/dashboard/{config-yaml-DSfYpzN6.js.map → config-yaml-DUu0JI25.js.map} +1 -1
  17. package/dist/dashboard/{factory-C8nhLGHB.js → factory-CBY0WWeE.js} +2 -2
  18. package/dist/dashboard/{factory-C8nhLGHB.js.map → factory-CBY0WWeE.js.map} +1 -1
  19. package/dist/dashboard/{inspect-agent-7eour7EA.js → inspect-agent-KKOeNR7E.js} +3 -3
  20. package/dist/dashboard/{inspect-agent-7eour7EA.js.map → inspect-agent-KKOeNR7E.js.map} +1 -1
  21. package/dist/dashboard/{issue-service-singleton-Wv4xBm3y.js → issue-service-singleton-BCZ62hLj.js} +3 -3
  22. package/dist/dashboard/{issue-service-singleton-Wv4xBm3y.js.map → issue-service-singleton-BCZ62hLj.js.map} +1 -1
  23. package/dist/dashboard/{issue-service-singleton-Co__-6kL.js → issue-service-singleton-BGKf0A95.js} +1 -1
  24. package/dist/dashboard/{lifecycle-BcUmtkR4.js → lifecycle-Dpgg-IeP.js} +1 -1
  25. package/dist/dashboard/{merge-agent-CGN3TT0a.js → merge-agent-CqvQu-n_.js} +1 -1
  26. package/dist/dashboard/{merge-agent-yudQOPZc.js → merge-agent-Dxxc4JEE.js} +5 -5
  27. package/dist/dashboard/{merge-agent-yudQOPZc.js.map → merge-agent-Dxxc4JEE.js.map} +1 -1
  28. package/dist/dashboard/public/assets/{dist-C-wcq54x.js → dist-DS1gmhe1.js} +1 -1
  29. package/dist/dashboard/public/assets/index-DjGsaJLv.js +212 -0
  30. package/dist/dashboard/public/index.html +1 -1
  31. package/dist/dashboard/{review-status-BtXqWBhS.js → review-status-Dww2OKUX.js} +1 -1
  32. package/dist/dashboard/{review-status-Bymwzh2i.js → review-status-d_wOE-XQ.js} +3 -3
  33. package/dist/dashboard/{review-status-Bymwzh2i.js.map → review-status-d_wOE-XQ.js.map} +1 -1
  34. package/dist/dashboard/server.js +97 -97
  35. package/dist/dashboard/settings-BHlDG7TK.js.map +1 -1
  36. package/dist/dashboard/{spawn-planning-session-D5hrVdWM.js → spawn-planning-session-D5uEpHzf.js} +1 -1
  37. package/dist/dashboard/{spawn-planning-session-33Jf-d5T.js → spawn-planning-session-DtbNfA2Q.js} +3 -3
  38. package/dist/dashboard/{spawn-planning-session-33Jf-d5T.js.map → spawn-planning-session-DtbNfA2Q.js.map} +1 -1
  39. package/dist/dashboard/{specialist-context-DGukHSn8.js → specialist-context-CEKqWqyF.js} +4 -4
  40. package/dist/dashboard/{specialist-context-DGukHSn8.js.map → specialist-context-CEKqWqyF.js.map} +1 -1
  41. package/dist/dashboard/{specialist-logs-CIw4qfTy.js → specialist-logs-CBGVRoQF.js} +1 -1
  42. package/dist/dashboard/{specialists-Cp-PgspS.js → specialists-sIFlMd3s.js} +1 -1
  43. package/dist/dashboard/{specialists-B_zrayaP.js → specialists-saEYE0-z.js} +20 -20
  44. package/dist/dashboard/{specialists-B_zrayaP.js.map → specialists-saEYE0-z.js.map} +1 -1
  45. package/dist/dashboard/{test-agent-queue-ypF_ecHo.js → test-agent-queue-7jXB2KkN.js} +3 -3
  46. package/dist/dashboard/{test-agent-queue-ypF_ecHo.js.map → test-agent-queue-7jXB2KkN.js.map} +1 -1
  47. package/dist/dashboard/{tracker-config-BP59uH4V.js → tracker-config-BX6ijWOc.js} +1 -1
  48. package/dist/dashboard/{tracker-config-e7ph1QqT.js → tracker-config-tD22z5sv.js} +2 -2
  49. package/dist/dashboard/{tracker-config-e7ph1QqT.js.map → tracker-config-tD22z5sv.js.map} +1 -1
  50. package/dist/dashboard/{work-agent-prompt-fCg67nyo.js → work-agent-prompt-D3tPzPvb.js} +2 -2
  51. package/dist/dashboard/{work-agent-prompt-fCg67nyo.js.map → work-agent-prompt-D3tPzPvb.js.map} +1 -1
  52. package/dist/dashboard/{work-type-router-CWVW2Wk_.js → work-type-router-7kwLSwrP.js} +4 -2
  53. package/dist/dashboard/work-type-router-7kwLSwrP.js.map +1 -0
  54. package/dist/dashboard/{work-type-router-Di5gCQwh.js → work-type-router-ByOOudGz.js} +1 -1
  55. package/dist/dashboard/workflows-BDpPjK18.js +2 -0
  56. package/dist/dashboard/{workflows-BSMipN07.js → workflows-DcEeDkbS.js} +3 -3
  57. package/dist/dashboard/{workflows-BSMipN07.js.map → workflows-DcEeDkbS.js.map} +1 -1
  58. package/dist/{factory-BRBGw6OB.js → factory-BR48tuUR.js} +1 -1
  59. package/dist/{factory-DzsOiZVc.js → factory-D6LJaZ__.js} +2 -2
  60. package/dist/{factory-DzsOiZVc.js.map → factory-D6LJaZ__.js.map} +1 -1
  61. package/dist/index.d.ts +1 -1
  62. package/dist/index.js +3 -3
  63. package/dist/{merge-agent-DlUiUanN.js → merge-agent-BBwHwpn2.js} +3 -3
  64. package/dist/{merge-agent-DlUiUanN.js.map → merge-agent-BBwHwpn2.js.map} +1 -1
  65. package/dist/{review-status-DEDvCKMP.js → review-status-Ba6llgCb.js} +3 -3
  66. package/dist/{review-status-DEDvCKMP.js.map → review-status-Ba6llgCb.js.map} +1 -1
  67. package/dist/{review-status-D6H2WOw8.js → review-status-Chxzuwn2.js} +1 -1
  68. package/dist/{settings-BcWPTrua.js → settings-A-CWz_ph.js} +6 -2
  69. package/dist/{settings-BcWPTrua.js.map → settings-A-CWz_ph.js.map} +1 -1
  70. package/dist/{specialist-context-BAUWL1Fl.js → specialist-context-B3lknlwi.js} +4 -4
  71. package/dist/{specialist-context-BAUWL1Fl.js.map → specialist-context-B3lknlwi.js.map} +1 -1
  72. package/dist/{specialist-logs-DQKKQV9B.js → specialist-logs-DDyY4xqo.js} +1 -1
  73. package/dist/{specialists-D7Kj5o6s.js → specialists-DvTYu1VZ.js} +20 -20
  74. package/dist/{specialists-D7Kj5o6s.js.map → specialists-DvTYu1VZ.js.map} +1 -1
  75. package/dist/{specialists-Bfb9ATzw.js → specialists-DyB4IRlM.js} +1 -1
  76. package/dist/sync-CLVqiGl4.js +2 -0
  77. package/dist/{sync-DMfgd389.js → sync-DTHFlEc-.js} +2 -2
  78. package/dist/{sync-DMfgd389.js.map → sync-DTHFlEc-.js.map} +1 -1
  79. package/dist/{tracker-BhYYvU3p.js → tracker-CYpb7oUa.js} +2 -2
  80. package/dist/{tracker-BhYYvU3p.js.map → tracker-CYpb7oUa.js.map} +1 -1
  81. package/dist/{work-type-router-CHjciPyS.js → work-type-router-oCgTPXsP.js} +4 -2
  82. package/dist/work-type-router-oCgTPXsP.js.map +1 -0
  83. package/package.json +1 -1
  84. package/dist/config-yaml-DGbLSMCa.js.map +0 -1
  85. package/dist/dashboard/public/assets/index-DKlrFY1k.js +0 -212
  86. package/dist/dashboard/work-type-router-CWVW2Wk_.js.map +0 -1
  87. package/dist/dashboard/workflows-DaYWQIS2.js +0 -2
  88. package/dist/sync-TL6y-8K6.js +0 -2
  89. package/dist/work-type-router-CHjciPyS.js.map +0 -1
@@ -1,5 +1,5 @@
1
1
  import { n as __esmMin } from "./chunk-DORXReHP.js";
2
- import { i as loadConfig, r as init_config_yaml } from "./config-yaml-DSfYpzN6.js";
2
+ import { i as loadConfig, r as init_config_yaml } from "./config-yaml-DUu0JI25.js";
3
3
  import { n as init_dist_src, t as Octokit } from "./dist-src-DTm11oQr.js";
4
4
  import { a as TrackerAuthError, i as NotImplementedError, n as init_rally, o as init_interface, r as IssueNotFoundError, t as RallyTracker } from "./rally-YjFRxIiC.js";
5
5
  import { LinearClient } from "@linear/sdk";
@@ -498,4 +498,4 @@ var init_factory = __esmMin((() => {
498
498
  //#endregion
499
499
  export { createTrackerFromConfig as n, init_factory as r, createTracker as t };
500
500
 
501
- //# sourceMappingURL=factory-C8nhLGHB.js.map
501
+ //# sourceMappingURL=factory-CBY0WWeE.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"factory-C8nhLGHB.js","names":["loadYamlConfig"],"sources":["../../src/lib/tracker/linear.ts","../../src/lib/tracker/github.ts","../../src/lib/tracker/gitlab.ts","../../src/lib/tracker/factory.ts"],"sourcesContent":["/**\n * Linear Issue Tracker Adapter\n *\n * Implements IssueTracker interface for Linear.\n */\n\nimport { LinearClient } from '@linear/sdk';\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { IssueNotFoundError, TrackerAuthError } from './interface.js';\n\n// Map Linear state types to our normalized states\nconst STATE_MAP: Record<string, IssueState> = {\n backlog: 'open',\n unstarted: 'open',\n started: 'in_progress',\n completed: 'closed',\n canceled: 'closed',\n};\n\nexport class LinearTracker implements IssueTracker {\n readonly name: TrackerType = 'linear';\n private client: LinearClient;\n private defaultTeam?: string;\n\n constructor(apiKey: string, options?: { team?: string }) {\n if (!apiKey) {\n throw new TrackerAuthError('linear', 'API key is required');\n }\n this.client = new LinearClient({ apiKey });\n this.defaultTeam = options?.team;\n }\n\n async listIssues(filters?: IssueFilters): Promise<Issue[]> {\n const team = filters?.team ?? this.defaultTeam;\n\n const result = await this.client.issues({\n first: filters?.limit ?? 50,\n filter: {\n team: team ? { key: { eq: team } } : undefined,\n state: filters?.state\n ? { type: { eq: this.reverseMapState(filters.state) } }\n : filters?.includeClosed\n ? undefined\n : { type: { neq: 'completed' } },\n labels: filters?.labels?.length\n ? { name: { in: filters.labels } }\n : undefined,\n assignee: filters?.assignee\n ? { name: { containsIgnoreCase: filters.assignee } }\n : undefined,\n },\n });\n\n const issues: Issue[] = [];\n for (const node of result.nodes) {\n issues.push(await this.normalizeIssue(node));\n }\n return issues;\n }\n\n async getIssue(id: string): Promise<Issue> {\n try {\n // Check if it's a UUID (36 chars with hyphens)\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n\n if (isUuid) {\n // Fetch directly by UUID\n const issue = await this.client.issue(id);\n if (issue) {\n return this.normalizeIssue(issue);\n }\n } else {\n // Parse identifier (e.g., MIN-630) and search\n const match = id.match(/^([A-Z]+)-(\\d+)$/i);\n if (match) {\n const [, teamKey, number] = match;\n // Use searchIssues which supports identifier matching\n const results = await this.client.searchIssues(id, { first: 1 });\n if (results.nodes.length > 0) {\n return this.normalizeIssue(results.nodes[0]);\n }\n }\n }\n\n throw new IssueNotFoundError(id, 'linear');\n } catch (error) {\n if (error instanceof IssueNotFoundError) throw error;\n throw new IssueNotFoundError(id, 'linear');\n }\n }\n\n async updateIssue(id: string, update: IssueUpdate): Promise<Issue> {\n const issue = await this.getIssue(id);\n\n const updatePayload: Record<string, unknown> = {};\n\n if (update.title !== undefined) {\n updatePayload.title = update.title;\n }\n if (update.description !== undefined) {\n updatePayload.description = update.description;\n }\n if (update.priority !== undefined) {\n updatePayload.priority = update.priority;\n }\n if (update.dueDate !== undefined) {\n updatePayload.dueDate = update.dueDate;\n }\n if (update.state !== undefined) {\n // Need to find the state ID - this is complex in Linear\n // For now, we'll use the transition method\n await this.transitionIssue(id, update.state);\n }\n if (update.labels !== undefined) {\n // Need to look up label IDs - complex operation\n // TODO: Implement label updates\n }\n\n if (Object.keys(updatePayload).length > 0) {\n await this.client.updateIssue(issue.id, updatePayload);\n }\n\n return this.getIssue(id);\n }\n\n async createIssue(newIssue: NewIssue): Promise<Issue> {\n const team = newIssue.team ?? this.defaultTeam;\n\n if (!team) {\n throw new Error('Team is required to create an issue');\n }\n\n // Get team ID from key\n const teams = await this.client.teams({\n filter: { key: { eq: team } },\n });\n\n if (teams.nodes.length === 0) {\n throw new Error(`Team not found: ${team}`);\n }\n\n const teamId = teams.nodes[0].id;\n\n const result = await this.client.createIssue({\n teamId,\n title: newIssue.title,\n description: newIssue.description,\n priority: newIssue.priority,\n dueDate: newIssue.dueDate,\n });\n\n const created = await result.issue;\n if (!created) {\n throw new Error('Failed to create issue');\n }\n\n return this.normalizeIssue(created);\n }\n\n async getComments(issueId: string): Promise<Comment[]> {\n const issue = await this.client.issue(issueId);\n const comments = await issue.comments();\n\n return comments.nodes.map((c) => ({\n id: c.id,\n issueId,\n body: c.body,\n author: c.user?.then((u) => u?.name ?? 'Unknown') as unknown as string, // Simplified\n createdAt: c.createdAt.toISOString(),\n updatedAt: c.updatedAt.toISOString(),\n }));\n }\n\n async addComment(issueId: string, body: string): Promise<Comment> {\n const result = await this.client.createComment({\n issueId,\n body,\n });\n\n const comment = await result.comment;\n if (!comment) {\n throw new Error('Failed to create comment');\n }\n\n return {\n id: comment.id,\n issueId,\n body: comment.body,\n author: 'Panopticon', // Simplified\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n };\n }\n\n async transitionIssue(id: string, state: IssueState): Promise<void> {\n // Resolve the Linear issue directly (avoid normalizeIssue which may fail on SDK edge cases)\n let linearIssue: any;\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n if (isUuid) {\n linearIssue = await this.client.issue(id);\n } else {\n const results = await this.client.searchIssues(id, { first: 1 });\n if (results.nodes.length > 0) {\n linearIssue = results.nodes[0];\n } else {\n throw new IssueNotFoundError(id, 'linear');\n }\n }\n\n // Get workflow states for the issue's team\n const team = await linearIssue.team;\n if (!team) {\n throw new Error('Could not determine issue team');\n }\n\n const states = await team.states();\n\n let targetState: any;\n if (state === 'in_review') {\n // Find a state named \"In Review\" (case-insensitive) — more precise than matching by type,\n // since \"In Progress\" and \"In Review\" are both type \"started\" in Linear.\n targetState = states.nodes.find((s: any) => s.name.toLowerCase() === 'in review');\n if (!targetState) {\n // Fall back to lowest-position \"started\" state if no \"In Review\" state exists\n const startedStates = states.nodes\n .filter((s: any) => s.type === 'started')\n .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0));\n targetState = startedStates[0];\n if (!targetState) {\n throw new Error('No \"In Review\" or \"started\" state found in Linear');\n }\n }\n } else {\n const targetStateType = this.reverseMapState(state);\n\n // Find a state matching the target type.\n // Multiple states can share the same type (e.g., \"In Planning\", \"In Progress\", \"In Review\"\n // are all type \"started\"). Prefer the one with the lowest position (most basic/default state\n // for that type), which matches Linear's convention.\n const matchingStates = states.nodes\n .filter((s: any) => s.type === targetStateType)\n .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0));\n targetState = matchingStates[0];\n if (!targetState) {\n throw new Error(`No state found matching type: ${targetStateType}`);\n }\n }\n\n await this.client.updateIssue(linearIssue.id, {\n stateId: targetState.id,\n });\n }\n\n async linkPR(issueId: string, prUrl: string): Promise<void> {\n const issue = await this.getIssue(issueId);\n\n await this.client.createAttachment({\n issueId: issue.id,\n title: 'Pull Request',\n url: prUrl,\n });\n }\n\n private async normalizeIssue(linearIssue: any): Promise<Issue> {\n const state = await linearIssue.state;\n const assignee = await linearIssue.assignee;\n const labels = await linearIssue.labels();\n\n // Handle dueDate - can be Date, string, or undefined\n let dueDate: string | undefined;\n if (linearIssue.dueDate) {\n dueDate = linearIssue.dueDate instanceof Date\n ? linearIssue.dueDate.toISOString()\n : String(linearIssue.dueDate);\n }\n\n return {\n id: linearIssue.id,\n ref: linearIssue.identifier,\n title: linearIssue.title,\n description: linearIssue.description ?? '',\n state: this.mapState(state?.type ?? 'backlog'),\n labels: labels?.nodes?.map((l: any) => l.name) ?? [],\n assignee: assignee?.name,\n url: linearIssue.url,\n tracker: 'linear',\n priority: linearIssue.priority,\n dueDate,\n createdAt: linearIssue.createdAt instanceof Date\n ? linearIssue.createdAt.toISOString()\n : String(linearIssue.createdAt),\n updatedAt: linearIssue.updatedAt instanceof Date\n ? linearIssue.updatedAt.toISOString()\n : String(linearIssue.updatedAt),\n };\n }\n\n private mapState(linearState: string): IssueState {\n return STATE_MAP[linearState] ?? 'open';\n }\n\n private reverseMapState(state: IssueState): string {\n switch (state) {\n case 'open':\n return 'unstarted';\n case 'in_progress':\n case 'in_review':\n return 'started';\n case 'closed':\n return 'completed';\n default:\n return 'unstarted';\n }\n }\n}\n","/**\n * GitHub Issues Tracker Adapter\n *\n * Implements IssueTracker interface for GitHub Issues.\n */\n\nimport { Octokit } from '@octokit/rest';\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { IssueNotFoundError, TrackerAuthError } from './interface.js';\n\n/**\n * Extract issue number from various formats: \"300\", \"#300\", \"PAN-300\"\n */\nfunction parseIssueNumber(id: string): number {\n const match = id.match(/(\\d+)$/);\n return match ? parseInt(match[1], 10) : NaN;\n}\n\nexport class GitHubTracker implements IssueTracker {\n readonly name: TrackerType = 'github';\n private octokit: Octokit;\n private owner: string;\n private repo: string;\n\n constructor(token: string, owner: string, repo: string) {\n if (!token) {\n throw new TrackerAuthError('github', 'Token is required');\n }\n if (!owner || !repo) {\n throw new Error('GitHub owner and repo are required');\n }\n\n this.octokit = new Octokit({ auth: token });\n this.owner = owner;\n this.repo = repo;\n }\n\n async listIssues(filters?: IssueFilters): Promise<Issue[]> {\n const state = this.mapStateToGitHub(filters?.state);\n\n const response = await this.octokit.issues.listForRepo({\n owner: this.owner,\n repo: this.repo,\n state: filters?.includeClosed ? 'all' : state,\n labels: filters?.labels?.join(',') || undefined,\n assignee: filters?.assignee || undefined,\n per_page: filters?.limit ?? 50,\n });\n\n // Filter out pull requests (GitHub API returns both)\n const issues = response.data.filter((item) => !item.pull_request);\n\n return issues.map((issue) => this.normalizeIssue(issue));\n }\n\n async getIssue(id: string): Promise<Issue> {\n try {\n // Parse the issue number from refs like \"#42\" or just \"42\"\n const issueNumber = parseIssueNumber(id);\n\n if (isNaN(issueNumber)) {\n throw new IssueNotFoundError(id, 'github');\n }\n\n const { data: issue } = await this.octokit.issues.get({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n });\n\n return this.normalizeIssue(issue);\n } catch (error: any) {\n if (error?.status === 404) {\n throw new IssueNotFoundError(id, 'github');\n }\n throw error;\n }\n }\n\n async updateIssue(id: string, update: IssueUpdate): Promise<Issue> {\n const issueNumber = parseIssueNumber(id);\n\n const updatePayload: Record<string, unknown> = {};\n\n if (update.title !== undefined) {\n updatePayload.title = update.title;\n }\n if (update.description !== undefined) {\n updatePayload.body = update.description;\n }\n if (update.state !== undefined) {\n updatePayload.state = update.state === 'closed' ? 'closed' : 'open';\n }\n if (update.labels !== undefined) {\n updatePayload.labels = update.labels;\n }\n if (update.assignee !== undefined) {\n updatePayload.assignees = update.assignee ? [update.assignee] : [];\n }\n\n await this.octokit.issues.update({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n ...updatePayload,\n });\n\n return this.getIssue(id);\n }\n\n async createIssue(newIssue: NewIssue): Promise<Issue> {\n const { data: issue } = await this.octokit.issues.create({\n owner: this.owner,\n repo: this.repo,\n title: newIssue.title,\n body: newIssue.description,\n labels: newIssue.labels,\n assignees: newIssue.assignee ? [newIssue.assignee] : undefined,\n });\n\n return this.normalizeIssue(issue);\n }\n\n async getComments(issueId: string): Promise<Comment[]> {\n const issueNumber = parseIssueNumber(issueId);\n\n const { data: comments } = await this.octokit.issues.listComments({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n });\n\n return comments.map((c) => ({\n id: String(c.id),\n issueId,\n body: c.body ?? '',\n author: c.user?.login ?? 'Unknown',\n createdAt: c.created_at,\n updatedAt: c.updated_at,\n }));\n }\n\n async addComment(issueId: string, body: string): Promise<Comment> {\n const issueNumber = parseIssueNumber(issueId);\n\n const { data: comment } = await this.octokit.issues.createComment({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n body,\n });\n\n return {\n id: String(comment.id),\n issueId,\n body: comment.body ?? '',\n author: comment.user?.login ?? 'Unknown',\n createdAt: comment.created_at,\n updatedAt: comment.updated_at,\n };\n }\n\n async transitionIssue(id: string, state: IssueState): Promise<void> {\n const issueNumber = parseIssueNumber(id);\n\n if (state === 'in_progress') {\n // GitHub has no native \"in progress\" state — use a label instead.\n await this.ensureLabelExists('in-progress', 'In progress', '0075ca');\n await this.octokit.issues.addLabels({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n labels: ['in-progress'],\n });\n } else if (state === 'in_review') {\n // Swap in-progress label for in-review label\n await this.ensureLabelExists('in-review', 'In review', 'e4e669');\n await this.octokit.issues.addLabels({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n labels: ['in-review'],\n });\n // Remove in-progress label if present\n await this.octokit.issues.removeLabel({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n name: 'in-progress',\n }).catch(() => {/* label may not exist, ignore */});\n } else {\n // Remove in-progress and in-review labels when moving to open or closed\n const issue = await this.getIssue(id);\n for (const label of ['in-progress', 'in-review']) {\n if (issue.labels?.includes(label)) {\n await this.octokit.issues.removeLabel({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n name: label,\n }).catch(() => {/* label may not exist, ignore */});\n }\n }\n await this.updateIssue(id, { state });\n }\n }\n\n /** Ensure a label exists in the repo, creating it if needed. */\n private async ensureLabelExists(name: string, description: string, color: string): Promise<void> {\n try {\n await this.octokit.issues.getLabel({ owner: this.owner, repo: this.repo, name });\n } catch {\n await this.octokit.issues.createLabel({\n owner: this.owner,\n repo: this.repo,\n name,\n description,\n color,\n }).catch(() => {/* race condition: another process created it first */});\n }\n }\n\n async linkPR(issueId: string, prUrl: string): Promise<void> {\n // GitHub auto-links PRs that mention issues\n // Add a comment with the PR link\n await this.addComment(\n issueId,\n `Linked Pull Request: ${prUrl}`\n );\n }\n\n private normalizeIssue(ghIssue: any): Issue {\n const labels: string[] = ghIssue.labels.map((l: any) =>\n typeof l === 'string' ? l : l.name\n );\n return {\n id: String(ghIssue.id),\n ref: `#${ghIssue.number}`,\n title: ghIssue.title,\n description: ghIssue.body ?? '',\n state: this.mapStateFromGitHub(ghIssue.state, labels),\n labels,\n assignee: ghIssue.assignee?.login,\n url: ghIssue.html_url,\n tracker: 'github',\n priority: undefined, // GitHub doesn't have priority\n dueDate: undefined, // GitHub doesn't have due dates on issues\n createdAt: ghIssue.created_at,\n updatedAt: ghIssue.updated_at,\n };\n }\n\n private mapStateFromGitHub(ghState: string, labels: string[] = []): IssueState {\n if (ghState === 'closed') return 'closed';\n if (labels.includes('in-progress')) return 'in_progress';\n return 'open';\n }\n\n private mapStateToGitHub(\n state?: IssueState\n ): 'open' | 'closed' | 'all' {\n if (!state) return 'open';\n if (state === 'closed') return 'closed';\n return 'open'; // Both 'open' and 'in_progress' map to 'open'\n }\n}\n","/**\n * GitLab Issues Tracker Adapter (Stub)\n *\n * Placeholder implementation for GitLab Issues support.\n * Full implementation will use @gitbeaker/rest.\n */\n\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { NotImplementedError } from './interface.js';\n\nexport class GitLabTracker implements IssueTracker {\n readonly name: TrackerType = 'gitlab';\n\n constructor(\n private token: string,\n private projectId: string\n ) {\n // Stub - will initialize @gitbeaker client when implemented\n }\n\n async listIssues(_filters?: IssueFilters): Promise<Issue[]> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async getIssue(_id: string): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async updateIssue(_id: string, _update: IssueUpdate): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async createIssue(_issue: NewIssue): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async getComments(_issueId: string): Promise<Comment[]> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async addComment(_issueId: string, _body: string): Promise<Comment> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async transitionIssue(_id: string, _state: IssueState): Promise<void> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async linkPR(_issueId: string, _prUrl: string): Promise<void> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n}\n","/**\n * Tracker Factory\n *\n * Creates appropriate tracker instances based on configuration.\n */\n\nimport type { IssueTracker, TrackerType } from './interface.js';\nimport { TrackerAuthError } from './interface.js';\nimport { LinearTracker } from './linear.js';\nimport { GitHubTracker } from './github.js';\nimport { GitLabTracker } from './gitlab.js';\nimport { RallyTracker } from './rally.js';\nimport type { TrackersConfig } from '../config.js';\nimport { loadConfig as loadYamlConfig } from '../config-yaml.js';\n\n// Configuration for a single tracker\nexport interface TrackerConfig {\n type: TrackerType;\n\n // Linear-specific\n apiKeyEnv?: string;\n team?: string;\n\n // GitHub-specific\n tokenEnv?: string;\n owner?: string;\n repo?: string;\n\n // GitLab-specific\n projectId?: string;\n\n // Rally-specific\n server?: string;\n workspace?: string;\n project?: string;\n}\n\n// Multi-tracker configuration (re-exported from config.ts)\n// Note: Use TrackersConfig from config.ts for full type with nested configs\n\n/**\n * Get tracker API key from config.yaml (Settings page).\n * This is checked FIRST — env vars are the fallback, not the other way around.\n */\nfunction getTrackerKeyFromConfig(trackerType: TrackerType): string | undefined {\n try {\n const { config: yamlConfig } = loadYamlConfig();\n return yamlConfig.trackerKeys[trackerType];\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a tracker instance from configuration.\n * Priority: config.yaml (Settings) > environment variable > custom env var name\n */\nexport function createTracker(config: TrackerConfig): IssueTracker {\n switch (config.type) {\n case 'linear': {\n const configKey = getTrackerKeyFromConfig('linear');\n const envKey = config.apiKeyEnv\n ? process.env[config.apiKeyEnv]\n : process.env.LINEAR_API_KEY;\n const apiKey = configKey || envKey;\n\n if (!apiKey) {\n throw new TrackerAuthError(\n 'linear',\n `API key not found. Configure in Settings or set ${config.apiKeyEnv ?? 'LINEAR_API_KEY'} environment variable.`\n );\n }\n\n return new LinearTracker(apiKey, { team: config.team });\n }\n\n case 'github': {\n const configKey = getTrackerKeyFromConfig('github');\n const envToken = config.tokenEnv\n ? process.env[config.tokenEnv]\n : process.env.GITHUB_TOKEN;\n const token = configKey || envToken;\n\n if (!token) {\n throw new TrackerAuthError(\n 'github',\n `Token not found. Configure in Settings or set ${config.tokenEnv ?? 'GITHUB_TOKEN'} environment variable.`\n );\n }\n\n if (!config.owner || !config.repo) {\n throw new Error(\n 'GitHub tracker requires owner and repo configuration'\n );\n }\n\n return new GitHubTracker(token, config.owner, config.repo);\n }\n\n case 'gitlab': {\n const configKey = getTrackerKeyFromConfig('gitlab');\n const envToken = config.tokenEnv\n ? process.env[config.tokenEnv]\n : process.env.GITLAB_TOKEN;\n const token = configKey || envToken;\n\n if (!token) {\n throw new TrackerAuthError(\n 'gitlab',\n `Token not found. Configure in Settings or set ${config.tokenEnv ?? 'GITLAB_TOKEN'} environment variable.`\n );\n }\n\n if (!config.projectId) {\n throw new Error('GitLab tracker requires projectId configuration');\n }\n\n return new GitLabTracker(token, config.projectId);\n }\n\n case 'rally': {\n const configKey = getTrackerKeyFromConfig('rally');\n const envKey = config.apiKeyEnv\n ? process.env[config.apiKeyEnv]\n : process.env.RALLY_API_KEY;\n const apiKey = configKey || envKey;\n\n if (!apiKey) {\n throw new TrackerAuthError(\n 'rally',\n `API key not found. Configure in Settings or set ${config.apiKeyEnv ?? 'RALLY_API_KEY'} environment variable.`\n );\n }\n\n return new RallyTracker({\n apiKey,\n server: config.server,\n workspace: config.workspace,\n project: config.project,\n });\n }\n\n default:\n throw new Error(`Unknown tracker type: ${config.type}`);\n }\n}\n\n/**\n * Create tracker from trackers configuration section\n */\nexport function createTrackerFromConfig(\n trackersConfig: TrackersConfig,\n trackerType: TrackerType\n): IssueTracker {\n const config = trackersConfig[trackerType];\n\n if (!config) {\n throw new Error(\n `No configuration found for tracker: ${trackerType}. Add [trackers.${trackerType}] to config.`\n );\n }\n\n return createTracker({ ...config, type: trackerType });\n}\n\n/**\n * Get the primary tracker from configuration\n */\nexport function getPrimaryTracker(trackersConfig: TrackersConfig): IssueTracker {\n return createTrackerFromConfig(trackersConfig, trackersConfig.primary);\n}\n\n/**\n * Get the secondary tracker from configuration (if configured)\n */\nexport function getSecondaryTracker(\n trackersConfig: TrackersConfig\n): IssueTracker | null {\n if (!trackersConfig.secondary) {\n return null;\n }\n return createTrackerFromConfig(trackersConfig, trackersConfig.secondary);\n}\n\n/**\n * Get all configured trackers\n */\nexport function getAllTrackers(trackersConfig: TrackersConfig): IssueTracker[] {\n const trackers: IssueTracker[] = [getPrimaryTracker(trackersConfig)];\n\n const secondary = getSecondaryTracker(trackersConfig);\n if (secondary) {\n trackers.push(secondary);\n }\n\n return trackers;\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"factory-CBY0WWeE.js","names":["loadYamlConfig"],"sources":["../../src/lib/tracker/linear.ts","../../src/lib/tracker/github.ts","../../src/lib/tracker/gitlab.ts","../../src/lib/tracker/factory.ts"],"sourcesContent":["/**\n * Linear Issue Tracker Adapter\n *\n * Implements IssueTracker interface for Linear.\n */\n\nimport { LinearClient } from '@linear/sdk';\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { IssueNotFoundError, TrackerAuthError } from './interface.js';\n\n// Map Linear state types to our normalized states\nconst STATE_MAP: Record<string, IssueState> = {\n backlog: 'open',\n unstarted: 'open',\n started: 'in_progress',\n completed: 'closed',\n canceled: 'closed',\n};\n\nexport class LinearTracker implements IssueTracker {\n readonly name: TrackerType = 'linear';\n private client: LinearClient;\n private defaultTeam?: string;\n\n constructor(apiKey: string, options?: { team?: string }) {\n if (!apiKey) {\n throw new TrackerAuthError('linear', 'API key is required');\n }\n this.client = new LinearClient({ apiKey });\n this.defaultTeam = options?.team;\n }\n\n async listIssues(filters?: IssueFilters): Promise<Issue[]> {\n const team = filters?.team ?? this.defaultTeam;\n\n const result = await this.client.issues({\n first: filters?.limit ?? 50,\n filter: {\n team: team ? { key: { eq: team } } : undefined,\n state: filters?.state\n ? { type: { eq: this.reverseMapState(filters.state) } }\n : filters?.includeClosed\n ? undefined\n : { type: { neq: 'completed' } },\n labels: filters?.labels?.length\n ? { name: { in: filters.labels } }\n : undefined,\n assignee: filters?.assignee\n ? { name: { containsIgnoreCase: filters.assignee } }\n : undefined,\n },\n });\n\n const issues: Issue[] = [];\n for (const node of result.nodes) {\n issues.push(await this.normalizeIssue(node));\n }\n return issues;\n }\n\n async getIssue(id: string): Promise<Issue> {\n try {\n // Check if it's a UUID (36 chars with hyphens)\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n\n if (isUuid) {\n // Fetch directly by UUID\n const issue = await this.client.issue(id);\n if (issue) {\n return this.normalizeIssue(issue);\n }\n } else {\n // Parse identifier (e.g., MIN-630) and search\n const match = id.match(/^([A-Z]+)-(\\d+)$/i);\n if (match) {\n const [, teamKey, number] = match;\n // Use searchIssues which supports identifier matching\n const results = await this.client.searchIssues(id, { first: 1 });\n if (results.nodes.length > 0) {\n return this.normalizeIssue(results.nodes[0]);\n }\n }\n }\n\n throw new IssueNotFoundError(id, 'linear');\n } catch (error) {\n if (error instanceof IssueNotFoundError) throw error;\n throw new IssueNotFoundError(id, 'linear');\n }\n }\n\n async updateIssue(id: string, update: IssueUpdate): Promise<Issue> {\n const issue = await this.getIssue(id);\n\n const updatePayload: Record<string, unknown> = {};\n\n if (update.title !== undefined) {\n updatePayload.title = update.title;\n }\n if (update.description !== undefined) {\n updatePayload.description = update.description;\n }\n if (update.priority !== undefined) {\n updatePayload.priority = update.priority;\n }\n if (update.dueDate !== undefined) {\n updatePayload.dueDate = update.dueDate;\n }\n if (update.state !== undefined) {\n // Need to find the state ID - this is complex in Linear\n // For now, we'll use the transition method\n await this.transitionIssue(id, update.state);\n }\n if (update.labels !== undefined) {\n // Need to look up label IDs - complex operation\n // TODO: Implement label updates\n }\n\n if (Object.keys(updatePayload).length > 0) {\n await this.client.updateIssue(issue.id, updatePayload);\n }\n\n return this.getIssue(id);\n }\n\n async createIssue(newIssue: NewIssue): Promise<Issue> {\n const team = newIssue.team ?? this.defaultTeam;\n\n if (!team) {\n throw new Error('Team is required to create an issue');\n }\n\n // Get team ID from key\n const teams = await this.client.teams({\n filter: { key: { eq: team } },\n });\n\n if (teams.nodes.length === 0) {\n throw new Error(`Team not found: ${team}`);\n }\n\n const teamId = teams.nodes[0].id;\n\n const result = await this.client.createIssue({\n teamId,\n title: newIssue.title,\n description: newIssue.description,\n priority: newIssue.priority,\n dueDate: newIssue.dueDate,\n });\n\n const created = await result.issue;\n if (!created) {\n throw new Error('Failed to create issue');\n }\n\n return this.normalizeIssue(created);\n }\n\n async getComments(issueId: string): Promise<Comment[]> {\n const issue = await this.client.issue(issueId);\n const comments = await issue.comments();\n\n return comments.nodes.map((c) => ({\n id: c.id,\n issueId,\n body: c.body,\n author: c.user?.then((u) => u?.name ?? 'Unknown') as unknown as string, // Simplified\n createdAt: c.createdAt.toISOString(),\n updatedAt: c.updatedAt.toISOString(),\n }));\n }\n\n async addComment(issueId: string, body: string): Promise<Comment> {\n const result = await this.client.createComment({\n issueId,\n body,\n });\n\n const comment = await result.comment;\n if (!comment) {\n throw new Error('Failed to create comment');\n }\n\n return {\n id: comment.id,\n issueId,\n body: comment.body,\n author: 'Panopticon', // Simplified\n createdAt: comment.createdAt.toISOString(),\n updatedAt: comment.updatedAt.toISOString(),\n };\n }\n\n async transitionIssue(id: string, state: IssueState): Promise<void> {\n // Resolve the Linear issue directly (avoid normalizeIssue which may fail on SDK edge cases)\n let linearIssue: any;\n const isUuid = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(id);\n if (isUuid) {\n linearIssue = await this.client.issue(id);\n } else {\n const results = await this.client.searchIssues(id, { first: 1 });\n if (results.nodes.length > 0) {\n linearIssue = results.nodes[0];\n } else {\n throw new IssueNotFoundError(id, 'linear');\n }\n }\n\n // Get workflow states for the issue's team\n const team = await linearIssue.team;\n if (!team) {\n throw new Error('Could not determine issue team');\n }\n\n const states = await team.states();\n\n let targetState: any;\n if (state === 'in_review') {\n // Find a state named \"In Review\" (case-insensitive) — more precise than matching by type,\n // since \"In Progress\" and \"In Review\" are both type \"started\" in Linear.\n targetState = states.nodes.find((s: any) => s.name.toLowerCase() === 'in review');\n if (!targetState) {\n // Fall back to lowest-position \"started\" state if no \"In Review\" state exists\n const startedStates = states.nodes\n .filter((s: any) => s.type === 'started')\n .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0));\n targetState = startedStates[0];\n if (!targetState) {\n throw new Error('No \"In Review\" or \"started\" state found in Linear');\n }\n }\n } else {\n const targetStateType = this.reverseMapState(state);\n\n // Find a state matching the target type.\n // Multiple states can share the same type (e.g., \"In Planning\", \"In Progress\", \"In Review\"\n // are all type \"started\"). Prefer the one with the lowest position (most basic/default state\n // for that type), which matches Linear's convention.\n const matchingStates = states.nodes\n .filter((s: any) => s.type === targetStateType)\n .sort((a: any, b: any) => (a.position ?? 0) - (b.position ?? 0));\n targetState = matchingStates[0];\n if (!targetState) {\n throw new Error(`No state found matching type: ${targetStateType}`);\n }\n }\n\n await this.client.updateIssue(linearIssue.id, {\n stateId: targetState.id,\n });\n }\n\n async linkPR(issueId: string, prUrl: string): Promise<void> {\n const issue = await this.getIssue(issueId);\n\n await this.client.createAttachment({\n issueId: issue.id,\n title: 'Pull Request',\n url: prUrl,\n });\n }\n\n private async normalizeIssue(linearIssue: any): Promise<Issue> {\n const state = await linearIssue.state;\n const assignee = await linearIssue.assignee;\n const labels = await linearIssue.labels();\n\n // Handle dueDate - can be Date, string, or undefined\n let dueDate: string | undefined;\n if (linearIssue.dueDate) {\n dueDate = linearIssue.dueDate instanceof Date\n ? linearIssue.dueDate.toISOString()\n : String(linearIssue.dueDate);\n }\n\n return {\n id: linearIssue.id,\n ref: linearIssue.identifier,\n title: linearIssue.title,\n description: linearIssue.description ?? '',\n state: this.mapState(state?.type ?? 'backlog'),\n labels: labels?.nodes?.map((l: any) => l.name) ?? [],\n assignee: assignee?.name,\n url: linearIssue.url,\n tracker: 'linear',\n priority: linearIssue.priority,\n dueDate,\n createdAt: linearIssue.createdAt instanceof Date\n ? linearIssue.createdAt.toISOString()\n : String(linearIssue.createdAt),\n updatedAt: linearIssue.updatedAt instanceof Date\n ? linearIssue.updatedAt.toISOString()\n : String(linearIssue.updatedAt),\n };\n }\n\n private mapState(linearState: string): IssueState {\n return STATE_MAP[linearState] ?? 'open';\n }\n\n private reverseMapState(state: IssueState): string {\n switch (state) {\n case 'open':\n return 'unstarted';\n case 'in_progress':\n case 'in_review':\n return 'started';\n case 'closed':\n return 'completed';\n default:\n return 'unstarted';\n }\n }\n}\n","/**\n * GitHub Issues Tracker Adapter\n *\n * Implements IssueTracker interface for GitHub Issues.\n */\n\nimport { Octokit } from '@octokit/rest';\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { IssueNotFoundError, TrackerAuthError } from './interface.js';\n\n/**\n * Extract issue number from various formats: \"300\", \"#300\", \"PAN-300\"\n */\nfunction parseIssueNumber(id: string): number {\n const match = id.match(/(\\d+)$/);\n return match ? parseInt(match[1], 10) : NaN;\n}\n\nexport class GitHubTracker implements IssueTracker {\n readonly name: TrackerType = 'github';\n private octokit: Octokit;\n private owner: string;\n private repo: string;\n\n constructor(token: string, owner: string, repo: string) {\n if (!token) {\n throw new TrackerAuthError('github', 'Token is required');\n }\n if (!owner || !repo) {\n throw new Error('GitHub owner and repo are required');\n }\n\n this.octokit = new Octokit({ auth: token });\n this.owner = owner;\n this.repo = repo;\n }\n\n async listIssues(filters?: IssueFilters): Promise<Issue[]> {\n const state = this.mapStateToGitHub(filters?.state);\n\n const response = await this.octokit.issues.listForRepo({\n owner: this.owner,\n repo: this.repo,\n state: filters?.includeClosed ? 'all' : state,\n labels: filters?.labels?.join(',') || undefined,\n assignee: filters?.assignee || undefined,\n per_page: filters?.limit ?? 50,\n });\n\n // Filter out pull requests (GitHub API returns both)\n const issues = response.data.filter((item) => !item.pull_request);\n\n return issues.map((issue) => this.normalizeIssue(issue));\n }\n\n async getIssue(id: string): Promise<Issue> {\n try {\n // Parse the issue number from refs like \"#42\" or just \"42\"\n const issueNumber = parseIssueNumber(id);\n\n if (isNaN(issueNumber)) {\n throw new IssueNotFoundError(id, 'github');\n }\n\n const { data: issue } = await this.octokit.issues.get({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n });\n\n return this.normalizeIssue(issue);\n } catch (error: any) {\n if (error?.status === 404) {\n throw new IssueNotFoundError(id, 'github');\n }\n throw error;\n }\n }\n\n async updateIssue(id: string, update: IssueUpdate): Promise<Issue> {\n const issueNumber = parseIssueNumber(id);\n\n const updatePayload: Record<string, unknown> = {};\n\n if (update.title !== undefined) {\n updatePayload.title = update.title;\n }\n if (update.description !== undefined) {\n updatePayload.body = update.description;\n }\n if (update.state !== undefined) {\n updatePayload.state = update.state === 'closed' ? 'closed' : 'open';\n }\n if (update.labels !== undefined) {\n updatePayload.labels = update.labels;\n }\n if (update.assignee !== undefined) {\n updatePayload.assignees = update.assignee ? [update.assignee] : [];\n }\n\n await this.octokit.issues.update({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n ...updatePayload,\n });\n\n return this.getIssue(id);\n }\n\n async createIssue(newIssue: NewIssue): Promise<Issue> {\n const { data: issue } = await this.octokit.issues.create({\n owner: this.owner,\n repo: this.repo,\n title: newIssue.title,\n body: newIssue.description,\n labels: newIssue.labels,\n assignees: newIssue.assignee ? [newIssue.assignee] : undefined,\n });\n\n return this.normalizeIssue(issue);\n }\n\n async getComments(issueId: string): Promise<Comment[]> {\n const issueNumber = parseIssueNumber(issueId);\n\n const { data: comments } = await this.octokit.issues.listComments({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n });\n\n return comments.map((c) => ({\n id: String(c.id),\n issueId,\n body: c.body ?? '',\n author: c.user?.login ?? 'Unknown',\n createdAt: c.created_at,\n updatedAt: c.updated_at,\n }));\n }\n\n async addComment(issueId: string, body: string): Promise<Comment> {\n const issueNumber = parseIssueNumber(issueId);\n\n const { data: comment } = await this.octokit.issues.createComment({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n body,\n });\n\n return {\n id: String(comment.id),\n issueId,\n body: comment.body ?? '',\n author: comment.user?.login ?? 'Unknown',\n createdAt: comment.created_at,\n updatedAt: comment.updated_at,\n };\n }\n\n async transitionIssue(id: string, state: IssueState): Promise<void> {\n const issueNumber = parseIssueNumber(id);\n\n if (state === 'in_progress') {\n // GitHub has no native \"in progress\" state — use a label instead.\n await this.ensureLabelExists('in-progress', 'In progress', '0075ca');\n await this.octokit.issues.addLabels({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n labels: ['in-progress'],\n });\n } else if (state === 'in_review') {\n // Swap in-progress label for in-review label\n await this.ensureLabelExists('in-review', 'In review', 'e4e669');\n await this.octokit.issues.addLabels({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n labels: ['in-review'],\n });\n // Remove in-progress label if present\n await this.octokit.issues.removeLabel({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n name: 'in-progress',\n }).catch(() => {/* label may not exist, ignore */});\n } else {\n // Remove in-progress and in-review labels when moving to open or closed\n const issue = await this.getIssue(id);\n for (const label of ['in-progress', 'in-review']) {\n if (issue.labels?.includes(label)) {\n await this.octokit.issues.removeLabel({\n owner: this.owner,\n repo: this.repo,\n issue_number: issueNumber,\n name: label,\n }).catch(() => {/* label may not exist, ignore */});\n }\n }\n await this.updateIssue(id, { state });\n }\n }\n\n /** Ensure a label exists in the repo, creating it if needed. */\n private async ensureLabelExists(name: string, description: string, color: string): Promise<void> {\n try {\n await this.octokit.issues.getLabel({ owner: this.owner, repo: this.repo, name });\n } catch {\n await this.octokit.issues.createLabel({\n owner: this.owner,\n repo: this.repo,\n name,\n description,\n color,\n }).catch(() => {/* race condition: another process created it first */});\n }\n }\n\n async linkPR(issueId: string, prUrl: string): Promise<void> {\n // GitHub auto-links PRs that mention issues\n // Add a comment with the PR link\n await this.addComment(\n issueId,\n `Linked Pull Request: ${prUrl}`\n );\n }\n\n private normalizeIssue(ghIssue: any): Issue {\n const labels: string[] = ghIssue.labels.map((l: any) =>\n typeof l === 'string' ? l : l.name\n );\n return {\n id: String(ghIssue.id),\n ref: `#${ghIssue.number}`,\n title: ghIssue.title,\n description: ghIssue.body ?? '',\n state: this.mapStateFromGitHub(ghIssue.state, labels),\n labels,\n assignee: ghIssue.assignee?.login,\n url: ghIssue.html_url,\n tracker: 'github',\n priority: undefined, // GitHub doesn't have priority\n dueDate: undefined, // GitHub doesn't have due dates on issues\n createdAt: ghIssue.created_at,\n updatedAt: ghIssue.updated_at,\n };\n }\n\n private mapStateFromGitHub(ghState: string, labels: string[] = []): IssueState {\n if (ghState === 'closed') return 'closed';\n if (labels.includes('in-progress')) return 'in_progress';\n return 'open';\n }\n\n private mapStateToGitHub(\n state?: IssueState\n ): 'open' | 'closed' | 'all' {\n if (!state) return 'open';\n if (state === 'closed') return 'closed';\n return 'open'; // Both 'open' and 'in_progress' map to 'open'\n }\n}\n","/**\n * GitLab Issues Tracker Adapter (Stub)\n *\n * Placeholder implementation for GitLab Issues support.\n * Full implementation will use @gitbeaker/rest.\n */\n\nimport type {\n Issue,\n IssueFilters,\n IssueState,\n IssueTracker,\n IssueUpdate,\n NewIssue,\n Comment,\n TrackerType,\n} from './interface.js';\nimport { NotImplementedError } from './interface.js';\n\nexport class GitLabTracker implements IssueTracker {\n readonly name: TrackerType = 'gitlab';\n\n constructor(\n private token: string,\n private projectId: string\n ) {\n // Stub - will initialize @gitbeaker client when implemented\n }\n\n async listIssues(_filters?: IssueFilters): Promise<Issue[]> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async getIssue(_id: string): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async updateIssue(_id: string, _update: IssueUpdate): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async createIssue(_issue: NewIssue): Promise<Issue> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async getComments(_issueId: string): Promise<Comment[]> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async addComment(_issueId: string, _body: string): Promise<Comment> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async transitionIssue(_id: string, _state: IssueState): Promise<void> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n\n async linkPR(_issueId: string, _prUrl: string): Promise<void> {\n throw new NotImplementedError(\n 'GitLab tracker is not yet implemented. Coming soon!'\n );\n }\n}\n","/**\n * Tracker Factory\n *\n * Creates appropriate tracker instances based on configuration.\n */\n\nimport type { IssueTracker, TrackerType } from './interface.js';\nimport { TrackerAuthError } from './interface.js';\nimport { LinearTracker } from './linear.js';\nimport { GitHubTracker } from './github.js';\nimport { GitLabTracker } from './gitlab.js';\nimport { RallyTracker } from './rally.js';\nimport type { TrackersConfig } from '../config.js';\nimport { loadConfig as loadYamlConfig } from '../config-yaml.js';\n\n// Configuration for a single tracker\nexport interface TrackerConfig {\n type: TrackerType;\n\n // Linear-specific\n apiKeyEnv?: string;\n team?: string;\n\n // GitHub-specific\n tokenEnv?: string;\n owner?: string;\n repo?: string;\n\n // GitLab-specific\n projectId?: string;\n\n // Rally-specific\n server?: string;\n workspace?: string;\n project?: string;\n}\n\n// Multi-tracker configuration (re-exported from config.ts)\n// Note: Use TrackersConfig from config.ts for full type with nested configs\n\n/**\n * Get tracker API key from config.yaml (Settings page).\n * This is checked FIRST — env vars are the fallback, not the other way around.\n */\nfunction getTrackerKeyFromConfig(trackerType: TrackerType): string | undefined {\n try {\n const { config: yamlConfig } = loadYamlConfig();\n return yamlConfig.trackerKeys[trackerType];\n } catch {\n return undefined;\n }\n}\n\n/**\n * Create a tracker instance from configuration.\n * Priority: config.yaml (Settings) > environment variable > custom env var name\n */\nexport function createTracker(config: TrackerConfig): IssueTracker {\n switch (config.type) {\n case 'linear': {\n const configKey = getTrackerKeyFromConfig('linear');\n const envKey = config.apiKeyEnv\n ? process.env[config.apiKeyEnv]\n : process.env.LINEAR_API_KEY;\n const apiKey = configKey || envKey;\n\n if (!apiKey) {\n throw new TrackerAuthError(\n 'linear',\n `API key not found. Configure in Settings or set ${config.apiKeyEnv ?? 'LINEAR_API_KEY'} environment variable.`\n );\n }\n\n return new LinearTracker(apiKey, { team: config.team });\n }\n\n case 'github': {\n const configKey = getTrackerKeyFromConfig('github');\n const envToken = config.tokenEnv\n ? process.env[config.tokenEnv]\n : process.env.GITHUB_TOKEN;\n const token = configKey || envToken;\n\n if (!token) {\n throw new TrackerAuthError(\n 'github',\n `Token not found. Configure in Settings or set ${config.tokenEnv ?? 'GITHUB_TOKEN'} environment variable.`\n );\n }\n\n if (!config.owner || !config.repo) {\n throw new Error(\n 'GitHub tracker requires owner and repo configuration'\n );\n }\n\n return new GitHubTracker(token, config.owner, config.repo);\n }\n\n case 'gitlab': {\n const configKey = getTrackerKeyFromConfig('gitlab');\n const envToken = config.tokenEnv\n ? process.env[config.tokenEnv]\n : process.env.GITLAB_TOKEN;\n const token = configKey || envToken;\n\n if (!token) {\n throw new TrackerAuthError(\n 'gitlab',\n `Token not found. Configure in Settings or set ${config.tokenEnv ?? 'GITLAB_TOKEN'} environment variable.`\n );\n }\n\n if (!config.projectId) {\n throw new Error('GitLab tracker requires projectId configuration');\n }\n\n return new GitLabTracker(token, config.projectId);\n }\n\n case 'rally': {\n const configKey = getTrackerKeyFromConfig('rally');\n const envKey = config.apiKeyEnv\n ? process.env[config.apiKeyEnv]\n : process.env.RALLY_API_KEY;\n const apiKey = configKey || envKey;\n\n if (!apiKey) {\n throw new TrackerAuthError(\n 'rally',\n `API key not found. Configure in Settings or set ${config.apiKeyEnv ?? 'RALLY_API_KEY'} environment variable.`\n );\n }\n\n return new RallyTracker({\n apiKey,\n server: config.server,\n workspace: config.workspace,\n project: config.project,\n });\n }\n\n default:\n throw new Error(`Unknown tracker type: ${config.type}`);\n }\n}\n\n/**\n * Create tracker from trackers configuration section\n */\nexport function createTrackerFromConfig(\n trackersConfig: TrackersConfig,\n trackerType: TrackerType\n): IssueTracker {\n const config = trackersConfig[trackerType];\n\n if (!config) {\n throw new Error(\n `No configuration found for tracker: ${trackerType}. Add [trackers.${trackerType}] to config.`\n );\n }\n\n return createTracker({ ...config, type: trackerType });\n}\n\n/**\n * Get the primary tracker from configuration\n */\nexport function getPrimaryTracker(trackersConfig: TrackersConfig): IssueTracker {\n return createTrackerFromConfig(trackersConfig, trackersConfig.primary);\n}\n\n/**\n * Get the secondary tracker from configuration (if configured)\n */\nexport function getSecondaryTracker(\n trackersConfig: TrackersConfig\n): IssueTracker | null {\n if (!trackersConfig.secondary) {\n return null;\n }\n return createTrackerFromConfig(trackersConfig, trackersConfig.secondary);\n}\n\n/**\n * Get all configured trackers\n */\nexport function getAllTrackers(trackersConfig: TrackersConfig): IssueTracker[] {\n const trackers: IssueTracker[] = [getPrimaryTracker(trackersConfig)];\n\n const secondary = getSecondaryTracker(trackersConfig);\n if (secondary) {\n trackers.push(secondary);\n }\n\n return trackers;\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
- import { i as init_review_status } from "./review-status-Bymwzh2i.js";
2
- import { j as init_specialists } from "./specialists-B_zrayaP.js";
1
+ import { i as init_review_status } from "./review-status-d_wOE-XQ.js";
2
+ import { j as init_specialists } from "./specialists-saEYE0-z.js";
3
3
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "fs";
4
4
  import { dirname, join } from "path";
5
5
  import { homedir } from "os";
@@ -95,4 +95,4 @@ async function onInspectComplete(projectKey, issueId, beadId, status, workspaceP
95
95
  //#endregion
96
96
  export { onInspectComplete };
97
97
 
98
- //# sourceMappingURL=inspect-agent-7eour7EA.js.map
98
+ //# sourceMappingURL=inspect-agent-KKOeNR7E.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"inspect-agent-7eour7EA.js","names":["execAsync"],"sources":["../../src/lib/cloister/inspect-checkpoints.ts","../../src/lib/cloister/inspect-agent.ts"],"sourcesContent":["/**\n * PAN-382: Inspection checkpoint system.\n *\n * Tracks commit SHAs where inspections passed, scoping subsequent\n * inspection diffs to only the changes since the last checkpoint.\n *\n * First inspection: diff from branch base (main...HEAD)\n * Subsequent: diff from last checkpoint SHA to HEAD\n */\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\n\nconst execAsync = promisify(exec);\n\nconst PANOPTICON_HOME = join(homedir(), '.panopticon');\n\nexport interface InspectCheckpoint {\n beadId: string;\n commitSha: string;\n passedAt: string; // ISO 8601\n}\n\nexport interface InspectCheckpointFile {\n issueId: string;\n checkpoints: InspectCheckpoint[];\n}\n\n/**\n * Get the directory for a project's inspect checkpoints.\n */\nfunction getCheckpointDir(projectKey: string): string {\n return join(PANOPTICON_HOME, 'specialists', projectKey, 'inspect-agent', 'checkpoints');\n}\n\n/**\n * Get the checkpoint file path for an issue.\n */\nfunction getCheckpointPath(projectKey: string, issueId: string): string {\n return join(getCheckpointDir(projectKey), `${issueId.toUpperCase()}.json`);\n}\n\n/**\n * Load checkpoints for an issue. Returns null if no checkpoints exist.\n */\nexport function loadCheckpoints(projectKey: string, issueId: string): InspectCheckpointFile | null {\n const filePath = getCheckpointPath(projectKey, issueId);\n if (!existsSync(filePath)) return null;\n\n try {\n return JSON.parse(readFileSync(filePath, 'utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Get the last checkpoint for an issue, or null if none exist.\n */\nexport function getLastCheckpoint(projectKey: string, issueId: string): InspectCheckpoint | null {\n const data = loadCheckpoints(projectKey, issueId);\n if (!data || data.checkpoints.length === 0) return null;\n return data.checkpoints[data.checkpoints.length - 1];\n}\n\n/**\n * Save a new checkpoint after a successful inspection.\n */\nexport function saveCheckpoint(\n projectKey: string,\n issueId: string,\n beadId: string,\n commitSha: string\n): InspectCheckpoint {\n const dir = getCheckpointDir(projectKey);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const data = loadCheckpoints(projectKey, issueId) || {\n issueId: issueId.toUpperCase(),\n checkpoints: [],\n };\n\n const checkpoint: InspectCheckpoint = {\n beadId,\n commitSha,\n passedAt: new Date().toISOString(),\n };\n\n data.checkpoints.push(checkpoint);\n writeFileSync(getCheckpointPath(projectKey, issueId), JSON.stringify(data, null, 2));\n\n return checkpoint;\n}\n\n/**\n * Get the diff base for an inspection.\n *\n * - If a previous checkpoint exists, diff from that commit\n * - Otherwise, diff from the merge-base with main (full branch diff)\n *\n * Returns the commit SHA or ref to diff from.\n */\nexport async function getDiffBase(projectKey: string, issueId: string, workspacePath: string): Promise<string> {\n const lastCheckpoint = getLastCheckpoint(projectKey, issueId);\n\n if (lastCheckpoint) {\n return lastCheckpoint.commitSha;\n }\n\n // No checkpoint — use the merge-base with main\n try {\n const { stdout } = await execAsync('git merge-base main HEAD', {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n return stdout.trim();\n } catch {\n // Fallback to 'main' if merge-base fails\n return 'main';\n }\n}\n\n/**\n * Get the diff stats (files changed, insertions, deletions) for the inspection scope.\n */\nexport async function getDiffStats(workspacePath: string, diffBase: string): Promise<string> {\n try {\n const { stdout } = await execAsync(`git diff --stat ${diffBase}...HEAD`, {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n return stdout.trim() || 'No changes detected';\n } catch {\n return 'Unable to compute diff stats';\n }\n}\n\n/**\n * Get the current HEAD commit SHA.\n */\nexport async function getCurrentHead(workspacePath: string): Promise<string> {\n try {\n const { stdout } = await execAsync('git rev-parse HEAD', {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n return stdout.trim();\n } catch {\n return 'unknown';\n }\n}\n","/**\n * PAN-382: Inspect Agent — Per-step verification specialist.\n *\n * Spawns after each bead completion to verify the implementation matches\n * its specification and architectural constraints before the agent\n * proceeds to the next bead.\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport {\n getDiffBase,\n getDiffStats,\n getCurrentHead,\n saveCheckpoint,\n} from './inspect-checkpoints.js';\nimport { spawnEphemeralSpecialist, type SpecialistType } from './specialists.js';\nimport { setReviewStatus } from '../review-status.js';\n\nconst execAsync = promisify(exec);\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Context for an inspection request\n */\nexport interface InspectContext {\n projectKey: string;\n projectPath: string;\n issueId: string;\n beadId: string;\n workspace: string;\n branch?: string;\n}\n\n/**\n * Result of inspection\n */\nexport interface InspectResult {\n success: boolean;\n inspectResult: 'PASS' | 'BLOCKED';\n beadId: string;\n notes?: string;\n}\n\n/**\n * Read a bead's description using the bd CLI.\n */\nasync function getBeadDescription(beadId: string, workspacePath: string): Promise<string> {\n try {\n const { stdout } = await execAsync(`bd show ${beadId} --json`, {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n const bead = JSON.parse(stdout);\n const parts: string[] = [];\n if (bead.title) parts.push(`**Title:** ${bead.title}`);\n if (bead.description) parts.push(`**Description:** ${bead.description}`);\n if (bead.acceptance) parts.push(`**Acceptance Criteria:** ${bead.acceptance}`);\n if (bead.notes) parts.push(`**Notes:** ${bead.notes}`);\n if (bead.labels?.length) parts.push(`**Labels:** ${bead.labels.join(', ')}`);\n return parts.join('\\n\\n') || `Bead ${beadId} (no description available)`;\n } catch {\n // Fallback: try without --json\n try {\n const { stdout } = await execAsync(`bd show ${beadId}`, {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n return stdout.trim() || `Bead ${beadId} (no description available)`;\n } catch {\n return `Bead ${beadId} (unable to read bead description)`;\n }\n }\n}\n\n/**\n * Detect the compile/lint command for the workspace.\n */\nfunction detectCompileCommand(workspacePath: string): string {\n // Check for common project types\n const checks: Array<{ file: string; command: string }> = [\n { file: 'tsconfig.json', command: 'npx tsc --noEmit && npx eslint . --max-warnings=0 2>/dev/null || npx eslint .' },\n { file: 'package.json', command: 'npm run build 2>&1 | tail -20' },\n { file: 'pom.xml', command: './mvnw compile -q' },\n { file: 'Cargo.toml', command: 'cargo check' },\n { file: 'go.mod', command: 'go build ./...' },\n ];\n\n for (const check of checks) {\n // Check workspace root and common subdirectories\n for (const subdir of ['', 'fe', 'api', 'frontend', 'backend']) {\n const checkPath = subdir ? join(workspacePath, subdir, check.file) : join(workspacePath, check.file);\n if (existsSync(checkPath)) {\n const cwd = subdir ? `cd ${subdir} && ` : '';\n return `${cwd}${check.command}`;\n }\n }\n }\n\n return 'echo \"No compile command detected — skipping compile check\"';\n}\n\n/**\n * Build the prompt for the inspect specialist.\n */\nexport async function buildInspectPrompt(context: InspectContext): Promise<string> {\n const templatePath = join(__dirname, 'prompts', 'inspect-agent.md');\n\n if (!existsSync(templatePath)) {\n throw new Error(`Inspect agent prompt template not found at ${templatePath}`);\n }\n\n const template = readFileSync(templatePath, 'utf-8');\n\n // Get bead description\n const beadDescription = await getBeadDescription(context.beadId, context.workspace);\n\n // Get diff scope\n const diffBase = await getDiffBase(context.projectKey, context.issueId, context.workspace);\n const diffStats = await getDiffStats(context.workspace, diffBase);\n const compileCommand = detectCompileCommand(context.workspace);\n\n const apiUrl = process.env.DASHBOARD_URL || `http://localhost:${process.env.API_PORT || process.env.PORT || '3011'}`;\n\n // Replace template variables\n const prompt = template\n .replace(/\\{\\{apiUrl\\}\\}/g, apiUrl)\n .replace(/\\{\\{projectPath\\}\\}/g, context.projectPath)\n .replace(/\\{\\{issueId\\}\\}/g, context.issueId)\n .replace(/\\{\\{beadId\\}\\}/g, context.beadId)\n .replace(/\\{\\{workspacePath\\}\\}/g, context.workspace)\n .replace(/\\{\\{checkpoint\\}\\}/g, diffBase.substring(0, 8))\n .replace(/\\{\\{diffBase\\}\\}/g, diffBase)\n .replace(/\\{\\{diffStats\\}\\}/g, diffStats)\n .replace(/\\{\\{beadDescription\\}\\}/g, beadDescription)\n .replace(/\\{\\{compileCommand\\}\\}/g, compileCommand)\n .replace(/\\{\\{resultStatus\\}\\}/g, '${RESULT_STATUS}') // Placeholder for specialist to fill\n .replace(/\\{\\{resultNotes\\}\\}/g, '${RESULT_NOTES}'); // Placeholder for specialist to fill\n\n return `<!-- panopticon:orchestration-context-start -->\\n${prompt}\\n<!-- panopticon:orchestration-context-end -->`;\n}\n\n/**\n * Spawn the inspect specialist for a bead.\n */\nexport async function spawnInspectAgent(context: InspectContext): Promise<{\n success: boolean;\n runId?: string;\n tmuxSession?: string;\n message: string;\n error?: string;\n}> {\n // Build the prompt\n const prompt = await buildInspectPrompt(context);\n\n // Update status to inspecting\n setReviewStatus(context.issueId.toUpperCase(), {\n inspectStatus: 'inspecting',\n inspectNotes: `Inspecting bead ${context.beadId}`,\n });\n\n // Spawn the ephemeral specialist\n return spawnEphemeralSpecialist(context.projectKey, 'inspect-agent' as SpecialistType, {\n issueId: context.issueId,\n branch: context.branch,\n workspace: context.workspace,\n promptOverride: prompt,\n });\n}\n\n/**\n * Handle inspect completion — called when the inspect specialist signals done.\n * Saves checkpoint on PASS.\n */\nexport async function onInspectComplete(\n projectKey: string,\n issueId: string,\n beadId: string,\n status: 'passed' | 'failed',\n workspacePath: string\n): Promise<void> {\n if (status === 'passed') {\n const commitSha = await getCurrentHead(workspacePath);\n saveCheckpoint(projectKey, issueId, beadId, commitSha);\n console.log(`[inspect] Checkpoint saved for ${issueId} bead ${beadId} at ${commitSha.substring(0, 8)}`);\n\n } else {\n console.log(`[inspect] Bead ${beadId} blocked for ${issueId} — no checkpoint saved`);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAeA,MAAMA,cAAY,UAAU,KAAK;AAEjC,MAAM,kBAAkB,KAAK,SAAS,EAAE,cAAc;;;;AAgBtD,SAAS,iBAAiB,YAA4B;AACpD,QAAO,KAAK,iBAAiB,eAAe,YAAY,iBAAiB,cAAc;;;;;AAMzF,SAAS,kBAAkB,YAAoB,SAAyB;AACtE,QAAO,KAAK,iBAAiB,WAAW,EAAE,GAAG,QAAQ,aAAa,CAAC,OAAO;;;;;AAM5E,SAAgB,gBAAgB,YAAoB,SAA+C;CACjG,MAAM,WAAW,kBAAkB,YAAY,QAAQ;AACvD,KAAI,CAAC,WAAW,SAAS,CAAE,QAAO;AAElC,KAAI;AACF,SAAO,KAAK,MAAM,aAAa,UAAU,QAAQ,CAAC;SAC5C;AACN,SAAO;;;;;;AAgBX,SAAgB,eACd,YACA,SACA,QACA,WACmB;CACnB,MAAM,MAAM,iBAAiB,WAAW;AACxC,KAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CAGrC,MAAM,OAAO,gBAAgB,YAAY,QAAQ,IAAI;EACnD,SAAS,QAAQ,aAAa;EAC9B,aAAa,EAAE;EAChB;CAED,MAAM,aAAgC;EACpC;EACA;EACA,2BAAU,IAAI,MAAM,EAAC,aAAa;EACnC;AAED,MAAK,YAAY,KAAK,WAAW;AACjC,eAAc,kBAAkB,YAAY,QAAQ,EAAE,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;AAEpF,QAAO;;;;;AAiDT,eAAsB,eAAe,eAAwC;AAC3E,KAAI;EACF,MAAM,EAAE,WAAW,MAAMA,YAAU,sBAAsB;GACvD,KAAK;GACL,UAAU;GACX,CAAC;AACF,SAAO,OAAO,MAAM;SACd;AACN,SAAO;;;;;kBCrIsE;oBAC3B;AAEpC,UAAU,KAAK;AAGf,QADC,cAAc,OAAO,KAAK,IAAI,CACZ;;;;;AA0JrC,eAAsB,kBACpB,YACA,SACA,QACA,QACA,eACe;AACf,KAAI,WAAW,UAAU;EACvB,MAAM,YAAY,MAAM,eAAe,cAAc;AACrD,iBAAe,YAAY,SAAS,QAAQ,UAAU;AACtD,UAAQ,IAAI,kCAAkC,QAAQ,QAAQ,OAAO,MAAM,UAAU,UAAU,GAAG,EAAE,GAAG;OAGvG,SAAQ,IAAI,kBAAkB,OAAO,eAAe,QAAQ,wBAAwB"}
1
+ {"version":3,"file":"inspect-agent-KKOeNR7E.js","names":["execAsync"],"sources":["../../src/lib/cloister/inspect-checkpoints.ts","../../src/lib/cloister/inspect-agent.ts"],"sourcesContent":["/**\n * PAN-382: Inspection checkpoint system.\n *\n * Tracks commit SHAs where inspections passed, scoping subsequent\n * inspection diffs to only the changes since the last checkpoint.\n *\n * First inspection: diff from branch base (main...HEAD)\n * Subsequent: diff from last checkpoint SHA to HEAD\n */\nimport { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';\nimport { join } from 'path';\nimport { homedir } from 'os';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\n\nconst execAsync = promisify(exec);\n\nconst PANOPTICON_HOME = join(homedir(), '.panopticon');\n\nexport interface InspectCheckpoint {\n beadId: string;\n commitSha: string;\n passedAt: string; // ISO 8601\n}\n\nexport interface InspectCheckpointFile {\n issueId: string;\n checkpoints: InspectCheckpoint[];\n}\n\n/**\n * Get the directory for a project's inspect checkpoints.\n */\nfunction getCheckpointDir(projectKey: string): string {\n return join(PANOPTICON_HOME, 'specialists', projectKey, 'inspect-agent', 'checkpoints');\n}\n\n/**\n * Get the checkpoint file path for an issue.\n */\nfunction getCheckpointPath(projectKey: string, issueId: string): string {\n return join(getCheckpointDir(projectKey), `${issueId.toUpperCase()}.json`);\n}\n\n/**\n * Load checkpoints for an issue. Returns null if no checkpoints exist.\n */\nexport function loadCheckpoints(projectKey: string, issueId: string): InspectCheckpointFile | null {\n const filePath = getCheckpointPath(projectKey, issueId);\n if (!existsSync(filePath)) return null;\n\n try {\n return JSON.parse(readFileSync(filePath, 'utf-8'));\n } catch {\n return null;\n }\n}\n\n/**\n * Get the last checkpoint for an issue, or null if none exist.\n */\nexport function getLastCheckpoint(projectKey: string, issueId: string): InspectCheckpoint | null {\n const data = loadCheckpoints(projectKey, issueId);\n if (!data || data.checkpoints.length === 0) return null;\n return data.checkpoints[data.checkpoints.length - 1];\n}\n\n/**\n * Save a new checkpoint after a successful inspection.\n */\nexport function saveCheckpoint(\n projectKey: string,\n issueId: string,\n beadId: string,\n commitSha: string\n): InspectCheckpoint {\n const dir = getCheckpointDir(projectKey);\n if (!existsSync(dir)) {\n mkdirSync(dir, { recursive: true });\n }\n\n const data = loadCheckpoints(projectKey, issueId) || {\n issueId: issueId.toUpperCase(),\n checkpoints: [],\n };\n\n const checkpoint: InspectCheckpoint = {\n beadId,\n commitSha,\n passedAt: new Date().toISOString(),\n };\n\n data.checkpoints.push(checkpoint);\n writeFileSync(getCheckpointPath(projectKey, issueId), JSON.stringify(data, null, 2));\n\n return checkpoint;\n}\n\n/**\n * Get the diff base for an inspection.\n *\n * - If a previous checkpoint exists, diff from that commit\n * - Otherwise, diff from the merge-base with main (full branch diff)\n *\n * Returns the commit SHA or ref to diff from.\n */\nexport async function getDiffBase(projectKey: string, issueId: string, workspacePath: string): Promise<string> {\n const lastCheckpoint = getLastCheckpoint(projectKey, issueId);\n\n if (lastCheckpoint) {\n return lastCheckpoint.commitSha;\n }\n\n // No checkpoint — use the merge-base with main\n try {\n const { stdout } = await execAsync('git merge-base main HEAD', {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n return stdout.trim();\n } catch {\n // Fallback to 'main' if merge-base fails\n return 'main';\n }\n}\n\n/**\n * Get the diff stats (files changed, insertions, deletions) for the inspection scope.\n */\nexport async function getDiffStats(workspacePath: string, diffBase: string): Promise<string> {\n try {\n const { stdout } = await execAsync(`git diff --stat ${diffBase}...HEAD`, {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n return stdout.trim() || 'No changes detected';\n } catch {\n return 'Unable to compute diff stats';\n }\n}\n\n/**\n * Get the current HEAD commit SHA.\n */\nexport async function getCurrentHead(workspacePath: string): Promise<string> {\n try {\n const { stdout } = await execAsync('git rev-parse HEAD', {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n return stdout.trim();\n } catch {\n return 'unknown';\n }\n}\n","/**\n * PAN-382: Inspect Agent — Per-step verification specialist.\n *\n * Spawns after each bead completion to verify the implementation matches\n * its specification and architectural constraints before the agent\n * proceeds to the next bead.\n */\n\nimport { readFileSync, existsSync } from 'fs';\nimport { join, dirname } from 'path';\nimport { fileURLToPath } from 'url';\nimport { exec } from 'child_process';\nimport { promisify } from 'util';\nimport {\n getDiffBase,\n getDiffStats,\n getCurrentHead,\n saveCheckpoint,\n} from './inspect-checkpoints.js';\nimport { spawnEphemeralSpecialist, type SpecialistType } from './specialists.js';\nimport { setReviewStatus } from '../review-status.js';\n\nconst execAsync = promisify(exec);\n\nconst __filename = fileURLToPath(import.meta.url);\nconst __dirname = dirname(__filename);\n\n/**\n * Context for an inspection request\n */\nexport interface InspectContext {\n projectKey: string;\n projectPath: string;\n issueId: string;\n beadId: string;\n workspace: string;\n branch?: string;\n}\n\n/**\n * Result of inspection\n */\nexport interface InspectResult {\n success: boolean;\n inspectResult: 'PASS' | 'BLOCKED';\n beadId: string;\n notes?: string;\n}\n\n/**\n * Read a bead's description using the bd CLI.\n */\nasync function getBeadDescription(beadId: string, workspacePath: string): Promise<string> {\n try {\n const { stdout } = await execAsync(`bd show ${beadId} --json`, {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n const bead = JSON.parse(stdout);\n const parts: string[] = [];\n if (bead.title) parts.push(`**Title:** ${bead.title}`);\n if (bead.description) parts.push(`**Description:** ${bead.description}`);\n if (bead.acceptance) parts.push(`**Acceptance Criteria:** ${bead.acceptance}`);\n if (bead.notes) parts.push(`**Notes:** ${bead.notes}`);\n if (bead.labels?.length) parts.push(`**Labels:** ${bead.labels.join(', ')}`);\n return parts.join('\\n\\n') || `Bead ${beadId} (no description available)`;\n } catch {\n // Fallback: try without --json\n try {\n const { stdout } = await execAsync(`bd show ${beadId}`, {\n cwd: workspacePath,\n encoding: 'utf-8',\n });\n return stdout.trim() || `Bead ${beadId} (no description available)`;\n } catch {\n return `Bead ${beadId} (unable to read bead description)`;\n }\n }\n}\n\n/**\n * Detect the compile/lint command for the workspace.\n */\nfunction detectCompileCommand(workspacePath: string): string {\n // Check for common project types\n const checks: Array<{ file: string; command: string }> = [\n { file: 'tsconfig.json', command: 'npx tsc --noEmit && npx eslint . --max-warnings=0 2>/dev/null || npx eslint .' },\n { file: 'package.json', command: 'npm run build 2>&1 | tail -20' },\n { file: 'pom.xml', command: './mvnw compile -q' },\n { file: 'Cargo.toml', command: 'cargo check' },\n { file: 'go.mod', command: 'go build ./...' },\n ];\n\n for (const check of checks) {\n // Check workspace root and common subdirectories\n for (const subdir of ['', 'fe', 'api', 'frontend', 'backend']) {\n const checkPath = subdir ? join(workspacePath, subdir, check.file) : join(workspacePath, check.file);\n if (existsSync(checkPath)) {\n const cwd = subdir ? `cd ${subdir} && ` : '';\n return `${cwd}${check.command}`;\n }\n }\n }\n\n return 'echo \"No compile command detected — skipping compile check\"';\n}\n\n/**\n * Build the prompt for the inspect specialist.\n */\nexport async function buildInspectPrompt(context: InspectContext): Promise<string> {\n const templatePath = join(__dirname, 'prompts', 'inspect-agent.md');\n\n if (!existsSync(templatePath)) {\n throw new Error(`Inspect agent prompt template not found at ${templatePath}`);\n }\n\n const template = readFileSync(templatePath, 'utf-8');\n\n // Get bead description\n const beadDescription = await getBeadDescription(context.beadId, context.workspace);\n\n // Get diff scope\n const diffBase = await getDiffBase(context.projectKey, context.issueId, context.workspace);\n const diffStats = await getDiffStats(context.workspace, diffBase);\n const compileCommand = detectCompileCommand(context.workspace);\n\n const apiUrl = process.env.DASHBOARD_URL || `http://localhost:${process.env.API_PORT || process.env.PORT || '3011'}`;\n\n // Replace template variables\n const prompt = template\n .replace(/\\{\\{apiUrl\\}\\}/g, apiUrl)\n .replace(/\\{\\{projectPath\\}\\}/g, context.projectPath)\n .replace(/\\{\\{issueId\\}\\}/g, context.issueId)\n .replace(/\\{\\{beadId\\}\\}/g, context.beadId)\n .replace(/\\{\\{workspacePath\\}\\}/g, context.workspace)\n .replace(/\\{\\{checkpoint\\}\\}/g, diffBase.substring(0, 8))\n .replace(/\\{\\{diffBase\\}\\}/g, diffBase)\n .replace(/\\{\\{diffStats\\}\\}/g, diffStats)\n .replace(/\\{\\{beadDescription\\}\\}/g, beadDescription)\n .replace(/\\{\\{compileCommand\\}\\}/g, compileCommand)\n .replace(/\\{\\{resultStatus\\}\\}/g, '${RESULT_STATUS}') // Placeholder for specialist to fill\n .replace(/\\{\\{resultNotes\\}\\}/g, '${RESULT_NOTES}'); // Placeholder for specialist to fill\n\n return `<!-- panopticon:orchestration-context-start -->\\n${prompt}\\n<!-- panopticon:orchestration-context-end -->`;\n}\n\n/**\n * Spawn the inspect specialist for a bead.\n */\nexport async function spawnInspectAgent(context: InspectContext): Promise<{\n success: boolean;\n runId?: string;\n tmuxSession?: string;\n message: string;\n error?: string;\n}> {\n // Build the prompt\n const prompt = await buildInspectPrompt(context);\n\n // Update status to inspecting\n setReviewStatus(context.issueId.toUpperCase(), {\n inspectStatus: 'inspecting',\n inspectNotes: `Inspecting bead ${context.beadId}`,\n });\n\n // Spawn the ephemeral specialist\n return spawnEphemeralSpecialist(context.projectKey, 'inspect-agent' as SpecialistType, {\n issueId: context.issueId,\n branch: context.branch,\n workspace: context.workspace,\n promptOverride: prompt,\n });\n}\n\n/**\n * Handle inspect completion — called when the inspect specialist signals done.\n * Saves checkpoint on PASS.\n */\nexport async function onInspectComplete(\n projectKey: string,\n issueId: string,\n beadId: string,\n status: 'passed' | 'failed',\n workspacePath: string\n): Promise<void> {\n if (status === 'passed') {\n const commitSha = await getCurrentHead(workspacePath);\n saveCheckpoint(projectKey, issueId, beadId, commitSha);\n console.log(`[inspect] Checkpoint saved for ${issueId} bead ${beadId} at ${commitSha.substring(0, 8)}`);\n\n } else {\n console.log(`[inspect] Bead ${beadId} blocked for ${issueId} — no checkpoint saved`);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAeA,MAAMA,cAAY,UAAU,KAAK;AAEjC,MAAM,kBAAkB,KAAK,SAAS,EAAE,cAAc;;;;AAgBtD,SAAS,iBAAiB,YAA4B;AACpD,QAAO,KAAK,iBAAiB,eAAe,YAAY,iBAAiB,cAAc;;;;;AAMzF,SAAS,kBAAkB,YAAoB,SAAyB;AACtE,QAAO,KAAK,iBAAiB,WAAW,EAAE,GAAG,QAAQ,aAAa,CAAC,OAAO;;;;;AAM5E,SAAgB,gBAAgB,YAAoB,SAA+C;CACjG,MAAM,WAAW,kBAAkB,YAAY,QAAQ;AACvD,KAAI,CAAC,WAAW,SAAS,CAAE,QAAO;AAElC,KAAI;AACF,SAAO,KAAK,MAAM,aAAa,UAAU,QAAQ,CAAC;SAC5C;AACN,SAAO;;;;;;AAgBX,SAAgB,eACd,YACA,SACA,QACA,WACmB;CACnB,MAAM,MAAM,iBAAiB,WAAW;AACxC,KAAI,CAAC,WAAW,IAAI,CAClB,WAAU,KAAK,EAAE,WAAW,MAAM,CAAC;CAGrC,MAAM,OAAO,gBAAgB,YAAY,QAAQ,IAAI;EACnD,SAAS,QAAQ,aAAa;EAC9B,aAAa,EAAE;EAChB;CAED,MAAM,aAAgC;EACpC;EACA;EACA,2BAAU,IAAI,MAAM,EAAC,aAAa;EACnC;AAED,MAAK,YAAY,KAAK,WAAW;AACjC,eAAc,kBAAkB,YAAY,QAAQ,EAAE,KAAK,UAAU,MAAM,MAAM,EAAE,CAAC;AAEpF,QAAO;;;;;AAiDT,eAAsB,eAAe,eAAwC;AAC3E,KAAI;EACF,MAAM,EAAE,WAAW,MAAMA,YAAU,sBAAsB;GACvD,KAAK;GACL,UAAU;GACX,CAAC;AACF,SAAO,OAAO,MAAM;SACd;AACN,SAAO;;;;;kBCrIsE;oBAC3B;AAEpC,UAAU,KAAK;AAGf,QADC,cAAc,OAAO,KAAK,IAAI,CACZ;;;;;AA0JrC,eAAsB,kBACpB,YACA,SACA,QACA,QACA,eACe;AACf,KAAI,WAAW,UAAU;EACvB,MAAM,YAAY,MAAM,eAAe,cAAc;AACrD,iBAAe,YAAY,SAAS,QAAQ,UAAU;AACtD,UAAQ,IAAI,kCAAkC,QAAQ,QAAQ,OAAO,MAAM,UAAU,UAAU,GAAG,EAAE,GAAG;OAGvG,SAAQ,IAAI,kBAAkB,OAAO,eAAe,QAAQ,wBAAwB"}
@@ -1,7 +1,7 @@
1
1
  import { n as __esmMin, r as __exportAll } from "./chunk-DORXReHP.js";
2
- import { a as validateRallyConfig, i as init_tracker_config, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-e7ph1QqT.js";
2
+ import { a as validateRallyConfig, i as init_tracker_config, n as getLinearApiKey, r as getRallyConfig, t as getGitHubConfig } from "./tracker-config-tD22z5sv.js";
3
3
  import { n as init_dist_src, t as Octokit } from "./dist-src-DTm11oQr.js";
4
- import { a as loadReviewStatuses, i as init_review_status } from "./review-status-Bymwzh2i.js";
4
+ import { a as loadReviewStatuses, i as init_review_status } from "./review-status-d_wOE-XQ.js";
5
5
  import { existsSync, mkdirSync } from "fs";
6
6
  import { join } from "path";
7
7
  import { homedir } from "os";
@@ -1123,4 +1123,4 @@ var init_issue_service_singleton = __esmMin((() => {
1123
1123
  //#endregion
1124
1124
  export { startSharedIssueService as i, init_issue_service_singleton as n, issue_service_singleton_exports as r, getSharedIssueService as t };
1125
1125
 
1126
- //# sourceMappingURL=issue-service-singleton-Wv4xBm3y.js.map
1126
+ //# sourceMappingURL=issue-service-singleton-BCZ62hLj.js.map