@staff0rd/assist 0.372.0 → 0.374.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 +4 -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 +1235 -616
- package/package.json +12 -2
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.374.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -27,7 +27,15 @@ var package_default = {
|
|
|
27
27
|
build: "tsup",
|
|
28
28
|
start: "node dist/index.js",
|
|
29
29
|
"check:types": "tsc --noEmit",
|
|
30
|
-
test: "vitest run"
|
|
30
|
+
test: "vitest run",
|
|
31
|
+
"verify:lint": "oxlint --config .oxlintrc.json src",
|
|
32
|
+
"verify:format": "oxfmt .",
|
|
33
|
+
"verify:knip": "knip --no-progress --treat-config-hints-as-errors",
|
|
34
|
+
"verify:duplicate-code": 'jscpd --format typescript,tsx,python --exitCode 1 --ignore "**/*.test.*,**/.venv/**" -r consoleFull src',
|
|
35
|
+
"verify:test": "vitest run --changed --passWithNoTests --reporter dot",
|
|
36
|
+
"verify:config-keys": "tsx src/index.ts verify config-keys",
|
|
37
|
+
"verify:react-build": "esbuild src/commands/sessions/web/ui/App.tsx --bundle --minify --format=iife --target=es2020 --outfile=dist/commands/sessions/web/bundle.verify.js --jsx=automatic --jsx-import-source=react --define:process.env.NODE_ENV='production'",
|
|
38
|
+
"verify:circular-deps": "skott --displayMode=raw --showCircularDependencies --exitCodeOnCircularDependencies=1 src/index.ts"
|
|
31
39
|
},
|
|
32
40
|
overrides: {
|
|
33
41
|
lodash: "^4.18.0",
|
|
@@ -107,7 +115,9 @@ var package_default = {
|
|
|
107
115
|
"react-dom": "^19.2.4",
|
|
108
116
|
"react-router": "^7.14.0",
|
|
109
117
|
"semantic-release": "^25.0.2",
|
|
118
|
+
skott: "^0.35.11",
|
|
110
119
|
tsup: "^8.5.1",
|
|
120
|
+
tsx: "^4.23.0",
|
|
111
121
|
vitest: "^4.0.18"
|
|
112
122
|
}
|
|
113
123
|
};
|
|
@@ -120,8 +130,8 @@ var ITEM_ID_ENV = "ASSIST_BACKLOG_ITEM_ID";
|
|
|
120
130
|
function resolveSessionItemId() {
|
|
121
131
|
const raw = process.env[ITEM_ID_ENV];
|
|
122
132
|
if (!raw) return null;
|
|
123
|
-
const
|
|
124
|
-
return Number.isInteger(
|
|
133
|
+
const id = Number.parseInt(raw, 10);
|
|
134
|
+
return Number.isInteger(id) && id > 0 ? id : null;
|
|
125
135
|
}
|
|
126
136
|
|
|
127
137
|
// src/shared/db/getDb.ts
|
|
@@ -1005,9 +1015,9 @@ async function closeDb() {
|
|
|
1005
1015
|
}
|
|
1006
1016
|
|
|
1007
1017
|
// src/shared/db/recordGitRef.ts
|
|
1008
|
-
async function recordGitRef(db,
|
|
1018
|
+
async function recordGitRef(db, itemId2, ref) {
|
|
1009
1019
|
await db.insert(itemGitRefs).values({
|
|
1010
|
-
itemId,
|
|
1020
|
+
itemId: itemId2,
|
|
1011
1021
|
kind: ref.kind,
|
|
1012
1022
|
ref: ref.ref,
|
|
1013
1023
|
title: ref.title ?? null,
|
|
@@ -1025,15 +1035,15 @@ async function recordGitRef(db, itemId, ref) {
|
|
|
1025
1035
|
|
|
1026
1036
|
// src/shared/db/recordSessionRefs.ts
|
|
1027
1037
|
async function recordSessionRefs(refs) {
|
|
1028
|
-
const
|
|
1029
|
-
if (
|
|
1030
|
-
await persist(
|
|
1038
|
+
const itemId2 = resolveSessionItemId();
|
|
1039
|
+
if (itemId2 === null || refs.length === 0) return;
|
|
1040
|
+
await persist(itemId2, refs).catch(ignore);
|
|
1031
1041
|
await closeDb().catch(ignore);
|
|
1032
1042
|
}
|
|
1033
|
-
async function persist(
|
|
1043
|
+
async function persist(itemId2, refs) {
|
|
1034
1044
|
const db = await getDb();
|
|
1035
1045
|
for (const ref of refs) {
|
|
1036
|
-
await recordGitRef(db,
|
|
1046
|
+
await recordGitRef(db, itemId2, ref);
|
|
1037
1047
|
}
|
|
1038
1048
|
}
|
|
1039
1049
|
function ignore() {
|
|
@@ -2583,7 +2593,7 @@ function collectHashComments(content, options2) {
|
|
|
2583
2593
|
function collectComments(sourceFile) {
|
|
2584
2594
|
const seen = /* @__PURE__ */ new Set();
|
|
2585
2595
|
const comments3 = [];
|
|
2586
|
-
const
|
|
2596
|
+
const collect6 = (node) => {
|
|
2587
2597
|
for (const range of [
|
|
2588
2598
|
...node.getLeadingCommentRanges(),
|
|
2589
2599
|
...node.getTrailingCommentRanges()
|
|
@@ -2594,8 +2604,8 @@ function collectComments(sourceFile) {
|
|
|
2594
2604
|
comments3.push({ pos, text: range.getText() });
|
|
2595
2605
|
}
|
|
2596
2606
|
};
|
|
2597
|
-
|
|
2598
|
-
sourceFile.forEachDescendant(
|
|
2607
|
+
collect6(sourceFile);
|
|
2608
|
+
sourceFile.forEachDescendant(collect6);
|
|
2599
2609
|
return comments3;
|
|
2600
2610
|
}
|
|
2601
2611
|
|
|
@@ -2780,6 +2790,104 @@ Total: ${findings.length} comment(s)`);
|
|
|
2780
2790
|
process.exit(1);
|
|
2781
2791
|
}
|
|
2782
2792
|
|
|
2793
|
+
// src/shared/configHelp.ts
|
|
2794
|
+
var documentedKeys = /* @__PURE__ */ new Set();
|
|
2795
|
+
function getDocumentedConfigKeys() {
|
|
2796
|
+
return documentedKeys;
|
|
2797
|
+
}
|
|
2798
|
+
function renderConfigHelp(entries, preamble) {
|
|
2799
|
+
const width = Math.max(...entries.map((entry) => entry.setter.length));
|
|
2800
|
+
const lines = entries.map(
|
|
2801
|
+
(entry) => ` ${entry.setter.padEnd(width)} # ${entry.note}`
|
|
2802
|
+
);
|
|
2803
|
+
const body = preamble ? ["", preamble, "", "Config:", ...lines] : ["", "Config:", ...lines];
|
|
2804
|
+
return body.join("\n");
|
|
2805
|
+
}
|
|
2806
|
+
function configHelp(command, entries, preamble) {
|
|
2807
|
+
for (const entry of entries) documentedKeys.add(entry.key);
|
|
2808
|
+
return command.addHelpText(
|
|
2809
|
+
"after",
|
|
2810
|
+
() => renderConfigHelp(entries, preamble)
|
|
2811
|
+
);
|
|
2812
|
+
}
|
|
2813
|
+
|
|
2814
|
+
// src/shared/enumerateConfigLeafKeys.ts
|
|
2815
|
+
var WRAPPER_TYPES = /* @__PURE__ */ new Set([
|
|
2816
|
+
"optional",
|
|
2817
|
+
"default",
|
|
2818
|
+
"nullable",
|
|
2819
|
+
"nullish",
|
|
2820
|
+
"readonly",
|
|
2821
|
+
"catch"
|
|
2822
|
+
]);
|
|
2823
|
+
function unwrap(node) {
|
|
2824
|
+
let current = node;
|
|
2825
|
+
while (current.def && WRAPPER_TYPES.has(current.def.type ?? "") && current.def.innerType) {
|
|
2826
|
+
current = current.def.innerType;
|
|
2827
|
+
}
|
|
2828
|
+
return current;
|
|
2829
|
+
}
|
|
2830
|
+
function collect(node, prefix2, out) {
|
|
2831
|
+
const unwrapped = unwrap(node);
|
|
2832
|
+
const shape = unwrapped.def?.shape;
|
|
2833
|
+
if (unwrapped.def?.type === "object" && shape) {
|
|
2834
|
+
for (const [key, child] of Object.entries(shape)) {
|
|
2835
|
+
collect(child, prefix2 ? `${prefix2}.${key}` : key, out);
|
|
2836
|
+
}
|
|
2837
|
+
return;
|
|
2838
|
+
}
|
|
2839
|
+
out.push(prefix2);
|
|
2840
|
+
}
|
|
2841
|
+
function enumerateConfigLeafKeys(schema2) {
|
|
2842
|
+
const out = [];
|
|
2843
|
+
collect(schema2, "", out);
|
|
2844
|
+
return out.sort();
|
|
2845
|
+
}
|
|
2846
|
+
|
|
2847
|
+
// src/commands/verify/pendingConfigDocumentation.ts
|
|
2848
|
+
var pendingConfigDocumentation = /* @__PURE__ */ new Set();
|
|
2849
|
+
|
|
2850
|
+
// src/commands/verify/configKeys.ts
|
|
2851
|
+
function section(label2, keys) {
|
|
2852
|
+
if (keys.length === 0) return void 0;
|
|
2853
|
+
const list5 = keys.sort().map((key) => ` ${key}`).join("\n");
|
|
2854
|
+
return `${label2}:
|
|
2855
|
+
${list5}`;
|
|
2856
|
+
}
|
|
2857
|
+
function configKeys() {
|
|
2858
|
+
const schemaKeys = new Set(enumerateConfigLeafKeys(assistConfigSchema));
|
|
2859
|
+
const documented = getDocumentedConfigKeys();
|
|
2860
|
+
const pending = pendingConfigDocumentation;
|
|
2861
|
+
const problems = [
|
|
2862
|
+
section(
|
|
2863
|
+
"Config keys documented by no command (surface them with configHelp)",
|
|
2864
|
+
[...schemaKeys].filter(
|
|
2865
|
+
(key) => !documented.has(key) && !pending.has(key)
|
|
2866
|
+
)
|
|
2867
|
+
),
|
|
2868
|
+
section(
|
|
2869
|
+
"Documented config keys not present in assistConfigSchema",
|
|
2870
|
+
[...documented].filter((key) => !schemaKeys.has(key))
|
|
2871
|
+
),
|
|
2872
|
+
section(
|
|
2873
|
+
"Keys now documented but still in pendingConfigDocumentation (remove them)",
|
|
2874
|
+
[...pending].filter((key) => documented.has(key))
|
|
2875
|
+
),
|
|
2876
|
+
section(
|
|
2877
|
+
"pendingConfigDocumentation lists keys not in assistConfigSchema (remove them)",
|
|
2878
|
+
[...pending].filter((key) => !schemaKeys.has(key))
|
|
2879
|
+
)
|
|
2880
|
+
].filter((problem) => problem !== void 0);
|
|
2881
|
+
if (problems.length > 0) {
|
|
2882
|
+
console.log(problems.join("\n\n"));
|
|
2883
|
+
process.exit(1);
|
|
2884
|
+
}
|
|
2885
|
+
console.log(
|
|
2886
|
+
`All ${schemaKeys.size} config keys are surfaced in --help or pending documentation.`
|
|
2887
|
+
);
|
|
2888
|
+
process.exit(0);
|
|
2889
|
+
}
|
|
2890
|
+
|
|
2783
2891
|
// src/commands/verify/forbiddenStrings/index.ts
|
|
2784
2892
|
import { existsSync as existsSync12, readFileSync as readFileSync9 } from "fs";
|
|
2785
2893
|
|
|
@@ -4082,6 +4190,13 @@ function registerBackup(program2) {
|
|
|
4082
4190
|
scheduleCommand.command("remove").description(
|
|
4083
4191
|
"Remove the marked backup schedule block, leaving other crontab lines intact"
|
|
4084
4192
|
).action(scheduleRemove);
|
|
4193
|
+
configHelp(backupCommand, [
|
|
4194
|
+
{
|
|
4195
|
+
key: "backup.dir",
|
|
4196
|
+
setter: "assist config set backup.dir ~/.assist/backups",
|
|
4197
|
+
note: "directory dumps are written to (default: ~/.assist/backups)"
|
|
4198
|
+
}
|
|
4199
|
+
]);
|
|
4085
4200
|
}
|
|
4086
4201
|
|
|
4087
4202
|
// src/commands/backlog/next.ts
|
|
@@ -4120,6 +4235,24 @@ function hasLocalChanges() {
|
|
|
4120
4235
|
}
|
|
4121
4236
|
}
|
|
4122
4237
|
|
|
4238
|
+
// src/commands/backlog/formatItemId.ts
|
|
4239
|
+
var InvalidItemIdError = class extends Error {
|
|
4240
|
+
name = "InvalidItemIdError";
|
|
4241
|
+
};
|
|
4242
|
+
function formatItemId(id) {
|
|
4243
|
+
return `a${id}`;
|
|
4244
|
+
}
|
|
4245
|
+
var itemIdPattern = /^a?(\d+)$/;
|
|
4246
|
+
function parseItemId(input) {
|
|
4247
|
+
const match = itemIdPattern.exec(input.trim());
|
|
4248
|
+
if (!match) {
|
|
4249
|
+
throw new InvalidItemIdError(
|
|
4250
|
+
`Invalid backlog item id "${input}" (e.g. a574 or 574).`
|
|
4251
|
+
);
|
|
4252
|
+
}
|
|
4253
|
+
return Number.parseInt(match[1], 10);
|
|
4254
|
+
}
|
|
4255
|
+
|
|
4123
4256
|
// src/commands/backlog/acquireLock.ts
|
|
4124
4257
|
import {
|
|
4125
4258
|
existsSync as existsSync16,
|
|
@@ -4133,8 +4266,8 @@ import { join as join12 } from "path";
|
|
|
4133
4266
|
function getLocksDir() {
|
|
4134
4267
|
return join12(homedir4(), ".assist", "locks");
|
|
4135
4268
|
}
|
|
4136
|
-
function getLockPath(
|
|
4137
|
-
return join12(getLocksDir(), `lock-${
|
|
4269
|
+
function getLockPath(itemId2) {
|
|
4270
|
+
return join12(getLocksDir(), `lock-${itemId2}.json`);
|
|
4138
4271
|
}
|
|
4139
4272
|
function isProcessAlive(pid) {
|
|
4140
4273
|
try {
|
|
@@ -4144,8 +4277,8 @@ function isProcessAlive(pid) {
|
|
|
4144
4277
|
return false;
|
|
4145
4278
|
}
|
|
4146
4279
|
}
|
|
4147
|
-
function isLockedByOther(
|
|
4148
|
-
const lockPath = getLockPath(
|
|
4280
|
+
function isLockedByOther(itemId2) {
|
|
4281
|
+
const lockPath = getLockPath(itemId2);
|
|
4149
4282
|
if (!existsSync16(lockPath)) return false;
|
|
4150
4283
|
try {
|
|
4151
4284
|
const lock2 = JSON.parse(readFileSync12(lockPath, "utf8"));
|
|
@@ -4155,15 +4288,15 @@ function isLockedByOther(itemId) {
|
|
|
4155
4288
|
return false;
|
|
4156
4289
|
}
|
|
4157
4290
|
}
|
|
4158
|
-
function acquireLock(
|
|
4291
|
+
function acquireLock(itemId2) {
|
|
4159
4292
|
mkdirSync4(getLocksDir(), { recursive: true });
|
|
4160
4293
|
writeFileSync14(
|
|
4161
|
-
getLockPath(
|
|
4294
|
+
getLockPath(itemId2),
|
|
4162
4295
|
JSON.stringify({ pid: process.pid, timestamp: (/* @__PURE__ */ new Date()).toISOString() })
|
|
4163
4296
|
);
|
|
4164
4297
|
}
|
|
4165
|
-
function releaseLock(
|
|
4166
|
-
const lockPath = getLockPath(
|
|
4298
|
+
function releaseLock(itemId2) {
|
|
4299
|
+
const lockPath = getLockPath(itemId2);
|
|
4167
4300
|
try {
|
|
4168
4301
|
unlinkSync2(lockPath);
|
|
4169
4302
|
} catch {
|
|
@@ -4366,27 +4499,27 @@ import { join as join14 } from "path";
|
|
|
4366
4499
|
function getControlsDir() {
|
|
4367
4500
|
return join14(homedir6(), ".assist", "controls");
|
|
4368
4501
|
}
|
|
4369
|
-
function getPausePath(
|
|
4370
|
-
return join14(getControlsDir(), `pause-${
|
|
4502
|
+
function getPausePath(itemId2) {
|
|
4503
|
+
return join14(getControlsDir(), `pause-${itemId2}.json`);
|
|
4371
4504
|
}
|
|
4372
|
-
function requestPause(
|
|
4505
|
+
function requestPause(itemId2) {
|
|
4373
4506
|
mkdirSync6(getControlsDir(), { recursive: true });
|
|
4374
4507
|
writeFileSync16(
|
|
4375
|
-
getPausePath(
|
|
4508
|
+
getPausePath(itemId2),
|
|
4376
4509
|
JSON.stringify({ timestamp: (/* @__PURE__ */ new Date()).toISOString() })
|
|
4377
4510
|
);
|
|
4378
4511
|
}
|
|
4379
|
-
function isPausePending(
|
|
4380
|
-
return existsSync17(getPausePath(
|
|
4512
|
+
function isPausePending(itemId2) {
|
|
4513
|
+
return existsSync17(getPausePath(itemId2));
|
|
4381
4514
|
}
|
|
4382
|
-
function clearPause(
|
|
4515
|
+
function clearPause(itemId2) {
|
|
4383
4516
|
try {
|
|
4384
|
-
unlinkSync3(getPausePath(
|
|
4517
|
+
unlinkSync3(getPausePath(itemId2));
|
|
4385
4518
|
} catch {
|
|
4386
4519
|
}
|
|
4387
4520
|
}
|
|
4388
|
-
function consumePause(
|
|
4389
|
-
const pausePath = getPausePath(
|
|
4521
|
+
function consumePause(itemId2) {
|
|
4522
|
+
const pausePath = getPausePath(itemId2);
|
|
4390
4523
|
if (!existsSync17(pausePath)) return false;
|
|
4391
4524
|
try {
|
|
4392
4525
|
unlinkSync3(pausePath);
|
|
@@ -4716,8 +4849,8 @@ function rowToGitRef(r) {
|
|
|
4716
4849
|
if (r.createdAt != null) ref.createdAt = new Date(r.createdAt).toISOString();
|
|
4717
4850
|
return ref;
|
|
4718
4851
|
}
|
|
4719
|
-
function attachGitRefs(item, rel,
|
|
4720
|
-
const gitRefs = (rel.gitRefs.get(
|
|
4852
|
+
function attachGitRefs(item, rel, id) {
|
|
4853
|
+
const gitRefs = (rel.gitRefs.get(id) ?? []).map(rowToGitRef);
|
|
4721
4854
|
if (gitRefs.length > 0) item.gitRefs = gitRefs;
|
|
4722
4855
|
}
|
|
4723
4856
|
|
|
@@ -4730,8 +4863,8 @@ function rowToSubtask(s) {
|
|
|
4730
4863
|
if (s.description != null) subtask.description = s.description;
|
|
4731
4864
|
return subtask;
|
|
4732
4865
|
}
|
|
4733
|
-
function attachSubtasks(item, rel,
|
|
4734
|
-
const subtasks = (rel.subtasks.get(
|
|
4866
|
+
function attachSubtasks(item, rel, id) {
|
|
4867
|
+
const subtasks = (rel.subtasks.get(id) ?? []).map(rowToSubtask);
|
|
4735
4868
|
if (subtasks.length > 0) item.subtasks = subtasks;
|
|
4736
4869
|
}
|
|
4737
4870
|
|
|
@@ -4744,8 +4877,8 @@ function rowToUsage(u) {
|
|
|
4744
4877
|
activeMs: u.activeMs
|
|
4745
4878
|
};
|
|
4746
4879
|
}
|
|
4747
|
-
function attachUsage(item, rel,
|
|
4748
|
-
const usage = (rel.usage.get(
|
|
4880
|
+
function attachUsage(item, rel, id) {
|
|
4881
|
+
const usage = (rel.usage.get(id) ?? []).map(rowToUsage);
|
|
4749
4882
|
if (usage.length > 0) item.phaseUsage = usage;
|
|
4750
4883
|
}
|
|
4751
4884
|
|
|
@@ -4804,17 +4937,17 @@ function baseItem(row) {
|
|
|
4804
4937
|
assignOptionalColumns(item, row);
|
|
4805
4938
|
return item;
|
|
4806
4939
|
}
|
|
4807
|
-
function attachComments(item, rel,
|
|
4808
|
-
const comments3 = (rel.comments.get(
|
|
4940
|
+
function attachComments(item, rel, id) {
|
|
4941
|
+
const comments3 = (rel.comments.get(id) ?? []).map(rowToComment);
|
|
4809
4942
|
if (comments3.length > 0) item.comments = comments3;
|
|
4810
4943
|
}
|
|
4811
|
-
function attachLinks(item, rel,
|
|
4812
|
-
const links2 = (rel.links.get(
|
|
4944
|
+
function attachLinks(item, rel, id) {
|
|
4945
|
+
const links2 = (rel.links.get(id) ?? []).map(rowToLink);
|
|
4813
4946
|
if (links2.length > 0) item.links = links2;
|
|
4814
4947
|
}
|
|
4815
|
-
function attachPlan(item, rel,
|
|
4816
|
-
const phases = rel.phases.get(
|
|
4817
|
-
if (phases.length > 0) item.plan = buildPlan(phases, rel.tasks.get(
|
|
4948
|
+
function attachPlan(item, rel, id) {
|
|
4949
|
+
const phases = rel.phases.get(id) ?? [];
|
|
4950
|
+
if (phases.length > 0) item.plan = buildPlan(phases, rel.tasks.get(id) ?? []);
|
|
4818
4951
|
}
|
|
4819
4952
|
function rowToItem(row, rel) {
|
|
4820
4953
|
const item = baseItem(row);
|
|
@@ -4966,8 +5099,8 @@ async function ensureMigrated(orm, dir, origin) {
|
|
|
4966
5099
|
|
|
4967
5100
|
// src/commands/backlog/deleteItem.ts
|
|
4968
5101
|
import { eq as eq4 } from "drizzle-orm";
|
|
4969
|
-
async function deleteItem(orm,
|
|
4970
|
-
const [row] = await orm.delete(items).where(eq4(items.id,
|
|
5102
|
+
async function deleteItem(orm, id) {
|
|
5103
|
+
const [row] = await orm.delete(items).where(eq4(items.id, id)).returning({ name: items.name });
|
|
4971
5104
|
return row?.name;
|
|
4972
5105
|
}
|
|
4973
5106
|
|
|
@@ -5047,10 +5180,10 @@ function getCurrentOrigin(cwd) {
|
|
|
5047
5180
|
|
|
5048
5181
|
// src/commands/backlog/loadItem.ts
|
|
5049
5182
|
import { eq as eq5 } from "drizzle-orm";
|
|
5050
|
-
async function loadItem(orm,
|
|
5051
|
-
const [row] = await orm.select().from(items).where(eq5(items.id,
|
|
5183
|
+
async function loadItem(orm, id) {
|
|
5184
|
+
const [row] = await orm.select().from(items).where(eq5(items.id, id));
|
|
5052
5185
|
if (!row) return void 0;
|
|
5053
|
-
const rel = await loadRelations(orm, [
|
|
5186
|
+
const rel = await loadRelations(orm, [id], {
|
|
5054
5187
|
includeUsage: true,
|
|
5055
5188
|
includeGitRefs: true
|
|
5056
5189
|
});
|
|
@@ -5087,14 +5220,14 @@ async function searchItemIds(orm, query, origin) {
|
|
|
5087
5220
|
|
|
5088
5221
|
// src/commands/backlog/updateCurrentPhase.ts
|
|
5089
5222
|
import { eq as eq9 } from "drizzle-orm";
|
|
5090
|
-
async function updateCurrentPhase(orm,
|
|
5091
|
-
await orm.update(items).set({ currentPhase: phase }).where(eq9(items.id,
|
|
5223
|
+
async function updateCurrentPhase(orm, id, phase) {
|
|
5224
|
+
await orm.update(items).set({ currentPhase: phase }).where(eq9(items.id, id));
|
|
5092
5225
|
}
|
|
5093
5226
|
|
|
5094
5227
|
// src/commands/backlog/updateStatus.ts
|
|
5095
5228
|
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,
|
|
5229
|
+
async function updateStatus(orm, id, status2) {
|
|
5230
|
+
const [row] = await orm.update(items).set({ status: status2 }).where(eq10(items.id, id)).returning({ name: items.name });
|
|
5098
5231
|
return row?.name;
|
|
5099
5232
|
}
|
|
5100
5233
|
|
|
@@ -5126,35 +5259,39 @@ async function searchBacklog(query) {
|
|
|
5126
5259
|
const allItems = await loadAllItems(orm, origin);
|
|
5127
5260
|
return allItems.filter((item) => ids.includes(item.id));
|
|
5128
5261
|
}
|
|
5129
|
-
async function findOneItem(
|
|
5130
|
-
const numId =
|
|
5262
|
+
async function findOneItem(id) {
|
|
5263
|
+
const numId = parseItemId(id);
|
|
5131
5264
|
const { orm } = await getReady();
|
|
5132
|
-
const item =
|
|
5265
|
+
const item = await loadItem(orm, numId);
|
|
5133
5266
|
if (!item) {
|
|
5134
|
-
console.log(chalk37.red(`Item
|
|
5267
|
+
console.log(chalk37.red(`Item ${formatItemId(numId)} not found.`));
|
|
5135
5268
|
return void 0;
|
|
5136
5269
|
}
|
|
5137
5270
|
return { orm, item };
|
|
5138
5271
|
}
|
|
5139
|
-
async function setStatus(
|
|
5272
|
+
async function setStatus(id, status2) {
|
|
5140
5273
|
const { orm } = await getReady();
|
|
5141
|
-
const
|
|
5142
|
-
|
|
5274
|
+
const numId = parseItemId(id);
|
|
5275
|
+
const name = await updateStatus(orm, numId, status2);
|
|
5276
|
+
if (name === void 0)
|
|
5277
|
+
console.log(chalk37.red(`Item ${formatItemId(numId)} not found.`));
|
|
5143
5278
|
return name;
|
|
5144
5279
|
}
|
|
5145
|
-
async function setCurrentPhase(
|
|
5280
|
+
async function setCurrentPhase(id, phase) {
|
|
5146
5281
|
const { orm } = await getReady();
|
|
5147
|
-
await updateCurrentPhase(orm,
|
|
5282
|
+
await updateCurrentPhase(orm, parseItemId(id), phase);
|
|
5148
5283
|
}
|
|
5149
|
-
async function removeItem(
|
|
5284
|
+
async function removeItem(id) {
|
|
5150
5285
|
const { orm } = await getReady();
|
|
5151
|
-
const
|
|
5152
|
-
|
|
5286
|
+
const numId = parseItemId(id);
|
|
5287
|
+
const name = await deleteItem(orm, numId);
|
|
5288
|
+
if (name === void 0)
|
|
5289
|
+
console.log(chalk37.red(`Item ${formatItemId(numId)} not found.`));
|
|
5153
5290
|
return name;
|
|
5154
5291
|
}
|
|
5155
5292
|
|
|
5156
5293
|
// src/commands/backlog/handleReviewResult.ts
|
|
5157
|
-
async function handleReviewResult(
|
|
5294
|
+
async function handleReviewResult(id, review2) {
|
|
5158
5295
|
if (review2.kind === "fail" || review2.kind === "abort")
|
|
5159
5296
|
return { kind: "stop", success: false };
|
|
5160
5297
|
if (review2.kind === "paused") return { kind: "stop", success: true };
|
|
@@ -5164,12 +5301,12 @@ async function handleReviewResult(id2, review2) {
|
|
|
5164
5301
|
startPhase: review2.targetPhase,
|
|
5165
5302
|
plan: review2.plan
|
|
5166
5303
|
};
|
|
5167
|
-
await ensureDone(
|
|
5304
|
+
await ensureDone(id);
|
|
5168
5305
|
return { kind: "stop", success: true };
|
|
5169
5306
|
}
|
|
5170
|
-
async function ensureDone(
|
|
5307
|
+
async function ensureDone(id) {
|
|
5171
5308
|
try {
|
|
5172
|
-
await setStatus(
|
|
5309
|
+
await setStatus(id, "done");
|
|
5173
5310
|
} catch {
|
|
5174
5311
|
}
|
|
5175
5312
|
}
|
|
@@ -5191,20 +5328,24 @@ function resolvePlan(item) {
|
|
|
5191
5328
|
}
|
|
5192
5329
|
|
|
5193
5330
|
// src/commands/backlog/prepareRun.ts
|
|
5194
|
-
async function prepareRun(
|
|
5195
|
-
const found = await findOneItem(
|
|
5331
|
+
async function prepareRun(id) {
|
|
5332
|
+
const found = await findOneItem(id);
|
|
5196
5333
|
if (!found) return void 0;
|
|
5197
5334
|
const { item } = found;
|
|
5198
5335
|
const plan2 = resolvePlan(item);
|
|
5199
5336
|
const startPhase = (item.currentPhase ?? 1) - 1;
|
|
5200
5337
|
if (item.status === "done") {
|
|
5201
|
-
console.log(
|
|
5338
|
+
console.log(
|
|
5339
|
+
chalk38.green(`Already done: ${formatItemId(item.id)}: ${item.name}`)
|
|
5340
|
+
);
|
|
5202
5341
|
return void 0;
|
|
5203
5342
|
}
|
|
5204
5343
|
if (startPhase > plan2.length) {
|
|
5205
|
-
await setStatus(
|
|
5344
|
+
await setStatus(id, "done");
|
|
5206
5345
|
console.log(
|
|
5207
|
-
chalk38.green(
|
|
5346
|
+
chalk38.green(
|
|
5347
|
+
`All phases already complete for ${formatItemId(item.id)}: ${item.name}`
|
|
5348
|
+
)
|
|
5208
5349
|
);
|
|
5209
5350
|
return void 0;
|
|
5210
5351
|
}
|
|
@@ -5284,10 +5425,10 @@ function buildCommentLines(comments3) {
|
|
|
5284
5425
|
return ["", "Comments:", ...comments3.map(formatPromptComment)];
|
|
5285
5426
|
}
|
|
5286
5427
|
function formatPromptComment(entry) {
|
|
5287
|
-
const
|
|
5428
|
+
const id = entry.id !== void 0 ? `#${entry.id} ` : "";
|
|
5288
5429
|
const tag = entry.type === "summary" ? "[summary]" : "[comment]";
|
|
5289
5430
|
const phase = entry.phase !== void 0 ? ` (phase ${entry.phase})` : "";
|
|
5290
|
-
return `${
|
|
5431
|
+
return `${id}${tag}${phase} ${entry.timestamp}
|
|
5291
5432
|
${entry.text}`;
|
|
5292
5433
|
}
|
|
5293
5434
|
|
|
@@ -5305,16 +5446,16 @@ function buildAuthoredPhasePrompt(item, phaseNumber, phase) {
|
|
|
5305
5446
|
"When you have completed all tasks for this phase, run /verify to check your work.",
|
|
5306
5447
|
...buildManualCheckLines(manualChecks),
|
|
5307
5448
|
"",
|
|
5308
|
-
`Post concise comments for any notable findings or changes using \`assist backlog comment ${item.id} "<text>"\`.`,
|
|
5449
|
+
`Post concise comments for any notable findings or changes using \`assist backlog comment ${formatItemId(item.id)} "<text>"\`.`,
|
|
5309
5450
|
"",
|
|
5310
|
-
`Once verify passes${confirmSuffix}, run: assist backlog phase-done ${item.id} ${phaseNumber} "<summary>"`,
|
|
5451
|
+
`Once verify passes${confirmSuffix}, run: assist backlog phase-done ${formatItemId(item.id)} ${phaseNumber} "<summary>"`,
|
|
5311
5452
|
"Replace <summary> with a concise summary of what was done in this phase."
|
|
5312
5453
|
].filter((line) => line !== void 0).join("\n");
|
|
5313
5454
|
}
|
|
5314
5455
|
function buildContextLines(item, phaseNumber, phase) {
|
|
5315
5456
|
const ac = item.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join("\n");
|
|
5316
5457
|
return [
|
|
5317
|
-
`You are implementing phase ${phaseNumber} of backlog item
|
|
5458
|
+
`You are implementing phase ${phaseNumber} of backlog item ${formatItemId(item.id)}: ${item.name}`,
|
|
5318
5459
|
"",
|
|
5319
5460
|
item.description ? `Description: ${item.description}` : "",
|
|
5320
5461
|
"",
|
|
@@ -5355,8 +5496,9 @@ function formatTasks(phase) {
|
|
|
5355
5496
|
// src/commands/backlog/buildReviewPrompt.ts
|
|
5356
5497
|
function buildReviewPrompt(item, phaseNumber) {
|
|
5357
5498
|
const acLines = item.acceptanceCriteria.map((ac, i) => `${i + 1}. ${ac}`).join("\n");
|
|
5499
|
+
const itemId2 = formatItemId(item.id);
|
|
5358
5500
|
return [
|
|
5359
|
-
`You are reviewing backlog item
|
|
5501
|
+
`You are reviewing backlog item ${itemId2}: ${item.name}`,
|
|
5360
5502
|
"",
|
|
5361
5503
|
item.description ? `Description: ${item.description}` : "",
|
|
5362
5504
|
"",
|
|
@@ -5369,9 +5511,9 @@ function buildReviewPrompt(item, phaseNumber) {
|
|
|
5369
5511
|
"",
|
|
5370
5512
|
"If any criterion fails, fix the issue and re-verify.",
|
|
5371
5513
|
"",
|
|
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 ${
|
|
5514
|
+
`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
5515
|
"",
|
|
5374
|
-
`Post concise comments for any notable findings or changes using \`assist backlog comment ${
|
|
5516
|
+
`Post concise comments for any notable findings or changes using \`assist backlog comment ${itemId2} "<text>"\`.`,
|
|
5375
5517
|
"",
|
|
5376
5518
|
"After all criteria pass, ask the user to confirm any manual checks",
|
|
5377
5519
|
"(e.g. end-to-end behaviour they need to verify themselves).",
|
|
@@ -5384,13 +5526,13 @@ function buildReviewPrompt(item, phaseNumber) {
|
|
|
5384
5526
|
"",
|
|
5385
5527
|
"Once the user gives that explicit completion instruction:",
|
|
5386
5528
|
"1. Run: /commit",
|
|
5387
|
-
`2. Run: assist backlog done ${
|
|
5388
|
-
`3. Run: assist backlog phase-done ${
|
|
5529
|
+
`2. Run: assist backlog done ${itemId2} "<summary>"`,
|
|
5530
|
+
`3. Run: assist backlog phase-done ${itemId2} ${phaseNumber} "done"`,
|
|
5389
5531
|
"",
|
|
5390
5532
|
"Precedence rules \u2014 read carefully:",
|
|
5391
5533
|
"- 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
5534
|
"- 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 ${
|
|
5535
|
+
`- 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
5536
|
].filter((line) => line !== void 0).join("\n");
|
|
5395
5537
|
}
|
|
5396
5538
|
|
|
@@ -5525,15 +5667,15 @@ function cleanupSignal() {
|
|
|
5525
5667
|
unlinkSync4(statusPath);
|
|
5526
5668
|
}
|
|
5527
5669
|
}
|
|
5528
|
-
async function isTerminalStatus(
|
|
5670
|
+
async function isTerminalStatus(itemId2) {
|
|
5529
5671
|
const { orm } = await getReady();
|
|
5530
|
-
const item = await loadItem(orm,
|
|
5672
|
+
const item = await loadItem(orm, itemId2);
|
|
5531
5673
|
return item?.status === "done" || item?.status === "wontdo";
|
|
5532
5674
|
}
|
|
5533
|
-
async function resolvePhaseResult(phaseIndex,
|
|
5675
|
+
async function resolvePhaseResult(phaseIndex, itemId2) {
|
|
5534
5676
|
const signalPath = getSignalPath();
|
|
5535
5677
|
if (!signalPath || !existsSync22(signalPath)) {
|
|
5536
|
-
if (await isTerminalStatus(
|
|
5678
|
+
if (await isTerminalStatus(itemId2)) return -1;
|
|
5537
5679
|
const action = await handleIncompletePhase();
|
|
5538
5680
|
if (action === "abort") return -1;
|
|
5539
5681
|
return action === "skip" ? phaseIndex + 1 : phaseIndex;
|
|
@@ -5617,8 +5759,8 @@ async function executePhase(item, phaseIndex, phases, spawnOptions, totalPhases
|
|
|
5617
5759
|
}
|
|
5618
5760
|
|
|
5619
5761
|
// src/commands/backlog/reloadPlan.ts
|
|
5620
|
-
async function reloadPlan(
|
|
5621
|
-
const found = await findOneItem(String(
|
|
5762
|
+
async function reloadPlan(id) {
|
|
5763
|
+
const found = await findOneItem(String(id));
|
|
5622
5764
|
if (!found) return void 0;
|
|
5623
5765
|
return resolvePlan(found.item);
|
|
5624
5766
|
}
|
|
@@ -5690,23 +5832,23 @@ async function runOnce(item, startPhase, plan2, spawnOptions) {
|
|
|
5690
5832
|
}
|
|
5691
5833
|
|
|
5692
5834
|
// src/commands/backlog/run.ts
|
|
5693
|
-
async function run2(
|
|
5694
|
-
const prepared = await prepareRun(
|
|
5835
|
+
async function run2(id, spawnOptions) {
|
|
5836
|
+
const prepared = await prepareRun(id);
|
|
5695
5837
|
if (!prepared) return false;
|
|
5696
5838
|
await ensureStoryBranch(prepared.item);
|
|
5697
|
-
await setStatus(
|
|
5839
|
+
await setStatus(id, "in-progress");
|
|
5698
5840
|
discardStalePause(prepared.item.id);
|
|
5699
|
-
logProgress(
|
|
5700
|
-
return runPrepared(
|
|
5841
|
+
logProgress(prepared);
|
|
5842
|
+
return runPrepared(id, prepared, spawnOptions);
|
|
5701
5843
|
}
|
|
5702
|
-
function discardStalePause(
|
|
5703
|
-
if (!isPausePending(
|
|
5704
|
-
clearPause(
|
|
5844
|
+
function discardStalePause(itemId2) {
|
|
5845
|
+
if (!isPausePending(itemId2)) return;
|
|
5846
|
+
clearPause(itemId2);
|
|
5705
5847
|
appendDaemonLog(
|
|
5706
|
-
`backlog run ${
|
|
5848
|
+
`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
5849
|
);
|
|
5708
5850
|
}
|
|
5709
|
-
async function runPrepared(
|
|
5851
|
+
async function runPrepared(id, prepared, spawnOptions) {
|
|
5710
5852
|
const { item } = prepared;
|
|
5711
5853
|
let { plan: plan2, startPhase } = prepared;
|
|
5712
5854
|
acquireLock(item.id);
|
|
@@ -5714,7 +5856,7 @@ async function runPrepared(id2, prepared, spawnOptions) {
|
|
|
5714
5856
|
while (true) {
|
|
5715
5857
|
const review2 = await runOnce(item, startPhase, plan2, spawnOptions);
|
|
5716
5858
|
spawnOptions = withoutResumeSession(spawnOptions);
|
|
5717
|
-
const outcome = await handleReviewResult(
|
|
5859
|
+
const outcome = await handleReviewResult(id, review2);
|
|
5718
5860
|
if (outcome.kind === "stop") {
|
|
5719
5861
|
return outcome.success;
|
|
5720
5862
|
}
|
|
@@ -5725,8 +5867,10 @@ async function runPrepared(id2, prepared, spawnOptions) {
|
|
|
5725
5867
|
releaseLock(item.id);
|
|
5726
5868
|
}
|
|
5727
5869
|
}
|
|
5728
|
-
function logProgress(
|
|
5729
|
-
console.log(
|
|
5870
|
+
function logProgress({ plan: plan2, startPhase, item }) {
|
|
5871
|
+
console.log(
|
|
5872
|
+
chalk42.bold(`Running plan for ${formatItemId(item.id)}: ${item.name}`)
|
|
5873
|
+
);
|
|
5730
5874
|
const totalPhases = plan2.length + 1;
|
|
5731
5875
|
if (startPhase > 0) {
|
|
5732
5876
|
const phaseNumber = startPhase + 1;
|
|
@@ -5742,7 +5886,7 @@ function logProgress(id2, { plan: plan2, startPhase, item }) {
|
|
|
5742
5886
|
|
|
5743
5887
|
// src/commands/backlog/next.ts
|
|
5744
5888
|
function toChoice(item, items2) {
|
|
5745
|
-
const name = `${typeLabel(item.type)}
|
|
5889
|
+
const name = `${typeLabel(item.type)} ${formatItemId(item.id)}: ${item.name}`;
|
|
5746
5890
|
return isBlocked(item, items2) ? { name, disabled: chalk43.red("[blocked]") } : { name };
|
|
5747
5891
|
}
|
|
5748
5892
|
async function selectItem(todo, items2) {
|
|
@@ -5754,20 +5898,24 @@ async function selectItem(todo, items2) {
|
|
|
5754
5898
|
choices: todo.map((i) => toChoice(i, items2))
|
|
5755
5899
|
})
|
|
5756
5900
|
);
|
|
5757
|
-
return selected.match(
|
|
5901
|
+
return selected.match(/(a\d+):/)?.[1] ?? "";
|
|
5758
5902
|
}
|
|
5759
5903
|
function pickResumable(items2) {
|
|
5760
5904
|
const resumable = findResumable(items2);
|
|
5761
5905
|
if (!resumable) return void 0;
|
|
5762
5906
|
console.log(
|
|
5763
|
-
chalk43.bold(
|
|
5907
|
+
chalk43.bold(
|
|
5908
|
+
`Resuming in-progress item ${formatItemId(resumable.id)}: ${resumable.name}`
|
|
5909
|
+
)
|
|
5764
5910
|
);
|
|
5765
|
-
return
|
|
5911
|
+
return formatItemId(resumable.id);
|
|
5766
5912
|
}
|
|
5767
5913
|
function autoSelect(unblocked) {
|
|
5768
5914
|
const item = unblocked[0];
|
|
5769
|
-
console.log(
|
|
5770
|
-
|
|
5915
|
+
console.log(
|
|
5916
|
+
chalk43.bold(`Auto-selecting item ${formatItemId(item.id)}: ${item.name}`)
|
|
5917
|
+
);
|
|
5918
|
+
return formatItemId(item.id);
|
|
5771
5919
|
}
|
|
5772
5920
|
async function pickItem(items2, firstPick = false) {
|
|
5773
5921
|
const resumable = pickResumable(items2);
|
|
@@ -5783,11 +5931,11 @@ async function next(options2, startId) {
|
|
|
5783
5931
|
let pendingId = startId;
|
|
5784
5932
|
let firstPick = true;
|
|
5785
5933
|
while (true) {
|
|
5786
|
-
const
|
|
5934
|
+
const id = pendingId ?? await pickItem(await loadBacklog(), firstPick);
|
|
5787
5935
|
pendingId = void 0;
|
|
5788
5936
|
firstPick = false;
|
|
5789
|
-
if (
|
|
5790
|
-
const completed = await run2(
|
|
5937
|
+
if (id === void 0) return;
|
|
5938
|
+
const completed = await run2(id, options2);
|
|
5791
5939
|
if (!completed || options2?.once) return;
|
|
5792
5940
|
}
|
|
5793
5941
|
}
|
|
@@ -5797,10 +5945,10 @@ import chalk45 from "chalk";
|
|
|
5797
5945
|
|
|
5798
5946
|
// src/commands/backlog/appendComment.ts
|
|
5799
5947
|
import { sql as sql4 } from "drizzle-orm";
|
|
5800
|
-
async function appendComment(orm,
|
|
5948
|
+
async function appendComment(orm, itemId2, text9, opts = {}) {
|
|
5801
5949
|
await orm.insert(comments).values({
|
|
5802
|
-
itemId,
|
|
5803
|
-
idx: sql4`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${
|
|
5950
|
+
itemId: itemId2,
|
|
5951
|
+
idx: sql4`(SELECT COALESCE(MAX(${comments.idx}) + 1, 0) FROM ${comments} WHERE ${comments.itemId} = ${itemId2})`,
|
|
5804
5952
|
text: text9,
|
|
5805
5953
|
phase: opts.phase ?? null,
|
|
5806
5954
|
timestamp: (/* @__PURE__ */ new Date()).toISOString(),
|
|
@@ -5810,12 +5958,12 @@ async function appendComment(orm, itemId, text9, opts = {}) {
|
|
|
5810
5958
|
|
|
5811
5959
|
// src/commands/backlog/checkSubtasksComplete.ts
|
|
5812
5960
|
import chalk44 from "chalk";
|
|
5813
|
-
function checkSubtasksComplete(
|
|
5961
|
+
function checkSubtasksComplete(item) {
|
|
5814
5962
|
const pending = (item.subtasks ?? []).filter((s) => s.status !== "done");
|
|
5815
5963
|
if (pending.length === 0) return true;
|
|
5816
5964
|
console.log(
|
|
5817
5965
|
chalk44.red(
|
|
5818
|
-
`Cannot complete item
|
|
5966
|
+
`Cannot complete item ${formatItemId(item.id)}: ${pending.length} incomplete sub-task(s):`
|
|
5819
5967
|
)
|
|
5820
5968
|
);
|
|
5821
5969
|
for (const subtask of pending) {
|
|
@@ -5825,48 +5973,51 @@ function checkSubtasksComplete(id2, item) {
|
|
|
5825
5973
|
}
|
|
5826
5974
|
|
|
5827
5975
|
// src/commands/backlog/phaseDone.ts
|
|
5828
|
-
async function phaseDone(
|
|
5976
|
+
async function phaseDone(id, phase, summary) {
|
|
5829
5977
|
const phaseNumber = Number.parseInt(phase, 10);
|
|
5830
5978
|
const phaseIndex = phaseNumber - 1;
|
|
5831
|
-
const
|
|
5979
|
+
const itemId2 = parseItemId(id);
|
|
5980
|
+
const label2 = formatItemId(itemId2);
|
|
5832
5981
|
const { orm } = await getReady();
|
|
5833
|
-
const item = await loadItem(orm,
|
|
5982
|
+
const item = await loadItem(orm, itemId2);
|
|
5834
5983
|
if (item === void 0) {
|
|
5835
|
-
console.log(chalk45.red(`Item
|
|
5984
|
+
console.log(chalk45.red(`Item ${label2} not found.`));
|
|
5836
5985
|
return;
|
|
5837
5986
|
}
|
|
5838
5987
|
function signal() {
|
|
5839
5988
|
writeSignal("phase-done", {
|
|
5840
|
-
itemId,
|
|
5989
|
+
itemId: itemId2,
|
|
5841
5990
|
phaseIndex,
|
|
5842
5991
|
completedAt: (/* @__PURE__ */ new Date()).toISOString()
|
|
5843
5992
|
});
|
|
5844
5993
|
}
|
|
5845
5994
|
if (item.status === "done") {
|
|
5846
5995
|
signal();
|
|
5847
|
-
console.log(
|
|
5996
|
+
console.log(
|
|
5997
|
+
chalk45.dim(`Item ${label2} already done, skipping phase advance.`)
|
|
5998
|
+
);
|
|
5848
5999
|
return;
|
|
5849
6000
|
}
|
|
5850
6001
|
const reviewPhaseNumber = resolvePlan(item).length + 1;
|
|
5851
|
-
if (phaseNumber === reviewPhaseNumber && !checkSubtasksComplete(
|
|
6002
|
+
if (phaseNumber === reviewPhaseNumber && !checkSubtasksComplete(item)) {
|
|
5852
6003
|
process.exitCode = 1;
|
|
5853
6004
|
return;
|
|
5854
6005
|
}
|
|
5855
6006
|
signal();
|
|
5856
|
-
await appendComment(orm,
|
|
6007
|
+
await appendComment(orm, itemId2, summary, {
|
|
5857
6008
|
phase: phaseNumber,
|
|
5858
6009
|
type: "summary"
|
|
5859
6010
|
});
|
|
5860
|
-
await setCurrentPhase(
|
|
6011
|
+
await setCurrentPhase(id, phaseNumber + 1);
|
|
5861
6012
|
console.log(
|
|
5862
|
-
chalk45.green(`Phase ${phaseNumber} of item
|
|
6013
|
+
chalk45.green(`Phase ${phaseNumber} of item ${label2} marked as complete.`)
|
|
5863
6014
|
);
|
|
5864
6015
|
}
|
|
5865
6016
|
|
|
5866
6017
|
// src/commands/backlog/plan.ts
|
|
5867
6018
|
import chalk46 from "chalk";
|
|
5868
|
-
async function plan(
|
|
5869
|
-
const found = await findOneItem(
|
|
6019
|
+
async function plan(id) {
|
|
6020
|
+
const found = await findOneItem(id);
|
|
5870
6021
|
if (!found) return;
|
|
5871
6022
|
const { item } = found;
|
|
5872
6023
|
if (!item.plan || item.plan.length === 0) {
|
|
@@ -5890,11 +6041,11 @@ import chalk53 from "chalk";
|
|
|
5890
6041
|
// src/commands/backlog/formatComment.ts
|
|
5891
6042
|
import chalk47 from "chalk";
|
|
5892
6043
|
function formatComment(entry) {
|
|
5893
|
-
const
|
|
6044
|
+
const id = entry.id !== void 0 ? chalk47.dim(`#${entry.id} `) : "";
|
|
5894
6045
|
const tag = entry.type === "summary" ? chalk47.magenta("[summary]") : chalk47.cyan("[comment]");
|
|
5895
6046
|
const phase = entry.phase !== void 0 ? chalk47.dim(` (phase ${entry.phase})`) : "";
|
|
5896
6047
|
const time = chalk47.dim(entry.timestamp);
|
|
5897
|
-
return `${
|
|
6048
|
+
return `${id}${tag}${phase} ${time}
|
|
5898
6049
|
${entry.text}`;
|
|
5899
6050
|
}
|
|
5900
6051
|
|
|
@@ -5991,11 +6142,11 @@ async function printLinks(orm, item) {
|
|
|
5991
6142
|
const typeLabel2 = link3.type === "depends-on" ? chalk49.red("depends-on") : chalk49.blue("relates-to");
|
|
5992
6143
|
if (target) {
|
|
5993
6144
|
console.log(
|
|
5994
|
-
` ${typeLabel2}
|
|
6145
|
+
` ${typeLabel2} ${formatItemId(target.id)} ${target.name} ${chalk49.dim(`(${target.status})`)}`
|
|
5995
6146
|
);
|
|
5996
6147
|
} else {
|
|
5997
6148
|
console.log(
|
|
5998
|
-
` ${typeLabel2}
|
|
6149
|
+
` ${typeLabel2} ${formatItemId(link3.targetId)} ${chalk49.dim("(not found)")}`
|
|
5999
6150
|
);
|
|
6000
6151
|
}
|
|
6001
6152
|
}
|
|
@@ -6061,7 +6212,7 @@ function printSubtasks(item) {
|
|
|
6061
6212
|
|
|
6062
6213
|
// src/commands/backlog/show/index.ts
|
|
6063
6214
|
function printHeader(item) {
|
|
6064
|
-
console.log(chalk53.bold(
|
|
6215
|
+
console.log(chalk53.bold(`${formatItemId(item.id)} ${item.name}`));
|
|
6065
6216
|
console.log(
|
|
6066
6217
|
`${chalk53.dim("Type:")} ${item.type} ${chalk53.dim("Status:")} ${item.status}`
|
|
6067
6218
|
);
|
|
@@ -6087,8 +6238,8 @@ function printComments(item) {
|
|
|
6087
6238
|
}
|
|
6088
6239
|
console.log();
|
|
6089
6240
|
}
|
|
6090
|
-
async function show(
|
|
6091
|
-
const found = await findOneItem(
|
|
6241
|
+
async function show(id) {
|
|
6242
|
+
const found = await findOneItem(id);
|
|
6092
6243
|
if (!found) process.exit(1);
|
|
6093
6244
|
const { orm, item } = found;
|
|
6094
6245
|
printHeader(item);
|
|
@@ -6518,18 +6669,18 @@ async function getBacklogSummary(req, res) {
|
|
|
6518
6669
|
|
|
6519
6670
|
// src/commands/backlog/deleteComment.ts
|
|
6520
6671
|
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,
|
|
6672
|
+
async function deleteComment(orm, itemId2, commentId) {
|
|
6673
|
+
const [row] = await orm.select({ type: comments.type }).from(comments).where(and4(eq13(comments.id, commentId), eq13(comments.itemId, itemId2)));
|
|
6523
6674
|
if (!row) return "not-found";
|
|
6524
6675
|
if (row.type === "summary") return "is-summary";
|
|
6525
|
-
await orm.delete(comments).where(and4(eq13(comments.id, commentId), eq13(comments.itemId,
|
|
6676
|
+
await orm.delete(comments).where(and4(eq13(comments.id, commentId), eq13(comments.itemId, itemId2)));
|
|
6526
6677
|
return "deleted";
|
|
6527
6678
|
}
|
|
6528
6679
|
|
|
6529
6680
|
// src/commands/backlog/updateStarred.ts
|
|
6530
6681
|
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,
|
|
6682
|
+
async function updateStarred(orm, id, starred) {
|
|
6683
|
+
const [row] = await orm.update(items).set({ starred }).where(eq14(items.id, id)).returning({ name: items.name });
|
|
6533
6684
|
return row?.name;
|
|
6534
6685
|
}
|
|
6535
6686
|
|
|
@@ -6581,50 +6732,50 @@ async function listItems(req, res) {
|
|
|
6581
6732
|
const items2 = await loadVisibleItems(req);
|
|
6582
6733
|
respondJson(res, 200, items2.slice().reverse());
|
|
6583
6734
|
}
|
|
6584
|
-
async function findItemOr404(res,
|
|
6735
|
+
async function findItemOr404(res, id) {
|
|
6585
6736
|
const { orm } = await getReady();
|
|
6586
|
-
const item = await loadItem(orm,
|
|
6737
|
+
const item = await loadItem(orm, id);
|
|
6587
6738
|
if (!item) {
|
|
6588
6739
|
respondJson(res, 404, { error: "Not found" });
|
|
6589
6740
|
return void 0;
|
|
6590
6741
|
}
|
|
6591
6742
|
return { orm, item };
|
|
6592
6743
|
}
|
|
6593
|
-
async function getItemById(res,
|
|
6594
|
-
const result = await findItemOr404(res,
|
|
6744
|
+
async function getItemById(res, id) {
|
|
6745
|
+
const result = await findItemOr404(res, id);
|
|
6595
6746
|
if (result) respondJson(res, 200, withReviewPhase(result.item));
|
|
6596
6747
|
}
|
|
6597
|
-
async function deleteItem2(res,
|
|
6598
|
-
const result = await findItemOr404(res,
|
|
6748
|
+
async function deleteItem2(res, id) {
|
|
6749
|
+
const result = await findItemOr404(res, id);
|
|
6599
6750
|
if (!result) return;
|
|
6600
|
-
await deleteItem(result.orm,
|
|
6751
|
+
await deleteItem(result.orm, id);
|
|
6601
6752
|
respondJson(res, 200, result.item);
|
|
6602
6753
|
}
|
|
6603
|
-
async function patchItemStatus(req, res,
|
|
6754
|
+
async function patchItemStatus(req, res, id) {
|
|
6604
6755
|
const { status: status2 } = await parseStatusBody(req);
|
|
6605
|
-
const result = await findItemOr404(res,
|
|
6756
|
+
const result = await findItemOr404(res, id);
|
|
6606
6757
|
if (!result) return;
|
|
6607
|
-
await updateStatus(result.orm,
|
|
6758
|
+
await updateStatus(result.orm, id, status2);
|
|
6608
6759
|
const updated = { ...result.item, status: status2 };
|
|
6609
6760
|
respondJson(res, 200, updated);
|
|
6610
6761
|
}
|
|
6611
|
-
async function patchItemStar(req, res,
|
|
6762
|
+
async function patchItemStar(req, res, id) {
|
|
6612
6763
|
const { starred } = await parseStarBody(req);
|
|
6613
|
-
const result = await findItemOr404(res,
|
|
6764
|
+
const result = await findItemOr404(res, id);
|
|
6614
6765
|
if (!result) return;
|
|
6615
|
-
await updateStarred(result.orm,
|
|
6766
|
+
await updateStarred(result.orm, id, starred);
|
|
6616
6767
|
const updated = { ...result.item, starred };
|
|
6617
6768
|
respondJson(res, 200, updated);
|
|
6618
6769
|
}
|
|
6619
6770
|
|
|
6620
6771
|
// src/commands/backlog/web/deleteItemComment.ts
|
|
6621
|
-
async function deleteItemComment(res,
|
|
6622
|
-
const result = await findItemOr404(res,
|
|
6772
|
+
async function deleteItemComment(res, itemId2, commentId) {
|
|
6773
|
+
const result = await findItemOr404(res, itemId2);
|
|
6623
6774
|
if (!result) return;
|
|
6624
|
-
const outcome = await deleteComment(result.orm,
|
|
6775
|
+
const outcome = await deleteComment(result.orm, itemId2, commentId);
|
|
6625
6776
|
if (outcome === "not-found") {
|
|
6626
6777
|
respondJson(res, 404, {
|
|
6627
|
-
error: `Comment #${commentId} not found on item
|
|
6778
|
+
error: `Comment #${commentId} not found on item ${formatItemId(itemId2)}.`
|
|
6628
6779
|
});
|
|
6629
6780
|
return;
|
|
6630
6781
|
}
|
|
@@ -6634,19 +6785,19 @@ async function deleteItemComment(res, itemId, commentId) {
|
|
|
6634
6785
|
});
|
|
6635
6786
|
return;
|
|
6636
6787
|
}
|
|
6637
|
-
respondJson(res, 200, await loadItem(result.orm,
|
|
6788
|
+
respondJson(res, 200, await loadItem(result.orm, itemId2));
|
|
6638
6789
|
}
|
|
6639
6790
|
|
|
6640
6791
|
// src/commands/backlog/updateSubtaskStatus.ts
|
|
6641
6792
|
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,
|
|
6793
|
+
async function updateSubtaskStatus(orm, itemId2, idx, status2) {
|
|
6794
|
+
const [row] = await orm.update(itemSubtasks).set({ status: status2 }).where(and5(eq15(itemSubtasks.itemId, itemId2), eq15(itemSubtasks.idx, idx))).returning({ title: itemSubtasks.title });
|
|
6644
6795
|
return row?.title;
|
|
6645
6796
|
}
|
|
6646
6797
|
|
|
6647
6798
|
// src/commands/backlog/web/patchSubtaskStatus.ts
|
|
6648
6799
|
var validStatuses = ["todo", "in-progress", "done"];
|
|
6649
|
-
async function patchSubtaskStatus(req, res,
|
|
6800
|
+
async function patchSubtaskStatus(req, res, id, idx) {
|
|
6650
6801
|
const { status: status2 } = await parseStatusBody(req);
|
|
6651
6802
|
if (!validStatuses.includes(status2)) {
|
|
6652
6803
|
respondJson(res, 400, {
|
|
@@ -6654,16 +6805,18 @@ async function patchSubtaskStatus(req, res, id2, idx) {
|
|
|
6654
6805
|
});
|
|
6655
6806
|
return;
|
|
6656
6807
|
}
|
|
6657
|
-
const result = await findItemOr404(res,
|
|
6808
|
+
const result = await findItemOr404(res, id);
|
|
6658
6809
|
if (!result) return;
|
|
6659
6810
|
const { orm, item } = result;
|
|
6660
6811
|
const subtasks = item.subtasks ?? [];
|
|
6661
6812
|
if (idx < 0 || idx >= subtasks.length) {
|
|
6662
|
-
respondJson(res, 400, {
|
|
6813
|
+
respondJson(res, 400, {
|
|
6814
|
+
error: `Item ${formatItemId(id)} has no sub-task ${idx}.`
|
|
6815
|
+
});
|
|
6663
6816
|
return;
|
|
6664
6817
|
}
|
|
6665
|
-
await updateSubtaskStatus(orm,
|
|
6666
|
-
respondJson(res, 200, await loadItem(orm,
|
|
6818
|
+
await updateSubtaskStatus(orm, id, idx, status2);
|
|
6819
|
+
respondJson(res, 200, await loadItem(orm, id));
|
|
6667
6820
|
}
|
|
6668
6821
|
|
|
6669
6822
|
// src/commands/backlog/rewindItemToPhase.ts
|
|
@@ -6696,9 +6849,9 @@ async function rewindItemToPhase(orm, item, phaseNumber, reason) {
|
|
|
6696
6849
|
}
|
|
6697
6850
|
|
|
6698
6851
|
// src/commands/backlog/web/rewindItemPhase.ts
|
|
6699
|
-
async function rewindItemPhase(req, res,
|
|
6852
|
+
async function rewindItemPhase(req, res, id) {
|
|
6700
6853
|
const { phase, reason } = await parseRewindBody(req);
|
|
6701
|
-
const result = await findItemOr404(res,
|
|
6854
|
+
const result = await findItemOr404(res, id);
|
|
6702
6855
|
if (!result) return;
|
|
6703
6856
|
const { orm, item } = result;
|
|
6704
6857
|
const outcome = await rewindItemToPhase(orm, item, phase, reason);
|
|
@@ -6706,46 +6859,47 @@ async function rewindItemPhase(req, res, id2) {
|
|
|
6706
6859
|
respondJson(res, 400, { error: outcome.error });
|
|
6707
6860
|
return;
|
|
6708
6861
|
}
|
|
6709
|
-
respondJson(res, 200, await loadItem(orm,
|
|
6862
|
+
respondJson(res, 200, await loadItem(orm, id));
|
|
6710
6863
|
}
|
|
6711
6864
|
|
|
6712
6865
|
// src/commands/backlog/web/handleItemRoute.ts
|
|
6713
|
-
var
|
|
6866
|
+
var itemId = (match, group = 1) => parseItemId(match[group]);
|
|
6867
|
+
var subId = (match, group) => Number.parseInt(match[group], 10);
|
|
6714
6868
|
var routes = [
|
|
6715
6869
|
{
|
|
6716
|
-
pattern: /^\/api\/items\/(
|
|
6870
|
+
pattern: /^\/api\/items\/(a?\d+)\/rewind$/,
|
|
6717
6871
|
method: "POST",
|
|
6718
|
-
run: (req, res, m) => rewindItemPhase(req, res,
|
|
6872
|
+
run: (req, res, m) => rewindItemPhase(req, res, itemId(m))
|
|
6719
6873
|
},
|
|
6720
6874
|
{
|
|
6721
|
-
pattern: /^\/api\/items\/(
|
|
6875
|
+
pattern: /^\/api\/items\/(a?\d+)\/star$/,
|
|
6722
6876
|
method: "POST",
|
|
6723
|
-
run: (req, res, m) => patchItemStar(req, res,
|
|
6877
|
+
run: (req, res, m) => patchItemStar(req, res, itemId(m))
|
|
6724
6878
|
},
|
|
6725
6879
|
{
|
|
6726
|
-
pattern: /^\/api\/items\/(
|
|
6880
|
+
pattern: /^\/api\/items\/(a?\d+)\/comments\/(\d+)$/,
|
|
6727
6881
|
method: "DELETE",
|
|
6728
|
-
run: (_req, res, m) => deleteItemComment(res,
|
|
6882
|
+
run: (_req, res, m) => deleteItemComment(res, itemId(m), subId(m, 2))
|
|
6729
6883
|
},
|
|
6730
6884
|
{
|
|
6731
|
-
pattern: /^\/api\/items\/(
|
|
6885
|
+
pattern: /^\/api\/items\/(a?\d+)\/subtasks\/(\d+)$/,
|
|
6732
6886
|
method: "PATCH",
|
|
6733
|
-
run: (req, res, m) => patchSubtaskStatus(req, res,
|
|
6887
|
+
run: (req, res, m) => patchSubtaskStatus(req, res, itemId(m), subId(m, 2))
|
|
6734
6888
|
},
|
|
6735
6889
|
{
|
|
6736
|
-
pattern: /^\/api\/items\/(
|
|
6890
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
6737
6891
|
method: "GET",
|
|
6738
|
-
run: (_req, res, m) => getItemById(res,
|
|
6892
|
+
run: (_req, res, m) => getItemById(res, itemId(m))
|
|
6739
6893
|
},
|
|
6740
6894
|
{
|
|
6741
|
-
pattern: /^\/api\/items\/(
|
|
6895
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
6742
6896
|
method: "PATCH",
|
|
6743
|
-
run: (req, res, m) => patchItemStatus(req, res,
|
|
6897
|
+
run: (req, res, m) => patchItemStatus(req, res, itemId(m))
|
|
6744
6898
|
},
|
|
6745
6899
|
{
|
|
6746
|
-
pattern: /^\/api\/items\/(
|
|
6900
|
+
pattern: /^\/api\/items\/(a?\d+)$/,
|
|
6747
6901
|
method: "DELETE",
|
|
6748
|
-
run: (_req, res, m) => deleteItem2(res,
|
|
6902
|
+
run: (_req, res, m) => deleteItem2(res, itemId(m))
|
|
6749
6903
|
}
|
|
6750
6904
|
];
|
|
6751
6905
|
async function handleItemRoute(req, res, pathname) {
|
|
@@ -7816,9 +7970,23 @@ async function web2(options2) {
|
|
|
7816
7970
|
});
|
|
7817
7971
|
}
|
|
7818
7972
|
|
|
7973
|
+
// src/commands/backlog/backlogConfigHelp.ts
|
|
7974
|
+
var backlogConfigHelp = [
|
|
7975
|
+
{
|
|
7976
|
+
key: "database.url",
|
|
7977
|
+
setter: "assist config set database.url postgresql://...",
|
|
7978
|
+
note: "Postgres connection string for backlog storage (or ASSIST_DATABASE_URL)"
|
|
7979
|
+
},
|
|
7980
|
+
{
|
|
7981
|
+
key: "subtasks",
|
|
7982
|
+
setter: 'assist config set subtasks.0.title "Review"',
|
|
7983
|
+
note: "default sub-tasks seeded onto every new backlog item"
|
|
7984
|
+
}
|
|
7985
|
+
];
|
|
7986
|
+
|
|
7819
7987
|
// src/commands/backlog/addActivity.ts
|
|
7820
7988
|
import chalk60 from "chalk";
|
|
7821
|
-
async function addActivity(
|
|
7989
|
+
async function addActivity(id, kind, ref, options2) {
|
|
7822
7990
|
const parsedKind = gitRefKindSchema.safeParse(kind);
|
|
7823
7991
|
if (!parsedKind.success) {
|
|
7824
7992
|
console.log(
|
|
@@ -7829,7 +7997,7 @@ async function addActivity(id2, kind, ref, options2) {
|
|
|
7829
7997
|
process.exitCode = 1;
|
|
7830
7998
|
return;
|
|
7831
7999
|
}
|
|
7832
|
-
const found = await findOneItem(
|
|
8000
|
+
const found = await findOneItem(id);
|
|
7833
8001
|
if (!found) {
|
|
7834
8002
|
process.exitCode = 1;
|
|
7835
8003
|
return;
|
|
@@ -7844,7 +8012,9 @@ async function addActivity(id2, kind, ref, options2) {
|
|
|
7844
8012
|
state: options2.state
|
|
7845
8013
|
});
|
|
7846
8014
|
console.log(
|
|
7847
|
-
chalk60.green(
|
|
8015
|
+
chalk60.green(
|
|
8016
|
+
`Attached ${parsedKind.data} "${ref}" to item ${formatItemId(item.id)}.`
|
|
8017
|
+
)
|
|
7848
8018
|
);
|
|
7849
8019
|
}
|
|
7850
8020
|
|
|
@@ -7890,17 +8060,19 @@ function fetchIssue(issueKey, fields) {
|
|
|
7890
8060
|
|
|
7891
8061
|
// src/commands/backlog/associate-jira/index.ts
|
|
7892
8062
|
var JIRA_KEY_PATTERN = /^[A-Z]+-\d+$/;
|
|
7893
|
-
async function associateJira(
|
|
7894
|
-
const found = await findOneItem(
|
|
8063
|
+
async function associateJira(id, key, options2) {
|
|
8064
|
+
const found = await findOneItem(id);
|
|
7895
8065
|
if (!found) {
|
|
7896
8066
|
process.exitCode = 1;
|
|
7897
8067
|
return;
|
|
7898
8068
|
}
|
|
7899
8069
|
const { orm } = found;
|
|
7900
|
-
const
|
|
8070
|
+
const itemId2 = found.item.id;
|
|
7901
8071
|
if (options2.clear) {
|
|
7902
|
-
await orm.update(items).set({ jiraKey: null }).where(eq16(items.id,
|
|
7903
|
-
console.log(
|
|
8072
|
+
await orm.update(items).set({ jiraKey: null }).where(eq16(items.id, itemId2));
|
|
8073
|
+
console.log(
|
|
8074
|
+
chalk62.green(`Cleared Jira association on item ${formatItemId(itemId2)}.`)
|
|
8075
|
+
);
|
|
7904
8076
|
return;
|
|
7905
8077
|
}
|
|
7906
8078
|
if (!key) {
|
|
@@ -7918,9 +8090,9 @@ async function associateJira(id2, key, options2) {
|
|
|
7918
8090
|
const parsed = fetchIssue(key, "summary");
|
|
7919
8091
|
const fields = parsed?.fields;
|
|
7920
8092
|
const summary = fields?.summary;
|
|
7921
|
-
await orm.update(items).set({ jiraKey: key }).where(eq16(items.id,
|
|
8093
|
+
await orm.update(items).set({ jiraKey: key }).where(eq16(items.id, itemId2));
|
|
7922
8094
|
console.log(
|
|
7923
|
-
chalk62.green(`Associated ${key} with item
|
|
8095
|
+
chalk62.green(`Associated ${key} with item ${formatItemId(itemId2)}.`),
|
|
7924
8096
|
summary ? chalk62.dim(`(${summary})`) : ""
|
|
7925
8097
|
);
|
|
7926
8098
|
}
|
|
@@ -7932,26 +8104,30 @@ function registerAssociateJiraCommand(cmd) {
|
|
|
7932
8104
|
|
|
7933
8105
|
// src/commands/backlog/comment/index.ts
|
|
7934
8106
|
import chalk63 from "chalk";
|
|
7935
|
-
async function comment(
|
|
7936
|
-
const found = await findOneItem(
|
|
8107
|
+
async function comment(id, text9) {
|
|
8108
|
+
const found = await findOneItem(id);
|
|
7937
8109
|
if (!found) process.exit(1);
|
|
7938
8110
|
await appendComment(found.orm, found.item.id, text9);
|
|
7939
|
-
console.log(
|
|
8111
|
+
console.log(
|
|
8112
|
+
chalk63.green(`Comment added to item ${formatItemId(found.item.id)}.`)
|
|
8113
|
+
);
|
|
7940
8114
|
}
|
|
7941
8115
|
|
|
7942
8116
|
// src/commands/backlog/comments/index.ts
|
|
7943
8117
|
import chalk64 from "chalk";
|
|
7944
|
-
async function comments2(
|
|
7945
|
-
const found = await findOneItem(
|
|
8118
|
+
async function comments2(id) {
|
|
8119
|
+
const found = await findOneItem(id);
|
|
7946
8120
|
if (!found) process.exit(1);
|
|
7947
8121
|
const { item } = found;
|
|
7948
8122
|
const entries = item.comments ?? [];
|
|
7949
8123
|
if (entries.length === 0) {
|
|
7950
|
-
console.log(chalk64.dim(`No comments on item
|
|
8124
|
+
console.log(chalk64.dim(`No comments on item ${formatItemId(item.id)}.`));
|
|
7951
8125
|
return;
|
|
7952
8126
|
}
|
|
7953
|
-
console.log(
|
|
7954
|
-
`)
|
|
8127
|
+
console.log(
|
|
8128
|
+
chalk64.bold(`Comments for ${formatItemId(item.id)}: ${item.name}
|
|
8129
|
+
`)
|
|
8130
|
+
);
|
|
7955
8131
|
for (const entry of entries) {
|
|
7956
8132
|
console.log(`${formatComment(entry)}
|
|
7957
8133
|
`);
|
|
@@ -7960,22 +8136,29 @@ async function comments2(id2) {
|
|
|
7960
8136
|
|
|
7961
8137
|
// src/commands/backlog/delete-comment/index.ts
|
|
7962
8138
|
import chalk65 from "chalk";
|
|
7963
|
-
async function deleteCommentCmd(
|
|
7964
|
-
const found = await findOneItem(
|
|
8139
|
+
async function deleteCommentCmd(id, commentId) {
|
|
8140
|
+
const found = await findOneItem(id);
|
|
7965
8141
|
if (!found) process.exit(1);
|
|
8142
|
+
const itemId2 = found.item.id;
|
|
7966
8143
|
const outcome = await deleteComment(
|
|
7967
8144
|
found.orm,
|
|
7968
|
-
|
|
8145
|
+
itemId2,
|
|
7969
8146
|
Number.parseInt(commentId, 10)
|
|
7970
8147
|
);
|
|
7971
8148
|
switch (outcome) {
|
|
7972
8149
|
case "deleted":
|
|
7973
8150
|
console.log(
|
|
7974
|
-
chalk65.green(
|
|
8151
|
+
chalk65.green(
|
|
8152
|
+
`Comment #${commentId} deleted from item ${formatItemId(itemId2)}.`
|
|
8153
|
+
)
|
|
7975
8154
|
);
|
|
7976
8155
|
break;
|
|
7977
8156
|
case "not-found":
|
|
7978
|
-
console.log(
|
|
8157
|
+
console.log(
|
|
8158
|
+
chalk65.red(
|
|
8159
|
+
`Comment #${commentId} not found on item ${formatItemId(itemId2)}.`
|
|
8160
|
+
)
|
|
8161
|
+
);
|
|
7979
8162
|
process.exit(1);
|
|
7980
8163
|
break;
|
|
7981
8164
|
case "is-summary":
|
|
@@ -8060,7 +8243,7 @@ function parseDump(dump) {
|
|
|
8060
8243
|
function validateDump({ header, sections }) {
|
|
8061
8244
|
const tables = header.tables ?? [];
|
|
8062
8245
|
const missing = tables.find(({ name }) => !sections.has(name))?.name;
|
|
8063
|
-
const
|
|
8246
|
+
const checks2 = [
|
|
8064
8247
|
[
|
|
8065
8248
|
header.format === DUMP_FORMAT,
|
|
8066
8249
|
`Unrecognised dump format "${header.format}" (expected "${DUMP_FORMAT}").`
|
|
@@ -8072,7 +8255,7 @@ function validateDump({ header, sections }) {
|
|
|
8072
8255
|
[tables.length > 0, "Dump header lists no tables; cannot restore."],
|
|
8073
8256
|
[!missing, `Invalid dump: missing data section for "${missing}".`]
|
|
8074
8257
|
];
|
|
8075
|
-
const failure =
|
|
8258
|
+
const failure = checks2.find(([ok]) => !ok);
|
|
8076
8259
|
if (failure) throw new Error(failure[1]);
|
|
8077
8260
|
}
|
|
8078
8261
|
|
|
@@ -8220,37 +8403,37 @@ import { count as count2, eq as eq18 } from "drizzle-orm";
|
|
|
8220
8403
|
|
|
8221
8404
|
// src/commands/backlog/shiftPhasesUp.ts
|
|
8222
8405
|
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,
|
|
8406
|
+
async function shiftPhasesUp(db, itemId2, fromIdx) {
|
|
8407
|
+
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
8408
|
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,
|
|
8409
|
+
await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and6(eq17(planTasks.itemId, itemId2), eq17(planTasks.phaseIdx, p.idx)));
|
|
8410
|
+
await db.update(planPhases).set({ idx: p.idx + 1 }).where(and6(eq17(planPhases.itemId, itemId2), eq17(planPhases.idx, p.idx)));
|
|
8228
8411
|
}
|
|
8229
8412
|
}
|
|
8230
8413
|
|
|
8231
8414
|
// src/commands/backlog/insertPhaseAt.ts
|
|
8232
|
-
async function insertPhaseAt(orm,
|
|
8415
|
+
async function insertPhaseAt(orm, itemId2, phaseIdx, name, tasks, manualChecks, currentPhase) {
|
|
8233
8416
|
await orm.transaction(async (tx) => {
|
|
8234
|
-
const [row] = await tx.select({ cnt: count2() }).from(planPhases).where(eq18(planPhases.itemId,
|
|
8417
|
+
const [row] = await tx.select({ cnt: count2() }).from(planPhases).where(eq18(planPhases.itemId, itemId2));
|
|
8235
8418
|
const phaseCount = row?.cnt ?? 0;
|
|
8236
|
-
await shiftPhasesUp(tx,
|
|
8237
|
-
await tx.insert(planPhases).values({ itemId, idx: phaseIdx, name, manualChecks });
|
|
8419
|
+
await shiftPhasesUp(tx, itemId2, phaseIdx);
|
|
8420
|
+
await tx.insert(planPhases).values({ itemId: itemId2, idx: phaseIdx, name, manualChecks });
|
|
8238
8421
|
if (tasks.length) {
|
|
8239
|
-
await tx.insert(planTasks).values(tasks.map((task, i) => ({ itemId, phaseIdx, idx: i, task })));
|
|
8422
|
+
await tx.insert(planTasks).values(tasks.map((task, i) => ({ itemId: itemId2, phaseIdx, idx: i, task })));
|
|
8240
8423
|
}
|
|
8241
8424
|
if (currentPhase !== void 0 && currentPhase - 1 >= phaseIdx) {
|
|
8242
8425
|
const atReviewSlot = currentPhase - 1 >= phaseCount;
|
|
8243
|
-
await tx.update(items).set({ currentPhase: atReviewSlot ? phaseIdx + 1 : currentPhase + 1 }).where(eq18(items.id,
|
|
8426
|
+
await tx.update(items).set({ currentPhase: atReviewSlot ? phaseIdx + 1 : currentPhase + 1 }).where(eq18(items.id, itemId2));
|
|
8244
8427
|
}
|
|
8245
8428
|
});
|
|
8246
8429
|
}
|
|
8247
8430
|
|
|
8248
8431
|
// src/commands/backlog/insertSubtask.ts
|
|
8249
8432
|
import { sql as sql6 } from "drizzle-orm";
|
|
8250
|
-
async function insertSubtask(orm,
|
|
8433
|
+
async function insertSubtask(orm, itemId2, title, description) {
|
|
8251
8434
|
await orm.insert(itemSubtasks).values({
|
|
8252
|
-
itemId,
|
|
8253
|
-
idx: sql6`(SELECT COALESCE(MAX(${itemSubtasks.idx}) + 1, 0) FROM ${itemSubtasks} WHERE ${itemSubtasks.itemId} = ${
|
|
8435
|
+
itemId: itemId2,
|
|
8436
|
+
idx: sql6`(SELECT COALESCE(MAX(${itemSubtasks.idx}) + 1, 0) FROM ${itemSubtasks} WHERE ${itemSubtasks.itemId} = ${itemId2})`,
|
|
8254
8437
|
title,
|
|
8255
8438
|
description: description ?? null,
|
|
8256
8439
|
status: "todo"
|
|
@@ -8348,7 +8531,7 @@ async function add(options2) {
|
|
|
8348
8531
|
const description = options2.desc?.replaceAll(String.raw`\n`, "\n") ?? await promptDescription();
|
|
8349
8532
|
const acceptanceCriteria2 = options2.ac ?? await promptAcceptanceCriteria();
|
|
8350
8533
|
const orm = await getDb();
|
|
8351
|
-
const
|
|
8534
|
+
const id = await insertItem(
|
|
8352
8535
|
orm,
|
|
8353
8536
|
{
|
|
8354
8537
|
type,
|
|
@@ -8361,12 +8544,12 @@ async function add(options2) {
|
|
|
8361
8544
|
getOrigin()
|
|
8362
8545
|
);
|
|
8363
8546
|
if (type === "bug") {
|
|
8364
|
-
await insertPhaseAt(orm,
|
|
8547
|
+
await insertPhaseAt(orm, id, 0, "Fix", ["Fix the bug"], null, void 0);
|
|
8365
8548
|
}
|
|
8366
8549
|
for (const subtask of loadConfig().subtasks ?? []) {
|
|
8367
|
-
await insertSubtask(orm,
|
|
8550
|
+
await insertSubtask(orm, id, subtask.title, subtask.description);
|
|
8368
8551
|
}
|
|
8369
|
-
console.log(chalk69.green(`Added item
|
|
8552
|
+
console.log(chalk69.green(`Added item ${formatItemId(id)}: ${name}`));
|
|
8370
8553
|
}
|
|
8371
8554
|
|
|
8372
8555
|
// src/commands/backlog/addPhase.ts
|
|
@@ -8375,8 +8558,8 @@ import chalk71 from "chalk";
|
|
|
8375
8558
|
// src/commands/backlog/resolveInsertPosition.ts
|
|
8376
8559
|
import chalk70 from "chalk";
|
|
8377
8560
|
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,
|
|
8561
|
+
async function resolveInsertPosition(orm, itemId2, position) {
|
|
8562
|
+
const [row] = await orm.select({ cnt: count3() }).from(planPhases).where(eq19(planPhases.itemId, itemId2));
|
|
8380
8563
|
const phaseCount = row?.cnt ?? 0;
|
|
8381
8564
|
if (position === void 0) return phaseCount;
|
|
8382
8565
|
const pos = Number.parseInt(position, 10);
|
|
@@ -8398,9 +8581,9 @@ function serializeManualChecks(manualCheck) {
|
|
|
8398
8581
|
}
|
|
8399
8582
|
|
|
8400
8583
|
// src/commands/backlog/addPhase.ts
|
|
8401
|
-
async function addPhase(
|
|
8584
|
+
async function addPhase(id, name, options2) {
|
|
8402
8585
|
if (!ensureRemoteOrigin()) return;
|
|
8403
|
-
const found = await findOneItem(
|
|
8586
|
+
const found = await findOneItem(id);
|
|
8404
8587
|
if (!found) return;
|
|
8405
8588
|
const tasks = options2.task ?? [];
|
|
8406
8589
|
if (tasks.length === 0) {
|
|
@@ -8409,12 +8592,12 @@ async function addPhase(id2, name, options2) {
|
|
|
8409
8592
|
return;
|
|
8410
8593
|
}
|
|
8411
8594
|
const { orm } = found;
|
|
8412
|
-
const
|
|
8413
|
-
const phaseIdx = await resolveInsertPosition(orm,
|
|
8595
|
+
const itemId2 = found.item.id;
|
|
8596
|
+
const phaseIdx = await resolveInsertPosition(orm, itemId2, options2.position);
|
|
8414
8597
|
if (phaseIdx === void 0) return;
|
|
8415
8598
|
await insertPhaseAt(
|
|
8416
8599
|
orm,
|
|
8417
|
-
|
|
8600
|
+
itemId2,
|
|
8418
8601
|
phaseIdx,
|
|
8419
8602
|
name,
|
|
8420
8603
|
tasks,
|
|
@@ -8424,7 +8607,7 @@ async function addPhase(id2, name, options2) {
|
|
|
8424
8607
|
const verb = options2.position !== void 0 ? "Inserted" : "Added";
|
|
8425
8608
|
console.log(
|
|
8426
8609
|
chalk71.green(
|
|
8427
|
-
`${verb} phase ${phaseIdx + 1} "${name}" to item
|
|
8610
|
+
`${verb} phase ${phaseIdx + 1} "${name}" to item ${formatItemId(itemId2)} with ${tasks.length} task(s).`
|
|
8428
8611
|
)
|
|
8429
8612
|
);
|
|
8430
8613
|
}
|
|
@@ -8437,6 +8620,15 @@ function filterItems(items2, options2) {
|
|
|
8437
8620
|
return items2.filter((i) => i.status !== "done" && i.status !== "wontdo");
|
|
8438
8621
|
return items2;
|
|
8439
8622
|
}
|
|
8623
|
+
function repoPrefixer(items2, allRepos) {
|
|
8624
|
+
if (!allRepos) return () => "";
|
|
8625
|
+
const labels = originDisplayLabels(
|
|
8626
|
+
items2.flatMap((i) => i.origin ? [i.origin] : [])
|
|
8627
|
+
);
|
|
8628
|
+
const repoNameOf = (item) => item.origin ? labels.get(item.origin) ?? "" : "";
|
|
8629
|
+
const width = Math.max(0, ...items2.map((i) => repoNameOf(i).length));
|
|
8630
|
+
return (item) => `${chalk72.dim(repoNameOf(item).padEnd(width))} `;
|
|
8631
|
+
}
|
|
8440
8632
|
async function list2(options2) {
|
|
8441
8633
|
const allItems = await loadBacklog(options2.allRepos);
|
|
8442
8634
|
const items2 = filterItems(allItems, options2);
|
|
@@ -8444,15 +8636,10 @@ async function list2(options2) {
|
|
|
8444
8636
|
console.log(chalk72.dim("Backlog is empty."));
|
|
8445
8637
|
return;
|
|
8446
8638
|
}
|
|
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;
|
|
8639
|
+
const prefixOf = repoPrefixer(items2, !!options2.allRepos);
|
|
8452
8640
|
for (const item of items2) {
|
|
8453
|
-
const repoPrefix = options2.allRepos ? `${chalk72.dim(repoNameOf(item).padEnd(prefixWidth))} ` : "";
|
|
8454
8641
|
console.log(
|
|
8455
|
-
`${
|
|
8642
|
+
`${prefixOf(item)}${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk72.dim(formatItemId(item.id))} ${starMarker(item)}${item.name}${phaseLabel(item)}${dependencyLabel(item, allItems)}`
|
|
8456
8643
|
);
|
|
8457
8644
|
if (options2.verbose) {
|
|
8458
8645
|
printVerboseDetails(item);
|
|
@@ -8504,10 +8691,10 @@ import { eq as eq20 } from "drizzle-orm";
|
|
|
8504
8691
|
async function loadDependencyGraph(orm) {
|
|
8505
8692
|
const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(eq20(links.type, "depends-on"));
|
|
8506
8693
|
const graph = /* @__PURE__ */ new Map();
|
|
8507
|
-
for (const { itemId, targetId } of rows) {
|
|
8508
|
-
const bucket = graph.get(
|
|
8694
|
+
for (const { itemId: itemId2, targetId } of rows) {
|
|
8695
|
+
const bucket = graph.get(itemId2);
|
|
8509
8696
|
if (bucket) bucket.push(targetId);
|
|
8510
|
-
else graph.set(
|
|
8697
|
+
else graph.set(itemId2, [targetId]);
|
|
8511
8698
|
}
|
|
8512
8699
|
return graph;
|
|
8513
8700
|
}
|
|
@@ -8520,7 +8707,9 @@ function validateLinkTarget(fromItem, fromNum, toNum, linkType) {
|
|
|
8520
8707
|
);
|
|
8521
8708
|
if (duplicate) {
|
|
8522
8709
|
console.log(
|
|
8523
|
-
chalk73.yellow(
|
|
8710
|
+
chalk73.yellow(
|
|
8711
|
+
`Link already exists: ${formatItemId(fromNum)} ${linkType} ${formatItemId(toNum)}`
|
|
8712
|
+
)
|
|
8524
8713
|
);
|
|
8525
8714
|
return false;
|
|
8526
8715
|
}
|
|
@@ -8541,50 +8730,62 @@ async function createsCycle(orm, linkType, fromNum, toNum) {
|
|
|
8541
8730
|
if (linkType !== "depends-on") return false;
|
|
8542
8731
|
const graph = await loadDependencyGraph(orm);
|
|
8543
8732
|
if (!hasCycle(graph, fromNum, toNum)) return false;
|
|
8544
|
-
fail2(
|
|
8733
|
+
fail2(
|
|
8734
|
+
`Cannot add dependency: ${formatItemId(fromNum)} \u2192 ${formatItemId(toNum)} would create a cycle.`
|
|
8735
|
+
);
|
|
8545
8736
|
return true;
|
|
8546
8737
|
}
|
|
8547
8738
|
async function link(fromId, toId, opts) {
|
|
8548
8739
|
const linkType = parseLinkType(opts.type);
|
|
8549
8740
|
if (!linkType) return;
|
|
8550
|
-
const fromNum =
|
|
8551
|
-
const toNum =
|
|
8741
|
+
const fromNum = parseItemId(fromId);
|
|
8742
|
+
const toNum = parseItemId(toId);
|
|
8552
8743
|
if (fromNum === toNum) return void fail2("Cannot link an item to itself.");
|
|
8744
|
+
const from = formatItemId(fromNum);
|
|
8745
|
+
const to = formatItemId(toNum);
|
|
8553
8746
|
const { orm } = await getReady();
|
|
8554
8747
|
const fromItem = await loadItem(orm, fromNum);
|
|
8555
|
-
if (!fromItem) return void fail2(`Item
|
|
8748
|
+
if (!fromItem) return void fail2(`Item ${from} not found.`);
|
|
8556
8749
|
const toItem = await loadItem(orm, toNum);
|
|
8557
|
-
if (!toItem) return void fail2(`Item
|
|
8750
|
+
if (!toItem) return void fail2(`Item ${to} not found.`);
|
|
8558
8751
|
if (!validateLinkTarget(fromItem, fromNum, toNum, linkType)) return;
|
|
8559
8752
|
if (await createsCycle(orm, linkType, fromNum, toNum)) return;
|
|
8560
8753
|
await orm.insert(links).values({ itemId: fromNum, type: linkType, targetId: toNum });
|
|
8561
|
-
console.log(
|
|
8562
|
-
chalk74.green(`Linked #${fromNum} ${linkType} #${toNum} (${toItem.name})`)
|
|
8563
|
-
);
|
|
8754
|
+
console.log(chalk74.green(`Linked ${from} ${linkType} ${to} (${toItem.name})`));
|
|
8564
8755
|
}
|
|
8565
8756
|
|
|
8566
8757
|
// src/commands/backlog/unlink.ts
|
|
8567
8758
|
import chalk75 from "chalk";
|
|
8568
8759
|
import { and as and7, eq as eq21 } from "drizzle-orm";
|
|
8569
8760
|
async function unlink(fromId, toId) {
|
|
8570
|
-
const fromNum =
|
|
8571
|
-
const toNum =
|
|
8761
|
+
const fromNum = parseItemId(fromId);
|
|
8762
|
+
const toNum = parseItemId(toId);
|
|
8572
8763
|
const { orm } = await getReady();
|
|
8573
8764
|
const fromItem = await loadItem(orm, fromNum);
|
|
8574
8765
|
if (!fromItem) {
|
|
8575
|
-
console.log(chalk75.red(`Item
|
|
8766
|
+
console.log(chalk75.red(`Item ${formatItemId(fromNum)} not found.`));
|
|
8576
8767
|
return;
|
|
8577
8768
|
}
|
|
8578
8769
|
if (!fromItem.links || fromItem.links.length === 0) {
|
|
8579
|
-
console.log(
|
|
8770
|
+
console.log(
|
|
8771
|
+
chalk75.yellow(`No links found on item ${formatItemId(fromNum)}.`)
|
|
8772
|
+
);
|
|
8580
8773
|
return;
|
|
8581
8774
|
}
|
|
8582
8775
|
if (!fromItem.links.some((l) => l.targetId === toNum)) {
|
|
8583
|
-
console.log(
|
|
8776
|
+
console.log(
|
|
8777
|
+
chalk75.yellow(
|
|
8778
|
+
`No link from ${formatItemId(fromNum)} to ${formatItemId(toNum)} found.`
|
|
8779
|
+
)
|
|
8780
|
+
);
|
|
8584
8781
|
return;
|
|
8585
8782
|
}
|
|
8586
8783
|
await orm.delete(links).where(and7(eq21(links.itemId, fromNum), eq21(links.targetId, toNum)));
|
|
8587
|
-
console.log(
|
|
8784
|
+
console.log(
|
|
8785
|
+
chalk75.green(
|
|
8786
|
+
`Removed link from ${formatItemId(fromNum)} to ${formatItemId(toNum)}.`
|
|
8787
|
+
)
|
|
8788
|
+
);
|
|
8588
8789
|
}
|
|
8589
8790
|
|
|
8590
8791
|
// src/commands/backlog/registerLinkCommands.ts
|
|
@@ -8679,7 +8880,7 @@ function registerMoveRepoCommand(cmd) {
|
|
|
8679
8880
|
// src/commands/backlog/registerNextCommand.ts
|
|
8680
8881
|
function registerNextCommand(cmd) {
|
|
8681
8882
|
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
|
-
(
|
|
8883
|
+
(id, opts) => next({ allowEdits: opts.write !== false, once: opts.once }, id)
|
|
8683
8884
|
);
|
|
8684
8885
|
}
|
|
8685
8886
|
|
|
@@ -8700,9 +8901,14 @@ import { randomUUID as randomUUID2 } from "crypto";
|
|
|
8700
8901
|
import chalk79 from "chalk";
|
|
8701
8902
|
|
|
8702
8903
|
// src/commands/backlog/surfaceCreatedItem.ts
|
|
8703
|
-
async function surfaceCreatedItem(slashCommand,
|
|
8704
|
-
|
|
8705
|
-
|
|
8904
|
+
async function surfaceCreatedItem(slashCommand, id) {
|
|
8905
|
+
let numericId;
|
|
8906
|
+
try {
|
|
8907
|
+
numericId = parseItemId(id);
|
|
8908
|
+
} catch (error) {
|
|
8909
|
+
if (error instanceof InvalidItemIdError) return;
|
|
8910
|
+
throw error;
|
|
8911
|
+
}
|
|
8706
8912
|
const { orm } = await getReady();
|
|
8707
8913
|
const item = await loadItem(orm, numericId);
|
|
8708
8914
|
if (!item) return;
|
|
@@ -8716,33 +8922,34 @@ async function surfaceCreatedItem(slashCommand, id2) {
|
|
|
8716
8922
|
|
|
8717
8923
|
// src/commands/backlog/tryRunById.ts
|
|
8718
8924
|
import chalk78 from "chalk";
|
|
8719
|
-
async function tryRunById(
|
|
8720
|
-
const numericId =
|
|
8925
|
+
async function tryRunById(id, options2) {
|
|
8926
|
+
const numericId = parseItemId(id);
|
|
8927
|
+
const label2 = formatItemId(numericId);
|
|
8721
8928
|
const { orm } = await getReady();
|
|
8722
|
-
const item =
|
|
8929
|
+
const item = await loadItem(orm, numericId);
|
|
8723
8930
|
if (!item) {
|
|
8724
|
-
console.log(chalk78.red(`Item
|
|
8931
|
+
console.log(chalk78.red(`Item ${label2} not found.`));
|
|
8725
8932
|
return false;
|
|
8726
8933
|
}
|
|
8727
8934
|
if (item.status === "done") {
|
|
8728
|
-
console.log(chalk78.red(`Item
|
|
8935
|
+
console.log(chalk78.red(`Item ${label2} is already done.`));
|
|
8729
8936
|
return false;
|
|
8730
8937
|
}
|
|
8731
8938
|
if (item.status === "wontdo") {
|
|
8732
|
-
console.log(chalk78.red(`Item
|
|
8939
|
+
console.log(chalk78.red(`Item ${label2} is marked won't do.`));
|
|
8733
8940
|
return false;
|
|
8734
8941
|
}
|
|
8735
8942
|
const hasDeps = (item.links ?? []).some((l) => l.type === "depends-on");
|
|
8736
8943
|
if (hasDeps && isBlocked(item, await loadItemSummaries(orm, getOrigin()))) {
|
|
8737
8944
|
console.log(
|
|
8738
|
-
chalk78.red(`Item
|
|
8945
|
+
chalk78.red(`Item ${label2} is blocked by unresolved dependencies.`)
|
|
8739
8946
|
);
|
|
8740
8947
|
return false;
|
|
8741
8948
|
}
|
|
8742
8949
|
console.log(chalk78.bold(`
|
|
8743
|
-
Running backlog item
|
|
8950
|
+
Running backlog item ${label2}...
|
|
8744
8951
|
`));
|
|
8745
|
-
await run2(
|
|
8952
|
+
await run2(id, options2);
|
|
8746
8953
|
return true;
|
|
8747
8954
|
}
|
|
8748
8955
|
|
|
@@ -8803,8 +9010,10 @@ async function pickItemForRefine() {
|
|
|
8803
9010
|
}
|
|
8804
9011
|
if (active.length === 1) {
|
|
8805
9012
|
const item = active[0];
|
|
8806
|
-
console.log(
|
|
8807
|
-
|
|
9013
|
+
console.log(
|
|
9014
|
+
chalk80.bold(`Auto-selecting item ${formatItemId(item.id)}: ${item.name}`)
|
|
9015
|
+
);
|
|
9016
|
+
return formatItemId(item.id);
|
|
8808
9017
|
}
|
|
8809
9018
|
const { selected } = await exitOnCancel(
|
|
8810
9019
|
enquirer7.prompt({
|
|
@@ -8812,18 +9021,18 @@ async function pickItemForRefine() {
|
|
|
8812
9021
|
name: "selected",
|
|
8813
9022
|
message: "Choose a backlog item to refine:",
|
|
8814
9023
|
choices: active.map((item) => ({
|
|
8815
|
-
name: `${typeLabel(item.type)}
|
|
9024
|
+
name: `${typeLabel(item.type)} ${formatItemId(item.id)}: ${item.name}`
|
|
8816
9025
|
}))
|
|
8817
9026
|
})
|
|
8818
9027
|
);
|
|
8819
|
-
return selected.match(
|
|
9028
|
+
return selected.match(/(a\d+):/)?.[1] ?? "";
|
|
8820
9029
|
}
|
|
8821
|
-
async function refine(
|
|
8822
|
-
const
|
|
8823
|
-
if (!
|
|
8824
|
-
const numericId =
|
|
8825
|
-
const item =
|
|
8826
|
-
await launchMode(`refine ${
|
|
9030
|
+
async function refine(id, options2) {
|
|
9031
|
+
const itemId2 = id ?? await pickItemForRefine();
|
|
9032
|
+
if (!itemId2) return;
|
|
9033
|
+
const numericId = parseItemId(itemId2);
|
|
9034
|
+
const item = await loadItem((await getReady()).orm, numericId);
|
|
9035
|
+
await launchMode(`refine ${itemId2}`, {
|
|
8827
9036
|
...options2,
|
|
8828
9037
|
itemId: item ? numericId : void 0,
|
|
8829
9038
|
itemName: item?.name
|
|
@@ -8833,20 +9042,17 @@ async function refine(id2, options2) {
|
|
|
8833
9042
|
// src/commands/backlog/registerRefineCommand.ts
|
|
8834
9043
|
function registerRefineCommand(cmd) {
|
|
8835
9044
|
cmd.command("refine").argument("[id]", "Backlog item ID").description("Alias for refine").option("--once", "Exit when the initial task completes").action(
|
|
8836
|
-
(
|
|
9045
|
+
(id, opts) => refine(id, { once: opts.once })
|
|
8837
9046
|
);
|
|
8838
9047
|
}
|
|
8839
9048
|
|
|
8840
9049
|
// src/commands/backlog/rewindPhase.ts
|
|
8841
9050
|
import chalk81 from "chalk";
|
|
8842
|
-
async function rewindPhase(
|
|
9051
|
+
async function rewindPhase(id, phase, opts) {
|
|
8843
9052
|
const phaseNumber = Number.parseInt(phase, 10);
|
|
8844
|
-
const
|
|
8845
|
-
|
|
8846
|
-
|
|
8847
|
-
console.log(chalk81.red(`Item #${id2} not found.`));
|
|
8848
|
-
return;
|
|
8849
|
-
}
|
|
9053
|
+
const found = await findOneItem(id);
|
|
9054
|
+
if (!found) return;
|
|
9055
|
+
const { orm, item } = found;
|
|
8850
9056
|
const result = await rewindItemToPhase(orm, item, phaseNumber, opts.reason);
|
|
8851
9057
|
if (!result.ok) {
|
|
8852
9058
|
console.log(chalk81.red(result.error));
|
|
@@ -8855,7 +9061,7 @@ async function rewindPhase(id2, phase, opts) {
|
|
|
8855
9061
|
}
|
|
8856
9062
|
console.log(
|
|
8857
9063
|
chalk81.green(
|
|
8858
|
-
`Rewound item
|
|
9064
|
+
`Rewound item ${formatItemId(item.id)} to phase ${phaseNumber} (${result.phaseName}).`
|
|
8859
9065
|
)
|
|
8860
9066
|
);
|
|
8861
9067
|
}
|
|
@@ -8871,9 +9077,9 @@ function registerRunCommand(cmd) {
|
|
|
8871
9077
|
"--resume-session <id>",
|
|
8872
9078
|
"Resume an interrupted Claude session for the current phase (used by the sessions daemon on restart)"
|
|
8873
9079
|
).action(
|
|
8874
|
-
async (
|
|
9080
|
+
async (id, opts) => {
|
|
8875
9081
|
if (!opts.resumeSession) pullIfConfigured();
|
|
8876
|
-
await run2(
|
|
9082
|
+
await run2(id, {
|
|
8877
9083
|
allowEdits: opts.write !== false,
|
|
8878
9084
|
resumeSessionId: opts.resumeSession
|
|
8879
9085
|
});
|
|
@@ -8897,7 +9103,7 @@ async function search(query) {
|
|
|
8897
9103
|
);
|
|
8898
9104
|
for (const item of items2) {
|
|
8899
9105
|
console.log(
|
|
8900
|
-
`${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk82.dim(
|
|
9106
|
+
`${statusIcon(item.status)} ${typeLabel(item.type)} ${chalk82.dim(formatItemId(item.id))} ${item.name}`
|
|
8901
9107
|
);
|
|
8902
9108
|
}
|
|
8903
9109
|
}
|
|
@@ -8909,20 +9115,22 @@ function registerSearchCommand(cmd) {
|
|
|
8909
9115
|
|
|
8910
9116
|
// src/commands/backlog/delete/index.ts
|
|
8911
9117
|
import chalk83 from "chalk";
|
|
8912
|
-
async function del(
|
|
8913
|
-
const name = await removeItem(
|
|
9118
|
+
async function del(id) {
|
|
9119
|
+
const name = await removeItem(id);
|
|
8914
9120
|
if (name) {
|
|
8915
|
-
console.log(
|
|
9121
|
+
console.log(
|
|
9122
|
+
chalk83.green(`Deleted item ${formatItemId(parseItemId(id))}: ${name}`)
|
|
9123
|
+
);
|
|
8916
9124
|
}
|
|
8917
9125
|
}
|
|
8918
9126
|
|
|
8919
9127
|
// src/commands/backlog/done/index.ts
|
|
8920
9128
|
import chalk84 from "chalk";
|
|
8921
|
-
async function done(
|
|
8922
|
-
const found = await findOneItem(
|
|
9129
|
+
async function done(id, summary) {
|
|
9130
|
+
const found = await findOneItem(id);
|
|
8923
9131
|
if (!found) return;
|
|
8924
9132
|
const { orm, item } = found;
|
|
8925
|
-
if (!checkSubtasksComplete(
|
|
9133
|
+
if (!checkSubtasksComplete(item)) {
|
|
8926
9134
|
process.exitCode = 1;
|
|
8927
9135
|
return;
|
|
8928
9136
|
}
|
|
@@ -8932,7 +9140,7 @@ async function done(id2, summary) {
|
|
|
8932
9140
|
if (pending.length > 0) {
|
|
8933
9141
|
console.log(
|
|
8934
9142
|
chalk84.red(
|
|
8935
|
-
`Cannot complete item
|
|
9143
|
+
`Cannot complete item ${formatItemId(item.id)}: ${pending.length} pending phase(s):`
|
|
8936
9144
|
)
|
|
8937
9145
|
);
|
|
8938
9146
|
for (const phase of pending) {
|
|
@@ -8947,7 +9155,9 @@ async function done(id2, summary) {
|
|
|
8947
9155
|
const phase = item.currentPhase ?? 1;
|
|
8948
9156
|
await appendComment(orm, item.id, summary, { phase, type: "summary" });
|
|
8949
9157
|
}
|
|
8950
|
-
console.log(
|
|
9158
|
+
console.log(
|
|
9159
|
+
chalk84.green(`Completed item ${formatItemId(item.id)}: ${item.name}`)
|
|
9160
|
+
);
|
|
8951
9161
|
}
|
|
8952
9162
|
|
|
8953
9163
|
// src/commands/backlog/set-status/index.ts
|
|
@@ -8958,7 +9168,7 @@ var allowedStatuses = [
|
|
|
8958
9168
|
"done",
|
|
8959
9169
|
"wontdo"
|
|
8960
9170
|
];
|
|
8961
|
-
async function setStatusCommand(
|
|
9171
|
+
async function setStatusCommand(id, status2) {
|
|
8962
9172
|
if (!allowedStatuses.includes(status2)) {
|
|
8963
9173
|
console.log(
|
|
8964
9174
|
chalk85.red(
|
|
@@ -8968,9 +9178,13 @@ async function setStatusCommand(id2, status2) {
|
|
|
8968
9178
|
process.exitCode = 1;
|
|
8969
9179
|
return;
|
|
8970
9180
|
}
|
|
8971
|
-
const name = await setStatus(
|
|
9181
|
+
const name = await setStatus(id, status2);
|
|
8972
9182
|
if (name) {
|
|
8973
|
-
console.log(
|
|
9183
|
+
console.log(
|
|
9184
|
+
chalk85.green(
|
|
9185
|
+
`Set item ${formatItemId(parseItemId(id))} to ${status2}: ${name}`
|
|
9186
|
+
)
|
|
9187
|
+
);
|
|
8974
9188
|
}
|
|
8975
9189
|
}
|
|
8976
9190
|
|
|
@@ -8979,27 +9193,33 @@ import chalk87 from "chalk";
|
|
|
8979
9193
|
|
|
8980
9194
|
// src/commands/backlog/setStarred.ts
|
|
8981
9195
|
import chalk86 from "chalk";
|
|
8982
|
-
async function setStarred(
|
|
9196
|
+
async function setStarred(id, starred) {
|
|
8983
9197
|
const { orm } = await getReady();
|
|
8984
|
-
const
|
|
8985
|
-
|
|
9198
|
+
const numId = parseItemId(id);
|
|
9199
|
+
const name = await updateStarred(orm, numId, starred);
|
|
9200
|
+
if (name === void 0)
|
|
9201
|
+
console.log(chalk86.red(`Item ${formatItemId(numId)} not found.`));
|
|
8986
9202
|
return name;
|
|
8987
9203
|
}
|
|
8988
9204
|
|
|
8989
9205
|
// src/commands/backlog/star/index.ts
|
|
8990
|
-
async function star(
|
|
8991
|
-
const name = await setStarred(
|
|
9206
|
+
async function star(id) {
|
|
9207
|
+
const name = await setStarred(id, true);
|
|
8992
9208
|
if (name) {
|
|
8993
|
-
console.log(
|
|
9209
|
+
console.log(
|
|
9210
|
+
chalk87.green(`Starred item ${formatItemId(parseItemId(id))}: ${name}`)
|
|
9211
|
+
);
|
|
8994
9212
|
}
|
|
8995
9213
|
}
|
|
8996
9214
|
|
|
8997
9215
|
// src/commands/backlog/start/index.ts
|
|
8998
9216
|
import chalk88 from "chalk";
|
|
8999
|
-
async function start(
|
|
9000
|
-
const name = await setStatus(
|
|
9217
|
+
async function start(id) {
|
|
9218
|
+
const name = await setStatus(id, "in-progress");
|
|
9001
9219
|
if (name) {
|
|
9002
|
-
console.log(
|
|
9220
|
+
console.log(
|
|
9221
|
+
chalk88.green(`Started item ${formatItemId(parseItemId(id))}: ${name}`)
|
|
9222
|
+
);
|
|
9003
9223
|
}
|
|
9004
9224
|
}
|
|
9005
9225
|
|
|
@@ -9014,23 +9234,27 @@ async function stop() {
|
|
|
9014
9234
|
return;
|
|
9015
9235
|
}
|
|
9016
9236
|
for (const item of stopped) {
|
|
9017
|
-
console.log(
|
|
9237
|
+
console.log(
|
|
9238
|
+
chalk89.yellow(`Stopped item ${formatItemId(item.id)}: ${item.name}`)
|
|
9239
|
+
);
|
|
9018
9240
|
}
|
|
9019
9241
|
}
|
|
9020
9242
|
|
|
9021
9243
|
// src/commands/backlog/unstar/index.ts
|
|
9022
9244
|
import chalk90 from "chalk";
|
|
9023
|
-
async function unstar(
|
|
9024
|
-
const name = await setStarred(
|
|
9245
|
+
async function unstar(id) {
|
|
9246
|
+
const name = await setStarred(id, false);
|
|
9025
9247
|
if (name) {
|
|
9026
|
-
console.log(
|
|
9248
|
+
console.log(
|
|
9249
|
+
chalk90.green(`Unstarred item ${formatItemId(parseItemId(id))}: ${name}`)
|
|
9250
|
+
);
|
|
9027
9251
|
}
|
|
9028
9252
|
}
|
|
9029
9253
|
|
|
9030
9254
|
// src/commands/backlog/wontdo/index.ts
|
|
9031
9255
|
import chalk91 from "chalk";
|
|
9032
|
-
async function wontdo(
|
|
9033
|
-
const found = await findOneItem(
|
|
9256
|
+
async function wontdo(id, reason) {
|
|
9257
|
+
const found = await findOneItem(id);
|
|
9034
9258
|
if (!found) return;
|
|
9035
9259
|
const { orm, item } = found;
|
|
9036
9260
|
await updateStatus(orm, item.id, "wontdo");
|
|
@@ -9038,7 +9262,9 @@ async function wontdo(id2, reason) {
|
|
|
9038
9262
|
const phase = item.currentPhase ?? 1;
|
|
9039
9263
|
await appendComment(orm, item.id, reason, { phase, type: "summary" });
|
|
9040
9264
|
}
|
|
9041
|
-
console.log(
|
|
9265
|
+
console.log(
|
|
9266
|
+
chalk91.red(`Won't do item ${formatItemId(item.id)}: ${item.name}`)
|
|
9267
|
+
);
|
|
9042
9268
|
}
|
|
9043
9269
|
|
|
9044
9270
|
// src/commands/backlog/registerStatusCommands.ts
|
|
@@ -9057,7 +9283,7 @@ function registerStatusCommands(cmd) {
|
|
|
9057
9283
|
|
|
9058
9284
|
// src/commands/backlog/addSubtask.ts
|
|
9059
9285
|
import chalk92 from "chalk";
|
|
9060
|
-
async function addSubtask(
|
|
9286
|
+
async function addSubtask(id, options2) {
|
|
9061
9287
|
const title = options2.title?.trim();
|
|
9062
9288
|
if (!title) {
|
|
9063
9289
|
console.log(chalk92.red("A sub-task title is required (--title)."));
|
|
@@ -9073,7 +9299,7 @@ async function addSubtask(id2, options2) {
|
|
|
9073
9299
|
process.exitCode = 1;
|
|
9074
9300
|
return;
|
|
9075
9301
|
}
|
|
9076
|
-
const found = await findOneItem(
|
|
9302
|
+
const found = await findOneItem(id);
|
|
9077
9303
|
if (!found) {
|
|
9078
9304
|
process.exitCode = 1;
|
|
9079
9305
|
return;
|
|
@@ -9081,7 +9307,9 @@ async function addSubtask(id2, options2) {
|
|
|
9081
9307
|
const { orm, item } = found;
|
|
9082
9308
|
const description = options2.desc?.replaceAll(String.raw`\n`, "\n");
|
|
9083
9309
|
await insertSubtask(orm, item.id, title, description);
|
|
9084
|
-
console.log(
|
|
9310
|
+
console.log(
|
|
9311
|
+
chalk92.green(`Added sub-task to item ${formatItemId(item.id)}: ${title}`)
|
|
9312
|
+
);
|
|
9085
9313
|
}
|
|
9086
9314
|
|
|
9087
9315
|
// src/commands/backlog/editSubtask.ts
|
|
@@ -9089,13 +9317,13 @@ import chalk94 from "chalk";
|
|
|
9089
9317
|
|
|
9090
9318
|
// src/commands/backlog/resolveSubtaskIndex.ts
|
|
9091
9319
|
import chalk93 from "chalk";
|
|
9092
|
-
function resolveSubtaskIndex(
|
|
9320
|
+
function resolveSubtaskIndex(index3, item) {
|
|
9093
9321
|
const position = Number.parseInt(index3, 10);
|
|
9094
9322
|
const subtasks = item.subtasks ?? [];
|
|
9095
9323
|
if (Number.isNaN(position) || position < 1 || position > subtasks.length) {
|
|
9096
9324
|
console.log(
|
|
9097
9325
|
chalk93.red(
|
|
9098
|
-
`Item
|
|
9326
|
+
`Item ${formatItemId(item.id)} has no sub-task ${index3}${subtasks.length > 0 ? ` (1-${subtasks.length})` : ""}.`
|
|
9099
9327
|
)
|
|
9100
9328
|
);
|
|
9101
9329
|
process.exitCode = 1;
|
|
@@ -9105,25 +9333,25 @@ function resolveSubtaskIndex(id2, index3, item) {
|
|
|
9105
9333
|
}
|
|
9106
9334
|
|
|
9107
9335
|
// src/commands/backlog/findSubtask.ts
|
|
9108
|
-
async function findSubtask(
|
|
9109
|
-
const found = await findOneItem(
|
|
9336
|
+
async function findSubtask(id, index3) {
|
|
9337
|
+
const found = await findOneItem(id);
|
|
9110
9338
|
if (!found) {
|
|
9111
9339
|
process.exitCode = 1;
|
|
9112
9340
|
return void 0;
|
|
9113
9341
|
}
|
|
9114
|
-
const idx = resolveSubtaskIndex(
|
|
9342
|
+
const idx = resolveSubtaskIndex(index3, found.item);
|
|
9115
9343
|
if (idx === void 0) return void 0;
|
|
9116
9344
|
return { orm: found.orm, item: found.item, idx };
|
|
9117
9345
|
}
|
|
9118
9346
|
|
|
9119
9347
|
// src/commands/backlog/updateSubtask.ts
|
|
9120
9348
|
import { and as and9, eq as eq25 } from "drizzle-orm";
|
|
9121
|
-
async function updateSubtask(orm,
|
|
9349
|
+
async function updateSubtask(orm, itemId2, idx, fields) {
|
|
9122
9350
|
const set = {};
|
|
9123
9351
|
if (fields.title !== void 0) set.title = fields.title;
|
|
9124
9352
|
if (fields.description !== void 0) set.description = fields.description;
|
|
9125
9353
|
if (fields.status !== void 0) set.status = fields.status;
|
|
9126
|
-
const [row] = await orm.update(itemSubtasks).set(set).where(and9(eq25(itemSubtasks.itemId,
|
|
9354
|
+
const [row] = await orm.update(itemSubtasks).set(set).where(and9(eq25(itemSubtasks.itemId, itemId2), eq25(itemSubtasks.idx, idx))).returning({ title: itemSubtasks.title });
|
|
9127
9355
|
return row?.title;
|
|
9128
9356
|
}
|
|
9129
9357
|
|
|
@@ -9156,8 +9384,8 @@ function buildUpdate(options2) {
|
|
|
9156
9384
|
}
|
|
9157
9385
|
return fields;
|
|
9158
9386
|
}
|
|
9159
|
-
async function editSubtask(
|
|
9160
|
-
const found = await findSubtask(
|
|
9387
|
+
async function editSubtask(id, index3, options2) {
|
|
9388
|
+
const found = await findSubtask(id, index3);
|
|
9161
9389
|
if (!found) return;
|
|
9162
9390
|
const fields = buildUpdate(options2);
|
|
9163
9391
|
if (typeof fields === "string") {
|
|
@@ -9168,7 +9396,9 @@ async function editSubtask(id2, index3, options2) {
|
|
|
9168
9396
|
const { orm, item, idx } = found;
|
|
9169
9397
|
const title = await updateSubtask(orm, item.id, idx, fields);
|
|
9170
9398
|
console.log(
|
|
9171
|
-
chalk94.green(
|
|
9399
|
+
chalk94.green(
|
|
9400
|
+
`Updated sub-task ${idx + 1} of item ${formatItemId(item.id)}: ${title}`
|
|
9401
|
+
)
|
|
9172
9402
|
);
|
|
9173
9403
|
}
|
|
9174
9404
|
|
|
@@ -9177,34 +9407,36 @@ import chalk95 from "chalk";
|
|
|
9177
9407
|
|
|
9178
9408
|
// src/commands/backlog/deleteSubtask.ts
|
|
9179
9409
|
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,
|
|
9410
|
+
async function deleteSubtask(orm, itemId2, idx) {
|
|
9411
|
+
const [row] = await orm.delete(itemSubtasks).where(and10(eq26(itemSubtasks.itemId, itemId2), eq26(itemSubtasks.idx, idx))).returning({ title: itemSubtasks.title });
|
|
9182
9412
|
if (!row) return void 0;
|
|
9183
|
-
const remaining = await orm.select({ idx: itemSubtasks.idx }).from(itemSubtasks).where(eq26(itemSubtasks.itemId,
|
|
9413
|
+
const remaining = await orm.select({ idx: itemSubtasks.idx }).from(itemSubtasks).where(eq26(itemSubtasks.itemId, itemId2)).orderBy(asc6(itemSubtasks.idx));
|
|
9184
9414
|
for (let i = 0; i < remaining.length; i++) {
|
|
9185
9415
|
const oldIdx = remaining[i].idx;
|
|
9186
9416
|
if (oldIdx === i) continue;
|
|
9187
9417
|
await orm.update(itemSubtasks).set({ idx: i }).where(
|
|
9188
|
-
and10(eq26(itemSubtasks.itemId,
|
|
9418
|
+
and10(eq26(itemSubtasks.itemId, itemId2), eq26(itemSubtasks.idx, oldIdx))
|
|
9189
9419
|
);
|
|
9190
9420
|
}
|
|
9191
9421
|
return row.title;
|
|
9192
9422
|
}
|
|
9193
9423
|
|
|
9194
9424
|
// src/commands/backlog/removeSubtask.ts
|
|
9195
|
-
async function removeSubtask(
|
|
9196
|
-
const found = await findSubtask(
|
|
9425
|
+
async function removeSubtask(id, index3) {
|
|
9426
|
+
const found = await findSubtask(id, index3);
|
|
9197
9427
|
if (!found) return;
|
|
9198
9428
|
const { orm, item, idx } = found;
|
|
9199
9429
|
const title = await deleteSubtask(orm, item.id, idx);
|
|
9200
9430
|
console.log(
|
|
9201
|
-
chalk95.green(
|
|
9431
|
+
chalk95.green(
|
|
9432
|
+
`Removed sub-task ${idx + 1} of item ${formatItemId(item.id)}: ${title}`
|
|
9433
|
+
)
|
|
9202
9434
|
);
|
|
9203
9435
|
}
|
|
9204
9436
|
|
|
9205
9437
|
// src/commands/backlog/subtaskStatus.ts
|
|
9206
9438
|
import chalk96 from "chalk";
|
|
9207
|
-
async function subtaskStatus(
|
|
9439
|
+
async function subtaskStatus(id, index3, status2) {
|
|
9208
9440
|
if (!validSubtaskStatuses.includes(status2)) {
|
|
9209
9441
|
console.log(
|
|
9210
9442
|
chalk96.red(
|
|
@@ -9214,7 +9446,7 @@ async function subtaskStatus(id2, index3, status2) {
|
|
|
9214
9446
|
process.exitCode = 1;
|
|
9215
9447
|
return;
|
|
9216
9448
|
}
|
|
9217
|
-
const found = await findSubtask(
|
|
9449
|
+
const found = await findSubtask(id, index3);
|
|
9218
9450
|
if (!found) return;
|
|
9219
9451
|
const { orm, item, idx } = found;
|
|
9220
9452
|
const title = await updateSubtaskStatus(
|
|
@@ -9225,7 +9457,7 @@ async function subtaskStatus(id2, index3, status2) {
|
|
|
9225
9457
|
);
|
|
9226
9458
|
console.log(
|
|
9227
9459
|
chalk96.green(
|
|
9228
|
-
`Set sub-task ${idx + 1} of item
|
|
9460
|
+
`Set sub-task ${idx + 1} of item ${formatItemId(item.id)} to ${status2}: ${title}`
|
|
9229
9461
|
)
|
|
9230
9462
|
);
|
|
9231
9463
|
}
|
|
@@ -9259,13 +9491,13 @@ import { and as and12, asc as asc8, eq as eq28 } from "drizzle-orm";
|
|
|
9259
9491
|
|
|
9260
9492
|
// src/commands/backlog/reindexPhases.ts
|
|
9261
9493
|
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,
|
|
9494
|
+
async function reindexPhases(db, itemId2) {
|
|
9495
|
+
const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(eq27(planPhases.itemId, itemId2)).orderBy(asc7(planPhases.idx));
|
|
9264
9496
|
for (let i = 0; i < remaining.length; i++) {
|
|
9265
9497
|
const oldIdx = remaining[i].idx;
|
|
9266
9498
|
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,
|
|
9499
|
+
await db.update(planTasks).set({ phaseIdx: i }).where(and11(eq27(planTasks.itemId, itemId2), eq27(planTasks.phaseIdx, oldIdx)));
|
|
9500
|
+
await db.update(planPhases).set({ idx: i }).where(and11(eq27(planPhases.itemId, itemId2), eq27(planPhases.idx, oldIdx)));
|
|
9269
9501
|
}
|
|
9270
9502
|
}
|
|
9271
9503
|
async function adjustCurrentPhase(db, item, removedIdx) {
|
|
@@ -9283,18 +9515,18 @@ async function adjustCurrentPhase(db, item, removedIdx) {
|
|
|
9283
9515
|
}
|
|
9284
9516
|
|
|
9285
9517
|
// src/commands/backlog/reorderPhaseRows.ts
|
|
9286
|
-
async function reorderPhaseRows(orm,
|
|
9518
|
+
async function reorderPhaseRows(orm, itemId2, fromIdx, toIdx) {
|
|
9287
9519
|
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,
|
|
9520
|
+
const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(and12(eq28(planPhases.itemId, itemId2), eq28(planPhases.idx, fromIdx)));
|
|
9521
|
+
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
9522
|
await tx.delete(planTasks).where(
|
|
9291
|
-
and12(eq28(planTasks.itemId,
|
|
9523
|
+
and12(eq28(planTasks.itemId, itemId2), eq28(planTasks.phaseIdx, fromIdx))
|
|
9292
9524
|
);
|
|
9293
|
-
await tx.delete(planPhases).where(and12(eq28(planPhases.itemId,
|
|
9294
|
-
await reindexPhases(tx,
|
|
9295
|
-
await shiftPhasesUp(tx,
|
|
9525
|
+
await tx.delete(planPhases).where(and12(eq28(planPhases.itemId, itemId2), eq28(planPhases.idx, fromIdx)));
|
|
9526
|
+
await reindexPhases(tx, itemId2);
|
|
9527
|
+
await shiftPhasesUp(tx, itemId2, toIdx);
|
|
9296
9528
|
await tx.insert(planPhases).values({
|
|
9297
|
-
itemId,
|
|
9529
|
+
itemId: itemId2,
|
|
9298
9530
|
idx: toIdx,
|
|
9299
9531
|
name: phaseRow.name,
|
|
9300
9532
|
manualChecks: phaseRow.manualChecks
|
|
@@ -9302,7 +9534,7 @@ async function reorderPhaseRows(orm, itemId, fromIdx, toIdx) {
|
|
|
9302
9534
|
if (tasks.length) {
|
|
9303
9535
|
await tx.insert(planTasks).values(
|
|
9304
9536
|
tasks.map((t) => ({
|
|
9305
|
-
itemId,
|
|
9537
|
+
itemId: itemId2,
|
|
9306
9538
|
phaseIdx: toIdx,
|
|
9307
9539
|
idx: t.idx,
|
|
9308
9540
|
task: t.task
|
|
@@ -9326,19 +9558,19 @@ function toIndex(value, phaseCount) {
|
|
|
9326
9558
|
}
|
|
9327
9559
|
return pos - 1;
|
|
9328
9560
|
}
|
|
9329
|
-
async function movePhase(
|
|
9330
|
-
const found = await findOneItem(
|
|
9561
|
+
async function movePhase(id, from, to) {
|
|
9562
|
+
const found = await findOneItem(id);
|
|
9331
9563
|
if (!found) return;
|
|
9332
9564
|
const { orm, item } = found;
|
|
9333
|
-
const
|
|
9334
|
-
const [row] = await orm.select({ cnt: count6() }).from(planPhases).where(eq29(planPhases.itemId,
|
|
9565
|
+
const itemId2 = item.id;
|
|
9566
|
+
const [row] = await orm.select({ cnt: count6() }).from(planPhases).where(eq29(planPhases.itemId, itemId2));
|
|
9335
9567
|
const phaseCount = row?.cnt ?? 0;
|
|
9336
9568
|
const fromIdx = toIndex(from, phaseCount);
|
|
9337
9569
|
if (fromIdx === void 0) return;
|
|
9338
9570
|
const toIdx = toIndex(to, phaseCount);
|
|
9339
9571
|
if (toIdx === void 0) return;
|
|
9340
9572
|
if (fromIdx !== toIdx) {
|
|
9341
|
-
await reorderPhaseRows(orm,
|
|
9573
|
+
await reorderPhaseRows(orm, itemId2, fromIdx, toIdx);
|
|
9342
9574
|
}
|
|
9343
9575
|
console.log(chalk97.green(`Moved phase ${from} to position ${to}.`));
|
|
9344
9576
|
}
|
|
@@ -9348,26 +9580,26 @@ import chalk99 from "chalk";
|
|
|
9348
9580
|
|
|
9349
9581
|
// src/commands/backlog/applyPhaseUpdate.ts
|
|
9350
9582
|
import { and as and13, eq as eq30 } from "drizzle-orm";
|
|
9351
|
-
async function applyPhaseUpdate(orm,
|
|
9583
|
+
async function applyPhaseUpdate(orm, itemId2, phaseIdx, fields) {
|
|
9352
9584
|
await orm.transaction(async (tx) => {
|
|
9353
9585
|
if (fields.name) {
|
|
9354
9586
|
await tx.update(planPhases).set({ name: fields.name }).where(
|
|
9355
|
-
and13(eq30(planPhases.itemId,
|
|
9587
|
+
and13(eq30(planPhases.itemId, itemId2), eq30(planPhases.idx, phaseIdx))
|
|
9356
9588
|
);
|
|
9357
9589
|
}
|
|
9358
9590
|
if (fields.manualCheck) {
|
|
9359
9591
|
await tx.update(planPhases).set({ manualChecks: JSON.stringify(fields.manualCheck) }).where(
|
|
9360
|
-
and13(eq30(planPhases.itemId,
|
|
9592
|
+
and13(eq30(planPhases.itemId, itemId2), eq30(planPhases.idx, phaseIdx))
|
|
9361
9593
|
);
|
|
9362
9594
|
}
|
|
9363
9595
|
if (fields.task) {
|
|
9364
9596
|
await tx.delete(planTasks).where(
|
|
9365
|
-
and13(eq30(planTasks.itemId,
|
|
9597
|
+
and13(eq30(planTasks.itemId, itemId2), eq30(planTasks.phaseIdx, phaseIdx))
|
|
9366
9598
|
);
|
|
9367
9599
|
if (fields.task.length) {
|
|
9368
9600
|
await tx.insert(planTasks).values(
|
|
9369
9601
|
fields.task.map((task, i) => ({
|
|
9370
|
-
itemId,
|
|
9602
|
+
itemId: itemId2,
|
|
9371
9603
|
phaseIdx,
|
|
9372
9604
|
idx: i,
|
|
9373
9605
|
task
|
|
@@ -9381,21 +9613,23 @@ async function applyPhaseUpdate(orm, itemId, phaseIdx, fields) {
|
|
|
9381
9613
|
// src/commands/backlog/findPhase.ts
|
|
9382
9614
|
import chalk98 from "chalk";
|
|
9383
9615
|
import { and as and14, count as count7, eq as eq31 } from "drizzle-orm";
|
|
9384
|
-
async function findPhase(
|
|
9385
|
-
const found = await findOneItem(
|
|
9616
|
+
async function findPhase(id, phase) {
|
|
9617
|
+
const found = await findOneItem(id);
|
|
9386
9618
|
if (!found) return void 0;
|
|
9387
9619
|
const { orm, item } = found;
|
|
9388
|
-
const
|
|
9620
|
+
const itemId2 = item.id;
|
|
9389
9621
|
const phaseIdx = Number.parseInt(phase, 10) - 1;
|
|
9390
|
-
const [row] = await orm.select({ cnt: count7() }).from(planPhases).where(and14(eq31(planPhases.itemId,
|
|
9622
|
+
const [row] = await orm.select({ cnt: count7() }).from(planPhases).where(and14(eq31(planPhases.itemId, itemId2), eq31(planPhases.idx, phaseIdx)));
|
|
9391
9623
|
if (!row || row.cnt === 0) {
|
|
9392
9624
|
console.log(
|
|
9393
|
-
chalk98.red(
|
|
9625
|
+
chalk98.red(
|
|
9626
|
+
`Phase ${phaseIdx + 1} not found on item ${formatItemId(itemId2)}.`
|
|
9627
|
+
)
|
|
9394
9628
|
);
|
|
9395
9629
|
process.exitCode = 1;
|
|
9396
9630
|
return void 0;
|
|
9397
9631
|
}
|
|
9398
|
-
return { item, orm, itemId, phaseIdx };
|
|
9632
|
+
return { item, orm, itemId: itemId2, phaseIdx };
|
|
9399
9633
|
}
|
|
9400
9634
|
|
|
9401
9635
|
// src/commands/backlog/update/parseListIndex.ts
|
|
@@ -9493,11 +9727,11 @@ function resolveChecks(o, current) {
|
|
|
9493
9727
|
CHECK_FLAGS
|
|
9494
9728
|
);
|
|
9495
9729
|
}
|
|
9496
|
-
function buildFields(name, tasks,
|
|
9730
|
+
function buildFields(name, tasks, checks2) {
|
|
9497
9731
|
const fields = {
|
|
9498
9732
|
name,
|
|
9499
9733
|
task: tasks.items,
|
|
9500
|
-
manualCheck:
|
|
9734
|
+
manualCheck: checks2.items
|
|
9501
9735
|
};
|
|
9502
9736
|
if (!fields.name && !fields.task && !fields.manualCheck) {
|
|
9503
9737
|
return {
|
|
@@ -9510,16 +9744,16 @@ function buildFields(name, tasks, checks) {
|
|
|
9510
9744
|
function resolvePhaseFields(options2, current) {
|
|
9511
9745
|
const tasks = resolveTasks(options2, current);
|
|
9512
9746
|
if (!tasks.ok) return tasks;
|
|
9513
|
-
const
|
|
9514
|
-
if (!
|
|
9515
|
-
return buildFields(options2.name, tasks,
|
|
9747
|
+
const checks2 = resolveChecks(options2, current);
|
|
9748
|
+
if (!checks2.ok) return checks2;
|
|
9749
|
+
return buildFields(options2.name, tasks, checks2);
|
|
9516
9750
|
}
|
|
9517
9751
|
|
|
9518
9752
|
// src/commands/backlog/updatePhase.ts
|
|
9519
|
-
async function updatePhase(
|
|
9520
|
-
const found = await findPhase(
|
|
9753
|
+
async function updatePhase(id, phase, options2) {
|
|
9754
|
+
const found = await findPhase(id, phase);
|
|
9521
9755
|
if (!found) return;
|
|
9522
|
-
const { item, orm, itemId, phaseIdx } = found;
|
|
9756
|
+
const { item, orm, itemId: itemId2, phaseIdx } = found;
|
|
9523
9757
|
const resolved = resolvePhaseFields(options2, item.plan?.[phaseIdx]);
|
|
9524
9758
|
if (!resolved.ok) {
|
|
9525
9759
|
console.log(chalk99.red(resolved.error));
|
|
@@ -9527,7 +9761,7 @@ async function updatePhase(id2, phase, options2) {
|
|
|
9527
9761
|
return;
|
|
9528
9762
|
}
|
|
9529
9763
|
const { name, task, manualCheck } = resolved.fields;
|
|
9530
|
-
await applyPhaseUpdate(orm,
|
|
9764
|
+
await applyPhaseUpdate(orm, itemId2, phaseIdx, { name, task, manualCheck });
|
|
9531
9765
|
const fields = [
|
|
9532
9766
|
name && "name",
|
|
9533
9767
|
task && "tasks",
|
|
@@ -9535,20 +9769,20 @@ async function updatePhase(id2, phase, options2) {
|
|
|
9535
9769
|
].filter(Boolean).join(", ");
|
|
9536
9770
|
console.log(
|
|
9537
9771
|
chalk99.green(
|
|
9538
|
-
`Updated ${fields} on phase ${phaseIdx + 1} of item
|
|
9772
|
+
`Updated ${fields} on phase ${phaseIdx + 1} of item ${formatItemId(itemId2)}.`
|
|
9539
9773
|
)
|
|
9540
9774
|
);
|
|
9541
9775
|
}
|
|
9542
9776
|
|
|
9543
9777
|
// src/commands/backlog/registerUpdatePhaseCommand.ts
|
|
9544
|
-
function
|
|
9778
|
+
function collect2(value, previous) {
|
|
9545
9779
|
return [...previous, value];
|
|
9546
9780
|
}
|
|
9547
9781
|
function registerUpdatePhaseCommand(cmd) {
|
|
9548
|
-
cmd.command("update-phase <id> <phase>").description("Modify a plan phase on a backlog item").option("--name <name>", "New phase name").option("--task <task...>", "Replace tasks (repeatable)").option("--manual-check <check...>", "Replace manual checks (repeatable)").option("--add-task <text>", "Append one task (repeatable)",
|
|
9782
|
+
cmd.command("update-phase <id> <phase>").description("Modify a plan phase on a backlog item").option("--name <name>", "New phase name").option("--task <task...>", "Replace tasks (repeatable)").option("--manual-check <check...>", "Replace manual checks (repeatable)").option("--add-task <text>", "Append one task (repeatable)", collect2, []).option("--edit-task <n> <text...>", "Replace task n (1-based) in place").option("--remove-task <n>", "Remove task n (1-based)").option(
|
|
9549
9783
|
"--add-check <text>",
|
|
9550
9784
|
"Append one manual check (repeatable)",
|
|
9551
|
-
|
|
9785
|
+
collect2,
|
|
9552
9786
|
[]
|
|
9553
9787
|
).option(
|
|
9554
9788
|
"--edit-check <n> <text...>",
|
|
@@ -9559,20 +9793,22 @@ function registerUpdatePhaseCommand(cmd) {
|
|
|
9559
9793
|
// src/commands/backlog/removePhase.ts
|
|
9560
9794
|
import chalk100 from "chalk";
|
|
9561
9795
|
import { and as and15, eq as eq32 } from "drizzle-orm";
|
|
9562
|
-
async function removePhase(
|
|
9563
|
-
const found = await findPhase(
|
|
9796
|
+
async function removePhase(id, phase) {
|
|
9797
|
+
const found = await findPhase(id, phase);
|
|
9564
9798
|
if (!found) return;
|
|
9565
|
-
const { item, orm, itemId, phaseIdx } = found;
|
|
9799
|
+
const { item, orm, itemId: itemId2, phaseIdx } = found;
|
|
9566
9800
|
await orm.transaction(async (tx) => {
|
|
9567
9801
|
await tx.delete(planTasks).where(
|
|
9568
|
-
and15(eq32(planTasks.itemId,
|
|
9802
|
+
and15(eq32(planTasks.itemId, itemId2), eq32(planTasks.phaseIdx, phaseIdx))
|
|
9569
9803
|
);
|
|
9570
|
-
await tx.delete(planPhases).where(and15(eq32(planPhases.itemId,
|
|
9571
|
-
await reindexPhases(tx,
|
|
9804
|
+
await tx.delete(planPhases).where(and15(eq32(planPhases.itemId, itemId2), eq32(planPhases.idx, phaseIdx)));
|
|
9805
|
+
await reindexPhases(tx, itemId2);
|
|
9572
9806
|
await adjustCurrentPhase(tx, item, phaseIdx);
|
|
9573
9807
|
});
|
|
9574
9808
|
console.log(
|
|
9575
|
-
chalk100.green(
|
|
9809
|
+
chalk100.green(
|
|
9810
|
+
`Removed phase ${phaseIdx + 1} from item ${formatItemId(itemId2)}.`
|
|
9811
|
+
)
|
|
9576
9812
|
);
|
|
9577
9813
|
}
|
|
9578
9814
|
|
|
@@ -9634,8 +9870,8 @@ function buildUpdateValues(options2) {
|
|
|
9634
9870
|
}
|
|
9635
9871
|
|
|
9636
9872
|
// src/commands/backlog/update/index.ts
|
|
9637
|
-
async function update(
|
|
9638
|
-
const found = await findOneItem(
|
|
9873
|
+
async function update(id, options2) {
|
|
9874
|
+
const found = await findOneItem(id);
|
|
9639
9875
|
if (!found) return;
|
|
9640
9876
|
let ac = options2.ac;
|
|
9641
9877
|
if (hasAcMutations(options2)) {
|
|
@@ -9657,13 +9893,15 @@ async function update(id2, options2) {
|
|
|
9657
9893
|
const built = buildUpdateValues({ ...options2, ac });
|
|
9658
9894
|
if (!built) return;
|
|
9659
9895
|
const { orm } = found;
|
|
9660
|
-
const
|
|
9661
|
-
await orm.update(items).set(built.set).where(eq33(items.id,
|
|
9662
|
-
console.log(
|
|
9896
|
+
const itemId2 = found.item.id;
|
|
9897
|
+
await orm.update(items).set(built.set).where(eq33(items.id, itemId2));
|
|
9898
|
+
console.log(
|
|
9899
|
+
chalk102.green(`Updated ${built.fields} on item ${formatItemId(itemId2)}.`)
|
|
9900
|
+
);
|
|
9663
9901
|
}
|
|
9664
9902
|
|
|
9665
9903
|
// src/commands/backlog/registerUpdateCommands.ts
|
|
9666
|
-
function
|
|
9904
|
+
function collect3(value, previous) {
|
|
9667
9905
|
return [...previous, value];
|
|
9668
9906
|
}
|
|
9669
9907
|
function registerUpdateCommands(cmd) {
|
|
@@ -9673,7 +9911,7 @@ function registerUpdateCommands(cmd) {
|
|
|
9673
9911
|
).option("--type <type>", "New type (story or bug)").option("--ac <criterion...>", "Replace acceptance criteria (repeatable)").option(
|
|
9674
9912
|
"--add-ac <text>",
|
|
9675
9913
|
"Append one acceptance criterion (repeatable)",
|
|
9676
|
-
|
|
9914
|
+
collect3,
|
|
9677
9915
|
[]
|
|
9678
9916
|
).option(
|
|
9679
9917
|
"--edit-ac <n> <text...>",
|
|
@@ -9721,6 +9959,7 @@ function registerBacklog(program2) {
|
|
|
9721
9959
|
for (const register of registrars) {
|
|
9722
9960
|
register(cmd);
|
|
9723
9961
|
}
|
|
9962
|
+
configHelp(cmd, backlogConfigHelp);
|
|
9724
9963
|
}
|
|
9725
9964
|
|
|
9726
9965
|
// src/commands/branch/validateSlug.ts
|
|
@@ -9769,19 +10008,27 @@ async function branch(slug, options2) {
|
|
|
9769
10008
|
|
|
9770
10009
|
// src/commands/registerBranch.ts
|
|
9771
10010
|
function registerBranch(program2) {
|
|
9772
|
-
program2.command("branch <slug>").description(
|
|
10011
|
+
const branchCommand = program2.command("branch <slug>").description(
|
|
9773
10012
|
"Create and switch to a branch off the fresh remote default branch"
|
|
9774
|
-
).option("--jira <key>", "Jira issue key to include in the branch name").
|
|
9775
|
-
"after",
|
|
9776
|
-
`
|
|
9777
|
-
Branch name is assembled as [<prefix>/][<JIRA>-]<slug>, e.g. sw/BAD-671-add-login-form.
|
|
9778
|
-
|
|
9779
|
-
Config:
|
|
9780
|
-
assist config set branch.prefix sw # optional; prepends "<prefix>/" (omitted when unset)
|
|
9781
|
-
assist config set branch.defaultBranch main # optional; overrides the live remote default branch`
|
|
9782
|
-
).action(
|
|
10013
|
+
).option("--jira <key>", "Jira issue key to include in the branch name").action(
|
|
9783
10014
|
(slug, options2) => branch(slug, options2)
|
|
9784
10015
|
);
|
|
10016
|
+
configHelp(
|
|
10017
|
+
branchCommand,
|
|
10018
|
+
[
|
|
10019
|
+
{
|
|
10020
|
+
key: "branch.prefix",
|
|
10021
|
+
setter: "assist config set branch.prefix sw",
|
|
10022
|
+
note: 'optional; prepends "<prefix>/" (omitted when unset)'
|
|
10023
|
+
},
|
|
10024
|
+
{
|
|
10025
|
+
key: "branch.defaultBranch",
|
|
10026
|
+
setter: "assist config set branch.defaultBranch main",
|
|
10027
|
+
note: "optional; overrides the live remote default branch"
|
|
10028
|
+
}
|
|
10029
|
+
],
|
|
10030
|
+
"Branch name is assembled as [<prefix>/][<JIRA>-]<slug>, e.g. sw/BAD-671-add-login-form."
|
|
10031
|
+
);
|
|
9785
10032
|
}
|
|
9786
10033
|
|
|
9787
10034
|
// src/commands/cliHook/index.ts
|
|
@@ -10834,6 +11081,13 @@ function denyRemove(pattern2, options2) {
|
|
|
10834
11081
|
// src/commands/registerDeny.ts
|
|
10835
11082
|
function registerDeny(parent) {
|
|
10836
11083
|
const denyCommand = parent.command("deny").description("Manage command deny rules").action(denyList);
|
|
11084
|
+
configHelp(denyCommand, [
|
|
11085
|
+
{
|
|
11086
|
+
key: "deny",
|
|
11087
|
+
setter: "assist deny add <pattern> <message>",
|
|
11088
|
+
note: "command patterns blocked with a correction message"
|
|
11089
|
+
}
|
|
11090
|
+
]);
|
|
10837
11091
|
denyCommand.command("add").description("Add a deny rule for a command pattern").argument("<pattern>", "Command prefix to deny").argument("<message>", "Correction message shown to the agent").option("-g, --global", "Write to global ~/.assist.yml").action(denyAdd);
|
|
10838
11092
|
denyCommand.command("remove").description("Remove a deny rule by pattern").argument("<pattern>", "Command prefix to remove").option("-g, --global", "Remove from global ~/.assist.yml").action(denyRemove);
|
|
10839
11093
|
denyCommand.command("list").description("List all deny rules").action(denyList);
|
|
@@ -10844,6 +11098,13 @@ function registerCliHook(program2) {
|
|
|
10844
11098
|
const cmd = program2.command("cli-hook").description("PreToolUse hook for auto-approving read-only CLI commands").action(() => {
|
|
10845
11099
|
cliHook();
|
|
10846
11100
|
});
|
|
11101
|
+
configHelp(cmd, [
|
|
11102
|
+
{
|
|
11103
|
+
key: "cliReadVerbs",
|
|
11104
|
+
setter: 'assist config set cliReadVerbs.git "status"',
|
|
11105
|
+
note: "extra per-CLI verbs treated as read-only for auto-approval"
|
|
11106
|
+
}
|
|
11107
|
+
]);
|
|
10847
11108
|
cmd.command("check <command>").description("Check whether a command would be auto-approved").option("--tool <tool>", "Tool name to check against", "Bash").action((command, options2) => {
|
|
10848
11109
|
cliHookCheck(command, options2.tool);
|
|
10849
11110
|
});
|
|
@@ -11589,6 +11850,15 @@ async function analyze(pattern2) {
|
|
|
11589
11850
|
await maintainability(searchPattern);
|
|
11590
11851
|
}
|
|
11591
11852
|
|
|
11853
|
+
// src/commands/complexity/complexityConfigHelp.ts
|
|
11854
|
+
var complexityConfigHelp = [
|
|
11855
|
+
{
|
|
11856
|
+
key: "complexity.ignore",
|
|
11857
|
+
setter: 'assist config set complexity.ignore "**/*test.ts*"',
|
|
11858
|
+
note: "globs excluded from maintainability (default: **/*test.ts*)"
|
|
11859
|
+
}
|
|
11860
|
+
];
|
|
11861
|
+
|
|
11592
11862
|
// src/commands/registerComplexity.ts
|
|
11593
11863
|
function registerComplexity(program2) {
|
|
11594
11864
|
const complexityCommand = program2.command("complexity").description("Analyze TypeScript code complexity metrics").argument("[pattern]").action((pattern2) => {
|
|
@@ -11611,6 +11881,7 @@ function registerComplexity(program2) {
|
|
|
11611
11881
|
[]
|
|
11612
11882
|
).action(maintainability);
|
|
11613
11883
|
complexityCommand.command("sloc [pattern]").description("Count source lines of code per file").option("--threshold <number>", "Max lines threshold", Number.parseInt).action(sloc);
|
|
11884
|
+
configHelp(complexityCommand, complexityConfigHelp);
|
|
11614
11885
|
}
|
|
11615
11886
|
|
|
11616
11887
|
// src/commands/config/index.ts
|
|
@@ -12312,6 +12583,25 @@ function version() {
|
|
|
12312
12583
|
console.log(`${chalk127.bold("next:")} ${nextVersion ?? chalk127.dim("none")}`);
|
|
12313
12584
|
}
|
|
12314
12585
|
|
|
12586
|
+
// src/commands/devlog/devlogConfigHelp.ts
|
|
12587
|
+
var devlogConfigHelp = [
|
|
12588
|
+
{
|
|
12589
|
+
key: "devlog.name",
|
|
12590
|
+
setter: "assist config set devlog.name my-repo",
|
|
12591
|
+
note: "override the repo name reported in devlog entries"
|
|
12592
|
+
},
|
|
12593
|
+
{
|
|
12594
|
+
key: "devlog.ignore",
|
|
12595
|
+
setter: 'assist config set devlog.ignore "**/*.lock"',
|
|
12596
|
+
note: "globs excluded when grouping commits"
|
|
12597
|
+
},
|
|
12598
|
+
{
|
|
12599
|
+
key: "devlog.skip",
|
|
12600
|
+
setter: "assist devlog skip 2026-07-10",
|
|
12601
|
+
note: "dates (per repo) omitted from devlog output"
|
|
12602
|
+
}
|
|
12603
|
+
];
|
|
12604
|
+
|
|
12315
12605
|
// src/commands/registerDevlog.ts
|
|
12316
12606
|
function registerDevlog(program2) {
|
|
12317
12607
|
const devlogCommand = program2.command("devlog").description("Development log utilities");
|
|
@@ -12328,6 +12618,7 @@ function registerDevlog(program2) {
|
|
|
12328
12618
|
"Only show repos pushed within N days (default: 30)",
|
|
12329
12619
|
Number.parseInt
|
|
12330
12620
|
).option("--all", "Show all non-archived repos regardless of push date").action(repos);
|
|
12621
|
+
configHelp(devlogCommand, devlogConfigHelp);
|
|
12331
12622
|
}
|
|
12332
12623
|
|
|
12333
12624
|
// src/commands/dotnet/checkBuildLocks.ts
|
|
@@ -12953,6 +13244,13 @@ function registerDotnet(program2) {
|
|
|
12953
13244
|
cmd.command("check-locks").description("Check if build output files are locked by a debugger").action(checkBuildLocksCommand);
|
|
12954
13245
|
cmd.command("deps").description("Show .csproj project dependency tree and solution membership").argument("<csproj>", "Path to a .csproj file").option("--json", "Output as JSON").action(deps);
|
|
12955
13246
|
cmd.command("in-sln").description("Check whether a .csproj is referenced by any .sln file").argument("<csproj>", "Path to a .csproj file").action(inSln);
|
|
13247
|
+
configHelp(cmd, [
|
|
13248
|
+
{
|
|
13249
|
+
key: "dotnet.inspect.suppress",
|
|
13250
|
+
setter: "assist config set dotnet.inspect.suppress CSharpWarnings::CS0168",
|
|
13251
|
+
note: "inspection issue-type IDs suppressed by default"
|
|
13252
|
+
}
|
|
13253
|
+
]);
|
|
12956
13254
|
}
|
|
12957
13255
|
|
|
12958
13256
|
// src/commands/editHook/index.ts
|
|
@@ -13546,12 +13844,12 @@ async function printPendingHandovers() {
|
|
|
13546
13844
|
|
|
13547
13845
|
// src/commands/handover/recallHandover.ts
|
|
13548
13846
|
import { and as and18, desc as desc5, eq as eq36, isNull as isNull3 } from "drizzle-orm";
|
|
13549
|
-
async function recallHandover(orm, origin,
|
|
13847
|
+
async function recallHandover(orm, origin, id) {
|
|
13550
13848
|
const [row] = await orm.select().from(handovers).where(
|
|
13551
13849
|
and18(
|
|
13552
13850
|
eq36(handovers.origin, origin),
|
|
13553
13851
|
isNull3(handovers.recalledAt),
|
|
13554
|
-
...
|
|
13852
|
+
...id === void 0 ? [] : [eq36(handovers.id, id)]
|
|
13555
13853
|
)
|
|
13556
13854
|
).orderBy(desc5(handovers.createdAt), desc5(handovers.id)).limit(1);
|
|
13557
13855
|
if (!row) return void 0;
|
|
@@ -13560,12 +13858,12 @@ async function recallHandover(orm, origin, id2) {
|
|
|
13560
13858
|
}
|
|
13561
13859
|
|
|
13562
13860
|
// src/commands/handover/recallAndPrint.ts
|
|
13563
|
-
async function recallAndPrint(
|
|
13861
|
+
async function recallAndPrint(id) {
|
|
13564
13862
|
const orm = await getDb();
|
|
13565
13863
|
const content = await recallHandover(
|
|
13566
13864
|
orm,
|
|
13567
13865
|
getCurrentOrigin(process.cwd()),
|
|
13568
|
-
|
|
13866
|
+
id
|
|
13569
13867
|
);
|
|
13570
13868
|
if (content !== void 0) console.log(content);
|
|
13571
13869
|
}
|
|
@@ -13595,7 +13893,7 @@ function registerHandover(program2) {
|
|
|
13595
13893
|
cmd.command("recall").argument("[id]", "Id of a specific handover to recall").description(
|
|
13596
13894
|
"Print an unrecalled handover for this repo and mark it recalled (most recent by default, or the given id)"
|
|
13597
13895
|
).action(
|
|
13598
|
-
(
|
|
13896
|
+
(id) => recallAndPrint(id === void 0 ? void 0 : Number(id))
|
|
13599
13897
|
);
|
|
13600
13898
|
cmd.command("load").description(
|
|
13601
13899
|
"SessionStart hook: migrate any disk handovers, then advise how many unrecalled handovers exist for this repo"
|
|
@@ -13770,6 +14068,13 @@ function registerJira(program2) {
|
|
|
13770
14068
|
jiraCommand.command("auth").description("Authenticate with Jira via API token").action(() => jiraAuth());
|
|
13771
14069
|
jiraCommand.command("ac <issue-key>").description("Print acceptance criteria for a Jira issue").action((issueKey) => acceptanceCriteria(issueKey));
|
|
13772
14070
|
jiraCommand.command("view <issue-key>").description("Print the title and description of a Jira issue").action((issueKey) => viewIssue(issueKey));
|
|
14071
|
+
configHelp(jiraCommand, [
|
|
14072
|
+
{
|
|
14073
|
+
key: "jira.acField",
|
|
14074
|
+
setter: "assist config set jira.acField customfield_11937",
|
|
14075
|
+
note: "custom field holding acceptance criteria (default: customfield_11937)"
|
|
14076
|
+
}
|
|
14077
|
+
]);
|
|
13773
14078
|
}
|
|
13774
14079
|
|
|
13775
14080
|
// src/commands/reviewComments.ts
|
|
@@ -13798,7 +14103,7 @@ async function reviewComments(number) {
|
|
|
13798
14103
|
var RESUME_SESSION_FLAG = "Resume an interrupted Claude session (used by the sessions daemon on restart)";
|
|
13799
14104
|
function registerNext(program2) {
|
|
13800
14105
|
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
|
-
(
|
|
14106
|
+
(id, opts) => next({ allowEdits: true, once: opts.once }, id)
|
|
13802
14107
|
);
|
|
13803
14108
|
}
|
|
13804
14109
|
function registerDescriptionLaunch(program2, name, slashCommand, description, alias) {
|
|
@@ -13819,7 +14124,7 @@ function registerReviewComments(program2) {
|
|
|
13819
14124
|
}
|
|
13820
14125
|
function registerRefine(program2) {
|
|
13821
14126
|
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
|
-
(
|
|
14127
|
+
(id, opts) => refine(id, { once: opts.once, resumeSessionId: opts.resumeSession })
|
|
13823
14128
|
);
|
|
13824
14129
|
}
|
|
13825
14130
|
function registerLaunch(program2) {
|
|
@@ -13957,6 +14262,13 @@ function registerMermaid(program2) {
|
|
|
13957
14262
|
).action(
|
|
13958
14263
|
(file, options2) => mermaidExport(file, options2)
|
|
13959
14264
|
);
|
|
14265
|
+
configHelp(cmd, [
|
|
14266
|
+
{
|
|
14267
|
+
key: "mermaid.krokiUrl",
|
|
14268
|
+
setter: "assist config set mermaid.krokiUrl https://kroki.io",
|
|
14269
|
+
note: "Kroki server used to render diagrams (default: https://kroki.io)"
|
|
14270
|
+
}
|
|
14271
|
+
]);
|
|
13960
14272
|
}
|
|
13961
14273
|
|
|
13962
14274
|
// src/commands/netcap/netcap.ts
|
|
@@ -14190,8 +14502,8 @@ function parseRscRows(flight) {
|
|
|
14190
14502
|
}
|
|
14191
14503
|
function makeRscResolver(rows) {
|
|
14192
14504
|
return (ref) => {
|
|
14193
|
-
const [
|
|
14194
|
-
let v = rows[
|
|
14505
|
+
const [id, ...path58] = ref.slice(1).replace(/^[L@]/, "").split(":");
|
|
14506
|
+
let v = rows[id];
|
|
14195
14507
|
for (const key of path58) {
|
|
14196
14508
|
if (v == null || typeof v !== "object") return void 0;
|
|
14197
14509
|
v = v[key];
|
|
@@ -14280,9 +14592,9 @@ function activityPermalink(urn) {
|
|
|
14280
14592
|
// src/commands/netcap/activityUrnDate.ts
|
|
14281
14593
|
function activityUrnDate(urn) {
|
|
14282
14594
|
if (!urn) return void 0;
|
|
14283
|
-
const
|
|
14284
|
-
if (!
|
|
14285
|
-
return new Date(Number(BigInt(
|
|
14595
|
+
const id = urn.split(":").pop();
|
|
14596
|
+
if (!id || !/^\d+$/.test(id)) return void 0;
|
|
14597
|
+
return new Date(Number(BigInt(id) >> 22n)).toISOString();
|
|
14286
14598
|
}
|
|
14287
14599
|
|
|
14288
14600
|
// src/commands/netcap/dedupeLinks.ts
|
|
@@ -14379,7 +14691,7 @@ function findAuthorSlug(flight) {
|
|
|
14379
14691
|
}
|
|
14380
14692
|
function findCommentaryRows(rows) {
|
|
14381
14693
|
return Object.keys(rows).filter(
|
|
14382
|
-
(
|
|
14694
|
+
(id) => /"componentkey":"feed-commentary_/.test(JSON.stringify(rows[id]))
|
|
14383
14695
|
);
|
|
14384
14696
|
}
|
|
14385
14697
|
function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
|
|
@@ -14387,7 +14699,7 @@ function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
|
|
|
14387
14699
|
const resolve18 = makeRscResolver(rows);
|
|
14388
14700
|
const mentionMap = buildMentionMap(rows, resolve18);
|
|
14389
14701
|
const posts = [];
|
|
14390
|
-
for (const
|
|
14702
|
+
for (const id of findCommentaryRows(rows)) {
|
|
14391
14703
|
const raw = {
|
|
14392
14704
|
text: [],
|
|
14393
14705
|
hashtags: [],
|
|
@@ -14395,7 +14707,7 @@ function extractLinkedInPosts(flight, author = findAuthorSlug(flight)) {
|
|
|
14395
14707
|
links: [],
|
|
14396
14708
|
related: []
|
|
14397
14709
|
};
|
|
14398
|
-
walkPostRow(rows[
|
|
14710
|
+
walkPostRow(rows[id], resolve18, raw);
|
|
14399
14711
|
const post = buildPost(raw, mentionMap, author);
|
|
14400
14712
|
if (post) posts.push(post);
|
|
14401
14713
|
}
|
|
@@ -14827,8 +15139,8 @@ function assertThreadCreated(stdout) {
|
|
|
14827
15139
|
} catch {
|
|
14828
15140
|
throw new Error(`GitHub returned an unparseable response: ${stdout}`);
|
|
14829
15141
|
}
|
|
14830
|
-
const
|
|
14831
|
-
if (typeof
|
|
15142
|
+
const id = parsed.data?.addPullRequestReviewThread?.thread?.id;
|
|
15143
|
+
if (typeof id !== "string" || id.length === 0) {
|
|
14832
15144
|
throw new Error(
|
|
14833
15145
|
"GitHub did not create a review thread (no thread id returned); the line is likely outside the PR diff."
|
|
14834
15146
|
);
|
|
@@ -14917,9 +15229,9 @@ function parsePrBody(body) {
|
|
|
14917
15229
|
return sections;
|
|
14918
15230
|
}
|
|
14919
15231
|
function serializePrBody(sections) {
|
|
14920
|
-
return sections.map((
|
|
15232
|
+
return sections.map((section3) => `## ${section3.heading}
|
|
14921
15233
|
|
|
14922
|
-
${
|
|
15234
|
+
${section3.content}`).join("\n\n");
|
|
14923
15235
|
}
|
|
14924
15236
|
|
|
14925
15237
|
// src/commands/prs/editPrBody.ts
|
|
@@ -14967,11 +15279,11 @@ function countSentences(paragraph) {
|
|
|
14967
15279
|
}
|
|
14968
15280
|
function splitParagraphs(body) {
|
|
14969
15281
|
const paragraphs = [];
|
|
14970
|
-
let
|
|
15282
|
+
let section3 = "(intro)";
|
|
14971
15283
|
let lines = [];
|
|
14972
15284
|
const flush = () => {
|
|
14973
15285
|
if (lines.length > 0) {
|
|
14974
|
-
paragraphs.push({ section:
|
|
15286
|
+
paragraphs.push({ section: section3, lines });
|
|
14975
15287
|
lines = [];
|
|
14976
15288
|
}
|
|
14977
15289
|
};
|
|
@@ -14979,7 +15291,7 @@ function splitParagraphs(body) {
|
|
|
14979
15291
|
const heading = line.match(/^#{1,6}\s+(.*)$/);
|
|
14980
15292
|
if (heading) {
|
|
14981
15293
|
flush();
|
|
14982
|
-
|
|
15294
|
+
section3 = heading[1].trim();
|
|
14983
15295
|
} else if (line.trim() === "") {
|
|
14984
15296
|
flush();
|
|
14985
15297
|
} else {
|
|
@@ -15639,8 +15951,22 @@ function wontfix(commentId, reason) {
|
|
|
15639
15951
|
}
|
|
15640
15952
|
}
|
|
15641
15953
|
|
|
15954
|
+
// src/commands/prs/prsConfigHelp.ts
|
|
15955
|
+
var prsConfigHelp = [
|
|
15956
|
+
{
|
|
15957
|
+
key: "prs.slack",
|
|
15958
|
+
setter: 'assist config set prs.slack "#pull-requests"',
|
|
15959
|
+
note: "Slack channel PRs are announced to (used by /prs-slack)"
|
|
15960
|
+
},
|
|
15961
|
+
{
|
|
15962
|
+
key: "prs.required",
|
|
15963
|
+
setter: "assist config set prs.required true",
|
|
15964
|
+
note: "require a branch when running a backlog item"
|
|
15965
|
+
}
|
|
15966
|
+
];
|
|
15967
|
+
|
|
15642
15968
|
// src/commands/registerPrsEdit.ts
|
|
15643
|
-
function
|
|
15969
|
+
function collect4(value, previous) {
|
|
15644
15970
|
return previous.concat([value]);
|
|
15645
15971
|
}
|
|
15646
15972
|
function registerPrsEdit(prsCommand) {
|
|
@@ -15649,13 +15975,13 @@ function registerPrsEdit(prsCommand) {
|
|
|
15649
15975
|
).option("-t, --title <title>", "New title for the pull request").option("--what <what>", "Replace the ## What section").option("--why <why>", "Replace the ## Why section").option("--how <how>", "Replace the ## How section").option(
|
|
15650
15976
|
"--resolves <key>",
|
|
15651
15977
|
"Jira issue key resolved by this PR, appended to ## Why (repeatable)",
|
|
15652
|
-
|
|
15978
|
+
collect4,
|
|
15653
15979
|
[]
|
|
15654
15980
|
).action(edit);
|
|
15655
15981
|
}
|
|
15656
15982
|
|
|
15657
15983
|
// src/commands/registerPrsRaise.ts
|
|
15658
|
-
function
|
|
15984
|
+
function collect5(value, previous) {
|
|
15659
15985
|
return previous.concat([value]);
|
|
15660
15986
|
}
|
|
15661
15987
|
function registerPrsRaise(prsCommand) {
|
|
@@ -15664,20 +15990,20 @@ function registerPrsRaise(prsCommand) {
|
|
|
15664
15990
|
).option("-t, --title <title>", "Title for the pull request").option("--what <what>", "What the change does (## What section)").option("--why <why>", "Why the change is needed (## Why section)").option("--how <how>", "How the change works (optional ## How section)").option(
|
|
15665
15991
|
"--resolves <key>",
|
|
15666
15992
|
"Jira issue key resolved by this PR, appended to ## Why (repeatable)",
|
|
15667
|
-
|
|
15993
|
+
collect5,
|
|
15668
15994
|
[]
|
|
15669
15995
|
).option(
|
|
15670
15996
|
"--force",
|
|
15671
15997
|
"Overwrite the title and body of an existing pull request"
|
|
15672
|
-
).option("-B, --base <branch>", "Branch into which the pull request merges").option("-H, --head <branch>", "Branch that contains the commits").option("-d, --draft", "Mark the pull request as a draft").option("-w, --web", "Open the browser to create the pull request").option("-l, --label <label>", "Add a label (repeatable)",
|
|
15998
|
+
).option("-B, --base <branch>", "Branch into which the pull request merges").option("-H, --head <branch>", "Branch that contains the commits").option("-d, --draft", "Mark the pull request as a draft").option("-w, --web", "Open the browser to create the pull request").option("-l, --label <label>", "Add a label (repeatable)", collect5, []).option(
|
|
15673
15999
|
"-a, --assignee <login>",
|
|
15674
16000
|
"Assign a person by login (repeatable)",
|
|
15675
|
-
|
|
16001
|
+
collect5,
|
|
15676
16002
|
[]
|
|
15677
16003
|
).option(
|
|
15678
16004
|
"-r, --reviewer <handle>",
|
|
15679
16005
|
"Request a review (repeatable)",
|
|
15680
|
-
|
|
16006
|
+
collect5,
|
|
15681
16007
|
[]
|
|
15682
16008
|
).option("-m, --milestone <name>", "Add the pull request to a milestone").action(raise);
|
|
15683
16009
|
}
|
|
@@ -15702,6 +16028,7 @@ function registerPrs(program2) {
|
|
|
15702
16028
|
prsCommand.command("comment <path> <line> <body>").description("Add a line comment to the pending review").action((path58, line, body) => {
|
|
15703
16029
|
comment2(path58, Number.parseInt(line, 10), body);
|
|
15704
16030
|
});
|
|
16031
|
+
configHelp(prsCommand, prsConfigHelp);
|
|
15705
16032
|
}
|
|
15706
16033
|
|
|
15707
16034
|
// src/commands/ravendb/ravendbAuth.ts
|
|
@@ -15797,9 +16124,9 @@ function searchItems(search2) {
|
|
|
15797
16124
|
const lower = search2.toLowerCase();
|
|
15798
16125
|
return items2.filter((i) => i.title.toLowerCase().includes(lower));
|
|
15799
16126
|
}
|
|
15800
|
-
function getItemFields(
|
|
16127
|
+
function getItemFields(itemId2) {
|
|
15801
16128
|
try {
|
|
15802
|
-
const item = JSON.parse(opExec(`item get "${
|
|
16129
|
+
const item = JSON.parse(opExec(`item get "${itemId2}" --format=json`));
|
|
15803
16130
|
return item.fields.filter((f) => f.reference && f.label);
|
|
15804
16131
|
} catch {
|
|
15805
16132
|
console.error(chalk154.red("Failed to get item details from 1Password."));
|
|
@@ -15824,11 +16151,11 @@ async function selectOpSecret(searchTerm) {
|
|
|
15824
16151
|
console.error(chalk155.red(`No items found matching "${search2}".`));
|
|
15825
16152
|
process.exit(1);
|
|
15826
16153
|
}
|
|
15827
|
-
const
|
|
16154
|
+
const itemId2 = await selectOne(
|
|
15828
16155
|
"Select item:",
|
|
15829
16156
|
items2.map((i) => ({ name: `${i.title} (${i.vault.name})`, value: i.id }))
|
|
15830
16157
|
);
|
|
15831
|
-
const fields = getItemFields(
|
|
16158
|
+
const fields = getItemFields(itemId2);
|
|
15832
16159
|
if (fields.length === 0) {
|
|
15833
16160
|
console.error(chalk155.red("No fields with references found on this item."));
|
|
15834
16161
|
process.exit(1);
|
|
@@ -16036,6 +16363,20 @@ async function ravendbCollections(connectionName) {
|
|
|
16036
16363
|
}
|
|
16037
16364
|
}
|
|
16038
16365
|
|
|
16366
|
+
// src/commands/ravendb/ravendbConfigHelp.ts
|
|
16367
|
+
var ravendbConfigHelp = [
|
|
16368
|
+
{
|
|
16369
|
+
key: "ravendb.connections",
|
|
16370
|
+
setter: "assist ravendb auth add",
|
|
16371
|
+
note: "named RavenDB connections (add/list/remove via ravendb auth)"
|
|
16372
|
+
},
|
|
16373
|
+
{
|
|
16374
|
+
key: "ravendb.defaultConnection",
|
|
16375
|
+
setter: "assist ravendb set-connection <name>",
|
|
16376
|
+
note: "connection used when none is passed"
|
|
16377
|
+
}
|
|
16378
|
+
];
|
|
16379
|
+
|
|
16039
16380
|
// src/commands/ravendb/ravendbQuery.ts
|
|
16040
16381
|
import chalk164 from "chalk";
|
|
16041
16382
|
|
|
@@ -16124,6 +16465,7 @@ function registerRavendb(program2) {
|
|
|
16124
16465
|
(connection, collection, options2) => ravendbQuery(connection, collection, options2)
|
|
16125
16466
|
);
|
|
16126
16467
|
cmd.command("collections [connection]").description("List collections in a RavenDB database").action((connection) => ravendbCollections(connection));
|
|
16468
|
+
configHelp(cmd, ravendbConfigHelp);
|
|
16127
16469
|
}
|
|
16128
16470
|
|
|
16129
16471
|
// src/commands/refactor/check/index.ts
|
|
@@ -16369,9 +16711,9 @@ function addImportPreservingSuppressions(sourceFile, structure) {
|
|
|
16369
16711
|
import { SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
16370
16712
|
function collectReferencedNames(sourceFile) {
|
|
16371
16713
|
const names = /* @__PURE__ */ new Set();
|
|
16372
|
-
for (const
|
|
16373
|
-
if (
|
|
16374
|
-
names.add(
|
|
16714
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind3.Identifier)) {
|
|
16715
|
+
if (id.getFirstAncestorByKind(SyntaxKind3.ImportDeclaration)) continue;
|
|
16716
|
+
names.add(id.getText());
|
|
16375
16717
|
}
|
|
16376
16718
|
return names;
|
|
16377
16719
|
}
|
|
@@ -16422,7 +16764,7 @@ function updateImporters(functionName, sourceFile, importers) {
|
|
|
16422
16764
|
|
|
16423
16765
|
// src/commands/refactor/extract/applyExtraction.ts
|
|
16424
16766
|
function isNameReferencedInSource(sourceFile, name) {
|
|
16425
|
-
return sourceFile.getDescendantsOfKind(SyntaxKind4.Identifier).some((
|
|
16767
|
+
return sourceFile.getDescendantsOfKind(SyntaxKind4.Identifier).some((id) => id.getText() === name);
|
|
16426
16768
|
}
|
|
16427
16769
|
async function applyExtraction(functionName, sourceFile, destPath, plan2, project) {
|
|
16428
16770
|
project.createSourceFile(destPath, plan2.destContent, { overwrite: false });
|
|
@@ -16506,8 +16848,8 @@ function collectPrivateFunctions(target, fnMap) {
|
|
|
16506
16848
|
import { SyntaxKind as SyntaxKind6 } from "ts-morph";
|
|
16507
16849
|
function getReferencedIdentifiers(fn) {
|
|
16508
16850
|
const names = /* @__PURE__ */ new Set();
|
|
16509
|
-
for (const
|
|
16510
|
-
names.add(
|
|
16851
|
+
for (const id of fn.getDescendantsOfKind(SyntaxKind6.Identifier)) {
|
|
16852
|
+
names.add(id.getText());
|
|
16511
16853
|
}
|
|
16512
16854
|
return names;
|
|
16513
16855
|
}
|
|
@@ -16675,8 +17017,8 @@ function matchImport(importDecl, neededNames) {
|
|
|
16675
17017
|
function getReferencedNames(nodes) {
|
|
16676
17018
|
const names = /* @__PURE__ */ new Set();
|
|
16677
17019
|
for (const node of nodes) {
|
|
16678
|
-
for (const
|
|
16679
|
-
names.add(
|
|
17020
|
+
for (const id of node.getDescendantsOfKind(SyntaxKind12.Identifier)) {
|
|
17021
|
+
names.add(id.getText());
|
|
16680
17022
|
}
|
|
16681
17023
|
}
|
|
16682
17024
|
return names;
|
|
@@ -16866,9 +17208,9 @@ function isInsideExtractedFunction(node, extracted) {
|
|
|
16866
17208
|
}
|
|
16867
17209
|
function sourceReferencesName(sourceFile, functionName, extractedNames) {
|
|
16868
17210
|
const extracted = new Set(extractedNames);
|
|
16869
|
-
for (const
|
|
16870
|
-
if (
|
|
16871
|
-
const parent =
|
|
17211
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind13.Identifier)) {
|
|
17212
|
+
if (id.getText() !== functionName) continue;
|
|
17213
|
+
const parent = id.getParent();
|
|
16872
17214
|
if (!parent || DECLARATION_KINDS.has(parent.getKind())) continue;
|
|
16873
17215
|
if (!isInsideExtractedFunction(parent, extracted)) return true;
|
|
16874
17216
|
}
|
|
@@ -16908,13 +17250,13 @@ function buildPlan2(functionName, sourceFile, sourcePath, destPath, project) {
|
|
|
16908
17250
|
// src/commands/refactor/extract/displayPlan.ts
|
|
16909
17251
|
import path33 from "path";
|
|
16910
17252
|
import chalk166 from "chalk";
|
|
16911
|
-
function
|
|
17253
|
+
function section2(title) {
|
|
16912
17254
|
return `
|
|
16913
17255
|
${chalk166.cyan(title)}`;
|
|
16914
17256
|
}
|
|
16915
17257
|
function displayImporters(plan2, cwd) {
|
|
16916
17258
|
if (plan2.importersToUpdate.length === 0) return;
|
|
16917
|
-
console.log(
|
|
17259
|
+
console.log(section2("Update importers:"));
|
|
16918
17260
|
for (const imp of plan2.importersToUpdate) {
|
|
16919
17261
|
const rel = path33.relative(cwd, imp.file.getFilePath());
|
|
16920
17262
|
console.log(` ${chalk166.dim(rel)}: \u2192 import from "${imp.relPath}"`);
|
|
@@ -16928,18 +17270,18 @@ function displayPlan(functionName, relDest, plan2, cwd) {
|
|
|
16928
17270
|
console.log(` ${name}`);
|
|
16929
17271
|
}
|
|
16930
17272
|
if (plan2.imports.length > 0) {
|
|
16931
|
-
console.log(
|
|
17273
|
+
console.log(section2("Imports to copy:"));
|
|
16932
17274
|
for (const imp of plan2.imports) {
|
|
16933
17275
|
console.log(` ${formatImportLine(imp)}`);
|
|
16934
17276
|
}
|
|
16935
17277
|
}
|
|
16936
17278
|
if (plan2.exportedDeps.length > 0) {
|
|
16937
|
-
console.log(
|
|
17279
|
+
console.log(section2("New imports from source:"));
|
|
16938
17280
|
console.log(
|
|
16939
17281
|
` import { ${plan2.exportedDeps.join(", ")} } from "${plan2.sourceRelPath}";`
|
|
16940
17282
|
);
|
|
16941
17283
|
}
|
|
16942
|
-
console.log(
|
|
17284
|
+
console.log(section2("Source file changes:"));
|
|
16943
17285
|
console.log(` Remove: ${plan2.extractedNames.join(", ")}`);
|
|
16944
17286
|
if (plan2.sourceNeedsReimport) {
|
|
16945
17287
|
console.log(
|
|
@@ -16948,7 +17290,7 @@ function displayPlan(functionName, relDest, plan2, cwd) {
|
|
|
16948
17290
|
}
|
|
16949
17291
|
displayImporters(plan2, cwd);
|
|
16950
17292
|
if (plan2.barrel) {
|
|
16951
|
-
console.log(
|
|
17293
|
+
console.log(section2("Barrel export:"));
|
|
16952
17294
|
console.log(
|
|
16953
17295
|
` Add: export { ${functionName} } from "${plan2.barrelRelPath}";`
|
|
16954
17296
|
);
|
|
@@ -17346,8 +17688,8 @@ function isDeclaration(identifier) {
|
|
|
17346
17688
|
return parent !== void 0 && declarationKinds.includes(parent.getKind());
|
|
17347
17689
|
}
|
|
17348
17690
|
function findSymbol(sourceFile, symbolName) {
|
|
17349
|
-
for (const
|
|
17350
|
-
if (
|
|
17691
|
+
for (const id of sourceFile.getDescendantsOfKind(SyntaxKind14.Identifier)) {
|
|
17692
|
+
if (id.getText() === symbolName && isDeclaration(id)) return id;
|
|
17351
17693
|
}
|
|
17352
17694
|
return void 0;
|
|
17353
17695
|
}
|
|
@@ -17717,6 +18059,15 @@ async function restructure(pattern2, options2 = {}) {
|
|
|
17717
18059
|
}
|
|
17718
18060
|
}
|
|
17719
18061
|
|
|
18062
|
+
// src/commands/refactor/refactorConfigHelp.ts
|
|
18063
|
+
var refactorConfigHelp = [
|
|
18064
|
+
{
|
|
18065
|
+
key: "restructure.ignore",
|
|
18066
|
+
setter: 'assist config set restructure.ignore "src/generated/**"',
|
|
18067
|
+
note: "globs excluded from restructure coupling analysis"
|
|
18068
|
+
}
|
|
18069
|
+
];
|
|
18070
|
+
|
|
17720
18071
|
// src/commands/registerRefactor.ts
|
|
17721
18072
|
function registerCheck(parent) {
|
|
17722
18073
|
parent.command("check [pattern]").description("Check for files that exceed the maximum line count").option("--modified", "Check only staged and unstaged files").option("--staged", "Check only staged files").option("--unstaged", "Check only unstaged files").option(
|
|
@@ -17733,13 +18084,14 @@ function registerRename(parent) {
|
|
|
17733
18084
|
).option("--apply", "Execute the rename (default: dry-run)").action(renameSymbol);
|
|
17734
18085
|
}
|
|
17735
18086
|
function registerRestructure(parent) {
|
|
17736
|
-
parent.command("restructure [pattern]").description(
|
|
18087
|
+
const restructureCommand = parent.command("restructure [pattern]").description(
|
|
17737
18088
|
"Analyze import graph and restructure tightly-coupled files into nested directories"
|
|
17738
18089
|
).option("--apply", "Execute the restructuring (default: dry-run)").option(
|
|
17739
18090
|
"--max-depth <number>",
|
|
17740
18091
|
"Maximum nesting iterations (default: 3)",
|
|
17741
18092
|
Number.parseInt
|
|
17742
18093
|
).action(restructure);
|
|
18094
|
+
configHelp(restructureCommand, refactorConfigHelp);
|
|
17743
18095
|
}
|
|
17744
18096
|
function registerRefactor(program2) {
|
|
17745
18097
|
const refactorCommand = program2.command("refactor").description("Run refactoring checks for code quality");
|
|
@@ -19554,6 +19906,20 @@ var seqAuth = createConnectionAuth({
|
|
|
19554
19906
|
onFirst: (c) => setDefaultConnection(c.name)
|
|
19555
19907
|
});
|
|
19556
19908
|
|
|
19909
|
+
// src/commands/seq/seqConfigHelp.ts
|
|
19910
|
+
var seqConfigHelp = [
|
|
19911
|
+
{
|
|
19912
|
+
key: "seq.connections",
|
|
19913
|
+
setter: "assist seq auth add",
|
|
19914
|
+
note: "named Seq connections (add/list/remove via seq auth)"
|
|
19915
|
+
},
|
|
19916
|
+
{
|
|
19917
|
+
key: "seq.defaultConnection",
|
|
19918
|
+
setter: "assist seq set-connection <name>",
|
|
19919
|
+
note: "connection used when none is passed"
|
|
19920
|
+
}
|
|
19921
|
+
];
|
|
19922
|
+
|
|
19557
19923
|
// src/commands/seq/seqQuery.ts
|
|
19558
19924
|
import chalk184 from "chalk";
|
|
19559
19925
|
|
|
@@ -19823,17 +20189,18 @@ function registerSeq(program2) {
|
|
|
19823
20189
|
"--to <date>",
|
|
19824
20190
|
"End of query window (UTC date or relative e.g. 5m, 1h, 2d)"
|
|
19825
20191
|
).option("--json", "Output raw JSON").action((filter, options2) => seqQuery(filter, options2));
|
|
20192
|
+
configHelp(cmd, seqConfigHelp);
|
|
19826
20193
|
}
|
|
19827
20194
|
|
|
19828
20195
|
// src/commands/registerSignal.ts
|
|
19829
20196
|
function registerSignal(program2) {
|
|
19830
20197
|
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",
|
|
20198
|
+
signalCommand.command("next").argument("[id]", "Backlog item ID to run directly").description("Write a next signal to chain into assist next").action((id) => {
|
|
20199
|
+
writeSignal("next", id ? { id } : void 0);
|
|
19833
20200
|
console.log("Signal written.");
|
|
19834
20201
|
});
|
|
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",
|
|
20202
|
+
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) => {
|
|
20203
|
+
writeSignal("done", id ? { id } : void 0);
|
|
19837
20204
|
console.log("Signal written.");
|
|
19838
20205
|
});
|
|
19839
20206
|
}
|
|
@@ -19977,6 +20344,20 @@ async function sqlColumns(table, connectionName) {
|
|
|
19977
20344
|
}
|
|
19978
20345
|
}
|
|
19979
20346
|
|
|
20347
|
+
// src/commands/sql/sqlConfigHelp.ts
|
|
20348
|
+
var sqlConfigHelp = [
|
|
20349
|
+
{
|
|
20350
|
+
key: "sql.connections",
|
|
20351
|
+
setter: "assist sql auth add",
|
|
20352
|
+
note: "named MSSQL connections (add/list/remove via sql auth)"
|
|
20353
|
+
},
|
|
20354
|
+
{
|
|
20355
|
+
key: "sql.defaultConnection",
|
|
20356
|
+
setter: "assist sql set-connection <name>",
|
|
20357
|
+
note: "connection used when none is passed"
|
|
20358
|
+
}
|
|
20359
|
+
];
|
|
20360
|
+
|
|
19980
20361
|
// src/commands/sql/sqlMutate.ts
|
|
19981
20362
|
import chalk189 from "chalk";
|
|
19982
20363
|
|
|
@@ -20105,6 +20486,7 @@ function registerSql(program2) {
|
|
|
20105
20486
|
).action(
|
|
20106
20487
|
(table, connection) => sqlColumns(table, connection)
|
|
20107
20488
|
);
|
|
20489
|
+
configHelp(cmd, sqlConfigHelp);
|
|
20108
20490
|
}
|
|
20109
20491
|
|
|
20110
20492
|
// src/commands/transcript/shared.ts
|
|
@@ -20440,9 +20822,29 @@ function move(file, options2) {
|
|
|
20440
20822
|
console.log(`Summary target: ${summaryPath}`);
|
|
20441
20823
|
}
|
|
20442
20824
|
|
|
20825
|
+
// src/commands/transcript/transcriptConfigHelp.ts
|
|
20826
|
+
var transcriptConfigHelp = [
|
|
20827
|
+
{
|
|
20828
|
+
key: "transcript.vttDir",
|
|
20829
|
+
setter: "assist transcript configure",
|
|
20830
|
+
note: "pick-up directory scanned for raw .vtt files"
|
|
20831
|
+
},
|
|
20832
|
+
{
|
|
20833
|
+
key: "transcript.transcriptsDir",
|
|
20834
|
+
setter: "assist transcript configure",
|
|
20835
|
+
note: "directory dated markdown transcripts are written to"
|
|
20836
|
+
},
|
|
20837
|
+
{
|
|
20838
|
+
key: "transcript.summaryDir",
|
|
20839
|
+
setter: "assist transcript configure",
|
|
20840
|
+
note: "directory generated transcript summaries are written to"
|
|
20841
|
+
}
|
|
20842
|
+
];
|
|
20843
|
+
|
|
20443
20844
|
// src/commands/registerTranscript.ts
|
|
20444
20845
|
function registerTranscript(program2) {
|
|
20445
20846
|
const transcriptCommand = program2.command("transcript").description("Meeting transcript utilities");
|
|
20847
|
+
configHelp(transcriptCommand, transcriptConfigHelp);
|
|
20446
20848
|
transcriptCommand.command("configure").description("Configure transcript directories").action(configure);
|
|
20447
20849
|
transcriptCommand.command("list").description("List raw .vtt filenames waiting in the pick-up directory").action(list4);
|
|
20448
20850
|
transcriptCommand.command("move <file>").description(
|
|
@@ -20450,6 +20852,59 @@ function registerTranscript(program2) {
|
|
|
20450
20852
|
).requiredOption("--date <YYYY-MM-DD>", "meeting date").requiredOption("--client <name>", "client name").action(move);
|
|
20451
20853
|
}
|
|
20452
20854
|
|
|
20855
|
+
// src/commands/registerVerifyChecks.ts
|
|
20856
|
+
var checks = [
|
|
20857
|
+
{
|
|
20858
|
+
name: "hardcoded-colors",
|
|
20859
|
+
description: "Check for hardcoded hex colors in src/",
|
|
20860
|
+
action: hardcodedColors
|
|
20861
|
+
},
|
|
20862
|
+
{
|
|
20863
|
+
name: "block-comments",
|
|
20864
|
+
description: "Fail on any comment on a changed line, whether added or edited",
|
|
20865
|
+
action: blockComments
|
|
20866
|
+
},
|
|
20867
|
+
{
|
|
20868
|
+
name: "no-venv",
|
|
20869
|
+
description: "Check that no venv folders exist in the repo",
|
|
20870
|
+
action: noVenv
|
|
20871
|
+
},
|
|
20872
|
+
{
|
|
20873
|
+
name: "forbidden-strings",
|
|
20874
|
+
description: "Check configured JSON files for values matching forbiddenStrings rules",
|
|
20875
|
+
action: forbiddenStrings
|
|
20876
|
+
},
|
|
20877
|
+
{
|
|
20878
|
+
name: "config-keys",
|
|
20879
|
+
description: "Check every assistConfigSchema key is surfaced in some command's --help via configHelp",
|
|
20880
|
+
action: configKeys
|
|
20881
|
+
}
|
|
20882
|
+
];
|
|
20883
|
+
function registerVerifyChecks(verifyCommand) {
|
|
20884
|
+
for (const check2 of checks) {
|
|
20885
|
+
verifyCommand.command(check2.name).description(check2.description).action(check2.action);
|
|
20886
|
+
}
|
|
20887
|
+
}
|
|
20888
|
+
|
|
20889
|
+
// src/commands/verify/verifyConfigHelp.ts
|
|
20890
|
+
var verifyConfigHelp = [
|
|
20891
|
+
{
|
|
20892
|
+
key: "blockComments.ignore",
|
|
20893
|
+
setter: 'assist config set blockComments.ignore "**/*.test.ts"',
|
|
20894
|
+
note: "globs exempt from the block-comments check"
|
|
20895
|
+
},
|
|
20896
|
+
{
|
|
20897
|
+
key: "hardcodedColors.ignore",
|
|
20898
|
+
setter: 'assist config set hardcodedColors.ignore "**/theme.ts"',
|
|
20899
|
+
note: "globs exempt from the hardcoded-colors check"
|
|
20900
|
+
},
|
|
20901
|
+
{
|
|
20902
|
+
key: "forbiddenStrings",
|
|
20903
|
+
setter: 'assist config set forbiddenStrings.0.disallowed "secret"',
|
|
20904
|
+
note: "per-file value rules enforced by the forbidden-strings check"
|
|
20905
|
+
}
|
|
20906
|
+
];
|
|
20907
|
+
|
|
20453
20908
|
// src/commands/registerVerify.ts
|
|
20454
20909
|
function runScope(scope, options2) {
|
|
20455
20910
|
if (scope && scope !== "all") {
|
|
@@ -20471,14 +20926,8 @@ function registerVerify(program2) {
|
|
|
20471
20926
|
"--package-json",
|
|
20472
20927
|
"Write scripts to package.json instead of assist.yml"
|
|
20473
20928
|
).action(init3);
|
|
20474
|
-
verifyCommand
|
|
20475
|
-
verifyCommand
|
|
20476
|
-
"Fail on any comment on a changed line, whether added or edited"
|
|
20477
|
-
).action(blockComments);
|
|
20478
|
-
verifyCommand.command("no-venv").description("Check that no venv folders exist in the repo").action(noVenv);
|
|
20479
|
-
verifyCommand.command("forbidden-strings").description(
|
|
20480
|
-
"Check configured JSON files for values matching forbiddenStrings rules"
|
|
20481
|
-
).action(forbiddenStrings);
|
|
20929
|
+
registerVerifyChecks(verifyCommand);
|
|
20930
|
+
configHelp(verifyCommand, verifyConfigHelp);
|
|
20482
20931
|
}
|
|
20483
20932
|
|
|
20484
20933
|
// src/commands/voice/devices.ts
|
|
@@ -20732,6 +21181,50 @@ function stop2() {
|
|
|
20732
21181
|
console.log("Voice daemon stopped");
|
|
20733
21182
|
}
|
|
20734
21183
|
|
|
21184
|
+
// src/commands/voice/voiceConfigHelp.ts
|
|
21185
|
+
var voiceConfigHelp = [
|
|
21186
|
+
{
|
|
21187
|
+
key: "voice.wakeWords",
|
|
21188
|
+
setter: 'assist config set voice.wakeWords "computer"',
|
|
21189
|
+
note: "wake words that activate the daemon (default: computer)"
|
|
21190
|
+
},
|
|
21191
|
+
{
|
|
21192
|
+
key: "voice.mic",
|
|
21193
|
+
setter: "assist config set voice.mic <device>",
|
|
21194
|
+
note: "audio input device (defaults to the system default)"
|
|
21195
|
+
},
|
|
21196
|
+
{
|
|
21197
|
+
key: "voice.cwd",
|
|
21198
|
+
setter: "assist config set voice.cwd <path>",
|
|
21199
|
+
note: "working directory commands are dispatched from"
|
|
21200
|
+
},
|
|
21201
|
+
{
|
|
21202
|
+
key: "voice.modelsDir",
|
|
21203
|
+
setter: "assist config set voice.modelsDir ~/.assist/voice/models",
|
|
21204
|
+
note: "directory VAD/STT models download to"
|
|
21205
|
+
},
|
|
21206
|
+
{
|
|
21207
|
+
key: "voice.lockDir",
|
|
21208
|
+
setter: "assist config set voice.lockDir <path>",
|
|
21209
|
+
note: "directory holding the daemon lock file"
|
|
21210
|
+
},
|
|
21211
|
+
{
|
|
21212
|
+
key: "voice.submitWindows",
|
|
21213
|
+
setter: 'assist config set voice.submitWindows "Code"',
|
|
21214
|
+
note: "window titles where transcriptions auto-submit"
|
|
21215
|
+
},
|
|
21216
|
+
{
|
|
21217
|
+
key: "voice.models.vad",
|
|
21218
|
+
setter: "assist config set voice.models.vad <path>",
|
|
21219
|
+
note: "override the voice-activity-detection model path"
|
|
21220
|
+
},
|
|
21221
|
+
{
|
|
21222
|
+
key: "voice.models.smartTurn",
|
|
21223
|
+
setter: "assist config set voice.models.smartTurn <path>",
|
|
21224
|
+
note: "override the smart-turn model path"
|
|
21225
|
+
}
|
|
21226
|
+
];
|
|
21227
|
+
|
|
20735
21228
|
// src/commands/registerVoice.ts
|
|
20736
21229
|
function registerVoice(program2) {
|
|
20737
21230
|
const voiceCommand = program2.command("voice").description("Voice interaction daemon for hands-free Claude commands");
|
|
@@ -20741,6 +21234,7 @@ function registerVoice(program2) {
|
|
|
20741
21234
|
voiceCommand.command("status").description("Check voice daemon status").action(status);
|
|
20742
21235
|
voiceCommand.command("devices").description("List available audio input devices").action(devices);
|
|
20743
21236
|
voiceCommand.command("logs").description("Tail voice daemon logs").option("-n, --lines <count>", "Number of lines to show", "20").action((options2) => logs(options2));
|
|
21237
|
+
configHelp(voiceCommand, voiceConfigHelp);
|
|
20744
21238
|
}
|
|
20745
21239
|
|
|
20746
21240
|
// src/commands/roam/auth.ts
|
|
@@ -20971,8 +21465,82 @@ function registerRoam(program2) {
|
|
|
20971
21465
|
roamCommand.command("stop-claude-code-icon").description("Signal Claude Code session stop to Roam local API").action(stopClaudeCodeIcon);
|
|
20972
21466
|
roamCommand.command("show-codex-icon").description("Forward Codex hook activity to Roam local API").action(showCodexIcon);
|
|
20973
21467
|
roamCommand.command("stop-codex-icon").description("Signal Codex session stop to Roam local API").action(stopCodexIcon);
|
|
21468
|
+
configHelp(roamCommand, [
|
|
21469
|
+
{
|
|
21470
|
+
key: "roam.clientId",
|
|
21471
|
+
setter: "assist roam auth",
|
|
21472
|
+
note: "OAuth client ID (written by the auth flow)"
|
|
21473
|
+
},
|
|
21474
|
+
{
|
|
21475
|
+
key: "roam.clientSecret",
|
|
21476
|
+
setter: "assist roam auth",
|
|
21477
|
+
note: "OAuth client secret (written by the auth flow)"
|
|
21478
|
+
},
|
|
21479
|
+
{
|
|
21480
|
+
key: "roam.accessToken",
|
|
21481
|
+
setter: "assist roam auth",
|
|
21482
|
+
note: "OAuth access token (managed by the auth flow)"
|
|
21483
|
+
},
|
|
21484
|
+
{
|
|
21485
|
+
key: "roam.refreshToken",
|
|
21486
|
+
setter: "assist roam auth",
|
|
21487
|
+
note: "OAuth refresh token (managed by the auth flow)"
|
|
21488
|
+
},
|
|
21489
|
+
{
|
|
21490
|
+
key: "roam.tokenExpiresAt",
|
|
21491
|
+
setter: "assist roam auth",
|
|
21492
|
+
note: "access-token expiry epoch (managed by the auth flow)"
|
|
21493
|
+
}
|
|
21494
|
+
]);
|
|
20974
21495
|
}
|
|
20975
21496
|
|
|
21497
|
+
// src/commands/rootConfigHelp.ts
|
|
21498
|
+
var rootConfigHelp = {
|
|
21499
|
+
sync: [
|
|
21500
|
+
{
|
|
21501
|
+
key: "sync.autoConfirm",
|
|
21502
|
+
setter: "assist config set sync.autoConfirm true",
|
|
21503
|
+
note: "skip the settings.json overwrite prompt"
|
|
21504
|
+
}
|
|
21505
|
+
],
|
|
21506
|
+
commit: [
|
|
21507
|
+
{
|
|
21508
|
+
key: "commit.conventional",
|
|
21509
|
+
setter: "assist config set commit.conventional true",
|
|
21510
|
+
note: "enforce conventional commit message format"
|
|
21511
|
+
},
|
|
21512
|
+
{
|
|
21513
|
+
key: "commit.pull",
|
|
21514
|
+
setter: "assist config set commit.pull true",
|
|
21515
|
+
note: "pull before committing"
|
|
21516
|
+
},
|
|
21517
|
+
{
|
|
21518
|
+
key: "commit.push",
|
|
21519
|
+
setter: "assist config set commit.push true",
|
|
21520
|
+
note: "push after committing"
|
|
21521
|
+
},
|
|
21522
|
+
{
|
|
21523
|
+
key: "commit.expectedBranch",
|
|
21524
|
+
setter: "assist config set commit.expectedBranch main",
|
|
21525
|
+
note: "warn when committing off this branch"
|
|
21526
|
+
}
|
|
21527
|
+
],
|
|
21528
|
+
notify: [
|
|
21529
|
+
{
|
|
21530
|
+
key: "notify.enabled",
|
|
21531
|
+
setter: "assist config set notify.enabled false",
|
|
21532
|
+
note: "enable or disable desktop notifications (default: enabled)"
|
|
21533
|
+
}
|
|
21534
|
+
],
|
|
21535
|
+
screenshot: [
|
|
21536
|
+
{
|
|
21537
|
+
key: "screenshot.outputDir",
|
|
21538
|
+
setter: "assist config set screenshot.outputDir ./screenshots",
|
|
21539
|
+
note: "directory screenshots are written to (default: ./screenshots)"
|
|
21540
|
+
}
|
|
21541
|
+
]
|
|
21542
|
+
};
|
|
21543
|
+
|
|
20976
21544
|
// src/commands/run/index.ts
|
|
20977
21545
|
import { resolve as resolve14 } from "path";
|
|
20978
21546
|
|
|
@@ -21137,30 +21705,32 @@ function execRunConfig(config, args) {
|
|
|
21137
21705
|
config.quiet
|
|
21138
21706
|
);
|
|
21139
21707
|
}
|
|
21140
|
-
function parseBacklogRunOptions(
|
|
21708
|
+
function parseBacklogRunOptions(id, args) {
|
|
21141
21709
|
let allowEdits = true;
|
|
21142
21710
|
for (const arg of args) {
|
|
21143
21711
|
if (arg === "--write" || arg === "-w") allowEdits = true;
|
|
21144
21712
|
else if (arg === "--no-write") allowEdits = false;
|
|
21145
21713
|
else {
|
|
21146
|
-
console.error(`error: unexpected argument '${arg}' for 'run ${
|
|
21714
|
+
console.error(`error: unexpected argument '${arg}' for 'run ${id}'`);
|
|
21147
21715
|
process.exit(1);
|
|
21148
21716
|
}
|
|
21149
21717
|
}
|
|
21150
21718
|
return { allowEdits };
|
|
21151
21719
|
}
|
|
21720
|
+
async function tryBacklogRunAlias(name, args) {
|
|
21721
|
+
if (!/^a?\d+$/.test(name)) return false;
|
|
21722
|
+
if (lookupRunConfig(name).kind !== "not-found") return false;
|
|
21723
|
+
pullIfConfigured();
|
|
21724
|
+
await run2(name, parseBacklogRunOptions(name, args));
|
|
21725
|
+
return true;
|
|
21726
|
+
}
|
|
21152
21727
|
async function run3(name, args) {
|
|
21153
21728
|
if (!name) {
|
|
21154
21729
|
console.error("error: missing required argument 'name'");
|
|
21155
21730
|
console.error(formatConfiguredCommands());
|
|
21156
21731
|
process.exit(1);
|
|
21157
21732
|
}
|
|
21158
|
-
if (
|
|
21159
|
-
const options2 = parseBacklogRunOptions(name, args);
|
|
21160
|
-
pullIfConfigured();
|
|
21161
|
-
await run2(name, options2);
|
|
21162
|
-
return;
|
|
21163
|
-
}
|
|
21733
|
+
if (await tryBacklogRunAlias(name, args)) return;
|
|
21164
21734
|
execRunConfig(findRunConfig(name), args);
|
|
21165
21735
|
}
|
|
21166
21736
|
|
|
@@ -21327,12 +21897,8 @@ function remove() {
|
|
|
21327
21897
|
console.log(`Removed run configuration: ${name}`);
|
|
21328
21898
|
}
|
|
21329
21899
|
|
|
21330
|
-
// src/commands/run/
|
|
21331
|
-
function
|
|
21332
|
-
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
|
-
"after",
|
|
21334
|
-
"\nA purely numeric name with no matching command is an alias for\n'assist backlog run <id>' (forwards --write/--no-write/-w)."
|
|
21335
|
-
).addHelpText("after", () => formatConfiguredCommands()).action((name, args) => run3(name, args));
|
|
21900
|
+
// src/commands/run/registerRunSubcommands.ts
|
|
21901
|
+
function registerRunSubcommands(runCommand) {
|
|
21336
21902
|
runCommand.command("list").description("List configured run commands").option("-v, --verbose", "Show full command details").action((opts) => listRunConfigs(!!opts.verbose));
|
|
21337
21903
|
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(
|
|
21338
21904
|
"--cwd <dir>",
|
|
@@ -21345,6 +21911,25 @@ function registerRun(program2) {
|
|
|
21345
21911
|
runCommand.command("remove").description("Remove a run configuration from assist.yml").argument("<name>", "Name of the run configuration to remove").action(() => remove());
|
|
21346
21912
|
}
|
|
21347
21913
|
|
|
21914
|
+
// src/commands/run/runConfigHelp.ts
|
|
21915
|
+
var runConfigHelp = [
|
|
21916
|
+
{
|
|
21917
|
+
key: "run",
|
|
21918
|
+
setter: "assist run add <name> <command>",
|
|
21919
|
+
note: "configured commands runnable via assist run <name>"
|
|
21920
|
+
}
|
|
21921
|
+
];
|
|
21922
|
+
|
|
21923
|
+
// src/commands/run/registerRun.ts
|
|
21924
|
+
function registerRun(program2) {
|
|
21925
|
+
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(
|
|
21926
|
+
"after",
|
|
21927
|
+
"\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)."
|
|
21928
|
+
).addHelpText("after", () => formatConfiguredCommands()).action((name, args) => run3(name, args));
|
|
21929
|
+
configHelp(runCommand, runConfigHelp);
|
|
21930
|
+
registerRunSubcommands(runCommand);
|
|
21931
|
+
}
|
|
21932
|
+
|
|
21348
21933
|
// src/commands/screenshot/index.ts
|
|
21349
21934
|
import { execSync as execSync54 } from "child_process";
|
|
21350
21935
|
import { existsSync as existsSync51, mkdirSync as mkdirSync22, unlinkSync as unlinkSync19, writeFileSync as writeFileSync39 } from "fs";
|
|
@@ -21815,7 +22400,7 @@ function broadcast(clients, msg) {
|
|
|
21815
22400
|
|
|
21816
22401
|
// src/commands/sessions/daemon/toSessionInfo.ts
|
|
21817
22402
|
function toSessionInfo({
|
|
21818
|
-
id
|
|
22403
|
+
id,
|
|
21819
22404
|
name,
|
|
21820
22405
|
title,
|
|
21821
22406
|
subtitle,
|
|
@@ -21835,7 +22420,7 @@ function toSessionInfo({
|
|
|
21835
22420
|
autoAdvance
|
|
21836
22421
|
}) {
|
|
21837
22422
|
return {
|
|
21838
|
-
id
|
|
22423
|
+
id,
|
|
21839
22424
|
name,
|
|
21840
22425
|
title,
|
|
21841
22426
|
subtitle,
|
|
@@ -21978,10 +22563,10 @@ function shellEscape(s) {
|
|
|
21978
22563
|
}
|
|
21979
22564
|
|
|
21980
22565
|
// src/commands/sessions/daemon/createAssistSession.ts
|
|
21981
|
-
function createAssistSession(
|
|
22566
|
+
function createAssistSession(id, assistArgs, cwd, meta) {
|
|
21982
22567
|
const startedAt = Date.now();
|
|
21983
22568
|
return {
|
|
21984
|
-
id
|
|
22569
|
+
id,
|
|
21985
22570
|
name: `assist ${assistArgs.join(" ")}`,
|
|
21986
22571
|
title: meta?.title,
|
|
21987
22572
|
subtitle: meta?.subtitle,
|
|
@@ -21990,7 +22575,7 @@ function createAssistSession(id2, assistArgs, cwd, meta) {
|
|
|
21990
22575
|
startedAt,
|
|
21991
22576
|
runningMs: 0,
|
|
21992
22577
|
runningSince: startedAt,
|
|
21993
|
-
pty: spawnPty(["assist", ...assistArgs], cwd,
|
|
22578
|
+
pty: spawnPty(["assist", ...assistArgs], cwd, id),
|
|
21994
22579
|
scrollback: "",
|
|
21995
22580
|
assistArgs,
|
|
21996
22581
|
cwd
|
|
@@ -21998,21 +22583,21 @@ function createAssistSession(id2, assistArgs, cwd, meta) {
|
|
|
21998
22583
|
}
|
|
21999
22584
|
|
|
22000
22585
|
// src/commands/sessions/daemon/dismissSession.ts
|
|
22001
|
-
function dismissSession(sessions,
|
|
22002
|
-
const s = sessions.get(
|
|
22586
|
+
function dismissSession(sessions, id) {
|
|
22587
|
+
const s = sessions.get(id);
|
|
22003
22588
|
if (!s) return false;
|
|
22004
22589
|
if (s.status !== "done") s.pty?.kill();
|
|
22005
22590
|
s.activityWatcher?.close();
|
|
22006
22591
|
removeActivity(s.id);
|
|
22007
22592
|
if (s.activity?.itemId != null) releaseLock(s.activity.itemId);
|
|
22008
|
-
sessions.delete(
|
|
22009
|
-
daemonLog(`session ${
|
|
22593
|
+
sessions.delete(id);
|
|
22594
|
+
daemonLog(`session ${id} dismissed (${s.name})`);
|
|
22010
22595
|
return true;
|
|
22011
22596
|
}
|
|
22012
22597
|
function drainSessions(sessions, onDrained) {
|
|
22013
22598
|
const names = [...sessions.values()].map((s) => s.name);
|
|
22014
22599
|
const ids = [...sessions.keys()];
|
|
22015
|
-
for (const
|
|
22600
|
+
for (const id of ids) dismissSession(sessions, id);
|
|
22016
22601
|
onDrained();
|
|
22017
22602
|
daemonLog(
|
|
22018
22603
|
ids.length > 0 ? `drained ${ids.length} session(s): ${names.join(", ")}` : "drained 0 sessions"
|
|
@@ -22043,10 +22628,10 @@ function spawnRun(opts) {
|
|
|
22043
22628
|
}
|
|
22044
22629
|
|
|
22045
22630
|
// src/commands/sessions/daemon/createSession.ts
|
|
22046
|
-
function sessionBase(
|
|
22631
|
+
function sessionBase(id, status2) {
|
|
22047
22632
|
const startedAt = Date.now();
|
|
22048
22633
|
return {
|
|
22049
|
-
id
|
|
22634
|
+
id,
|
|
22050
22635
|
status: status2,
|
|
22051
22636
|
startedAt,
|
|
22052
22637
|
runningMs: 0,
|
|
@@ -22056,21 +22641,21 @@ function sessionBase(id2, status2) {
|
|
|
22056
22641
|
scrollback: ""
|
|
22057
22642
|
};
|
|
22058
22643
|
}
|
|
22059
|
-
function createSession(
|
|
22644
|
+
function createSession(id, prompt, cwd) {
|
|
22060
22645
|
const claudeSessionId = randomUUID4();
|
|
22061
22646
|
return {
|
|
22062
|
-
...sessionBase(
|
|
22063
|
-
name: prompt?.slice(0, 40) || `Session ${
|
|
22647
|
+
...sessionBase(id, prompt ? "running" : "waiting"),
|
|
22648
|
+
name: prompt?.slice(0, 40) || `Session ${id}`,
|
|
22064
22649
|
commandType: "claude",
|
|
22065
|
-
pty: spawnClaude2({ prompt, cwd, sessionId:
|
|
22650
|
+
pty: spawnClaude2({ prompt, cwd, sessionId: id, claudeSessionId }),
|
|
22066
22651
|
cwd,
|
|
22067
22652
|
claudeSessionId,
|
|
22068
22653
|
initialPrompt: prompt
|
|
22069
22654
|
};
|
|
22070
22655
|
}
|
|
22071
|
-
function createRunSession(
|
|
22656
|
+
function createRunSession(id, runName, runArgs, cwd) {
|
|
22072
22657
|
return {
|
|
22073
|
-
...sessionBase(
|
|
22658
|
+
...sessionBase(id, "running"),
|
|
22074
22659
|
name: `run: ${runName}`,
|
|
22075
22660
|
commandType: "run",
|
|
22076
22661
|
pty: spawnRun({ name: runName, args: runArgs, cwd }),
|
|
@@ -22118,9 +22703,9 @@ function logSpawnedSession(session) {
|
|
|
22118
22703
|
|
|
22119
22704
|
// src/shared/db/recordPhaseActiveMs.ts
|
|
22120
22705
|
import { sql as sql9 } from "drizzle-orm";
|
|
22121
|
-
async function recordPhaseActiveMs(db,
|
|
22706
|
+
async function recordPhaseActiveMs(db, itemId2, phaseIdx, activeMs) {
|
|
22122
22707
|
if (activeMs <= 0) return;
|
|
22123
|
-
await db.insert(phaseUsage).values({ itemId, phaseIdx, activeMs }).onConflictDoUpdate({
|
|
22708
|
+
await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, activeMs }).onConflictDoUpdate({
|
|
22124
22709
|
target: [phaseUsage.itemId, phaseUsage.phaseIdx],
|
|
22125
22710
|
set: {
|
|
22126
22711
|
activeMs: sql9`${phaseUsage.activeMs} + excluded.active_ms`
|
|
@@ -22129,10 +22714,10 @@ async function recordPhaseActiveMs(db, itemId, phaseIdx, activeMs) {
|
|
|
22129
22714
|
}
|
|
22130
22715
|
|
|
22131
22716
|
// src/commands/sessions/daemon/persistPhaseActiveMs.ts
|
|
22132
|
-
async function persistPhaseActiveMs(
|
|
22717
|
+
async function persistPhaseActiveMs(itemId2, phaseIdx, activeMs) {
|
|
22133
22718
|
try {
|
|
22134
22719
|
if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url) return;
|
|
22135
|
-
await recordPhaseActiveMs(await getDb(),
|
|
22720
|
+
await recordPhaseActiveMs(await getDb(), itemId2, phaseIdx, activeMs);
|
|
22136
22721
|
} catch (error) {
|
|
22137
22722
|
daemonLog(`phase-active-ms persist failed: ${String(error)}`);
|
|
22138
22723
|
}
|
|
@@ -22189,13 +22774,13 @@ function shouldAutoRun(session) {
|
|
|
22189
22774
|
run: false,
|
|
22190
22775
|
reason: `command "${cmd ?? "(none)"}" is not draft/bug/refine`
|
|
22191
22776
|
};
|
|
22192
|
-
const
|
|
22193
|
-
if (
|
|
22777
|
+
const itemId2 = session.activity?.itemId;
|
|
22778
|
+
if (itemId2 == null)
|
|
22194
22779
|
return {
|
|
22195
22780
|
run: false,
|
|
22196
22781
|
reason: "no itemId in activity (draft did not create an item)"
|
|
22197
22782
|
};
|
|
22198
|
-
return { run: true, itemId };
|
|
22783
|
+
return { run: true, itemId: itemId2 };
|
|
22199
22784
|
}
|
|
22200
22785
|
|
|
22201
22786
|
// src/commands/sessions/daemon/watchEscInterrupt.ts
|
|
@@ -22267,8 +22852,8 @@ function makeStatusChangeHandler(dismiss, notify2, reuseForRun) {
|
|
|
22267
22852
|
|
|
22268
22853
|
// src/shared/db/recordPhaseTokens.ts
|
|
22269
22854
|
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({
|
|
22855
|
+
async function recordPhaseTokens(db, itemId2, phaseIdx, totalIn, totalOut) {
|
|
22856
|
+
await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, lastTotalIn: totalIn, lastTotalOut: totalOut }).onConflictDoUpdate({
|
|
22272
22857
|
target: [phaseUsage.itemId, phaseUsage.phaseIdx],
|
|
22273
22858
|
set: {
|
|
22274
22859
|
tokensDown: sql10`${phaseUsage.tokensDown} + CASE WHEN ${phaseUsage.lastTotalIn} IS NULL THEN 0 ELSE GREATEST(excluded.last_total_in - ${phaseUsage.lastTotalIn}, 0) END`,
|
|
@@ -22280,10 +22865,10 @@ async function recordPhaseTokens(db, itemId, phaseIdx, totalIn, totalOut) {
|
|
|
22280
22865
|
}
|
|
22281
22866
|
|
|
22282
22867
|
// src/commands/sessions/daemon/persistPhaseTokens.ts
|
|
22283
|
-
async function persistPhaseTokens(
|
|
22868
|
+
async function persistPhaseTokens(itemId2, phaseIdx, totalIn, totalOut) {
|
|
22284
22869
|
try {
|
|
22285
22870
|
if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url) return;
|
|
22286
|
-
await recordPhaseTokens(await getDb(),
|
|
22871
|
+
await recordPhaseTokens(await getDb(), itemId2, phaseIdx, totalIn, totalOut);
|
|
22287
22872
|
} catch (error) {
|
|
22288
22873
|
daemonLog(`phase-tokens persist failed: ${String(error)}`);
|
|
22289
22874
|
}
|
|
@@ -22317,19 +22902,19 @@ function applyReviewPause(session, activity2) {
|
|
|
22317
22902
|
|
|
22318
22903
|
// src/shared/db/getPhaseActiveMs.ts
|
|
22319
22904
|
import { and as and19, eq as eq37 } from "drizzle-orm";
|
|
22320
|
-
async function getPhaseActiveMs(db,
|
|
22905
|
+
async function getPhaseActiveMs(db, itemId2, phaseIdx) {
|
|
22321
22906
|
const [row] = await db.select({ activeMs: phaseUsage.activeMs }).from(phaseUsage).where(
|
|
22322
|
-
and19(eq37(phaseUsage.itemId,
|
|
22907
|
+
and19(eq37(phaseUsage.itemId, itemId2), eq37(phaseUsage.phaseIdx, phaseIdx))
|
|
22323
22908
|
);
|
|
22324
22909
|
return row?.activeMs ?? 0;
|
|
22325
22910
|
}
|
|
22326
22911
|
|
|
22327
22912
|
// src/commands/sessions/daemon/loadPhaseActiveMs.ts
|
|
22328
|
-
async function loadPhaseActiveMs(
|
|
22913
|
+
async function loadPhaseActiveMs(itemId2, phaseIdx) {
|
|
22329
22914
|
try {
|
|
22330
22915
|
if (!process.env.ASSIST_DATABASE_URL && !loadConfig().database.url)
|
|
22331
22916
|
return null;
|
|
22332
|
-
return await getPhaseActiveMs(await getDb(),
|
|
22917
|
+
return await getPhaseActiveMs(await getDb(), itemId2, phaseIdx);
|
|
22333
22918
|
} catch (error) {
|
|
22334
22919
|
daemonLog(`phase-active-ms load failed: ${String(error)}`);
|
|
22335
22920
|
return null;
|
|
@@ -22545,9 +23130,9 @@ function killPtyTree(pty2) {
|
|
|
22545
23130
|
}
|
|
22546
23131
|
|
|
22547
23132
|
// src/commands/sessions/daemon/restoreBase.ts
|
|
22548
|
-
function restoreBase(
|
|
23133
|
+
function restoreBase(id, persisted) {
|
|
22549
23134
|
return {
|
|
22550
|
-
id
|
|
23135
|
+
id,
|
|
22551
23136
|
name: persisted.name,
|
|
22552
23137
|
title: persisted.title,
|
|
22553
23138
|
subtitle: persisted.subtitle,
|
|
@@ -22560,9 +23145,9 @@ function restoreBase(id2, persisted) {
|
|
|
22560
23145
|
}
|
|
22561
23146
|
|
|
22562
23147
|
// src/commands/sessions/daemon/errorSession.ts
|
|
22563
|
-
function errorSession(
|
|
23148
|
+
function errorSession(id, persisted, error) {
|
|
22564
23149
|
return {
|
|
22565
|
-
...restoreBase(
|
|
23150
|
+
...restoreBase(id, persisted),
|
|
22566
23151
|
status: "error",
|
|
22567
23152
|
startedAt: persisted.startedAt,
|
|
22568
23153
|
runningMs: persisted.runningMs ?? 0,
|
|
@@ -22598,28 +23183,28 @@ function restoredSession(base, persisted, pty2, status2) {
|
|
|
22598
23183
|
}
|
|
22599
23184
|
|
|
22600
23185
|
// src/commands/sessions/daemon/restoreInteractiveSession.ts
|
|
22601
|
-
function restoreInteractiveSession(
|
|
23186
|
+
function restoreInteractiveSession(id, persisted, base) {
|
|
22602
23187
|
if (persisted.commandType !== "run" && persisted.claudeSessionId) {
|
|
22603
|
-
return resumeViaClaude(
|
|
23188
|
+
return resumeViaClaude(id, persisted, base);
|
|
22604
23189
|
}
|
|
22605
23190
|
if (persisted.commandType === "claude") {
|
|
22606
|
-
return unrecoverableClaude(
|
|
23191
|
+
return unrecoverableClaude(id, persisted);
|
|
22607
23192
|
}
|
|
22608
23193
|
return notRestoredStub(base, persisted);
|
|
22609
23194
|
}
|
|
22610
|
-
function resumeViaClaude(
|
|
23195
|
+
function resumeViaClaude(id, persisted, base) {
|
|
22611
23196
|
const idle = persisted.status === "waiting";
|
|
22612
23197
|
const pty2 = spawnClaude2({
|
|
22613
23198
|
resumeSessionId: persisted.claudeSessionId,
|
|
22614
23199
|
prompt: idle ? void 0 : buildResumePrompt(),
|
|
22615
23200
|
cwd: persisted.cwd,
|
|
22616
|
-
sessionId:
|
|
23201
|
+
sessionId: id
|
|
22617
23202
|
});
|
|
22618
23203
|
return idle ? waitingSession(base, persisted, pty2) : runningSession(base, persisted, pty2);
|
|
22619
23204
|
}
|
|
22620
|
-
function unrecoverableClaude(
|
|
23205
|
+
function unrecoverableClaude(id, persisted) {
|
|
22621
23206
|
return errorSession(
|
|
22622
|
-
|
|
23207
|
+
id,
|
|
22623
23208
|
persisted,
|
|
22624
23209
|
"no claude session id was recorded before the daemon stopped, so the conversation cannot be resumed"
|
|
22625
23210
|
);
|
|
@@ -22639,9 +23224,9 @@ function notRestoredStub(base, persisted) {
|
|
|
22639
23224
|
}
|
|
22640
23225
|
|
|
22641
23226
|
// src/commands/sessions/daemon/updatedSession.ts
|
|
22642
|
-
function updatedSession(
|
|
23227
|
+
function updatedSession(id, persisted) {
|
|
22643
23228
|
return {
|
|
22644
|
-
...restoreBase(
|
|
23229
|
+
...restoreBase(id, persisted),
|
|
22645
23230
|
status: "done",
|
|
22646
23231
|
startedAt: persisted.startedAt,
|
|
22647
23232
|
runningMs: persisted.runningMs ?? 0,
|
|
@@ -22654,20 +23239,20 @@ function isUpdate(persisted) {
|
|
|
22654
23239
|
}
|
|
22655
23240
|
|
|
22656
23241
|
// src/commands/sessions/daemon/restoreSession.ts
|
|
22657
|
-
function restoreSession(
|
|
22658
|
-
const base = restoreBase(
|
|
22659
|
-
if (isUpdate(persisted)) return updatedSession(
|
|
23242
|
+
function restoreSession(id, persisted) {
|
|
23243
|
+
const base = restoreBase(id, persisted);
|
|
23244
|
+
if (isUpdate(persisted)) return updatedSession(id, persisted);
|
|
22660
23245
|
if (needsWrapperRelaunch(persisted)) {
|
|
22661
23246
|
const idle = persisted.status === "waiting";
|
|
22662
23247
|
const pty2 = spawnPty(
|
|
22663
23248
|
assistResumeArgs(persisted),
|
|
22664
23249
|
persisted.cwd,
|
|
22665
|
-
|
|
23250
|
+
id,
|
|
22666
23251
|
idle ? { ASSIST_RESUME_IDLE: "1" } : void 0
|
|
22667
23252
|
);
|
|
22668
23253
|
return idle ? waitingSession(base, persisted, pty2) : runningSession(base, persisted, pty2);
|
|
22669
23254
|
}
|
|
22670
|
-
return restoreInteractiveSession(
|
|
23255
|
+
return restoreInteractiveSession(id, persisted, base);
|
|
22671
23256
|
}
|
|
22672
23257
|
function needsWrapperRelaunch(persisted) {
|
|
22673
23258
|
return isBacklogRun(persisted) || isOnceLaunch(persisted) && !!persisted.claudeSessionId;
|
|
@@ -22683,20 +23268,20 @@ function isOnceLaunch(persisted) {
|
|
|
22683
23268
|
// src/commands/sessions/daemon/restoreOne.ts
|
|
22684
23269
|
function restoreOne(persisted, spawn12, sessions) {
|
|
22685
23270
|
try {
|
|
22686
|
-
const
|
|
22687
|
-
logUnresumable(persisted.name,
|
|
23271
|
+
const id = spawn12((sid) => restoreSession(sid, persisted));
|
|
23272
|
+
logUnresumable(persisted.name, id, sessions.get(id));
|
|
22688
23273
|
} catch (error) {
|
|
22689
23274
|
const reason = logRestoreError(persisted.name, error);
|
|
22690
23275
|
try {
|
|
22691
|
-
spawn12((
|
|
23276
|
+
spawn12((id) => errorSession(id, persisted, reason));
|
|
22692
23277
|
} catch {
|
|
22693
23278
|
}
|
|
22694
23279
|
}
|
|
22695
23280
|
}
|
|
22696
|
-
function logUnresumable(name,
|
|
23281
|
+
function logUnresumable(name, id, session) {
|
|
22697
23282
|
if (session?.status === "error")
|
|
22698
23283
|
daemonLog(
|
|
22699
|
-
`could not resume restored session "${name}" (id ${
|
|
23284
|
+
`could not resume restored session "${name}" (id ${id}): ${session.error}`
|
|
22700
23285
|
);
|
|
22701
23286
|
}
|
|
22702
23287
|
function logRestoreError(name, error) {
|
|
@@ -22738,17 +23323,17 @@ function restoreAll(spawn12, sessions) {
|
|
|
22738
23323
|
}
|
|
22739
23324
|
|
|
22740
23325
|
// src/commands/sessions/daemon/resumeSession.ts
|
|
22741
|
-
function resumeSession(
|
|
23326
|
+
function resumeSession(id, sessionId, cwd, name) {
|
|
22742
23327
|
const startedAt = Date.now();
|
|
22743
23328
|
return {
|
|
22744
|
-
id
|
|
23329
|
+
id,
|
|
22745
23330
|
name: name ? `${name.slice(0, 36)} (R)` : `Resume ${sessionId.slice(0, 8)}`,
|
|
22746
23331
|
commandType: "claude",
|
|
22747
23332
|
status: "waiting",
|
|
22748
23333
|
startedAt,
|
|
22749
23334
|
runningMs: 0,
|
|
22750
23335
|
runningSince: null,
|
|
22751
|
-
pty: spawnClaude2({ resumeSessionId: sessionId, cwd, sessionId:
|
|
23336
|
+
pty: spawnClaude2({ resumeSessionId: sessionId, cwd, sessionId: id }),
|
|
22752
23337
|
scrollback: "",
|
|
22753
23338
|
cwd,
|
|
22754
23339
|
/* why: bind the card to the conversation it is resuming so a daemon restart
|
|
@@ -22777,8 +23362,8 @@ function respawnThunk(session) {
|
|
|
22777
23362
|
}
|
|
22778
23363
|
|
|
22779
23364
|
// src/commands/sessions/daemon/reuseSessionForRun.ts
|
|
22780
|
-
function reuseSessionForRun(session,
|
|
22781
|
-
const assistArgs = ["backlog", "run", String(
|
|
23365
|
+
function reuseSessionForRun(session, itemId2, clients, onStatusChange) {
|
|
23366
|
+
const assistArgs = ["backlog", "run", String(itemId2)];
|
|
22782
23367
|
session.pty?.kill();
|
|
22783
23368
|
session.assistArgs = assistArgs;
|
|
22784
23369
|
session.name = `assist ${assistArgs.join(" ")}`;
|
|
@@ -22796,7 +23381,7 @@ function reuseSessionForRun(session, itemId, clients, onStatusChange) {
|
|
|
22796
23381
|
broadcast(clients, { type: "clear", sessionId: session.id });
|
|
22797
23382
|
wirePtyEvents(session, clients, onStatusChange);
|
|
22798
23383
|
daemonLog(
|
|
22799
|
-
`session ${session.id} reused for backlog run ${
|
|
23384
|
+
`session ${session.id} reused for backlog run ${itemId2}: ${session.name}`
|
|
22800
23385
|
);
|
|
22801
23386
|
}
|
|
22802
23387
|
|
|
@@ -22936,14 +23521,14 @@ async function discoverWindowsSessions(conn) {
|
|
|
22936
23521
|
|
|
22937
23522
|
// src/commands/sessions/daemon/toWindowsSessionId.ts
|
|
22938
23523
|
var PREFIX = "w-";
|
|
22939
|
-
function toWindowsSessionId(
|
|
22940
|
-
return isWindowsSessionId(
|
|
23524
|
+
function toWindowsSessionId(id) {
|
|
23525
|
+
return isWindowsSessionId(id) ? id : `${PREFIX}${id}`;
|
|
22941
23526
|
}
|
|
22942
|
-
function isWindowsSessionId(
|
|
22943
|
-
return
|
|
23527
|
+
function isWindowsSessionId(id) {
|
|
23528
|
+
return id.startsWith(PREFIX);
|
|
22944
23529
|
}
|
|
22945
|
-
function stripWindowsSessionId(
|
|
22946
|
-
let stripped =
|
|
23530
|
+
function stripWindowsSessionId(id) {
|
|
23531
|
+
let stripped = id;
|
|
22947
23532
|
while (stripped.startsWith(PREFIX)) stripped = stripped.slice(PREFIX.length);
|
|
22948
23533
|
return stripped;
|
|
22949
23534
|
}
|
|
@@ -23038,8 +23623,8 @@ function handleSessions(state, msg) {
|
|
|
23038
23623
|
id: toWindowsSessionId(s.id)
|
|
23039
23624
|
}));
|
|
23040
23625
|
const live = new Set(state.windowsSessions.map((s) => s.id));
|
|
23041
|
-
for (const
|
|
23042
|
-
if (!live.has(
|
|
23626
|
+
for (const id of state.scrollback.keys())
|
|
23627
|
+
if (!live.has(id)) state.scrollback.delete(id);
|
|
23043
23628
|
state.onSessionsChanged();
|
|
23044
23629
|
}
|
|
23045
23630
|
|
|
@@ -23442,34 +24027,34 @@ function watchPromptSubmit(session, data, onStatusChange) {
|
|
|
23442
24027
|
}
|
|
23443
24028
|
|
|
23444
24029
|
// src/commands/sessions/daemon/writeToSession.ts
|
|
23445
|
-
function writeToSession(sessions,
|
|
23446
|
-
const s = sessions.get(
|
|
24030
|
+
function writeToSession(sessions, id, data, onStatusChange) {
|
|
24031
|
+
const s = sessions.get(id);
|
|
23447
24032
|
if (!s || s.status === "done") return;
|
|
23448
24033
|
s.pty?.write(data);
|
|
23449
24034
|
watchEscInterrupt(s, data, onStatusChange);
|
|
23450
24035
|
watchPromptSubmit(s, data, onStatusChange);
|
|
23451
24036
|
}
|
|
23452
|
-
function resizeSession(sessions,
|
|
23453
|
-
const s = sessions.get(
|
|
24037
|
+
function resizeSession(sessions, id, cols, rows) {
|
|
24038
|
+
const s = sessions.get(id);
|
|
23454
24039
|
if (s && s.status !== "done") s.pty?.resize(cols, rows);
|
|
23455
24040
|
}
|
|
23456
|
-
function setAutoRun(sessions,
|
|
23457
|
-
const s = sessions.get(
|
|
24041
|
+
function setAutoRun(sessions, id, enabled) {
|
|
24042
|
+
const s = sessions.get(id);
|
|
23458
24043
|
if (!s) return false;
|
|
23459
24044
|
s.autoRun = enabled;
|
|
23460
|
-
daemonLog(`session ${
|
|
24045
|
+
daemonLog(`session ${id} autorun ${enabled ? "on" : "off"}`);
|
|
23461
24046
|
return true;
|
|
23462
24047
|
}
|
|
23463
|
-
function setAutoAdvance(sessions,
|
|
23464
|
-
const s = sessions.get(
|
|
24048
|
+
function setAutoAdvance(sessions, id, enabled) {
|
|
24049
|
+
const s = sessions.get(id);
|
|
23465
24050
|
if (!s) return false;
|
|
23466
24051
|
s.autoAdvance = enabled;
|
|
23467
|
-
const
|
|
23468
|
-
if (
|
|
23469
|
-
if (enabled) clearPause(
|
|
23470
|
-
else requestPause(
|
|
24052
|
+
const itemId2 = s.activity?.itemId;
|
|
24053
|
+
if (itemId2 != null) {
|
|
24054
|
+
if (enabled) clearPause(itemId2);
|
|
24055
|
+
else requestPause(itemId2);
|
|
23471
24056
|
}
|
|
23472
|
-
daemonLog(`session ${
|
|
24057
|
+
daemonLog(`session ${id} autoadvance ${enabled ? "on" : "off"}`);
|
|
23473
24058
|
return true;
|
|
23474
24059
|
}
|
|
23475
24060
|
|
|
@@ -23513,58 +24098,58 @@ var SessionManager = class {
|
|
|
23513
24098
|
}
|
|
23514
24099
|
spawnWith = (create) => this.add(create(sessionLimits.nextId(this.sessions.size, this.idCounter)));
|
|
23515
24100
|
spawn(prompt, cwd) {
|
|
23516
|
-
return this.spawnWith((
|
|
24101
|
+
return this.spawnWith((id) => createSession(id, prompt, cwd));
|
|
23517
24102
|
}
|
|
23518
24103
|
spawnRun(runName, runArgs, cwd) {
|
|
23519
|
-
return this.spawnWith((
|
|
24104
|
+
return this.spawnWith((id) => createRunSession(id, runName, runArgs, cwd));
|
|
23520
24105
|
}
|
|
23521
24106
|
spawnAssist(assistArgs, cwd, meta) {
|
|
23522
24107
|
return this.spawnWith(
|
|
23523
|
-
(
|
|
24108
|
+
(id) => createAssistSession(id, assistArgs, cwd, meta)
|
|
23524
24109
|
);
|
|
23525
24110
|
}
|
|
23526
24111
|
resume(sessionId, cwd, name) {
|
|
23527
|
-
return this.spawnWith((
|
|
24112
|
+
return this.spawnWith((id) => resumeSession(id, sessionId, cwd, name));
|
|
23528
24113
|
}
|
|
23529
24114
|
onStatusChange = makeStatusChangeHandler(
|
|
23530
|
-
(
|
|
24115
|
+
(id) => this.dismissSession(id),
|
|
23531
24116
|
() => this.notify(),
|
|
23532
|
-
(session,
|
|
24117
|
+
(session, itemId2) => reuseSessionForRun(session, itemId2, this.clients, this.onStatusChange)
|
|
23533
24118
|
);
|
|
23534
24119
|
wire(session) {
|
|
23535
24120
|
this.sessions.set(session.id, session);
|
|
23536
24121
|
wirePtyEvents(session, this.clients, this.onStatusChange);
|
|
23537
24122
|
this.notify();
|
|
23538
24123
|
}
|
|
23539
|
-
writeToSession(
|
|
23540
|
-
writeToSession(this.sessions,
|
|
24124
|
+
writeToSession(id, data) {
|
|
24125
|
+
writeToSession(this.sessions, id, data, this.onStatusChange);
|
|
23541
24126
|
}
|
|
23542
|
-
resizeSession(
|
|
23543
|
-
resizeSession(this.sessions,
|
|
24127
|
+
resizeSession(id, cols, rows) {
|
|
24128
|
+
resizeSession(this.sessions, id, cols, rows);
|
|
23544
24129
|
}
|
|
23545
|
-
retrySession(
|
|
23546
|
-
const s = this.sessions.get(
|
|
24130
|
+
retrySession(id) {
|
|
24131
|
+
const s = this.sessions.get(id);
|
|
23547
24132
|
if (s && retrySession(s, this.clients, this.onStatusChange)) this.notify();
|
|
23548
24133
|
}
|
|
23549
|
-
restart(
|
|
23550
|
-
const s = this.sessions.get(
|
|
24134
|
+
restart(id) {
|
|
24135
|
+
const s = this.sessions.get(id);
|
|
23551
24136
|
if (s && restartSession(s, this.clients, this.onStatusChange))
|
|
23552
24137
|
this.notify();
|
|
23553
24138
|
}
|
|
23554
|
-
dismissSession = (
|
|
23555
|
-
if (dismissSession(this.sessions,
|
|
24139
|
+
dismissSession = (id) => {
|
|
24140
|
+
if (dismissSession(this.sessions, id)) this.notify();
|
|
23556
24141
|
};
|
|
23557
|
-
setAutoRun(
|
|
23558
|
-
if (setAutoRun(this.sessions,
|
|
24142
|
+
setAutoRun(id, enabled) {
|
|
24143
|
+
if (setAutoRun(this.sessions, id, enabled)) this.notify();
|
|
23559
24144
|
}
|
|
23560
|
-
setAutoAdvance(
|
|
23561
|
-
if (setAutoAdvance(this.sessions,
|
|
24145
|
+
setAutoAdvance(id, enabled) {
|
|
24146
|
+
if (setAutoAdvance(this.sessions, id, enabled)) this.notify();
|
|
23562
24147
|
}
|
|
23563
|
-
setStatus(
|
|
23564
|
-
const session = this.sessions.get(
|
|
24148
|
+
setStatus(id, status2) {
|
|
24149
|
+
const session = this.sessions.get(id);
|
|
23565
24150
|
if (!session) {
|
|
23566
24151
|
daemonLog(
|
|
23567
|
-
`set-status for unknown session id=${
|
|
24152
|
+
`set-status for unknown session id=${id} status=${status2} (no live session; ignoring)`
|
|
23568
24153
|
);
|
|
23569
24154
|
return;
|
|
23570
24155
|
}
|
|
@@ -23637,7 +24222,7 @@ function deriveSessionType(commandName, name) {
|
|
|
23637
24222
|
|
|
23638
24223
|
// src/commands/sessions/shared/backlogRunMarkers.ts
|
|
23639
24224
|
function backlogRunMarkers(text9) {
|
|
23640
|
-
const match = text9.match(/backlog item
|
|
24225
|
+
const match = text9.match(/backlog item a(\d+):[ \t]*([^\n]*)/);
|
|
23641
24226
|
if (!match) return { commandName: "", commandArgs: "" };
|
|
23642
24227
|
const title = match[2].trim();
|
|
23643
24228
|
return {
|
|
@@ -24057,7 +24642,7 @@ function onListening(manager, checkAutoExit) {
|
|
|
24057
24642
|
process.on("exit", cleanupOwnedFiles);
|
|
24058
24643
|
const restored = manager.restore();
|
|
24059
24644
|
const liveIds = new Set(manager.listSessions().map((s) => s.id));
|
|
24060
|
-
manager.active.restore((
|
|
24645
|
+
manager.active.restore((id) => liveIds.has(id));
|
|
24061
24646
|
daemonLog(
|
|
24062
24647
|
restored.length > 0 ? `restored ${restored.length} session(s): ${restored.join(", ")}` : "no persisted sessions to restore"
|
|
24063
24648
|
);
|
|
@@ -24253,27 +24838,27 @@ function truncate3(text9, maxChars = 500) {
|
|
|
24253
24838
|
function scanSessionBacklogRefs(filePath) {
|
|
24254
24839
|
const ids = /* @__PURE__ */ new Set();
|
|
24255
24840
|
for (const text9 of iterateUserMessages(filePath, Number.MAX_SAFE_INTEGER)) {
|
|
24256
|
-
for (const
|
|
24257
|
-
ids.add(
|
|
24841
|
+
for (const id of extractBacklogIds(text9)) {
|
|
24842
|
+
ids.add(id);
|
|
24258
24843
|
}
|
|
24259
24844
|
}
|
|
24260
24845
|
return [...ids].sort((a, b) => a - b);
|
|
24261
24846
|
}
|
|
24262
24847
|
function extractBacklogIds(text9) {
|
|
24263
24848
|
const ids = [];
|
|
24264
|
-
for (const m of text9.matchAll(/backlog\s+run\s+(\d+)/gi)) {
|
|
24849
|
+
for (const m of text9.matchAll(/backlog\s+run\s+a(\d+)/gi)) {
|
|
24265
24850
|
ids.push(Number.parseInt(m[1], 10));
|
|
24266
24851
|
}
|
|
24267
|
-
for (const m of text9.matchAll(/backlog\s+(?:item\s+)
|
|
24852
|
+
for (const m of text9.matchAll(/backlog\s+(?:item\s+)?a(\d+)/gi)) {
|
|
24268
24853
|
ids.push(Number.parseInt(m[1], 10));
|
|
24269
24854
|
}
|
|
24270
|
-
for (const m of text9.matchAll(/backlog\s+phase-done\s+(\d+)/gi)) {
|
|
24855
|
+
for (const m of text9.matchAll(/backlog\s+phase-done\s+a(\d+)/gi)) {
|
|
24271
24856
|
ids.push(Number.parseInt(m[1], 10));
|
|
24272
24857
|
}
|
|
24273
|
-
for (const m of text9.matchAll(/backlog\s+comment\s+(\d+)/gi)) {
|
|
24858
|
+
for (const m of text9.matchAll(/backlog\s+comment\s+a(\d+)/gi)) {
|
|
24274
24859
|
ids.push(Number.parseInt(m[1], 10));
|
|
24275
24860
|
}
|
|
24276
|
-
for (const m of text9.matchAll(/(?:^|[\s(])
|
|
24861
|
+
for (const m of text9.matchAll(/(?:^|[\s(])a(\d{1,4})(?=[\s).,;:!?]|$)/gm)) {
|
|
24277
24862
|
ids.push(Number.parseInt(m[1], 10));
|
|
24278
24863
|
}
|
|
24279
24864
|
return ids;
|
|
@@ -24306,7 +24891,7 @@ function buildPrompt2(firstMessage, backlogIds) {
|
|
|
24306
24891
|
"Return ONLY the summary, no quotes or explanation."
|
|
24307
24892
|
];
|
|
24308
24893
|
if (backlogIds.length > 0) {
|
|
24309
|
-
const refs = backlogIds.map((
|
|
24894
|
+
const refs = backlogIds.map((id) => formatItemId(id)).join(", ");
|
|
24310
24895
|
parts.push(
|
|
24311
24896
|
`The session references backlog item(s) ${refs}. Start the summary with "Backlog ${refs} \u2014" if relevant.`
|
|
24312
24897
|
);
|
|
@@ -24381,6 +24966,28 @@ function registerSessions(program2) {
|
|
|
24381
24966
|
cmd.command("set-status <status>").description(
|
|
24382
24967
|
"Report the current session's status to the sessions daemon (used by Claude Code hooks)"
|
|
24383
24968
|
).action(setSessionStatus);
|
|
24969
|
+
configHelp(cmd, [
|
|
24970
|
+
{
|
|
24971
|
+
key: "sessions.windowsProjectsRoot",
|
|
24972
|
+
setter: "assist config set sessions.windowsProjectsRoot /mnt/c/Users/you/.claude/projects",
|
|
24973
|
+
note: "WSL path to the Windows .claude/projects root to discover"
|
|
24974
|
+
},
|
|
24975
|
+
{
|
|
24976
|
+
key: "sessions.windowsDaemonHost",
|
|
24977
|
+
setter: "assist config set sessions.windowsDaemonHost 127.0.0.1",
|
|
24978
|
+
note: "host the WSL daemon dials to reach the Windows daemon (default: 127.0.0.1)"
|
|
24979
|
+
},
|
|
24980
|
+
{
|
|
24981
|
+
key: "sessions.windowsDaemonPort",
|
|
24982
|
+
setter: "assist config set sessions.windowsDaemonPort 51764",
|
|
24983
|
+
note: "TCP port the Windows daemon listens on (default: 51764)"
|
|
24984
|
+
},
|
|
24985
|
+
{
|
|
24986
|
+
key: "sessions.windowsVersionCheck",
|
|
24987
|
+
setter: "assist config set sessions.windowsVersionCheck block",
|
|
24988
|
+
note: "Windows daemon version mismatch handling: block | warn | off"
|
|
24989
|
+
}
|
|
24990
|
+
]);
|
|
24384
24991
|
}
|
|
24385
24992
|
|
|
24386
24993
|
// src/commands/statusLine.ts
|
|
@@ -24697,12 +25304,25 @@ async function update2() {
|
|
|
24697
25304
|
await restartDaemonAfterUpdate();
|
|
24698
25305
|
}
|
|
24699
25306
|
|
|
25307
|
+
// src/reportCliError.ts
|
|
25308
|
+
import chalk198 from "chalk";
|
|
25309
|
+
function reportCliError(error) {
|
|
25310
|
+
if (error instanceof InvalidItemIdError) {
|
|
25311
|
+
console.error(chalk198.red(error.message));
|
|
25312
|
+
} else {
|
|
25313
|
+
console.error(error);
|
|
25314
|
+
}
|
|
25315
|
+
process.exitCode = 1;
|
|
25316
|
+
}
|
|
25317
|
+
|
|
24700
25318
|
// src/index.ts
|
|
24701
25319
|
var program = new Command();
|
|
24702
25320
|
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 }));
|
|
24703
|
-
program.command("sync").description("Copy command files to ~/.claude/commands").option("-y, --yes", "Overwrite settings.json without prompting").action((options2) => sync(options2));
|
|
25321
|
+
var syncCommand = program.command("sync").description("Copy command files to ~/.claude/commands").option("-y, --yes", "Overwrite settings.json without prompting").action((options2) => sync(options2));
|
|
25322
|
+
configHelp(syncCommand, rootConfigHelp.sync);
|
|
24704
25323
|
program.command("init").description("Initialize VS Code and verify configurations").action(init5);
|
|
24705
|
-
program.command("commit").description("Create a git commit with validation").argument("<args...>", "status | <message> [files...]").action(commit);
|
|
25324
|
+
var commitCommand = program.command("commit").description("Create a git commit with validation").argument("<args...>", "status | <message> [files...]").action(commit);
|
|
25325
|
+
configHelp(commitCommand, rootConfigHelp.commit);
|
|
24706
25326
|
registerConfig(program);
|
|
24707
25327
|
registerRun(program);
|
|
24708
25328
|
registerNew(program);
|
|
@@ -24711,12 +25331,14 @@ lintCommand.command("init").description("Initialize oxlint with baseline linter
|
|
|
24711
25331
|
var vscodeCommand = program.command("vscode").description("VS Code configuration utilities");
|
|
24712
25332
|
vscodeCommand.command("init").description("Add VS Code configuration files").action(init4);
|
|
24713
25333
|
program.command("status-line").description("Format Claude Code status line from JSON stdin").action(statusLine);
|
|
24714
|
-
program.command("notify").description(
|
|
25334
|
+
var notifyCommand = program.command("notify").description(
|
|
24715
25335
|
"Show notification from Claude Code hook (reads JSON from stdin)"
|
|
24716
25336
|
).action(notify);
|
|
25337
|
+
configHelp(notifyCommand, rootConfigHelp.notify);
|
|
24717
25338
|
program.command("update").description("Update assist to the latest version and sync commands").action(update2);
|
|
24718
25339
|
program.command("coverage").description("Print global statement coverage percentage").action(coverage);
|
|
24719
|
-
program.command("screenshot").description("Capture a screenshot of a running application window").argument("<process>", "Name of the running process (e.g. notepad, code)").action(screenshot);
|
|
25340
|
+
var screenshotCommand = program.command("screenshot").description("Capture a screenshot of a running application window").argument("<process>", "Name of the running process (e.g. notepad, code)").action(screenshot);
|
|
25341
|
+
configHelp(screenshotCommand, rootConfigHelp.screenshot);
|
|
24720
25342
|
registerActivity(program);
|
|
24721
25343
|
registerBackup(program);
|
|
24722
25344
|
registerCliHook(program);
|
|
@@ -24751,7 +25373,4 @@ registerPrompts(program);
|
|
|
24751
25373
|
registerDeny(program);
|
|
24752
25374
|
registerLaunch(program);
|
|
24753
25375
|
registerSignal(program);
|
|
24754
|
-
program.parseAsync().catch((
|
|
24755
|
-
console.error(error);
|
|
24756
|
-
process.exitCode = 1;
|
|
24757
|
-
}).finally(() => closeDb());
|
|
25376
|
+
program.parseAsync().catch(reportCliError).finally(() => closeDb());
|