@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,251 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ analyzeStage,
4
+ createGitHubClient,
5
+ fetchStage
6
+ } from "./chunk-VCEONSWJ.js";
7
+ import "./chunk-UDTWVKRX.js";
8
+ import {
9
+ getClient
10
+ } from "./chunk-JOT4UVSO.js";
11
+ import {
12
+ createTelegramNotifier
13
+ } from "./chunk-PVKVCUNR.js";
14
+ import "./chunk-RZK74PDF.js";
15
+ import {
16
+ StateManager,
17
+ getMemoryDir,
18
+ getRunDir
19
+ } from "./chunk-VWBCDZWQ.js";
20
+ import {
21
+ loadConfig
22
+ } from "./chunk-3YHD4SIN.js";
23
+ import "./chunk-LIEW4ULF.js";
24
+ import "./chunk-3U373M37.js";
25
+ import "./chunk-OJ4CNF73.js";
26
+ import {
27
+ createLogger
28
+ } from "./chunk-IT74N3UH.js";
29
+
30
+ // src/commands/triage.ts
31
+ import pc from "picocolors";
32
+ import path from "path";
33
+ import os from "os";
34
+ import fs from "fs";
35
+ var MAX_PARALLEL = 5;
36
+ async function runTriage(opts) {
37
+ const logger = createLogger("triage");
38
+ const config = loadConfig(opts.projectName);
39
+ const jiraClient = getClient(config.jira.instance);
40
+ logger.info({ project: opts.projectName }, "Starting triage");
41
+ process.stdout.write(
42
+ `
43
+ ${pc.bold("jiraACP triage")} \u2014 ${opts.projectName}
44
+
45
+ `
46
+ );
47
+ const tmpRunDir = path.join(
48
+ os.homedir(),
49
+ ".jira-acp",
50
+ "runs",
51
+ opts.projectName,
52
+ "__triage__"
53
+ );
54
+ fs.mkdirSync(tmpRunDir, { recursive: true });
55
+ const tmpState = new StateManager(tmpRunDir);
56
+ const tmpMemoryDir = getMemoryDir(opts.projectName, "__triage__");
57
+ fs.mkdirSync(tmpMemoryDir, { recursive: true });
58
+ const baseCtx = {
59
+ config,
60
+ ticketKey: "__triage__",
61
+ projectDir: config.workspace.rootDir,
62
+ state: tmpState,
63
+ memoryDir: tmpMemoryDir,
64
+ dryRun: opts.dryRun ?? false,
65
+ logger,
66
+ jira: jiraClient,
67
+ github: createGitHubClient(
68
+ config.github.token,
69
+ config.github.owner,
70
+ config.github.repo
71
+ ),
72
+ telegram: createTelegramNotifier(
73
+ config.telegram.botToken,
74
+ config.telegram.chatId,
75
+ config.telegram.topicId
76
+ )
77
+ };
78
+ process.stdout.write("Fetching sprint tickets from Jira...\n");
79
+ let fetchOutput;
80
+ try {
81
+ fetchOutput = await fetchStage.run(baseCtx);
82
+ } catch (err) {
83
+ process.stderr.write(
84
+ `Failed to fetch tickets: ${err instanceof Error ? err.message : String(err)}
85
+ `
86
+ );
87
+ process.exitCode = 1;
88
+ return;
89
+ }
90
+ const tickets = fetchOutput["tickets"];
91
+ if (!tickets || tickets.length === 0) {
92
+ process.stdout.write(
93
+ pc.gray(" No assigned tickets found in current sprint.\n\n")
94
+ );
95
+ return;
96
+ }
97
+ process.stdout.write(
98
+ `Found ${pc.bold(String(tickets.length))} tickets \u2014 analyzing...
99
+
100
+ `
101
+ );
102
+ const results = [];
103
+ for (let i = 0; i < tickets.length; i += MAX_PARALLEL) {
104
+ const batch = tickets.slice(i, i + MAX_PARALLEL);
105
+ const batchResults = await Promise.allSettled(
106
+ batch.map(
107
+ (ticket) => analyzeTicket(ticket, opts, config, logger).catch(
108
+ (err) => ({
109
+ ticketKey: ticket.key,
110
+ title: ticket.summary,
111
+ score: 0,
112
+ needsClarification: true,
113
+ missing: [],
114
+ questions: [],
115
+ error: err instanceof Error ? err.message : String(err)
116
+ })
117
+ )
118
+ )
119
+ );
120
+ for (const r of batchResults) {
121
+ if (r.status === "fulfilled") {
122
+ results.push(r.value);
123
+ }
124
+ }
125
+ }
126
+ printTriageTable(results);
127
+ const needsClarification = results.filter(
128
+ (r) => r.needsClarification && !r.error
129
+ );
130
+ if (needsClarification.length > 0 && !opts.dryRun) {
131
+ await sendClarificationDigest(config, needsClarification, logger);
132
+ }
133
+ const clear = results.filter((r) => !r.needsClarification && !r.error).length;
134
+ const clarify = needsClarification.length;
135
+ const errors = results.filter((r) => r.error).length;
136
+ process.stdout.write(
137
+ `
138
+ Summary: ${pc.green(`${clear} clear`)} ${pc.yellow(`${clarify} need clarification`)} ${errors > 0 ? pc.red(`${errors} errors`) : ""}
139
+
140
+ `
141
+ );
142
+ }
143
+ async function analyzeTicket(ticket, opts, config, logger) {
144
+ const runDir = getRunDir(opts.projectName, ticket.key);
145
+ fs.mkdirSync(runDir, { recursive: true });
146
+ const memoryDir = getMemoryDir(opts.projectName, ticket.key);
147
+ fs.mkdirSync(memoryDir, { recursive: true });
148
+ const ctx = {
149
+ config,
150
+ ticketKey: ticket.key,
151
+ projectDir: config.workspace.rootDir,
152
+ state: new StateManager(runDir),
153
+ memoryDir,
154
+ dryRun: opts.dryRun ?? false,
155
+ logger,
156
+ jira: getClient(config.jira.instance),
157
+ github: createGitHubClient(
158
+ config.github.token,
159
+ config.github.owner,
160
+ config.github.repo
161
+ ),
162
+ telegram: createTelegramNotifier(
163
+ config.telegram.botToken,
164
+ config.telegram.chatId,
165
+ config.telegram.topicId
166
+ )
167
+ };
168
+ try {
169
+ const output = await analyzeStage.run(ctx);
170
+ return {
171
+ ticketKey: ticket.key,
172
+ title: ticket.summary,
173
+ score: output["score"] ?? 0,
174
+ needsClarification: output["needsClarification"] ?? false,
175
+ missing: output["missing"] ?? [],
176
+ questions: output["questions"] ?? []
177
+ };
178
+ } catch (err) {
179
+ return {
180
+ ticketKey: ticket.key,
181
+ title: ticket.summary,
182
+ score: 0,
183
+ needsClarification: true,
184
+ missing: [],
185
+ questions: [],
186
+ error: err instanceof Error ? err.message : String(err)
187
+ };
188
+ }
189
+ }
190
+ function printTriageTable(results) {
191
+ const col = { key: 14, title: 40, score: 8, status: 22 };
192
+ const header = ` ${"Ticket".padEnd(col.key)} ${"Title".padEnd(col.title)} ${"Score".padEnd(col.score)} Status`;
193
+ process.stdout.write(pc.bold(header) + "\n");
194
+ process.stdout.write(
195
+ " " + "\u2500".repeat(col.key + col.title + col.score + 24) + "\n"
196
+ );
197
+ for (const r of results) {
198
+ const key = r.ticketKey.padEnd(col.key);
199
+ const title = r.title.slice(0, col.title - 1).padEnd(col.title);
200
+ const scoreStr = r.error ? "err" : `${(r.score * 100).toFixed(0)}%`;
201
+ const score = scoreStr.padEnd(col.score);
202
+ const status = r.error ? pc.red("error") : r.needsClarification ? pc.yellow("needs clarification") : pc.green("clear");
203
+ process.stdout.write(` ${key} ${title} ${score} ${status}
204
+ `);
205
+ if (r.needsClarification && r.missing.length > 0) {
206
+ for (const m of r.missing) {
207
+ process.stdout.write(
208
+ ` ${" ".repeat(col.key + 1)}${pc.gray(` \u2022 ${m}`)}
209
+ `
210
+ );
211
+ }
212
+ }
213
+ }
214
+ }
215
+ async function sendClarificationDigest(config, tickets, logger) {
216
+ const notifier = createTelegramNotifier(
217
+ config.telegram.botToken,
218
+ config.telegram.chatId,
219
+ config.telegram.topicId
220
+ );
221
+ const lines = [
222
+ `\u{1F4CB} <b>Triage Digest</b> \u2014 ${tickets.length} ticket(s) need clarification`,
223
+ ""
224
+ ];
225
+ for (const t of tickets) {
226
+ lines.push(`<b>${t.ticketKey}</b>: ${t.title}`);
227
+ for (const q of t.questions) {
228
+ lines.push(` \u2022 ${q}`);
229
+ }
230
+ lines.push("");
231
+ }
232
+ lines.push(
233
+ "Use <code>/answer TICKET-KEY</code> to respond to individual tickets."
234
+ );
235
+ try {
236
+ await notifier.send(lines.join("\n"));
237
+ logger.info(
238
+ { count: tickets.length },
239
+ "Clarification digest sent to Telegram"
240
+ );
241
+ } catch (err) {
242
+ logger.warn(
243
+ { err: err instanceof Error ? err.message : String(err) },
244
+ "Failed to send Telegram digest"
245
+ );
246
+ }
247
+ }
248
+ export {
249
+ runTriage
250
+ };
251
+ //# sourceMappingURL=triage-WNHGPVZQ.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/triage.ts"],"sourcesContent":["import pc from \"picocolors\";\nimport { loadConfig } from \"../config/loader.js\";\nimport { createLogger } from \"../utils/logger.js\";\nimport { getClient as getJiraClient } from \"../integrations/jira/client.js\";\nimport { createTelegramNotifier } from \"../integrations/telegram/notifier.js\";\nimport { analyzeStage } from \"../pipeline/stages/2-analyze.js\";\nimport { fetchStage } from \"../pipeline/stages/1-fetch.js\";\nimport type { PipelineContext } from \"../pipeline/stages/types.js\";\nimport { StateManager, getRunDir, getMemoryDir } from \"../pipeline/state.js\";\nimport { createGitHubClient } from \"../integrations/github/client.js\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport fs from \"node:fs\";\n\nexport interface TriageOptions {\n projectName: string;\n sprint?: string;\n dryRun?: boolean;\n}\n\ninterface TriageResult {\n ticketKey: string;\n title: string;\n score: number;\n needsClarification: boolean;\n missing: string[];\n questions: string[];\n error?: string;\n}\n\nconst MAX_PARALLEL = 5;\n\nexport async function runTriage(opts: TriageOptions): Promise<void> {\n const logger = createLogger(\"triage\");\n const config = loadConfig(opts.projectName);\n const jiraClient = getJiraClient(config.jira.instance);\n\n logger.info({ project: opts.projectName }, \"Starting triage\");\n process.stdout.write(\n `\\n${pc.bold(\"jiraACP triage\")} — ${opts.projectName}\\n\\n`,\n );\n\n // Fetch all assigned tickets (reuse fetch stage logic)\n const tmpRunDir = path.join(\n os.homedir(),\n \".jira-acp\",\n \"runs\",\n opts.projectName,\n \"__triage__\",\n );\n fs.mkdirSync(tmpRunDir, { recursive: true });\n\n const tmpState = new StateManager(tmpRunDir);\n const tmpMemoryDir = getMemoryDir(opts.projectName, \"__triage__\");\n fs.mkdirSync(tmpMemoryDir, { recursive: true });\n\n const baseCtx: PipelineContext = {\n config,\n ticketKey: \"__triage__\",\n projectDir: config.workspace.rootDir,\n state: tmpState,\n memoryDir: tmpMemoryDir,\n dryRun: opts.dryRun ?? false,\n logger,\n jira: jiraClient,\n github: createGitHubClient(\n config.github.token,\n config.github.owner,\n config.github.repo,\n ),\n telegram: createTelegramNotifier(\n config.telegram.botToken,\n config.telegram.chatId,\n config.telegram.topicId,\n ),\n };\n\n // Fetch tickets\n process.stdout.write(\"Fetching sprint tickets from Jira...\\n\");\n let fetchOutput: Record<string, unknown>;\n try {\n fetchOutput = (await fetchStage.run(baseCtx)) as Record<string, unknown>;\n } catch (err) {\n process.stderr.write(\n `Failed to fetch tickets: ${err instanceof Error ? err.message : String(err)}\\n`,\n );\n process.exitCode = 1;\n return;\n }\n\n const tickets = fetchOutput[\"tickets\"] as Array<{\n key: string;\n summary: string;\n }>;\n\n if (!tickets || tickets.length === 0) {\n process.stdout.write(\n pc.gray(\" No assigned tickets found in current sprint.\\n\\n\"),\n );\n return;\n }\n\n process.stdout.write(\n `Found ${pc.bold(String(tickets.length))} tickets — analyzing...\\n\\n`,\n );\n\n // Analyze tickets in parallel batches of MAX_PARALLEL\n const results: TriageResult[] = [];\n for (let i = 0; i < tickets.length; i += MAX_PARALLEL) {\n const batch = tickets.slice(i, i + MAX_PARALLEL);\n const batchResults = await Promise.allSettled(\n batch.map((ticket) =>\n analyzeTicket(ticket, opts, config, logger).catch(\n (err): TriageResult => ({\n ticketKey: ticket.key,\n title: ticket.summary,\n score: 0,\n needsClarification: true,\n missing: [],\n questions: [],\n error: err instanceof Error ? err.message : String(err),\n }),\n ),\n ),\n );\n for (const r of batchResults) {\n if (r.status === \"fulfilled\") {\n results.push(r.value);\n }\n }\n }\n\n // Print triage table\n printTriageTable(results);\n\n // Batch clarification digest to Telegram\n const needsClarification = results.filter(\n (r) => r.needsClarification && !r.error,\n );\n if (needsClarification.length > 0 && !opts.dryRun) {\n await sendClarificationDigest(config, needsClarification, logger);\n }\n\n // Summary\n const clear = results.filter((r) => !r.needsClarification && !r.error).length;\n const clarify = needsClarification.length;\n const errors = results.filter((r) => r.error).length;\n\n process.stdout.write(\n `\\nSummary: ${pc.green(`${clear} clear`)} ${pc.yellow(`${clarify} need clarification`)} ${errors > 0 ? pc.red(`${errors} errors`) : \"\"}\\n\\n`,\n );\n}\n\nasync function analyzeTicket(\n ticket: { key: string; summary: string },\n opts: TriageOptions,\n config: ReturnType<typeof loadConfig>,\n logger: ReturnType<typeof createLogger>,\n): Promise<TriageResult> {\n const runDir = getRunDir(opts.projectName, ticket.key);\n fs.mkdirSync(runDir, { recursive: true });\n const memoryDir = getMemoryDir(opts.projectName, ticket.key);\n fs.mkdirSync(memoryDir, { recursive: true });\n\n const ctx: PipelineContext = {\n config,\n ticketKey: ticket.key,\n projectDir: config.workspace.rootDir,\n state: new StateManager(runDir),\n memoryDir,\n dryRun: opts.dryRun ?? false,\n logger,\n jira: getJiraClient(config.jira.instance),\n github: createGitHubClient(\n config.github.token,\n config.github.owner,\n config.github.repo,\n ),\n telegram: createTelegramNotifier(\n config.telegram.botToken,\n config.telegram.chatId,\n config.telegram.topicId,\n ),\n };\n\n try {\n const output = (await analyzeStage.run(ctx)) as Record<string, unknown>;\n return {\n ticketKey: ticket.key,\n title: ticket.summary,\n score: (output[\"score\"] as number) ?? 0,\n needsClarification: (output[\"needsClarification\"] as boolean) ?? false,\n missing: (output[\"missing\"] as string[]) ?? [],\n questions: (output[\"questions\"] as string[]) ?? [],\n };\n } catch (err) {\n return {\n ticketKey: ticket.key,\n title: ticket.summary,\n score: 0,\n needsClarification: true,\n missing: [],\n questions: [],\n error: err instanceof Error ? err.message : String(err),\n };\n }\n}\n\nfunction printTriageTable(results: TriageResult[]): void {\n const col = { key: 14, title: 40, score: 8, status: 22 };\n const header = ` ${\"Ticket\".padEnd(col.key)} ${\"Title\".padEnd(col.title)} ${\"Score\".padEnd(col.score)} Status`;\n process.stdout.write(pc.bold(header) + \"\\n\");\n process.stdout.write(\n \" \" + \"─\".repeat(col.key + col.title + col.score + 24) + \"\\n\",\n );\n\n for (const r of results) {\n const key = r.ticketKey.padEnd(col.key);\n const title = r.title.slice(0, col.title - 1).padEnd(col.title);\n const scoreStr = r.error ? \"err\" : `${(r.score * 100).toFixed(0)}%`;\n const score = scoreStr.padEnd(col.score);\n const status = r.error\n ? pc.red(\"error\")\n : r.needsClarification\n ? pc.yellow(\"needs clarification\")\n : pc.green(\"clear\");\n\n process.stdout.write(` ${key} ${title} ${score} ${status}\\n`);\n\n if (r.needsClarification && r.missing.length > 0) {\n for (const m of r.missing) {\n process.stdout.write(\n ` ${\" \".repeat(col.key + 1)}${pc.gray(` • ${m}`)}\\n`,\n );\n }\n }\n }\n}\n\nasync function sendClarificationDigest(\n config: ReturnType<typeof loadConfig>,\n tickets: TriageResult[],\n logger: ReturnType<typeof createLogger>,\n): Promise<void> {\n const notifier = createTelegramNotifier(\n config.telegram.botToken,\n config.telegram.chatId,\n config.telegram.topicId,\n );\n\n const lines = [\n `📋 <b>Triage Digest</b> — ${tickets.length} ticket(s) need clarification`,\n \"\",\n ];\n\n for (const t of tickets) {\n lines.push(`<b>${t.ticketKey}</b>: ${t.title}`);\n for (const q of t.questions) {\n lines.push(` • ${q}`);\n }\n lines.push(\"\");\n }\n\n lines.push(\n \"Use <code>/answer TICKET-KEY</code> to respond to individual tickets.\",\n );\n\n try {\n await notifier.send(lines.join(\"\\n\"));\n logger.info(\n { count: tickets.length },\n \"Clarification digest sent to Telegram\",\n );\n } catch (err) {\n logger.warn(\n { err: err instanceof Error ? err.message : String(err) },\n \"Failed to send Telegram digest\",\n );\n }\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,155 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ estimateCostUsd,
4
+ extractTokenUsage
5
+ } from "./chunk-RXT4WSIY.js";
6
+ import {
7
+ getEvents
8
+ } from "./chunk-VWBCDZWQ.js";
9
+ import {
10
+ createLogger
11
+ } from "./chunk-IT74N3UH.js";
12
+
13
+ // src/commands/usage.ts
14
+ import fs from "fs";
15
+ import os from "os";
16
+ import path from "path";
17
+ import pc from "picocolors";
18
+ var log = createLogger("usage");
19
+ var RUNS_DIR = path.join(os.homedir(), ".jira-acp", "runs");
20
+ function formatNumber(n) {
21
+ return n.toLocaleString("en-US");
22
+ }
23
+ function formatCost(n) {
24
+ return `$${n.toFixed(2)}`;
25
+ }
26
+ function padEnd(s, len) {
27
+ return s.length >= len ? s : s + " ".repeat(len - s.length);
28
+ }
29
+ function padStart(s, len) {
30
+ return s.length >= len ? s : " ".repeat(len - s.length) + s;
31
+ }
32
+ async function showUsage(opts) {
33
+ log.debug({ opts }, "showUsage called");
34
+ if (!fs.existsSync(RUNS_DIR)) {
35
+ process.stdout.write("No runs directory found at ~/.jira-acp/runs/\n");
36
+ return;
37
+ }
38
+ const projectDirs = fs.readdirSync(RUNS_DIR).filter((entry) => {
39
+ try {
40
+ return fs.statSync(path.join(RUNS_DIR, entry)).isDirectory();
41
+ } catch {
42
+ return false;
43
+ }
44
+ });
45
+ const stats = {};
46
+ let totalTokensFound = false;
47
+ for (const projectName of projectDirs) {
48
+ if (opts.project && projectName !== opts.project) continue;
49
+ const projectRunsDir = path.join(RUNS_DIR, projectName);
50
+ const ticketDirs = fs.readdirSync(projectRunsDir).filter((entry) => {
51
+ if (entry === "__triage__") return false;
52
+ try {
53
+ return fs.statSync(path.join(projectRunsDir, entry)).isDirectory();
54
+ } catch {
55
+ return false;
56
+ }
57
+ });
58
+ for (const ticketKey of ticketDirs) {
59
+ const runDir = path.join(projectRunsDir, ticketKey);
60
+ const events = getEvents(runDir);
61
+ const startedEvent = events.find((e) => e.type === "STARTED");
62
+ if (!startedEvent) continue;
63
+ const runMonth = startedEvent.timestamp.slice(0, 7);
64
+ if (opts.month && runMonth !== opts.month) continue;
65
+ let runHasTokens = false;
66
+ for (const event of events) {
67
+ if (event.type !== "STAGE_COMPLETED") continue;
68
+ const tu = extractTokenUsage(event.output);
69
+ if (!tu) continue;
70
+ const { inputTokens, outputTokens, model } = tu;
71
+ const cost = estimateCostUsd(inputTokens, outputTokens, model);
72
+ totalTokensFound = true;
73
+ runHasTokens = true;
74
+ if (!stats[projectName]) {
75
+ stats[projectName] = {
76
+ runs: 0,
77
+ inputTokens: 0,
78
+ outputTokens: 0,
79
+ costUsd: 0,
80
+ stages: {}
81
+ };
82
+ }
83
+ stats[projectName].inputTokens += inputTokens;
84
+ stats[projectName].outputTokens += outputTokens;
85
+ stats[projectName].costUsd += cost;
86
+ if (opts.verbose) {
87
+ const stageName = event.stage;
88
+ if (!stats[projectName].stages[stageName]) {
89
+ stats[projectName].stages[stageName] = {
90
+ inputTokens: 0,
91
+ outputTokens: 0,
92
+ costUsd: 0,
93
+ count: 0
94
+ };
95
+ }
96
+ stats[projectName].stages[stageName].inputTokens += inputTokens;
97
+ stats[projectName].stages[stageName].outputTokens += outputTokens;
98
+ stats[projectName].stages[stageName].costUsd += cost;
99
+ stats[projectName].stages[stageName].count += 1;
100
+ }
101
+ }
102
+ if (runHasTokens) {
103
+ stats[projectName].runs += 1;
104
+ }
105
+ }
106
+ }
107
+ if (!totalTokensFound) {
108
+ process.stdout.write(
109
+ "No token usage data found. Token tracking requires stage outputs to include tokenUsage field.\n"
110
+ );
111
+ return;
112
+ }
113
+ const monthLabel = opts.month ?? "all time";
114
+ process.stdout.write(
115
+ `
116
+ ${pc.bold("jiraACP usage report")} \u2014 ${pc.cyan(monthLabel)}
117
+
118
+ `
119
+ );
120
+ const COL_PROJECT = 24;
121
+ const COL_RUNS = 6;
122
+ const COL_INPUT = 15;
123
+ const COL_OUTPUT = 15;
124
+ const COL_COST = 11;
125
+ const SEPARATOR_LEN = COL_PROJECT + COL_RUNS + COL_INPUT + COL_OUTPUT + COL_COST + 4;
126
+ const header = " " + padEnd("Project", COL_PROJECT) + padStart("Runs", COL_RUNS) + padStart("Input tokens", COL_INPUT) + padStart("Output tokens", COL_OUTPUT) + padStart("Est. cost", COL_COST);
127
+ const separator = " " + pc.dim("\u2500".repeat(SEPARATOR_LEN));
128
+ process.stdout.write(pc.bold(header) + "\n");
129
+ process.stdout.write(separator + "\n");
130
+ let totalRuns = 0;
131
+ let totalInput = 0;
132
+ let totalOutput = 0;
133
+ let totalCost = 0;
134
+ for (const [projectName, s] of Object.entries(stats)) {
135
+ totalRuns += s.runs;
136
+ totalInput += s.inputTokens;
137
+ totalOutput += s.outputTokens;
138
+ totalCost += s.costUsd;
139
+ const row = " " + padEnd(projectName, COL_PROJECT) + padStart(String(s.runs), COL_RUNS) + padStart(formatNumber(s.inputTokens), COL_INPUT) + padStart(formatNumber(s.outputTokens), COL_OUTPUT) + padStart(pc.green(formatCost(s.costUsd)), COL_COST);
140
+ process.stdout.write(row + "\n");
141
+ if (opts.verbose && Object.keys(s.stages).length > 0) {
142
+ for (const [stageName, st] of Object.entries(s.stages)) {
143
+ const stageRow = " " + pc.dim(padEnd(`\u21B3 ${stageName}`, COL_PROJECT - 2)) + padStart(String(st.count), COL_RUNS) + padStart(formatNumber(st.inputTokens), COL_INPUT) + padStart(formatNumber(st.outputTokens), COL_OUTPUT) + padStart(pc.dim(formatCost(st.costUsd)), COL_COST);
144
+ process.stdout.write(stageRow + "\n");
145
+ }
146
+ }
147
+ }
148
+ process.stdout.write(separator + "\n");
149
+ const totalRow = " " + padEnd(pc.bold("TOTAL"), COL_PROJECT) + padStart(pc.bold(String(totalRuns)), COL_RUNS) + padStart(pc.bold(formatNumber(totalInput)), COL_INPUT) + padStart(pc.bold(formatNumber(totalOutput)), COL_OUTPUT) + padStart(pc.bold(pc.green(formatCost(totalCost))), COL_COST);
150
+ process.stdout.write(totalRow + "\n\n");
151
+ }
152
+ export {
153
+ showUsage
154
+ };
155
+ //# sourceMappingURL=usage-AWWBI37F.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/commands/usage.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport pc from \"picocolors\";\nimport { getEvents } from \"../pipeline/state.js\";\nimport { estimateCostUsd, extractTokenUsage } from \"../utils/pricing.js\";\nimport { createLogger } from \"../utils/logger.js\";\n\nconst log = createLogger(\"usage\");\n\nexport interface UsageOptions {\n month?: string; // \"YYYY-MM\" format\n project?: string; // filter by project name\n verbose?: boolean; // show per-stage breakdown\n}\n\ninterface ProjectStats {\n runs: number;\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n stages: Record<string, StageStats>;\n}\n\ninterface StageStats {\n inputTokens: number;\n outputTokens: number;\n costUsd: number;\n count: number;\n}\n\nconst RUNS_DIR = path.join(os.homedir(), \".jira-acp\", \"runs\");\n\nfunction formatNumber(n: number): string {\n return n.toLocaleString(\"en-US\");\n}\n\nfunction formatCost(n: number): string {\n return `$${n.toFixed(2)}`;\n}\n\nfunction padEnd(s: string, len: number): string {\n return s.length >= len ? s : s + \" \".repeat(len - s.length);\n}\n\nfunction padStart(s: string, len: number): string {\n return s.length >= len ? s : \" \".repeat(len - s.length) + s;\n}\n\nexport async function showUsage(opts: UsageOptions): Promise<void> {\n log.debug({ opts }, \"showUsage called\");\n\n if (!fs.existsSync(RUNS_DIR)) {\n process.stdout.write(\"No runs directory found at ~/.jira-acp/runs/\\n\");\n return;\n }\n\n const projectDirs = fs.readdirSync(RUNS_DIR).filter((entry) => {\n try {\n return fs.statSync(path.join(RUNS_DIR, entry)).isDirectory();\n } catch {\n return false;\n }\n });\n\n const stats: Record<string, ProjectStats> = {};\n let totalTokensFound = false;\n\n for (const projectName of projectDirs) {\n if (opts.project && projectName !== opts.project) continue;\n\n const projectRunsDir = path.join(RUNS_DIR, projectName);\n const ticketDirs = fs.readdirSync(projectRunsDir).filter((entry) => {\n if (entry === \"__triage__\") return false;\n try {\n return fs.statSync(path.join(projectRunsDir, entry)).isDirectory();\n } catch {\n return false;\n }\n });\n\n for (const ticketKey of ticketDirs) {\n const runDir = path.join(projectRunsDir, ticketKey);\n const events = getEvents(runDir);\n\n // Determine run month from STARTED event\n const startedEvent = events.find((e) => e.type === \"STARTED\");\n if (!startedEvent) continue;\n\n const runMonth = startedEvent.timestamp.slice(0, 7); // \"YYYY-MM\"\n if (opts.month && runMonth !== opts.month) continue;\n\n // Check if any STAGE_COMPLETED events have tokenUsage\n let runHasTokens = false;\n\n for (const event of events) {\n if (event.type !== \"STAGE_COMPLETED\") continue;\n\n const tu = extractTokenUsage(event.output);\n if (!tu) continue;\n\n const { inputTokens, outputTokens, model } = tu;\n const cost = estimateCostUsd(inputTokens, outputTokens, model);\n\n totalTokensFound = true;\n runHasTokens = true;\n\n if (!stats[projectName]) {\n stats[projectName] = {\n runs: 0,\n inputTokens: 0,\n outputTokens: 0,\n costUsd: 0,\n stages: {},\n };\n }\n\n stats[projectName].inputTokens += inputTokens;\n stats[projectName].outputTokens += outputTokens;\n stats[projectName].costUsd += cost;\n\n if (opts.verbose) {\n const stageName = event.stage;\n if (!stats[projectName].stages[stageName]) {\n stats[projectName].stages[stageName] = {\n inputTokens: 0,\n outputTokens: 0,\n costUsd: 0,\n count: 0,\n };\n }\n stats[projectName].stages[stageName].inputTokens += inputTokens;\n stats[projectName].stages[stageName].outputTokens += outputTokens;\n stats[projectName].stages[stageName].costUsd += cost;\n stats[projectName].stages[stageName].count += 1;\n }\n }\n\n if (runHasTokens) {\n stats[projectName].runs += 1;\n }\n }\n }\n\n if (!totalTokensFound) {\n process.stdout.write(\n \"No token usage data found. Token tracking requires stage outputs to include tokenUsage field.\\n\",\n );\n return;\n }\n\n const monthLabel = opts.month ?? \"all time\";\n process.stdout.write(\n `\\n ${pc.bold(\"jiraACP usage report\")} — ${pc.cyan(monthLabel)}\\n\\n`,\n );\n\n const COL_PROJECT = 24;\n const COL_RUNS = 6;\n const COL_INPUT = 15;\n const COL_OUTPUT = 15;\n const COL_COST = 11;\n const SEPARATOR_LEN =\n COL_PROJECT + COL_RUNS + COL_INPUT + COL_OUTPUT + COL_COST + 4;\n\n const header =\n \" \" +\n padEnd(\"Project\", COL_PROJECT) +\n padStart(\"Runs\", COL_RUNS) +\n padStart(\"Input tokens\", COL_INPUT) +\n padStart(\"Output tokens\", COL_OUTPUT) +\n padStart(\"Est. cost\", COL_COST);\n\n const separator = \" \" + pc.dim(\"─\".repeat(SEPARATOR_LEN));\n\n process.stdout.write(pc.bold(header) + \"\\n\");\n process.stdout.write(separator + \"\\n\");\n\n let totalRuns = 0;\n let totalInput = 0;\n let totalOutput = 0;\n let totalCost = 0;\n\n for (const [projectName, s] of Object.entries(stats)) {\n totalRuns += s.runs;\n totalInput += s.inputTokens;\n totalOutput += s.outputTokens;\n totalCost += s.costUsd;\n\n const row =\n \" \" +\n padEnd(projectName, COL_PROJECT) +\n padStart(String(s.runs), COL_RUNS) +\n padStart(formatNumber(s.inputTokens), COL_INPUT) +\n padStart(formatNumber(s.outputTokens), COL_OUTPUT) +\n padStart(pc.green(formatCost(s.costUsd)), COL_COST);\n\n process.stdout.write(row + \"\\n\");\n\n if (opts.verbose && Object.keys(s.stages).length > 0) {\n for (const [stageName, st] of Object.entries(s.stages)) {\n const stageRow =\n \" \" +\n pc.dim(padEnd(`↳ ${stageName}`, COL_PROJECT - 2)) +\n padStart(String(st.count), COL_RUNS) +\n padStart(formatNumber(st.inputTokens), COL_INPUT) +\n padStart(formatNumber(st.outputTokens), COL_OUTPUT) +\n padStart(pc.dim(formatCost(st.costUsd)), COL_COST);\n process.stdout.write(stageRow + \"\\n\");\n }\n }\n }\n\n process.stdout.write(separator + \"\\n\");\n\n const totalRow =\n \" \" +\n padEnd(pc.bold(\"TOTAL\"), COL_PROJECT) +\n padStart(pc.bold(String(totalRuns)), COL_RUNS) +\n padStart(pc.bold(formatNumber(totalInput)), COL_INPUT) +\n padStart(pc.bold(formatNumber(totalOutput)), COL_OUTPUT) +\n padStart(pc.bold(pc.green(formatCost(totalCost))), COL_COST);\n\n process.stdout.write(totalRow + \"\\n\\n\");\n}\n"],"mappings":"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","names":[]}
@@ -0,0 +1,190 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ generateClaudeMd
4
+ } from "./chunk-H7YXX4UA.js";
5
+ import {
6
+ saveConfig
7
+ } from "./chunk-3YHD4SIN.js";
8
+ import "./chunk-LIEW4ULF.js";
9
+
10
+ // src/config/wizard.ts
11
+ import * as p from "@clack/prompts";
12
+ import pc from "picocolors";
13
+
14
+ // src/memory/mcp-json.ts
15
+ import fs from "fs";
16
+ import path from "path";
17
+ async function writeMcpJson(projectDir, config, instanceName) {
18
+ const upper = instanceName.toUpperCase();
19
+ const claudeDir = path.join(projectDir, ".claude");
20
+ fs.mkdirSync(claudeDir, { recursive: true });
21
+ const mcpJson = {
22
+ mcpServers: {
23
+ jira: {
24
+ command: "jiraACP-mcp",
25
+ env: {
26
+ [`JIRA_${upper}_URL`]: `env:JIRA_${upper}_URL`,
27
+ [`JIRA_${upper}_TOKEN`]: `env:JIRA_${upper}_TOKEN`,
28
+ [`JIRA_${upper}_EMAIL`]: `env:JIRA_${upper}_EMAIL`
29
+ }
30
+ }
31
+ }
32
+ };
33
+ fs.writeFileSync(
34
+ path.join(claudeDir, ".mcp.json"),
35
+ JSON.stringify(mcpJson, null, 2)
36
+ );
37
+ }
38
+
39
+ // src/config/wizard.ts
40
+ async function runWizard(projectName, projectDir) {
41
+ console.log(pc.bold("\n jiraACP \u2014 Project Setup\n"));
42
+ p.intro(pc.cyan(`Configuring project: ${pc.bold(projectName)}`));
43
+ const s = p.spinner();
44
+ p.note("Jira configuration", "Integration");
45
+ const jiraUrl = await p.text({
46
+ message: "Jira URL",
47
+ placeholder: "https://your-team.atlassian.net"
48
+ });
49
+ if (p.isCancel(jiraUrl)) {
50
+ p.cancel("Setup cancelled");
51
+ process.exit(0);
52
+ }
53
+ const instanceName = projectName.toLowerCase().replace(/[^a-z0-9]/g, "");
54
+ const jiraEmail = await p.text({ message: "Jira email" });
55
+ if (p.isCancel(jiraEmail)) {
56
+ p.cancel("Setup cancelled");
57
+ process.exit(0);
58
+ }
59
+ const jiraToken = await p.password({ message: "Jira API token" });
60
+ if (p.isCancel(jiraToken)) {
61
+ p.cancel("Setup cancelled");
62
+ process.exit(0);
63
+ }
64
+ const jiraProjectKey = await p.text({
65
+ message: "Jira project key",
66
+ placeholder: "PROJ"
67
+ });
68
+ if (p.isCancel(jiraProjectKey)) {
69
+ p.cancel("Setup cancelled");
70
+ process.exit(0);
71
+ }
72
+ const assigneesRaw = await p.text({
73
+ message: "Assignee Jira account IDs (comma-separated)",
74
+ placeholder: "accountId1,accountId2"
75
+ });
76
+ if (p.isCancel(assigneesRaw)) {
77
+ p.cancel("Setup cancelled");
78
+ process.exit(0);
79
+ }
80
+ const reassignTo = await p.text({
81
+ message: "Reassign completed tickets to (accountId, optional)",
82
+ placeholder: "reviewerAccountId"
83
+ });
84
+ if (p.isCancel(reassignTo)) {
85
+ p.cancel("Setup cancelled");
86
+ process.exit(0);
87
+ }
88
+ p.note("GitHub configuration", "Integration");
89
+ const githubOwner = await p.text({ message: "GitHub owner (org or user)" });
90
+ if (p.isCancel(githubOwner)) {
91
+ p.cancel("Setup cancelled");
92
+ process.exit(0);
93
+ }
94
+ const githubRepo = await p.text({ message: "GitHub repository name" });
95
+ if (p.isCancel(githubRepo)) {
96
+ p.cancel("Setup cancelled");
97
+ process.exit(0);
98
+ }
99
+ const githubToken = await p.password({ message: "GitHub token (ghp_...)" });
100
+ if (p.isCancel(githubToken)) {
101
+ p.cancel("Setup cancelled");
102
+ process.exit(0);
103
+ }
104
+ p.note("Telegram configuration", "Notifications");
105
+ const telegramBotToken = await p.password({ message: "Telegram bot token" });
106
+ if (p.isCancel(telegramBotToken)) {
107
+ p.cancel("Setup cancelled");
108
+ process.exit(0);
109
+ }
110
+ const telegramChatId = await p.text({
111
+ message: "Telegram chat ID (group or user)",
112
+ placeholder: "-100xxxxxxxxx"
113
+ });
114
+ if (p.isCancel(telegramChatId)) {
115
+ p.cancel("Setup cancelled");
116
+ process.exit(0);
117
+ }
118
+ const deployEnabled = await p.confirm({
119
+ message: "Enable auto-deploy to dev server?"
120
+ });
121
+ if (p.isCancel(deployEnabled)) {
122
+ p.cancel("Setup cancelled");
123
+ process.exit(0);
124
+ }
125
+ let deployCommand;
126
+ let healthCheckUrl;
127
+ if (deployEnabled) {
128
+ const cmd = await p.text({
129
+ message: "Deploy command",
130
+ placeholder: "./scripts/deploy-dev.sh"
131
+ });
132
+ if (p.isCancel(cmd)) {
133
+ p.cancel("Setup cancelled");
134
+ process.exit(0);
135
+ }
136
+ deployCommand = String(cmd);
137
+ const hc = await p.text({
138
+ message: "Health check URL (optional)",
139
+ placeholder: "https://dev.example.com/health"
140
+ });
141
+ if (!p.isCancel(hc) && hc) healthCheckUrl = String(hc);
142
+ }
143
+ const config = {
144
+ name: projectName,
145
+ jira: {
146
+ instance: instanceName,
147
+ url: String(jiraUrl),
148
+ email: String(jiraEmail),
149
+ token: String(jiraToken),
150
+ projectKey: String(jiraProjectKey).toUpperCase(),
151
+ assignees: String(assigneesRaw).split(",").map((s2) => s2.trim()).filter(Boolean),
152
+ reassignTo: reassignTo && !p.isCancel(reassignTo) ? String(reassignTo) : void 0
153
+ },
154
+ github: {
155
+ owner: String(githubOwner),
156
+ repo: String(githubRepo),
157
+ token: String(githubToken)
158
+ },
159
+ workspace: {
160
+ rootDir: projectDir
161
+ },
162
+ deploy: {
163
+ enabled: Boolean(deployEnabled),
164
+ command: deployCommand,
165
+ healthCheckUrl
166
+ },
167
+ telegram: {
168
+ botToken: String(telegramBotToken),
169
+ chatId: String(telegramChatId)
170
+ }
171
+ };
172
+ s.start("Saving config");
173
+ saveConfig(projectName, config);
174
+ s.stop(`Config saved \u2192 ~/.jira-acp/projects/${projectName}.json`);
175
+ s.start("Generating CLAUDE.md");
176
+ await generateClaudeMd(projectDir, config);
177
+ s.stop("CLAUDE.md generated");
178
+ s.start("Writing .mcp.json for Claude Code");
179
+ await writeMcpJson(projectDir, config, instanceName);
180
+ s.stop(".mcp.json written");
181
+ p.outro(
182
+ pc.green(
183
+ `Ready! Run: ${pc.bold(`jiraACP run <TICKET-KEY> --project ${projectName}`)}`
184
+ )
185
+ );
186
+ }
187
+ export {
188
+ runWizard
189
+ };
190
+ //# sourceMappingURL=wizard-CYEJJLNF.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/config/wizard.ts","../src/memory/mcp-json.ts"],"sourcesContent":["import * as p from \"@clack/prompts\";\nimport pc from \"picocolors\";\nimport { saveConfig } from \"./loader.js\";\nimport { writeMcpJson } from \"../memory/mcp-json.js\";\nimport { generateClaudeMd } from \"../memory/claude-md.js\";\n\nexport async function runWizard(\n projectName: string,\n projectDir: string,\n): Promise<void> {\n console.log(pc.bold(\"\\n jiraACP — Project Setup\\n\"));\n p.intro(pc.cyan(`Configuring project: ${pc.bold(projectName)}`));\n\n const s = p.spinner();\n\n // ── Jira ──────────────────────────────────────────────────────────────────\n p.note(\"Jira configuration\", \"Integration\");\n\n const jiraUrl = await p.text({\n message: \"Jira URL\",\n placeholder: \"https://your-team.atlassian.net\",\n });\n if (p.isCancel(jiraUrl)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n const instanceName = projectName.toLowerCase().replace(/[^a-z0-9]/g, \"\");\n\n const jiraEmail = await p.text({ message: \"Jira email\" });\n if (p.isCancel(jiraEmail)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n const jiraToken = await p.password({ message: \"Jira API token\" });\n if (p.isCancel(jiraToken)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n const jiraProjectKey = await p.text({\n message: \"Jira project key\",\n placeholder: \"PROJ\",\n });\n if (p.isCancel(jiraProjectKey)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n const assigneesRaw = await p.text({\n message: \"Assignee Jira account IDs (comma-separated)\",\n placeholder: \"accountId1,accountId2\",\n });\n if (p.isCancel(assigneesRaw)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n const reassignTo = await p.text({\n message: \"Reassign completed tickets to (accountId, optional)\",\n placeholder: \"reviewerAccountId\",\n });\n if (p.isCancel(reassignTo)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n // ── GitHub ────────────────────────────────────────────────────────────────\n p.note(\"GitHub configuration\", \"Integration\");\n\n const githubOwner = await p.text({ message: \"GitHub owner (org or user)\" });\n if (p.isCancel(githubOwner)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n const githubRepo = await p.text({ message: \"GitHub repository name\" });\n if (p.isCancel(githubRepo)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n const githubToken = await p.password({ message: \"GitHub token (ghp_...)\" });\n if (p.isCancel(githubToken)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n // ── Telegram ──────────────────────────────────────────────────────────────\n p.note(\"Telegram configuration\", \"Notifications\");\n\n const telegramBotToken = await p.password({ message: \"Telegram bot token\" });\n if (p.isCancel(telegramBotToken)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n const telegramChatId = await p.text({\n message: \"Telegram chat ID (group or user)\",\n placeholder: \"-100xxxxxxxxx\",\n });\n if (p.isCancel(telegramChatId)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n // ── Deploy (optional) ─────────────────────────────────────────────────────\n const deployEnabled = await p.confirm({\n message: \"Enable auto-deploy to dev server?\",\n });\n if (p.isCancel(deployEnabled)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n\n let deployCommand: string | undefined;\n let healthCheckUrl: string | undefined;\n if (deployEnabled) {\n const cmd = await p.text({\n message: \"Deploy command\",\n placeholder: \"./scripts/deploy-dev.sh\",\n });\n if (p.isCancel(cmd)) {\n p.cancel(\"Setup cancelled\");\n process.exit(0);\n }\n deployCommand = String(cmd);\n\n const hc = await p.text({\n message: \"Health check URL (optional)\",\n placeholder: \"https://dev.example.com/health\",\n });\n if (!p.isCancel(hc) && hc) healthCheckUrl = String(hc);\n }\n\n // ── Build config ──────────────────────────────────────────────────────────\n const config = {\n name: projectName,\n jira: {\n instance: instanceName,\n url: String(jiraUrl),\n email: String(jiraEmail),\n token: String(jiraToken),\n projectKey: String(jiraProjectKey).toUpperCase(),\n assignees: String(assigneesRaw)\n .split(\",\")\n .map((s) => s.trim())\n .filter(Boolean),\n reassignTo:\n reassignTo && !p.isCancel(reassignTo) ? String(reassignTo) : undefined,\n },\n github: {\n owner: String(githubOwner),\n repo: String(githubRepo),\n token: String(githubToken),\n },\n workspace: {\n rootDir: projectDir,\n },\n deploy: {\n enabled: Boolean(deployEnabled),\n command: deployCommand,\n healthCheckUrl,\n },\n telegram: {\n botToken: String(telegramBotToken),\n chatId: String(telegramChatId),\n },\n };\n\n // ── Save to ~/.jira-acp/projects/<name>.json ──────────────────────────────\n s.start(\"Saving config\");\n saveConfig(projectName, config);\n s.stop(`Config saved → ~/.jira-acp/projects/${projectName}.json`);\n\n s.start(\"Generating CLAUDE.md\");\n await generateClaudeMd(projectDir, config as never);\n s.stop(\"CLAUDE.md generated\");\n\n s.start(\"Writing .mcp.json for Claude Code\");\n await writeMcpJson(projectDir, config as never, instanceName);\n s.stop(\".mcp.json written\");\n\n p.outro(\n pc.green(\n `Ready! Run: ${pc.bold(`jiraACP run <TICKET-KEY> --project ${projectName}`)}`,\n ),\n );\n}\n","import fs from \"node:fs\";\nimport path from \"node:path\";\nimport type { ProjectConfig } from \"../config/schema.js\";\n\nexport async function writeMcpJson(\n projectDir: string,\n config: ProjectConfig,\n instanceName: string,\n): Promise<void> {\n const upper = instanceName.toUpperCase();\n const claudeDir = path.join(projectDir, \".claude\");\n fs.mkdirSync(claudeDir, { recursive: true });\n\n const mcpJson = {\n mcpServers: {\n jira: {\n command: \"jiraACP-mcp\",\n env: {\n [`JIRA_${upper}_URL`]: `env:JIRA_${upper}_URL`,\n [`JIRA_${upper}_TOKEN`]: `env:JIRA_${upper}_TOKEN`,\n [`JIRA_${upper}_EMAIL`]: `env:JIRA_${upper}_EMAIL`,\n },\n },\n },\n };\n\n fs.writeFileSync(\n path.join(claudeDir, \".mcp.json\"),\n JSON.stringify(mcpJson, null, 2),\n );\n}\n"],"mappings":"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","names":["s"]}