@jiraacp/cli 2026.405.4

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 (113) hide show
  1. package/README.md +283 -0
  2. package/dist/abort-GQE4OI5S.js +103 -0
  3. package/dist/abort-GQE4OI5S.js.map +1 -0
  4. package/dist/abort-VMRQOADY.js +96 -0
  5. package/dist/abort-VMRQOADY.js.map +1 -0
  6. package/dist/bot-WOTETAJY.js +13 -0
  7. package/dist/bot-WOTETAJY.js.map +1 -0
  8. package/dist/cancel-clarification-4G5S2HJZ.js +64 -0
  9. package/dist/cancel-clarification-4G5S2HJZ.js.map +1 -0
  10. package/dist/chunk-3U373M37.js +67 -0
  11. package/dist/chunk-3U373M37.js.map +1 -0
  12. package/dist/chunk-3YHD4SIN.js +97 -0
  13. package/dist/chunk-3YHD4SIN.js.map +1 -0
  14. package/dist/chunk-6IY6CRUJ.js +690 -0
  15. package/dist/chunk-6IY6CRUJ.js.map +1 -0
  16. package/dist/chunk-B6OA3XJK.js +1167 -0
  17. package/dist/chunk-B6OA3XJK.js.map +1 -0
  18. package/dist/chunk-BM4R6NST.js +191 -0
  19. package/dist/chunk-BM4R6NST.js.map +1 -0
  20. package/dist/chunk-FLPIU2QO.js +77 -0
  21. package/dist/chunk-FLPIU2QO.js.map +1 -0
  22. package/dist/chunk-H7YXX4UA.js +86 -0
  23. package/dist/chunk-H7YXX4UA.js.map +1 -0
  24. package/dist/chunk-IT74N3UH.js +19 -0
  25. package/dist/chunk-IT74N3UH.js.map +1 -0
  26. package/dist/chunk-JOT4UVSO.js +186 -0
  27. package/dist/chunk-JOT4UVSO.js.map +1 -0
  28. package/dist/chunk-KSJKCLEJ.js +222 -0
  29. package/dist/chunk-KSJKCLEJ.js.map +1 -0
  30. package/dist/chunk-LIEW4ULF.js +139 -0
  31. package/dist/chunk-LIEW4ULF.js.map +1 -0
  32. package/dist/chunk-M4V3YOCY.js +82 -0
  33. package/dist/chunk-M4V3YOCY.js.map +1 -0
  34. package/dist/chunk-MMWQHH25.js +207 -0
  35. package/dist/chunk-MMWQHH25.js.map +1 -0
  36. package/dist/chunk-OJ4CNF73.js +78 -0
  37. package/dist/chunk-OJ4CNF73.js.map +1 -0
  38. package/dist/chunk-PFJAC3RO.js +137 -0
  39. package/dist/chunk-PFJAC3RO.js.map +1 -0
  40. package/dist/chunk-PVKVCUNR.js +159 -0
  41. package/dist/chunk-PVKVCUNR.js.map +1 -0
  42. package/dist/chunk-RXT4WSIY.js +35 -0
  43. package/dist/chunk-RXT4WSIY.js.map +1 -0
  44. package/dist/chunk-RZK74PDF.js +34 -0
  45. package/dist/chunk-RZK74PDF.js.map +1 -0
  46. package/dist/chunk-UDTWVKRX.js +68 -0
  47. package/dist/chunk-UDTWVKRX.js.map +1 -0
  48. package/dist/chunk-VCEONSWJ.js +307 -0
  49. package/dist/chunk-VCEONSWJ.js.map +1 -0
  50. package/dist/chunk-VWBCDZWQ.js +119 -0
  51. package/dist/chunk-VWBCDZWQ.js.map +1 -0
  52. package/dist/chunk-WEJCTFQB.js +228 -0
  53. package/dist/chunk-WEJCTFQB.js.map +1 -0
  54. package/dist/chunk-YJK7IRPI.js +223 -0
  55. package/dist/chunk-YJK7IRPI.js.map +1 -0
  56. package/dist/claude-md-HQ6L4CRP.js +8 -0
  57. package/dist/claude-md-HQ6L4CRP.js.map +1 -0
  58. package/dist/cli.js +276 -0
  59. package/dist/cli.js.map +1 -0
  60. package/dist/commands-RG45VBTZ.js +407 -0
  61. package/dist/commands-RG45VBTZ.js.map +1 -0
  62. package/dist/commands-WYVRVE5Z.js +400 -0
  63. package/dist/commands-WYVRVE5Z.js.map +1 -0
  64. package/dist/config-edit-G7O56HXO.js +50 -0
  65. package/dist/config-edit-G7O56HXO.js.map +1 -0
  66. package/dist/config-set-QN3JRNZL.js +63 -0
  67. package/dist/config-set-QN3JRNZL.js.map +1 -0
  68. package/dist/daemon-CGBV55JK.js +104 -0
  69. package/dist/daemon-CGBV55JK.js.map +1 -0
  70. package/dist/dashboard-YVFJ5DXR.js +143 -0
  71. package/dist/dashboard-YVFJ5DXR.js.map +1 -0
  72. package/dist/doctor-BPTLVLTD.js +98 -0
  73. package/dist/doctor-BPTLVLTD.js.map +1 -0
  74. package/dist/human-loop-RBTA2TYK.js +16 -0
  75. package/dist/human-loop-RBTA2TYK.js.map +1 -0
  76. package/dist/human-loop-XGWXUNCS.js +18 -0
  77. package/dist/human-loop-XGWXUNCS.js.map +1 -0
  78. package/dist/index.d.ts +583 -0
  79. package/dist/index.js +28 -0
  80. package/dist/index.js.map +1 -0
  81. package/dist/loader-DGW7HCJ5.js +21 -0
  82. package/dist/loader-DGW7HCJ5.js.map +1 -0
  83. package/dist/logs-JUVQWN6C.js +93 -0
  84. package/dist/logs-JUVQWN6C.js.map +1 -0
  85. package/dist/mcp.js +132 -0
  86. package/dist/mcp.js.map +1 -0
  87. package/dist/orchestrator-3MGXX3QW.js +22 -0
  88. package/dist/orchestrator-3MGXX3QW.js.map +1 -0
  89. package/dist/orchestrator-BVUKN5N3.js +13 -0
  90. package/dist/orchestrator-BVUKN5N3.js.map +1 -0
  91. package/dist/pause-FLDZ3OD6.js +62 -0
  92. package/dist/pause-FLDZ3OD6.js.map +1 -0
  93. package/dist/projects-QMIGNW7U.js +129 -0
  94. package/dist/projects-QMIGNW7U.js.map +1 -0
  95. package/dist/replay-M4JEG4Z4.js +151 -0
  96. package/dist/replay-M4JEG4Z4.js.map +1 -0
  97. package/dist/schedule-CDHD77VZ.js +17 -0
  98. package/dist/schedule-CDHD77VZ.js.map +1 -0
  99. package/dist/serve-XI7JTIPZ.js +231 -0
  100. package/dist/serve-XI7JTIPZ.js.map +1 -0
  101. package/dist/sprint-KZZWVNK6.js +200 -0
  102. package/dist/sprint-KZZWVNK6.js.map +1 -0
  103. package/dist/status-I6GU2LWE.js +48 -0
  104. package/dist/status-I6GU2LWE.js.map +1 -0
  105. package/dist/topic-manager-4AMEPMFI.js +12 -0
  106. package/dist/topic-manager-4AMEPMFI.js.map +1 -0
  107. package/dist/triage-WNHGPVZQ.js +251 -0
  108. package/dist/triage-WNHGPVZQ.js.map +1 -0
  109. package/dist/usage-AWWBI37F.js +155 -0
  110. package/dist/usage-AWWBI37F.js.map +1 -0
  111. package/dist/wizard-CYEJJLNF.js +190 -0
  112. package/dist/wizard-CYEJJLNF.js.map +1 -0
  113. package/package.json +56 -0
@@ -0,0 +1,186 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/integrations/jira/client.ts
4
+ import axios from "axios";
5
+ function loadInstances() {
6
+ const instances = {};
7
+ for (const key of Object.keys(process.env)) {
8
+ const match = key.match(/^JIRA_([A-Z0-9]+)_URL$/);
9
+ if (!match) continue;
10
+ const name = match[1].toLowerCase();
11
+ const upper = match[1];
12
+ const url = process.env[`JIRA_${upper}_URL`];
13
+ const token = process.env[`JIRA_${upper}_TOKEN`];
14
+ const email = process.env[`JIRA_${upper}_EMAIL`];
15
+ if (url && token && email) {
16
+ instances[name] = { url, token, email };
17
+ }
18
+ }
19
+ return instances;
20
+ }
21
+ var INSTANCES = loadInstances();
22
+ function getClient(instance) {
23
+ const config = INSTANCES[instance];
24
+ if (!config) {
25
+ const available = Object.keys(INSTANCES).join(", ") || "none";
26
+ throw new Error(
27
+ `Unknown Jira instance: "${instance}". Available: ${available}`
28
+ );
29
+ }
30
+ return axios.create({
31
+ baseURL: `${config.url}/rest/api/3`,
32
+ headers: {
33
+ Authorization: `Basic ${Buffer.from(`${config.email}:${config.token}`).toString("base64")}`,
34
+ "Content-Type": "application/json",
35
+ Accept: "application/json"
36
+ }
37
+ });
38
+ }
39
+
40
+ // src/integrations/jira/tools.ts
41
+ import { z } from "zod";
42
+ var instanceSchema = z.string().describe('Instance name (e.g. "hi", "geo")');
43
+ var GetTasksSchema = z.object({
44
+ instance: instanceSchema,
45
+ assignees: z.array(z.string()).describe("List of Jira usernames or accountIds"),
46
+ project_key: z.string().optional().describe('Filter by project key (e.g. "HI")'),
47
+ status: z.string().optional().describe('Filter by status (e.g. "In Progress")'),
48
+ max_results: z.number().default(20)
49
+ });
50
+ var GetTicketSchema = z.object({
51
+ instance: instanceSchema,
52
+ ticket_key: z.string().describe('Jira ticket key (e.g. "HI-123")')
53
+ });
54
+ var GetTransitionsSchema = z.object({
55
+ instance: instanceSchema,
56
+ ticket_key: z.string()
57
+ });
58
+ var TransitionTicketSchema = z.object({
59
+ instance: instanceSchema,
60
+ ticket_key: z.string(),
61
+ transition_name: z.string().describe('Transition name (e.g. "In Review", "Done")')
62
+ });
63
+ var AddCommentSchema = z.object({
64
+ instance: instanceSchema,
65
+ ticket_key: z.string(),
66
+ comment: z.string().describe("Comment text (plain text or Jira markdown)")
67
+ });
68
+ var ReassignSchema = z.object({
69
+ instance: instanceSchema,
70
+ ticket_key: z.string(),
71
+ account_id: z.string().describe("Jira accountId of the new assignee")
72
+ });
73
+ async function getTasks(args) {
74
+ const client = getClient(args.instance);
75
+ const conditions = [
76
+ args.assignees.map((a) => `assignee = "${a}"`).join(" OR "),
77
+ args.project_key ? `project = ${args.project_key}` : null,
78
+ args.status ? `status = "${args.status}"` : null
79
+ ].filter(Boolean);
80
+ const jql = conditions.join(" AND ") + " ORDER BY updated DESC";
81
+ const { data } = await client.get("/search", {
82
+ params: {
83
+ jql,
84
+ maxResults: args.max_results,
85
+ fields: "summary,status,assignee,priority,description,created,updated"
86
+ }
87
+ });
88
+ const issues = data.issues.map((issue) => ({
89
+ key: issue.key,
90
+ summary: issue.fields.summary,
91
+ status: issue.fields.status.name,
92
+ assignee: issue.fields.assignee?.displayName ?? "Unassigned",
93
+ priority: issue.fields.priority?.name ?? "None"
94
+ }));
95
+ return JSON.stringify({ total: data.total, issues }, null, 2);
96
+ }
97
+ async function getTicket(args) {
98
+ const client = getClient(args.instance);
99
+ const { data } = await client.get(`/issue/${args.ticket_key}`);
100
+ return JSON.stringify(
101
+ {
102
+ key: data.key,
103
+ summary: data.fields.summary,
104
+ status: data.fields.status.name,
105
+ assignee: data.fields.assignee?.displayName ?? "Unassigned",
106
+ priority: data.fields.priority?.name,
107
+ description: extractText(data.fields.description),
108
+ acceptance_criteria: extractText(data.fields.customfield_10016),
109
+ created: data.fields.created,
110
+ updated: data.fields.updated
111
+ },
112
+ null,
113
+ 2
114
+ );
115
+ }
116
+ async function getTransitions(args) {
117
+ const client = getClient(args.instance);
118
+ const { data } = await client.get(`/issue/${args.ticket_key}/transitions`);
119
+ const transitions = data.transitions.map((t) => ({
120
+ id: t.id,
121
+ name: t.name
122
+ }));
123
+ return JSON.stringify(transitions, null, 2);
124
+ }
125
+ async function transitionTicket(args) {
126
+ const client = getClient(args.instance);
127
+ const { data } = await client.get(`/issue/${args.ticket_key}/transitions`);
128
+ const transition = data.transitions.find(
129
+ (t) => t.name.toLowerCase() === args.transition_name.toLowerCase()
130
+ );
131
+ if (!transition) {
132
+ const available = data.transitions.map((t) => t.name).join(", ");
133
+ throw new Error(
134
+ `Transition "${args.transition_name}" not found. Available: ${available}`
135
+ );
136
+ }
137
+ await client.post(`/issue/${args.ticket_key}/transitions`, {
138
+ transition: { id: transition.id }
139
+ });
140
+ return `Transitioned ${args.ticket_key} to "${transition.name}"`;
141
+ }
142
+ async function addComment(args) {
143
+ const client = getClient(args.instance);
144
+ await client.post(`/issue/${args.ticket_key}/comment`, {
145
+ body: {
146
+ type: "doc",
147
+ version: 1,
148
+ content: [
149
+ { type: "paragraph", content: [{ type: "text", text: args.comment }] }
150
+ ]
151
+ }
152
+ });
153
+ return `Comment added to ${args.ticket_key}`;
154
+ }
155
+ async function reassign(args) {
156
+ const client = getClient(args.instance);
157
+ await client.put(`/issue/${args.ticket_key}/assignee`, {
158
+ accountId: args.account_id
159
+ });
160
+ return `${args.ticket_key} reassigned to accountId: ${args.account_id}`;
161
+ }
162
+ function extractText(adfNode) {
163
+ if (!adfNode) return "";
164
+ if (typeof adfNode === "string") return adfNode;
165
+ if (adfNode.type === "text") return adfNode.text ?? "";
166
+ if (adfNode.content) return adfNode.content.map(extractText).join(" ");
167
+ return "";
168
+ }
169
+
170
+ export {
171
+ INSTANCES,
172
+ getClient,
173
+ GetTasksSchema,
174
+ GetTicketSchema,
175
+ GetTransitionsSchema,
176
+ TransitionTicketSchema,
177
+ AddCommentSchema,
178
+ ReassignSchema,
179
+ getTasks,
180
+ getTicket,
181
+ getTransitions,
182
+ transitionTicket,
183
+ addComment,
184
+ reassign
185
+ };
186
+ //# sourceMappingURL=chunk-JOT4UVSO.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/integrations/jira/client.ts","../src/integrations/jira/tools.ts"],"sourcesContent":["import axios, { type AxiosInstance } from \"axios\";\n\nexport interface JiraConfig {\n url: string;\n token: string;\n email: string;\n}\n\n// Auto-discover instances from env vars: JIRA_{NAME}_URL, JIRA_{NAME}_TOKEN, JIRA_{NAME}_EMAIL\nfunction loadInstances(): Record<string, JiraConfig> {\n const instances: Record<string, JiraConfig> = {};\n\n for (const key of Object.keys(process.env)) {\n const match = key.match(/^JIRA_([A-Z0-9]+)_URL$/);\n if (!match) continue;\n\n const name = match[1].toLowerCase();\n const upper = match[1];\n const url = process.env[`JIRA_${upper}_URL`];\n const token = process.env[`JIRA_${upper}_TOKEN`];\n const email = process.env[`JIRA_${upper}_EMAIL`];\n\n if (url && token && email) {\n instances[name] = { url, token, email };\n }\n }\n\n return instances;\n}\n\nexport const INSTANCES = loadInstances();\n\nexport type JiraClient = AxiosInstance;\n\nexport function getClient(instance: string): JiraClient {\n const config = INSTANCES[instance];\n if (!config) {\n const available = Object.keys(INSTANCES).join(\", \") || \"none\";\n throw new Error(\n `Unknown Jira instance: \"${instance}\". Available: ${available}`,\n );\n }\n\n return axios.create({\n baseURL: `${config.url}/rest/api/3`,\n headers: {\n Authorization: `Basic ${Buffer.from(`${config.email}:${config.token}`).toString(\"base64\")}`,\n \"Content-Type\": \"application/json\",\n Accept: \"application/json\",\n },\n });\n}\n","import { z } from \"zod\";\nimport { getClient } from \"./client.js\";\n\n// ─── Schemas ────────────────────────────────────────────────────────────────\n\nconst instanceSchema = z.string().describe('Instance name (e.g. \"hi\", \"geo\")');\n\nexport const GetTasksSchema = z.object({\n instance: instanceSchema,\n assignees: z\n .array(z.string())\n .describe(\"List of Jira usernames or accountIds\"),\n project_key: z\n .string()\n .optional()\n .describe('Filter by project key (e.g. \"HI\")'),\n status: z\n .string()\n .optional()\n .describe('Filter by status (e.g. \"In Progress\")'),\n max_results: z.number().default(20),\n});\n\nexport const GetTicketSchema = z.object({\n instance: instanceSchema,\n ticket_key: z.string().describe('Jira ticket key (e.g. \"HI-123\")'),\n});\n\nexport const GetTransitionsSchema = z.object({\n instance: instanceSchema,\n ticket_key: z.string(),\n});\n\nexport const TransitionTicketSchema = z.object({\n instance: instanceSchema,\n ticket_key: z.string(),\n transition_name: z\n .string()\n .describe('Transition name (e.g. \"In Review\", \"Done\")'),\n});\n\nexport const AddCommentSchema = z.object({\n instance: instanceSchema,\n ticket_key: z.string(),\n comment: z.string().describe(\"Comment text (plain text or Jira markdown)\"),\n});\n\nexport const ReassignSchema = z.object({\n instance: instanceSchema,\n ticket_key: z.string(),\n account_id: z.string().describe(\"Jira accountId of the new assignee\"),\n});\n\n// ─── Tool implementations ────────────────────────────────────────────────────\n\nexport async function getTasks(\n args: z.infer<typeof GetTasksSchema>,\n): Promise<string> {\n const client = getClient(args.instance);\n\n const conditions = [\n args.assignees.map((a) => `assignee = \"${a}\"`).join(\" OR \"),\n args.project_key ? `project = ${args.project_key}` : null,\n args.status ? `status = \"${args.status}\"` : null,\n ].filter(Boolean);\n\n const jql = conditions.join(\" AND \") + \" ORDER BY updated DESC\";\n\n const { data } = await client.get(\"/search\", {\n params: {\n jql,\n maxResults: args.max_results,\n fields: \"summary,status,assignee,priority,description,created,updated\",\n },\n });\n\n const issues = data.issues.map((issue: JiraIssue) => ({\n key: issue.key,\n summary: issue.fields.summary,\n status: issue.fields.status.name,\n assignee: issue.fields.assignee?.displayName ?? \"Unassigned\",\n priority: issue.fields.priority?.name ?? \"None\",\n }));\n\n return JSON.stringify({ total: data.total, issues }, null, 2);\n}\n\nexport async function getTicket(\n args: z.infer<typeof GetTicketSchema>,\n): Promise<string> {\n const client = getClient(args.instance);\n const { data } = await client.get(`/issue/${args.ticket_key}`);\n\n return JSON.stringify(\n {\n key: data.key,\n summary: data.fields.summary,\n status: data.fields.status.name,\n assignee: data.fields.assignee?.displayName ?? \"Unassigned\",\n priority: data.fields.priority?.name,\n description: extractText(data.fields.description),\n acceptance_criteria: extractText(data.fields.customfield_10016),\n created: data.fields.created,\n updated: data.fields.updated,\n },\n null,\n 2,\n );\n}\n\nexport async function getTransitions(\n args: z.infer<typeof GetTransitionsSchema>,\n): Promise<string> {\n const client = getClient(args.instance);\n const { data } = await client.get(`/issue/${args.ticket_key}/transitions`);\n const transitions = data.transitions.map((t: JiraTransition) => ({\n id: t.id,\n name: t.name,\n }));\n return JSON.stringify(transitions, null, 2);\n}\n\nexport async function transitionTicket(\n args: z.infer<typeof TransitionTicketSchema>,\n): Promise<string> {\n const client = getClient(args.instance);\n\n const { data } = await client.get(`/issue/${args.ticket_key}/transitions`);\n const transition = data.transitions.find(\n (t: JiraTransition) =>\n t.name.toLowerCase() === args.transition_name.toLowerCase(),\n );\n\n if (!transition) {\n const available = data.transitions\n .map((t: JiraTransition) => t.name)\n .join(\", \");\n throw new Error(\n `Transition \"${args.transition_name}\" not found. Available: ${available}`,\n );\n }\n\n await client.post(`/issue/${args.ticket_key}/transitions`, {\n transition: { id: transition.id },\n });\n return `Transitioned ${args.ticket_key} to \"${transition.name}\"`;\n}\n\nexport async function addComment(\n args: z.infer<typeof AddCommentSchema>,\n): Promise<string> {\n const client = getClient(args.instance);\n\n await client.post(`/issue/${args.ticket_key}/comment`, {\n body: {\n type: \"doc\",\n version: 1,\n content: [\n { type: \"paragraph\", content: [{ type: \"text\", text: args.comment }] },\n ],\n },\n });\n\n return `Comment added to ${args.ticket_key}`;\n}\n\nexport async function reassign(\n args: z.infer<typeof ReassignSchema>,\n): Promise<string> {\n const client = getClient(args.instance);\n await client.put(`/issue/${args.ticket_key}/assignee`, {\n accountId: args.account_id,\n });\n return `${args.ticket_key} reassigned to accountId: ${args.account_id}`;\n}\n\n// ─── Helpers ────────────────────────────────────────────────────────────────\n\nfunction extractText(adfNode: AdfNode | null | undefined): string {\n if (!adfNode) return \"\";\n if (typeof adfNode === \"string\") return adfNode;\n if (adfNode.type === \"text\") return adfNode.text ?? \"\";\n if (adfNode.content) return adfNode.content.map(extractText).join(\" \");\n return \"\";\n}\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\ninterface JiraIssue {\n key: string;\n fields: {\n summary: string;\n status: { name: string };\n assignee: { displayName: string } | null;\n priority: { name: string } | null;\n description: AdfNode | null;\n customfield_10016: AdfNode | null;\n created: string;\n updated: string;\n };\n}\n\ninterface JiraTransition {\n id: string;\n name: string;\n}\n\ninterface AdfNode {\n type: string;\n text?: string;\n content?: AdfNode[];\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,222 @@
1
+ import {
2
+ getBot
3
+ } from "./chunk-M4V3YOCY.js";
4
+
5
+ // src/integrations/telegram/human-loop.ts
6
+ import fs from "fs";
7
+ import path from "path";
8
+ var pending = /* @__PURE__ */ new Map();
9
+ function loadPendingStore(storeDir) {
10
+ const storePath = path.join(storeDir, "pending-clarifications.json");
11
+ if (!fs.existsSync(storePath)) return;
12
+ const items = JSON.parse(
13
+ fs.readFileSync(storePath, "utf8")
14
+ );
15
+ for (const item of items) {
16
+ if (new Date(item.expiresAt) > /* @__PURE__ */ new Date()) {
17
+ pending.set(item.ticketKey, {
18
+ ...item,
19
+ resolve: () => {
20
+ },
21
+ reject: () => {
22
+ }
23
+ });
24
+ }
25
+ }
26
+ }
27
+ function savePendingStore(storeDir) {
28
+ const storePath = path.join(storeDir, "pending-clarifications.json");
29
+ const items = Array.from(pending.values()).map(
30
+ ({ resolve: _r, reject: _j, ...rest }) => rest
31
+ );
32
+ fs.writeFileSync(storePath, JSON.stringify(items, null, 2));
33
+ }
34
+ async function requestClarification(token, chatId, ticketKey, questions, storeDir, opts) {
35
+ const bot = getBot(token);
36
+ const threadOpts = opts.topicId ? { message_thread_id: opts.topicId } : {};
37
+ const formatted = [
38
+ `\u26A0\uFE0F <b>${ticketKey}</b> \u2014 C\u1EA7n clarify tr\u01B0\u1EDBc khi code`,
39
+ "",
40
+ ...questions.map((q, i) => `${i + 1}. ${q}`),
41
+ "",
42
+ "Reply v\u1EDBi:",
43
+ `<code>/answer ${ticketKey}</code>`,
44
+ ...questions.map((_, i) => `${i + 1}. (your answer)`)
45
+ ].join("\n");
46
+ const msg = await bot.api.sendMessage(chatId, formatted, {
47
+ parse_mode: "HTML",
48
+ ...threadOpts
49
+ });
50
+ const expiresAt = new Date(Date.now() + opts.timeoutMs).toISOString();
51
+ return new Promise((resolve, reject) => {
52
+ pending.set(ticketKey, {
53
+ ticketKey,
54
+ messageId: msg.message_id,
55
+ questions,
56
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
57
+ expiresAt,
58
+ resolve,
59
+ reject
60
+ });
61
+ savePendingStore(storeDir);
62
+ setTimeout(async () => {
63
+ if (pending.has(ticketKey)) {
64
+ await bot.api.sendMessage(
65
+ chatId,
66
+ `\u23F0 <b>${ticketKey}</b> \u2014 Nh\u1EAFc l\u1EA1i: c\u1EA7n clarify (c\xF2n ${Math.round(opts.timeoutMs * 0.5 / 6e4)}min)`,
67
+ { parse_mode: "HTML", ...threadOpts }
68
+ );
69
+ }
70
+ }, opts.timeoutMs * 0.5);
71
+ setTimeout(async () => {
72
+ if (!pending.has(ticketKey)) return;
73
+ pending.delete(ticketKey);
74
+ savePendingStore(storeDir);
75
+ if (opts.onTimeout === "abort") {
76
+ reject(new Error(`Clarification timeout for ${ticketKey}`));
77
+ } else if (opts.onTimeout === "skip") {
78
+ reject(new Error(`SKIP:Clarification timeout \u2014 ticket skipped`));
79
+ } else {
80
+ resolve(
81
+ "(No clarification received \u2014 proceeding with original description)"
82
+ );
83
+ }
84
+ }, opts.timeoutMs);
85
+ });
86
+ }
87
+ function registerAnswerHandler(token, storeDir) {
88
+ const bot = getBot(token);
89
+ bot.command("answer", async (ctx) => {
90
+ const text = ctx.message?.text ?? "";
91
+ const lines = text.replace(/^\/answer(?:@\S+)?\s*/, "").split("\n");
92
+ const ticketKey = lines[0]?.trim();
93
+ const answers = lines.slice(1).join("\n").trim();
94
+ if (!ticketKey) return;
95
+ const entry = pending.get(ticketKey);
96
+ if (entry) {
97
+ pending.delete(ticketKey);
98
+ savePendingStore(storeDir);
99
+ entry.resolve(answers);
100
+ await ctx.reply(
101
+ `\u2705 Answers received for ${ticketKey}. Pipeline continues.`
102
+ );
103
+ } else {
104
+ await ctx.reply(`No pending clarification found for ${ticketKey}.`);
105
+ }
106
+ });
107
+ }
108
+ var pendingApprovals = /* @__PURE__ */ new Map();
109
+ async function requestApproval(token, chatId, ticketKey, issues, opts) {
110
+ const bot = getBot(token);
111
+ const threadOpts = opts.topicId ? { message_thread_id: opts.topicId } : {};
112
+ const issueLines = issues.filter((i) => i.severity === "major").map((i) => `\u2022 ${i.message}`).join("\n");
113
+ const text = [
114
+ `\u26A0\uFE0F <b>${ticketKey}</b> \u2014 Review found major issues`,
115
+ "",
116
+ issueLines,
117
+ "",
118
+ "Approve to merge or reject to abort pipeline.",
119
+ `Use /approve ${ticketKey} or /reject ${ticketKey}`
120
+ ].join("\n");
121
+ const msg = await bot.api.sendMessage(chatId, text, {
122
+ parse_mode: "HTML",
123
+ reply_markup: {
124
+ inline_keyboard: [
125
+ [
126
+ { text: "\u2705 Approve & Merge", callback_data: `approve:${ticketKey}` },
127
+ { text: "\u274C Reject", callback_data: `reject:${ticketKey}` }
128
+ ]
129
+ ]
130
+ },
131
+ ...threadOpts
132
+ });
133
+ const expiresAt = new Date(Date.now() + opts.timeoutMs).toISOString();
134
+ return new Promise((resolve, reject) => {
135
+ pendingApprovals.set(ticketKey, {
136
+ ticketKey,
137
+ messageId: msg.message_id,
138
+ createdAt: (/* @__PURE__ */ new Date()).toISOString(),
139
+ expiresAt,
140
+ resolve,
141
+ reject
142
+ });
143
+ setTimeout(() => {
144
+ if (!pendingApprovals.has(ticketKey)) return;
145
+ pendingApprovals.delete(ticketKey);
146
+ if (opts.onTimeout === "merge-anyway") {
147
+ resolve(true);
148
+ } else {
149
+ reject(new Error(`REVIEW_APPROVAL_TIMEOUT:${ticketKey}`));
150
+ }
151
+ }, opts.timeoutMs);
152
+ });
153
+ }
154
+ function registerApprovalHandlers(token) {
155
+ const bot = getBot(token);
156
+ bot.on("callback_query:data", async (ctx) => {
157
+ const data = ctx.callbackQuery.data;
158
+ if (!data) return;
159
+ if (data.startsWith("approve:")) {
160
+ const ticketKey = data.slice("approve:".length);
161
+ const entry = pendingApprovals.get(ticketKey);
162
+ if (entry) {
163
+ pendingApprovals.delete(ticketKey);
164
+ entry.resolve(true);
165
+ await ctx.answerCallbackQuery({ text: `\u2705 Approved ${ticketKey}` });
166
+ await ctx.editMessageReplyMarkup({
167
+ reply_markup: { inline_keyboard: [] }
168
+ });
169
+ } else {
170
+ await ctx.answerCallbackQuery({ text: "No pending approval found." });
171
+ }
172
+ return;
173
+ }
174
+ if (data.startsWith("reject:")) {
175
+ const ticketKey = data.slice("reject:".length);
176
+ const entry = pendingApprovals.get(ticketKey);
177
+ if (entry) {
178
+ pendingApprovals.delete(ticketKey);
179
+ entry.resolve(false);
180
+ await ctx.answerCallbackQuery({ text: `\u274C Rejected ${ticketKey}` });
181
+ await ctx.editMessageReplyMarkup({
182
+ reply_markup: { inline_keyboard: [] }
183
+ });
184
+ } else {
185
+ await ctx.answerCallbackQuery({ text: "No pending approval found." });
186
+ }
187
+ }
188
+ });
189
+ bot.command("approve", async (ctx) => {
190
+ const ticketKey = ctx.message?.text?.replace(/^\/approve(?:@\S+)?\s*/, "").trim();
191
+ if (!ticketKey) return;
192
+ const entry = pendingApprovals.get(ticketKey);
193
+ if (entry) {
194
+ pendingApprovals.delete(ticketKey);
195
+ entry.resolve(true);
196
+ await ctx.reply(`\u2705 ${ticketKey} approved \u2014 proceeding to merge.`);
197
+ } else {
198
+ await ctx.reply(`No pending approval for ${ticketKey}.`);
199
+ }
200
+ });
201
+ bot.command("reject", async (ctx) => {
202
+ const ticketKey = ctx.message?.text?.replace(/^\/reject(?:@\S+)?\s*/, "").trim();
203
+ if (!ticketKey) return;
204
+ const entry = pendingApprovals.get(ticketKey);
205
+ if (entry) {
206
+ pendingApprovals.delete(ticketKey);
207
+ entry.resolve(false);
208
+ await ctx.reply(`\u274C ${ticketKey} rejected \u2014 pipeline will abort.`);
209
+ } else {
210
+ await ctx.reply(`No pending approval for ${ticketKey}.`);
211
+ }
212
+ });
213
+ }
214
+
215
+ export {
216
+ loadPendingStore,
217
+ requestClarification,
218
+ registerAnswerHandler,
219
+ requestApproval,
220
+ registerApprovalHandlers
221
+ };
222
+ //# sourceMappingURL=chunk-KSJKCLEJ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/integrations/telegram/human-loop.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport { getBot } from \"./bot.js\";\n\nexport type TimeoutAction = \"skip\" | \"abort\" | \"proceed-with-warning\";\n\ninterface PendingClarification {\n ticketKey: string;\n messageId: number;\n questions: string[];\n createdAt: string;\n expiresAt: string;\n}\n\ninterface PendingEntry extends PendingClarification {\n resolve: (answer: string) => void;\n reject: (reason: Error) => void;\n}\n\nconst pending = new Map<string, PendingEntry>();\n\nexport function loadPendingStore(storeDir: string): void {\n const storePath = path.join(storeDir, \"pending-clarifications.json\");\n if (!fs.existsSync(storePath)) return;\n\n const items = JSON.parse(\n fs.readFileSync(storePath, \"utf8\"),\n ) as PendingClarification[];\n for (const item of items) {\n if (new Date(item.expiresAt) > new Date()) {\n // Re-register as expired-on-start (will be resolved via /answer or timeout)\n pending.set(item.ticketKey, {\n ...item,\n resolve: () => {\n /* will be overwritten when pipeline resumes */\n },\n reject: () => {\n /* will be overwritten when pipeline resumes */\n },\n });\n }\n }\n}\n\nfunction savePendingStore(storeDir: string): void {\n const storePath = path.join(storeDir, \"pending-clarifications.json\");\n const items: PendingClarification[] = Array.from(pending.values()).map(\n ({ resolve: _r, reject: _j, ...rest }) => rest,\n );\n fs.writeFileSync(storePath, JSON.stringify(items, null, 2));\n}\n\nexport async function requestClarification(\n token: string,\n chatId: number | string,\n ticketKey: string,\n questions: string[],\n storeDir: string,\n opts: { timeoutMs: number; onTimeout: TimeoutAction; topicId?: number },\n): Promise<string> {\n const bot = getBot(token);\n const threadOpts = opts.topicId ? { message_thread_id: opts.topicId } : {};\n\n const formatted = [\n `⚠️ <b>${ticketKey}</b> — Cần clarify trước khi code`,\n \"\",\n ...questions.map((q, i) => `${i + 1}. ${q}`),\n \"\",\n \"Reply với:\",\n `<code>/answer ${ticketKey}</code>`,\n ...questions.map((_, i) => `${i + 1}. (your answer)`),\n ].join(\"\\n\");\n\n const msg = await bot.api.sendMessage(chatId, formatted, {\n parse_mode: \"HTML\",\n ...threadOpts,\n });\n\n const expiresAt = new Date(Date.now() + opts.timeoutMs).toISOString();\n\n return new Promise((resolve, reject) => {\n pending.set(ticketKey, {\n ticketKey,\n messageId: msg.message_id,\n questions,\n createdAt: new Date().toISOString(),\n expiresAt,\n resolve,\n reject,\n });\n savePendingStore(storeDir);\n\n // Reminders at 50% and 80% of timeout\n setTimeout(async () => {\n if (pending.has(ticketKey)) {\n await bot.api.sendMessage(\n chatId,\n `⏰ <b>${ticketKey}</b> — Nhắc lại: cần clarify (còn ${Math.round((opts.timeoutMs * 0.5) / 60000)}min)`,\n { parse_mode: \"HTML\", ...threadOpts },\n );\n }\n }, opts.timeoutMs * 0.5);\n\n setTimeout(async () => {\n if (!pending.has(ticketKey)) return;\n pending.delete(ticketKey);\n savePendingStore(storeDir);\n\n if (opts.onTimeout === \"abort\") {\n reject(new Error(`Clarification timeout for ${ticketKey}`));\n } else if (opts.onTimeout === \"skip\") {\n reject(new Error(`SKIP:Clarification timeout — ticket skipped`));\n } else {\n resolve(\n \"(No clarification received — proceeding with original description)\",\n );\n }\n }, opts.timeoutMs);\n });\n}\n\nexport function registerAnswerHandler(token: string, storeDir: string): void {\n const bot = getBot(token);\n\n bot.command(\"answer\", async (ctx) => {\n const text = ctx.message?.text ?? \"\";\n const lines = text.replace(/^\\/answer(?:@\\S+)?\\s*/, \"\").split(\"\\n\");\n const ticketKey = lines[0]?.trim();\n const answers = lines.slice(1).join(\"\\n\").trim();\n\n if (!ticketKey) return;\n\n const entry = pending.get(ticketKey);\n if (entry) {\n pending.delete(ticketKey);\n savePendingStore(storeDir);\n entry.resolve(answers);\n await ctx.reply(\n `✅ Answers received for ${ticketKey}. Pipeline continues.`,\n );\n } else {\n await ctx.reply(`No pending clarification found for ${ticketKey}.`);\n }\n });\n}\n\n// ── Review approval flow ──────────────────────────────────────────────────\n\nexport type ApprovalTimeoutAction = \"abort\" | \"merge-anyway\";\n\ninterface PendingApproval {\n ticketKey: string;\n messageId: number;\n createdAt: string;\n expiresAt: string;\n}\n\ninterface PendingApprovalEntry extends PendingApproval {\n resolve: (approved: boolean) => void;\n reject: (reason: Error) => void;\n}\n\nconst pendingApprovals = new Map<string, PendingApprovalEntry>();\n\nexport async function requestApproval(\n token: string,\n chatId: number | string,\n ticketKey: string,\n issues: Array<{ severity: string; message: string }>,\n opts: {\n timeoutMs: number;\n onTimeout: ApprovalTimeoutAction;\n topicId?: number;\n },\n): Promise<boolean> {\n const bot = getBot(token);\n const threadOpts = opts.topicId ? { message_thread_id: opts.topicId } : {};\n\n const issueLines = issues\n .filter((i) => i.severity === \"major\")\n .map((i) => `• ${i.message}`)\n .join(\"\\n\");\n\n const text = [\n `⚠️ <b>${ticketKey}</b> — Review found major issues`,\n \"\",\n issueLines,\n \"\",\n \"Approve to merge or reject to abort pipeline.\",\n `Use /approve ${ticketKey} or /reject ${ticketKey}`,\n ].join(\"\\n\");\n\n const msg = await bot.api.sendMessage(chatId, text, {\n parse_mode: \"HTML\",\n reply_markup: {\n inline_keyboard: [\n [\n { text: \"✅ Approve & Merge\", callback_data: `approve:${ticketKey}` },\n { text: \"❌ Reject\", callback_data: `reject:${ticketKey}` },\n ],\n ],\n },\n ...threadOpts,\n });\n\n const expiresAt = new Date(Date.now() + opts.timeoutMs).toISOString();\n\n return new Promise((resolve, reject) => {\n pendingApprovals.set(ticketKey, {\n ticketKey,\n messageId: msg.message_id,\n createdAt: new Date().toISOString(),\n expiresAt,\n resolve,\n reject,\n });\n\n setTimeout(() => {\n if (!pendingApprovals.has(ticketKey)) return;\n pendingApprovals.delete(ticketKey);\n\n if (opts.onTimeout === \"merge-anyway\") {\n resolve(true);\n } else {\n reject(new Error(`REVIEW_APPROVAL_TIMEOUT:${ticketKey}`));\n }\n }, opts.timeoutMs);\n });\n}\n\nexport function registerApprovalHandlers(token: string): void {\n const bot = getBot(token);\n\n // Inline keyboard callback handler\n bot.on(\"callback_query:data\", async (ctx) => {\n const data = ctx.callbackQuery.data;\n if (!data) return;\n\n if (data.startsWith(\"approve:\")) {\n const ticketKey = data.slice(\"approve:\".length);\n const entry = pendingApprovals.get(ticketKey);\n if (entry) {\n pendingApprovals.delete(ticketKey);\n entry.resolve(true);\n await ctx.answerCallbackQuery({ text: `✅ Approved ${ticketKey}` });\n await ctx.editMessageReplyMarkup({\n reply_markup: { inline_keyboard: [] },\n });\n } else {\n await ctx.answerCallbackQuery({ text: \"No pending approval found.\" });\n }\n return;\n }\n\n if (data.startsWith(\"reject:\")) {\n const ticketKey = data.slice(\"reject:\".length);\n const entry = pendingApprovals.get(ticketKey);\n if (entry) {\n pendingApprovals.delete(ticketKey);\n entry.resolve(false);\n await ctx.answerCallbackQuery({ text: `❌ Rejected ${ticketKey}` });\n await ctx.editMessageReplyMarkup({\n reply_markup: { inline_keyboard: [] },\n });\n } else {\n await ctx.answerCallbackQuery({ text: \"No pending approval found.\" });\n }\n }\n });\n\n // Text command fallback\n bot.command(\"approve\", async (ctx) => {\n const ticketKey = ctx.message?.text\n ?.replace(/^\\/approve(?:@\\S+)?\\s*/, \"\")\n .trim();\n if (!ticketKey) return;\n const entry = pendingApprovals.get(ticketKey);\n if (entry) {\n pendingApprovals.delete(ticketKey);\n entry.resolve(true);\n await ctx.reply(`✅ ${ticketKey} approved — proceeding to merge.`);\n } else {\n await ctx.reply(`No pending approval for ${ticketKey}.`);\n }\n });\n\n bot.command(\"reject\", async (ctx) => {\n const ticketKey = ctx.message?.text\n ?.replace(/^\\/reject(?:@\\S+)?\\s*/, \"\")\n .trim();\n if (!ticketKey) return;\n const entry = pendingApprovals.get(ticketKey);\n if (entry) {\n pendingApprovals.delete(ticketKey);\n entry.resolve(false);\n await ctx.reply(`❌ ${ticketKey} rejected — pipeline will abort.`);\n } else {\n await ctx.reply(`No pending approval for ${ticketKey}.`);\n }\n });\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,139 @@
1
+ #!/usr/bin/env node
2
+
3
+ // src/config/schema.ts
4
+ import { z } from "zod";
5
+ var StageId = z.enum([
6
+ "fetch",
7
+ "analyze",
8
+ "clarify",
9
+ "code",
10
+ "git",
11
+ "review",
12
+ "deploy",
13
+ "test",
14
+ "notify"
15
+ ]);
16
+ var ProjectConfigSchema = z.object({
17
+ name: z.string(),
18
+ extends: z.string().optional(),
19
+ jira: z.object({
20
+ instance: z.string(),
21
+ url: z.string(),
22
+ email: z.string(),
23
+ token: z.string(),
24
+ projectKey: z.string(),
25
+ assignees: z.array(z.string()).min(1),
26
+ reassignTo: z.string().optional(),
27
+ acceptanceCriteriaField: z.string().default("customfield_10016"),
28
+ inProgressTransition: z.string().default("In Progress"),
29
+ inReviewTransition: z.string().default("In Review"),
30
+ doneTransition: z.string().default("Done"),
31
+ blockedTransition: z.string().optional(),
32
+ clarityScoreThreshold: z.number().min(0).max(1).default(0.7),
33
+ requiredFields: z.array(
34
+ z.enum([
35
+ "description",
36
+ "acceptanceCriteria",
37
+ "storyPoints",
38
+ "designLink"
39
+ ])
40
+ ).default(["description", "acceptanceCriteria"])
41
+ }),
42
+ github: z.object({
43
+ owner: z.string(),
44
+ repo: z.string(),
45
+ token: z.string(),
46
+ defaultBranch: z.string().default("main"),
47
+ branchPattern: z.string().default("{prefix}/{ticketKey}-{slug}"),
48
+ branchPrefix: z.string().default("feature"),
49
+ autoMergeStrategy: z.enum(["squash", "merge", "rebase"]).default("squash"),
50
+ reviewers: z.array(z.string()).default([]),
51
+ prDraftByDefault: z.boolean().default(false),
52
+ majorIssueThreshold: z.number().default(1),
53
+ ciWaitTimeoutMs: z.number().default(6e5)
54
+ }),
55
+ workspace: z.object({
56
+ rootDir: z.string(),
57
+ buildCommand: z.string().optional(),
58
+ testCommand: z.string().optional(),
59
+ allowedPaths: z.array(z.string()).default([])
60
+ }),
61
+ deploy: z.object({
62
+ enabled: z.boolean().default(false),
63
+ command: z.string().optional(),
64
+ rollbackCommand: z.string().optional(),
65
+ timeoutMs: z.number().default(12e5),
66
+ healthCheckUrl: z.string().optional(),
67
+ healthCheckTimeoutMs: z.number().default(3e4),
68
+ env: z.record(z.string()).default({})
69
+ }).default({}),
70
+ test: z.object({
71
+ enabled: z.boolean().default(false),
72
+ baseUrl: z.string().optional(),
73
+ retries: z.number().default(2),
74
+ waitBeforeTestMs: z.number().default(5e3),
75
+ timeoutMs: z.number().default(3e5),
76
+ specPattern: z.string().default("e2e/**/*.spec.ts")
77
+ }).default({}),
78
+ telegram: z.object({
79
+ botToken: z.string(),
80
+ chatId: z.union([z.string(), z.number()]),
81
+ topicId: z.number().optional(),
82
+ notifyOnStages: z.array(
83
+ z.enum([
84
+ "start",
85
+ "clarification",
86
+ "code-done",
87
+ "pr-created",
88
+ "review-pass",
89
+ "review-fail",
90
+ "deployed",
91
+ "test-pass",
92
+ "test-fail",
93
+ "done",
94
+ "error"
95
+ ])
96
+ ).default(["clarification", "review-fail", "test-fail", "error", "done"]),
97
+ humanInTheLoop: z.object({
98
+ clarificationTimeoutMs: z.number().default(36e5),
99
+ clarificationTimeoutAction: z.enum(["skip", "abort", "proceed-with-warning"]).default("skip"),
100
+ reviewApprovalTimeoutMs: z.number().default(864e5),
101
+ reviewApprovalTimeoutAction: z.enum(["abort", "merge-anyway"]).default("abort")
102
+ }).default({})
103
+ }),
104
+ pipeline: z.object({
105
+ maxConcurrentRuns: z.number().default(2),
106
+ stageTimeouts: z.object({
107
+ fetch: z.number().default(3e4),
108
+ analyze: z.number().default(6e4),
109
+ clarify: z.number().default(36e5),
110
+ code: z.number().default(18e5),
111
+ git: z.number().default(6e4),
112
+ review: z.number().default(6e5),
113
+ deploy: z.number().default(12e5),
114
+ test: z.number().default(3e5),
115
+ notify: z.number().default(3e4)
116
+ }).default({}),
117
+ agentStallTimeoutMs: z.number().default(3e5),
118
+ skipClarificationIfClear: z.boolean().default(true),
119
+ failOnTestFailure: z.boolean().default(false),
120
+ failOnDeployFailure: z.boolean().default(true),
121
+ maxCostUsdPerRun: z.number().optional(),
122
+ hooks: z.object({
123
+ beforePipeline: z.string().optional(),
124
+ beforeCode: z.string().optional(),
125
+ afterCode: z.string().optional(),
126
+ afterDeploy: z.string().optional(),
127
+ afterPipeline: z.string().optional()
128
+ }).default({})
129
+ }).default({})
130
+ });
131
+ var GlobalConfigSchema = ProjectConfigSchema.deepPartial().omit({
132
+ name: true
133
+ });
134
+
135
+ export {
136
+ ProjectConfigSchema,
137
+ GlobalConfigSchema
138
+ };
139
+ //# sourceMappingURL=chunk-LIEW4ULF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/schema.ts"],"sourcesContent":["import { z } from \"zod\";\n\nexport const StageId = z.enum([\n \"fetch\",\n \"analyze\",\n \"clarify\",\n \"code\",\n \"git\",\n \"review\",\n \"deploy\",\n \"test\",\n \"notify\",\n]);\nexport type StageId = z.infer<typeof StageId>;\n\nexport const ProjectConfigSchema = z.object({\n name: z.string(),\n extends: z.string().optional(),\n\n jira: z.object({\n instance: z.string(),\n url: z.string(),\n email: z.string(),\n token: z.string(),\n projectKey: z.string(),\n assignees: z.array(z.string()).min(1),\n reassignTo: z.string().optional(),\n acceptanceCriteriaField: z.string().default(\"customfield_10016\"),\n inProgressTransition: z.string().default(\"In Progress\"),\n inReviewTransition: z.string().default(\"In Review\"),\n doneTransition: z.string().default(\"Done\"),\n blockedTransition: z.string().optional(),\n clarityScoreThreshold: z.number().min(0).max(1).default(0.7),\n requiredFields: z\n .array(\n z.enum([\n \"description\",\n \"acceptanceCriteria\",\n \"storyPoints\",\n \"designLink\",\n ]),\n )\n .default([\"description\", \"acceptanceCriteria\"]),\n }),\n\n github: z.object({\n owner: z.string(),\n repo: z.string(),\n token: z.string(),\n defaultBranch: z.string().default(\"main\"),\n branchPattern: z.string().default(\"{prefix}/{ticketKey}-{slug}\"),\n branchPrefix: z.string().default(\"feature\"),\n autoMergeStrategy: z.enum([\"squash\", \"merge\", \"rebase\"]).default(\"squash\"),\n reviewers: z.array(z.string()).default([]),\n prDraftByDefault: z.boolean().default(false),\n majorIssueThreshold: z.number().default(1),\n ciWaitTimeoutMs: z.number().default(600_000),\n }),\n\n workspace: z.object({\n rootDir: z.string(),\n buildCommand: z.string().optional(),\n testCommand: z.string().optional(),\n allowedPaths: z.array(z.string()).default([]),\n }),\n\n deploy: z\n .object({\n enabled: z.boolean().default(false),\n command: z.string().optional(),\n rollbackCommand: z.string().optional(),\n timeoutMs: z.number().default(1_200_000),\n healthCheckUrl: z.string().optional(),\n healthCheckTimeoutMs: z.number().default(30_000),\n env: z.record(z.string()).default({}),\n })\n .default({}),\n\n test: z\n .object({\n enabled: z.boolean().default(false),\n baseUrl: z.string().optional(),\n retries: z.number().default(2),\n waitBeforeTestMs: z.number().default(5_000),\n timeoutMs: z.number().default(300_000),\n specPattern: z.string().default(\"e2e/**/*.spec.ts\"),\n })\n .default({}),\n\n telegram: z.object({\n botToken: z.string(),\n chatId: z.union([z.string(), z.number()]),\n topicId: z.number().optional(),\n notifyOnStages: z\n .array(\n z.enum([\n \"start\",\n \"clarification\",\n \"code-done\",\n \"pr-created\",\n \"review-pass\",\n \"review-fail\",\n \"deployed\",\n \"test-pass\",\n \"test-fail\",\n \"done\",\n \"error\",\n ]),\n )\n .default([\"clarification\", \"review-fail\", \"test-fail\", \"error\", \"done\"]),\n humanInTheLoop: z\n .object({\n clarificationTimeoutMs: z.number().default(3_600_000),\n clarificationTimeoutAction: z\n .enum([\"skip\", \"abort\", \"proceed-with-warning\"])\n .default(\"skip\"),\n reviewApprovalTimeoutMs: z.number().default(86_400_000),\n reviewApprovalTimeoutAction: z\n .enum([\"abort\", \"merge-anyway\"])\n .default(\"abort\"),\n })\n .default({}),\n }),\n\n pipeline: z\n .object({\n maxConcurrentRuns: z.number().default(2),\n stageTimeouts: z\n .object({\n fetch: z.number().default(30_000),\n analyze: z.number().default(60_000),\n clarify: z.number().default(3_600_000),\n code: z.number().default(1_800_000),\n git: z.number().default(60_000),\n review: z.number().default(600_000),\n deploy: z.number().default(1_200_000),\n test: z.number().default(300_000),\n notify: z.number().default(30_000),\n })\n .default({}),\n agentStallTimeoutMs: z.number().default(300_000),\n skipClarificationIfClear: z.boolean().default(true),\n failOnTestFailure: z.boolean().default(false),\n failOnDeployFailure: z.boolean().default(true),\n maxCostUsdPerRun: z.number().optional(),\n hooks: z\n .object({\n beforePipeline: z.string().optional(),\n beforeCode: z.string().optional(),\n afterCode: z.string().optional(),\n afterDeploy: z.string().optional(),\n afterPipeline: z.string().optional(),\n })\n .default({}),\n })\n .default({}),\n});\n\nexport type ProjectConfig = z.infer<typeof ProjectConfigSchema>;\n\n// Global config: all fields optional, no required `name`\nexport const GlobalConfigSchema = ProjectConfigSchema.deepPartial().omit({\n name: true,\n});\nexport type GlobalConfig = z.infer<typeof GlobalConfigSchema>;\n"],"mappings":";;;AAAA,SAAS,SAAS;AAEX,IAAM,UAAU,EAAE,KAAK;AAAA,EAC5B;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AAAA,EACA;AACF,CAAC;AAGM,IAAM,sBAAsB,EAAE,OAAO;AAAA,EAC1C,MAAM,EAAE,OAAO;AAAA,EACf,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,EAE7B,MAAM,EAAE,OAAO;AAAA,IACb,UAAU,EAAE,OAAO;AAAA,IACnB,KAAK,EAAE,OAAO;AAAA,IACd,OAAO,EAAE,OAAO;AAAA,IAChB,OAAO,EAAE,OAAO;AAAA,IAChB,YAAY,EAAE,OAAO;AAAA,IACrB,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,CAAC;AAAA,IACpC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,IAChC,yBAAyB,EAAE,OAAO,EAAE,QAAQ,mBAAmB;AAAA,IAC/D,sBAAsB,EAAE,OAAO,EAAE,QAAQ,aAAa;AAAA,IACtD,oBAAoB,EAAE,OAAO,EAAE,QAAQ,WAAW;AAAA,IAClD,gBAAgB,EAAE,OAAO,EAAE,QAAQ,MAAM;AAAA,IACzC,mBAAmB,EAAE,OAAO,EAAE,SAAS;AAAA,IACvC,uBAAuB,EAAE,OAAO,EAAE,IAAI,CAAC,EAAE,IAAI,CAAC,EAAE,QAAQ,GAAG;AAAA,IAC3D,gBAAgB,EACb;AAAA,MACC,EAAE,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,EACC,QAAQ,CAAC,eAAe,oBAAoB,CAAC;AAAA,EAClD,CAAC;AAAA,EAED,QAAQ,EAAE,OAAO;AAAA,IACf,OAAO,EAAE,OAAO;AAAA,IAChB,MAAM,EAAE,OAAO;AAAA,IACf,OAAO,EAAE,OAAO;AAAA,IAChB,eAAe,EAAE,OAAO,EAAE,QAAQ,MAAM;AAAA,IACxC,eAAe,EAAE,OAAO,EAAE,QAAQ,6BAA6B;AAAA,IAC/D,cAAc,EAAE,OAAO,EAAE,QAAQ,SAAS;AAAA,IAC1C,mBAAmB,EAAE,KAAK,CAAC,UAAU,SAAS,QAAQ,CAAC,EAAE,QAAQ,QAAQ;AAAA,IACzE,WAAW,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,IACzC,kBAAkB,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAC3C,qBAAqB,EAAE,OAAO,EAAE,QAAQ,CAAC;AAAA,IACzC,iBAAiB,EAAE,OAAO,EAAE,QAAQ,GAAO;AAAA,EAC7C,CAAC;AAAA,EAED,WAAW,EAAE,OAAO;AAAA,IAClB,SAAS,EAAE,OAAO;AAAA,IAClB,cAAc,EAAE,OAAO,EAAE,SAAS;AAAA,IAClC,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,IACjC,cAAc,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EAC9C,CAAC;AAAA,EAED,QAAQ,EACL,OAAO;AAAA,IACN,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAClC,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,iBAAiB,EAAE,OAAO,EAAE,SAAS;AAAA,IACrC,WAAW,EAAE,OAAO,EAAE,QAAQ,IAAS;AAAA,IACvC,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,IACpC,sBAAsB,EAAE,OAAO,EAAE,QAAQ,GAAM;AAAA,IAC/C,KAAK,EAAE,OAAO,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,CAAC;AAAA,EACtC,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,MAAM,EACH,OAAO;AAAA,IACN,SAAS,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAClC,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,SAAS,EAAE,OAAO,EAAE,QAAQ,CAAC;AAAA,IAC7B,kBAAkB,EAAE,OAAO,EAAE,QAAQ,GAAK;AAAA,IAC1C,WAAW,EAAE,OAAO,EAAE,QAAQ,GAAO;AAAA,IACrC,aAAa,EAAE,OAAO,EAAE,QAAQ,kBAAkB;AAAA,EACpD,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EAEb,UAAU,EAAE,OAAO;AAAA,IACjB,UAAU,EAAE,OAAO;AAAA,IACnB,QAAQ,EAAE,MAAM,CAAC,EAAE,OAAO,GAAG,EAAE,OAAO,CAAC,CAAC;AAAA,IACxC,SAAS,EAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,gBAAgB,EACb;AAAA,MACC,EAAE,KAAK;AAAA,QACL;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH,EACC,QAAQ,CAAC,iBAAiB,eAAe,aAAa,SAAS,MAAM,CAAC;AAAA,IACzE,gBAAgB,EACb,OAAO;AAAA,MACN,wBAAwB,EAAE,OAAO,EAAE,QAAQ,IAAS;AAAA,MACpD,4BAA4B,EACzB,KAAK,CAAC,QAAQ,SAAS,sBAAsB,CAAC,EAC9C,QAAQ,MAAM;AAAA,MACjB,yBAAyB,EAAE,OAAO,EAAE,QAAQ,KAAU;AAAA,MACtD,6BAA6B,EAC1B,KAAK,CAAC,SAAS,cAAc,CAAC,EAC9B,QAAQ,OAAO;AAAA,IACpB,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EACf,CAAC;AAAA,EAED,UAAU,EACP,OAAO;AAAA,IACN,mBAAmB,EAAE,OAAO,EAAE,QAAQ,CAAC;AAAA,IACvC,eAAe,EACZ,OAAO;AAAA,MACN,OAAO,EAAE,OAAO,EAAE,QAAQ,GAAM;AAAA,MAChC,SAAS,EAAE,OAAO,EAAE,QAAQ,GAAM;AAAA,MAClC,SAAS,EAAE,OAAO,EAAE,QAAQ,IAAS;AAAA,MACrC,MAAM,EAAE,OAAO,EAAE,QAAQ,IAAS;AAAA,MAClC,KAAK,EAAE,OAAO,EAAE,QAAQ,GAAM;AAAA,MAC9B,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAO;AAAA,MAClC,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAS;AAAA,MACpC,MAAM,EAAE,OAAO,EAAE,QAAQ,GAAO;AAAA,MAChC,QAAQ,EAAE,OAAO,EAAE,QAAQ,GAAM;AAAA,IACnC,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,IACb,qBAAqB,EAAE,OAAO,EAAE,QAAQ,GAAO;AAAA,IAC/C,0BAA0B,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IAClD,mBAAmB,EAAE,QAAQ,EAAE,QAAQ,KAAK;AAAA,IAC5C,qBAAqB,EAAE,QAAQ,EAAE,QAAQ,IAAI;AAAA,IAC7C,kBAAkB,EAAE,OAAO,EAAE,SAAS;AAAA,IACtC,OAAO,EACJ,OAAO;AAAA,MACN,gBAAgB,EAAE,OAAO,EAAE,SAAS;AAAA,MACpC,YAAY,EAAE,OAAO,EAAE,SAAS;AAAA,MAChC,WAAW,EAAE,OAAO,EAAE,SAAS;AAAA,MAC/B,aAAa,EAAE,OAAO,EAAE,SAAS;AAAA,MACjC,eAAe,EAAE,OAAO,EAAE,SAAS;AAAA,IACrC,CAAC,EACA,QAAQ,CAAC,CAAC;AAAA,EACf,CAAC,EACA,QAAQ,CAAC,CAAC;AACf,CAAC;AAKM,IAAM,qBAAqB,oBAAoB,YAAY,EAAE,KAAK;AAAA,EACvE,MAAM;AACR,CAAC;","names":[]}