@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,82 @@
|
|
|
1
|
+
// src/utils/logger.ts
|
|
2
|
+
import pino from "pino";
|
|
3
|
+
var logger = pino({
|
|
4
|
+
transport: process.stdout.isTTY ? {
|
|
5
|
+
target: "pino-pretty",
|
|
6
|
+
options: { colorize: true, ignore: "pid,hostname" }
|
|
7
|
+
} : void 0,
|
|
8
|
+
level: process.env["LOG_LEVEL"] ?? "info"
|
|
9
|
+
});
|
|
10
|
+
function createLogger(name) {
|
|
11
|
+
return logger.child({ name });
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// src/integrations/telegram/bot.ts
|
|
15
|
+
import path from "path";
|
|
16
|
+
import os from "os";
|
|
17
|
+
import { Bot } from "grammy";
|
|
18
|
+
var _bot = null;
|
|
19
|
+
var _polling = false;
|
|
20
|
+
var STORE_DIR = path.join(os.homedir(), ".jira-acp");
|
|
21
|
+
function getBot(token) {
|
|
22
|
+
if (!_bot) {
|
|
23
|
+
_bot = new Bot(token);
|
|
24
|
+
}
|
|
25
|
+
return _bot;
|
|
26
|
+
}
|
|
27
|
+
async function initBot(token) {
|
|
28
|
+
const logger2 = createLogger("telegram:bot");
|
|
29
|
+
const bot = getBot(token);
|
|
30
|
+
if (_polling) return;
|
|
31
|
+
_polling = true;
|
|
32
|
+
const { registerAnswerHandler, registerApprovalHandlers, loadPendingStore } = await import("./human-loop-RBTA2TYK.js");
|
|
33
|
+
const { registerBotCommands } = await import("./commands-WYVRVE5Z.js");
|
|
34
|
+
loadPendingStore(STORE_DIR);
|
|
35
|
+
registerAnswerHandler(token, STORE_DIR);
|
|
36
|
+
registerApprovalHandlers(token);
|
|
37
|
+
registerBotCommands(token);
|
|
38
|
+
await bot.api.setMyCommands([
|
|
39
|
+
// Pipeline control
|
|
40
|
+
{ command: "run", description: "Start pipeline: /run PROJ-123 [project]" },
|
|
41
|
+
{ command: "abort", description: "Abort pipeline: /abort PROJ-123" },
|
|
42
|
+
{ command: "resume", description: "Resume pipeline: /resume PROJ-123" },
|
|
43
|
+
// Monitoring
|
|
44
|
+
{
|
|
45
|
+
command: "status",
|
|
46
|
+
description: "Show pipeline status: /status [PROJ-123]"
|
|
47
|
+
},
|
|
48
|
+
{ command: "logs", description: "Show recent events: /logs PROJ-123" },
|
|
49
|
+
// Jira
|
|
50
|
+
{
|
|
51
|
+
command: "tickets",
|
|
52
|
+
description: "List sprint tickets: /tickets [project]"
|
|
53
|
+
},
|
|
54
|
+
{ command: "ticket", description: "View ticket detail: /ticket PROJ-123" },
|
|
55
|
+
// Human-in-the-loop
|
|
56
|
+
{
|
|
57
|
+
command: "answer",
|
|
58
|
+
description: "Reply to clarification: /answer PROJ-123\\n1. answer"
|
|
59
|
+
},
|
|
60
|
+
{ command: "approve", description: "Approve PR review: /approve PROJ-123" },
|
|
61
|
+
{ command: "reject", description: "Reject PR review: /reject PROJ-123" },
|
|
62
|
+
// Utility
|
|
63
|
+
{ command: "projects", description: "List configured projects" },
|
|
64
|
+
{ command: "archive", description: "Archive topic: /archive PROJ-123" },
|
|
65
|
+
{
|
|
66
|
+
command: "verbosity",
|
|
67
|
+
description: "Set notification level: /verbosity low|medium|high"
|
|
68
|
+
}
|
|
69
|
+
]);
|
|
70
|
+
logger2.info("Telegram commands registered");
|
|
71
|
+
bot.start().catch((err) => {
|
|
72
|
+
logger2.error({ err }, "Telegram bot polling error");
|
|
73
|
+
});
|
|
74
|
+
logger2.info("Telegram bot polling started");
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export {
|
|
78
|
+
createLogger,
|
|
79
|
+
getBot,
|
|
80
|
+
initBot
|
|
81
|
+
};
|
|
82
|
+
//# sourceMappingURL=chunk-M4V3YOCY.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/utils/logger.ts","../src/integrations/telegram/bot.ts"],"sourcesContent":["import pino from \"pino\";\n\nexport const logger = pino({\n transport: process.stdout.isTTY\n ? {\n target: \"pino-pretty\",\n options: { colorize: true, ignore: \"pid,hostname\" },\n }\n : undefined,\n level: process.env[\"LOG_LEVEL\"] ?? \"info\",\n});\n\nexport function createLogger(name: string): pino.Logger {\n return logger.child({ name });\n}\n","import path from \"node:path\";\nimport os from \"node:os\";\nimport { Bot } from \"grammy\";\nimport { createLogger } from \"../../utils/logger.js\";\n\nlet _bot: Bot | null = null;\nlet _polling = false;\n\nconst STORE_DIR = path.join(os.homedir(), \".jira-acp\");\n\nexport function getBot(token: string): Bot {\n if (!_bot) {\n _bot = new Bot(token);\n }\n return _bot;\n}\n\nexport function resetBot(): void {\n _bot = null;\n _polling = false;\n}\n\n/**\n * One-time bot setup: registers all command handlers, pushes command menu to\n * Telegram, and starts long-polling in the background.\n * Safe to call multiple times — idempotent after first call.\n */\nexport async function initBot(token: string): Promise<void> {\n const logger = createLogger(\"telegram:bot\");\n const bot = getBot(token);\n\n if (_polling) return;\n _polling = true;\n\n const { registerAnswerHandler, registerApprovalHandlers, loadPendingStore } =\n await import(\"./human-loop.js\");\n const { registerBotCommands } = await import(\"./commands.js\");\n\n loadPendingStore(STORE_DIR);\n registerAnswerHandler(token, STORE_DIR);\n registerApprovalHandlers(token);\n registerBotCommands(token);\n\n await bot.api.setMyCommands([\n // Pipeline control\n { command: \"run\", description: \"Start pipeline: /run PROJ-123 [project]\" },\n { command: \"abort\", description: \"Abort pipeline: /abort PROJ-123\" },\n { command: \"resume\", description: \"Resume pipeline: /resume PROJ-123\" },\n // Monitoring\n {\n command: \"status\",\n description: \"Show pipeline status: /status [PROJ-123]\",\n },\n { command: \"logs\", description: \"Show recent events: /logs PROJ-123\" },\n // Jira\n {\n command: \"tickets\",\n description: \"List sprint tickets: /tickets [project]\",\n },\n { command: \"ticket\", description: \"View ticket detail: /ticket PROJ-123\" },\n // Human-in-the-loop\n {\n command: \"answer\",\n description: \"Reply to clarification: /answer PROJ-123\\\\n1. answer\",\n },\n { command: \"approve\", description: \"Approve PR review: /approve PROJ-123\" },\n { command: \"reject\", description: \"Reject PR review: /reject PROJ-123\" },\n // Utility\n { command: \"projects\", description: \"List configured projects\" },\n { command: \"archive\", description: \"Archive topic: /archive PROJ-123\" },\n {\n command: \"verbosity\",\n description: \"Set notification level: /verbosity low|medium|high\",\n },\n ]);\n\n logger.info(\"Telegram commands registered\");\n\n bot.start().catch((err: unknown) => {\n logger.error({ err }, \"Telegram bot polling error\");\n });\n\n logger.info(\"Telegram bot polling started\");\n}\n"],"mappings":";AAAA,OAAO,UAAU;AAEV,IAAM,SAAS,KAAK;AAAA,EACzB,WAAW,QAAQ,OAAO,QACtB;AAAA,IACE,QAAQ;AAAA,IACR,SAAS,EAAE,UAAU,MAAM,QAAQ,eAAe;AAAA,EACpD,IACA;AAAA,EACJ,OAAO,QAAQ,IAAI,WAAW,KAAK;AACrC,CAAC;AAEM,SAAS,aAAa,MAA2B;AACtD,SAAO,OAAO,MAAM,EAAE,KAAK,CAAC;AAC9B;;;ACdA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,WAAW;AAGpB,IAAI,OAAmB;AACvB,IAAI,WAAW;AAEf,IAAM,YAAY,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW;AAE9C,SAAS,OAAO,OAAoB;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,IAAI,IAAI,KAAK;AAAA,EACtB;AACA,SAAO;AACT;AAYA,eAAsB,QAAQ,OAA8B;AAC1D,QAAMA,UAAS,aAAa,cAAc;AAC1C,QAAM,MAAM,OAAO,KAAK;AAExB,MAAI,SAAU;AACd,aAAW;AAEX,QAAM,EAAE,uBAAuB,0BAA0B,iBAAiB,IACxE,MAAM,OAAO,0BAAiB;AAChC,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,wBAAe;AAE5D,mBAAiB,SAAS;AAC1B,wBAAsB,OAAO,SAAS;AACtC,2BAAyB,KAAK;AAC9B,sBAAoB,KAAK;AAEzB,QAAM,IAAI,IAAI,cAAc;AAAA;AAAA,IAE1B,EAAE,SAAS,OAAO,aAAa,0CAA0C;AAAA,IACzE,EAAE,SAAS,SAAS,aAAa,kCAAkC;AAAA,IACnE,EAAE,SAAS,UAAU,aAAa,oCAAoC;AAAA;AAAA,IAEtE;AAAA,MACE,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,IACA,EAAE,SAAS,QAAQ,aAAa,qCAAqC;AAAA;AAAA,IAErE;AAAA,MACE,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,IACA,EAAE,SAAS,UAAU,aAAa,uCAAuC;AAAA;AAAA,IAEzE;AAAA,MACE,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,IACA,EAAE,SAAS,WAAW,aAAa,uCAAuC;AAAA,IAC1E,EAAE,SAAS,UAAU,aAAa,qCAAqC;AAAA;AAAA,IAEvE,EAAE,SAAS,YAAY,aAAa,2BAA2B;AAAA,IAC/D,EAAE,SAAS,WAAW,aAAa,mCAAmC;AAAA,IACtE;AAAA,MACE,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,EACF,CAAC;AAED,EAAAA,QAAO,KAAK,8BAA8B;AAE1C,MAAI,MAAM,EAAE,MAAM,CAAC,QAAiB;AAClC,IAAAA,QAAO,MAAM,EAAE,IAAI,GAAG,4BAA4B;AAAA,EACpD,CAAC;AAED,EAAAA,QAAO,KAAK,8BAA8B;AAC5C;","names":["logger"]}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
// src/config/schema.ts
|
|
2
|
+
import { z } from "zod";
|
|
3
|
+
var StageId = z.enum([
|
|
4
|
+
"fetch",
|
|
5
|
+
"analyze",
|
|
6
|
+
"clarify",
|
|
7
|
+
"code",
|
|
8
|
+
"git",
|
|
9
|
+
"review",
|
|
10
|
+
"deploy",
|
|
11
|
+
"test",
|
|
12
|
+
"notify"
|
|
13
|
+
]);
|
|
14
|
+
var ProjectConfigSchema = z.object({
|
|
15
|
+
name: z.string(),
|
|
16
|
+
extends: z.string().optional(),
|
|
17
|
+
jira: z.object({
|
|
18
|
+
instance: z.string(),
|
|
19
|
+
url: z.string(),
|
|
20
|
+
email: z.string(),
|
|
21
|
+
token: z.string(),
|
|
22
|
+
projectKey: z.string(),
|
|
23
|
+
assignees: z.array(z.string()).min(1),
|
|
24
|
+
reassignTo: z.string().optional(),
|
|
25
|
+
acceptanceCriteriaField: z.string().default("customfield_10016"),
|
|
26
|
+
inProgressTransition: z.string().default("In Progress"),
|
|
27
|
+
inReviewTransition: z.string().default("In Review"),
|
|
28
|
+
doneTransition: z.string().default("Done"),
|
|
29
|
+
blockedTransition: z.string().optional(),
|
|
30
|
+
clarityScoreThreshold: z.number().min(0).max(1).default(0.7),
|
|
31
|
+
requiredFields: z.array(
|
|
32
|
+
z.enum([
|
|
33
|
+
"description",
|
|
34
|
+
"acceptanceCriteria",
|
|
35
|
+
"storyPoints",
|
|
36
|
+
"designLink"
|
|
37
|
+
])
|
|
38
|
+
).default(["description", "acceptanceCriteria"])
|
|
39
|
+
}),
|
|
40
|
+
github: z.object({
|
|
41
|
+
owner: z.string(),
|
|
42
|
+
repo: z.string(),
|
|
43
|
+
token: z.string(),
|
|
44
|
+
defaultBranch: z.string().default("main"),
|
|
45
|
+
branchPattern: z.string().default("{prefix}/{ticketKey}-{slug}"),
|
|
46
|
+
branchPrefix: z.string().default("feature"),
|
|
47
|
+
autoMergeStrategy: z.enum(["squash", "merge", "rebase"]).default("squash"),
|
|
48
|
+
reviewers: z.array(z.string()).default([]),
|
|
49
|
+
prDraftByDefault: z.boolean().default(false),
|
|
50
|
+
majorIssueThreshold: z.number().default(1),
|
|
51
|
+
ciWaitTimeoutMs: z.number().default(6e5)
|
|
52
|
+
}),
|
|
53
|
+
workspace: z.object({
|
|
54
|
+
rootDir: z.string(),
|
|
55
|
+
buildCommand: z.string().optional(),
|
|
56
|
+
testCommand: z.string().optional(),
|
|
57
|
+
allowedPaths: z.array(z.string()).default([])
|
|
58
|
+
}),
|
|
59
|
+
deploy: z.object({
|
|
60
|
+
enabled: z.boolean().default(false),
|
|
61
|
+
command: z.string().optional(),
|
|
62
|
+
rollbackCommand: z.string().optional(),
|
|
63
|
+
timeoutMs: z.number().default(12e5),
|
|
64
|
+
healthCheckUrl: z.string().optional(),
|
|
65
|
+
healthCheckTimeoutMs: z.number().default(3e4),
|
|
66
|
+
env: z.record(z.string()).default({})
|
|
67
|
+
}).default({}),
|
|
68
|
+
test: z.object({
|
|
69
|
+
enabled: z.boolean().default(false),
|
|
70
|
+
baseUrl: z.string().optional(),
|
|
71
|
+
retries: z.number().default(2),
|
|
72
|
+
waitBeforeTestMs: z.number().default(5e3),
|
|
73
|
+
timeoutMs: z.number().default(3e5),
|
|
74
|
+
specPattern: z.string().default("e2e/**/*.spec.ts")
|
|
75
|
+
}).default({}),
|
|
76
|
+
telegram: z.object({
|
|
77
|
+
botToken: z.string(),
|
|
78
|
+
chatId: z.union([z.string(), z.number()]),
|
|
79
|
+
topicId: z.number().optional(),
|
|
80
|
+
notifyOnStages: z.array(
|
|
81
|
+
z.enum([
|
|
82
|
+
"start",
|
|
83
|
+
"clarification",
|
|
84
|
+
"code-done",
|
|
85
|
+
"pr-created",
|
|
86
|
+
"review-pass",
|
|
87
|
+
"review-fail",
|
|
88
|
+
"deployed",
|
|
89
|
+
"test-pass",
|
|
90
|
+
"test-fail",
|
|
91
|
+
"done",
|
|
92
|
+
"error"
|
|
93
|
+
])
|
|
94
|
+
).default(["clarification", "review-fail", "test-fail", "error", "done"]),
|
|
95
|
+
humanInTheLoop: z.object({
|
|
96
|
+
clarificationTimeoutMs: z.number().default(36e5),
|
|
97
|
+
clarificationTimeoutAction: z.enum(["skip", "abort", "proceed-with-warning"]).default("skip"),
|
|
98
|
+
reviewApprovalTimeoutMs: z.number().default(864e5),
|
|
99
|
+
reviewApprovalTimeoutAction: z.enum(["abort", "merge-anyway"]).default("abort")
|
|
100
|
+
}).default({})
|
|
101
|
+
}),
|
|
102
|
+
pipeline: z.object({
|
|
103
|
+
maxConcurrentRuns: z.number().default(2),
|
|
104
|
+
stageTimeouts: z.object({
|
|
105
|
+
fetch: z.number().default(3e4),
|
|
106
|
+
analyze: z.number().default(6e4),
|
|
107
|
+
clarify: z.number().default(36e5),
|
|
108
|
+
code: z.number().default(18e5),
|
|
109
|
+
git: z.number().default(6e4),
|
|
110
|
+
review: z.number().default(6e5),
|
|
111
|
+
deploy: z.number().default(12e5),
|
|
112
|
+
test: z.number().default(3e5),
|
|
113
|
+
notify: z.number().default(3e4)
|
|
114
|
+
}).default({}),
|
|
115
|
+
agentStallTimeoutMs: z.number().default(3e5),
|
|
116
|
+
skipClarificationIfClear: z.boolean().default(true),
|
|
117
|
+
failOnTestFailure: z.boolean().default(false),
|
|
118
|
+
failOnDeployFailure: z.boolean().default(true),
|
|
119
|
+
maxCostUsdPerRun: z.number().optional(),
|
|
120
|
+
hooks: z.object({
|
|
121
|
+
beforePipeline: z.string().optional(),
|
|
122
|
+
beforeCode: z.string().optional(),
|
|
123
|
+
afterCode: z.string().optional(),
|
|
124
|
+
afterDeploy: z.string().optional(),
|
|
125
|
+
afterPipeline: z.string().optional()
|
|
126
|
+
}).default({})
|
|
127
|
+
}).default({})
|
|
128
|
+
});
|
|
129
|
+
var GlobalConfigSchema = ProjectConfigSchema.deepPartial().omit({
|
|
130
|
+
name: true
|
|
131
|
+
});
|
|
132
|
+
|
|
133
|
+
// src/config/loader.ts
|
|
134
|
+
import fs from "fs";
|
|
135
|
+
import os from "os";
|
|
136
|
+
import path from "path";
|
|
137
|
+
import { spawnSync } from "child_process";
|
|
138
|
+
var HOME_DIR = path.join(os.homedir(), ".jira-acp");
|
|
139
|
+
var PROJECTS_DIR = path.join(HOME_DIR, "projects");
|
|
140
|
+
var RUNS_DIR = path.join(HOME_DIR, "runs");
|
|
141
|
+
var GLOBAL_CONFIG_FILE = path.join(HOME_DIR, "config.json");
|
|
142
|
+
function configPath(projectName) {
|
|
143
|
+
return path.join(PROJECTS_DIR, `${projectName}.json`);
|
|
144
|
+
}
|
|
145
|
+
function deepMerge(base, override) {
|
|
146
|
+
const result = { ...base };
|
|
147
|
+
for (const [key, value] of Object.entries(override)) {
|
|
148
|
+
if (value === void 0) continue;
|
|
149
|
+
const baseVal = base[key];
|
|
150
|
+
if (typeof value === "object" && value !== null && !Array.isArray(value) && typeof baseVal === "object" && baseVal !== null && !Array.isArray(baseVal)) {
|
|
151
|
+
result[key] = deepMerge(
|
|
152
|
+
baseVal,
|
|
153
|
+
value
|
|
154
|
+
);
|
|
155
|
+
} else {
|
|
156
|
+
result[key] = value;
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
return result;
|
|
160
|
+
}
|
|
161
|
+
function loadGlobalConfig() {
|
|
162
|
+
if (!fs.existsSync(GLOBAL_CONFIG_FILE)) return {};
|
|
163
|
+
try {
|
|
164
|
+
const raw = JSON.parse(
|
|
165
|
+
fs.readFileSync(GLOBAL_CONFIG_FILE, "utf8")
|
|
166
|
+
);
|
|
167
|
+
const parsed = GlobalConfigSchema.safeParse(raw);
|
|
168
|
+
if (!parsed.success) return {};
|
|
169
|
+
return parsed.data;
|
|
170
|
+
} catch {
|
|
171
|
+
return {};
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
function loadConfig(projectName) {
|
|
175
|
+
const filePath = configPath(projectName);
|
|
176
|
+
if (!fs.existsSync(filePath)) {
|
|
177
|
+
throw new Error(
|
|
178
|
+
`No config found for project "${projectName}" at ${filePath}.
|
|
179
|
+
Run: jiraACP init --name ${projectName}`
|
|
180
|
+
);
|
|
181
|
+
}
|
|
182
|
+
const projectRaw = JSON.parse(fs.readFileSync(filePath, "utf8"));
|
|
183
|
+
const globalRaw = loadGlobalConfig();
|
|
184
|
+
const merged = deepMerge(globalRaw, projectRaw);
|
|
185
|
+
return ProjectConfigSchema.parse(merged);
|
|
186
|
+
}
|
|
187
|
+
function configExists(projectName) {
|
|
188
|
+
return fs.existsSync(configPath(projectName));
|
|
189
|
+
}
|
|
190
|
+
function saveConfig(projectName, config) {
|
|
191
|
+
fs.mkdirSync(PROJECTS_DIR, { recursive: true });
|
|
192
|
+
fs.writeFileSync(configPath(projectName), JSON.stringify(config, null, 2));
|
|
193
|
+
}
|
|
194
|
+
function listProjects() {
|
|
195
|
+
if (!fs.existsSync(PROJECTS_DIR)) return [];
|
|
196
|
+
return fs.readdirSync(PROJECTS_DIR).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export {
|
|
200
|
+
ProjectConfigSchema,
|
|
201
|
+
HOME_DIR,
|
|
202
|
+
loadConfig,
|
|
203
|
+
configExists,
|
|
204
|
+
saveConfig,
|
|
205
|
+
listProjects
|
|
206
|
+
};
|
|
207
|
+
//# sourceMappingURL=chunk-MMWQHH25.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/config/schema.ts","../src/config/loader.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","import fs from \"node:fs\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { spawnSync } from \"node:child_process\";\nimport {\n ProjectConfigSchema,\n GlobalConfigSchema,\n type ProjectConfig,\n} from \"./schema.js\";\n\n// ~/.jira-acp/\nexport const HOME_DIR = path.join(os.homedir(), \".jira-acp\");\nconst PROJECTS_DIR = path.join(HOME_DIR, \"projects\");\nconst RUNS_DIR = path.join(HOME_DIR, \"runs\");\n\n/** Detect project name from git remote origin URL, fallback to folder name */\nexport function detectProjectName(cwd: string = process.cwd()): string {\n const result = spawnSync(\"git\", [\"remote\", \"get-url\", \"origin\"], {\n cwd,\n encoding: \"utf8\",\n });\n if (result.status === 0 && result.stdout) {\n const remote = result.stdout.trim();\n // https://github.com/owner/repo.git or git@github.com:owner/repo.git\n const match = remote.match(/[:/]([^/]+\\/[^/]+?)(?:\\.git)?$/);\n if (match) return match[1].replace(\"/\", \"-\");\n }\n return path.basename(cwd);\n}\n\nconst GLOBAL_CONFIG_FILE = path.join(HOME_DIR, \"config.json\");\n\nfunction configPath(projectName: string): string {\n return path.join(PROJECTS_DIR, `${projectName}.json`);\n}\n\nfunction deepMerge(\n base: Record<string, unknown>,\n override: Record<string, unknown>,\n): Record<string, unknown> {\n const result: Record<string, unknown> = { ...base };\n for (const [key, value] of Object.entries(override)) {\n if (value === undefined) continue;\n const baseVal = base[key];\n if (\n typeof value === \"object\" &&\n value !== null &&\n !Array.isArray(value) &&\n typeof baseVal === \"object\" &&\n baseVal !== null &&\n !Array.isArray(baseVal)\n ) {\n result[key] = deepMerge(\n baseVal as Record<string, unknown>,\n value as Record<string, unknown>,\n );\n } else {\n result[key] = value;\n }\n }\n return result;\n}\n\nfunction loadGlobalConfig(): Record<string, unknown> {\n if (!fs.existsSync(GLOBAL_CONFIG_FILE)) return {};\n try {\n const raw = JSON.parse(\n fs.readFileSync(GLOBAL_CONFIG_FILE, \"utf8\"),\n ) as unknown;\n const parsed = GlobalConfigSchema.safeParse(raw);\n if (!parsed.success) return {};\n return parsed.data as Record<string, unknown>;\n } catch {\n return {};\n }\n}\n\nexport function loadConfig(projectName: string): ProjectConfig {\n const filePath = configPath(projectName);\n\n if (!fs.existsSync(filePath)) {\n throw new Error(\n `No config found for project \"${projectName}\" at ${filePath}.\\nRun: jiraACP init --name ${projectName}`,\n );\n }\n\n const projectRaw = JSON.parse(fs.readFileSync(filePath, \"utf8\")) as Record<\n string,\n unknown\n >;\n const globalRaw = loadGlobalConfig();\n const merged = deepMerge(globalRaw, projectRaw); // project overrides global\n return ProjectConfigSchema.parse(merged);\n}\n\nexport function configExists(projectName: string): boolean {\n return fs.existsSync(configPath(projectName));\n}\n\nexport function saveConfig(projectName: string, config: unknown): void {\n fs.mkdirSync(PROJECTS_DIR, { recursive: true });\n fs.writeFileSync(configPath(projectName), JSON.stringify(config, null, 2));\n}\n\nexport function listProjects(): string[] {\n if (!fs.existsSync(PROJECTS_DIR)) return [];\n return fs\n .readdirSync(PROJECTS_DIR)\n .filter((f) => f.endsWith(\".json\"))\n .map((f) => f.replace(/\\.json$/, \"\"));\n}\n\nexport function getRunsDir(projectName: string): string {\n return path.join(RUNS_DIR, projectName);\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,78 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createLogger
|
|
4
|
+
} from "./chunk-IT74N3UH.js";
|
|
5
|
+
|
|
6
|
+
// src/integrations/telegram/bot.ts
|
|
7
|
+
import path from "path";
|
|
8
|
+
import os from "os";
|
|
9
|
+
import { Bot } from "grammy";
|
|
10
|
+
var _bot = null;
|
|
11
|
+
var _polling = false;
|
|
12
|
+
var STORE_DIR = path.join(os.homedir(), ".jira-acp");
|
|
13
|
+
function getBot(token) {
|
|
14
|
+
if (!_bot) {
|
|
15
|
+
_bot = new Bot(token);
|
|
16
|
+
}
|
|
17
|
+
return _bot;
|
|
18
|
+
}
|
|
19
|
+
function resetBot() {
|
|
20
|
+
_bot = null;
|
|
21
|
+
_polling = false;
|
|
22
|
+
}
|
|
23
|
+
async function initBot(token) {
|
|
24
|
+
const logger = createLogger("telegram:bot");
|
|
25
|
+
const bot = getBot(token);
|
|
26
|
+
if (_polling) return;
|
|
27
|
+
_polling = true;
|
|
28
|
+
const { registerAnswerHandler, registerApprovalHandlers, loadPendingStore } = await import("./human-loop-XGWXUNCS.js");
|
|
29
|
+
const { registerBotCommands } = await import("./commands-RG45VBTZ.js");
|
|
30
|
+
loadPendingStore(STORE_DIR);
|
|
31
|
+
registerAnswerHandler(token, STORE_DIR);
|
|
32
|
+
registerApprovalHandlers(token);
|
|
33
|
+
registerBotCommands(token);
|
|
34
|
+
await bot.api.setMyCommands([
|
|
35
|
+
// Pipeline control
|
|
36
|
+
{ command: "run", description: "Start pipeline: /run PROJ-123 [project]" },
|
|
37
|
+
{ command: "abort", description: "Abort pipeline: /abort PROJ-123" },
|
|
38
|
+
{ command: "resume", description: "Resume pipeline: /resume PROJ-123" },
|
|
39
|
+
// Monitoring
|
|
40
|
+
{
|
|
41
|
+
command: "status",
|
|
42
|
+
description: "Show pipeline status: /status [PROJ-123]"
|
|
43
|
+
},
|
|
44
|
+
{ command: "logs", description: "Show recent events: /logs PROJ-123" },
|
|
45
|
+
// Jira
|
|
46
|
+
{
|
|
47
|
+
command: "tickets",
|
|
48
|
+
description: "List sprint tickets: /tickets [project]"
|
|
49
|
+
},
|
|
50
|
+
{ command: "ticket", description: "View ticket detail: /ticket PROJ-123" },
|
|
51
|
+
// Human-in-the-loop
|
|
52
|
+
{
|
|
53
|
+
command: "answer",
|
|
54
|
+
description: "Reply to clarification: /answer PROJ-123\\n1. answer"
|
|
55
|
+
},
|
|
56
|
+
{ command: "approve", description: "Approve PR review: /approve PROJ-123" },
|
|
57
|
+
{ command: "reject", description: "Reject PR review: /reject PROJ-123" },
|
|
58
|
+
// Utility
|
|
59
|
+
{ command: "projects", description: "List configured projects" },
|
|
60
|
+
{ command: "archive", description: "Archive topic: /archive PROJ-123" },
|
|
61
|
+
{
|
|
62
|
+
command: "verbosity",
|
|
63
|
+
description: "Set notification level: /verbosity low|medium|high"
|
|
64
|
+
}
|
|
65
|
+
]);
|
|
66
|
+
logger.info("Telegram commands registered");
|
|
67
|
+
bot.start().catch((err) => {
|
|
68
|
+
logger.error({ err }, "Telegram bot polling error");
|
|
69
|
+
});
|
|
70
|
+
logger.info("Telegram bot polling started");
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export {
|
|
74
|
+
getBot,
|
|
75
|
+
resetBot,
|
|
76
|
+
initBot
|
|
77
|
+
};
|
|
78
|
+
//# sourceMappingURL=chunk-OJ4CNF73.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/integrations/telegram/bot.ts"],"sourcesContent":["import path from \"node:path\";\nimport os from \"node:os\";\nimport { Bot } from \"grammy\";\nimport { createLogger } from \"../../utils/logger.js\";\n\nlet _bot: Bot | null = null;\nlet _polling = false;\n\nconst STORE_DIR = path.join(os.homedir(), \".jira-acp\");\n\nexport function getBot(token: string): Bot {\n if (!_bot) {\n _bot = new Bot(token);\n }\n return _bot;\n}\n\nexport function resetBot(): void {\n _bot = null;\n _polling = false;\n}\n\n/**\n * One-time bot setup: registers all command handlers, pushes command menu to\n * Telegram, and starts long-polling in the background.\n * Safe to call multiple times — idempotent after first call.\n */\nexport async function initBot(token: string): Promise<void> {\n const logger = createLogger(\"telegram:bot\");\n const bot = getBot(token);\n\n if (_polling) return;\n _polling = true;\n\n const { registerAnswerHandler, registerApprovalHandlers, loadPendingStore } =\n await import(\"./human-loop.js\");\n const { registerBotCommands } = await import(\"./commands.js\");\n\n loadPendingStore(STORE_DIR);\n registerAnswerHandler(token, STORE_DIR);\n registerApprovalHandlers(token);\n registerBotCommands(token);\n\n await bot.api.setMyCommands([\n // Pipeline control\n { command: \"run\", description: \"Start pipeline: /run PROJ-123 [project]\" },\n { command: \"abort\", description: \"Abort pipeline: /abort PROJ-123\" },\n { command: \"resume\", description: \"Resume pipeline: /resume PROJ-123\" },\n // Monitoring\n {\n command: \"status\",\n description: \"Show pipeline status: /status [PROJ-123]\",\n },\n { command: \"logs\", description: \"Show recent events: /logs PROJ-123\" },\n // Jira\n {\n command: \"tickets\",\n description: \"List sprint tickets: /tickets [project]\",\n },\n { command: \"ticket\", description: \"View ticket detail: /ticket PROJ-123\" },\n // Human-in-the-loop\n {\n command: \"answer\",\n description: \"Reply to clarification: /answer PROJ-123\\\\n1. answer\",\n },\n { command: \"approve\", description: \"Approve PR review: /approve PROJ-123\" },\n { command: \"reject\", description: \"Reject PR review: /reject PROJ-123\" },\n // Utility\n { command: \"projects\", description: \"List configured projects\" },\n { command: \"archive\", description: \"Archive topic: /archive PROJ-123\" },\n {\n command: \"verbosity\",\n description: \"Set notification level: /verbosity low|medium|high\",\n },\n ]);\n\n logger.info(\"Telegram commands registered\");\n\n bot.start().catch((err: unknown) => {\n logger.error({ err }, \"Telegram bot polling error\");\n });\n\n logger.info(\"Telegram bot polling started\");\n}\n"],"mappings":";;;;;;AAAA,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,WAAW;AAGpB,IAAI,OAAmB;AACvB,IAAI,WAAW;AAEf,IAAM,YAAY,KAAK,KAAK,GAAG,QAAQ,GAAG,WAAW;AAE9C,SAAS,OAAO,OAAoB;AACzC,MAAI,CAAC,MAAM;AACT,WAAO,IAAI,IAAI,KAAK;AAAA,EACtB;AACA,SAAO;AACT;AAEO,SAAS,WAAiB;AAC/B,SAAO;AACP,aAAW;AACb;AAOA,eAAsB,QAAQ,OAA8B;AAC1D,QAAM,SAAS,aAAa,cAAc;AAC1C,QAAM,MAAM,OAAO,KAAK;AAExB,MAAI,SAAU;AACd,aAAW;AAEX,QAAM,EAAE,uBAAuB,0BAA0B,iBAAiB,IACxE,MAAM,OAAO,0BAAiB;AAChC,QAAM,EAAE,oBAAoB,IAAI,MAAM,OAAO,wBAAe;AAE5D,mBAAiB,SAAS;AAC1B,wBAAsB,OAAO,SAAS;AACtC,2BAAyB,KAAK;AAC9B,sBAAoB,KAAK;AAEzB,QAAM,IAAI,IAAI,cAAc;AAAA;AAAA,IAE1B,EAAE,SAAS,OAAO,aAAa,0CAA0C;AAAA,IACzE,EAAE,SAAS,SAAS,aAAa,kCAAkC;AAAA,IACnE,EAAE,SAAS,UAAU,aAAa,oCAAoC;AAAA;AAAA,IAEtE;AAAA,MACE,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,IACA,EAAE,SAAS,QAAQ,aAAa,qCAAqC;AAAA;AAAA,IAErE;AAAA,MACE,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,IACA,EAAE,SAAS,UAAU,aAAa,uCAAuC;AAAA;AAAA,IAEzE;AAAA,MACE,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,IACA,EAAE,SAAS,WAAW,aAAa,uCAAuC;AAAA,IAC1E,EAAE,SAAS,UAAU,aAAa,qCAAqC;AAAA;AAAA,IAEvE,EAAE,SAAS,YAAY,aAAa,2BAA2B;AAAA,IAC/D,EAAE,SAAS,WAAW,aAAa,mCAAmC;AAAA,IACtE;AAAA,MACE,SAAS;AAAA,MACT,aAAa;AAAA,IACf;AAAA,EACF,CAAC;AAED,SAAO,KAAK,8BAA8B;AAE1C,MAAI,MAAM,EAAE,MAAM,CAAC,QAAiB;AAClC,WAAO,MAAM,EAAE,IAAI,GAAG,4BAA4B;AAAA,EACpD,CAAC;AAED,SAAO,KAAK,8BAA8B;AAC5C;","names":[]}
|
|
@@ -0,0 +1,137 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
createLogger
|
|
4
|
+
} from "./chunk-IT74N3UH.js";
|
|
5
|
+
|
|
6
|
+
// src/commands/schedule.ts
|
|
7
|
+
import fs from "fs";
|
|
8
|
+
import path from "path";
|
|
9
|
+
import os from "os";
|
|
10
|
+
import { randomUUID } from "crypto";
|
|
11
|
+
import pc from "picocolors";
|
|
12
|
+
var SCHEDULES_PATH = path.join(os.homedir(), ".jira-acp", "schedules.json");
|
|
13
|
+
function loadSchedules() {
|
|
14
|
+
if (!fs.existsSync(SCHEDULES_PATH)) return [];
|
|
15
|
+
try {
|
|
16
|
+
return JSON.parse(
|
|
17
|
+
fs.readFileSync(SCHEDULES_PATH, "utf8")
|
|
18
|
+
);
|
|
19
|
+
} catch {
|
|
20
|
+
return [];
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function saveSchedules(entries) {
|
|
24
|
+
fs.mkdirSync(path.dirname(SCHEDULES_PATH), { recursive: true });
|
|
25
|
+
fs.writeFileSync(SCHEDULES_PATH, JSON.stringify(entries, null, 2));
|
|
26
|
+
}
|
|
27
|
+
var CRON_REGEX = /^(\*|([0-5]?\d))(\/\d+)?(\s+(\*|([01]?\d|2[0-3]))(\/\d+)?){1}(\s+(\*|([12]?\d|3[01]))(\/\d+)?){1}(\s+(\*|(1[0-2]|[1-9]))(\/\d+)?){1}(\s+(\*|[0-7])(\/\d+)?){1}$/;
|
|
28
|
+
function isValidCron(expr) {
|
|
29
|
+
const parts = expr.trim().split(/\s+/);
|
|
30
|
+
if (parts.length !== 5) return false;
|
|
31
|
+
return CRON_REGEX.test(expr.trim());
|
|
32
|
+
}
|
|
33
|
+
function scheduleAdd(projectName, cron) {
|
|
34
|
+
const logger = createLogger("schedule:add");
|
|
35
|
+
if (!isValidCron(cron)) {
|
|
36
|
+
process.stderr.write(
|
|
37
|
+
`Invalid cron expression: "${cron}"
|
|
38
|
+
Expected 5-field cron: "min hour day month weekday"
|
|
39
|
+
Example: "0 9 * * 1-5"
|
|
40
|
+
`
|
|
41
|
+
);
|
|
42
|
+
process.exitCode = 2;
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
const entries = loadSchedules();
|
|
46
|
+
const entry = {
|
|
47
|
+
id: randomUUID(),
|
|
48
|
+
projectName,
|
|
49
|
+
cron,
|
|
50
|
+
createdAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
51
|
+
enabled: true
|
|
52
|
+
};
|
|
53
|
+
entries.push(entry);
|
|
54
|
+
saveSchedules(entries);
|
|
55
|
+
logger.info({ id: entry.id, projectName, cron }, "Schedule added");
|
|
56
|
+
process.stdout.write(
|
|
57
|
+
`\u2713 Schedule added
|
|
58
|
+
ID: ${entry.id}
|
|
59
|
+
Project: ${projectName}
|
|
60
|
+
Cron: ${cron}
|
|
61
|
+
`
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
function scheduleList() {
|
|
65
|
+
const entries = loadSchedules();
|
|
66
|
+
if (entries.length === 0) {
|
|
67
|
+
process.stdout.write(
|
|
68
|
+
pc.gray(" No schedules configured. Run: jiraACP schedule add\n")
|
|
69
|
+
);
|
|
70
|
+
return;
|
|
71
|
+
}
|
|
72
|
+
process.stdout.write(`
|
|
73
|
+
${pc.bold(" Scheduled pipeline runs")}
|
|
74
|
+
|
|
75
|
+
`);
|
|
76
|
+
process.stdout.write(
|
|
77
|
+
` ${"ID".padEnd(38)} ${"Project".padEnd(20)} ${"Cron".padEnd(18)} Status
|
|
78
|
+
`
|
|
79
|
+
);
|
|
80
|
+
process.stdout.write(" " + "\u2500".repeat(86) + "\n");
|
|
81
|
+
for (const e of entries) {
|
|
82
|
+
const status = e.enabled ? pc.green("enabled") : pc.gray("disabled");
|
|
83
|
+
process.stdout.write(
|
|
84
|
+
` ${e.id.padEnd(38)} ${e.projectName.padEnd(20)} ${e.cron.padEnd(18)} ${status}
|
|
85
|
+
`
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
process.stdout.write("\n");
|
|
89
|
+
}
|
|
90
|
+
function scheduleRemove(id) {
|
|
91
|
+
const logger = createLogger("schedule:remove");
|
|
92
|
+
const entries = loadSchedules();
|
|
93
|
+
const idx = entries.findIndex((e) => e.id === id || e.id.startsWith(id));
|
|
94
|
+
if (idx === -1) {
|
|
95
|
+
process.stderr.write(`No schedule found with ID: ${id}
|
|
96
|
+
`);
|
|
97
|
+
process.exitCode = 1;
|
|
98
|
+
return;
|
|
99
|
+
}
|
|
100
|
+
const [removed] = entries.splice(idx, 1);
|
|
101
|
+
saveSchedules(entries);
|
|
102
|
+
logger.info({ id: removed.id }, "Schedule removed");
|
|
103
|
+
process.stdout.write(
|
|
104
|
+
`\u2713 Schedule removed: ${removed.id} (${removed.projectName} \u2014 ${removed.cron})
|
|
105
|
+
`
|
|
106
|
+
);
|
|
107
|
+
}
|
|
108
|
+
function matchField(field, value) {
|
|
109
|
+
if (field === "*") return true;
|
|
110
|
+
if (field.includes("/")) {
|
|
111
|
+
const [base, stepStr] = field.split("/");
|
|
112
|
+
const step = Number(stepStr);
|
|
113
|
+
if (!step) return false;
|
|
114
|
+
const start = base === "*" ? 0 : Number(base);
|
|
115
|
+
return value >= start && (value - start) % step === 0;
|
|
116
|
+
}
|
|
117
|
+
if (field.includes("-")) {
|
|
118
|
+
const [min, max] = field.split("-").map(Number);
|
|
119
|
+
return value >= min && value <= max;
|
|
120
|
+
}
|
|
121
|
+
return Number(field) === value;
|
|
122
|
+
}
|
|
123
|
+
function cronMatchesNow(cron, now = /* @__PURE__ */ new Date()) {
|
|
124
|
+
const parts = cron.trim().split(/\s+/);
|
|
125
|
+
if (parts.length !== 5) return false;
|
|
126
|
+
const [minF, hourF, domF, monF, dowF] = parts;
|
|
127
|
+
return matchField(minF, now.getMinutes()) && matchField(hourF, now.getHours()) && matchField(domF, now.getDate()) && matchField(monF, now.getMonth() + 1) && matchField(dowF, now.getDay());
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export {
|
|
131
|
+
loadSchedules,
|
|
132
|
+
scheduleAdd,
|
|
133
|
+
scheduleList,
|
|
134
|
+
scheduleRemove,
|
|
135
|
+
cronMatchesNow
|
|
136
|
+
};
|
|
137
|
+
//# sourceMappingURL=chunk-PFJAC3RO.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/commands/schedule.ts"],"sourcesContent":["import fs from \"node:fs\";\nimport path from \"node:path\";\nimport os from \"node:os\";\nimport { randomUUID } from \"node:crypto\";\nimport pc from \"picocolors\";\nimport { createLogger } from \"../utils/logger.js\";\n\nconst SCHEDULES_PATH = path.join(os.homedir(), \".jira-acp\", \"schedules.json\");\n\nexport interface ScheduleEntry {\n id: string;\n projectName: string;\n cron: string;\n createdAt: string;\n enabled: boolean;\n}\n\n// ── Storage ───────────────────────────────────────────────────────────────\n\nexport function loadSchedules(): ScheduleEntry[] {\n if (!fs.existsSync(SCHEDULES_PATH)) return [];\n try {\n return JSON.parse(\n fs.readFileSync(SCHEDULES_PATH, \"utf8\"),\n ) as ScheduleEntry[];\n } catch {\n return [];\n }\n}\n\nfunction saveSchedules(entries: ScheduleEntry[]): void {\n fs.mkdirSync(path.dirname(SCHEDULES_PATH), { recursive: true });\n fs.writeFileSync(SCHEDULES_PATH, JSON.stringify(entries, null, 2));\n}\n\n// ── Cron validation ───────────────────────────────────────────────────────\n\nconst CRON_REGEX =\n /^(\\*|([0-5]?\\d))(\\/\\d+)?(\\s+(\\*|([01]?\\d|2[0-3]))(\\/\\d+)?){1}(\\s+(\\*|([12]?\\d|3[01]))(\\/\\d+)?){1}(\\s+(\\*|(1[0-2]|[1-9]))(\\/\\d+)?){1}(\\s+(\\*|[0-7])(\\/\\d+)?){1}$/;\n\nfunction isValidCron(expr: string): boolean {\n const parts = expr.trim().split(/\\s+/);\n if (parts.length !== 5) return false;\n return CRON_REGEX.test(expr.trim());\n}\n\n// ── Commands ──────────────────────────────────────────────────────────────\n\nexport function scheduleAdd(projectName: string, cron: string): void {\n const logger = createLogger(\"schedule:add\");\n\n if (!isValidCron(cron)) {\n process.stderr.write(\n `Invalid cron expression: \"${cron}\"\\nExpected 5-field cron: \"min hour day month weekday\"\\nExample: \"0 9 * * 1-5\"\\n`,\n );\n process.exitCode = 2;\n return;\n }\n\n const entries = loadSchedules();\n const entry: ScheduleEntry = {\n id: randomUUID(),\n projectName,\n cron,\n createdAt: new Date().toISOString(),\n enabled: true,\n };\n\n entries.push(entry);\n saveSchedules(entries);\n logger.info({ id: entry.id, projectName, cron }, \"Schedule added\");\n process.stdout.write(\n `✓ Schedule added\\n ID: ${entry.id}\\n Project: ${projectName}\\n Cron: ${cron}\\n`,\n );\n}\n\nexport function scheduleList(): void {\n const entries = loadSchedules();\n\n if (entries.length === 0) {\n process.stdout.write(\n pc.gray(\" No schedules configured. Run: jiraACP schedule add\\n\"),\n );\n return;\n }\n\n process.stdout.write(`\\n${pc.bold(\" Scheduled pipeline runs\")}\\n\\n`);\n process.stdout.write(\n ` ${\"ID\".padEnd(38)} ${\"Project\".padEnd(20)} ${\"Cron\".padEnd(18)} Status\\n`,\n );\n process.stdout.write(\" \" + \"─\".repeat(86) + \"\\n\");\n\n for (const e of entries) {\n const status = e.enabled ? pc.green(\"enabled\") : pc.gray(\"disabled\");\n process.stdout.write(\n ` ${e.id.padEnd(38)} ${e.projectName.padEnd(20)} ${e.cron.padEnd(18)} ${status}\\n`,\n );\n }\n process.stdout.write(\"\\n\");\n}\n\nexport function scheduleRemove(id: string): void {\n const logger = createLogger(\"schedule:remove\");\n const entries = loadSchedules();\n const idx = entries.findIndex((e) => e.id === id || e.id.startsWith(id));\n\n if (idx === -1) {\n process.stderr.write(`No schedule found with ID: ${id}\\n`);\n process.exitCode = 1;\n return;\n }\n\n const [removed] = entries.splice(idx, 1);\n saveSchedules(entries);\n logger.info({ id: removed.id }, \"Schedule removed\");\n process.stdout.write(\n `✓ Schedule removed: ${removed.id} (${removed.projectName} — ${removed.cron})\\n`,\n );\n}\n\n// ── Cron runtime (used by serve) ──────────────────────────────────────────\n\n/** Parse a cron field value and return whether the given number matches. */\nfunction matchField(field: string, value: number): boolean {\n if (field === \"*\") return true;\n if (field.includes(\"/\")) {\n const [base, stepStr] = field.split(\"/\");\n const step = Number(stepStr);\n if (!step) return false; // step=0 is invalid\n const start = base === \"*\" ? 0 : Number(base);\n return value >= start && (value - start) % step === 0;\n }\n if (field.includes(\"-\")) {\n const [min, max] = field.split(\"-\").map(Number);\n return value >= min && value <= max;\n }\n return Number(field) === value;\n}\n\n/** Returns true if a cron expression fires at the given Date (minute precision). */\nexport function cronMatchesNow(cron: string, now: Date = new Date()): boolean {\n const parts = cron.trim().split(/\\s+/);\n if (parts.length !== 5) return false;\n const [minF, hourF, domF, monF, dowF] = parts;\n return (\n matchField(minF, now.getMinutes()) &&\n matchField(hourF, now.getHours()) &&\n matchField(domF, now.getDate()) &&\n matchField(monF, now.getMonth() + 1) &&\n matchField(dowF, now.getDay())\n );\n}\n"],"mappings":";;;;;;AAAA,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,QAAQ;AACf,SAAS,kBAAkB;AAC3B,OAAO,QAAQ;AAGf,IAAM,iBAAiB,KAAK,KAAK,GAAG,QAAQ,GAAG,aAAa,gBAAgB;AAYrE,SAAS,gBAAiC;AAC/C,MAAI,CAAC,GAAG,WAAW,cAAc,EAAG,QAAO,CAAC;AAC5C,MAAI;AACF,WAAO,KAAK;AAAA,MACV,GAAG,aAAa,gBAAgB,MAAM;AAAA,IACxC;AAAA,EACF,QAAQ;AACN,WAAO,CAAC;AAAA,EACV;AACF;AAEA,SAAS,cAAc,SAAgC;AACrD,KAAG,UAAU,KAAK,QAAQ,cAAc,GAAG,EAAE,WAAW,KAAK,CAAC;AAC9D,KAAG,cAAc,gBAAgB,KAAK,UAAU,SAAS,MAAM,CAAC,CAAC;AACnE;AAIA,IAAM,aACJ;AAEF,SAAS,YAAY,MAAuB;AAC1C,QAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK;AACrC,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,SAAO,WAAW,KAAK,KAAK,KAAK,CAAC;AACpC;AAIO,SAAS,YAAY,aAAqB,MAAoB;AACnE,QAAM,SAAS,aAAa,cAAc;AAE1C,MAAI,CAAC,YAAY,IAAI,GAAG;AACtB,YAAQ,OAAO;AAAA,MACb,6BAA6B,IAAI;AAAA;AAAA;AAAA;AAAA,IACnC;AACA,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,UAAU,cAAc;AAC9B,QAAM,QAAuB;AAAA,IAC3B,IAAI,WAAW;AAAA,IACf;AAAA,IACA;AAAA,IACA,YAAW,oBAAI,KAAK,GAAE,YAAY;AAAA,IAClC,SAAS;AAAA,EACX;AAEA,UAAQ,KAAK,KAAK;AAClB,gBAAc,OAAO;AACrB,SAAO,KAAK,EAAE,IAAI,MAAM,IAAI,aAAa,KAAK,GAAG,gBAAgB;AACjE,UAAQ,OAAO;AAAA,IACb;AAAA,aAAgC,MAAM,EAAE;AAAA,aAAgB,WAAW;AAAA,aAAgB,IAAI;AAAA;AAAA,EACzF;AACF;AAEO,SAAS,eAAqB;AACnC,QAAM,UAAU,cAAc;AAE9B,MAAI,QAAQ,WAAW,GAAG;AACxB,YAAQ,OAAO;AAAA,MACb,GAAG,KAAK,wDAAwD;AAAA,IAClE;AACA;AAAA,EACF;AAEA,UAAQ,OAAO,MAAM;AAAA,EAAK,GAAG,KAAK,2BAA2B,CAAC;AAAA;AAAA,CAAM;AACpE,UAAQ,OAAO;AAAA,IACb,KAAK,KAAK,OAAO,EAAE,CAAC,IAAI,UAAU,OAAO,EAAE,CAAC,IAAI,OAAO,OAAO,EAAE,CAAC;AAAA;AAAA,EACnE;AACA,UAAQ,OAAO,MAAM,OAAO,SAAI,OAAO,EAAE,IAAI,IAAI;AAEjD,aAAW,KAAK,SAAS;AACvB,UAAM,SAAS,EAAE,UAAU,GAAG,MAAM,SAAS,IAAI,GAAG,KAAK,UAAU;AACnE,YAAQ,OAAO;AAAA,MACb,KAAK,EAAE,GAAG,OAAO,EAAE,CAAC,IAAI,EAAE,YAAY,OAAO,EAAE,CAAC,IAAI,EAAE,KAAK,OAAO,EAAE,CAAC,IAAI,MAAM;AAAA;AAAA,IACjF;AAAA,EACF;AACA,UAAQ,OAAO,MAAM,IAAI;AAC3B;AAEO,SAAS,eAAe,IAAkB;AAC/C,QAAM,SAAS,aAAa,iBAAiB;AAC7C,QAAM,UAAU,cAAc;AAC9B,QAAM,MAAM,QAAQ,UAAU,CAAC,MAAM,EAAE,OAAO,MAAM,EAAE,GAAG,WAAW,EAAE,CAAC;AAEvE,MAAI,QAAQ,IAAI;AACd,YAAQ,OAAO,MAAM,8BAA8B,EAAE;AAAA,CAAI;AACzD,YAAQ,WAAW;AACnB;AAAA,EACF;AAEA,QAAM,CAAC,OAAO,IAAI,QAAQ,OAAO,KAAK,CAAC;AACvC,gBAAc,OAAO;AACrB,SAAO,KAAK,EAAE,IAAI,QAAQ,GAAG,GAAG,kBAAkB;AAClD,UAAQ,OAAO;AAAA,IACb,4BAAuB,QAAQ,EAAE,KAAK,QAAQ,WAAW,WAAM,QAAQ,IAAI;AAAA;AAAA,EAC7E;AACF;AAKA,SAAS,WAAW,OAAe,OAAwB;AACzD,MAAI,UAAU,IAAK,QAAO;AAC1B,MAAI,MAAM,SAAS,GAAG,GAAG;AACvB,UAAM,CAAC,MAAM,OAAO,IAAI,MAAM,MAAM,GAAG;AACvC,UAAM,OAAO,OAAO,OAAO;AAC3B,QAAI,CAAC,KAAM,QAAO;AAClB,UAAM,QAAQ,SAAS,MAAM,IAAI,OAAO,IAAI;AAC5C,WAAO,SAAS,UAAU,QAAQ,SAAS,SAAS;AAAA,EACtD;AACA,MAAI,MAAM,SAAS,GAAG,GAAG;AACvB,UAAM,CAAC,KAAK,GAAG,IAAI,MAAM,MAAM,GAAG,EAAE,IAAI,MAAM;AAC9C,WAAO,SAAS,OAAO,SAAS;AAAA,EAClC;AACA,SAAO,OAAO,KAAK,MAAM;AAC3B;AAGO,SAAS,eAAe,MAAc,MAAY,oBAAI,KAAK,GAAY;AAC5E,QAAM,QAAQ,KAAK,KAAK,EAAE,MAAM,KAAK;AACrC,MAAI,MAAM,WAAW,EAAG,QAAO;AAC/B,QAAM,CAAC,MAAM,OAAO,MAAM,MAAM,IAAI,IAAI;AACxC,SACE,WAAW,MAAM,IAAI,WAAW,CAAC,KACjC,WAAW,OAAO,IAAI,SAAS,CAAC,KAChC,WAAW,MAAM,IAAI,QAAQ,CAAC,KAC9B,WAAW,MAAM,IAAI,SAAS,IAAI,CAAC,KACnC,WAAW,MAAM,IAAI,OAAO,CAAC;AAEjC;","names":[]}
|