@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.
- package/README.md +283 -0
- package/dist/abort-GQE4OI5S.js +103 -0
- package/dist/abort-GQE4OI5S.js.map +1 -0
- package/dist/abort-VMRQOADY.js +96 -0
- package/dist/abort-VMRQOADY.js.map +1 -0
- package/dist/bot-WOTETAJY.js +13 -0
- package/dist/bot-WOTETAJY.js.map +1 -0
- package/dist/cancel-clarification-4G5S2HJZ.js +64 -0
- package/dist/cancel-clarification-4G5S2HJZ.js.map +1 -0
- package/dist/chunk-3U373M37.js +67 -0
- package/dist/chunk-3U373M37.js.map +1 -0
- package/dist/chunk-3YHD4SIN.js +97 -0
- package/dist/chunk-3YHD4SIN.js.map +1 -0
- package/dist/chunk-6IY6CRUJ.js +690 -0
- package/dist/chunk-6IY6CRUJ.js.map +1 -0
- package/dist/chunk-B6OA3XJK.js +1167 -0
- package/dist/chunk-B6OA3XJK.js.map +1 -0
- package/dist/chunk-BM4R6NST.js +191 -0
- package/dist/chunk-BM4R6NST.js.map +1 -0
- package/dist/chunk-FLPIU2QO.js +77 -0
- package/dist/chunk-FLPIU2QO.js.map +1 -0
- package/dist/chunk-H7YXX4UA.js +86 -0
- package/dist/chunk-H7YXX4UA.js.map +1 -0
- package/dist/chunk-IT74N3UH.js +19 -0
- package/dist/chunk-IT74N3UH.js.map +1 -0
- package/dist/chunk-JOT4UVSO.js +186 -0
- package/dist/chunk-JOT4UVSO.js.map +1 -0
- package/dist/chunk-KSJKCLEJ.js +222 -0
- package/dist/chunk-KSJKCLEJ.js.map +1 -0
- package/dist/chunk-LIEW4ULF.js +139 -0
- package/dist/chunk-LIEW4ULF.js.map +1 -0
- package/dist/chunk-M4V3YOCY.js +82 -0
- package/dist/chunk-M4V3YOCY.js.map +1 -0
- package/dist/chunk-MMWQHH25.js +207 -0
- package/dist/chunk-MMWQHH25.js.map +1 -0
- package/dist/chunk-OJ4CNF73.js +78 -0
- package/dist/chunk-OJ4CNF73.js.map +1 -0
- package/dist/chunk-PFJAC3RO.js +137 -0
- package/dist/chunk-PFJAC3RO.js.map +1 -0
- package/dist/chunk-PVKVCUNR.js +159 -0
- package/dist/chunk-PVKVCUNR.js.map +1 -0
- package/dist/chunk-RXT4WSIY.js +35 -0
- package/dist/chunk-RXT4WSIY.js.map +1 -0
- package/dist/chunk-RZK74PDF.js +34 -0
- package/dist/chunk-RZK74PDF.js.map +1 -0
- package/dist/chunk-UDTWVKRX.js +68 -0
- package/dist/chunk-UDTWVKRX.js.map +1 -0
- package/dist/chunk-VCEONSWJ.js +307 -0
- package/dist/chunk-VCEONSWJ.js.map +1 -0
- package/dist/chunk-VWBCDZWQ.js +119 -0
- package/dist/chunk-VWBCDZWQ.js.map +1 -0
- package/dist/chunk-WEJCTFQB.js +228 -0
- package/dist/chunk-WEJCTFQB.js.map +1 -0
- package/dist/chunk-YJK7IRPI.js +223 -0
- package/dist/chunk-YJK7IRPI.js.map +1 -0
- package/dist/claude-md-HQ6L4CRP.js +8 -0
- package/dist/claude-md-HQ6L4CRP.js.map +1 -0
- package/dist/cli.js +276 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands-RG45VBTZ.js +407 -0
- package/dist/commands-RG45VBTZ.js.map +1 -0
- package/dist/commands-WYVRVE5Z.js +400 -0
- package/dist/commands-WYVRVE5Z.js.map +1 -0
- package/dist/config-edit-G7O56HXO.js +50 -0
- package/dist/config-edit-G7O56HXO.js.map +1 -0
- package/dist/config-set-QN3JRNZL.js +63 -0
- package/dist/config-set-QN3JRNZL.js.map +1 -0
- package/dist/daemon-CGBV55JK.js +104 -0
- package/dist/daemon-CGBV55JK.js.map +1 -0
- package/dist/dashboard-YVFJ5DXR.js +143 -0
- package/dist/dashboard-YVFJ5DXR.js.map +1 -0
- package/dist/doctor-BPTLVLTD.js +98 -0
- package/dist/doctor-BPTLVLTD.js.map +1 -0
- package/dist/human-loop-RBTA2TYK.js +16 -0
- package/dist/human-loop-RBTA2TYK.js.map +1 -0
- package/dist/human-loop-XGWXUNCS.js +18 -0
- package/dist/human-loop-XGWXUNCS.js.map +1 -0
- package/dist/index.d.ts +583 -0
- package/dist/index.js +28 -0
- package/dist/index.js.map +1 -0
- package/dist/loader-DGW7HCJ5.js +21 -0
- package/dist/loader-DGW7HCJ5.js.map +1 -0
- package/dist/logs-JUVQWN6C.js +93 -0
- package/dist/logs-JUVQWN6C.js.map +1 -0
- package/dist/mcp.js +132 -0
- package/dist/mcp.js.map +1 -0
- package/dist/orchestrator-3MGXX3QW.js +22 -0
- package/dist/orchestrator-3MGXX3QW.js.map +1 -0
- package/dist/orchestrator-BVUKN5N3.js +13 -0
- package/dist/orchestrator-BVUKN5N3.js.map +1 -0
- package/dist/pause-FLDZ3OD6.js +62 -0
- package/dist/pause-FLDZ3OD6.js.map +1 -0
- package/dist/projects-QMIGNW7U.js +129 -0
- package/dist/projects-QMIGNW7U.js.map +1 -0
- package/dist/replay-M4JEG4Z4.js +151 -0
- package/dist/replay-M4JEG4Z4.js.map +1 -0
- package/dist/schedule-CDHD77VZ.js +17 -0
- package/dist/schedule-CDHD77VZ.js.map +1 -0
- package/dist/serve-XI7JTIPZ.js +231 -0
- package/dist/serve-XI7JTIPZ.js.map +1 -0
- package/dist/sprint-KZZWVNK6.js +200 -0
- package/dist/sprint-KZZWVNK6.js.map +1 -0
- package/dist/status-I6GU2LWE.js +48 -0
- package/dist/status-I6GU2LWE.js.map +1 -0
- package/dist/topic-manager-4AMEPMFI.js +12 -0
- package/dist/topic-manager-4AMEPMFI.js.map +1 -0
- package/dist/triage-WNHGPVZQ.js +251 -0
- package/dist/triage-WNHGPVZQ.js.map +1 -0
- package/dist/usage-AWWBI37F.js +155 -0
- package/dist/usage-AWWBI37F.js.map +1 -0
- package/dist/wizard-CYEJJLNF.js +190 -0
- package/dist/wizard-CYEJJLNF.js.map +1 -0
- package/package.json +56 -0
|
@@ -0,0 +1,159 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
getVerbosity
|
|
4
|
+
} from "./chunk-RZK74PDF.js";
|
|
5
|
+
import {
|
|
6
|
+
getOrCreateTopic
|
|
7
|
+
} from "./chunk-3U373M37.js";
|
|
8
|
+
import {
|
|
9
|
+
getBot
|
|
10
|
+
} from "./chunk-OJ4CNF73.js";
|
|
11
|
+
|
|
12
|
+
// src/integrations/telegram/notifier.ts
|
|
13
|
+
var STAGE_IDS = [
|
|
14
|
+
"fetch",
|
|
15
|
+
"analyze",
|
|
16
|
+
"clarify",
|
|
17
|
+
"code",
|
|
18
|
+
"git",
|
|
19
|
+
"review",
|
|
20
|
+
"deploy",
|
|
21
|
+
"test",
|
|
22
|
+
"notify"
|
|
23
|
+
];
|
|
24
|
+
var STAGE_EMOJI = {
|
|
25
|
+
fetch: "\u{1F4E5}",
|
|
26
|
+
analyze: "\u{1F50D}",
|
|
27
|
+
clarify: "\u{1F4AC}",
|
|
28
|
+
code: "\u{1F4BB}",
|
|
29
|
+
git: "\u{1F500}",
|
|
30
|
+
review: "\u{1F50E}",
|
|
31
|
+
deploy: "\u{1F680}",
|
|
32
|
+
test: "\u{1F9EA}",
|
|
33
|
+
notify: "\u{1F4E2}"
|
|
34
|
+
};
|
|
35
|
+
var STATUS_ICON = {
|
|
36
|
+
pending: "\u2B1C",
|
|
37
|
+
running: "\u25B6\uFE0F",
|
|
38
|
+
done: "\u2705",
|
|
39
|
+
skipped: "\u23ED",
|
|
40
|
+
failed: "\u274C"
|
|
41
|
+
};
|
|
42
|
+
function createTelegramNotifier(token, chatId, ticketKey, projectName, fallbackTopicId) {
|
|
43
|
+
const bot = getBot(token);
|
|
44
|
+
const stageStatus = Object.fromEntries(
|
|
45
|
+
STAGE_IDS.map((id) => [id, "pending"])
|
|
46
|
+
);
|
|
47
|
+
let progressMsgId;
|
|
48
|
+
let _topicResolved = false;
|
|
49
|
+
let _topicId = fallbackTopicId;
|
|
50
|
+
async function resolveTopicId() {
|
|
51
|
+
if (_topicResolved) return _topicId;
|
|
52
|
+
_topicResolved = true;
|
|
53
|
+
const created = await getOrCreateTopic(
|
|
54
|
+
bot,
|
|
55
|
+
chatId,
|
|
56
|
+
ticketKey,
|
|
57
|
+
projectName
|
|
58
|
+
).catch(() => void 0);
|
|
59
|
+
if (created !== void 0) _topicId = created;
|
|
60
|
+
return _topicId;
|
|
61
|
+
}
|
|
62
|
+
async function makeThreadOpts() {
|
|
63
|
+
const topicId = await resolveTopicId();
|
|
64
|
+
return topicId ? { message_thread_id: topicId } : {};
|
|
65
|
+
}
|
|
66
|
+
function buildProgressText() {
|
|
67
|
+
const pills = STAGE_IDS.map((id) => {
|
|
68
|
+
const emoji = STAGE_EMOJI[id];
|
|
69
|
+
const icon = STATUS_ICON[stageStatus[id] ?? "pending"];
|
|
70
|
+
return `${emoji}${icon}`;
|
|
71
|
+
}).join(" ");
|
|
72
|
+
const current = STAGE_IDS.find((id) => stageStatus[id] === "running");
|
|
73
|
+
const statusLine = current ? `
|
|
74
|
+
Stage: <b>${current}</b> \u2014 in progress\u2026` : "";
|
|
75
|
+
return `\u{1F504} <b>${ticketKey}</b>
|
|
76
|
+
${pills}${statusLine}`;
|
|
77
|
+
}
|
|
78
|
+
async function upsertProgress() {
|
|
79
|
+
const verbosity = getVerbosity(chatId);
|
|
80
|
+
if (verbosity === "low") return;
|
|
81
|
+
const text = buildProgressText();
|
|
82
|
+
const threadOpts = await makeThreadOpts();
|
|
83
|
+
if (progressMsgId !== void 0) {
|
|
84
|
+
try {
|
|
85
|
+
await bot.api.editMessageText(chatId, progressMsgId, text, {
|
|
86
|
+
parse_mode: "HTML"
|
|
87
|
+
});
|
|
88
|
+
return;
|
|
89
|
+
} catch {
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
const msg = await bot.api.sendMessage(chatId, text, {
|
|
93
|
+
parse_mode: "HTML",
|
|
94
|
+
...threadOpts
|
|
95
|
+
});
|
|
96
|
+
progressMsgId = msg.message_id;
|
|
97
|
+
}
|
|
98
|
+
return {
|
|
99
|
+
async send(message) {
|
|
100
|
+
const threadOpts = await makeThreadOpts();
|
|
101
|
+
const msg = await bot.api.sendMessage(chatId, message, {
|
|
102
|
+
parse_mode: "HTML",
|
|
103
|
+
...threadOpts
|
|
104
|
+
});
|
|
105
|
+
return msg.message_id;
|
|
106
|
+
},
|
|
107
|
+
async sendError(tk, err) {
|
|
108
|
+
const text = err instanceof Error ? err.message : String(err);
|
|
109
|
+
const threadOpts = await makeThreadOpts();
|
|
110
|
+
await bot.api.sendMessage(
|
|
111
|
+
chatId,
|
|
112
|
+
`\u274C <b>${tk}</b> pipeline failed
|
|
113
|
+
<code>${text}</code>`,
|
|
114
|
+
{ parse_mode: "HTML", ...threadOpts }
|
|
115
|
+
);
|
|
116
|
+
},
|
|
117
|
+
async sendDone(tk, { summary, prNumber, deployUrl }) {
|
|
118
|
+
const lines = [`\u2705 <b>${tk}</b> completed`, summary];
|
|
119
|
+
if (prNumber) lines.push(`PR: #${prNumber}`);
|
|
120
|
+
if (deployUrl) lines.push(`Deploy: ${deployUrl}`);
|
|
121
|
+
const threadOpts = await makeThreadOpts();
|
|
122
|
+
await bot.api.sendMessage(chatId, lines.join("\n"), {
|
|
123
|
+
parse_mode: "HTML",
|
|
124
|
+
...threadOpts
|
|
125
|
+
});
|
|
126
|
+
},
|
|
127
|
+
async notifyStageStarted(stage) {
|
|
128
|
+
stageStatus[stage] = "running";
|
|
129
|
+
await upsertProgress();
|
|
130
|
+
},
|
|
131
|
+
async notifyStageCompleted(stage) {
|
|
132
|
+
stageStatus[stage] = "done";
|
|
133
|
+
await upsertProgress();
|
|
134
|
+
},
|
|
135
|
+
async notifyStageFailed(stage, error) {
|
|
136
|
+
stageStatus[stage] = "failed";
|
|
137
|
+
await upsertProgress().catch(() => void 0);
|
|
138
|
+
const verbosity = getVerbosity(chatId);
|
|
139
|
+
if (verbosity !== "low") {
|
|
140
|
+
const threadOpts = await makeThreadOpts();
|
|
141
|
+
await bot.api.sendMessage(
|
|
142
|
+
chatId,
|
|
143
|
+
`\u274C Stage <b>${stage}</b> failed:
|
|
144
|
+
<code>${error.slice(0, 300)}</code>`,
|
|
145
|
+
{ parse_mode: "HTML", ...threadOpts }
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
},
|
|
149
|
+
async notifyStageSkipped(stage) {
|
|
150
|
+
stageStatus[stage] = "skipped";
|
|
151
|
+
await upsertProgress();
|
|
152
|
+
}
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
export {
|
|
157
|
+
createTelegramNotifier
|
|
158
|
+
};
|
|
159
|
+
//# sourceMappingURL=chunk-PVKVCUNR.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/integrations/telegram/notifier.ts"],"sourcesContent":["import type { StageId } from \"../../config/schema.js\";\nimport { getBot } from \"./bot.js\";\nimport { getOrCreateTopic } from \"./topic-manager.js\";\nimport { getVerbosity } from \"./prefs.js\";\n\nexport interface TelegramNotifier {\n send(message: string): Promise<number>;\n sendError(ticketKey: string, err: unknown): Promise<void>;\n sendDone(\n ticketKey: string,\n opts: { summary: string; prNumber?: number; deployUrl?: string },\n ): Promise<void>;\n notifyStageStarted(stage: StageId): Promise<void>;\n notifyStageCompleted(stage: StageId): Promise<void>;\n notifyStageFailed(stage: StageId, error: string): Promise<void>;\n notifyStageSkipped(stage: StageId): Promise<void>;\n}\n\ntype StageStatus = \"pending\" | \"running\" | \"done\" | \"skipped\" | \"failed\";\n\nconst STAGE_IDS: StageId[] = [\n \"fetch\",\n \"analyze\",\n \"clarify\",\n \"code\",\n \"git\",\n \"review\",\n \"deploy\",\n \"test\",\n \"notify\",\n];\n\nconst STAGE_EMOJI: Record<StageId, string> = {\n fetch: \"📥\",\n analyze: \"🔍\",\n clarify: \"💬\",\n code: \"💻\",\n git: \"🔀\",\n review: \"🔎\",\n deploy: \"🚀\",\n test: \"🧪\",\n notify: \"📢\",\n};\n\nconst STATUS_ICON: Record<StageStatus, string> = {\n pending: \"⬜\",\n running: \"▶️\",\n done: \"✅\",\n skipped: \"⏭\",\n failed: \"❌\",\n};\n\nexport function createTelegramNotifier(\n token: string,\n chatId: number | string,\n ticketKey: string,\n projectName: string,\n fallbackTopicId?: number,\n): TelegramNotifier {\n const bot = getBot(token);\n\n // Stage progress state\n const stageStatus: Record<string, StageStatus> = Object.fromEntries(\n STAGE_IDS.map((id) => [id, \"pending\" as StageStatus]),\n );\n let progressMsgId: number | undefined;\n\n // Lazy topic resolution — try forum topic, fall back to configured topicId\n let _topicResolved = false;\n let _topicId: number | undefined = fallbackTopicId;\n\n async function resolveTopicId(): Promise<number | undefined> {\n if (_topicResolved) return _topicId;\n _topicResolved = true;\n const created = await getOrCreateTopic(\n bot,\n chatId,\n ticketKey,\n projectName,\n ).catch(() => undefined);\n if (created !== undefined) _topicId = created;\n return _topicId;\n }\n\n async function makeThreadOpts(): Promise<\n Record<string, number> | Record<string, never>\n > {\n const topicId = await resolveTopicId();\n return topicId ? { message_thread_id: topicId } : {};\n }\n\n function buildProgressText(): string {\n const pills = STAGE_IDS.map((id) => {\n const emoji = STAGE_EMOJI[id];\n const icon = STATUS_ICON[stageStatus[id] ?? \"pending\"];\n return `${emoji}${icon}`;\n }).join(\" \");\n\n const current = STAGE_IDS.find((id) => stageStatus[id] === \"running\");\n const statusLine = current\n ? `\\nStage: <b>${current}</b> — in progress…`\n : \"\";\n\n return `🔄 <b>${ticketKey}</b>\\n${pills}${statusLine}`;\n }\n\n async function upsertProgress(): Promise<void> {\n const verbosity = getVerbosity(chatId);\n if (verbosity === \"low\") return;\n\n const text = buildProgressText();\n const threadOpts = await makeThreadOpts();\n\n if (progressMsgId !== undefined) {\n try {\n await bot.api.editMessageText(chatId, progressMsgId, text, {\n parse_mode: \"HTML\",\n });\n return;\n } catch {\n // Message deleted or too old — fall through to send new\n }\n }\n\n const msg = await bot.api.sendMessage(chatId, text, {\n parse_mode: \"HTML\",\n ...threadOpts,\n });\n progressMsgId = msg.message_id;\n }\n\n return {\n async send(message) {\n const threadOpts = await makeThreadOpts();\n const msg = await bot.api.sendMessage(chatId, message, {\n parse_mode: \"HTML\",\n ...threadOpts,\n });\n return msg.message_id;\n },\n\n async sendError(tk, err) {\n const text = err instanceof Error ? err.message : String(err);\n const threadOpts = await makeThreadOpts();\n await bot.api.sendMessage(\n chatId,\n `❌ <b>${tk}</b> pipeline failed\\n<code>${text}</code>`,\n { parse_mode: \"HTML\", ...threadOpts },\n );\n },\n\n async sendDone(tk, { summary, prNumber, deployUrl }) {\n const lines = [`✅ <b>${tk}</b> completed`, summary];\n if (prNumber) lines.push(`PR: #${prNumber}`);\n if (deployUrl) lines.push(`Deploy: ${deployUrl}`);\n const threadOpts = await makeThreadOpts();\n await bot.api.sendMessage(chatId, lines.join(\"\\n\"), {\n parse_mode: \"HTML\",\n ...threadOpts,\n });\n },\n\n async notifyStageStarted(stage) {\n stageStatus[stage] = \"running\";\n await upsertProgress();\n },\n\n async notifyStageCompleted(stage) {\n stageStatus[stage] = \"done\";\n await upsertProgress();\n },\n\n async notifyStageFailed(stage, error) {\n stageStatus[stage] = \"failed\";\n await upsertProgress().catch(() => undefined);\n\n const verbosity = getVerbosity(chatId);\n if (verbosity !== \"low\") {\n const threadOpts = await makeThreadOpts();\n await bot.api.sendMessage(\n chatId,\n `❌ Stage <b>${stage}</b> failed:\\n<code>${error.slice(0, 300)}</code>`,\n { parse_mode: \"HTML\", ...threadOpts },\n );\n }\n },\n\n async notifyStageSkipped(stage) {\n stageStatus[stage] = \"skipped\";\n await upsertProgress();\n },\n };\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/pricing.ts
|
|
4
|
+
var MODEL_PRICING = {
|
|
5
|
+
"claude-haiku-4-5": { inputPer1M: 0.8, outputPer1M: 4 },
|
|
6
|
+
"claude-sonnet-4-5": { inputPer1M: 3, outputPer1M: 15 },
|
|
7
|
+
"claude-opus-4-5": { inputPer1M: 15, outputPer1M: 75 },
|
|
8
|
+
"claude-haiku-4": { inputPer1M: 0.8, outputPer1M: 4 },
|
|
9
|
+
"claude-sonnet-4": { inputPer1M: 3, outputPer1M: 15 },
|
|
10
|
+
"claude-opus-4": { inputPer1M: 15, outputPer1M: 75 }
|
|
11
|
+
};
|
|
12
|
+
var DEFAULT_PRICING = { inputPer1M: 3, outputPer1M: 15 };
|
|
13
|
+
function extractTokenUsage(output) {
|
|
14
|
+
if (typeof output !== "object" || output === null) return null;
|
|
15
|
+
const raw = output["tokenUsage"];
|
|
16
|
+
if (typeof raw !== "object" || raw === null) return null;
|
|
17
|
+
const tu = raw;
|
|
18
|
+
if (typeof tu["inputTokens"] !== "number" || typeof tu["outputTokens"] !== "number")
|
|
19
|
+
return null;
|
|
20
|
+
return {
|
|
21
|
+
inputTokens: tu["inputTokens"],
|
|
22
|
+
outputTokens: tu["outputTokens"],
|
|
23
|
+
model: typeof tu["model"] === "string" ? tu["model"] : "claude-sonnet-4"
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
function estimateCostUsd(inputTokens, outputTokens, model) {
|
|
27
|
+
const pricing = MODEL_PRICING[model] ?? DEFAULT_PRICING;
|
|
28
|
+
return inputTokens / 1e6 * pricing.inputPer1M + outputTokens / 1e6 * pricing.outputPer1M;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export {
|
|
32
|
+
extractTokenUsage,
|
|
33
|
+
estimateCostUsd
|
|
34
|
+
};
|
|
35
|
+
//# sourceMappingURL=chunk-RXT4WSIY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/pricing.ts"],"sourcesContent":["export interface ModelPricing {\n inputPer1M: number;\n outputPer1M: number;\n}\n\nexport interface TokenUsage {\n inputTokens: number;\n outputTokens: number;\n model: string;\n}\n\n// Prices in USD per 1M tokens — https://www.anthropic.com/pricing (updated 2026-01)\nexport const MODEL_PRICING: Record<string, ModelPricing> = {\n \"claude-haiku-4-5\": { inputPer1M: 0.8, outputPer1M: 4.0 },\n \"claude-sonnet-4-5\": { inputPer1M: 3.0, outputPer1M: 15.0 },\n \"claude-opus-4-5\": { inputPer1M: 15.0, outputPer1M: 75.0 },\n \"claude-haiku-4\": { inputPer1M: 0.8, outputPer1M: 4.0 },\n \"claude-sonnet-4\": { inputPer1M: 3.0, outputPer1M: 15.0 },\n \"claude-opus-4\": { inputPer1M: 15.0, outputPer1M: 75.0 },\n};\n\nconst DEFAULT_PRICING: ModelPricing = { inputPer1M: 3.0, outputPer1M: 15.0 };\n\n/** Type-safe extractor for tokenUsage from a stage output. Returns null if not present or malformed. */\nexport function extractTokenUsage(output: unknown): TokenUsage | null {\n if (typeof output !== \"object\" || output === null) return null;\n const raw = (output as Record<string, unknown>)[\"tokenUsage\"];\n if (typeof raw !== \"object\" || raw === null) return null;\n const tu = raw as Record<string, unknown>;\n if (\n typeof tu[\"inputTokens\"] !== \"number\" ||\n typeof tu[\"outputTokens\"] !== \"number\"\n )\n return null;\n return {\n inputTokens: tu[\"inputTokens\"] as number,\n outputTokens: tu[\"outputTokens\"] as number,\n model: typeof tu[\"model\"] === \"string\" ? tu[\"model\"] : \"claude-sonnet-4\",\n };\n}\n\nexport function estimateCostUsd(\n inputTokens: number,\n outputTokens: number,\n model: string,\n): number {\n const pricing = MODEL_PRICING[model] ?? DEFAULT_PRICING;\n return (\n (inputTokens / 1_000_000) * pricing.inputPer1M +\n (outputTokens / 1_000_000) * pricing.outputPer1M\n );\n}\n"],"mappings":";;;AAYO,IAAM,gBAA8C;AAAA,EACzD,oBAAoB,EAAE,YAAY,KAAK,aAAa,EAAI;AAAA,EACxD,qBAAqB,EAAE,YAAY,GAAK,aAAa,GAAK;AAAA,EAC1D,mBAAmB,EAAE,YAAY,IAAM,aAAa,GAAK;AAAA,EACzD,kBAAkB,EAAE,YAAY,KAAK,aAAa,EAAI;AAAA,EACtD,mBAAmB,EAAE,YAAY,GAAK,aAAa,GAAK;AAAA,EACxD,iBAAiB,EAAE,YAAY,IAAM,aAAa,GAAK;AACzD;AAEA,IAAM,kBAAgC,EAAE,YAAY,GAAK,aAAa,GAAK;AAGpE,SAAS,kBAAkB,QAAoC;AACpE,MAAI,OAAO,WAAW,YAAY,WAAW,KAAM,QAAO;AAC1D,QAAM,MAAO,OAAmC,YAAY;AAC5D,MAAI,OAAO,QAAQ,YAAY,QAAQ,KAAM,QAAO;AACpD,QAAM,KAAK;AACX,MACE,OAAO,GAAG,aAAa,MAAM,YAC7B,OAAO,GAAG,cAAc,MAAM;AAE9B,WAAO;AACT,SAAO;AAAA,IACL,aAAa,GAAG,aAAa;AAAA,IAC7B,cAAc,GAAG,cAAc;AAAA,IAC/B,OAAO,OAAO,GAAG,OAAO,MAAM,WAAW,GAAG,OAAO,IAAI;AAAA,EACzD;AACF;AAEO,SAAS,gBACd,aACA,cACA,OACQ;AACR,QAAM,UAAU,cAAc,KAAK,KAAK;AACxC,SACG,cAAc,MAAa,QAAQ,aACnC,eAAe,MAAa,QAAQ;AAEzC;","names":[]}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/integrations/telegram/prefs.ts
|
|
4
|
+
import fs from "fs";
|
|
5
|
+
import path from "path";
|
|
6
|
+
import os from "os";
|
|
7
|
+
var PREFS_PATH = path.join(os.homedir(), ".jira-acp", "telegram-prefs.json");
|
|
8
|
+
function load() {
|
|
9
|
+
if (!fs.existsSync(PREFS_PATH)) return { verbosity: {} };
|
|
10
|
+
try {
|
|
11
|
+
return JSON.parse(fs.readFileSync(PREFS_PATH, "utf8"));
|
|
12
|
+
} catch {
|
|
13
|
+
return { verbosity: {} };
|
|
14
|
+
}
|
|
15
|
+
}
|
|
16
|
+
function save(prefs) {
|
|
17
|
+
fs.mkdirSync(path.dirname(PREFS_PATH), { recursive: true });
|
|
18
|
+
fs.writeFileSync(PREFS_PATH, JSON.stringify(prefs, null, 2));
|
|
19
|
+
}
|
|
20
|
+
function getVerbosity(chatId) {
|
|
21
|
+
const prefs = load();
|
|
22
|
+
return prefs.verbosity[String(chatId)] ?? "medium";
|
|
23
|
+
}
|
|
24
|
+
function setVerbosity(chatId, level) {
|
|
25
|
+
const prefs = load();
|
|
26
|
+
prefs.verbosity[String(chatId)] = level;
|
|
27
|
+
save(prefs);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export {
|
|
31
|
+
getVerbosity,
|
|
32
|
+
setVerbosity
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=chunk-RZK74PDF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/integrations/telegram/prefs.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\n\nconst PREFS_PATH = path.join(os.homedir(), \".jira-acp\", \"telegram-prefs.json\");\n\nexport type Verbosity = \"low\" | \"medium\" | \"high\";\n\ninterface TelegramPrefs {\n verbosity: Record<string, Verbosity>;\n}\n\nfunction load(): TelegramPrefs {\n if (!fs.existsSync(PREFS_PATH)) return { verbosity: {} };\n try {\n return JSON.parse(fs.readFileSync(PREFS_PATH, \"utf8\")) as TelegramPrefs;\n } catch {\n return { verbosity: {} };\n }\n}\n\nfunction save(prefs: TelegramPrefs): void {\n fs.mkdirSync(path.dirname(PREFS_PATH), { recursive: true });\n fs.writeFileSync(PREFS_PATH, JSON.stringify(prefs, null, 2));\n}\n\nexport function getVerbosity(chatId: number | string): Verbosity {\n const prefs = load();\n return prefs.verbosity[String(chatId)] ?? \"medium\";\n}\n\nexport function setVerbosity(chatId: number | string, level: Verbosity): void {\n const prefs = load();\n prefs.verbosity[String(chatId)] = level;\n save(prefs);\n}\n"],"mappings":";;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AAEf,IAAM,aAAa,KAAK,KAAK,GAAG,QAAQ,GAAG,aAAa,qBAAqB;AAQ7E,SAAS,OAAsB;AAC7B,MAAI,CAAC,GAAG,WAAW,UAAU,EAAG,QAAO,EAAE,WAAW,CAAC,EAAE;AACvD,MAAI;AACF,WAAO,KAAK,MAAM,GAAG,aAAa,YAAY,MAAM,CAAC;AAAA,EACvD,QAAQ;AACN,WAAO,EAAE,WAAW,CAAC,EAAE;AAAA,EACzB;AACF;AAEA,SAAS,KAAK,OAA4B;AACxC,KAAG,UAAU,KAAK,QAAQ,UAAU,GAAG,EAAE,WAAW,KAAK,CAAC;AAC1D,KAAG,cAAc,YAAY,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAC7D;AAEO,SAAS,aAAa,QAAoC;AAC/D,QAAM,QAAQ,KAAK;AACnB,SAAO,MAAM,UAAU,OAAO,MAAM,CAAC,KAAK;AAC5C;AAEO,SAAS,aAAa,QAAyB,OAAwB;AAC5E,QAAM,QAAQ,KAAK;AACnB,QAAM,UAAU,OAAO,MAAM,CAAC,IAAI;AAClC,OAAK,KAAK;AACZ;","names":[]}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/utils/process.ts
|
|
4
|
+
import { spawn } from "child_process";
|
|
5
|
+
async function spawnSafe(bin, args, opts = {}) {
|
|
6
|
+
return new Promise((resolve, reject) => {
|
|
7
|
+
const proc = spawn(bin, args, {
|
|
8
|
+
cwd: opts.cwd,
|
|
9
|
+
env: opts.env ?? buildMinimalEnv(),
|
|
10
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
11
|
+
});
|
|
12
|
+
let stdout = "";
|
|
13
|
+
let stderr = "";
|
|
14
|
+
let stallTimer;
|
|
15
|
+
let globalTimer;
|
|
16
|
+
const resetStall = () => {
|
|
17
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
18
|
+
if (opts.stallTimeoutMs) {
|
|
19
|
+
stallTimer = setTimeout(() => {
|
|
20
|
+
proc.kill("SIGKILL");
|
|
21
|
+
reject(
|
|
22
|
+
new Error(
|
|
23
|
+
`Process stalled (no output for ${opts.stallTimeoutMs}ms)`
|
|
24
|
+
)
|
|
25
|
+
);
|
|
26
|
+
}, opts.stallTimeoutMs);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
if (opts.timeoutMs) {
|
|
30
|
+
globalTimer = setTimeout(() => {
|
|
31
|
+
proc.kill("SIGKILL");
|
|
32
|
+
reject(new Error(`Process timed out after ${opts.timeoutMs}ms`));
|
|
33
|
+
}, opts.timeoutMs);
|
|
34
|
+
}
|
|
35
|
+
resetStall();
|
|
36
|
+
proc.stdout.on("data", (chunk) => {
|
|
37
|
+
stdout += chunk.toString();
|
|
38
|
+
resetStall();
|
|
39
|
+
});
|
|
40
|
+
proc.stderr.on("data", (chunk) => {
|
|
41
|
+
stderr += chunk.toString();
|
|
42
|
+
});
|
|
43
|
+
proc.on("close", (code) => {
|
|
44
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
45
|
+
if (globalTimer) clearTimeout(globalTimer);
|
|
46
|
+
resolve({ stdout, stderr, exitCode: code ?? 1 });
|
|
47
|
+
});
|
|
48
|
+
proc.on("error", (err) => {
|
|
49
|
+
if (stallTimer) clearTimeout(stallTimer);
|
|
50
|
+
if (globalTimer) clearTimeout(globalTimer);
|
|
51
|
+
reject(err);
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
function buildMinimalEnv(extra = {}) {
|
|
56
|
+
return {
|
|
57
|
+
PATH: process.env["PATH"] ?? "",
|
|
58
|
+
HOME: process.env["HOME"] ?? "",
|
|
59
|
+
ANTHROPIC_API_KEY: process.env["ANTHROPIC_API_KEY"] ?? "",
|
|
60
|
+
...extra
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export {
|
|
65
|
+
spawnSafe,
|
|
66
|
+
buildMinimalEnv
|
|
67
|
+
};
|
|
68
|
+
//# sourceMappingURL=chunk-UDTWVKRX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/process.ts"],"sourcesContent":["import { spawn } from \"node:child_process\";\n\nexport interface RunResult {\n stdout: string;\n stderr: string;\n exitCode: number;\n}\n\nexport interface SpawnOptions {\n cwd?: string;\n env?: Record<string, string>;\n timeoutMs?: number;\n stallTimeoutMs?: number;\n}\n\n/** Spawn a process safely — args as array, never shell string (no injection risk) */\nexport async function spawnSafe(\n bin: string,\n args: string[],\n opts: SpawnOptions = {},\n): Promise<RunResult> {\n return new Promise((resolve, reject) => {\n const proc = spawn(bin, args, {\n cwd: opts.cwd,\n env: opts.env ?? buildMinimalEnv(),\n stdio: [\"pipe\", \"pipe\", \"pipe\"],\n });\n\n let stdout = \"\";\n let stderr = \"\";\n let stallTimer: NodeJS.Timeout | undefined;\n let globalTimer: NodeJS.Timeout | undefined;\n\n const resetStall = (): void => {\n if (stallTimer) clearTimeout(stallTimer);\n if (opts.stallTimeoutMs) {\n stallTimer = setTimeout(() => {\n proc.kill(\"SIGKILL\");\n reject(\n new Error(\n `Process stalled (no output for ${opts.stallTimeoutMs}ms)`,\n ),\n );\n }, opts.stallTimeoutMs);\n }\n };\n\n if (opts.timeoutMs) {\n globalTimer = setTimeout(() => {\n proc.kill(\"SIGKILL\");\n reject(new Error(`Process timed out after ${opts.timeoutMs}ms`));\n }, opts.timeoutMs);\n }\n\n resetStall();\n\n proc.stdout.on(\"data\", (chunk: Buffer) => {\n stdout += chunk.toString();\n resetStall();\n });\n\n proc.stderr.on(\"data\", (chunk: Buffer) => {\n stderr += chunk.toString();\n });\n\n proc.on(\"close\", (code) => {\n if (stallTimer) clearTimeout(stallTimer);\n if (globalTimer) clearTimeout(globalTimer);\n resolve({ stdout, stderr, exitCode: code ?? 1 });\n });\n\n proc.on(\"error\", (err) => {\n if (stallTimer) clearTimeout(stallTimer);\n if (globalTimer) clearTimeout(globalTimer);\n reject(err);\n });\n });\n}\n\n/** Only forward what agents need — never spread full process.env */\nexport function buildMinimalEnv(\n extra: Record<string, string> = {},\n): Record<string, string> {\n return {\n PATH: process.env[\"PATH\"] ?? \"\",\n HOME: process.env[\"HOME\"] ?? \"\",\n ANTHROPIC_API_KEY: process.env[\"ANTHROPIC_API_KEY\"] ?? \"\",\n ...extra,\n };\n}\n"],"mappings":";;;AAAA,SAAS,aAAa;AAgBtB,eAAsB,UACpB,KACA,MACA,OAAqB,CAAC,GACF;AACpB,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAW;AACtC,UAAM,OAAO,MAAM,KAAK,MAAM;AAAA,MAC5B,KAAK,KAAK;AAAA,MACV,KAAK,KAAK,OAAO,gBAAgB;AAAA,MACjC,OAAO,CAAC,QAAQ,QAAQ,MAAM;AAAA,IAChC,CAAC;AAED,QAAI,SAAS;AACb,QAAI,SAAS;AACb,QAAI;AACJ,QAAI;AAEJ,UAAM,aAAa,MAAY;AAC7B,UAAI,WAAY,cAAa,UAAU;AACvC,UAAI,KAAK,gBAAgB;AACvB,qBAAa,WAAW,MAAM;AAC5B,eAAK,KAAK,SAAS;AACnB;AAAA,YACE,IAAI;AAAA,cACF,kCAAkC,KAAK,cAAc;AAAA,YACvD;AAAA,UACF;AAAA,QACF,GAAG,KAAK,cAAc;AAAA,MACxB;AAAA,IACF;AAEA,QAAI,KAAK,WAAW;AAClB,oBAAc,WAAW,MAAM;AAC7B,aAAK,KAAK,SAAS;AACnB,eAAO,IAAI,MAAM,2BAA2B,KAAK,SAAS,IAAI,CAAC;AAAA,MACjE,GAAG,KAAK,SAAS;AAAA,IACnB;AAEA,eAAW;AAEX,SAAK,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACxC,gBAAU,MAAM,SAAS;AACzB,iBAAW;AAAA,IACb,CAAC;AAED,SAAK,OAAO,GAAG,QAAQ,CAAC,UAAkB;AACxC,gBAAU,MAAM,SAAS;AAAA,IAC3B,CAAC;AAED,SAAK,GAAG,SAAS,CAAC,SAAS;AACzB,UAAI,WAAY,cAAa,UAAU;AACvC,UAAI,YAAa,cAAa,WAAW;AACzC,cAAQ,EAAE,QAAQ,QAAQ,UAAU,QAAQ,EAAE,CAAC;AAAA,IACjD,CAAC;AAED,SAAK,GAAG,SAAS,CAAC,QAAQ;AACxB,UAAI,WAAY,cAAa,UAAU;AACvC,UAAI,YAAa,cAAa,WAAW;AACzC,aAAO,GAAG;AAAA,IACZ,CAAC;AAAA,EACH,CAAC;AACH;AAGO,SAAS,gBACd,QAAgC,CAAC,GACT;AACxB,SAAO;AAAA,IACL,MAAM,QAAQ,IAAI,MAAM,KAAK;AAAA,IAC7B,MAAM,QAAQ,IAAI,MAAM,KAAK;AAAA,IAC7B,mBAAmB,QAAQ,IAAI,mBAAmB,KAAK;AAAA,IACvD,GAAG;AAAA,EACL;AACF;","names":[]}
|
|
@@ -0,0 +1,307 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
buildMinimalEnv,
|
|
4
|
+
spawnSafe
|
|
5
|
+
} from "./chunk-UDTWVKRX.js";
|
|
6
|
+
import {
|
|
7
|
+
getTicket
|
|
8
|
+
} from "./chunk-JOT4UVSO.js";
|
|
9
|
+
|
|
10
|
+
// src/integrations/github/client.ts
|
|
11
|
+
import { Octokit } from "@octokit/rest";
|
|
12
|
+
function createGitHubClient(token, owner, repo) {
|
|
13
|
+
const octokit = new Octokit({ auth: token });
|
|
14
|
+
return {
|
|
15
|
+
async createBranch(base, branch) {
|
|
16
|
+
const { data: ref } = await octokit.git.getRef({
|
|
17
|
+
owner,
|
|
18
|
+
repo,
|
|
19
|
+
ref: `heads/${base}`
|
|
20
|
+
});
|
|
21
|
+
await octokit.git.createRef({
|
|
22
|
+
owner,
|
|
23
|
+
repo,
|
|
24
|
+
ref: `refs/heads/${branch}`,
|
|
25
|
+
sha: ref.object.sha
|
|
26
|
+
});
|
|
27
|
+
},
|
|
28
|
+
async createPR({ title, body, head, base, draft = false }) {
|
|
29
|
+
const { data } = await octokit.pulls.create({
|
|
30
|
+
owner,
|
|
31
|
+
repo,
|
|
32
|
+
title,
|
|
33
|
+
body,
|
|
34
|
+
head,
|
|
35
|
+
base,
|
|
36
|
+
draft
|
|
37
|
+
});
|
|
38
|
+
return data.number;
|
|
39
|
+
},
|
|
40
|
+
async mergePR(prNumber, strategy) {
|
|
41
|
+
const mergeMethod = strategy === "squash" ? "squash" : strategy === "rebase" ? "rebase" : "merge";
|
|
42
|
+
await octokit.pulls.merge({
|
|
43
|
+
owner,
|
|
44
|
+
repo,
|
|
45
|
+
pull_number: prNumber,
|
|
46
|
+
merge_method: mergeMethod
|
|
47
|
+
});
|
|
48
|
+
},
|
|
49
|
+
async addReviewers(prNumber, reviewers) {
|
|
50
|
+
if (reviewers.length === 0) return;
|
|
51
|
+
await octokit.pulls.requestReviewers({
|
|
52
|
+
owner,
|
|
53
|
+
repo,
|
|
54
|
+
pull_number: prNumber,
|
|
55
|
+
reviewers
|
|
56
|
+
});
|
|
57
|
+
},
|
|
58
|
+
async getPR(prNumber) {
|
|
59
|
+
const { data } = await octokit.pulls.get({
|
|
60
|
+
owner,
|
|
61
|
+
repo,
|
|
62
|
+
pull_number: prNumber
|
|
63
|
+
});
|
|
64
|
+
return { state: data.state, merged: data.merged };
|
|
65
|
+
},
|
|
66
|
+
async getRunStatus(branch) {
|
|
67
|
+
try {
|
|
68
|
+
const { data } = await octokit.repos.getCombinedStatusForRef({
|
|
69
|
+
owner,
|
|
70
|
+
repo,
|
|
71
|
+
ref: branch
|
|
72
|
+
});
|
|
73
|
+
if (data.state === "success") return "success";
|
|
74
|
+
if (data.state === "failure") return "failure";
|
|
75
|
+
return "pending";
|
|
76
|
+
} catch {
|
|
77
|
+
return "unknown";
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
};
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// src/memory/context-builder.ts
|
|
84
|
+
import fs from "fs";
|
|
85
|
+
import path from "path";
|
|
86
|
+
function writeTicketContext(memoryDir, ticket) {
|
|
87
|
+
fs.mkdirSync(memoryDir, { recursive: true });
|
|
88
|
+
const content = `# Ticket: ${ticket.key}
|
|
89
|
+
|
|
90
|
+
## Summary
|
|
91
|
+
${ticket.summary}
|
|
92
|
+
|
|
93
|
+
## Description
|
|
94
|
+
${ticket.description || "(none)"}
|
|
95
|
+
|
|
96
|
+
## Acceptance Criteria
|
|
97
|
+
${ticket.acceptanceCriteria || "(none)"}
|
|
98
|
+
|
|
99
|
+
## Priority
|
|
100
|
+
${ticket.priority}
|
|
101
|
+
${ticket.clarifications ? `
|
|
102
|
+
## Clarifications from Team
|
|
103
|
+
${ticket.clarifications}` : ""}
|
|
104
|
+
`;
|
|
105
|
+
fs.writeFileSync(path.join(memoryDir, "ticket-context.md"), content);
|
|
106
|
+
}
|
|
107
|
+
function readTicketContext(memoryDir) {
|
|
108
|
+
const p = path.join(memoryDir, "ticket-context.md");
|
|
109
|
+
return fs.existsSync(p) ? fs.readFileSync(p, "utf8") : "";
|
|
110
|
+
}
|
|
111
|
+
function appendClarifications(memoryDir, answers) {
|
|
112
|
+
const p = path.join(memoryDir, "ticket-context.md");
|
|
113
|
+
if (fs.existsSync(p)) {
|
|
114
|
+
fs.appendFileSync(p, `
|
|
115
|
+
## Clarifications from Team
|
|
116
|
+
${answers}
|
|
117
|
+
`);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
function writeReviewFeedback(memoryDir, feedback) {
|
|
121
|
+
const lines = [
|
|
122
|
+
`# Review Results`,
|
|
123
|
+
`PR: #${feedback.prNumber}`,
|
|
124
|
+
`Major issues: ${feedback.issues.filter((i) => i.severity === "major").length}`,
|
|
125
|
+
`Minor issues: ${feedback.issues.filter((i) => i.severity === "minor").length}`,
|
|
126
|
+
`Auto-resolved: ${feedback.autoResolved}`,
|
|
127
|
+
"",
|
|
128
|
+
"## Issues",
|
|
129
|
+
...feedback.issues.map(
|
|
130
|
+
(i) => `- [${i.severity.toUpperCase()}] ${i.message}`
|
|
131
|
+
)
|
|
132
|
+
];
|
|
133
|
+
fs.writeFileSync(
|
|
134
|
+
path.join(memoryDir, "review-feedback.md"),
|
|
135
|
+
lines.join("\n")
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
function getContextFilesForStage(projectDir, memoryDir, stage) {
|
|
139
|
+
const claudeMd = path.join(projectDir, ".claude", "CLAUDE.md");
|
|
140
|
+
const ticketCtx = path.join(memoryDir, "ticket-context.md");
|
|
141
|
+
const reviewFeedback = path.join(memoryDir, "review-feedback.md");
|
|
142
|
+
const files = [];
|
|
143
|
+
if (fs.existsSync(claudeMd)) files.push(claudeMd);
|
|
144
|
+
if (["code", "git", "review", "deploy", "test", "notify"].includes(stage)) {
|
|
145
|
+
if (fs.existsSync(ticketCtx)) files.push(ticketCtx);
|
|
146
|
+
}
|
|
147
|
+
if (stage === "test" && fs.existsSync(reviewFeedback)) {
|
|
148
|
+
files.push(reviewFeedback);
|
|
149
|
+
}
|
|
150
|
+
return files;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
// src/pipeline/stages/1-fetch.ts
|
|
154
|
+
var fetchStage = {
|
|
155
|
+
id: "fetch",
|
|
156
|
+
name: "Fetch Ticket",
|
|
157
|
+
model: "haiku",
|
|
158
|
+
async run(ctx) {
|
|
159
|
+
const { config, ticketKey, memoryDir } = ctx;
|
|
160
|
+
ctx.logger.info({ ticketKey }, "Fetching ticket from Jira");
|
|
161
|
+
const raw = await getTicket({
|
|
162
|
+
instance: config.jira.instance,
|
|
163
|
+
ticket_key: ticketKey
|
|
164
|
+
});
|
|
165
|
+
const ticket = JSON.parse(raw);
|
|
166
|
+
writeTicketContext(memoryDir, {
|
|
167
|
+
key: ticket.key,
|
|
168
|
+
summary: ticket.summary,
|
|
169
|
+
description: ticket.description ?? "",
|
|
170
|
+
acceptanceCriteria: ticket.acceptance_criteria ?? "",
|
|
171
|
+
priority: ticket.priority ?? "Medium"
|
|
172
|
+
});
|
|
173
|
+
ctx.logger.info({ ticketKey, summary: ticket.summary }, "Ticket fetched");
|
|
174
|
+
return {
|
|
175
|
+
summary: ticket.summary,
|
|
176
|
+
description: ticket.description,
|
|
177
|
+
acceptanceCriteria: ticket.acceptance_criteria,
|
|
178
|
+
status: ticket.status
|
|
179
|
+
};
|
|
180
|
+
}
|
|
181
|
+
};
|
|
182
|
+
|
|
183
|
+
// src/pipeline/runner.ts
|
|
184
|
+
var MODEL_IDS = {
|
|
185
|
+
haiku: "claude-haiku-4-5-20251001",
|
|
186
|
+
sonnet: "claude-sonnet-4-6",
|
|
187
|
+
opus: "claude-opus-4-6"
|
|
188
|
+
};
|
|
189
|
+
async function runAgent(opts) {
|
|
190
|
+
const args = [
|
|
191
|
+
"--model",
|
|
192
|
+
MODEL_IDS[opts.model],
|
|
193
|
+
"--print",
|
|
194
|
+
"--output-format",
|
|
195
|
+
"text"
|
|
196
|
+
];
|
|
197
|
+
for (const f of opts.contextFiles ?? []) {
|
|
198
|
+
args.push("--context", f);
|
|
199
|
+
}
|
|
200
|
+
args.push(opts.prompt);
|
|
201
|
+
const result = await spawnSafe("claude", args, {
|
|
202
|
+
cwd: opts.workdir,
|
|
203
|
+
env: buildMinimalEnv(opts.extraEnv),
|
|
204
|
+
timeoutMs: opts.timeoutMs ?? 18e5,
|
|
205
|
+
stallTimeoutMs: opts.stallTimeoutMs ?? 3e5
|
|
206
|
+
});
|
|
207
|
+
if (result.exitCode !== 0) {
|
|
208
|
+
throw new Error(
|
|
209
|
+
`Agent exited with code ${result.exitCode}:
|
|
210
|
+
${result.stderr}`
|
|
211
|
+
);
|
|
212
|
+
}
|
|
213
|
+
return result.stdout.trim();
|
|
214
|
+
}
|
|
215
|
+
async function runAgentsParallel(a, b) {
|
|
216
|
+
return Promise.all([runAgent(a), runAgent(b)]);
|
|
217
|
+
}
|
|
218
|
+
function detectComplexity(description) {
|
|
219
|
+
const complexKeywords = [
|
|
220
|
+
"auth",
|
|
221
|
+
"payment",
|
|
222
|
+
"stripe",
|
|
223
|
+
"oauth",
|
|
224
|
+
"jwt",
|
|
225
|
+
"migration",
|
|
226
|
+
"schema",
|
|
227
|
+
"database",
|
|
228
|
+
"refactor",
|
|
229
|
+
"cross-module",
|
|
230
|
+
"multi-service",
|
|
231
|
+
"security",
|
|
232
|
+
"encryption",
|
|
233
|
+
"permission"
|
|
234
|
+
];
|
|
235
|
+
const lower = description.toLowerCase();
|
|
236
|
+
const matches = complexKeywords.filter((k) => lower.includes(k));
|
|
237
|
+
return matches.length >= 2 ? "opus" : "sonnet";
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// src/pipeline/stages/2-analyze.ts
|
|
241
|
+
var CLARITY_PROMPT = (ticketCtx, requiredFields) => `
|
|
242
|
+
Analyze this Jira ticket for clarity. Score from 0.0 to 1.0.
|
|
243
|
+
|
|
244
|
+
Required fields: ${requiredFields.join(", ")}
|
|
245
|
+
|
|
246
|
+
Ticket:
|
|
247
|
+
${ticketCtx}
|
|
248
|
+
|
|
249
|
+
Reply with JSON only:
|
|
250
|
+
{
|
|
251
|
+
"score": 0.0-1.0,
|
|
252
|
+
"missing": ["list of missing or ambiguous items"],
|
|
253
|
+
"questions": ["specific questions to ask the team"]
|
|
254
|
+
}
|
|
255
|
+
`.trim();
|
|
256
|
+
var analyzeStage = {
|
|
257
|
+
id: "analyze",
|
|
258
|
+
name: "Analyze Clarity",
|
|
259
|
+
model: "sonnet",
|
|
260
|
+
async run(ctx) {
|
|
261
|
+
const { config, memoryDir, ticketKey } = ctx;
|
|
262
|
+
const ticketCtx = readTicketContext(memoryDir);
|
|
263
|
+
if (!ticketCtx)
|
|
264
|
+
throw new Error("ticket-context.md not found \u2014 run fetch stage first");
|
|
265
|
+
ctx.logger.info({ ticketKey }, "Analyzing ticket clarity");
|
|
266
|
+
const raw = await runAgent({
|
|
267
|
+
prompt: CLARITY_PROMPT(
|
|
268
|
+
ticketCtx,
|
|
269
|
+
config.jira.requiredFields ?? ["description", "acceptanceCriteria"]
|
|
270
|
+
),
|
|
271
|
+
workdir: config.workspace.rootDir,
|
|
272
|
+
model: "haiku",
|
|
273
|
+
timeoutMs: 6e4,
|
|
274
|
+
stallTimeoutMs: 3e4
|
|
275
|
+
});
|
|
276
|
+
const jsonMatch = raw.match(/\{[\s\S]*\}/);
|
|
277
|
+
if (!jsonMatch) {
|
|
278
|
+
ctx.logger.warn(
|
|
279
|
+
{ ticketKey },
|
|
280
|
+
"Could not parse clarity JSON \u2014 defaulting to low score"
|
|
281
|
+
);
|
|
282
|
+
return { score: 0, missing: [], questions: [], needsClarification: true };
|
|
283
|
+
}
|
|
284
|
+
const result = JSON.parse(jsonMatch[0]);
|
|
285
|
+
const threshold = config.jira.clarityScoreThreshold ?? 0.7;
|
|
286
|
+
const needsClarification = result.score < threshold;
|
|
287
|
+
ctx.logger.info(
|
|
288
|
+
{ ticketKey, score: result.score, threshold, needsClarification },
|
|
289
|
+
"Clarity analysis done"
|
|
290
|
+
);
|
|
291
|
+
return { ...result, needsClarification };
|
|
292
|
+
}
|
|
293
|
+
};
|
|
294
|
+
|
|
295
|
+
export {
|
|
296
|
+
createGitHubClient,
|
|
297
|
+
readTicketContext,
|
|
298
|
+
appendClarifications,
|
|
299
|
+
writeReviewFeedback,
|
|
300
|
+
getContextFilesForStage,
|
|
301
|
+
fetchStage,
|
|
302
|
+
runAgent,
|
|
303
|
+
runAgentsParallel,
|
|
304
|
+
detectComplexity,
|
|
305
|
+
analyzeStage
|
|
306
|
+
};
|
|
307
|
+
//# sourceMappingURL=chunk-VCEONSWJ.js.map
|