kantban-cli 0.1.38 → 0.1.40
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/dist/{chunk-2CY5OPZN.js → chunk-FOVFBT7C.js} +8 -4
- package/dist/chunk-FOVFBT7C.js.map +1 -0
- package/dist/{cron-3R2UWFO7.js → cron-CVAFGFXN.js} +2 -2
- package/dist/index.js +3 -3
- package/dist/lib/gate-proxy-server.js +34 -2
- package/dist/lib/gate-proxy-server.js.map +1 -1
- package/dist/{pipeline-IAKINX5A.js → pipeline-ILE7LMGI.js} +69 -17
- package/dist/pipeline-ILE7LMGI.js.map +1 -0
- package/package.json +1 -1
- package/dist/chunk-2CY5OPZN.js.map +0 -1
- package/dist/pipeline-IAKINX5A.js.map +0 -1
- /package/dist/{cron-3R2UWFO7.js.map → cron-CVAFGFXN.js.map} +0 -0
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
generateMcpConfig,
|
|
13
13
|
parseJsonFromLlmOutput,
|
|
14
14
|
parseStuckDetectionResponse
|
|
15
|
-
} from "./chunk-
|
|
15
|
+
} from "./chunk-FOVFBT7C.js";
|
|
16
16
|
import {
|
|
17
17
|
LoopCheckpointSchema,
|
|
18
18
|
VerdictSchema,
|
|
@@ -50,9 +50,17 @@ function resolveToolRestrictions(builtinTools, allowedTools, disallowedTools) {
|
|
|
50
50
|
// src/lib/worktree.ts
|
|
51
51
|
import { execFile as defaultExecFile, execFileSync } from "child_process";
|
|
52
52
|
function generateWorktreeName(ticketNumber, columnName) {
|
|
53
|
-
const slug = columnName
|
|
53
|
+
const slug = columnSlug(columnName);
|
|
54
54
|
return `kantban-${ticketNumber}-${slug}`;
|
|
55
55
|
}
|
|
56
|
+
function columnSlug(columnName) {
|
|
57
|
+
return columnName.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
|
|
58
|
+
}
|
|
59
|
+
function renderWorktreePath(ticketNumber, columnName, pathPattern) {
|
|
60
|
+
const worktreeName = generateWorktreeName(ticketNumber, columnName);
|
|
61
|
+
if (!pathPattern) return worktreeName;
|
|
62
|
+
return pathPattern.replace(/\{ticket_number\}/g, String(ticketNumber)).replace(/\{column_slug\}/g, columnSlug(columnName)).replace(/\{worktree_name\}/g, worktreeName);
|
|
63
|
+
}
|
|
56
64
|
function isPlausibleRemoteUrl(url) {
|
|
57
65
|
return url.includes("/") || url.includes("@") || url.includes("://");
|
|
58
66
|
}
|
|
@@ -91,16 +99,18 @@ function ensureWorktreeRemote(worktreePath) {
|
|
|
91
99
|
}
|
|
92
100
|
}
|
|
93
101
|
async function cleanupWorktree(worktreeName, exec = defaultExecFile) {
|
|
102
|
+
let target = worktreeName;
|
|
94
103
|
try {
|
|
95
104
|
const path = await findWorktreeForBranch(exec, worktreeName);
|
|
96
105
|
if (!path) return true;
|
|
106
|
+
target = path;
|
|
97
107
|
} catch {
|
|
98
108
|
return true;
|
|
99
109
|
}
|
|
100
110
|
return new Promise((resolve) => {
|
|
101
|
-
exec("git", ["worktree", "remove", "--force",
|
|
111
|
+
exec("git", ["worktree", "remove", "--force", target], (err) => {
|
|
102
112
|
if (err) {
|
|
103
|
-
console.error(`[worktree] cleanup failed for ${worktreeName}: ${err.message}`);
|
|
113
|
+
console.error(`[worktree] cleanup failed for ${worktreeName} (${target}): ${err.message}`);
|
|
104
114
|
resolve(false);
|
|
105
115
|
} else {
|
|
106
116
|
resolve(true);
|
|
@@ -486,6 +496,8 @@ var PipelineOrchestrator = class {
|
|
|
486
496
|
spawning = /* @__PURE__ */ new Set();
|
|
487
497
|
/** Ticket IDs currently in onLoopComplete (prevents double-spawn during async advisor recovery) */
|
|
488
498
|
completing = /* @__PURE__ */ new Set();
|
|
499
|
+
/** Ticket IDs aborted due to ticket:moved — suppresses onLoopComplete comment/advisor */
|
|
500
|
+
abortedForMove = /* @__PURE__ */ new Set();
|
|
489
501
|
/** Per-column reservation count for in-flight spawns (prevents concurrency overshoot) */
|
|
490
502
|
columnReservations = /* @__PURE__ */ new Map();
|
|
491
503
|
/** Cached board scope for constraint evaluation — refreshed each scan cycle */
|
|
@@ -592,6 +604,7 @@ var PipelineOrchestrator = class {
|
|
|
592
604
|
modelPreference: cfg?.model_preference,
|
|
593
605
|
maxBudgetUsd: cfg?.max_budget_usd,
|
|
594
606
|
worktreeEnabled: cfg?.worktree?.enabled,
|
|
607
|
+
worktreePathPattern: cfg?.worktree?.path_pattern,
|
|
595
608
|
worktreeOnMove: cfg?.worktree?.on_move,
|
|
596
609
|
worktreeOnDone: cfg?.worktree?.on_done,
|
|
597
610
|
worktreeIntegrationBranch: cfg?.worktree?.integration_branch,
|
|
@@ -924,6 +937,26 @@ var PipelineOrchestrator = class {
|
|
|
924
937
|
switch (event.type) {
|
|
925
938
|
case "ticket:moved":
|
|
926
939
|
case "ticket:created": {
|
|
940
|
+
if (event.type === "ticket:moved" && event.fromColumnId) {
|
|
941
|
+
const oldLoop = this.activeLoops.get(event.ticketId);
|
|
942
|
+
if (oldLoop) {
|
|
943
|
+
oldLoop.abort.abort();
|
|
944
|
+
this.activeLoops.delete(event.ticketId);
|
|
945
|
+
this.abortedForMove.add(event.ticketId);
|
|
946
|
+
}
|
|
947
|
+
this.knownTickets.delete(event.ticketId);
|
|
948
|
+
this.spawning.delete(event.ticketId);
|
|
949
|
+
this.deferredTickets.delete(event.ticketId);
|
|
950
|
+
const oldQueue = this.loopQueues.get(event.fromColumnId);
|
|
951
|
+
if (oldQueue) {
|
|
952
|
+
const idx = oldQueue.indexOf(event.ticketId);
|
|
953
|
+
if (idx !== -1) oldQueue.splice(idx, 1);
|
|
954
|
+
}
|
|
955
|
+
void this.drainQueue(event.fromColumnId).catch((err) => {
|
|
956
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
957
|
+
console.error(` [error] drainQueue failed for old column ${event.fromColumnId}: ${msg}`);
|
|
958
|
+
});
|
|
959
|
+
}
|
|
927
960
|
if (event.columnId && this.pipelineColumns.has(event.columnId)) {
|
|
928
961
|
if (this.isColumnBlocked(event.columnId)) {
|
|
929
962
|
console.error(` [event] ${event.type} ${event.ticketId} \u2192 column ${event.columnId}: BLOCKED by firing constraints \u2014 deferred`);
|
|
@@ -1113,7 +1146,8 @@ var PipelineOrchestrator = class {
|
|
|
1113
1146
|
lastError: err instanceof Error ? err.message : String(err)
|
|
1114
1147
|
})
|
|
1115
1148
|
);
|
|
1116
|
-
|
|
1149
|
+
const abort = new AbortController();
|
|
1150
|
+
this.activeLoops.set(ticketId, { columnId, promise, abort });
|
|
1117
1151
|
this.lastFiredAt.set(columnId, /* @__PURE__ */ new Date());
|
|
1118
1152
|
void promise.then(
|
|
1119
1153
|
(result) => this.onLoopComplete(ticketId, columnId, result).catch((err) => {
|
|
@@ -1138,7 +1172,8 @@ var PipelineOrchestrator = class {
|
|
|
1138
1172
|
});
|
|
1139
1173
|
placeholder.catch(() => {
|
|
1140
1174
|
});
|
|
1141
|
-
|
|
1175
|
+
const abort = new AbortController();
|
|
1176
|
+
this.activeLoops.set(ticketId, { columnId, promise: placeholder, abort });
|
|
1142
1177
|
this.lastFiredAt.set(columnId, /* @__PURE__ */ new Date());
|
|
1143
1178
|
void readCheckpoint(
|
|
1144
1179
|
{
|
|
@@ -1150,6 +1185,7 @@ var PipelineOrchestrator = class {
|
|
|
1150
1185
|
columnId
|
|
1151
1186
|
).then((checkpoint) => {
|
|
1152
1187
|
clearTimeout(timeoutId);
|
|
1188
|
+
if (!this.activeLoops.has(ticketId)) return;
|
|
1153
1189
|
if (checkpoint) {
|
|
1154
1190
|
console.error(` [checkpoint] Resuming ${ticketId} at iteration ${String(checkpoint.iteration + 1)} (model: ${checkpoint.model_tier})`);
|
|
1155
1191
|
this.startLoopWithConfig(ticketId, columnId, config, checkpoint.iteration + 1, checkpoint.gutter_count, modelOverride ?? checkpoint.model_tier, checkpoint.last_fingerprint);
|
|
@@ -1158,6 +1194,7 @@ var PipelineOrchestrator = class {
|
|
|
1158
1194
|
}
|
|
1159
1195
|
}).catch((err) => {
|
|
1160
1196
|
clearTimeout(timeoutId);
|
|
1197
|
+
if (!this.activeLoops.has(ticketId)) return;
|
|
1161
1198
|
const msg = err instanceof Error ? err.message : String(err);
|
|
1162
1199
|
console.error(` [warn] Checkpoint read failed for ${ticketId} (starting fresh): ${msg}`);
|
|
1163
1200
|
this.startLoopWithConfig(ticketId, columnId, config, void 0, void 0, modelOverride);
|
|
@@ -1177,12 +1214,16 @@ var PipelineOrchestrator = class {
|
|
|
1177
1214
|
gutterThreshold: config.gutterThreshold,
|
|
1178
1215
|
...effectiveModel !== void 0 && { model: effectiveModel },
|
|
1179
1216
|
...config.maxBudgetUsd !== void 0 && { maxBudgetUsd: config.maxBudgetUsd },
|
|
1180
|
-
// Resolve worktree name from ticket context
|
|
1217
|
+
// Resolve worktree name (branch) and path from ticket context. When the
|
|
1218
|
+
// column's agent_config specifies a path_pattern, the filesystem path is
|
|
1219
|
+
// rendered from it — otherwise the name doubles as a relative path.
|
|
1181
1220
|
...(() => {
|
|
1182
1221
|
const colScope = this.columnScopes.get(columnId);
|
|
1183
1222
|
const ticket = colScope?.tickets.find((t) => t.id === ticketId);
|
|
1184
|
-
|
|
1185
|
-
|
|
1223
|
+
if (!config.worktreeEnabled || !ticket) return {};
|
|
1224
|
+
const wName = generateWorktreeName(ticket.ticket_number, config.name);
|
|
1225
|
+
const wPath = renderWorktreePath(ticket.ticket_number, config.name, config.worktreePathPattern);
|
|
1226
|
+
return wPath !== wName ? { worktreeName: wName, worktreePath: wPath } : { worktreeName: wName };
|
|
1186
1227
|
})(),
|
|
1187
1228
|
...config.lookaheadColumnId !== void 0 && { lookaheadColumnId: config.lookaheadColumnId },
|
|
1188
1229
|
// Resume from checkpoint iteration/gutter if provided
|
|
@@ -1200,8 +1241,10 @@ var PipelineOrchestrator = class {
|
|
|
1200
1241
|
if (toolRestrictions.tools !== void 0 || toolRestrictions.allowedTools || toolRestrictions.disallowedTools) {
|
|
1201
1242
|
loopConfig.toolRestrictions = toolRestrictions;
|
|
1202
1243
|
}
|
|
1244
|
+
const abort = new AbortController();
|
|
1245
|
+
loopConfig.abortSignal = abort.signal;
|
|
1203
1246
|
const promise = this.deps.startLoop(ticketId, columnId, loopConfig);
|
|
1204
|
-
this.activeLoops.set(ticketId, { columnId, promise });
|
|
1247
|
+
this.activeLoops.set(ticketId, { columnId, promise, abort });
|
|
1205
1248
|
this.lastFiredAt.set(columnId, /* @__PURE__ */ new Date());
|
|
1206
1249
|
void promise.then(
|
|
1207
1250
|
(result) => this.onLoopComplete(ticketId, columnId, result).catch((err) => {
|
|
@@ -1661,6 +1704,11 @@ ${findingsText}`)
|
|
|
1661
1704
|
async onLoopComplete(ticketId, columnId, result) {
|
|
1662
1705
|
this.completing.add(ticketId);
|
|
1663
1706
|
this.activeLoops.delete(ticketId);
|
|
1707
|
+
if (this.abortedForMove.has(ticketId)) {
|
|
1708
|
+
this.abortedForMove.delete(ticketId);
|
|
1709
|
+
this.completing.delete(ticketId);
|
|
1710
|
+
return;
|
|
1711
|
+
}
|
|
1664
1712
|
try {
|
|
1665
1713
|
const colScope = this.columnScopes.get(columnId);
|
|
1666
1714
|
const ticket = colScope?.tickets.find((t) => t.id === ticketId);
|
|
@@ -3301,14 +3349,15 @@ var CodexProvider = class {
|
|
|
3301
3349
|
const env = this.buildEnv();
|
|
3302
3350
|
const startTime = Date.now();
|
|
3303
3351
|
if (request.workingDirectory) {
|
|
3352
|
+
const branch = request.branch ?? request.workingDirectory;
|
|
3304
3353
|
if (!existsSync(request.workingDirectory)) {
|
|
3305
3354
|
try {
|
|
3306
|
-
execFileSync2("git", ["worktree", "add", "-b",
|
|
3355
|
+
execFileSync2("git", ["worktree", "add", "-b", branch, request.workingDirectory, "HEAD"], {
|
|
3307
3356
|
stdio: "pipe"
|
|
3308
3357
|
});
|
|
3309
3358
|
} catch {
|
|
3310
3359
|
try {
|
|
3311
|
-
execFileSync2("git", ["worktree", "add", request.workingDirectory,
|
|
3360
|
+
execFileSync2("git", ["worktree", "add", request.workingDirectory, branch], {
|
|
3312
3361
|
stdio: "pipe"
|
|
3313
3362
|
});
|
|
3314
3363
|
} catch {
|
|
@@ -3323,12 +3372,12 @@ var CodexProvider = class {
|
|
|
3323
3372
|
} catch {
|
|
3324
3373
|
try {
|
|
3325
3374
|
rmSync2(request.workingDirectory, { recursive: true, force: true });
|
|
3326
|
-
execFileSync2("git", ["worktree", "add", "-b",
|
|
3375
|
+
execFileSync2("git", ["worktree", "add", "-b", branch, request.workingDirectory, "HEAD"], {
|
|
3327
3376
|
stdio: "pipe"
|
|
3328
3377
|
});
|
|
3329
3378
|
} catch {
|
|
3330
3379
|
try {
|
|
3331
|
-
execFileSync2("git", ["worktree", "add", request.workingDirectory,
|
|
3380
|
+
execFileSync2("git", ["worktree", "add", request.workingDirectory, branch], {
|
|
3332
3381
|
stdio: "pipe"
|
|
3333
3382
|
});
|
|
3334
3383
|
} catch {
|
|
@@ -3423,6 +3472,8 @@ var CodexProvider = class {
|
|
|
3423
3472
|
for (const [name, server] of Object.entries(request.mcpConfig.servers)) {
|
|
3424
3473
|
args.push("-c", `mcp_servers.${name}.command=${JSON.stringify(server.command)}`);
|
|
3425
3474
|
args.push("-c", `mcp_servers.${name}.args=${JSON.stringify(server.args)}`);
|
|
3475
|
+
args.push("-c", `mcp_servers.${name}.tool_timeout_sec=1800`);
|
|
3476
|
+
args.push("-c", `mcp_servers.${name}.startup_timeout_sec=120`);
|
|
3426
3477
|
if (Object.keys(server.env).length > 0) {
|
|
3427
3478
|
for (const [key, value] of Object.entries(server.env)) {
|
|
3428
3479
|
args.push("-c", `mcp_servers.${name}.env.${key}=${JSON.stringify(value)}`);
|
|
@@ -4740,6 +4791,7 @@ Received ${signal}. Shutting down gracefully...`);
|
|
|
4740
4791
|
const ticket = payload["ticket"];
|
|
4741
4792
|
const ticketId = (typeof payload["ticketId"] === "string" ? payload["ticketId"] : null) ?? (ticket && typeof ticket["id"] === "string" ? ticket["id"] : null);
|
|
4742
4793
|
const columnId = (typeof payload["columnId"] === "string" ? payload["columnId"] : null) ?? (ticket && typeof ticket["column_id"] === "string" ? ticket["column_id"] : null);
|
|
4794
|
+
const fromColumnId = typeof payload["fromColumnId"] === "string" ? payload["fromColumnId"] : null;
|
|
4743
4795
|
if (wsEvent.type === "firing_constraint:created" || wsEvent.type === "firing_constraint:updated" || wsEvent.type === "firing_constraint:deleted") {
|
|
4744
4796
|
logger.orchestrator(`WS event: ${wsEvent.type} \u2014 refreshing constraint caches`);
|
|
4745
4797
|
void orchestrator.refreshConstraints().catch((err) => {
|
|
@@ -4751,9 +4803,9 @@ Received ${signal}. Shutting down gracefully...`);
|
|
|
4751
4803
|
if (!ticketId) return;
|
|
4752
4804
|
const eventType = wsEvent.type;
|
|
4753
4805
|
if (eventType === "ticket:created" || eventType === "ticket:moved" || eventType === "ticket:updated" || eventType === "ticket:archived" || eventType === "ticket:deleted") {
|
|
4754
|
-
const pipelineEvent = { type: eventType, ticketId, columnId };
|
|
4806
|
+
const pipelineEvent = { type: eventType, ticketId, columnId, fromColumnId };
|
|
4755
4807
|
eventQueue.push(pipelineEvent);
|
|
4756
|
-
logger.orchestrator(`WS event: ${eventType} ticket=${ticketId} column=${columnId ?? "null"}`);
|
|
4808
|
+
logger.orchestrator(`WS event: ${eventType} ticket=${ticketId} column=${columnId ?? "null"} from=${fromColumnId ?? "null"}`);
|
|
4757
4809
|
}
|
|
4758
4810
|
},
|
|
4759
4811
|
onConnect: () => {
|
|
@@ -4896,4 +4948,4 @@ export {
|
|
|
4896
4948
|
runPipeline,
|
|
4897
4949
|
stopPipeline
|
|
4898
4950
|
};
|
|
4899
|
-
//# sourceMappingURL=pipeline-
|
|
4951
|
+
//# sourceMappingURL=pipeline-ILE7LMGI.js.map
|