@staff0rd/assist 0.372.0 → 0.373.0
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 +3 -1
- package/claude/commands/bug.md +3 -3
- package/claude/commands/draft.md +4 -4
- package/claude/commands/jira.md +1 -1
- package/claude/commands/subtask.md +2 -2
- package/dist/commands/sessions/web/bundle.js +90 -90
- package/dist/index.js +673 -554
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.373.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -120,8 +120,8 @@ var ITEM_ID_ENV = "ASSIST_BACKLOG_ITEM_ID";
|
|
|
120
120
|
function resolveSessionItemId() {
|
|
121
121
|
const raw = process.env[ITEM_ID_ENV];
|
|
122
122
|
if (!raw) return null;
|
|
123
|
-
const
|
|
124
|
-
return Number.isInteger(
|
|
123
|
+
const id = Number.parseInt(raw, 10);
|
|
124
|
+
return Number.isInteger(id) && id > 0 ? id : null;
|
|
125
125
|
}
|
|
126
126
|
|
|
127
127
|
// src/shared/db/getDb.ts
|
|
@@ -1005,9 +1005,9 @@ async function closeDb() {
|
|
|
1005
1005
|
}
|
|
1006
1006
|
|
|
1007
1007
|
// src/shared/db/recordGitRef.ts
|
|
1008
|
-
async function recordGitRef(db,
|
|
1008
|
+
async function recordGitRef(db, itemId2, ref) {
|
|
1009
1009
|
await db.insert(itemGitRefs).values({
|
|
1010
|
-
itemId,
|
|
1010
|
+
itemId: itemId2,
|
|
1011
1011
|
kind: ref.kind,
|
|
1012
1012
|
ref: ref.ref,
|
|
1013
1013
|
title: ref.title ?? null,
|
|
@@ -1025,15 +1025,15 @@ async function recordGitRef(db, itemId, ref) {
|
|
|
1025
1025
|
|
|
1026
1026
|
// src/shared/db/recordSessionRefs.ts
|
|
1027
1027
|
async function recordSessionRefs(refs) {
|
|
1028
|
-
const
|
|
1029
|
-
if (
|
|
1030
|
-
await persist(
|
|
1028
|
+
const itemId2 = resolveSessionItemId();
|
|
1029
|
+
if (itemId2 === null || refs.length === 0) return;
|
|
1030
|
+
await persist(itemId2, refs).catch(ignore);
|
|
1031
1031
|
await closeDb().catch(ignore);
|
|
1032
1032
|
}
|
|
1033
|
-
async function persist(
|
|
1033
|
+
async function persist(itemId2, refs) {
|
|
1034
1034
|
const db = await getDb();
|
|
1035
1035
|
for (const ref of refs) {
|
|
1036
|
-
await recordGitRef(db,
|
|
1036
|
+
await recordGitRef(db, itemId2, ref);
|
|
1037
1037
|
}
|
|
1038
1038
|
}
|
|
1039
1039
|
function ignore() {
|
|
@@ -4120,6 +4120,24 @@ function hasLocalChanges() {
|
|
|
4120
4120
|
}
|
|
4121
4121
|
}
|
|
4122
4122
|
|
|
4123
|
+
// src/commands/backlog/formatItemId.ts
|
|
4124
|
+
var InvalidItemIdError = class extends Error {
|
|
4125
|
+
name = "InvalidItemIdError";
|
|
4126
|
+
};
|
|
4127
|
+
function formatItemId(id) {
|
|
4128
|
+
return `a${id}`;
|
|
4129
|
+
}
|
|
4130
|
+
var itemIdPattern = /^a?(\d+)$/;
|
|
4131
|
+
function parseItemId(input) {
|
|
4132
|
+
const match = itemIdPattern.exec(input.trim());
|
|
4133
|
+
if (!match) {
|
|
4134
|
+
throw new InvalidItemIdError(
|
|
4135
|
+
`Invalid backlog item id "${input}" (e.g. a574 or 574).`
|
|
4136
|
+
);
|
|
4137
|
+
}
|
|
4138
|
+
return Number.parseInt(match[1], 10);
|
|
4139
|
+
}
|
|
4140
|
+
|
|
4123
4141
|
// src/commands/backlog/acquireLock.ts
|
|
4124
4142
|
import {
|
|
4125
4143
|
existsSync as existsSync16,
|
|
@@ -4133,8 +4151,8 @@ import { join as join12 } from "path";
|
|
|
4133
4151
|
function getLocksDir() {
|
|
4134
4152
|
return join12(homedir4(), ".assist", "locks");
|
|
4135
4153
|
}
|
|
4136
|
-
function getLockPath(
|
|
4137
|
-
return join12(getLocksDir(), `lock-${
|
|
4154
|
+
function getLockPath(itemId2) {
|
|
4155
|
+
return join12(getLocksDir(), `lock-${itemId2}.json`);
|
|
4138
4156
|
}
|
|
4139
4157
|
function isProcessAlive(pid) {
|
|
4140
4158
|
try {
|
|
@@ -4144,8 +4162,8 @@ function isProcessAlive(pid) {
|
|
|
4144
4162
|
return false;
|
|
4145
4163
|
}
|
|
4146
4164
|
}
|
|
4147
|
-
function isLockedByOther(
|
|
4148
|
-
const lockPath = getLockPath(
|
|
4165
|
+
function isLockedByOther(itemId2) {
|
|
4166
|
+
const lockPath = getLockPath(itemId2);
|
|
4149
4167
|
if (!existsSync16(lockPath)) return false;
|
|
4150
4168
|
try {
|
|
4151
4169
|
const lock2 = JSON.parse(readFileSync12(lockPath, "utf8"));
|
|
@@ -4155,15 +4173,15 @@ function isLockedByOther(itemId) {
|
|
|
4155
4173
|
return false;
|
|
4156
4174
|
}
|
|
4157
4175
|
}
|
|
4158
|
-
function acquireLock(
|
|
4176
|
+
function acquireLock(itemId2) {
|
|
4159
4177
|
mkdirSync4(getLocksDir(), { recursive: true });
|
|
4160
4178
|
writeFileSync14(
|
|
4161
|
-
getLockPath(
|
|
4179
|
+
getLockPath(itemId2),
|
|
4162
4180
|
JSON.stringify({ pid: process.pid, timestamp: (/* @__PURE__ */ new Date()).toISOString() })
|
|
4163
4181
|
);
|
|
4164
4182
|
}
|
|
4165
|
-
function releaseLock(
|
|
4166
|
-
const lockPath = getLockPath(
|
|
4183
|
+
function releaseLock(itemId2) {
|
|
4184
|
+
const lockPath = getLockPath(itemId2);
|
|
4167
4185
|
try {
|
|
4168
4186
|
unlinkSync2(lockPath);
|
|
4169
4187
|
} catch {
|
|
@@ -4366,27 +4384,27 @@ import { join as join14 } from "path";
|
|
|
4366
4384
|
function getControlsDir() {
|
|
4367
4385
|
return join14(homedir6(), ".assist", "controls");
|
|
4368
4386
|
}
|
|
4369
|
-
function getPausePath(
|
|
4370
|
-
return join14(getControlsDir(), `pause-${
|
|
4387
|
+
function getPausePath(itemId2) {
|
|
4388
|
+
return join14(getControlsDir(), `pause-${itemId2}.json`);
|
|
4371
4389
|
}
|
|
4372
|
-
function requestPause(
|
|
4390
|
+
function requestPause(itemId2) {
|
|
4373
4391
|
mkdirSync6(getControlsDir(), { recursive: true });
|
|
4374
4392
|
writeFileSync16(
|
|
4375
|
-
getPausePath(
|
|
4393
|
+
getPausePath(itemId2),
|
|
4376
4394
|
JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString() })
|
|
4377
4395
|
);
|
|
4378
4396
|
}
|
|
4379
|
-
function isPausePending(
|
|
4380
|
-
return existsSync17(getPausePath(
|
|
4397
|
+
function isPausePending(itemId2) {
|
|
4398
|
+
return existsSync17(getPausePath(itemId2));
|
|
4381
4399
|
}
|
|
4382
|
-
function clearPause(
|
|
4400
|
+
function clearPause(itemId2) {
|
|
4383
4401
|
try {
|
|
4384
|
-
unlinkSync3(getPausePath(
|
|
4402
|
+
unlinkSync3(getPausePath(itemId2));
|
|
4385
4403
|
} catch {
|
|
4386
4404
|
}
|
|
4387
4405
|
}
|
|
4388
|
-
function consumePause(
|
|
4389
|
-
const pausePath = getPausePath(
|
|
4406
|
+
function consumePause(itemId2) {
|
|
4407
|
+
const pausePath = getPausePath(itemId2);
|
|
4390
4408
|
if (!existsSync17(pausePath)) return false;
|
|
4391
4409
|
try {
|
|
4392
4410
|
unlinkSync3(pausePath);
|
|
@@ -4716,8 +4734,8 @@ function rowToGitRef(r) {
|
|
|
4716
4734
|
if (r.createdAt != null) ref.createdAt = new Date(r.createdAt).toISOString();
|
|
4717
4735
|
return ref;
|
|
4718
4736
|
}
|
|
4719
|
-
function attachGitRefs(item, rel,
|
|
4720
|
-
const gitRefs = (rel.gitRefs.get(
|
|
4737
|
+
function attachGitRefs(item, rel, id) {
|
|
4738
|
+
const gitRefs = (rel.gitRefs.get(id) ?? []).map(rowToGitRef);
|
|
4721
4739
|
if (gitRefs.length > 0) item.gitRefs = gitRefs;
|
|
4722
4740
|
}
|
|
4723
4741
|
|
|
@@ -4730,8 +4748,8 @@ function rowToSubtask(s) {
|
|
|
4730
4748
|
if (s.description != null) subtask.description = s.description;
|
|
4731
4749
|
return subtask;
|
|
4732
4750
|
}
|
|
4733
|
-
function attachSubtasks(item, rel,
|
|
4734
|
-
const subtasks = (rel.subtasks.get(
|
|
4751
|
+
function attachSubtasks(item, rel, id) {
|
|
4752
|
+
const subtasks = (rel.subtasks.get(id) ?? []).map(rowToSubtask);
|
|
4735
4753
|
if (subtasks.length > 0) item.subtasks = subtasks;
|
|
4736
4754
|
}
|
|
4737
4755
|
|
|
@@ -4744,8 +4762,8 @@ function rowToUsage(u) {
|
|
|
4744
4762
|
activeMs: u.activeMs
|
|
4745
4763
|
};
|
|
4746
4764
|
}
|
|
4747
|
-
function attachUsage(item, rel,
|
|
4748
|
-
const usage = (rel.usage.get(
|
|
4765
|
+
function attachUsage(item, rel, id) {
|
|
4766
|
+
const usage = (rel.usage.get(id) ?? []).map(rowToUsage);
|
|
4749
4767
|
if (usage.length > 0) item.phaseUsage = usage;
|
|
4750
4768
|
}
|
|
4751
4769
|
|
|
@@ -4804,17 +4822,17 @@ function baseItem(row) {
|
|
|
4804
4822
|
assignOptionalColumns(item, row);
|
|
4805
4823
|
return item;
|
|
4806
4824
|
}
|
|
4807
|
-
function attachComments(item, rel,
|
|
4808
|
-
const comments3 = (rel.comments.get(
|
|
4825
|
+
function attachComments(item, rel, id) {
|
|
4826
|
+
const comments3 = (rel.comments.get(id) ?? []).map(rowToComment);
|
|
4809
4827
|
if (comments3.length > 0) item.comments = comments3;
|
|
4810
4828
|
}
|
|
4811
|
-
function attachLinks(item, rel,
|
|
4812
|
-
const links2 = (rel.links.get(
|
|
4829
|
+
function attachLinks(item, rel, id) {
|
|
4830
|
+
const links2 = (rel.links.get(id) ?? []).map(rowToLink);
|
|
4813
4831
|
if (links2.length > 0) item.links = links2;
|
|
4814
4832
|
}
|
|
4815
|
-
function attachPlan(item, rel,
|
|
4816
|
-
const phases = rel.phases.get(
|
|
4817
|
-
if (phases.length > 0) item.plan = buildPlan(phases, rel.tasks.get(
|
|
4833
|
+
function attachPlan(item, rel, id) {
|
|
4834
|
+
const phases = rel.phases.get(id) ?? [];
|
|
4835
|
+
if (phases.length > 0) item.plan = buildPlan(phases, rel.tasks.get(id) ?? []);
|
|
4818
4836
|
}
|
|
4819
4837
|
function rowToItem(row, rel) {
|
|
4820
4838
|
const item = baseItem(row);
|
|
@@ -4966,8 +4984,8 @@ async function ensureMigrated(orm, dir, origin) {
|
|
|
4966
4984
|
|
|
4967
4985
|
// src/commands/backlog/deleteItem.ts
|
|
4968
4986
|
import { eq as eq4 } from "drizzle-orm";
|
|
4969
|
-
async function deleteItem(orm,
|
|
4970
|
-
const [row] = await orm.delete(items).where(eq4(items.id,
|
|
4987
|
+
async function deleteItem(orm, id) {
|
|
4988
|
+
const [row] = await orm.delete(items).where(eq4(items.id, id)).returning({ name: items.name });
|
|
4971
4989
|
return row?.name;
|
|
4972
4990
|
}
|
|
4973
4991
|
|
|
@@ -5047,10 +5065,10 @@ function getCurrentOrigin(cwd) {
|
|
|
5047
5065
|
|
|
5048
5066
|
// src/commands/backlog/loadItem.ts
|
|
5049
5067
|
import { eq as eq5 } from "drizzle-orm";
|
|
5050
|
-
async function loadItem(orm,
|
|
5051
|
-
const [row] = await orm.select().from(items).where(eq5(items.id,
|
|
5068
|
+
async function loadItem(orm, id) {
|
|
5069
|
+
const [row] = await orm.select().from(items).where(eq5(items.id, id));
|
|
5052
5070
|
if (!row) return void 0;
|
|
5053
|
-
const rel = await loadRelations(orm, [
|
|
5071
|
+
const rel = await loadRelations(orm, [id], {
|
|
5054
5072
|
includeUsage: true,
|
|
5055
5073
|
includeGitRefs: true
|
|
5056
5074
|
});
|
|
@@ -5087,14 +5105,14 @@ async function searchItemIds(orm, query, origin) {
|
|
|
5087
5105
|
|
|
5088
5106
|
// src/commands/backlog/updateCurrentPhase.ts
|
|
5089
5107
|
import { eq as eq9 } from "drizzle-orm";
|
|
5090
|
-
async function updateCurrentPhase(orm,
|
|
5091
|
-
await orm.update(items).set({ currentPhase: phase }).where(eq9(items.id,
|
|
5108
|
+
async function updateCurrentPhase(orm, id, phase) {
|
|
5109
|
+
await orm.update(items).set({ currentPhase: phase }).where(eq9(items.id, id));
|
|
5092
5110
|
}
|
|
5093
5111
|
|
|
5094
5112
|
// src/commands/backlog/updateStatus.ts
|
|
5095
5113
|
import { eq as eq10 } from "drizzle-orm";
|
|
5096
|
-
async function updateStatus(orm,
|
|
5097
|
-
const [row] = await orm.update(items).set({ status: status2 }).where(eq10(items.id,
|
|
5114
|
+
async function updateStatus(orm, id, status2) {
|
|
5115
|
+
const [row] = await orm.update(items).set({ status: status2 }).where(eq10(items.id, id)).returning({ name: items.name });
|
|
5098
5116
|
return row?.name;
|
|
5099
5117
|
}
|
|
5100
5118
|
|
|
@@ -5126,35 +5144,39 @@ async function searchBacklog(query) {
|
|
|
5126
5144
|
const allItems = await loadAllItems(orm, origin);
|
|
5127
5145
|
return allItems.filter((item) => ids.includes(item.id));
|
|
5128
5146
|
}
|
|
5129
|
-
async function findOneItem(
|
|
5130
|
-
const numId =
|
|
5147
|
+
async function findOneItem(id) {
|
|
5148
|
+
const numId = parseItemId(id);
|
|
5131
5149
|
const { orm } = await getReady();
|
|
5132
|
-
const item =
|
|
5150
|
+
const item = await loadItem(orm, numId);
|
|
5133
5151
|
if (!item) {
|
|
5134
|
-
console.log(chalk37.red(`Item
|
|
5152
|
+
console.log(chalk37.red(`Item ${formatItemId(numId)} not found.`));
|
|
5135
5153
|
return void 0;
|
|
5136
5154
|
}
|
|
5137
5155
|
return { orm, item };
|
|
5138
5156
|
}
|
|
5139
|
-
async function setStatus(
|
|
5157
|
+
async function setStatus(id, status2) {
|
|
5140
5158
|
const { orm } = await getReady();
|
|
5141
|
-
const
|
|
5142
|
-
|
|
5159
|
+
const numId = parseItemId(id);
|
|
5160
|
+
const name = await updateStatus(orm, numId, status2);
|
|
5161
|
+
if (name === void 0)
|
|
5162
|
+
console.log(chalk37.red(`Item ${formatItemId(numId)} not found.`));
|
|
5143
5163
|
return name;
|
|
5144
5164
|
}
|
|
5145
|
-
async function setCurrentPhase(
|
|
5165
|
+
async function setCurrentPhase(id, phase) {
|
|
5146
5166
|
const { orm } = await getReady();
|
|
5147
|
-
await updateCurrentPhase(orm,
|
|
5167
|
+
await updateCurrentPhase(orm, parseItemId(id), phase);
|
|
5148
5168
|
}
|
|
5149
|
-
async function removeItem(
|
|
5169
|
+
async function removeItem(id) {
|
|
5150
5170
|
const { orm } = await getReady();
|
|
5151
|
-
const
|
|
5152
|
-
|
|
5171
|
+
const numId = parseItemId(id);
|
|
5172
|
+
const name = await deleteItem(orm, numId);
|
|
5173
|
+
if (name === void 0)
|
|
5174
|
+
console.log(chalk37.red(`Item ${formatItemId(numId)} not found.`));
|
|
5153
5175
|
return name;
|
|
5154
5176
|
}
|
|
5155
5177
|
|
|
5156
5178
|
// src/commands/backlog/handleReviewResult.ts
|
|
5157
|
-
async function handleReviewResult(
|
|
5179
|
+
async function handleReviewResult(id, review2) {
|
|
5158
5180
|
if (review2.kind === "fail" || review2.kind === "abort")
|
|
5159
5181
|
return { kind: "stop", success: false };
|
|
5160
5182
|
if (review2.kind === "paused") return { kind: "stop", success: true };
|
|
@@ -5164,12 +5186,12 @@ async function handleReviewResult(id2, review2) {
|
|
|
5164
5186
|
startPhase: review2.targetPhase,
|
|
5165
5187
|
plan: review2.plan
|
|
5166
5188
|
};
|
|
5167
|
-
await ensureDone(
|
|
5189
|
+
await ensureDone(id);
|
|
5168
5190
|
return { kind: "stop", success: true };
|
|
5169
5191
|
}
|
|
5170
|
-
async function ensureDone(
|
|
5192
|
+
async function ensureDone(id) {
|
|
5171
5193
|
try {
|
|
5172
|
-
await setStatus(
|
|
5194
|
+
await setStatus(id, "done");
|
|
5173
5195
|
} catch {
|
|
5174
5196
|
}
|
|
5175
5197
|
}
|
|
@@ -5191,20 +5213,24 @@ function resolvePlan(item) {
|
|
|
5191
5213
|
}
|
|
5192
5214
|
|
|
5193
5215
|
// src/commands/backlog/prepareRun.ts
|
|
5194
|
-
async function prepareRun(
|
|
5195
|
-
const found = await findOneItem(
|
|
5216
|
+
async function prepareRun(id) {
|
|
5217
|
+
const found = await findOneItem(id);
|
|
5196
5218
|
if (!found) return void 0;
|
|
5197
5219
|
const { item } = found;
|
|
5198
5220
|
const plan2 = resolvePlan(item);
|
|
5199
5221
|
const startPhase = (item.currentPhase ?? 1) - 1;
|
|
5200
5222
|
if (item.status === "done") {
|
|
5201
|
-
console.log(
|
|
5223
|
+
console.log(
|
|
5224
|
+
chalk38.green(`Already done: ${formatItemId(item.id)}: ${item.name}`)
|
|
5225
|
+
);
|
|
5202
5226
|
return void 0;
|
|
5203
5227
|
}
|
|
5204
5228
|
if (startPhase > plan2.length) {
|
|
5205
|
-
await setStatus(
|
|
5229
|
+
await setStatus(id, "done");
|
|
5206
5230
|
console.log(
|
|
5207
|
-
chalk38.green(
|
|
5231
|
+
chalk38.green(
|
|
5232
|
+
`All phases already complete for ${formatItemId(item.id)}: ${item.name}`
|
|
5233
|
+
)
|
|
5208
5234
|
);
|
|
5209
5235
|
return void 0;
|
|
5210
5236
|
}
|
|
@@ -5284,10 +5310,10 @@ function buildCommentLines(comments3) {
|
|
|
5284
5310
|
return ["", "Comments:", ...comments3.map(formatPromptComment)];
|
|
5285
5311
|
}
|
|
5286
5312
|
function formatPromptComment(entry) {
|
|
5287
|
-
const
|
|
5313
|
+
const id = entry.id !== void 0 ? `#${entry.id} ` : "";
|
|
5288
5314
|
const tag = entry.type === "summary" ? "[summary]" : "[comment]";
|
|
5289
5315
|
const phase = entry.phase !== void 0 ? ` (phase ${entry.phase})` : "";
|
|
5290
|
-
return `${
|
|
5316
|
+
return `${id}${tag}${phase} ${entry.timestamp}
|
|
5291
5317
|
${entry.text}`;
|
|
5292
5318
|
}
|
|
5293
5319
|
|
|
@@ -5305,16 +5331,16 @@ function buildAuthoredPhasePrompt(item, phaseNumber, phase) {
|
|
|
5305
5331
|
"When you have completed all tasks for this phase, run /verify to check your work.",
|
|
5306
5332
|
...buildManualCheckLines(manualChecks),
|
|
5307
5333
|
"",
|
|
5308
|
-
`Post concise comments for any notable findings or changes using \`assist backlog comment ${item.id} "<text>"\`.`,
|
|
5334
|
+
`Post concise comments for any notable findings or changes using \`assist backlog comment ${formatItemId(item.id)} "<text>"\`.`,
|
|
5309
5335
|
"",
|
|
5310
|
-
`Once verify passes${confirmSuffix}, run: assist backlog phase-done ${item.id} ${phaseNumber} "<summary>"`,
|
|
5336
|
+
`Once verify passes${confirmSuffix}, run: assist backlog phase-done ${formatItemId(item.id)} ${phaseNumber} "<summary>"`,
|
|
5311
5337
|
"Replace <summary> with a concise summary of what was done in this phase."
|
|
5312
5338
|
].filter((line) => line !== void 0).join("\n");
|
|
5313
5339
|
}
|
|
5314
5340
|
function buildContextLines(item, phaseNumber, phase) {
|
|
5315
5341
|
const ac = item.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join("\n");
|
|
5316
5342
|
return [
|
|
5317
|
-
`You are implementing phase ${phaseNumber} of backlog item
|
|
5343
|
+
`You are implementing phase ${phaseNumber} of backlog item ${formatItemId(item.id)}: ${item.name}`,
|
|
5318
5344
|
"",
|
|
5319
5345
|
item.description ? `Description: ${item.description}` : "",
|
|
5320
5346
|
"",
|
|
@@ -5355,8 +5381,9 @@ function formatTasks(phase) {
|
|
|
5355
5381
|
// src/commands/backlog/buildReviewPrompt.ts
|
|
5356
5382
|
function buildReviewPrompt(item, phaseNumber) {
|
|
5357
5383
|
const acLines = item.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join("\n");
|
|
5384
|
+
const itemId2 = formatItemId(item.id);
|
|
5358
5385
|
return [
|
|
5359
|
-
`You are reviewing backlog item
|
|
5386
|
+
`You are reviewing backlog item ${itemId2}: ${item.name}`,
|
|
5360
5387
|
"",
|
|
5361
5388
|
item.description ? `Description: ${item.description}` : "",
|
|
5362
5389
|
"",
|
|
@@ -5369,9 +5396,9 @@ function buildReviewPrompt(item, phaseNumber) {
|
|
|
5369
5396
|
"",
|
|
5370
5397
|
"If any criterion fails, fix the issue and re-verify.",
|
|
5371
5398
|
"",
|
|
5372
|
-
`Any sub-tasks on this item must all be done before it can be completed: \`assist backlog done\` and completing this Review phase both fail while any sub-task is not done. Check them with \`assist backlog show ${
|
|
5399
|
+
`Any sub-tasks on this item must all be done before it can be completed: \`assist backlog done\` and completing this Review phase both fail while any sub-task is not done. Check them with \`assist backlog show ${itemId2}\` and mark each finished one done via \`assist backlog subtask-status ${itemId2} <idx> done\`.`,
|
|
5373
5400
|
"",
|
|
5374
|
-
`Post concise comments for any notable findings or changes using \`assist backlog comment ${
|
|
5401
|
+
`Post concise comments for any notable findings or changes using \`assist backlog comment ${itemId2} "<text>"\`.`,
|
|
5375
5402
|
"",
|
|
5376
5403
|
"After all criteria pass, ask the user to confirm any manual checks",
|
|
5377
5404
|
"(e.g. end-to-end behaviour they need to verify themselves).",
|
|
@@ -5384,13 +5411,13 @@ function buildReviewPrompt(item, phaseNumber) {
|
|
|
5384
5411
|
"",
|
|
5385
5412
|
"Once the user gives that explicit completion instruction:",
|
|
5386
5413
|
"1. Run: /commit",
|
|
5387
|
-
`2. Run: assist backlog done ${
|
|
5388
|
-
`3. Run: assist backlog phase-done ${
|
|
5414
|
+
`2. Run: assist backlog done ${itemId2} "<summary>"`,
|
|
5415
|
+
`3. Run: assist backlog phase-done ${itemId2} ${phaseNumber} "done"`,
|
|
5389
5416
|
"",
|
|
5390
5417
|
"Precedence rules \u2014 read carefully:",
|
|
5391
5418
|
"- An explicit completion instruction plus a successful commit means the work is complete. Always finish with done + phase-done; never rewind already-approved, committed work as an automatic response.",
|
|
5392
5419
|
"- If the implementation has diverged from the recorded acceptance criteria but the user has approved it, the default is still to complete the item. You may optionally offer to update the acceptance criteria, but stale criteria are not a reason to rewind.",
|
|
5393
|
-
`- Rewinding is a confirm-first exception, not the normal path. If you believe a criterion genuinely no longer fits and the work should return to an earlier phase, you must ask the user and get explicit confirmation first. Only after they confirm may you run \`assist backlog rewind ${
|
|
5420
|
+
`- Rewinding is a confirm-first exception, not the normal path. If you believe a criterion genuinely no longer fits and the work should return to an earlier phase, you must ask the user and get explicit confirmation first. Only after they confirm may you run \`assist backlog rewind ${itemId2} <phase> --reason "<reason>"\`. Never rewind silently.`
|
|
5394
5421
|
].filter((line) => line !== void 0).join("\n");
|
|
5395
5422
|
}
|
|
5396
5423
|
|
|
@@ -5525,15 +5552,15 @@ function cleanupSignal() {
|
|
|
5525
5552
|
unlinkSync4(statusPath);
|
|
5526
5553
|
}
|
|
5527
5554
|
}
|
|
5528
|
-
async function isTerminalStatus(
|
|
5555
|
+
async function isTerminalStatus(itemId2) {
|
|
5529
5556
|
const { orm } = await getReady();
|
|
5530
|
-
const item = await loadItem(orm,
|
|
5557
|
+
const item = await loadItem(orm, itemId2);
|
|
5531
5558
|
return item?.status === "done" || item?.status === "wontdo";
|
|
5532
5559
|
}
|
|
5533
|
-
async function resolvePhaseResult(phaseIndex,
|
|
5560
|
+
async function resolvePhaseResult(phaseIndex, itemId2) {
|
|
5534
5561
|
const signalPath = getSignalPath();
|
|
5535
5562
|
if (!signalPath || !existsSync22(signalPath)) {
|
|
5536
|
-
if (await isTerminalStatus(
|
|
5563
|
+
if (await isTerminalStatus(itemId2)) return -1;
|
|
5537
5564
|
const action = await handleIncompletePhase();
|
|
5538
5565
|
if (action === "abort") return -1;
|
|
5539
5566
|
return action === "skip" ? phaseIndex + 1 : phaseIndex;
|
|
@@ -5617,8 +5644,8 @@ async function executePhase(item, phaseIndex, phases, spawnOptions, totalPhases
|
|
|
5617
5644
|
}
|
|
5618
5645
|
|
|
5619
5646
|
// src/commands/backlog/reloadPlan.ts
|
|
5620
|
-
async function reloadPlan(
|
|
5621
|
-
const found = await findOneItem(String(
|
|
5647
|
+
async function reloadPlan(id) {
|
|
5648
|
+
const found = await findOneItem(String(id));
|
|
5622
5649
|
if (!found) return void 0;
|
|
5623
5650
|
return resolvePlan(found.item);
|
|
5624
5651
|
}
|
|
@@ -5690,23 +5717,23 @@ async function runOnce(item, startPhase, plan2, spawnOptions) {
|
|
|
5690
5717
|
}
|
|
5691
5718
|
|
|
5692
5719
|
// src/commands/backlog/run.ts
|
|
5693
|
-
async function run2(
|
|
5694
|
-
const prepared = await prepareRun(
|
|
5720
|
+
async function run2(id, spawnOptions) {
|
|
5721
|
+
const prepared = await prepareRun(id);
|
|
5695
5722
|
if (!prepared) return false;
|
|
5696
5723
|
await ensureStoryBranch(prepared.item);
|
|
5697
|
-
await setStatus(
|
|
5724
|
+
await setStatus(id, "in-progress");
|
|
5698
5725
|
discardStalePause(prepared.item.id);
|
|
5699
|
-
logProgress(
|
|
5700
|
-
return runPrepared(
|
|
5726
|
+
logProgress(prepared);
|
|
5727
|
+
return runPrepared(id, prepared, spawnOptions);
|
|
5701
5728
|
}
|
|
5702
|
-
function discardStalePause(
|
|
5703
|
-
if (!isPausePending(
|
|
5704
|
-
clearPause(
|
|
5729
|
+
function discardStalePause(itemId2) {
|
|
5730
|
+
if (!isPausePending(itemId2)) return;
|
|
5731
|
+
clearPause(itemId2);
|
|
5705
5732
|
appendDaemonLog(
|
|
5706
|
-
`backlog run ${
|
|
5733
|
+
`backlog run ${itemId2}: discarded stale pause file at run start; this run starts auto-advancing (Continue on) regardless of a prior run's pause`
|
|
5707
5734
|
);
|
|
5708
5735
|
}
|
|
5709
|
-
async function runPrepared(
|
|
5736
|
+
async function runPrepared(id, prepared, spawnOptions) {
|
|
5710
5737
|
const { item } = prepared;
|
|
5711
5738
|
let { plan: plan2, startPhase } = prepared;
|
|
5712
5739
|
acquireLock(item.id);
|
|
@@ -5714,7 +5741,7 @@ async function runPrepared(id2, prepared, spawnOptions) {
|
|
|
5714
5741
|
while (true) {
|
|
5715
5742
|
const review2 = await runOnce(item, startPhase, plan2, spawnOptions);
|
|
5716
5743
|
spawnOptions = withoutResumeSession(spawnOptions);
|
|
5717
|
-
const outcome = await handleReviewResult(
|
|
5744
|
+
const outcome = await handleReviewResult(id, review2);
|
|
5718
5745
|
if (outcome.kind === "stop") {
|
|
5719
5746
|
return outcome.success;
|
|
5720
5747
|
}
|
|
@@ -5725,8 +5752,10 @@ async function runPrepared(id2, prepared, spawnOptions) {
|
|
|
5725
5752
|
releaseLock(item.id);
|
|
5726
5753
|
}
|
|
5727
5754
|
}
|
|
5728
|
-
function logProgress(
|
|
5729
|
-
console.log(
|
|
5755
|
+
function logProgress({ plan: plan2, startPhase, item }) {
|
|
5756
|
+
console.log(
|
|
5757
|
+
chalk42.bold(`Running plan for ${formatItemId(item.id)}: ${item.name}`)
|
|
5758
|
+
);
|
|
5730
5759
|
const totalPhases = plan2.length + 1;
|
|
5731
5760
|
if (startPhase > 0) {
|
|
5732
5761
|
const phaseNumber = startPhase + 1;
|
|
@@ -5742,7 +5771,7 @@ function logProgress(id2, { plan: plan2, startPhase, item }) {
|
|
|
5742
5771
|
|
|
5743
5772
|
// src/commands/backlog/next.ts
|
|
5744
5773
|
function toChoice(item, items2) {
|
|
5745
|
-
const name = `${typeLabel(item.type)}
|
|
5774
|
+
const name = `${typeLabel(item.type)} ${formatItemId(item.id)}: ${item.name}`;
|
|
5746
5775
|
return isBlocked(item, items2) ? { name, disabled: chalk43.red("[blocked]") } : { name };
|
|
5747
5776
|
}
|
|
5748
5777
|
async function selectItem(todo, items2) {
|
|
@@ -5754,20 +5783,24 @@ async function selectItem(todo, items2) {
|
|
|
5754
5783
|
choices: todo.map((i) => toChoice(i, items2))
|
|
5755
5784
|
})
|
|
5756
5785
|
);
|
|
5757
|
-
return selected.match(
|
|
5786
|
+
return selected.match(/(a\d+):/)?.[1] ?? "";
|
|
5758
5787
|
}
|
|
5759
5788
|
function pickResumable(items2) {
|
|
5760
5789
|
const resumable = findResumable(items2);
|
|
5761
5790
|
if (!resumable) return void 0;
|
|
5762
5791
|
console.log(
|
|
5763
|
-
chalk43.bold(
|
|
5792
|
+
chalk43.bold(
|
|
5793
|
+
`Resuming in-progress item ${formatItemId(resumable.id)}: ${resumable.name}`
|
|
5794
|
+
)
|
|
5764
5795
|
);
|
|
5765
|
-
return
|
|
5796
|
+
return formatItemId(resumable.id);
|
|
5766
5797
|
}
|
|
5767
5798
|
function autoSelect(unblocked) {
|
|
5768
5799
|
const item = unblocked[0];
|
|
5769
|
-
console.log(
|
|
5770
|
-
|
|
5800
|
+
console.log(
|
|
5801
|
+
chalk43.bold(`Auto-selecting item ${formatItemId(item.id)}: ${item.name}`)
|
|
5802
|
+
);
|
|
5803
|
+
return formatItemId(item.id);
|
|
5771
5804
|
}
|
|
5772
5805
|
async function pickItem(items2, firstPick = false) {
|
|
5773
5806
|
const resumable = pickResumable(items2);
|
|
@@ -5783,11 +5816,11 @@ async function next(options2, startId) {
|
|
|
5783
5816
|
let pendingId = startId;
|
|
5784
5817
|
let firstPick = true;
|
|
5785
5818
|
while (true) {
|
|
5786
|
-
const
|
|
5819
|
+
const id = pendingId ?? await pickItem(await loadBacklog(), firstPick);
|
|
5787
5820
|
pendingId = void 0;
|
|
5788
5821
|
firstPick = false;
|
|
5789
|
-
if (
|
|
5790
|
-
const completed = await run2(
|
|
5822
|
+
if (id === void 0) return;
|
|
5823
|
+
const completed = await run2(id, options2);
|
|
5791
5824
|
if (!completed || options2?.once) return;
|
|
5792
5825
|
}
|
|
5793
5826
|
}
|
|
@@ -5797,10 +5830,10 @@ import chalk45 from "chalk";
|
|
|
5797
5830
|
|
|
5798
5831
|
// src/commands/backlog/appendComment.ts
|
|
5799
5832
|
import { sql as sql4 } from "drizzle-orm";
|
|
5800
|
-
async function appendComment(orm,
|
|
5833
|
+
async function appendComment(orm, itemId2, text9, opts = {}) {
|
|
5801
5834
|
await orm.insert(comments).values({
|
|
5802
|
-
itemId,
|
|
5803
|
-
idx: sql4`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${
|
|
5835
|
+
itemId: itemId2,
|
|
5836
|
+
idx: sql4`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId2})`,
|
|
5804
5837
|
text: text9,
|
|
5805
5838
|
phase: opts.phase ?? null,
|
|
5806
5839
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5810,12 +5843,12 @@ async function appendComment(orm, itemId, text9, opts = {}) {
|
|
|
5810
5843
|
|
|
5811
5844
|
// src/commands/backlog/checkSubtasksComplete.ts
|
|
5812
5845
|
import chalk44 from "chalk";
|
|
5813
|
-
function checkSubtasksComplete(
|
|
5846
|
+
function checkSubtasksComplete(item) {
|
|
5814
5847
|
const pending = (item.subtasks ?? []).filter((s) => s.status !== "done");
|
|
5815
5848
|
if (pending.length === 0) return true;
|
|
5816
5849
|
console.log(
|
|
5817
5850
|
chalk44.red(
|
|
5818
|
-
`Cannot complete item
|
|
5851
|
+
`Cannot complete item ${formatItemId(item.id)}: ${pending.length} incomplete sub-task(s):`
|
|
5819
5852
|
)
|
|
5820
5853
|
);
|
|
5821
5854
|
for (const subtask of pending) {
|
|
@@ -5825,48 +5858,51 @@ function checkSubtasksComplete(id2, item) {
|
|
|
5825
5858
|
}
|
|
5826
5859
|
|
|
5827
5860
|
// src/commands/backlog/phaseDone.ts
|
|
5828
|
-
async function phaseDone(
|
|
5861
|
+
async function phaseDone(id, phase, summary) {
|
|
5829
5862
|
const phaseNumber = Number.parseInt(phase, 10);
|
|
5830
5863
|
const phaseIndex = phaseNumber - 1;
|
|
5831
|
-
const
|
|
5864
|
+
const itemId2 = parseItemId(id);
|
|
5865
|
+
const label2 = formatItemId(itemId2);
|
|
5832
5866
|
const { orm } = await getReady();
|
|
5833
|
-
const item = await loadItem(orm,
|
|
5867
|
+
const item = await loadItem(orm, itemId2);
|
|
5834
5868
|
if (item === void 0) {
|
|
5835
|
-
console.log(chalk45.red(`Item
|
|
5869
|
+
console.log(chalk45.red(`Item ${label2} not found.`));
|
|
5836
5870
|
return;
|
|
5837
5871
|
}
|
|
5838
5872
|
function signal() {
|
|
5839
5873
|
writeSignal("phase-done", {
|
|
5840
|
-
itemId,
|
|
5874
|
+
itemId: itemId2,
|
|
5841
5875
|
phaseIndex,
|
|
5842
5876
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5843
5877
|
});
|
|
5844
5878
|
}
|
|
5845
5879
|
if (item.status === "done") {
|
|
5846
5880
|
signal();
|
|
5847
|
-
console.log(
|
|
5881
|
+
console.log(
|
|
5882
|
+
chalk45.dim(`Item ${label2} already done, skipping phase advance.`)
|
|
5883
|
+
);
|
|
5848
5884
|
return;
|
|
5849
5885
|
}
|
|
5850
5886
|
const reviewPhaseNumber = resolvePlan(item).length + 1;
|
|
5851
|
-
if (phaseNumber === reviewPhaseNumber && !checkSubtasksComplete(
|
|
5887
|
+
if (phaseNumber === reviewPhaseNumber && !checkSubtasksComplete(item)) {
|
|
5852
5888
|
process.exitCode = 1;
|
|
5853
5889
|
return;
|
|
5854
5890
|
}
|
|
5855
5891
|
signal();
|
|
5856
|
-
await appendComment(orm,
|
|
5892
|
+
await appendComment(orm, itemId2, summary, {
|
|
5857
5893
|
phase: phaseNumber,
|
|
5858
5894
|
type: "summary"
|
|
5859
5895
|
});
|
|
5860
|
-
await setCurrentPhase(
|
|
5896
|
+
await setCurrentPhase(id, phaseNumber + 1);
|
|
5861
5897
|
console.log(
|
|
5862
|
-
chalk45.green(`Phase ${phaseNumber} of item
|
|
5898
|
+
chalk45.green(`Phase ${phaseNumber} of item ${label2} marked as complete.`)
|
|
5863
5899
|
);
|
|
5864
5900
|
}
|
|
5865
5901
|
|
|
5866
5902
|
// src/commands/backlog/plan.ts
|
|
5867
5903
|
import chalk46 from "chalk";
|
|
5868
|
-
async function plan(
|
|
5869
|
-
const found = await findOneItem(
|
|
5904
|
+
async function plan(id) {
|
|
5905
|
+
const found = await findOneItem(id);
|
|
5870
5906
|
if (!found) return;
|
|
5871
5907
|
const { item } = found;
|
|
5872
5908
|
if (!item.plan || item.plan.length === 0) {
|
|
@@ -5890,11 +5926,11 @@ import chalk53 from "chalk";
|
|
|
5890
5926
|
// src/commands/backlog/formatComment.ts
|
|
5891
5927
|
import chalk47 from "chalk";
|
|
5892
5928
|
function formatComment(entry) {
|
|
5893
|
-
const
|
|
5929
|
+
const id = entry.id !== void 0 ? chalk47.dim(`#${entry.id} `) : "";
|
|
5894
5930
|
const tag = entry.type === "summary" ? chalk47.magenta("[summary]") : chalk47.cyan("[comment]");
|
|
5895
5931
|
const phase = entry.phase !== void 0 ? chalk47.dim(` (phase ${entry.phase})`) : "";
|
|
5896
5932
|
const time = chalk47.dim(entry.timestamp);
|
|
5897
|
-
return `${
|
|
5933
|
+
return `${id}${tag}${phase} ${time}
|
|
5898
5934
|
${entry.text}`;
|
|
5899
5935
|
}
|
|
5900
5936
|
|
|
@@ -5991,11 +6027,11 @@ async function printLinks(orm, item) {
|
|
|
5991
6027
|
const typeLabel2 = link3.type === "depends-on" ? chalk49.red("depends-on") : chalk49.blue("relates-to");
|
|
5992
6028
|
if (target) {
|
|
5993
6029
|
console.log(
|
|
5994
|
-
` ${typeLabel2}
|
|
6030
|
+
` ${typeLabel2} ${formatItemId(target.id)} ${target.name} ${chalk49.dim(`(${target.status})`)}`
|
|
5995
6031
|
);
|
|
5996
6032
|
} else {
|
|
5997
6033
|
console.log(
|
|
5998
|
-
` ${typeLabel2}
|
|
6034
|
+
` ${typeLabel2} ${formatItemId(link3.targetId)} ${chalk49.dim("(not found)")}`
|
|
5999
6035
|
);
|
|
6000
6036
|
}
|
|
6001
6037
|
}
|
|
@@ -6061,7 +6097,7 @@ function printSubtasks(item) {
|
|
|
6061
6097
|
|
|
6062
6098
|
// src/commands/backlog/show/index.ts
|
|
6063
6099
|
function printHeader(item) {
|
|
6064
|
-
console.log(chalk53.bold(
|
|
6100
|
+
console.log(chalk53.bold(`${formatItemId(item.id)} ${item.name}`));
|
|
6065
6101
|
console.log(
|
|
6066
6102
|
`${chalk53.dim("Type:")} ${item.type} ${chalk53.dim("Status:")} ${item.status}`
|
|
6067
6103
|
);
|
|
@@ -6087,8 +6123,8 @@ function printComments(item) {
|
|
|
6087
6123
|
}
|
|
6088
6124
|
console.log();
|
|
6089
6125
|
}
|
|
6090
|
-
async function show(
|
|
6091
|
-
const found = await findOneItem(
|
|
6126
|
+
async function show(id) {
|
|
6127
|
+
const found = await findOneItem(id);
|
|
6092
6128
|
if (!found) process.exit(1);
|
|
6093
6129
|
const { orm, item } = found;
|
|
6094
6130
|
printHeader(item);
|
|
@@ -6518,18 +6554,18 @@ async function getBacklogSummary(req, res) {
|
|
|
6518
6554
|
|
|
6519
6555
|
// src/commands/backlog/deleteComment.ts
|
|
6520
6556
|
import { and as and4, eq as eq13 } from "drizzle-orm";
|
|
6521
|
-
async function deleteComment(orm,
|
|
6522
|
-
const [row] = await orm.select({ type: comments.type }).from(comments).where(and4(eq13(comments.id, commentId), eq13(comments.itemId,
|
|
6557
|
+
async function deleteComment(orm, itemId2, commentId) {
|
|
6558
|
+
const [row] = await orm.select({ type: comments.type }).from(comments).where(and4(eq13(comments.id, commentId), eq13(comments.itemId, itemId2)));
|
|
6523
6559
|
if (!row) return "not-found";
|
|
6524
6560
|
if (row.type === "summary") return "is-summary";
|
|
6525
|
-
await orm.delete(comments).where(and4(eq13(comments.id, commentId), eq13(comments.itemId,
|
|
6561
|
+
await orm.delete(comments).where(and4(eq13(comments.id, commentId), eq13(comments.itemId, itemId2)));
|
|
6526
6562
|
return "deleted";
|
|
6527
6563
|
}
|
|
6528
6564
|
|
|
6529
6565
|
// src/commands/backlog/updateStarred.ts
|
|
6530
6566
|
import { eq as eq14 } from "drizzle-orm";
|
|
6531
|
-
async function updateStarred(orm,
|
|
6532
|
-
const [row] = await orm.update(items).set({ starred }).where(eq14(items.id,
|
|
6567
|
+
async function updateStarred(orm, id, starred) {
|
|
6568
|
+
const [row] = await orm.update(items).set({ starred }).where(eq14(items.id, id)).returning({ name: items.name });
|
|
6533
6569
|
return row?.name;
|
|
6534
6570
|
}
|
|
6535
6571
|
|
|
@@ -6581,50 +6617,50 @@ async function listItems(req, res) {
|
|
|
6581
6617
|
const items2 = await loadVisibleItems(req);
|
|
6582
6618
|
respondJson(res, 200, items2.slice().reverse());
|
|
6583
6619
|
}
|
|
6584
|
-
async function findItemOr404(res,
|
|
6620
|
+
async function findItemOr404(res, id) {
|
|
6585
6621
|
const { orm } = await getReady();
|
|
6586
|
-
const item = await loadItem(orm,
|
|
6622
|
+
const item = await loadItem(orm, id);
|
|
6587
6623
|
if (!item) {
|
|
6588
6624
|
respondJson(res, 404, { error: "Not found" });
|
|
6589
6625
|
return void 0;
|
|
6590
6626
|
}
|
|
6591
6627
|
return { orm, item };
|
|
6592
6628
|
}
|
|
6593
|
-
async function getItemById(res,
|
|
6594
|
-
const result = await findItemOr404(res,
|
|
6629
|
+
async function getItemById(res, id) {
|
|
6630
|
+
const result = await findItemOr404(res, id);
|
|
6595
6631
|
if (result) respondJson(res, 200, withReviewPhase(result.item));
|
|
6596
6632
|
}
|
|
6597
|
-
async function deleteItem2(res,
|
|
6598
|
-
const result = await findItemOr404(res,
|
|
6633
|
+
async function deleteItem2(res, id) {
|
|
6634
|
+
const result = await findItemOr404(res, id);
|
|
6599
6635
|
if (!result) return;
|
|
6600
|
-
await deleteItem(result.orm,
|
|
6636
|
+
await deleteItem(result.orm, id);
|
|
6601
6637
|
respondJson(res, 200, result.item);
|
|
6602
6638
|
}
|
|
6603
|
-
async function patchItemStatus(req, res,
|
|
6639
|
+
async function patchItemStatus(req, res, id) {
|
|
6604
6640
|
const { status: status2 } = await parseStatusBody(req);
|
|
6605
|
-
const result = await findItemOr404(res,
|
|
6641
|
+
const result = await findItemOr404(res, id);
|
|
6606
6642
|
if (!result) return;
|
|
6607
|
-
await updateStatus(result.orm,
|
|
6643
|
+
await updateStatus(result.orm, id, status2);
|
|
6608
6644
|
const updated = { ...result.item, status: status2 };
|
|
6609
6645
|
respondJson(res, 200, updated);
|
|
6610
6646
|
}
|
|
6611
|
-
async function patchItemStar(req, res,
|
|
6647
|
+
async function patchItemStar(req, res, id) {
|
|
6612
6648
|
const { starred } = await parseStarBody(req);
|
|
6613
|
-
const result = await findItemOr404(res,
|
|
6649
|
+
const result = await findItemOr404(res, id);
|
|
6614
6650
|
if (!result) return;
|
|
6615
|
-
await updateStarred(result.orm,
|
|
6651
|
+
await updateStarred(result.orm, id, starred);
|
|
6616
6652
|
const updated = { ...result.item, starred };
|
|
6617
6653
|
respondJson(res, 200, updated);
|
|
6618
6654
|
}
|
|
6619
6655
|
|
|
6620
6656
|
// src/commands/backlog/web/deleteItemComment.ts
|
|
6621
|
-
async function deleteItemComment(res,
|
|
6622
|
-
const result = await findItemOr404(res,
|
|
6657
|
+
async function deleteItemComment(res, itemId2, commentId) {
|
|
6658
|
+
const result = await findItemOr404(res, itemId2);
|
|
6623
6659
|
if (!result) return;
|
|
6624
|
-
const outcome = await deleteComment(result.orm,
|
|
6660
|
+
const outcome = await deleteComment(result.orm, itemId2, commentId);
|
|
6625
6661
|
if (outcome === "not-found") {
|
|
6626
6662
|
respondJson(res, 404, {
|
|
6627
|
-
error: `Comment #${commentId} not found on item
|
|
6663
|
+
error: `Comment #${commentId} not found on item ${formatItemId(itemId2)}.`
|
|
6628
6664
|
});
|
|
6629
6665
|
return;
|
|
6630
6666
|
}
|
|
@@ -6634,19 +6670,19 @@ async function deleteItemComment(res, itemId, commentId) {
|
|
|
6634
6670
|
});
|
|
6635
6671
|
return;
|
|
6636
6672
|
}
|
|
6637
|
-
respondJson(res, 200, await loadItem(result.orm,
|
|
6673
|
+
respondJson(res, 200, await loadItem(result.orm, itemId2));
|
|
6638
6674
|
}
|
|
6639
6675
|
|
|
6640
6676
|
// src/commands/backlog/updateSubtaskStatus.ts
|
|
6641
6677
|
import { and as and5, eq as eq15 } from "drizzle-orm";
|
|
6642
|
-
async function updateSubtaskStatus(orm,
|
|
6643
|
-
const [row] = await orm.update(itemSubtasks).set({ status: status2 }).where(and5(eq15(itemSubtasks.itemId,
|
|
6678
|
+
async function updateSubtaskStatus(orm, itemId2, idx, status2) {
|
|
6679
|
+
const [row] = await orm.update(itemSubtasks).set({ status: status2 }).where(and5(eq15(itemSubtasks.itemId, itemId2), eq15(itemSubtasks.idx, idx))).returning({ title: itemSubtasks.title });
|
|
6644
6680
|
return row?.title;
|
|
6645
6681
|
}
|
|
6646
6682
|
|
|
6647
6683
|
// src/commands/backlog/web/patchSubtaskStatus.ts
|
|
6648
6684
|
var validStatuses = ["todo", "in-progress", "done"];
|
|
6649
|
-
async function patchSubtaskStatus(req, res,
|
|
6685
|
+
async function patchSubtaskStatus(req, res, id, idx) {
|
|
6650
6686
|
const { status: status2 } = await parseStatusBody(req);
|
|
6651
6687
|
if (!validStatuses.includes(status2)) {
|
|
6652
6688
|
respondJson(res, 400, {
|
|
@@ -6654,16 +6690,18 @@ async function patchSubtaskStatus(req, res, id2, idx) {
|
|
|
6654
6690
|
});
|
|
6655
6691
|
return;
|
|
6656
6692
|
}
|
|
6657
|
-
const result = await findItemOr404(res,
|
|
6693
|
+
const result = await findItemOr404(res, id);
|
|
6658
6694
|
if (!result) return;
|
|
6659
6695
|
const { orm, item } = result;
|
|
6660
6696
|
const subtasks = item.subtasks ?? [];
|
|
6661
6697
|
if (idx < 0 || idx >= subtasks.length) {
|
|
6662
|
-
respondJson(res, 400, {
|
|
6698
|
+
respondJson(res, 400, {
|
|
6699
|
+
error: `Item ${formatItemId(id)} has no sub-task ${idx}.`
|
|
6700
|
+
});
|
|
6663
6701
|
return;
|
|
6664
6702
|
}
|
|
6665
|
-
await updateSubtaskStatus(orm,
|
|
6666
|
-
respondJson(res, 200, await loadItem(orm,
|
|
6703
|
+
await updateSubtaskStatus(orm, id, idx, status2);
|
|
6704
|
+
respondJson(res, 200, await loadItem(orm, id));
|
|
6667
6705
|
}
|
|
6668
6706
|
|
|
6669
6707
|
// src/commands/backlog/rewindItemToPhase.ts
|
|
@@ -6696,9 +6734,9 @@ async function rewindItemToPhase(orm, item, phaseNumber, reason) {
|
|
|
6696
6734
|
}
|
|
6697
6735
|
|
|
6698
6736
|
// src/commands/backlog/web/rewindItemPhase.ts
|
|
6699
|
-
async function rewindItemPhase(req, res,
|
|
6737
|
+
async function rewindItemPhase(req, res, id) {
|
|
6700
6738
|
const { phase, reason } = await parseRewindBody(req);
|
|
6701
|
-
const result = await findItemOr404(res,
|
|
6739
|
+
const result = await findItemOr404(res, id);
|
|
6702
6740
|
if (!result) return;
|
|
6703
6741
|
const { orm, item } = result;
|
|
6704
6742
|
const outcome = await rewindItemToPhase(orm, item, phase, reason);
|
|
@@ -6706,46 +6744,47 @@ async function rewindItemPhase(req, res, id2) {
|
|
|
6706
6744
|
respondJson(res, 400, { error: outcome.error });
|
|
6707
6745
|
return;
|
|
6708
6746
|
}
|
|
6709
|
-
respondJson(res, 200, await loadItem(orm,
|
|
6747
|
+
respondJson(res, 200, await loadItem(orm, id));
|
|
6710
6748
|
}
|
|
6711
6749
|
|
|
6712
6750
|
// src/commands/backlog/web/handleItemRoute.ts
|
|
6713
|
-
var
|
|
6751
|
+
var itemId = (match, group = 1) => parseItemId(match[group]);
|
|
6752
|
+
var subId = (match, group) => Number.parseInt(match[group], 10);
|
|
6714
6753
|
var routes = [
|
|
6715
6754
|
{
|
|
6716
|
-
pattern: /^\/api\/items\/(
|
|
6755
|
+
pattern: /^\/api\/items\/(a?\d+)\/rewind$/,
|
|
6717
6756
|
method: "POST",
|
|
6718
|
-
run: (req, res, m) => rewindItemPhase(req, res,
|
|
6757
|
+
run: (req, res, m) => rewindItemPhase(req, res, itemId(m))
|
|
6719
6758
|
},
|
|
6720
6759
|
{
|
|
6721
|
-
pattern: /^\/api\/items\/(
|
|
6760
|
+
pattern: /^\/api\/items\/(a?\d+)\/star$/,
|
|
6722
6761
|
method: "POST",
|
|
6723
|
-
run: (req, res, m) => patchItemStar(req, res,
|
|
6762
|
+
run: (req, res, m) => patchItemStar(req, res, itemId(m))
|
|
6724
6763
|
},
|
|
6725
6764
|
{
|
|
6726
|
-
pattern: /^\/api\/items\/(
|
|
6765
|
+
pattern: /^\/api\/items\/(a?\d+)\/comments\/(\d+)$/,
|
|
6727
6766
|
method: "DELETE",
|
|
6728
|
-
run: (_req, res, m) => deleteItemComment(res,
|
|
6767
|
+
run: (_req, res, m) => deleteItemComment(res, itemId(m), subId(m, 2))
|
|
6729
6768
|
},
|
|
6730
6769
|
{
|
|
6731
|
-
pattern: /^\/api\/items\/(
|
|
6770
|
+
pattern: /^\/api\/items\/(a?\d+)\/subtasks\/(\d+)$/,
|
|
6732
6771
|
method: "PATCH",
|
|
6733
|
-
run: (req, res, m) => patchSubtaskStatus(req, res,
|
|
6772
|
+
run: (req, res, m) => patchSubtaskStatus(req, res, itemId(m), subId(m, 2))
|
|
6734
6773
|
},
|
|
6735
6774
|
{
|
|
6736
|
-
pattern: /^\/api\/items\/(
|
|
6775
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
6737
6776
|
method: "GET",
|
|
6738
|
-
run: (_req, res, m) => getItemById(res,
|
|
6777
|
+
run: (_req, res, m) => getItemById(res, itemId(m))
|
|
6739
6778
|
},
|
|
6740
6779
|
{
|
|
6741
|
-
pattern: /^\/api\/items\/(
|
|
6780
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
6742
6781
|
method: "PATCH",
|
|
6743
|
-
run: (req, res, m) => patchItemStatus(req, res,
|
|
6782
|
+
run: (req, res, m) => patchItemStatus(req, res, itemId(m))
|
|
6744
6783
|
},
|
|
6745
6784
|
{
|
|
6746
|
-
pattern: /^\/api\/items\/(
|
|
6785
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
6747
6786
|
method: "DELETE",
|
|
6748
|
-
run: (_req, res, m) => deleteItem2(res,
|
|
6787
|
+
run: (_req, res, m) => deleteItem2(res, itemId(m))
|
|
6749
6788
|
}
|
|
6750
6789
|
];
|
|
6751
6790
|
async function handleItemRoute(req, res, pathname) {
|
|
@@ -7818,7 +7857,7 @@ async function web2(options2) {
|
|
|
7818
7857
|
|
|
7819
7858
|
// src/commands/backlog/addActivity.ts
|
|
7820
7859
|
import chalk60 from "chalk";
|
|
7821
|
-
async function addActivity(
|
|
7860
|
+
async function addActivity(id, kind, ref, options2) {
|
|
7822
7861
|
const parsedKind = gitRefKindSchema.safeParse(kind);
|
|
7823
7862
|
if (!parsedKind.success) {
|
|
7824
7863
|
console.log(
|
|
@@ -7829,7 +7868,7 @@ async function addActivity(id2, kind, ref, options2) {
|
|
|
7829
7868
|
process.exitCode = 1;
|
|
7830
7869
|
return;
|
|
7831
7870
|
}
|
|
7832
|
-
const found = await findOneItem(
|
|
7871
|
+
const found = await findOneItem(id);
|
|
7833
7872
|
if (!found) {
|
|
7834
7873
|
process.exitCode = 1;
|
|
7835
7874
|
return;
|
|
@@ -7844,7 +7883,9 @@ async function addActivity(id2, kind, ref, options2) {
|
|
|
7844
7883
|
state: options2.state
|
|
7845
7884
|
});
|
|
7846
7885
|
console.log(
|
|
7847
|
-
chalk60.green(
|
|
7886
|
+
chalk60.green(
|
|
7887
|
+
`Attached ${parsedKind.data} "${ref}" to item ${formatItemId(item.id)}.`
|
|
7888
|
+
)
|
|
7848
7889
|
);
|
|
7849
7890
|
}
|
|
7850
7891
|
|
|
@@ -7890,17 +7931,19 @@ function fetchIssue(issueKey, fields) {
|
|
|
7890
7931
|
|
|
7891
7932
|
// src/commands/backlog/associate-jira/index.ts
|
|
7892
7933
|
var JIRA_KEY_PATTERN = /^[A-Z]+-\d+$/;
|
|
7893
|
-
async function associateJira(
|
|
7894
|
-
const found = await findOneItem(
|
|
7934
|
+
async function associateJira(id, key, options2) {
|
|
7935
|
+
const found = await findOneItem(id);
|
|
7895
7936
|
if (!found) {
|
|
7896
7937
|
process.exitCode = 1;
|
|
7897
7938
|
return;
|
|
7898
7939
|
}
|
|
7899
7940
|
const { orm } = found;
|
|
7900
|
-
const
|
|
7941
|
+
const itemId2 = found.item.id;
|
|
7901
7942
|
if (options2.clear) {
|
|
7902
|
-
await orm.update(items).set({ jiraKey: null }).where(eq16(items.id,
|
|
7903
|
-
console.log(
|
|
7943
|
+
await orm.update(items).set({ jiraKey: null }).where(eq16(items.id, itemId2));
|
|
7944
|
+
console.log(
|
|
7945
|
+
chalk62.green(`Cleared Jira association on item ${formatItemId(itemId2)}.`)
|
|
7946
|
+
);
|
|
7904
7947
|
return;
|
|
7905
7948
|
}
|
|
7906
7949
|
if (!key) {
|
|
@@ -7918,9 +7961,9 @@ async function associateJira(id2, key, options2) {
|
|
|
7918
7961
|
const parsed = fetchIssue(key, "summary");
|
|
7919
7962
|
const fields = parsed?.fields;
|
|
7920
7963
|
const summary = fields?.summary;
|
|
7921
|
-
await orm.update(items).set({ jiraKey: key }).where(eq16(items.id,
|
|
7964
|
+
await orm.update(items).set({ jiraKey: key }).where(eq16(items.id, itemId2));
|
|
7922
7965
|
console.log(
|
|
7923
|
-
chalk62.green(`Associated ${key} with item
|
|
7966
|
+
chalk62.green(`Associated ${key} with item ${formatItemId(itemId2)}.`),
|
|
7924
7967
|
summary ? chalk62.dim(`(${summary})`) : ""
|
|
7925
7968
|
);
|
|
7926
7969
|
}
|
|
@@ -7932,26 +7975,30 @@ function registerAssociateJiraCommand(cmd) {
|
|
|
7932
7975
|
|
|
7933
7976
|
// src/commands/backlog/comment/index.ts
|
|
7934
7977
|
import chalk63 from "chalk";
|
|
7935
|
-
async function comment(
|
|
7936
|
-
const found = await findOneItem(
|
|
7978
|
+
async function comment(id, text9) {
|
|
7979
|
+
const found = await findOneItem(id);
|
|
7937
7980
|
if (!found) process.exit(1);
|
|
7938
7981
|
await appendComment(found.orm, found.item.id, text9);
|
|
7939
|
-
console.log(
|
|
7982
|
+
console.log(
|
|
7983
|
+
chalk63.green(`Comment added to item ${formatItemId(found.item.id)}.`)
|
|
7984
|
+
);
|
|
7940
7985
|
}
|
|
7941
7986
|
|
|
7942
7987
|
// src/commands/backlog/comments/index.ts
|
|
7943
7988
|
import chalk64 from "chalk";
|
|
7944
|
-
async function comments2(
|
|
7945
|
-
const found = await findOneItem(
|
|
7989
|
+
async function comments2(id) {
|
|
7990
|
+
const found = await findOneItem(id);
|
|
7946
7991
|
if (!found) process.exit(1);
|
|
7947
7992
|
const { item } = found;
|
|
7948
7993
|
const entries = item.comments ?? [];
|
|
7949
7994
|
if (entries.length === 0) {
|
|
7950
|
-
console.log(chalk64.dim(`No comments on item
|
|
7995
|
+
console.log(chalk64.dim(`No comments on item ${formatItemId(item.id)}.`));
|
|
7951
7996
|
return;
|
|
7952
7997
|
}
|
|
7953
|
-
console.log(
|
|
7954
|
-
`)
|
|
7998
|
+
console.log(
|
|
7999
|
+
chalk64.bold(`Comments for ${formatItemId(item.id)}: ${item.name}
|
|
8000
|
+
`)
|
|
8001
|
+
);
|
|
7955
8002
|
for (const entry of entries) {
|
|
7956
8003
|
console.log(`${formatComment(entry)}
|
|
7957
8004
|
`);
|
|
@@ -7960,22 +8007,29 @@ async function comments2(id2) {
|
|
|
7960
8007
|
|
|
7961
8008
|
// src/commands/backlog/delete-comment/index.ts
|
|
7962
8009
|
import chalk65 from "chalk";
|
|
7963
|
-
async function deleteCommentCmd(
|
|
7964
|
-
const found = await findOneItem(
|
|
8010
|
+
async function deleteCommentCmd(id, commentId) {
|
|
8011
|
+
const found = await findOneItem(id);
|
|
7965
8012
|
if (!found) process.exit(1);
|
|
8013
|
+
const itemId2 = found.item.id;
|
|
7966
8014
|
const outcome = await deleteComment(
|
|
7967
8015
|
found.orm,
|
|
7968
|
-
|
|
8016
|
+
itemId2,
|
|
7969
8017
|
Number.parseInt(commentId, 10)
|
|
7970
8018
|
);
|
|
7971
8019
|
switch (outcome) {
|
|
7972
8020
|
case "deleted":
|
|
7973
8021
|
console.log(
|
|
7974
|
-
chalk65.green(
|
|
8022
|
+
chalk65.green(
|
|
8023
|
+
`Comment #${commentId} deleted from item ${formatItemId(itemId2)}.`
|
|
8024
|
+
)
|
|
7975
8025
|
);
|
|
7976
8026
|
break;
|
|
7977
8027
|
case "not-found":
|
|
7978
|
-
console.log(
|
|
8028
|
+
console.log(
|
|
8029
|
+
chalk65.red(
|
|
8030
|
+
`Comment #${commentId} not found on item ${formatItemId(itemId2)}.`
|
|
8031
|
+
)
|
|
8032
|
+
);
|
|
7979
8033
|
process.exit(1);
|
|
7980
8034
|
break;
|
|
7981
8035
|
case "is-summary":
|
|
@@ -8220,37 +8274,37 @@ import { count as count2, eq as eq18 } from "drizzle-orm";
|
|
|
8220
8274
|
|
|
8221
8275
|
// src/commands/backlog/shiftPhasesUp.ts
|
|
8222
8276
|
import { and as and6, desc as desc3, eq as eq17, gte } from "drizzle-orm";
|
|
8223
|
-
async function shiftPhasesUp(db,
|
|
8224
|
-
const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and6(eq17(planPhases.itemId,
|
|
8277
|
+
async function shiftPhasesUp(db, itemId2, fromIdx) {
|
|
8278
|
+
const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and6(eq17(planPhases.itemId, itemId2), gte(planPhases.idx, fromIdx))).orderBy(desc3(planPhases.idx));
|
|
8225
8279
|
for (const p of toShift) {
|
|
8226
|
-
await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and6(eq17(planTasks.itemId,
|
|
8227
|
-
await db.update(planPhases).set({ idx: p.idx + 1 }).where(and6(eq17(planPhases.itemId,
|
|
8280
|
+
await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and6(eq17(planTasks.itemId, itemId2), eq17(planTasks.phaseIdx, p.idx)));
|
|
8281
|
+
await db.update(planPhases).set({ idx: p.idx + 1 }).where(and6(eq17(planPhases.itemId, itemId2), eq17(planPhases.idx, p.idx)));
|
|
8228
8282
|
}
|
|
8229
8283
|
}
|
|
8230
8284
|
|
|
8231
8285
|
// src/commands/backlog/insertPhaseAt.ts
|
|
8232
|
-
async function insertPhaseAt(orm,
|
|
8286
|
+
async function insertPhaseAt(orm, itemId2, phaseIdx, name, tasks, manualChecks, currentPhase) {
|
|
8233
8287
|
await orm.transaction(async (tx) => {
|
|
8234
|
-
const [row] = await tx.select({ cnt: count2() }).from(planPhases).where(eq18(planPhases.itemId,
|
|
8288
|
+
const [row] = await tx.select({ cnt: count2() }).from(planPhases).where(eq18(planPhases.itemId, itemId2));
|
|
8235
8289
|
const phaseCount = row?.cnt ?? 0;
|
|
8236
|
-
await shiftPhasesUp(tx,
|
|
8237
|
-
await tx.insert(planPhases).values({ itemId, idx: phaseIdx, name, manualChecks });
|
|
8290
|
+
await shiftPhasesUp(tx, itemId2, phaseIdx);
|
|
8291
|
+
await tx.insert(planPhases).values({ itemId: itemId2, idx: phaseIdx, name, manualChecks });
|
|
8238
8292
|
if (tasks.length) {
|
|
8239
|
-
await tx.insert(planTasks).values(tasks.map((task, i) => ({ itemId, phaseIdx, idx: i, task })));
|
|
8293
|
+
await tx.insert(planTasks).values(tasks.map((task, i) => ({ itemId: itemId2, phaseIdx, idx: i, task })));
|
|
8240
8294
|
}
|
|
8241
8295
|
if (currentPhase !== void 0 && currentPhase - 1 >= phaseIdx) {
|
|
8242
8296
|
const atReviewSlot = currentPhase - 1 >= phaseCount;
|
|
8243
|
-
await tx.update(items).set({ currentPhase: atReviewSlot ? phaseIdx + 1 : currentPhase + 1 }).where(eq18(items.id,
|
|
8297
|
+
await tx.update(items).set({ currentPhase: atReviewSlot ? phaseIdx + 1 : currentPhase + 1 }).where(eq18(items.id, itemId2));
|
|
8244
8298
|
}
|
|
8245
8299
|
});
|
|
8246
8300
|
}
|
|
8247
8301
|
|
|
8248
8302
|
// src/commands/backlog/insertSubtask.ts
|
|
8249
8303
|
import { sql as sql6 } from "drizzle-orm";
|
|
8250
|
-
async function insertSubtask(orm,
|
|
8304
|
+
async function insertSubtask(orm, itemId2, title, description) {
|
|
8251
8305
|
await orm.insert(itemSubtasks).values({
|
|
8252
|
-
itemId,
|
|
8253
|
-
idx: sql6`(SELECT COALESCE(MAX(${itemSubtasks.idx}) + 1, 0) FROM ${itemSubtasks} WHERE ${itemSubtasks.itemId} = ${
|
|
8306
|
+
itemId: itemId2,
|
|
8307
|
+
idx: sql6`(SELECT COALESCE(MAX(${itemSubtasks.idx}) + 1, 0) FROM ${itemSubtasks} WHERE ${itemSubtasks.itemId} = ${itemId2})`,
|
|
8254
8308
|
title,
|
|
8255
8309
|
description: description ?? null,
|
|
8256
8310
|
status: "todo"
|
|
@@ -8348,7 +8402,7 @@ async function add(options2) {
|
|
|
8348
8402
|
const description = options2.desc?.replaceAll(String.raw`\n`, "\n") ?? await promptDescription();
|
|
8349
8403
|
const acceptanceCriteria2 = options2.ac ?? await promptAcceptanceCriteria();
|
|
8350
8404
|
const orm = await getDb();
|
|
8351
|
-
const
|
|
8405
|
+
const id = await insertItem(
|
|
8352
8406
|
orm,
|
|
8353
8407
|
{
|
|
8354
8408
|
type,
|
|
@@ -8361,12 +8415,12 @@ async function add(options2) {
|
|
|
8361
8415
|
getOrigin()
|
|
8362
8416
|
);
|
|
8363
8417
|
if (type === "bug") {
|
|
8364
|
-
await insertPhaseAt(orm,
|
|
8418
|
+
await insertPhaseAt(orm, id, 0, "Fix", ["Fix the bug"], null, void 0);
|
|
8365
8419
|
}
|
|
8366
8420
|
for (const subtask of loadConfig().subtasks ?? []) {
|
|
8367
|
-
await insertSubtask(orm,
|
|
8421
|
+
await insertSubtask(orm, id, subtask.title, subtask.description);
|
|
8368
8422
|
}
|
|
8369
|
-
console.log(chalk69.green(`Added item
|
|
8423
|
+
console.log(chalk69.green(`Added item ${formatItemId(id)}: ${name}`));
|
|
8370
8424
|
}
|
|
8371
8425
|
|
|
8372
8426
|
// src/commands/backlog/addPhase.ts
|
|
@@ -8375,8 +8429,8 @@ import chalk71 from "chalk";
|
|
|
8375
8429
|
// src/commands/backlog/resolveInsertPosition.ts
|
|
8376
8430
|
import chalk70 from "chalk";
|
|
8377
8431
|
import { count as count3, eq as eq19 } from "drizzle-orm";
|
|
8378
|
-
async function resolveInsertPosition(orm,
|
|
8379
|
-
const [row] = await orm.select({ cnt: count3() }).from(planPhases).where(eq19(planPhases.itemId,
|
|
8432
|
+
async function resolveInsertPosition(orm, itemId2, position) {
|
|
8433
|
+
const [row] = await orm.select({ cnt: count3() }).from(planPhases).where(eq19(planPhases.itemId, itemId2));
|
|
8380
8434
|
const phaseCount = row?.cnt ?? 0;
|
|
8381
8435
|
if (position === void 0) return phaseCount;
|
|
8382
8436
|
const pos = Number.parseInt(position, 10);
|
|
@@ -8398,9 +8452,9 @@ function serializeManualChecks(manualCheck) {
|
|
|
8398
8452
|
}
|
|
8399
8453
|
|
|
8400
8454
|
// src/commands/backlog/addPhase.ts
|
|
8401
|
-
async function addPhase(
|
|
8455
|
+
async function addPhase(id, name, options2) {
|
|
8402
8456
|
if (!ensureRemoteOrigin()) return;
|
|
8403
|
-
const found = await findOneItem(
|
|
8457
|
+
const found = await findOneItem(id);
|
|
8404
8458
|
if (!found) return;
|
|
8405
8459
|
const tasks = options2.task ?? [];
|
|
8406
8460
|
if (tasks.length === 0) {
|
|
@@ -8409,12 +8463,12 @@ async function addPhase(id2, name, options2) {
|
|
|
8409
8463
|
return;
|
|
8410
8464
|
}
|
|
8411
8465
|
const { orm } = found;
|
|
8412
|
-
const
|
|
8413
|
-
const phaseIdx = await resolveInsertPosition(orm,
|
|
8466
|
+
const itemId2 = found.item.id;
|
|
8467
|
+
const phaseIdx = await resolveInsertPosition(orm, itemId2, options2.position);
|
|
8414
8468
|
if (phaseIdx === void 0) return;
|
|
8415
8469
|
await insertPhaseAt(
|
|
8416
8470
|
orm,
|
|
8417
|
-
|
|
8471
|
+
itemId2,
|
|
8418
8472
|
phaseIdx,
|
|
8419
8473
|
name,
|
|
8420
8474
|
tasks,
|
|
@@ -8424,7 +8478,7 @@ async function addPhase(id2, name, options2) {
|
|
|
8424
8478
|
const verb = options2.position !== void 0 ? "Inserted" : "Added";
|
|
8425
8479
|
console.log(
|
|
8426
8480
|
chalk71.green(
|
|
8427
|
-
`${verb} phase ${phaseIdx + 1} "${name}" to item
|
|
8481
|
+
`${verb} phase ${phaseIdx + 1} "${name}" to item ${formatItemId(itemId2)} with ${tasks.length} task(s).`
|
|
8428
8482
|
)
|
|
8429
8483
|
);
|
|
8430
8484
|
}
|
|
@@ -8437,6 +8491,15 @@ function filterItems(items2, options2) {
|
|
|
8437
8491
|
return items2.filter((i) => i.status !== "done" && i.status !== "wontdo");
|
|
8438
8492
|
return items2;
|
|
8439
8493
|
}
|
|
8494
|
+
function repoPrefixer(items2, allRepos) {
|
|
8495
|
+
if (!allRepos) return () => "";
|
|
8496
|
+
const labels = originDisplayLabels(
|
|
8497
|
+
items2.flatMap((i) => i.origin ? [i.origin] : [])
|
|
8498
|
+
);
|
|
8499
|
+
const repoNameOf = (item) => item.origin ? labels.get(item.origin) ?? "" : "";
|
|
8500
|
+
const width = Math.max(0, ...items2.map((i) => repoNameOf(i).length));
|
|
8501
|
+
return (item) => `${chalk72.dim(repoNameOf(item).padEnd(width))} `;
|
|
8502
|
+
}
|
|
8440
8503
|
async function list2(options2) {
|
|
8441
8504
|
const allItems = await loadBacklog(options2.allRepos);
|
|
8442
8505
|
const items2 = filterItems(allItems, options2);
|
|
@@ -8444,15 +8507,10 @@ async function list2(options2) {
|
|
|
8444
8507
|
console.log(chalk72.dim("Backlog is empty."));
|
|
8445
8508
|
return;
|
|
8446
8509
|
}
|
|
8447
|
-
const
|
|
8448
|
-
items2.flatMap((i) => i.origin ? [i.origin] : [])
|
|
8449
|
-
);
|
|
8450
|
-
const repoNameOf = (item) => item.origin ? labels.get(item.origin) ?? "" : "";
|
|
8451
|
-
const prefixWidth = options2.allRepos ? Math.max(0, ...items2.map((i) => repoNameOf(i).length)) : 0;
|
|
8510
|
+
const prefixOf = repoPrefixer(items2, !!options2.allRepos);
|
|
8452
8511
|
for (const item of items2) {
|
|
8453
|
-
const repoPrefix = options2.allRepos ? `${chalk72.dim(repoNameOf(item).padEnd(prefixWidth))} ` : "";
|
|
8454
8512
|
console.log(
|
|
8455
|
-
`${
|
|
8513
|
+
`${prefixOf(item)}${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk72.dim(formatItemId(item.id))} ${starMarker(item)}${item.name}${phaseLabel(item)}${dependencyLabel(item, allItems)}`
|
|
8456
8514
|
);
|
|
8457
8515
|
if (options2.verbose) {
|
|
8458
8516
|
printVerboseDetails(item);
|
|
@@ -8504,10 +8562,10 @@ import { eq as eq20 } from "drizzle-orm";
|
|
|
8504
8562
|
async function loadDependencyGraph(orm) {
|
|
8505
8563
|
const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(eq20(links.type, "depends-on"));
|
|
8506
8564
|
const graph = /* @__PURE__ */ new Map();
|
|
8507
|
-
for (const { itemId, targetId } of rows) {
|
|
8508
|
-
const bucket = graph.get(
|
|
8565
|
+
for (const { itemId: itemId2, targetId } of rows) {
|
|
8566
|
+
const bucket = graph.get(itemId2);
|
|
8509
8567
|
if (bucket) bucket.push(targetId);
|
|
8510
|
-
else graph.set(
|
|
8568
|
+
else graph.set(itemId2, [targetId]);
|
|
8511
8569
|
}
|
|
8512
8570
|
return graph;
|
|
8513
8571
|
}
|
|
@@ -8520,7 +8578,9 @@ function validateLinkTarget(fromItem, fromNum, toNum, linkType) {
|
|
|
8520
8578
|
);
|
|
8521
8579
|
if (duplicate) {
|
|
8522
8580
|
console.log(
|
|
8523
|
-
chalk73.yellow(
|
|
8581
|
+
chalk73.yellow(
|
|
8582
|
+
`Link already exists: ${formatItemId(fromNum)} ${linkType} ${formatItemId(toNum)}`
|
|
8583
|
+
)
|
|
8524
8584
|
);
|
|
8525
8585
|
return false;
|
|
8526
8586
|
}
|
|
@@ -8541,50 +8601,62 @@ async function createsCycle(orm, linkType, fromNum, toNum) {
|
|
|
8541
8601
|
if (linkType !== "depends-on") return false;
|
|
8542
8602
|
const graph = await loadDependencyGraph(orm);
|
|
8543
8603
|
if (!hasCycle(graph, fromNum, toNum)) return false;
|
|
8544
|
-
fail2(
|
|
8604
|
+
fail2(
|
|
8605
|
+
`Cannot add dependency: ${formatItemId(fromNum)} \u2192 ${formatItemId(toNum)} would create a cycle.`
|
|
8606
|
+
);
|
|
8545
8607
|
return true;
|
|
8546
8608
|
}
|
|
8547
8609
|
async function link(fromId, toId, opts) {
|
|
8548
8610
|
const linkType = parseLinkType(opts.type);
|
|
8549
8611
|
if (!linkType) return;
|
|
8550
|
-
const fromNum =
|
|
8551
|
-
const toNum =
|
|
8612
|
+
const fromNum = parseItemId(fromId);
|
|
8613
|
+
const toNum = parseItemId(toId);
|
|
8552
8614
|
if (fromNum === toNum) return void fail2("Cannot link an item to itself.");
|
|
8615
|
+
const from = formatItemId(fromNum);
|
|
8616
|
+
const to = formatItemId(toNum);
|
|
8553
8617
|
const { orm } = await getReady();
|
|
8554
8618
|
const fromItem = await loadItem(orm, fromNum);
|
|
8555
|
-
if (!fromItem) return void fail2(`Item
|
|
8619
|
+
if (!fromItem) return void fail2(`Item ${from} not found.`);
|
|
8556
8620
|
const toItem = await loadItem(orm, toNum);
|
|
8557
|
-
if (!toItem) return void fail2(`Item
|
|
8621
|
+
if (!toItem) return void fail2(`Item ${to} not found.`);
|
|
8558
8622
|
if (!validateLinkTarget(fromItem, fromNum, toNum, linkType)) return;
|
|
8559
8623
|
if (await createsCycle(orm, linkType, fromNum, toNum)) return;
|
|
8560
8624
|
await orm.insert(links).values({ itemId: fromNum, type: linkType, targetId: toNum });
|
|
8561
|
-
console.log(
|
|
8562
|
-
chalk74.green(`Linked #${fromNum} ${linkType} #${toNum} (${toItem.name})`)
|
|
8563
|
-
);
|
|
8625
|
+
console.log(chalk74.green(`Linked ${from} ${linkType} ${to} (${toItem.name})`));
|
|
8564
8626
|
}
|
|
8565
8627
|
|
|
8566
8628
|
// src/commands/backlog/unlink.ts
|
|
8567
8629
|
import chalk75 from "chalk";
|
|
8568
8630
|
import { and as and7, eq as eq21 } from "drizzle-orm";
|
|
8569
8631
|
async function unlink(fromId, toId) {
|
|
8570
|
-
const fromNum =
|
|
8571
|
-
const toNum =
|
|
8632
|
+
const fromNum = parseItemId(fromId);
|
|
8633
|
+
const toNum = parseItemId(toId);
|
|
8572
8634
|
const { orm } = await getReady();
|
|
8573
8635
|
const fromItem = await loadItem(orm, fromNum);
|
|
8574
8636
|
if (!fromItem) {
|
|
8575
|
-
console.log(chalk75.red(`Item
|
|
8637
|
+
console.log(chalk75.red(`Item ${formatItemId(fromNum)} not found.`));
|
|
8576
8638
|
return;
|
|
8577
8639
|
}
|
|
8578
8640
|
if (!fromItem.links || fromItem.links.length === 0) {
|
|
8579
|
-
console.log(
|
|
8641
|
+
console.log(
|
|
8642
|
+
chalk75.yellow(`No links found on item ${formatItemId(fromNum)}.`)
|
|
8643
|
+
);
|
|
8580
8644
|
return;
|
|
8581
8645
|
}
|
|
8582
8646
|
if (!fromItem.links.some((l) => l.targetId === toNum)) {
|
|
8583
|
-
console.log(
|
|
8647
|
+
console.log(
|
|
8648
|
+
chalk75.yellow(
|
|
8649
|
+
`No link from ${formatItemId(fromNum)} to ${formatItemId(toNum)} found.`
|
|
8650
|
+
)
|
|
8651
|
+
);
|
|
8584
8652
|
return;
|
|
8585
8653
|
}
|
|
8586
8654
|
await orm.delete(links).where(and7(eq21(links.itemId, fromNum), eq21(links.targetId, toNum)));
|
|
8587
|
-
console.log(
|
|
8655
|
+
console.log(
|
|
8656
|
+
chalk75.green(
|
|
8657
|
+
`Removed link from ${formatItemId(fromNum)} to ${formatItemId(toNum)}.`
|
|
8658
|
+
)
|
|
8659
|
+
);
|
|
8588
8660
|
}
|
|
8589
8661
|
|
|
8590
8662
|
// src/commands/backlog/registerLinkCommands.ts
|
|
@@ -8679,7 +8751,7 @@ function registerMoveRepoCommand(cmd) {
|
|
|
8679
8751
|
// src/commands/backlog/registerNextCommand.ts
|
|
8680
8752
|
function registerNextCommand(cmd) {
|
|
8681
8753
|
cmd.command("next").argument("[id]", "Backlog item ID to run first").description("Pick and run the next backlog item, or open /draft if none").option("-w, --write", "Run Claude with auto permission mode (default)").option("--no-write", "Run Claude without auto permission mode").option("--once", "Exit after the first completed item run").action(
|
|
8682
|
-
(
|
|
8754
|
+
(id, opts) => next({ allowEdits: opts.write !== false, once: opts.once }, id)
|
|
8683
8755
|
);
|
|
8684
8756
|
}
|
|
8685
8757
|
|
|
@@ -8700,9 +8772,14 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
8700
8772
|
import chalk79 from "chalk";
|
|
8701
8773
|
|
|
8702
8774
|
// src/commands/backlog/surfaceCreatedItem.ts
|
|
8703
|
-
async function surfaceCreatedItem(slashCommand,
|
|
8704
|
-
|
|
8705
|
-
|
|
8775
|
+
async function surfaceCreatedItem(slashCommand, id) {
|
|
8776
|
+
let numericId;
|
|
8777
|
+
try {
|
|
8778
|
+
numericId = parseItemId(id);
|
|
8779
|
+
} catch (error) {
|
|
8780
|
+
if (error instanceof InvalidItemIdError) return;
|
|
8781
|
+
throw error;
|
|
8782
|
+
}
|
|
8706
8783
|
const { orm } = await getReady();
|
|
8707
8784
|
const item = await loadItem(orm, numericId);
|
|
8708
8785
|
if (!item) return;
|
|
@@ -8716,33 +8793,34 @@ async function surfaceCreatedItem(slashCommand, id2) {
|
|
|
8716
8793
|
|
|
8717
8794
|
// src/commands/backlog/tryRunById.ts
|
|
8718
8795
|
import chalk78 from "chalk";
|
|
8719
|
-
async function tryRunById(
|
|
8720
|
-
const numericId =
|
|
8796
|
+
async function tryRunById(id, options2) {
|
|
8797
|
+
const numericId = parseItemId(id);
|
|
8798
|
+
const label2 = formatItemId(numericId);
|
|
8721
8799
|
const { orm } = await getReady();
|
|
8722
|
-
const item =
|
|
8800
|
+
const item = await loadItem(orm, numericId);
|
|
8723
8801
|
if (!item) {
|
|
8724
|
-
console.log(chalk78.red(`Item
|
|
8802
|
+
console.log(chalk78.red(`Item ${label2} not found.`));
|
|
8725
8803
|
return false;
|
|
8726
8804
|
}
|
|
8727
8805
|
if (item.status === "done") {
|
|
8728
|
-
console.log(chalk78.red(`Item
|
|
8806
|
+
console.log(chalk78.red(`Item ${label2} is already done.`));
|
|
8729
8807
|
return false;
|
|
8730
8808
|
}
|
|
8731
8809
|
if (item.status === "wontdo") {
|
|
8732
|
-
console.log(chalk78.red(`Item
|
|
8810
|
+
console.log(chalk78.red(`Item ${label2} is marked won't do.`));
|
|
8733
8811
|
return false;
|
|
8734
8812
|
}
|
|
8735
8813
|
const hasDeps = (item.links ?? []).some((l) => l.type === "depends-on");
|
|
8736
8814
|
if (hasDeps && isBlocked(item, await loadItemSummaries(orm, getOrigin()))) {
|
|
8737
8815
|
console.log(
|
|
8738
|
-
chalk78.red(`Item
|
|
8816
|
+
chalk78.red(`Item ${label2} is blocked by unresolved dependencies.`)
|
|
8739
8817
|
);
|
|
8740
8818
|
return false;
|
|
8741
8819
|
}
|
|
8742
8820
|
console.log(chalk78.bold(`
|
|
8743
|
-
Running backlog item
|
|
8821
|
+
Running backlog item ${label2}...
|
|
8744
8822
|
`));
|
|
8745
|
-
await run2(
|
|
8823
|
+
await run2(id, options2);
|
|
8746
8824
|
return true;
|
|
8747
8825
|
}
|
|
8748
8826
|
|
|
@@ -8803,8 +8881,10 @@ async function pickItemForRefine() {
|
|
|
8803
8881
|
}
|
|
8804
8882
|
if (active.length === 1) {
|
|
8805
8883
|
const item = active[0];
|
|
8806
|
-
console.log(
|
|
8807
|
-
|
|
8884
|
+
console.log(
|
|
8885
|
+
chalk80.bold(`Auto-selecting item ${formatItemId(item.id)}: ${item.name}`)
|
|
8886
|
+
);
|
|
8887
|
+
return formatItemId(item.id);
|
|
8808
8888
|
}
|
|
8809
8889
|
const { selected } = await exitOnCancel(
|
|
8810
8890
|
enquirer7.prompt({
|
|
@@ -8812,18 +8892,18 @@ async function pickItemForRefine() {
|
|
|
8812
8892
|
name: "selected",
|
|
8813
8893
|
message: "Choose a backlog item to refine:",
|
|
8814
8894
|
choices: active.map((item) => ({
|
|
8815
|
-
name: `${typeLabel(item.type)}
|
|
8895
|
+
name: `${typeLabel(item.type)} ${formatItemId(item.id)}: ${item.name}`
|
|
8816
8896
|
}))
|
|
8817
8897
|
})
|
|
8818
8898
|
);
|
|
8819
|
-
return selected.match(
|
|
8899
|
+
return selected.match(/(a\d+):/)?.[1] ?? "";
|
|
8820
8900
|
}
|
|
8821
|
-
async function refine(
|
|
8822
|
-
const
|
|
8823
|
-
if (!
|
|
8824
|
-
const numericId =
|
|
8825
|
-
const item =
|
|
8826
|
-
await launchMode(`refine ${
|
|
8901
|
+
async function refine(id, options2) {
|
|
8902
|
+
const itemId2 = id ?? await pickItemForRefine();
|
|
8903
|
+
if (!itemId2) return;
|
|
8904
|
+
const numericId = parseItemId(itemId2);
|
|
8905
|
+
const item = await loadItem((await getReady()).orm, numericId);
|
|
8906
|
+
await launchMode(`refine ${itemId2}`, {
|
|
8827
8907
|
...options2,
|
|
8828
8908
|
itemId: item ? numericId : void 0,
|
|
8829
8909
|
itemName: item?.name
|
|
@@ -8833,20 +8913,17 @@ async function refine(id2, options2) {
|
|
|
8833
8913
|
// src/commands/backlog/registerRefineCommand.ts
|
|
8834
8914
|
function registerRefineCommand(cmd) {
|
|
8835
8915
|
cmd.command("refine").argument("[id]", "Backlog item ID").description("Alias for refine").option("--once", "Exit when the initial task completes").action(
|
|
8836
|
-
(
|
|
8916
|
+
(id, opts) => refine(id, { once: opts.once })
|
|
8837
8917
|
);
|
|
8838
8918
|
}
|
|
8839
8919
|
|
|
8840
8920
|
// src/commands/backlog/rewindPhase.ts
|
|
8841
8921
|
import chalk81 from "chalk";
|
|
8842
|
-
async function rewindPhase(
|
|
8922
|
+
async function rewindPhase(id, phase, opts) {
|
|
8843
8923
|
const phaseNumber = Number.parseInt(phase, 10);
|
|
8844
|
-
const
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
console.log(chalk81.red(`Item #${id2} not found.`));
|
|
8848
|
-
return;
|
|
8849
|
-
}
|
|
8924
|
+
const found = await findOneItem(id);
|
|
8925
|
+
if (!found) return;
|
|
8926
|
+
const { orm, item } = found;
|
|
8850
8927
|
const result = await rewindItemToPhase(orm, item, phaseNumber, opts.reason);
|
|
8851
8928
|
if (!result.ok) {
|
|
8852
8929
|
console.log(chalk81.red(result.error));
|
|
@@ -8855,7 +8932,7 @@ async function rewindPhase(id2, phase, opts) {
|
|
|
8855
8932
|
}
|
|
8856
8933
|
console.log(
|
|
8857
8934
|
chalk81.green(
|
|
8858
|
-
`Rewound item
|
|
8935
|
+
`Rewound item ${formatItemId(item.id)} to phase ${phaseNumber} (${result.phaseName}).`
|
|
8859
8936
|
)
|
|
8860
8937
|
);
|
|
8861
8938
|
}
|
|
@@ -8871,9 +8948,9 @@ function registerRunCommand(cmd) {
|
|
|
8871
8948
|
"--resume-session <id>",
|
|
8872
8949
|
"Resume an interrupted Claude session for the current phase (used by the sessions daemon on restart)"
|
|
8873
8950
|
).action(
|
|
8874
|
-
async (
|
|
8951
|
+
async (id, opts) => {
|
|
8875
8952
|
if (!opts.resumeSession) pullIfConfigured();
|
|
8876
|
-
await run2(
|
|
8953
|
+
await run2(id, {
|
|
8877
8954
|
allowEdits: opts.write !== false,
|
|
8878
8955
|
resumeSessionId: opts.resumeSession
|
|
8879
8956
|
});
|
|
@@ -8897,7 +8974,7 @@ async function search(query) {
|
|
|
8897
8974
|
);
|
|
8898
8975
|
for (const item of items2) {
|
|
8899
8976
|
console.log(
|
|
8900
|
-
`${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk82.dim(
|
|
8977
|
+
`${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk82.dim(formatItemId(item.id))} ${item.name}`
|
|
8901
8978
|
);
|
|
8902
8979
|
}
|
|
8903
8980
|
}
|
|
@@ -8909,20 +8986,22 @@ function registerSearchCommand(cmd) {
|
|
|
8909
8986
|
|
|
8910
8987
|
// src/commands/backlog/delete/index.ts
|
|
8911
8988
|
import chalk83 from "chalk";
|
|
8912
|
-
async function del(
|
|
8913
|
-
const name = await removeItem(
|
|
8989
|
+
async function del(id) {
|
|
8990
|
+
const name = await removeItem(id);
|
|
8914
8991
|
if (name) {
|
|
8915
|
-
console.log(
|
|
8992
|
+
console.log(
|
|
8993
|
+
chalk83.green(`Deleted item ${formatItemId(parseItemId(id))}: ${name}`)
|
|
8994
|
+
);
|
|
8916
8995
|
}
|
|
8917
8996
|
}
|
|
8918
8997
|
|
|
8919
8998
|
// src/commands/backlog/done/index.ts
|
|
8920
8999
|
import chalk84 from "chalk";
|
|
8921
|
-
async function done(
|
|
8922
|
-
const found = await findOneItem(
|
|
9000
|
+
async function done(id, summary) {
|
|
9001
|
+
const found = await findOneItem(id);
|
|
8923
9002
|
if (!found) return;
|
|
8924
9003
|
const { orm, item } = found;
|
|
8925
|
-
if (!checkSubtasksComplete(
|
|
9004
|
+
if (!checkSubtasksComplete(item)) {
|
|
8926
9005
|
process.exitCode = 1;
|
|
8927
9006
|
return;
|
|
8928
9007
|
}
|
|
@@ -8932,7 +9011,7 @@ async function done(id2, summary) {
|
|
|
8932
9011
|
if (pending.length > 0) {
|
|
8933
9012
|
console.log(
|
|
8934
9013
|
chalk84.red(
|
|
8935
|
-
`Cannot complete item
|
|
9014
|
+
`Cannot complete item ${formatItemId(item.id)}: ${pending.length} pending phase(s):`
|
|
8936
9015
|
)
|
|
8937
9016
|
);
|
|
8938
9017
|
for (const phase of pending) {
|
|
@@ -8947,7 +9026,9 @@ async function done(id2, summary) {
|
|
|
8947
9026
|
const phase = item.currentPhase ?? 1;
|
|
8948
9027
|
await appendComment(orm, item.id, summary, { phase, type: "summary" });
|
|
8949
9028
|
}
|
|
8950
|
-
console.log(
|
|
9029
|
+
console.log(
|
|
9030
|
+
chalk84.green(`Completed item ${formatItemId(item.id)}: ${item.name}`)
|
|
9031
|
+
);
|
|
8951
9032
|
}
|
|
8952
9033
|
|
|
8953
9034
|
// src/commands/backlog/set-status/index.ts
|
|
@@ -8958,7 +9039,7 @@ var allowedStatuses = [
|
|
|
8958
9039
|
"done",
|
|
8959
9040
|
"wontdo"
|
|
8960
9041
|
];
|
|
8961
|
-
async function setStatusCommand(
|
|
9042
|
+
async function setStatusCommand(id, status2) {
|
|
8962
9043
|
if (!allowedStatuses.includes(status2)) {
|
|
8963
9044
|
console.log(
|
|
8964
9045
|
chalk85.red(
|
|
@@ -8968,9 +9049,13 @@ async function setStatusCommand(id2, status2) {
|
|
|
8968
9049
|
process.exitCode = 1;
|
|
8969
9050
|
return;
|
|
8970
9051
|
}
|
|
8971
|
-
const name = await setStatus(
|
|
9052
|
+
const name = await setStatus(id, status2);
|
|
8972
9053
|
if (name) {
|
|
8973
|
-
console.log(
|
|
9054
|
+
console.log(
|
|
9055
|
+
chalk85.green(
|
|
9056
|
+
`Set item ${formatItemId(parseItemId(id))} to ${status2}: ${name}`
|
|
9057
|
+
)
|
|
9058
|
+
);
|
|
8974
9059
|
}
|
|
8975
9060
|
}
|
|
8976
9061
|
|
|
@@ -8979,27 +9064,33 @@ import chalk87 from "chalk";
|
|
|
8979
9064
|
|
|
8980
9065
|
// src/commands/backlog/setStarred.ts
|
|
8981
9066
|
import chalk86 from "chalk";
|
|
8982
|
-
async function setStarred(
|
|
9067
|
+
async function setStarred(id, starred) {
|
|
8983
9068
|
const { orm } = await getReady();
|
|
8984
|
-
const
|
|
8985
|
-
|
|
9069
|
+
const numId = parseItemId(id);
|
|
9070
|
+
const name = await updateStarred(orm, numId, starred);
|
|
9071
|
+
if (name === void 0)
|
|
9072
|
+
console.log(chalk86.red(`Item ${formatItemId(numId)} not found.`));
|
|
8986
9073
|
return name;
|
|
8987
9074
|
}
|
|
8988
9075
|
|
|
8989
9076
|
// src/commands/backlog/star/index.ts
|
|
8990
|
-
async function star(
|
|
8991
|
-
const name = await setStarred(
|
|
9077
|
+
async function star(id) {
|
|
9078
|
+
const name = await setStarred(id, true);
|
|
8992
9079
|
if (name) {
|
|
8993
|
-
console.log(
|
|
9080
|
+
console.log(
|
|
9081
|
+
chalk87.green(`Starred item ${formatItemId(parseItemId(id))}: ${name}`)
|
|
9082
|
+
);
|
|
8994
9083
|
}
|
|
8995
9084
|
}
|
|
8996
9085
|
|
|
8997
9086
|
// src/commands/backlog/start/index.ts
|
|
8998
9087
|
import chalk88 from "chalk";
|
|
8999
|
-
async function start(
|
|
9000
|
-
const name = await setStatus(
|
|
9088
|
+
async function start(id) {
|
|
9089
|
+
const name = await setStatus(id, "in-progress");
|
|
9001
9090
|
if (name) {
|
|
9002
|
-
console.log(
|
|
9091
|
+
console.log(
|
|
9092
|
+
chalk88.green(`Started item ${formatItemId(parseItemId(id))}: ${name}`)
|
|
9093
|
+
);
|
|
9003
9094
|
}
|
|
9004
9095
|
}
|
|
9005
9096
|
|
|
@@ -9014,23 +9105,27 @@ async function stop() {
|
|
|
9014
9105
|
return;
|
|
9015
9106
|
}
|
|
9016
9107
|
for (const item of stopped) {
|
|
9017
|
-
console.log(
|
|
9108
|
+
console.log(
|
|
9109
|
+
chalk89.yellow(`Stopped item ${formatItemId(item.id)}: ${item.name}`)
|
|
9110
|
+
);
|
|
9018
9111
|
}
|
|
9019
9112
|
}
|
|
9020
9113
|
|
|
9021
9114
|
// src/commands/backlog/unstar/index.ts
|
|
9022
9115
|
import chalk90 from "chalk";
|
|
9023
|
-
async function unstar(
|
|
9024
|
-
const name = await setStarred(
|
|
9116
|
+
async function unstar(id) {
|
|
9117
|
+
const name = await setStarred(id, false);
|
|
9025
9118
|
if (name) {
|
|
9026
|
-
console.log(
|
|
9119
|
+
console.log(
|
|
9120
|
+
chalk90.green(`Unstarred item ${formatItemId(parseItemId(id))}: ${name}`)
|
|
9121
|
+
);
|
|
9027
9122
|
}
|
|
9028
9123
|
}
|
|
9029
9124
|
|
|
9030
9125
|
// src/commands/backlog/wontdo/index.ts
|
|
9031
9126
|
import chalk91 from "chalk";
|
|
9032
|
-
async function wontdo(
|
|
9033
|
-
const found = await findOneItem(
|
|
9127
|
+
async function wontdo(id, reason) {
|
|
9128
|
+
const found = await findOneItem(id);
|
|
9034
9129
|
if (!found) return;
|
|
9035
9130
|
const { orm, item } = found;
|
|
9036
9131
|
await updateStatus(orm, item.id, "wontdo");
|
|
@@ -9038,7 +9133,9 @@ async function wontdo(id2, reason) {
|
|
|
9038
9133
|
const phase = item.currentPhase ?? 1;
|
|
9039
9134
|
await appendComment(orm, item.id, reason, { phase, type: "summary" });
|
|
9040
9135
|
}
|
|
9041
|
-
console.log(
|
|
9136
|
+
console.log(
|
|
9137
|
+
chalk91.red(`Won't do item ${formatItemId(item.id)}: ${item.name}`)
|
|
9138
|
+
);
|
|
9042
9139
|
}
|
|
9043
9140
|
|
|
9044
9141
|
// src/commands/backlog/registerStatusCommands.ts
|
|
@@ -9057,7 +9154,7 @@ function registerStatusCommands(cmd) {
|
|
|
9057
9154
|
|
|
9058
9155
|
// src/commands/backlog/addSubtask.ts
|
|
9059
9156
|
import chalk92 from "chalk";
|
|
9060
|
-
async function addSubtask(
|
|
9157
|
+
async function addSubtask(id, options2) {
|
|
9061
9158
|
const title = options2.title?.trim();
|
|
9062
9159
|
if (!title) {
|
|
9063
9160
|
console.log(chalk92.red("A sub-task title is required (--title)."));
|
|
@@ -9073,7 +9170,7 @@ async function addSubtask(id2, options2) {
|
|
|
9073
9170
|
process.exitCode = 1;
|
|
9074
9171
|
return;
|
|
9075
9172
|
}
|
|
9076
|
-
const found = await findOneItem(
|
|
9173
|
+
const found = await findOneItem(id);
|
|
9077
9174
|
if (!found) {
|
|
9078
9175
|
process.exitCode = 1;
|
|
9079
9176
|
return;
|
|
@@ -9081,7 +9178,9 @@ async function addSubtask(id2, options2) {
|
|
|
9081
9178
|
const { orm, item } = found;
|
|
9082
9179
|
const description = options2.desc?.replaceAll(String.raw`\n`, "\n");
|
|
9083
9180
|
await insertSubtask(orm, item.id, title, description);
|
|
9084
|
-
console.log(
|
|
9181
|
+
console.log(
|
|
9182
|
+
chalk92.green(`Added sub-task to item ${formatItemId(item.id)}: ${title}`)
|
|
9183
|
+
);
|
|
9085
9184
|
}
|
|
9086
9185
|
|
|
9087
9186
|
// src/commands/backlog/editSubtask.ts
|
|
@@ -9089,13 +9188,13 @@ import chalk94 from "chalk";
|
|
|
9089
9188
|
|
|
9090
9189
|
// src/commands/backlog/resolveSubtaskIndex.ts
|
|
9091
9190
|
import chalk93 from "chalk";
|
|
9092
|
-
function resolveSubtaskIndex(
|
|
9191
|
+
function resolveSubtaskIndex(index3, item) {
|
|
9093
9192
|
const position = Number.parseInt(index3, 10);
|
|
9094
9193
|
const subtasks = item.subtasks ?? [];
|
|
9095
9194
|
if (Number.isNaN(position) || position < 1 || position > subtasks.length) {
|
|
9096
9195
|
console.log(
|
|
9097
9196
|
chalk93.red(
|
|
9098
|
-
`Item
|
|
9197
|
+
`Item ${formatItemId(item.id)} has no sub-task ${index3}${subtasks.length > 0 ? ` (1-${subtasks.length})` : ""}.`
|
|
9099
9198
|
)
|
|
9100
9199
|
);
|
|
9101
9200
|
process.exitCode = 1;
|
|
@@ -9105,25 +9204,25 @@ function resolveSubtaskIndex(id2, index3, item) {
|
|
|
9105
9204
|
}
|
|
9106
9205
|
|
|
9107
9206
|
// src/commands/backlog/findSubtask.ts
|
|
9108
|
-
async function findSubtask(
|
|
9109
|
-
const found = await findOneItem(
|
|
9207
|
+
async function findSubtask(id, index3) {
|
|
9208
|
+
const found = await findOneItem(id);
|
|
9110
9209
|
if (!found) {
|
|
9111
9210
|
process.exitCode = 1;
|
|
9112
9211
|
return void 0;
|
|
9113
9212
|
}
|
|
9114
|
-
const idx = resolveSubtaskIndex(
|
|
9213
|
+
const idx = resolveSubtaskIndex(index3, found.item);
|
|
9115
9214
|
if (idx === void 0) return void 0;
|
|
9116
9215
|
return { orm: found.orm, item: found.item, idx };
|
|
9117
9216
|
}
|
|
9118
9217
|
|
|
9119
9218
|
// src/commands/backlog/updateSubtask.ts
|
|
9120
9219
|
import { and as and9, eq as eq25 } from "drizzle-orm";
|
|
9121
|
-
async function updateSubtask(orm,
|
|
9220
|
+
async function updateSubtask(orm, itemId2, idx, fields) {
|
|
9122
9221
|
const set = {};
|
|
9123
9222
|
if (fields.title !== void 0) set.title = fields.title;
|
|
9124
9223
|
if (fields.description !== void 0) set.description = fields.description;
|
|
9125
9224
|
if (fields.status !== void 0) set.status = fields.status;
|
|
9126
|
-
const [row] = await orm.update(itemSubtasks).set(set).where(and9(eq25(itemSubtasks.itemId,
|
|
9225
|
+
const [row] = await orm.update(itemSubtasks).set(set).where(and9(eq25(itemSubtasks.itemId, itemId2), eq25(itemSubtasks.idx, idx))).returning({ title: itemSubtasks.title });
|
|
9127
9226
|
return row?.title;
|
|
9128
9227
|
}
|
|
9129
9228
|
|
|
@@ -9156,8 +9255,8 @@ function buildUpdate(options2) {
|
|
|
9156
9255
|
}
|
|
9157
9256
|
return fields;
|
|
9158
9257
|
}
|
|
9159
|
-
async function editSubtask(
|
|
9160
|
-
const found = await findSubtask(
|
|
9258
|
+
async function editSubtask(id, index3, options2) {
|
|
9259
|
+
const found = await findSubtask(id, index3);
|
|
9161
9260
|
if (!found) return;
|
|
9162
9261
|
const fields = buildUpdate(options2);
|
|
9163
9262
|
if (typeof fields === "string") {
|
|
@@ -9168,7 +9267,9 @@ async function editSubtask(id2, index3, options2) {
|
|
|
9168
9267
|
const { orm, item, idx } = found;
|
|
9169
9268
|
const title = await updateSubtask(orm, item.id, idx, fields);
|
|
9170
9269
|
console.log(
|
|
9171
|
-
chalk94.green(
|
|
9270
|
+
chalk94.green(
|
|
9271
|
+
`Updated sub-task ${idx + 1} of item ${formatItemId(item.id)}: ${title}`
|
|
9272
|
+
)
|
|
9172
9273
|
);
|
|
9173
9274
|
}
|
|
9174
9275
|
|
|
@@ -9177,34 +9278,36 @@ import chalk95 from "chalk";
|
|
|
9177
9278
|
|
|
9178
9279
|
// src/commands/backlog/deleteSubtask.ts
|
|
9179
9280
|
import { and as and10, asc as asc6, eq as eq26 } from "drizzle-orm";
|
|
9180
|
-
async function deleteSubtask(orm,
|
|
9181
|
-
const [row] = await orm.delete(itemSubtasks).where(and10(eq26(itemSubtasks.itemId,
|
|
9281
|
+
async function deleteSubtask(orm, itemId2, idx) {
|
|
9282
|
+
const [row] = await orm.delete(itemSubtasks).where(and10(eq26(itemSubtasks.itemId, itemId2), eq26(itemSubtasks.idx, idx))).returning({ title: itemSubtasks.title });
|
|
9182
9283
|
if (!row) return void 0;
|
|
9183
|
-
const remaining = await orm.select({ idx: itemSubtasks.idx }).from(itemSubtasks).where(eq26(itemSubtasks.itemId,
|
|
9284
|
+
const remaining = await orm.select({ idx: itemSubtasks.idx }).from(itemSubtasks).where(eq26(itemSubtasks.itemId, itemId2)).orderBy(asc6(itemSubtasks.idx));
|
|
9184
9285
|
for (let i = 0; i < remaining.length; i++) {
|
|
9185
9286
|
const oldIdx = remaining[i].idx;
|
|
9186
9287
|
if (oldIdx === i) continue;
|
|
9187
9288
|
await orm.update(itemSubtasks).set({ idx: i }).where(
|
|
9188
|
-
and10(eq26(itemSubtasks.itemId,
|
|
9289
|
+
and10(eq26(itemSubtasks.itemId, itemId2), eq26(itemSubtasks.idx, oldIdx))
|
|
9189
9290
|
);
|
|
9190
9291
|
}
|
|
9191
9292
|
return row.title;
|
|
9192
9293
|
}
|
|
9193
9294
|
|
|
9194
9295
|
// src/commands/backlog/removeSubtask.ts
|
|
9195
|
-
async function removeSubtask(
|
|
9196
|
-
const found = await findSubtask(
|
|
9296
|
+
async function removeSubtask(id, index3) {
|
|
9297
|
+
const found = await findSubtask(id, index3);
|
|
9197
9298
|
if (!found) return;
|
|
9198
9299
|
const { orm, item, idx } = found;
|
|
9199
9300
|
const title = await deleteSubtask(orm, item.id, idx);
|
|
9200
9301
|
console.log(
|
|
9201
|
-
chalk95.green(
|
|
9302
|
+
chalk95.green(
|
|
9303
|
+
`Removed sub-task ${idx + 1} of item ${formatItemId(item.id)}: ${title}`
|
|
9304
|
+
)
|
|
9202
9305
|
);
|
|
9203
9306
|
}
|
|
9204
9307
|
|
|
9205
9308
|
// src/commands/backlog/subtaskStatus.ts
|
|
9206
9309
|
import chalk96 from "chalk";
|
|
9207
|
-
async function subtaskStatus(
|
|
9310
|
+
async function subtaskStatus(id, index3, status2) {
|
|
9208
9311
|
if (!validSubtaskStatuses.includes(status2)) {
|
|
9209
9312
|
console.log(
|
|
9210
9313
|
chalk96.red(
|
|
@@ -9214,7 +9317,7 @@ async function subtaskStatus(id2, index3, status2) {
|
|
|
9214
9317
|
process.exitCode = 1;
|
|
9215
9318
|
return;
|
|
9216
9319
|
}
|
|
9217
|
-
const found = await findSubtask(
|
|
9320
|
+
const found = await findSubtask(id, index3);
|
|
9218
9321
|
if (!found) return;
|
|
9219
9322
|
const { orm, item, idx } = found;
|
|
9220
9323
|
const title = await updateSubtaskStatus(
|
|
@@ -9225,7 +9328,7 @@ async function subtaskStatus(id2, index3, status2) {
|
|
|
9225
9328
|
);
|
|
9226
9329
|
console.log(
|
|
9227
9330
|
chalk96.green(
|
|
9228
|
-
`Set sub-task ${idx + 1} of item
|
|
9331
|
+
`Set sub-task ${idx + 1} of item ${formatItemId(item.id)} to ${status2}: ${title}`
|
|
9229
9332
|
)
|
|
9230
9333
|
);
|
|
9231
9334
|
}
|
|
@@ -9259,13 +9362,13 @@ import { and as and12, asc as asc8, eq as eq28 } from "drizzle-orm";
|
|
|
9259
9362
|
|
|
9260
9363
|
// src/commands/backlog/reindexPhases.ts
|
|
9261
9364
|
import { and as and11, asc as asc7, count as count5, eq as eq27 } from "drizzle-orm";
|
|
9262
|
-
async function reindexPhases(db,
|
|
9263
|
-
const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(eq27(planPhases.itemId,
|
|
9365
|
+
async function reindexPhases(db, itemId2) {
|
|
9366
|
+
const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(eq27(planPhases.itemId, itemId2)).orderBy(asc7(planPhases.idx));
|
|
9264
9367
|
for (let i = 0; i < remaining.length; i++) {
|
|
9265
9368
|
const oldIdx = remaining[i].idx;
|
|
9266
9369
|
if (oldIdx === i) continue;
|
|
9267
|
-
await db.update(planTasks).set({ phaseIdx: i }).where(and11(eq27(planTasks.itemId,
|
|
9268
|
-
await db.update(planPhases).set({ idx: i }).where(and11(eq27(planPhases.itemId,
|
|
9370
|
+
await db.update(planTasks).set({ phaseIdx: i }).where(and11(eq27(planTasks.itemId, itemId2), eq27(planTasks.phaseIdx, oldIdx)));
|
|
9371
|
+
await db.update(planPhases).set({ idx: i }).where(and11(eq27(planPhases.itemId, itemId2), eq27(planPhases.idx, oldIdx)));
|
|
9269
9372
|
}
|
|
9270
9373
|
}
|
|
9271
9374
|
async function adjustCurrentPhase(db, item, removedIdx) {
|
|
@@ -9283,18 +9386,18 @@ async function adjustCurrentPhase(db, item, removedIdx) {
|
|
|
9283
9386
|
}
|
|
9284
9387
|
|
|
9285
9388
|
// src/commands/backlog/reorderPhaseRows.ts
|
|
9286
|
-
async function reorderPhaseRows(orm,
|
|
9389
|
+
async function reorderPhaseRows(orm, itemId2, fromIdx, toIdx) {
|
|
9287
9390
|
await orm.transaction(async (tx) => {
|
|
9288
|
-
const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(and12(eq28(planPhases.itemId,
|
|
9289
|
-
const tasks = await tx.select({ idx: planTasks.idx, task: planTasks.task }).from(planTasks).where(and12(eq28(planTasks.itemId,
|
|
9391
|
+
const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(and12(eq28(planPhases.itemId, itemId2), eq28(planPhases.idx, fromIdx)));
|
|
9392
|
+
const tasks = await tx.select({ idx: planTasks.idx, task: planTasks.task }).from(planTasks).where(and12(eq28(planTasks.itemId, itemId2), eq28(planTasks.phaseIdx, fromIdx))).orderBy(asc8(planTasks.idx));
|
|
9290
9393
|
await tx.delete(planTasks).where(
|
|
9291
|
-
and12(eq28(planTasks.itemId,
|
|
9394
|
+
and12(eq28(planTasks.itemId, itemId2), eq28(planTasks.phaseIdx, fromIdx))
|
|
9292
9395
|
);
|
|
9293
|
-
await tx.delete(planPhases).where(and12(eq28(planPhases.itemId,
|
|
9294
|
-
await reindexPhases(tx,
|
|
9295
|
-
await shiftPhasesUp(tx,
|
|
9396
|
+
await tx.delete(planPhases).where(and12(eq28(planPhases.itemId, itemId2), eq28(planPhases.idx, fromIdx)));
|
|
9397
|
+
await reindexPhases(tx, itemId2);
|
|
9398
|
+
await shiftPhasesUp(tx, itemId2, toIdx);
|
|
9296
9399
|
await tx.insert(planPhases).values({
|
|
9297
|
-
itemId,
|
|
9400
|
+
itemId: itemId2,
|
|
9298
9401
|
idx: toIdx,
|
|
9299
9402
|
name: phaseRow.name,
|
|
9300
9403
|
manualChecks: phaseRow.manualChecks
|
|
@@ -9302,7 +9405,7 @@ async function reorderPhaseRows(orm, itemId, fromIdx, toIdx) {
|
|
|
9302
9405
|
if (tasks.length) {
|
|
9303
9406
|
await tx.insert(planTasks).values(
|
|
9304
9407
|
tasks.map((t) => ({
|
|
9305
|
-
itemId,
|
|
9408
|
+
itemId: itemId2,
|
|
9306
9409
|
phaseIdx: toIdx,
|
|
9307
9410
|
idx: t.idx,
|
|
9308
9411
|
task: t.task
|
|
@@ -9326,19 +9429,19 @@ function toIndex(value, phaseCount) {
|
|
|
9326
9429
|
}
|
|
9327
9430
|
return pos - 1;
|
|
9328
9431
|
}
|
|
9329
|
-
async function movePhase(
|
|
9330
|
-
const found = await findOneItem(
|
|
9432
|
+
async function movePhase(id, from, to) {
|
|
9433
|
+
const found = await findOneItem(id);
|
|
9331
9434
|
if (!found) return;
|
|
9332
9435
|
const { orm, item } = found;
|
|
9333
|
-
const
|
|
9334
|
-
const [row] = await orm.select({ cnt: count6() }).from(planPhases).where(eq29(planPhases.itemId,
|
|
9436
|
+
const itemId2 = item.id;
|
|
9437
|
+
const [row] = await orm.select({ cnt: count6() }).from(planPhases).where(eq29(planPhases.itemId, itemId2));
|
|
9335
9438
|
const phaseCount = row?.cnt ?? 0;
|
|
9336
9439
|
const fromIdx = toIndex(from, phaseCount);
|
|
9337
9440
|
if (fromIdx === void 0) return;
|
|
9338
9441
|
const toIdx = toIndex(to, phaseCount);
|
|
9339
9442
|
if (toIdx === void 0) return;
|
|
9340
9443
|
if (fromIdx !== toIdx) {
|
|
9341
|
-
await reorderPhaseRows(orm,
|
|
9444
|
+
await reorderPhaseRows(orm, itemId2, fromIdx, toIdx);
|
|
9342
9445
|
}
|
|
9343
9446
|
console.log(chalk97.green(`Moved phase ${from} to position ${to}.`));
|
|
9344
9447
|
}
|
|
@@ -9348,26 +9451,26 @@ import chalk99 from "chalk";
|
|
|
9348
9451
|
|
|
9349
9452
|
// src/commands/backlog/applyPhaseUpdate.ts
|
|
9350
9453
|
import { and as and13, eq as eq30 } from "drizzle-orm";
|
|
9351
|
-
async function applyPhaseUpdate(orm,
|
|
9454
|
+
async function applyPhaseUpdate(orm, itemId2, phaseIdx, fields) {
|
|
9352
9455
|
await orm.transaction(async (tx) => {
|
|
9353
9456
|
if (fields.name) {
|
|
9354
9457
|
await tx.update(planPhases).set({ name: fields.name }).where(
|
|
9355
|
-
and13(eq30(planPhases.itemId,
|
|
9458
|
+
and13(eq30(planPhases.itemId, itemId2), eq30(planPhases.idx, phaseIdx))
|
|
9356
9459
|
);
|
|
9357
9460
|
}
|
|
9358
9461
|
if (fields.manualCheck) {
|
|
9359
9462
|
await tx.update(planPhases).set({ manualChecks: JSON.stringify(fields.manualCheck) }).where(
|
|
9360
|
-
and13(eq30(planPhases.itemId,
|
|
9463
|
+
and13(eq30(planPhases.itemId, itemId2), eq30(planPhases.idx, phaseIdx))
|
|
9361
9464
|
);
|
|
9362
9465
|
}
|
|
9363
9466
|
if (fields.task) {
|
|
9364
9467
|
await tx.delete(planTasks).where(
|
|
9365
|
-
and13(eq30(planTasks.itemId,
|
|
9468
|
+
and13(eq30(planTasks.itemId, itemId2), eq30(planTasks.phaseIdx, phaseIdx))
|
|
9366
9469
|
);
|
|
9367
9470
|
if (fields.task.length) {
|
|
9368
9471
|
await tx.insert(planTasks).values(
|
|
9369
9472
|
fields.task.map((task, i) => ({
|
|
9370
|
-
itemId,
|
|
9473
|
+
itemId: itemId2,
|
|
9371
9474
|
phaseIdx,
|
|
9372
9475
|
idx: i,
|
|
9373
9476
|
task
|
|
@@ -9381,21 +9484,23 @@ async function applyPhaseUpdate(orm, itemId, phaseIdx, fields) {
|
|
|
9381
9484
|
// src/commands/backlog/findPhase.ts
|
|
9382
9485
|
import chalk98 from "chalk";
|
|
9383
9486
|
import { and as and14, count as count7, eq as eq31 } from "drizzle-orm";
|
|
9384
|
-
async function findPhase(
|
|
9385
|
-
const found = await findOneItem(
|
|
9487
|
+
async function findPhase(id, phase) {
|
|
9488
|
+
const found = await findOneItem(id);
|
|
9386
9489
|
if (!found) return void 0;
|
|
9387
9490
|
const { orm, item } = found;
|
|
9388
|
-
const
|
|
9491
|
+
const itemId2 = item.id;
|
|
9389
9492
|
const phaseIdx = Number.parseInt(phase, 10) - 1;
|
|
9390
|
-
const [row] = await orm.select({ cnt: count7() }).from(planPhases).where(and14(eq31(planPhases.itemId,
|
|
9493
|
+
const [row] = await orm.select({ cnt: count7() }).from(planPhases).where(and14(eq31(planPhases.itemId, itemId2), eq31(planPhases.idx, phaseIdx)));
|
|
9391
9494
|
if (!row || row.cnt === 0) {
|
|
9392
9495
|
console.log(
|
|
9393
|
-
chalk98.red(
|
|
9496
|
+
chalk98.red(
|
|
9497
|
+
`Phase ${phaseIdx + 1} not found on item ${formatItemId(itemId2)}.`
|
|
9498
|
+
)
|
|
9394
9499
|
);
|
|
9395
9500
|
process.exitCode = 1;
|
|
9396
9501
|
return void 0;
|
|
9397
9502
|
}
|
|
9398
|
-
return { item, orm, itemId, phaseIdx };
|
|
9503
|
+
return { item, orm, itemId: itemId2, phaseIdx };
|
|
9399
9504
|
}
|
|
9400
9505
|
|
|
9401
9506
|
// src/commands/backlog/update/parseListIndex.ts
|
|
@@ -9516,10 +9621,10 @@ function resolvePhaseFields(options2, current) {
|
|
|
9516
9621
|
}
|
|
9517
9622
|
|
|
9518
9623
|
// src/commands/backlog/updatePhase.ts
|
|
9519
|
-
async function updatePhase(
|
|
9520
|
-
const found = await findPhase(
|
|
9624
|
+
async function updatePhase(id, phase, options2) {
|
|
9625
|
+
const found = await findPhase(id, phase);
|
|
9521
9626
|
if (!found) return;
|
|
9522
|
-
const { item, orm, itemId, phaseIdx } = found;
|
|
9627
|
+
const { item, orm, itemId: itemId2, phaseIdx } = found;
|
|
9523
9628
|
const resolved = resolvePhaseFields(options2, item.plan?.[phaseIdx]);
|
|
9524
9629
|
if (!resolved.ok) {
|
|
9525
9630
|
console.log(chalk99.red(resolved.error));
|
|
@@ -9527,7 +9632,7 @@ async function updatePhase(id2, phase, options2) {
|
|
|
9527
9632
|
return;
|
|
9528
9633
|
}
|
|
9529
9634
|
const { name, task, manualCheck } = resolved.fields;
|
|
9530
|
-
await applyPhaseUpdate(orm,
|
|
9635
|
+
await applyPhaseUpdate(orm, itemId2, phaseIdx, { name, task, manualCheck });
|
|
9531
9636
|
const fields = [
|
|
9532
9637
|
name && "name",
|
|
9533
9638
|
task && "tasks",
|
|
@@ -9535,7 +9640,7 @@ async function updatePhase(id2, phase, options2) {
|
|
|
9535
9640
|
].filter(Boolean).join(", ");
|
|
9536
9641
|
console.log(
|
|
9537
9642
|
chalk99.green(
|
|
9538
|
-
`Updated ${fields} on phase ${phaseIdx + 1} of item
|
|
9643
|
+
`Updated ${fields} on phase ${phaseIdx + 1} of item ${formatItemId(itemId2)}.`
|
|
9539
9644
|
)
|
|
9540
9645
|
);
|
|
9541
9646
|
}
|
|
@@ -9559,20 +9664,22 @@ function registerUpdatePhaseCommand(cmd) {
|
|
|
9559
9664
|
// src/commands/backlog/removePhase.ts
|
|
9560
9665
|
import chalk100 from "chalk";
|
|
9561
9666
|
import { and as and15, eq as eq32 } from "drizzle-orm";
|
|
9562
|
-
async function removePhase(
|
|
9563
|
-
const found = await findPhase(
|
|
9667
|
+
async function removePhase(id, phase) {
|
|
9668
|
+
const found = await findPhase(id, phase);
|
|
9564
9669
|
if (!found) return;
|
|
9565
|
-
const { item, orm, itemId, phaseIdx } = found;
|
|
9670
|
+
const { item, orm, itemId: itemId2, phaseIdx } = found;
|
|
9566
9671
|
await orm.transaction(async (tx) => {
|
|
9567
9672
|
await tx.delete(planTasks).where(
|
|
9568
|
-
and15(eq32(planTasks.itemId,
|
|
9673
|
+
and15(eq32(planTasks.itemId, itemId2), eq32(planTasks.phaseIdx, phaseIdx))
|
|
9569
9674
|
);
|
|
9570
|
-
await tx.delete(planPhases).where(and15(eq32(planPhases.itemId,
|
|
9571
|
-
await reindexPhases(tx,
|
|
9675
|
+
await tx.delete(planPhases).where(and15(eq32(planPhases.itemId, itemId2), eq32(planPhases.idx, phaseIdx)));
|
|
9676
|
+
await reindexPhases(tx, itemId2);
|
|
9572
9677
|
await adjustCurrentPhase(tx, item, phaseIdx);
|
|
9573
9678
|
});
|
|
9574
9679
|
console.log(
|
|
9575
|
-
chalk100.green(
|
|
9680
|
+
chalk100.green(
|
|
9681
|
+
`Removed phase ${phaseIdx + 1} from item ${formatItemId(itemId2)}.`
|
|
9682
|
+
)
|
|
9576
9683
|
);
|
|
9577
9684
|
}
|
|
9578
9685
|
|
|
@@ -9634,8 +9741,8 @@ function buildUpdateValues(options2) {
|
|
|
9634
9741
|
}
|
|
9635
9742
|
|
|
9636
9743
|
// src/commands/backlog/update/index.ts
|
|
9637
|
-
async function update(
|
|
9638
|
-
const found = await findOneItem(
|
|
9744
|
+
async function update(id, options2) {
|
|
9745
|
+
const found = await findOneItem(id);
|
|
9639
9746
|
if (!found) return;
|
|
9640
9747
|
let ac = options2.ac;
|
|
9641
9748
|
if (hasAcMutations(options2)) {
|
|
@@ -9657,9 +9764,11 @@ async function update(id2, options2) {
|
|
|
9657
9764
|
const built = buildUpdateValues({ ...options2, ac });
|
|
9658
9765
|
if (!built) return;
|
|
9659
9766
|
const { orm } = found;
|
|
9660
|
-
const
|
|
9661
|
-
await orm.update(items).set(built.set).where(eq33(items.id,
|
|
9662
|
-
console.log(
|
|
9767
|
+
const itemId2 = found.item.id;
|
|
9768
|
+
await orm.update(items).set(built.set).where(eq33(items.id, itemId2));
|
|
9769
|
+
console.log(
|
|
9770
|
+
chalk102.green(`Updated ${built.fields} on item ${formatItemId(itemId2)}.`)
|
|
9771
|
+
);
|
|
9663
9772
|
}
|
|
9664
9773
|
|
|
9665
9774
|
// src/commands/backlog/registerUpdateCommands.ts
|
|
@@ -13546,12 +13655,12 @@ async function printPendingHandovers() {
|
|
|
13546
13655
|
|
|
13547
13656
|
// src/commands/handover/recallHandover.ts
|
|
13548
13657
|
import { and as and18, desc as desc5, eq as eq36, isNull as isNull3 } from "drizzle-orm";
|
|
13549
|
-
async function recallHandover(orm, origin,
|
|
13658
|
+
async function recallHandover(orm, origin, id) {
|
|
13550
13659
|
const [row] = await orm.select().from(handovers).where(
|
|
13551
13660
|
and18(
|
|
13552
13661
|
eq36(handovers.origin, origin),
|
|
13553
13662
|
isNull3(handovers.recalledAt),
|
|
13554
|
-
...
|
|
13663
|
+
...id === void 0 ? [] : [eq36(handovers.id, id)]
|
|
13555
13664
|
)
|
|
13556
13665
|
).orderBy(desc5(handovers.createdAt), desc5(handovers.id)).limit(1);
|
|
13557
13666
|
if (!row) return void 0;
|
|
@@ -13560,12 +13669,12 @@ async function recallHandover(orm, origin, id2) {
|
|
|
13560
13669
|
}
|
|
13561
13670
|
|
|
13562
13671
|
// src/commands/handover/recallAndPrint.ts
|
|
13563
|
-
async function recallAndPrint(
|
|
13672
|
+
async function recallAndPrint(id) {
|
|
13564
13673
|
const orm = await getDb();
|
|
13565
13674
|
const content = await recallHandover(
|
|
13566
13675
|
orm,
|
|
13567
13676
|
getCurrentOrigin(process.cwd()),
|
|
13568
|
-
|
|
13677
|
+
id
|
|
13569
13678
|
);
|
|
13570
13679
|
if (content !== void 0) console.log(content);
|
|
13571
13680
|
}
|
|
@@ -13595,7 +13704,7 @@ function registerHandover(program2) {
|
|
|
13595
13704
|
cmd.command("recall").argument("[id]", "Id of a specific handover to recall").description(
|
|
13596
13705
|
"Print an unrecalled handover for this repo and mark it recalled (most recent by default, or the given id)"
|
|
13597
13706
|
).action(
|
|
13598
|
-
(
|
|
13707
|
+
(id) => recallAndPrint(id === void 0 ? void 0 : Number(id))
|
|
13599
13708
|
);
|
|
13600
13709
|
cmd.command("load").description(
|
|
13601
13710
|
"SessionStart hook: migrate any disk handovers, then advise how many unrecalled handovers exist for this repo"
|
|
@@ -13798,7 +13907,7 @@ async function reviewComments(number) {
|
|
|
13798
13907
|
var RESUME_SESSION_FLAG = "Resume an interrupted Claude session (used by the sessions daemon on restart)";
|
|
13799
13908
|
function registerNext(program2) {
|
|
13800
13909
|
program2.command("next").argument("[id]", "Backlog item ID to run first").description("Alias for backlog next").option("--once", "Exit after the first completed item run").action(
|
|
13801
|
-
(
|
|
13910
|
+
(id, opts) => next({ allowEdits: true, once: opts.once }, id)
|
|
13802
13911
|
);
|
|
13803
13912
|
}
|
|
13804
13913
|
function registerDescriptionLaunch(program2, name, slashCommand, description, alias) {
|
|
@@ -13819,7 +13928,7 @@ function registerReviewComments(program2) {
|
|
|
13819
13928
|
}
|
|
13820
13929
|
function registerRefine(program2) {
|
|
13821
13930
|
program2.command("refine").argument("[id]", "Backlog item ID").description("Launch Claude in /refine mode to refine a backlog item").option("--once", "Exit when the initial task completes").option("--resume-session <id>", RESUME_SESSION_FLAG).action(
|
|
13822
|
-
(
|
|
13931
|
+
(id, opts) => refine(id, { once: opts.once, resumeSessionId: opts.resumeSession })
|
|
13823
13932
|
);
|
|
13824
13933
|
}
|
|
13825
13934
|
function registerLaunch(program2) {
|
|
@@ -14190,8 +14299,8 @@ function parseRscRows(flight) {
|
|
|
14190
14299
|
}
|
|
14191
14300
|
function makeRscResolver(rows) {
|
|
14192
14301
|
return (ref) => {
|
|
14193
|
-
const [
|
|
14194
|
-
let v = rows[
|
|
14302
|
+
const [id, ...path58] = ref.slice(1).replace(/^[L@]/, "").split(":");
|
|
14303
|
+
let v = rows[id];
|
|
14195
14304
|
for (const key of path58) {
|
|
14196
14305
|
if (v == null || typeof v !== "object") return void 0;
|
|
14197
14306
|
v = v[key];
|
|
@@ -14280,9 +14389,9 @@ function activityPermalink(urn) {
|
|
|
14280
14389
|
// src/commands/netcap/activityUrnDate.ts
|
|
14281
14390
|
function activityUrnDate(urn) {
|
|
14282
14391
|
if (!urn) return void 0;
|
|
14283
|
-
const
|
|
14284
|
-
if (!
|
|
14285
|
-
return new Date(Number(BigInt(
|
|
14392
|
+
const id = urn.split(":").pop();
|
|
14393
|
+
if (!id || !/^\d+$/.test(id)) return void 0;
|
|
14394
|
+
return new Date(Number(BigInt(id) >> 22n)).toISOString();
|
|
14286
14395
|
}
|
|
14287
14396
|
|
|
14288
14397
|
// src/commands/netcap/dedupeLinks.ts
|
|
@@ -14379,7 +14488,7 @@ function findAuthorSlug(flight) {
|
|
|
14379
14488
|
}
|
|
14380
14489
|
function findCommentaryRows(rows) {
|
|
14381
14490
|
return Object.keys(rows).filter(
|
|
14382
|
-
(
|
|
14491
|
+
(id) => /"componentkey":"feed-commentary_/.test(JSON.stringify(rows[id]))
|
|
14383
14492
|
);
|
|
14384
14493
|
}
|
|
14385
14494
|
function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
|
|
@@ -14387,7 +14496,7 @@ function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
|
|
|
14387
14496
|
const resolve18 = makeRscResolver(rows);
|
|
14388
14497
|
const mentionMap = buildMentionMap(rows, resolve18);
|
|
14389
14498
|
const posts = [];
|
|
14390
|
-
for (const
|
|
14499
|
+
for (const id of findCommentaryRows(rows)) {
|
|
14391
14500
|
const raw = {
|
|
14392
14501
|
text: [],
|
|
14393
14502
|
hashtags: [],
|
|
@@ -14395,7 +14504,7 @@ function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
|
|
|
14395
14504
|
links: [],
|
|
14396
14505
|
related: []
|
|
14397
14506
|
};
|
|
14398
|
-
walkPostRow(rows[
|
|
14507
|
+
walkPostRow(rows[id], resolve18, raw);
|
|
14399
14508
|
const post = buildPost(raw, mentionMap, author);
|
|
14400
14509
|
if (post) posts.push(post);
|
|
14401
14510
|
}
|
|
@@ -14827,8 +14936,8 @@ function assertThreadCreated(stdout) {
|
|
|
14827
14936
|
} catch {
|
|
14828
14937
|
throw new Error(`GitHub returned an unparseable response: ${stdout}`);
|
|
14829
14938
|
}
|
|
14830
|
-
const
|
|
14831
|
-
if (typeof
|
|
14939
|
+
const id = parsed.data?.addPullRequestReviewThread?.thread?.id;
|
|
14940
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
14832
14941
|
throw new Error(
|
|
14833
14942
|
"GitHub did not create a review thread (no thread id returned); the line is likely outside the PR diff."
|
|
14834
14943
|
);
|
|
@@ -15797,9 +15906,9 @@ function searchItems(search2) {
|
|
|
15797
15906
|
const lower = search2.toLowerCase();
|
|
15798
15907
|
return items2.filter((i) => i.title.toLowerCase().includes(lower));
|
|
15799
15908
|
}
|
|
15800
|
-
function getItemFields(
|
|
15909
|
+
function getItemFields(itemId2) {
|
|
15801
15910
|
try {
|
|
15802
|
-
const item = JSON.parse(opExec(`item get "${
|
|
15911
|
+
const item = JSON.parse(opExec(`item get "${itemId2}" --format=json`));
|
|
15803
15912
|
return item.fields.filter((f) => f.reference && f.label);
|
|
15804
15913
|
} catch {
|
|
15805
15914
|
console.error(chalk154.red("Failed to get item details from 1Password."));
|
|
@@ -15824,11 +15933,11 @@ async function selectOpSecret(searchTerm) {
|
|
|
15824
15933
|
console.error(chalk155.red(`No items found matching "${search2}".`));
|
|
15825
15934
|
process.exit(1);
|
|
15826
15935
|
}
|
|
15827
|
-
const
|
|
15936
|
+
const itemId2 = await selectOne(
|
|
15828
15937
|
"Select item:",
|
|
15829
15938
|
items2.map((i) => ({ name: `${i.title} (${i.vault.name})`, value: i.id }))
|
|
15830
15939
|
);
|
|
15831
|
-
const fields = getItemFields(
|
|
15940
|
+
const fields = getItemFields(itemId2);
|
|
15832
15941
|
if (fields.length === 0) {
|
|
15833
15942
|
console.error(chalk155.red("No fields with references found on this item."));
|
|
15834
15943
|
process.exit(1);
|
|
@@ -16369,9 +16478,9 @@ function addImportPreservingSuppressions(sourceFile, structure) {
|
|
|
16369
16478
|
import { SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
16370
16479
|
function collectReferencedNames(sourceFile) {
|
|
16371
16480
|
const names = /* @__PURE__ */ new Set();
|
|
16372
|
-
for (const
|
|
16373
|
-
if (
|
|
16374
|
-
names.add(
|
|
16481
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind3.Identifier)) {
|
|
16482
|
+
if (id.getFirstAncestorByKind(SyntaxKind3.ImportDeclaration)) continue;
|
|
16483
|
+
names.add(id.getText());
|
|
16375
16484
|
}
|
|
16376
16485
|
return names;
|
|
16377
16486
|
}
|
|
@@ -16422,7 +16531,7 @@ function updateImporters(functionName, sourceFile, importers) {
|
|
|
16422
16531
|
|
|
16423
16532
|
// src/commands/refactor/extract/applyExtraction.ts
|
|
16424
16533
|
function isNameReferencedInSource(sourceFile, name) {
|
|
16425
|
-
return sourceFile.getDescendantsOfKind(SyntaxKind4.Identifier).some((
|
|
16534
|
+
return sourceFile.getDescendantsOfKind(SyntaxKind4.Identifier).some((id) => id.getText() === name);
|
|
16426
16535
|
}
|
|
16427
16536
|
async function applyExtraction(functionName, sourceFile, destPath, plan2, project) {
|
|
16428
16537
|
project.createSourceFile(destPath, plan2.destContent, { overwrite: false });
|
|
@@ -16506,8 +16615,8 @@ function collectPrivateFunctions(target, fnMap) {
|
|
|
16506
16615
|
import { SyntaxKind as SyntaxKind6 } from "ts-morph";
|
|
16507
16616
|
function getReferencedIdentifiers(fn) {
|
|
16508
16617
|
const names = /* @__PURE__ */ new Set();
|
|
16509
|
-
for (const
|
|
16510
|
-
names.add(
|
|
16618
|
+
for (const id of fn.getDescendantsOfKind(SyntaxKind6.Identifier)) {
|
|
16619
|
+
names.add(id.getText());
|
|
16511
16620
|
}
|
|
16512
16621
|
return names;
|
|
16513
16622
|
}
|
|
@@ -16675,8 +16784,8 @@ function matchImport(importDecl, neededNames) {
|
|
|
16675
16784
|
function getReferencedNames(nodes) {
|
|
16676
16785
|
const names = /* @__PURE__ */ new Set();
|
|
16677
16786
|
for (const node of nodes) {
|
|
16678
|
-
for (const
|
|
16679
|
-
names.add(
|
|
16787
|
+
for (const id of node.getDescendantsOfKind(SyntaxKind12.Identifier)) {
|
|
16788
|
+
names.add(id.getText());
|
|
16680
16789
|
}
|
|
16681
16790
|
}
|
|
16682
16791
|
return names;
|
|
@@ -16866,9 +16975,9 @@ function isInsideExtractedFunction(node, extracted) {
|
|
|
16866
16975
|
}
|
|
16867
16976
|
function sourceReferencesName(sourceFile, functionName, extractedNames) {
|
|
16868
16977
|
const extracted = new Set(extractedNames);
|
|
16869
|
-
for (const
|
|
16870
|
-
if (
|
|
16871
|
-
const parent =
|
|
16978
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind13.Identifier)) {
|
|
16979
|
+
if (id.getText() !== functionName) continue;
|
|
16980
|
+
const parent = id.getParent();
|
|
16872
16981
|
if (!parent || DECLARATION_KINDS.has(parent.getKind())) continue;
|
|
16873
16982
|
if (!isInsideExtractedFunction(parent, extracted)) return true;
|
|
16874
16983
|
}
|
|
@@ -17346,8 +17455,8 @@ function isDeclaration(identifier) {
|
|
|
17346
17455
|
return parent !== void 0 && declarationKinds.includes(parent.getKind());
|
|
17347
17456
|
}
|
|
17348
17457
|
function findSymbol(sourceFile, symbolName) {
|
|
17349
|
-
for (const
|
|
17350
|
-
if (
|
|
17458
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind14.Identifier)) {
|
|
17459
|
+
if (id.getText() === symbolName && isDeclaration(id)) return id;
|
|
17351
17460
|
}
|
|
17352
17461
|
return void 0;
|
|
17353
17462
|
}
|
|
@@ -19828,12 +19937,12 @@ function registerSeq(program2) {
|
|
|
19828
19937
|
// src/commands/registerSignal.ts
|
|
19829
19938
|
function registerSignal(program2) {
|
|
19830
19939
|
const signalCommand = program2.command("signal").description("Write an assist signal file");
|
|
19831
|
-
signalCommand.command("next").argument("[id]", "Backlog item ID to run directly").description("Write a next signal to chain into assist next").action((
|
|
19832
|
-
writeSignal("next",
|
|
19940
|
+
signalCommand.command("next").argument("[id]", "Backlog item ID to run directly").description("Write a next signal to chain into assist next").action((id) => {
|
|
19941
|
+
writeSignal("next", id ? { id } : void 0);
|
|
19833
19942
|
console.log("Signal written.");
|
|
19834
19943
|
});
|
|
19835
|
-
signalCommand.command("done").argument("[id]", "Backlog item ID created by the session").description("Write a done signal to end a --once launch session").action((
|
|
19836
|
-
writeSignal("done",
|
|
19944
|
+
signalCommand.command("done").argument("[id]", "Backlog item ID created by the session").description("Write a done signal to end a --once launch session").action((id) => {
|
|
19945
|
+
writeSignal("done", id ? { id } : void 0);
|
|
19837
19946
|
console.log("Signal written.");
|
|
19838
19947
|
});
|
|
19839
19948
|
}
|
|
@@ -21137,30 +21246,32 @@ function execRunConfig(config, args) {
|
|
|
21137
21246
|
config.quiet
|
|
21138
21247
|
);
|
|
21139
21248
|
}
|
|
21140
|
-
function parseBacklogRunOptions(
|
|
21249
|
+
function parseBacklogRunOptions(id, args) {
|
|
21141
21250
|
let allowEdits = true;
|
|
21142
21251
|
for (const arg of args) {
|
|
21143
21252
|
if (arg === "--write" || arg === "-w") allowEdits = true;
|
|
21144
21253
|
else if (arg === "--no-write") allowEdits = false;
|
|
21145
21254
|
else {
|
|
21146
|
-
console.error(`error: unexpected argument '${arg}' for 'run ${
|
|
21255
|
+
console.error(`error: unexpected argument '${arg}' for 'run ${id}'`);
|
|
21147
21256
|
process.exit(1);
|
|
21148
21257
|
}
|
|
21149
21258
|
}
|
|
21150
21259
|
return { allowEdits };
|
|
21151
21260
|
}
|
|
21261
|
+
async function tryBacklogRunAlias(name, args) {
|
|
21262
|
+
if (!/^a?\d+$/.test(name)) return false;
|
|
21263
|
+
if (lookupRunConfig(name).kind !== "not-found") return false;
|
|
21264
|
+
pullIfConfigured();
|
|
21265
|
+
await run2(name, parseBacklogRunOptions(name, args));
|
|
21266
|
+
return true;
|
|
21267
|
+
}
|
|
21152
21268
|
async function run3(name, args) {
|
|
21153
21269
|
if (!name) {
|
|
21154
21270
|
console.error("error: missing required argument 'name'");
|
|
21155
21271
|
console.error(formatConfiguredCommands());
|
|
21156
21272
|
process.exit(1);
|
|
21157
21273
|
}
|
|
21158
|
-
if (
|
|
21159
|
-
const options2 = parseBacklogRunOptions(name, args);
|
|
21160
|
-
pullIfConfigured();
|
|
21161
|
-
await run2(name, options2);
|
|
21162
|
-
return;
|
|
21163
|
-
}
|
|
21274
|
+
if (await tryBacklogRunAlias(name, args)) return;
|
|
21164
21275
|
execRunConfig(findRunConfig(name), args);
|
|
21165
21276
|
}
|
|
21166
21277
|
|
|
@@ -21331,7 +21442,7 @@ function remove() {
|
|
|
21331
21442
|
function registerRun(program2) {
|
|
21332
21443
|
const runCommand = program2.command("run").description("Run a configured command from assist.yml").argument("[name]", "Name of the configured command").argument("[args...]", "Arguments to pass to the command").allowUnknownOption().addHelpText(
|
|
21333
21444
|
"after",
|
|
21334
|
-
"\
|
|
21445
|
+
"\nAn a-prefixed item id (e.g. a555) with no matching command is an alias for\n'assist backlog run <id>' (forwards --write/--no-write/-w)."
|
|
21335
21446
|
).addHelpText("after", () => formatConfiguredCommands()).action((name, args) => run3(name, args));
|
|
21336
21447
|
runCommand.command("list").description("List configured run commands").option("-v, --verbose", "Show full command details").action((opts) => listRunConfigs(!!opts.verbose));
|
|
21337
21448
|
runCommand.command("add").description("Add a new run configuration to assist.yml").argument("<name>", "Name for the run configuration").argument("<command>", "Command to execute").argument("[args...]", "Static args to pass to the command").option(
|
|
@@ -21815,7 +21926,7 @@ function broadcast(clients, msg) {
|
|
|
21815
21926
|
|
|
21816
21927
|
// src/commands/sessions/daemon/toSessionInfo.ts
|
|
21817
21928
|
function toSessionInfo({
|
|
21818
|
-
id
|
|
21929
|
+
id,
|
|
21819
21930
|
name,
|
|
21820
21931
|
title,
|
|
21821
21932
|
subtitle,
|
|
@@ -21835,7 +21946,7 @@ function toSessionInfo({
|
|
|
21835
21946
|
autoAdvance
|
|
21836
21947
|
}) {
|
|
21837
21948
|
return {
|
|
21838
|
-
id
|
|
21949
|
+
id,
|
|
21839
21950
|
name,
|
|
21840
21951
|
title,
|
|
21841
21952
|
subtitle,
|
|
@@ -21978,10 +22089,10 @@ function shellEscape(s) {
|
|
|
21978
22089
|
}
|
|
21979
22090
|
|
|
21980
22091
|
// src/commands/sessions/daemon/createAssistSession.ts
|
|
21981
|
-
function createAssistSession(
|
|
22092
|
+
function createAssistSession(id, assistArgs, cwd, meta) {
|
|
21982
22093
|
const startedAt = Date.now();
|
|
21983
22094
|
return {
|
|
21984
|
-
id
|
|
22095
|
+
id,
|
|
21985
22096
|
name: `assist ${assistArgs.join(" ")}`,
|
|
21986
22097
|
title: meta?.title,
|
|
21987
22098
|
subtitle: meta?.subtitle,
|
|
@@ -21990,7 +22101,7 @@ function createAssistSession(id2, assistArgs, cwd, meta) {
|
|
|
21990
22101
|
startedAt,
|
|
21991
22102
|
runningMs: 0,
|
|
21992
22103
|
runningSince: startedAt,
|
|
21993
|
-
pty: spawnPty(["assist", ...assistArgs], cwd,
|
|
22104
|
+
pty: spawnPty(["assist", ...assistArgs], cwd, id),
|
|
21994
22105
|
scrollback: "",
|
|
21995
22106
|
assistArgs,
|
|
21996
22107
|
cwd
|
|
@@ -21998,21 +22109,21 @@ function createAssistSession(id2, assistArgs, cwd, meta) {
|
|
|
21998
22109
|
}
|
|
21999
22110
|
|
|
22000
22111
|
// src/commands/sessions/daemon/dismissSession.ts
|
|
22001
|
-
function dismissSession(sessions,
|
|
22002
|
-
const s = sessions.get(
|
|
22112
|
+
function dismissSession(sessions, id) {
|
|
22113
|
+
const s = sessions.get(id);
|
|
22003
22114
|
if (!s) return false;
|
|
22004
22115
|
if (s.status !== "done") s.pty?.kill();
|
|
22005
22116
|
s.activityWatcher?.close();
|
|
22006
22117
|
removeActivity(s.id);
|
|
22007
22118
|
if (s.activity?.itemId != null) releaseLock(s.activity.itemId);
|
|
22008
|
-
sessions.delete(
|
|
22009
|
-
daemonLog(`session ${
|
|
22119
|
+
sessions.delete(id);
|
|
22120
|
+
daemonLog(`session ${id} dismissed (${s.name})`);
|
|
22010
22121
|
return true;
|
|
22011
22122
|
}
|
|
22012
22123
|
function drainSessions(sessions, onDrained) {
|
|
22013
22124
|
const names = [...sessions.values()].map((s) => s.name);
|
|
22014
22125
|
const ids = [...sessions.keys()];
|
|
22015
|
-
for (const
|
|
22126
|
+
for (const id of ids) dismissSession(sessions, id);
|
|
22016
22127
|
onDrained();
|
|
22017
22128
|
daemonLog(
|
|
22018
22129
|
ids.length > 0 ? `drained ${ids.length} session(s): ${names.join(", ")}` : "drained 0 sessions"
|
|
@@ -22043,10 +22154,10 @@ function spawnRun(opts) {
|
|
|
22043
22154
|
}
|
|
22044
22155
|
|
|
22045
22156
|
// src/commands/sessions/daemon/createSession.ts
|
|
22046
|
-
function sessionBase(
|
|
22157
|
+
function sessionBase(id, status2) {
|
|
22047
22158
|
const startedAt = Date.now();
|
|
22048
22159
|
return {
|
|
22049
|
-
id
|
|
22160
|
+
id,
|
|
22050
22161
|
status: status2,
|
|
22051
22162
|
startedAt,
|
|
22052
22163
|
runningMs: 0,
|
|
@@ -22056,21 +22167,21 @@ function sessionBase(id2, status2) {
|
|
|
22056
22167
|
scrollback: ""
|
|
22057
22168
|
};
|
|
22058
22169
|
}
|
|
22059
|
-
function createSession(
|
|
22170
|
+
function createSession(id, prompt, cwd) {
|
|
22060
22171
|
const claudeSessionId = randomUUID4();
|
|
22061
22172
|
return {
|
|
22062
|
-
...sessionBase(
|
|
22063
|
-
name: prompt?.slice(0, 40) || `Session ${
|
|
22173
|
+
...sessionBase(id, prompt ? "running" : "waiting"),
|
|
22174
|
+
name: prompt?.slice(0, 40) || `Session ${id}`,
|
|
22064
22175
|
commandType: "claude",
|
|
22065
|
-
pty: spawnClaude2({ prompt, cwd, sessionId:
|
|
22176
|
+
pty: spawnClaude2({ prompt, cwd, sessionId: id, claudeSessionId }),
|
|
22066
22177
|
cwd,
|
|
22067
22178
|
claudeSessionId,
|
|
22068
22179
|
initialPrompt: prompt
|
|
22069
22180
|
};
|
|
22070
22181
|
}
|
|
22071
|
-
function createRunSession(
|
|
22182
|
+
function createRunSession(id, runName, runArgs, cwd) {
|
|
22072
22183
|
return {
|
|
22073
|
-
...sessionBase(
|
|
22184
|
+
...sessionBase(id, "running"),
|
|
22074
22185
|
name: `run: ${runName}`,
|
|
22075
22186
|
commandType: "run",
|
|
22076
22187
|
pty: spawnRun({ name: runName, args: runArgs, cwd }),
|
|
@@ -22118,9 +22229,9 @@ function logSpawnedSession(session) {
|
|
|
22118
22229
|
|
|
22119
22230
|
// src/shared/db/recordPhaseActiveMs.ts
|
|
22120
22231
|
import { sql as sql9 } from "drizzle-orm";
|
|
22121
|
-
async function recordPhaseActiveMs(db,
|
|
22232
|
+
async function recordPhaseActiveMs(db, itemId2, phaseIdx, activeMs) {
|
|
22122
22233
|
if (activeMs <= 0) return;
|
|
22123
|
-
await db.insert(phaseUsage).values({ itemId, phaseIdx, activeMs }).onConflictDoUpdate({
|
|
22234
|
+
await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, activeMs }).onConflictDoUpdate({
|
|
22124
22235
|
target: [phaseUsage.itemId, phaseUsage.phaseIdx],
|
|
22125
22236
|
set: {
|
|
22126
22237
|
activeMs: sql9`${phaseUsage.activeMs} + excluded.active_ms`
|
|
@@ -22129,10 +22240,10 @@ async function recordPhaseActiveMs(db, itemId, phaseIdx, activeMs) {
|
|
|
22129
22240
|
}
|
|
22130
22241
|
|
|
22131
22242
|
// src/commands/sessions/daemon/persistPhaseActiveMs.ts
|
|
22132
|
-
async function persistPhaseActiveMs(
|
|
22243
|
+
async function persistPhaseActiveMs(itemId2, phaseIdx, activeMs) {
|
|
22133
22244
|
try {
|
|
22134
22245
|
if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url) return;
|
|
22135
|
-
await recordPhaseActiveMs(await getDb(),
|
|
22246
|
+
await recordPhaseActiveMs(await getDb(), itemId2, phaseIdx, activeMs);
|
|
22136
22247
|
} catch (error) {
|
|
22137
22248
|
daemonLog(`phase-active-ms persist failed: ${String(error)}`);
|
|
22138
22249
|
}
|
|
@@ -22189,13 +22300,13 @@ function shouldAutoRun(session) {
|
|
|
22189
22300
|
run: false,
|
|
22190
22301
|
reason: `command "${cmd ?? "(none)"}" is not draft/bug/refine`
|
|
22191
22302
|
};
|
|
22192
|
-
const
|
|
22193
|
-
if (
|
|
22303
|
+
const itemId2 = session.activity?.itemId;
|
|
22304
|
+
if (itemId2 == null)
|
|
22194
22305
|
return {
|
|
22195
22306
|
run: false,
|
|
22196
22307
|
reason: "no itemId in activity (draft did not create an item)"
|
|
22197
22308
|
};
|
|
22198
|
-
return { run: true, itemId };
|
|
22309
|
+
return { run: true, itemId: itemId2 };
|
|
22199
22310
|
}
|
|
22200
22311
|
|
|
22201
22312
|
// src/commands/sessions/daemon/watchEscInterrupt.ts
|
|
@@ -22267,8 +22378,8 @@ function makeStatusChangeHandler(dismiss, notify2, reuseForRun) {
|
|
|
22267
22378
|
|
|
22268
22379
|
// src/shared/db/recordPhaseTokens.ts
|
|
22269
22380
|
import { sql as sql10 } from "drizzle-orm";
|
|
22270
|
-
async function recordPhaseTokens(db,
|
|
22271
|
-
await db.insert(phaseUsage).values({ itemId, phaseIdx, lastTotalIn: totalIn, lastTotalOut: totalOut }).onConflictDoUpdate({
|
|
22381
|
+
async function recordPhaseTokens(db, itemId2, phaseIdx, totalIn, totalOut) {
|
|
22382
|
+
await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, lastTotalIn: totalIn, lastTotalOut: totalOut }).onConflictDoUpdate({
|
|
22272
22383
|
target: [phaseUsage.itemId, phaseUsage.phaseIdx],
|
|
22273
22384
|
set: {
|
|
22274
22385
|
tokensDown: sql10`${phaseUsage.tokensDown} + CASE WHEN ${phaseUsage.lastTotalIn} IS NULL THEN 0 ELSE GREATEST(excluded.last_total_in - ${phaseUsage.lastTotalIn}, 0) END`,
|
|
@@ -22280,10 +22391,10 @@ async function recordPhaseTokens(db, itemId, phaseIdx, totalIn, totalOut) {
|
|
|
22280
22391
|
}
|
|
22281
22392
|
|
|
22282
22393
|
// src/commands/sessions/daemon/persistPhaseTokens.ts
|
|
22283
|
-
async function persistPhaseTokens(
|
|
22394
|
+
async function persistPhaseTokens(itemId2, phaseIdx, totalIn, totalOut) {
|
|
22284
22395
|
try {
|
|
22285
22396
|
if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url) return;
|
|
22286
|
-
await recordPhaseTokens(await getDb(),
|
|
22397
|
+
await recordPhaseTokens(await getDb(), itemId2, phaseIdx, totalIn, totalOut);
|
|
22287
22398
|
} catch (error) {
|
|
22288
22399
|
daemonLog(`phase-tokens persist failed: ${String(error)}`);
|
|
22289
22400
|
}
|
|
@@ -22317,19 +22428,19 @@ function applyReviewPause(session, activity2) {
|
|
|
22317
22428
|
|
|
22318
22429
|
// src/shared/db/getPhaseActiveMs.ts
|
|
22319
22430
|
import { and as and19, eq as eq37 } from "drizzle-orm";
|
|
22320
|
-
async function getPhaseActiveMs(db,
|
|
22431
|
+
async function getPhaseActiveMs(db, itemId2, phaseIdx) {
|
|
22321
22432
|
const [row] = await db.select({ activeMs: phaseUsage.activeMs }).from(phaseUsage).where(
|
|
22322
|
-
and19(eq37(phaseUsage.itemId,
|
|
22433
|
+
and19(eq37(phaseUsage.itemId, itemId2), eq37(phaseUsage.phaseIdx, phaseIdx))
|
|
22323
22434
|
);
|
|
22324
22435
|
return row?.activeMs ?? 0;
|
|
22325
22436
|
}
|
|
22326
22437
|
|
|
22327
22438
|
// src/commands/sessions/daemon/loadPhaseActiveMs.ts
|
|
22328
|
-
async function loadPhaseActiveMs(
|
|
22439
|
+
async function loadPhaseActiveMs(itemId2, phaseIdx) {
|
|
22329
22440
|
try {
|
|
22330
22441
|
if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url)
|
|
22331
22442
|
return null;
|
|
22332
|
-
return await getPhaseActiveMs(await getDb(),
|
|
22443
|
+
return await getPhaseActiveMs(await getDb(), itemId2, phaseIdx);
|
|
22333
22444
|
} catch (error) {
|
|
22334
22445
|
daemonLog(`phase-active-ms load failed: ${String(error)}`);
|
|
22335
22446
|
return null;
|
|
@@ -22545,9 +22656,9 @@ function killPtyTree(pty2) {
|
|
|
22545
22656
|
}
|
|
22546
22657
|
|
|
22547
22658
|
// src/commands/sessions/daemon/restoreBase.ts
|
|
22548
|
-
function restoreBase(
|
|
22659
|
+
function restoreBase(id, persisted) {
|
|
22549
22660
|
return {
|
|
22550
|
-
id
|
|
22661
|
+
id,
|
|
22551
22662
|
name: persisted.name,
|
|
22552
22663
|
title: persisted.title,
|
|
22553
22664
|
subtitle: persisted.subtitle,
|
|
@@ -22560,9 +22671,9 @@ function restoreBase(id2, persisted) {
|
|
|
22560
22671
|
}
|
|
22561
22672
|
|
|
22562
22673
|
// src/commands/sessions/daemon/errorSession.ts
|
|
22563
|
-
function errorSession(
|
|
22674
|
+
function errorSession(id, persisted, error) {
|
|
22564
22675
|
return {
|
|
22565
|
-
...restoreBase(
|
|
22676
|
+
...restoreBase(id, persisted),
|
|
22566
22677
|
status: "error",
|
|
22567
22678
|
startedAt: persisted.startedAt,
|
|
22568
22679
|
runningMs: persisted.runningMs ?? 0,
|
|
@@ -22598,28 +22709,28 @@ function restoredSession(base, persisted, pty2, status2) {
|
|
|
22598
22709
|
}
|
|
22599
22710
|
|
|
22600
22711
|
// src/commands/sessions/daemon/restoreInteractiveSession.ts
|
|
22601
|
-
function restoreInteractiveSession(
|
|
22712
|
+
function restoreInteractiveSession(id, persisted, base) {
|
|
22602
22713
|
if (persisted.commandType !== "run" && persisted.claudeSessionId) {
|
|
22603
|
-
return resumeViaClaude(
|
|
22714
|
+
return resumeViaClaude(id, persisted, base);
|
|
22604
22715
|
}
|
|
22605
22716
|
if (persisted.commandType === "claude") {
|
|
22606
|
-
return unrecoverableClaude(
|
|
22717
|
+
return unrecoverableClaude(id, persisted);
|
|
22607
22718
|
}
|
|
22608
22719
|
return notRestoredStub(base, persisted);
|
|
22609
22720
|
}
|
|
22610
|
-
function resumeViaClaude(
|
|
22721
|
+
function resumeViaClaude(id, persisted, base) {
|
|
22611
22722
|
const idle = persisted.status === "waiting";
|
|
22612
22723
|
const pty2 = spawnClaude2({
|
|
22613
22724
|
resumeSessionId: persisted.claudeSessionId,
|
|
22614
22725
|
prompt: idle ? void 0 : buildResumePrompt(),
|
|
22615
22726
|
cwd: persisted.cwd,
|
|
22616
|
-
sessionId:
|
|
22727
|
+
sessionId: id
|
|
22617
22728
|
});
|
|
22618
22729
|
return idle ? waitingSession(base, persisted, pty2) : runningSession(base, persisted, pty2);
|
|
22619
22730
|
}
|
|
22620
|
-
function unrecoverableClaude(
|
|
22731
|
+
function unrecoverableClaude(id, persisted) {
|
|
22621
22732
|
return errorSession(
|
|
22622
|
-
|
|
22733
|
+
id,
|
|
22623
22734
|
persisted,
|
|
22624
22735
|
"no claude session id was recorded before the daemon stopped, so the conversation cannot be resumed"
|
|
22625
22736
|
);
|
|
@@ -22639,9 +22750,9 @@ function notRestoredStub(base, persisted) {
|
|
|
22639
22750
|
}
|
|
22640
22751
|
|
|
22641
22752
|
// src/commands/sessions/daemon/updatedSession.ts
|
|
22642
|
-
function updatedSession(
|
|
22753
|
+
function updatedSession(id, persisted) {
|
|
22643
22754
|
return {
|
|
22644
|
-
...restoreBase(
|
|
22755
|
+
...restoreBase(id, persisted),
|
|
22645
22756
|
status: "done",
|
|
22646
22757
|
startedAt: persisted.startedAt,
|
|
22647
22758
|
runningMs: persisted.runningMs ?? 0,
|
|
@@ -22654,20 +22765,20 @@ function isUpdate(persisted) {
|
|
|
22654
22765
|
}
|
|
22655
22766
|
|
|
22656
22767
|
// src/commands/sessions/daemon/restoreSession.ts
|
|
22657
|
-
function restoreSession(
|
|
22658
|
-
const base = restoreBase(
|
|
22659
|
-
if (isUpdate(persisted)) return updatedSession(
|
|
22768
|
+
function restoreSession(id, persisted) {
|
|
22769
|
+
const base = restoreBase(id, persisted);
|
|
22770
|
+
if (isUpdate(persisted)) return updatedSession(id, persisted);
|
|
22660
22771
|
if (needsWrapperRelaunch(persisted)) {
|
|
22661
22772
|
const idle = persisted.status === "waiting";
|
|
22662
22773
|
const pty2 = spawnPty(
|
|
22663
22774
|
assistResumeArgs(persisted),
|
|
22664
22775
|
persisted.cwd,
|
|
22665
|
-
|
|
22776
|
+
id,
|
|
22666
22777
|
idle ? { ASSIST_RESUME_IDLE: "1" } : void 0
|
|
22667
22778
|
);
|
|
22668
22779
|
return idle ? waitingSession(base, persisted, pty2) : runningSession(base, persisted, pty2);
|
|
22669
22780
|
}
|
|
22670
|
-
return restoreInteractiveSession(
|
|
22781
|
+
return restoreInteractiveSession(id, persisted, base);
|
|
22671
22782
|
}
|
|
22672
22783
|
function needsWrapperRelaunch(persisted) {
|
|
22673
22784
|
return isBacklogRun(persisted) || isOnceLaunch(persisted) && !!persisted.claudeSessionId;
|
|
@@ -22683,20 +22794,20 @@ function isOnceLaunch(persisted) {
|
|
|
22683
22794
|
// src/commands/sessions/daemon/restoreOne.ts
|
|
22684
22795
|
function restoreOne(persisted, spawn12, sessions) {
|
|
22685
22796
|
try {
|
|
22686
|
-
const
|
|
22687
|
-
logUnresumable(persisted.name,
|
|
22797
|
+
const id = spawn12((sid) => restoreSession(sid, persisted));
|
|
22798
|
+
logUnresumable(persisted.name, id, sessions.get(id));
|
|
22688
22799
|
} catch (error) {
|
|
22689
22800
|
const reason = logRestoreError(persisted.name, error);
|
|
22690
22801
|
try {
|
|
22691
|
-
spawn12((
|
|
22802
|
+
spawn12((id) => errorSession(id, persisted, reason));
|
|
22692
22803
|
} catch {
|
|
22693
22804
|
}
|
|
22694
22805
|
}
|
|
22695
22806
|
}
|
|
22696
|
-
function logUnresumable(name,
|
|
22807
|
+
function logUnresumable(name, id, session) {
|
|
22697
22808
|
if (session?.status === "error")
|
|
22698
22809
|
daemonLog(
|
|
22699
|
-
`could not resume restored session "${name}" (id ${
|
|
22810
|
+
`could not resume restored session "${name}" (id ${id}): ${session.error}`
|
|
22700
22811
|
);
|
|
22701
22812
|
}
|
|
22702
22813
|
function logRestoreError(name, error) {
|
|
@@ -22738,17 +22849,17 @@ function restoreAll(spawn12, sessions) {
|
|
|
22738
22849
|
}
|
|
22739
22850
|
|
|
22740
22851
|
// src/commands/sessions/daemon/resumeSession.ts
|
|
22741
|
-
function resumeSession(
|
|
22852
|
+
function resumeSession(id, sessionId, cwd, name) {
|
|
22742
22853
|
const startedAt = Date.now();
|
|
22743
22854
|
return {
|
|
22744
|
-
id
|
|
22855
|
+
id,
|
|
22745
22856
|
name: name ? `${name.slice(0, 36)} (R)` : `Resume ${sessionId.slice(0, 8)}`,
|
|
22746
22857
|
commandType: "claude",
|
|
22747
22858
|
status: "waiting",
|
|
22748
22859
|
startedAt,
|
|
22749
22860
|
runningMs: 0,
|
|
22750
22861
|
runningSince: null,
|
|
22751
|
-
pty: spawnClaude2({ resumeSessionId: sessionId, cwd, sessionId:
|
|
22862
|
+
pty: spawnClaude2({ resumeSessionId: sessionId, cwd, sessionId: id }),
|
|
22752
22863
|
scrollback: "",
|
|
22753
22864
|
cwd,
|
|
22754
22865
|
/* why: bind the card to the conversation it is resuming so a daemon restart
|
|
@@ -22777,8 +22888,8 @@ function respawnThunk(session) {
|
|
|
22777
22888
|
}
|
|
22778
22889
|
|
|
22779
22890
|
// src/commands/sessions/daemon/reuseSessionForRun.ts
|
|
22780
|
-
function reuseSessionForRun(session,
|
|
22781
|
-
const assistArgs = ["backlog", "run", String(
|
|
22891
|
+
function reuseSessionForRun(session, itemId2, clients, onStatusChange) {
|
|
22892
|
+
const assistArgs = ["backlog", "run", String(itemId2)];
|
|
22782
22893
|
session.pty?.kill();
|
|
22783
22894
|
session.assistArgs = assistArgs;
|
|
22784
22895
|
session.name = `assist ${assistArgs.join(" ")}`;
|
|
@@ -22796,7 +22907,7 @@ function reuseSessionForRun(session, itemId, clients, onStatusChange) {
|
|
|
22796
22907
|
broadcast(clients, { type: "clear", sessionId: session.id });
|
|
22797
22908
|
wirePtyEvents(session, clients, onStatusChange);
|
|
22798
22909
|
daemonLog(
|
|
22799
|
-
`session ${session.id} reused for backlog run ${
|
|
22910
|
+
`session ${session.id} reused for backlog run ${itemId2}: ${session.name}`
|
|
22800
22911
|
);
|
|
22801
22912
|
}
|
|
22802
22913
|
|
|
@@ -22936,14 +23047,14 @@ async function discoverWindowsSessions(conn) {
|
|
|
22936
23047
|
|
|
22937
23048
|
// src/commands/sessions/daemon/toWindowsSessionId.ts
|
|
22938
23049
|
var PREFIX = "w-";
|
|
22939
|
-
function toWindowsSessionId(
|
|
22940
|
-
return isWindowsSessionId(
|
|
23050
|
+
function toWindowsSessionId(id) {
|
|
23051
|
+
return isWindowsSessionId(id) ? id : `${PREFIX}${id}`;
|
|
22941
23052
|
}
|
|
22942
|
-
function isWindowsSessionId(
|
|
22943
|
-
return
|
|
23053
|
+
function isWindowsSessionId(id) {
|
|
23054
|
+
return id.startsWith(PREFIX);
|
|
22944
23055
|
}
|
|
22945
|
-
function stripWindowsSessionId(
|
|
22946
|
-
let stripped =
|
|
23056
|
+
function stripWindowsSessionId(id) {
|
|
23057
|
+
let stripped = id;
|
|
22947
23058
|
while (stripped.startsWith(PREFIX)) stripped = stripped.slice(PREFIX.length);
|
|
22948
23059
|
return stripped;
|
|
22949
23060
|
}
|
|
@@ -23038,8 +23149,8 @@ function handleSessions(state, msg) {
|
|
|
23038
23149
|
id: toWindowsSessionId(s.id)
|
|
23039
23150
|
}));
|
|
23040
23151
|
const live = new Set(state.windowsSessions.map((s) => s.id));
|
|
23041
|
-
for (const
|
|
23042
|
-
if (!live.has(
|
|
23152
|
+
for (const id of state.scrollback.keys())
|
|
23153
|
+
if (!live.has(id)) state.scrollback.delete(id);
|
|
23043
23154
|
state.onSessionsChanged();
|
|
23044
23155
|
}
|
|
23045
23156
|
|
|
@@ -23442,34 +23553,34 @@ function watchPromptSubmit(session, data, onStatusChange) {
|
|
|
23442
23553
|
}
|
|
23443
23554
|
|
|
23444
23555
|
// src/commands/sessions/daemon/writeToSession.ts
|
|
23445
|
-
function writeToSession(sessions,
|
|
23446
|
-
const s = sessions.get(
|
|
23556
|
+
function writeToSession(sessions, id, data, onStatusChange) {
|
|
23557
|
+
const s = sessions.get(id);
|
|
23447
23558
|
if (!s || s.status === "done") return;
|
|
23448
23559
|
s.pty?.write(data);
|
|
23449
23560
|
watchEscInterrupt(s, data, onStatusChange);
|
|
23450
23561
|
watchPromptSubmit(s, data, onStatusChange);
|
|
23451
23562
|
}
|
|
23452
|
-
function resizeSession(sessions,
|
|
23453
|
-
const s = sessions.get(
|
|
23563
|
+
function resizeSession(sessions, id, cols, rows) {
|
|
23564
|
+
const s = sessions.get(id);
|
|
23454
23565
|
if (s && s.status !== "done") s.pty?.resize(cols, rows);
|
|
23455
23566
|
}
|
|
23456
|
-
function setAutoRun(sessions,
|
|
23457
|
-
const s = sessions.get(
|
|
23567
|
+
function setAutoRun(sessions, id, enabled) {
|
|
23568
|
+
const s = sessions.get(id);
|
|
23458
23569
|
if (!s) return false;
|
|
23459
23570
|
s.autoRun = enabled;
|
|
23460
|
-
daemonLog(`session ${
|
|
23571
|
+
daemonLog(`session ${id} autorun ${enabled ? "on" : "off"}`);
|
|
23461
23572
|
return true;
|
|
23462
23573
|
}
|
|
23463
|
-
function setAutoAdvance(sessions,
|
|
23464
|
-
const s = sessions.get(
|
|
23574
|
+
function setAutoAdvance(sessions, id, enabled) {
|
|
23575
|
+
const s = sessions.get(id);
|
|
23465
23576
|
if (!s) return false;
|
|
23466
23577
|
s.autoAdvance = enabled;
|
|
23467
|
-
const
|
|
23468
|
-
if (
|
|
23469
|
-
if (enabled) clearPause(
|
|
23470
|
-
else requestPause(
|
|
23578
|
+
const itemId2 = s.activity?.itemId;
|
|
23579
|
+
if (itemId2 != null) {
|
|
23580
|
+
if (enabled) clearPause(itemId2);
|
|
23581
|
+
else requestPause(itemId2);
|
|
23471
23582
|
}
|
|
23472
|
-
daemonLog(`session ${
|
|
23583
|
+
daemonLog(`session ${id} autoadvance ${enabled ? "on" : "off"}`);
|
|
23473
23584
|
return true;
|
|
23474
23585
|
}
|
|
23475
23586
|
|
|
@@ -23513,58 +23624,58 @@ var SessionManager = class {
|
|
|
23513
23624
|
}
|
|
23514
23625
|
spawnWith = (create) => this.add(create(sessionLimits.nextId(this.sessions.size, this.idCounter)));
|
|
23515
23626
|
spawn(prompt, cwd) {
|
|
23516
|
-
return this.spawnWith((
|
|
23627
|
+
return this.spawnWith((id) => createSession(id, prompt, cwd));
|
|
23517
23628
|
}
|
|
23518
23629
|
spawnRun(runName, runArgs, cwd) {
|
|
23519
|
-
return this.spawnWith((
|
|
23630
|
+
return this.spawnWith((id) => createRunSession(id, runName, runArgs, cwd));
|
|
23520
23631
|
}
|
|
23521
23632
|
spawnAssist(assistArgs, cwd, meta) {
|
|
23522
23633
|
return this.spawnWith(
|
|
23523
|
-
(
|
|
23634
|
+
(id) => createAssistSession(id, assistArgs, cwd, meta)
|
|
23524
23635
|
);
|
|
23525
23636
|
}
|
|
23526
23637
|
resume(sessionId, cwd, name) {
|
|
23527
|
-
return this.spawnWith((
|
|
23638
|
+
return this.spawnWith((id) => resumeSession(id, sessionId, cwd, name));
|
|
23528
23639
|
}
|
|
23529
23640
|
onStatusChange = makeStatusChangeHandler(
|
|
23530
|
-
(
|
|
23641
|
+
(id) => this.dismissSession(id),
|
|
23531
23642
|
() => this.notify(),
|
|
23532
|
-
(session,
|
|
23643
|
+
(session, itemId2) => reuseSessionForRun(session, itemId2, this.clients, this.onStatusChange)
|
|
23533
23644
|
);
|
|
23534
23645
|
wire(session) {
|
|
23535
23646
|
this.sessions.set(session.id, session);
|
|
23536
23647
|
wirePtyEvents(session, this.clients, this.onStatusChange);
|
|
23537
23648
|
this.notify();
|
|
23538
23649
|
}
|
|
23539
|
-
writeToSession(
|
|
23540
|
-
writeToSession(this.sessions,
|
|
23650
|
+
writeToSession(id, data) {
|
|
23651
|
+
writeToSession(this.sessions, id, data, this.onStatusChange);
|
|
23541
23652
|
}
|
|
23542
|
-
resizeSession(
|
|
23543
|
-
resizeSession(this.sessions,
|
|
23653
|
+
resizeSession(id, cols, rows) {
|
|
23654
|
+
resizeSession(this.sessions, id, cols, rows);
|
|
23544
23655
|
}
|
|
23545
|
-
retrySession(
|
|
23546
|
-
const s = this.sessions.get(
|
|
23656
|
+
retrySession(id) {
|
|
23657
|
+
const s = this.sessions.get(id);
|
|
23547
23658
|
if (s && retrySession(s, this.clients, this.onStatusChange)) this.notify();
|
|
23548
23659
|
}
|
|
23549
|
-
restart(
|
|
23550
|
-
const s = this.sessions.get(
|
|
23660
|
+
restart(id) {
|
|
23661
|
+
const s = this.sessions.get(id);
|
|
23551
23662
|
if (s && restartSession(s, this.clients, this.onStatusChange))
|
|
23552
23663
|
this.notify();
|
|
23553
23664
|
}
|
|
23554
|
-
dismissSession = (
|
|
23555
|
-
if (dismissSession(this.sessions,
|
|
23665
|
+
dismissSession = (id) => {
|
|
23666
|
+
if (dismissSession(this.sessions, id)) this.notify();
|
|
23556
23667
|
};
|
|
23557
|
-
setAutoRun(
|
|
23558
|
-
if (setAutoRun(this.sessions,
|
|
23668
|
+
setAutoRun(id, enabled) {
|
|
23669
|
+
if (setAutoRun(this.sessions, id, enabled)) this.notify();
|
|
23559
23670
|
}
|
|
23560
|
-
setAutoAdvance(
|
|
23561
|
-
if (setAutoAdvance(this.sessions,
|
|
23671
|
+
setAutoAdvance(id, enabled) {
|
|
23672
|
+
if (setAutoAdvance(this.sessions, id, enabled)) this.notify();
|
|
23562
23673
|
}
|
|
23563
|
-
setStatus(
|
|
23564
|
-
const session = this.sessions.get(
|
|
23674
|
+
setStatus(id, status2) {
|
|
23675
|
+
const session = this.sessions.get(id);
|
|
23565
23676
|
if (!session) {
|
|
23566
23677
|
daemonLog(
|
|
23567
|
-
`set-status for unknown session id=${
|
|
23678
|
+
`set-status for unknown session id=${id} status=${status2} (no live session; ignoring)`
|
|
23568
23679
|
);
|
|
23569
23680
|
return;
|
|
23570
23681
|
}
|
|
@@ -23637,7 +23748,7 @@ function deriveSessionType(commandName, name) {
|
|
|
23637
23748
|
|
|
23638
23749
|
// src/commands/sessions/shared/backlogRunMarkers.ts
|
|
23639
23750
|
function backlogRunMarkers(text9) {
|
|
23640
|
-
const match = text9.match(/backlog item
|
|
23751
|
+
const match = text9.match(/backlog item a(\d+):[ \t]*([^\n]*)/);
|
|
23641
23752
|
if (!match) return { commandName: "", commandArgs: "" };
|
|
23642
23753
|
const title = match[2].trim();
|
|
23643
23754
|
return {
|
|
@@ -24057,7 +24168,7 @@ function onListening(manager, checkAutoExit) {
|
|
|
24057
24168
|
process.on("exit", cleanupOwnedFiles);
|
|
24058
24169
|
const restored = manager.restore();
|
|
24059
24170
|
const liveIds = new Set(manager.listSessions().map((s) => s.id));
|
|
24060
|
-
manager.active.restore((
|
|
24171
|
+
manager.active.restore((id) => liveIds.has(id));
|
|
24061
24172
|
daemonLog(
|
|
24062
24173
|
restored.length > 0 ? `restored ${restored.length} session(s): ${restored.join(", ")}` : "no persisted sessions to restore"
|
|
24063
24174
|
);
|
|
@@ -24253,27 +24364,27 @@ function truncate3(text9, maxChars = 500) {
|
|
|
24253
24364
|
function scanSessionBacklogRefs(filePath) {
|
|
24254
24365
|
const ids = /* @__PURE__ */ new Set();
|
|
24255
24366
|
for (const text9 of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
|
|
24256
|
-
for (const
|
|
24257
|
-
ids.add(
|
|
24367
|
+
for (const id of extractBacklogIds(text9)) {
|
|
24368
|
+
ids.add(id);
|
|
24258
24369
|
}
|
|
24259
24370
|
}
|
|
24260
24371
|
return [...ids].sort((a, b) => a - b);
|
|
24261
24372
|
}
|
|
24262
24373
|
function extractBacklogIds(text9) {
|
|
24263
24374
|
const ids = [];
|
|
24264
|
-
for (const m of text9.matchAll(/backlog\s+run\s+(\d+)/gi)) {
|
|
24375
|
+
for (const m of text9.matchAll(/backlog\s+run\s+a(\d+)/gi)) {
|
|
24265
24376
|
ids.push(Number.parseInt(m[1], 10));
|
|
24266
24377
|
}
|
|
24267
|
-
for (const m of text9.matchAll(/backlog\s+(?:item\s+)
|
|
24378
|
+
for (const m of text9.matchAll(/backlog\s+(?:item\s+)?a(\d+)/gi)) {
|
|
24268
24379
|
ids.push(Number.parseInt(m[1], 10));
|
|
24269
24380
|
}
|
|
24270
|
-
for (const m of text9.matchAll(/backlog\s+phase-done\s+(\d+)/gi)) {
|
|
24381
|
+
for (const m of text9.matchAll(/backlog\s+phase-done\s+a(\d+)/gi)) {
|
|
24271
24382
|
ids.push(Number.parseInt(m[1], 10));
|
|
24272
24383
|
}
|
|
24273
|
-
for (const m of text9.matchAll(/backlog\s+comment\s+(\d+)/gi)) {
|
|
24384
|
+
for (const m of text9.matchAll(/backlog\s+comment\s+a(\d+)/gi)) {
|
|
24274
24385
|
ids.push(Number.parseInt(m[1], 10));
|
|
24275
24386
|
}
|
|
24276
|
-
for (const m of text9.matchAll(/(?:^|[\s(])
|
|
24387
|
+
for (const m of text9.matchAll(/(?:^|[\s(])a(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
|
|
24277
24388
|
ids.push(Number.parseInt(m[1], 10));
|
|
24278
24389
|
}
|
|
24279
24390
|
return ids;
|
|
@@ -24306,7 +24417,7 @@ function buildPrompt2(firstMessage, backlogIds) {
|
|
|
24306
24417
|
"Return ONLY the summary, no quotes or explanation."
|
|
24307
24418
|
];
|
|
24308
24419
|
if (backlogIds.length > 0) {
|
|
24309
|
-
const refs = backlogIds.map((
|
|
24420
|
+
const refs = backlogIds.map((id) => formatItemId(id)).join(", ");
|
|
24310
24421
|
parts.push(
|
|
24311
24422
|
`The session references backlog item(s) ${refs}. Start the summary with "Backlog ${refs} \u2014" if relevant.`
|
|
24312
24423
|
);
|
|
@@ -24697,6 +24808,17 @@ async function update2() {
|
|
|
24697
24808
|
await restartDaemonAfterUpdate();
|
|
24698
24809
|
}
|
|
24699
24810
|
|
|
24811
|
+
// src/reportCliError.ts
|
|
24812
|
+
import chalk198 from "chalk";
|
|
24813
|
+
function reportCliError(error) {
|
|
24814
|
+
if (error instanceof InvalidItemIdError) {
|
|
24815
|
+
console.error(chalk198.red(error.message));
|
|
24816
|
+
} else {
|
|
24817
|
+
console.error(error);
|
|
24818
|
+
}
|
|
24819
|
+
process.exitCode = 1;
|
|
24820
|
+
}
|
|
24821
|
+
|
|
24700
24822
|
// src/index.ts
|
|
24701
24823
|
var program = new Command();
|
|
24702
24824
|
program.name("assist").description("CLI application").version(package_default.version).option("--no-open", "Do not open a browser on startup").action((options2) => web({ port: "3100", open: options2.open }));
|
|
@@ -24751,7 +24873,4 @@ registerPrompts(program);
|
|
|
24751
24873
|
registerDeny(program);
|
|
24752
24874
|
registerLaunch(program);
|
|
24753
24875
|
registerSignal(program);
|
|
24754
|
-
program.parseAsync().catch((
|
|
24755
|
-
console.error(error);
|
|
24756
|
-
process.exitCode = 1;
|
|
24757
|
-
}).finally(() => closeDb());
|
|
24876
|
+
program.parseAsync().catch(reportCliError).finally(() => closeDb());
|