@staff0rd/assist 0.630.2 → 0.632.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 -2
- package/allowed.cli-reads +1 -0
- package/dist/allowed.cli-reads +1 -0
- package/dist/commands/sessions/web/bundle.js +427 -433
- package/dist/index.js +428 -302
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.632.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -675,7 +675,10 @@ var assistConfigShape = {
|
|
|
675
675
|
required: z3.boolean().default(false),
|
|
676
676
|
promptJira: z3.boolean().default(false),
|
|
677
677
|
draft: z3.boolean().default(false),
|
|
678
|
-
readingWordsPerMinute: z3.number().int().positive().
|
|
678
|
+
readingWordsPerMinute: z3.number().int().positive().optional()
|
|
679
|
+
}).optional(),
|
|
680
|
+
readTime: z3.strictObject({
|
|
681
|
+
wordsPerMinute: z3.number().int().positive().default(200)
|
|
679
682
|
}).optional(),
|
|
680
683
|
worktree: z3.strictObject({
|
|
681
684
|
enabled: z3.boolean().default(false),
|
|
@@ -1420,8 +1423,8 @@ var options = [
|
|
|
1420
1423
|
];
|
|
1421
1424
|
|
|
1422
1425
|
// src/commands/verify/init/getAvailableOptions/index.ts
|
|
1423
|
-
function resolveDescription(
|
|
1424
|
-
return typeof
|
|
1426
|
+
function resolveDescription(desc7, setup2) {
|
|
1427
|
+
return typeof desc7 === "function" ? desc7(setup2) : desc7;
|
|
1425
1428
|
}
|
|
1426
1429
|
function toVerifyOption(def, setup2) {
|
|
1427
1430
|
return {
|
|
@@ -3126,13 +3129,6 @@ var prsRaiseConfigHelp = [
|
|
|
3126
3129
|
note: "'assist prs raise' creates a draft PR when neither --draft nor --no-draft is passed (default false)"
|
|
3127
3130
|
}
|
|
3128
3131
|
];
|
|
3129
|
-
var prsReadTimeConfigHelp = [
|
|
3130
|
-
{
|
|
3131
|
-
key: "prs.readingWordsPerMinute",
|
|
3132
|
-
setter: "assist config set prs.readingWordsPerMinute 150",
|
|
3133
|
-
note: "prose reading speed 'assist prs read-time' estimates with (default 80, measured on real pull request descriptions; fenced code counts at half that)"
|
|
3134
|
-
}
|
|
3135
|
-
];
|
|
3136
3132
|
var prsConfigHelp = [
|
|
3137
3133
|
{
|
|
3138
3134
|
key: "prs.slack",
|
|
@@ -3144,8 +3140,7 @@ var prsConfigHelp = [
|
|
|
3144
3140
|
setter: "assist config set prs.required true",
|
|
3145
3141
|
note: "require a branch when running a backlog item"
|
|
3146
3142
|
},
|
|
3147
|
-
...prsRaiseConfigHelp
|
|
3148
|
-
...prsReadTimeConfigHelp
|
|
3143
|
+
...prsRaiseConfigHelp
|
|
3149
3144
|
];
|
|
3150
3145
|
|
|
3151
3146
|
// src/commands/ravendb/ravendbConfigHelp.ts
|
|
@@ -3162,6 +3157,20 @@ var ravendbConfigHelp = [
|
|
|
3162
3157
|
}
|
|
3163
3158
|
];
|
|
3164
3159
|
|
|
3160
|
+
// src/commands/readTime/readTimeConfigHelp.ts
|
|
3161
|
+
var readTimeConfigHelp = [
|
|
3162
|
+
{
|
|
3163
|
+
key: "readTime.wordsPerMinute",
|
|
3164
|
+
setter: "assist config set readTime.wordsPerMinute 250",
|
|
3165
|
+
note: "nominal prose reading speed 'assist read-time' estimates with (default 200); the effective rate decays with length, so a 500-word document reads at ~70 wpm, and fenced code counts double"
|
|
3166
|
+
},
|
|
3167
|
+
{
|
|
3168
|
+
key: "prs.readingWordsPerMinute",
|
|
3169
|
+
setter: "assist config set prs.readingWordsPerMinute 250",
|
|
3170
|
+
note: "same speed under the command's former name, read only when readTime.wordsPerMinute is unset"
|
|
3171
|
+
}
|
|
3172
|
+
];
|
|
3173
|
+
|
|
3165
3174
|
// src/commands/refactor/refactorConfigHelp.ts
|
|
3166
3175
|
var refactorConfigHelp = [
|
|
3167
3176
|
{
|
|
@@ -3495,6 +3504,7 @@ var configHelpEntries = [
|
|
|
3495
3504
|
...miroConfigHelp,
|
|
3496
3505
|
...prsConfigHelp,
|
|
3497
3506
|
...ravendbConfigHelp,
|
|
3507
|
+
...readTimeConfigHelp,
|
|
3498
3508
|
...refactorConfigHelp,
|
|
3499
3509
|
...roamConfigHelp,
|
|
3500
3510
|
...runConfigHelp,
|
|
@@ -5579,8 +5589,8 @@ async function assertMigrationsInSync(exec3) {
|
|
|
5579
5589
|
// src/shared/db/migrations/MigrationExecutor.ts
|
|
5580
5590
|
function pgExecutor(queryable) {
|
|
5581
5591
|
return {
|
|
5582
|
-
exec: (
|
|
5583
|
-
query: async (
|
|
5592
|
+
exec: (sql28) => queryable.query(sql28),
|
|
5593
|
+
query: async (sql28, params) => (await queryable.query(sql28, params)).rows
|
|
5584
5594
|
};
|
|
5585
5595
|
}
|
|
5586
5596
|
|
|
@@ -5901,8 +5911,8 @@ async function buildDump(tables, copyOut) {
|
|
|
5901
5911
|
// src/commands/backlog/dump/copyTableOut.ts
|
|
5902
5912
|
import { to as copyTo } from "pg-copy-streams";
|
|
5903
5913
|
async function copyTableOut(client, table) {
|
|
5904
|
-
const
|
|
5905
|
-
const stream = client.query(copyTo(
|
|
5914
|
+
const sql28 = `COPY ${table.name} (${table.columns.join(", ")}) TO STDOUT`;
|
|
5915
|
+
const stream = client.query(copyTo(sql28));
|
|
5906
5916
|
const chunks = [];
|
|
5907
5917
|
for await (const chunk of stream) {
|
|
5908
5918
|
chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(chunk));
|
|
@@ -9874,7 +9884,7 @@ function incompleteSubtaskCounts(orm) {
|
|
|
9874
9884
|
}).from(itemSubtasks).where(ne(itemSubtasks.status, "done")).groupBy(itemSubtasks.itemId).as("incomplete_counts");
|
|
9875
9885
|
}
|
|
9876
9886
|
|
|
9877
|
-
// src/
|
|
9887
|
+
// src/shared/db/planPhaseCounts.ts
|
|
9878
9888
|
import { sql as sql14 } from "drizzle-orm";
|
|
9879
9889
|
function planPhaseCounts(orm) {
|
|
9880
9890
|
return orm.select({
|
|
@@ -9883,11 +9893,12 @@ function planPhaseCounts(orm) {
|
|
|
9883
9893
|
}).from(planPhases).groupBy(planPhases.itemId).as("plan_phase_counts");
|
|
9884
9894
|
}
|
|
9885
9895
|
|
|
9886
|
-
// src/
|
|
9896
|
+
// src/shared/db/phaseUsageTotals.ts
|
|
9887
9897
|
import { sql as sql15 } from "drizzle-orm";
|
|
9888
9898
|
function phaseUsageTotals(orm) {
|
|
9889
9899
|
return orm.select({
|
|
9890
9900
|
itemId: phaseUsage.itemId,
|
|
9901
|
+
recordedPhases: sql15`count(*)::int`.as("recorded_phases"),
|
|
9891
9902
|
tokensUp: sql15`sum(${phaseUsage.tokensUp})::bigint`.as(
|
|
9892
9903
|
"total_tokens_up"
|
|
9893
9904
|
),
|
|
@@ -11695,24 +11706,103 @@ async function listUsagePeaks(db, options2) {
|
|
|
11695
11706
|
return query;
|
|
11696
11707
|
}
|
|
11697
11708
|
|
|
11698
|
-
// src/commands/sessions/web/
|
|
11709
|
+
// src/commands/sessions/web/respondPagedRows.ts
|
|
11699
11710
|
var DEFAULT_PAGE_SIZE = 30;
|
|
11700
|
-
function
|
|
11701
|
-
return value === "five_hour" || value === "seven_day" ? value : void 0;
|
|
11702
|
-
}
|
|
11703
|
-
async function listUsageHistory(req, res) {
|
|
11711
|
+
async function respondPagedRows(req, res, load3) {
|
|
11704
11712
|
const params = new URL(req.url ?? "/", "http://localhost").searchParams;
|
|
11705
11713
|
const page = Math.max(0, Number(params.get("page")) || 0);
|
|
11706
|
-
const
|
|
11707
|
-
const
|
|
11708
|
-
const db = await getDb();
|
|
11709
|
-
const [rows, total] = await Promise.all([
|
|
11710
|
-
listUsagePeaks(db, { limit: pageSize, offset: page * pageSize, window }),
|
|
11711
|
-
countUsagePeaks(db, window)
|
|
11712
|
-
]);
|
|
11714
|
+
const limit = Number(params.get("pageSize")) || DEFAULT_PAGE_SIZE;
|
|
11715
|
+
const [rows, total] = await load3({ limit, offset: page * limit }, params);
|
|
11713
11716
|
respondJson(res, 200, { rows, total });
|
|
11714
11717
|
}
|
|
11715
11718
|
|
|
11719
|
+
// src/commands/sessions/web/listUsageHistory.ts
|
|
11720
|
+
function parseWindow(value) {
|
|
11721
|
+
return value === "five_hour" || value === "seven_day" ? value : void 0;
|
|
11722
|
+
}
|
|
11723
|
+
function listUsageHistory(req, res) {
|
|
11724
|
+
return respondPagedRows(req, res, async (range, params) => {
|
|
11725
|
+
const window = parseWindow(params.get("window"));
|
|
11726
|
+
const db = await getDb();
|
|
11727
|
+
return Promise.all([
|
|
11728
|
+
listUsagePeaks(db, { ...range, window }),
|
|
11729
|
+
countUsagePeaks(db, window)
|
|
11730
|
+
]);
|
|
11731
|
+
});
|
|
11732
|
+
}
|
|
11733
|
+
|
|
11734
|
+
// src/shared/db/countItemUsageSummaries.ts
|
|
11735
|
+
import { sql as sql17 } from "drizzle-orm";
|
|
11736
|
+
async function countItemUsageSummaries(db) {
|
|
11737
|
+
const [row] = await db.select({
|
|
11738
|
+
value: sql17`count(distinct ${phaseUsage.itemId})::int`
|
|
11739
|
+
}).from(phaseUsage);
|
|
11740
|
+
return row?.value ?? 0;
|
|
11741
|
+
}
|
|
11742
|
+
|
|
11743
|
+
// src/shared/db/listItemUsageSummaries.ts
|
|
11744
|
+
import { desc as desc3, eq as eq20, sql as sql19 } from "drizzle-orm";
|
|
11745
|
+
|
|
11746
|
+
// src/shared/db/lastPhaseActivity.ts
|
|
11747
|
+
import { sql as sql18 } from "drizzle-orm";
|
|
11748
|
+
function lastPhaseActivity(db) {
|
|
11749
|
+
return db.select({
|
|
11750
|
+
itemId: phaseSessions.itemId,
|
|
11751
|
+
at: sql18`max(${phaseSessions.createdAt})`.as("last_phase_at"),
|
|
11752
|
+
atIso: sql18`to_char(max(${phaseSessions.createdAt}) at time zone 'utc', 'YYYY-MM-DD"T"HH24:MI:SS.MS"Z"')`.as(
|
|
11753
|
+
"last_phase_at_iso"
|
|
11754
|
+
)
|
|
11755
|
+
}).from(phaseSessions).groupBy(phaseSessions.itemId).as("last_phase_activity");
|
|
11756
|
+
}
|
|
11757
|
+
|
|
11758
|
+
// src/shared/db/toItemUsageSummary.ts
|
|
11759
|
+
function toItemUsageSummary(row) {
|
|
11760
|
+
return {
|
|
11761
|
+
...row,
|
|
11762
|
+
phaseCount: row.phaseCount ?? 0,
|
|
11763
|
+
recordedPhases: Number(row.recordedPhases),
|
|
11764
|
+
tokensUp: Number(row.tokensUp),
|
|
11765
|
+
tokensDown: Number(row.tokensDown),
|
|
11766
|
+
activeMs: Number(row.activeMs),
|
|
11767
|
+
peakContextPct: Number(row.peakContextPct),
|
|
11768
|
+
lastPhaseAt: row.lastPhaseAt ?? null
|
|
11769
|
+
};
|
|
11770
|
+
}
|
|
11771
|
+
|
|
11772
|
+
// src/shared/db/listItemUsageSummaries.ts
|
|
11773
|
+
async function listItemUsageSummaries(db, options2) {
|
|
11774
|
+
const totals = phaseUsageTotals(db);
|
|
11775
|
+
const planned = planPhaseCounts(db);
|
|
11776
|
+
const lastPhase = lastPhaseActivity(db);
|
|
11777
|
+
const query = db.select({
|
|
11778
|
+
id: items.id,
|
|
11779
|
+
origin: items.origin,
|
|
11780
|
+
type: items.type,
|
|
11781
|
+
name: items.name,
|
|
11782
|
+
status: items.status,
|
|
11783
|
+
phaseCount: planned.count,
|
|
11784
|
+
recordedPhases: totals.recordedPhases,
|
|
11785
|
+
tokensUp: totals.tokensUp,
|
|
11786
|
+
tokensDown: totals.tokensDown,
|
|
11787
|
+
activeMs: totals.activeMs,
|
|
11788
|
+
peakContextPct: totals.peakContextPct,
|
|
11789
|
+
lastPhaseAt: lastPhase.atIso
|
|
11790
|
+
}).from(items).innerJoin(totals, eq20(totals.itemId, items.id)).leftJoin(planned, eq20(planned.itemId, items.id)).leftJoin(lastPhase, eq20(lastPhase.itemId, items.id)).orderBy(sql19`${lastPhase.at} desc nulls last`, desc3(items.id));
|
|
11791
|
+
const rows = options2?.limit === void 0 ? await query : await query.limit(options2.limit).offset(options2.offset ?? 0);
|
|
11792
|
+
return rows.map(toItemUsageSummary);
|
|
11793
|
+
}
|
|
11794
|
+
|
|
11795
|
+
// src/commands/sessions/web/listUsageItems.ts
|
|
11796
|
+
function listUsageItems(req, res) {
|
|
11797
|
+
return respondPagedRows(req, res, async (range) => {
|
|
11798
|
+
const db = await getDb();
|
|
11799
|
+
return Promise.all([
|
|
11800
|
+
listItemUsageSummaries(db, range),
|
|
11801
|
+
countItemUsageSummaries(db)
|
|
11802
|
+
]);
|
|
11803
|
+
});
|
|
11804
|
+
}
|
|
11805
|
+
|
|
11716
11806
|
// src/commands/sessions/web/openInCode.ts
|
|
11717
11807
|
import { exec } from "child_process";
|
|
11718
11808
|
import { promisify as promisify4 } from "util";
|
|
@@ -12699,7 +12789,7 @@ async function runGhImage(filePath, cwd) {
|
|
|
12699
12789
|
);
|
|
12700
12790
|
}
|
|
12701
12791
|
const lines2 = stdout.split("\n").map((line) => line.trim()).filter(Boolean);
|
|
12702
|
-
const markdown = lines2.find((line) => /^!\[.*]\(.+\)$/.test(line)) ?? lines2.find((line) => line.includes("http")) ?? lines2[0];
|
|
12792
|
+
const markdown = lines2.find((line) => /^!\[.*]\(.+\)$/.test(line)) ?? lines2.find((line) => /^https?:\/\/\S+$/.test(line)) ?? lines2.find((line) => line.includes("http")) ?? lines2[0];
|
|
12703
12793
|
if (!markdown) throw new Error("gh image produced no output");
|
|
12704
12794
|
return markdown;
|
|
12705
12795
|
}
|
|
@@ -12731,12 +12821,23 @@ var EXT_BY_MIME = {
|
|
|
12731
12821
|
"image/webp": "webp",
|
|
12732
12822
|
"image/svg+xml": "svg",
|
|
12733
12823
|
"image/bmp": "bmp",
|
|
12734
|
-
"image/avif": "avif"
|
|
12824
|
+
"image/avif": "avif",
|
|
12825
|
+
"video/mp4": "mp4",
|
|
12826
|
+
"video/webm": "webm",
|
|
12827
|
+
"video/quicktime": "mov",
|
|
12828
|
+
"video/ogg": "ogv"
|
|
12735
12829
|
};
|
|
12830
|
+
function extensionFromMime(contentType) {
|
|
12831
|
+
const mime = contentType.split(";")[0].trim().toLowerCase();
|
|
12832
|
+
const known = EXT_BY_MIME[mime];
|
|
12833
|
+
if (known) return known;
|
|
12834
|
+
const subtype = mime.split("/")[1]?.replace(/^x-/, "").replace(/\+.*$/, "");
|
|
12835
|
+
return subtype && /^[a-z0-9]+$/.test(subtype) ? subtype : void 0;
|
|
12836
|
+
}
|
|
12736
12837
|
function pickExtension(name, contentType) {
|
|
12737
12838
|
const fromName = extname(name).replace(/^\./, "").toLowerCase();
|
|
12738
12839
|
if (fromName) return fromName;
|
|
12739
|
-
return
|
|
12840
|
+
return extensionFromMime(contentType) ?? "png";
|
|
12740
12841
|
}
|
|
12741
12842
|
function safeBaseName(name) {
|
|
12742
12843
|
const base = name.replace(/\.[^.]+$/, "").replace(/[^a-zA-Z0-9_-]+/g, "-");
|
|
@@ -12949,6 +13050,7 @@ var routes2 = {
|
|
|
12949
13050
|
"GET /api/pr-list": prList,
|
|
12950
13051
|
"GET /api/news/items": listNewsItems,
|
|
12951
13052
|
"GET /api/usage/history": listUsageHistory,
|
|
13053
|
+
"GET /api/usage/items": listUsageItems,
|
|
12952
13054
|
"GET /api/backups/list": getBackups,
|
|
12953
13055
|
"GET /api/config": getConfig,
|
|
12954
13056
|
"POST /api/config/set": setConfig,
|
|
@@ -13520,11 +13622,11 @@ function registerActivityCommands(cmd) {
|
|
|
13520
13622
|
|
|
13521
13623
|
// src/commands/backlog/associate-github/index.ts
|
|
13522
13624
|
import chalk66 from "chalk";
|
|
13523
|
-
import { eq as
|
|
13625
|
+
import { eq as eq22 } from "drizzle-orm";
|
|
13524
13626
|
|
|
13525
13627
|
// src/commands/backlog/beginAssociation.ts
|
|
13526
13628
|
import chalk65 from "chalk";
|
|
13527
|
-
import { eq as
|
|
13629
|
+
import { eq as eq21 } from "drizzle-orm";
|
|
13528
13630
|
async function beginAssociation(id, options2, clearPatch, label2) {
|
|
13529
13631
|
const found = await findOneItem(id);
|
|
13530
13632
|
if (!found) {
|
|
@@ -13534,7 +13636,7 @@ async function beginAssociation(id, options2, clearPatch, label2) {
|
|
|
13534
13636
|
const { orm } = found;
|
|
13535
13637
|
const itemId2 = found.item.id;
|
|
13536
13638
|
if (options2.clear) {
|
|
13537
|
-
await orm.update(items).set(clearPatch).where(
|
|
13639
|
+
await orm.update(items).set(clearPatch).where(eq21(items.id, itemId2));
|
|
13538
13640
|
console.log(
|
|
13539
13641
|
chalk65.green(
|
|
13540
13642
|
`Cleared ${label2} association on item ${formatItemId(itemId2)}.`
|
|
@@ -13602,7 +13704,7 @@ async function associateGithub(id, issue, options2) {
|
|
|
13602
13704
|
return;
|
|
13603
13705
|
}
|
|
13604
13706
|
const title = fetchGithubIssueTitle(normalized);
|
|
13605
|
-
await orm.update(items).set({ githubIssue: normalized, jiraKey: null }).where(
|
|
13707
|
+
await orm.update(items).set({ githubIssue: normalized, jiraKey: null }).where(eq22(items.id, itemId2));
|
|
13606
13708
|
console.log(
|
|
13607
13709
|
chalk66.green(`Associated ${normalized} with item ${formatItemId(itemId2)}.`),
|
|
13608
13710
|
title ? chalk66.dim(`(${title})`) : ""
|
|
@@ -13616,7 +13718,7 @@ function registerAssociateGithubCommand(cmd) {
|
|
|
13616
13718
|
|
|
13617
13719
|
// src/commands/backlog/associate-jira/index.ts
|
|
13618
13720
|
import chalk68 from "chalk";
|
|
13619
|
-
import { eq as
|
|
13721
|
+
import { eq as eq23 } from "drizzle-orm";
|
|
13620
13722
|
|
|
13621
13723
|
// src/commands/jira/fetchIssue.ts
|
|
13622
13724
|
import { execSync as execSync27 } from "child_process";
|
|
@@ -13681,7 +13783,7 @@ async function associateJira(id, key, options2) {
|
|
|
13681
13783
|
const parsed = fetchIssue(normalized, "summary");
|
|
13682
13784
|
const fields = parsed?.fields;
|
|
13683
13785
|
const summary = fields?.summary;
|
|
13684
|
-
await orm.update(items).set({ jiraKey: normalized, githubIssue: null }).where(
|
|
13786
|
+
await orm.update(items).set({ jiraKey: normalized, githubIssue: null }).where(eq23(items.id, itemId2));
|
|
13685
13787
|
console.log(
|
|
13686
13788
|
chalk68.green(`Associated ${normalized} with item ${formatItemId(itemId2)}.`),
|
|
13687
13789
|
summary ? chalk68.dim(`(${summary})`) : ""
|
|
@@ -14085,8 +14187,8 @@ async function readStdinBuffer() {
|
|
|
14085
14187
|
import { finished } from "stream/promises";
|
|
14086
14188
|
import { from as copyFrom } from "pg-copy-streams";
|
|
14087
14189
|
async function copyTableIn(client, table, data) {
|
|
14088
|
-
const
|
|
14089
|
-
const stream = client.query(copyFrom(
|
|
14190
|
+
const sql28 = `COPY ${table.name} (${table.columns.join(", ")}) FROM STDIN`;
|
|
14191
|
+
const stream = client.query(copyFrom(sql28));
|
|
14090
14192
|
stream.end(data);
|
|
14091
14193
|
await finished(stream);
|
|
14092
14194
|
}
|
|
@@ -14188,15 +14290,15 @@ function isClaudeCode() {
|
|
|
14188
14290
|
}
|
|
14189
14291
|
|
|
14190
14292
|
// src/commands/backlog/insertPhaseAt.ts
|
|
14191
|
-
import { eq as
|
|
14293
|
+
import { eq as eq25 } from "drizzle-orm";
|
|
14192
14294
|
|
|
14193
14295
|
// src/commands/backlog/shiftPhasesUp.ts
|
|
14194
|
-
import { and as and9, desc as
|
|
14296
|
+
import { and as and9, desc as desc4, eq as eq24, gte } from "drizzle-orm";
|
|
14195
14297
|
async function shiftPhasesUp(db, itemId2, fromIdx) {
|
|
14196
|
-
const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and9(
|
|
14298
|
+
const toShift = await db.select({ idx: planPhases.idx }).from(planPhases).where(and9(eq24(planPhases.itemId, itemId2), gte(planPhases.idx, fromIdx))).orderBy(desc4(planPhases.idx));
|
|
14197
14299
|
for (const p of toShift) {
|
|
14198
|
-
await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and9(
|
|
14199
|
-
await db.update(planPhases).set({ idx: p.idx + 1 }).where(and9(
|
|
14300
|
+
await db.update(planTasks).set({ phaseIdx: p.idx + 1 }).where(and9(eq24(planTasks.itemId, itemId2), eq24(planTasks.phaseIdx, p.idx)));
|
|
14301
|
+
await db.update(planPhases).set({ idx: p.idx + 1 }).where(and9(eq24(planPhases.itemId, itemId2), eq24(planPhases.idx, p.idx)));
|
|
14200
14302
|
}
|
|
14201
14303
|
}
|
|
14202
14304
|
|
|
@@ -14209,17 +14311,17 @@ async function insertPhaseAt(orm, itemId2, phaseIdx, name, tasks, manualChecks,
|
|
|
14209
14311
|
await tx.insert(planTasks).values(tasks.map((task, i) => ({ itemId: itemId2, phaseIdx, idx: i, task })));
|
|
14210
14312
|
}
|
|
14211
14313
|
if (currentPhase !== void 0 && currentPhase - 1 >= phaseIdx) {
|
|
14212
|
-
await tx.update(items).set({ currentPhase: phaseIdx + 1 }).where(
|
|
14314
|
+
await tx.update(items).set({ currentPhase: phaseIdx + 1 }).where(eq25(items.id, itemId2));
|
|
14213
14315
|
}
|
|
14214
14316
|
});
|
|
14215
14317
|
}
|
|
14216
14318
|
|
|
14217
14319
|
// src/commands/backlog/insertSubtask.ts
|
|
14218
|
-
import { sql as
|
|
14320
|
+
import { sql as sql20 } from "drizzle-orm";
|
|
14219
14321
|
async function insertSubtask(orm, itemId2, title, description) {
|
|
14220
14322
|
await orm.insert(itemSubtasks).values({
|
|
14221
14323
|
itemId: itemId2,
|
|
14222
|
-
idx:
|
|
14324
|
+
idx: sql20`(SELECT COALESCE(MAX(${itemSubtasks.idx}) + 1, 0) FROM ${itemSubtasks} WHERE ${itemSubtasks.itemId} = ${itemId2})`,
|
|
14223
14325
|
title,
|
|
14224
14326
|
description: description ?? null,
|
|
14225
14327
|
status: "todo"
|
|
@@ -14381,9 +14483,9 @@ import chalk78 from "chalk";
|
|
|
14381
14483
|
|
|
14382
14484
|
// src/commands/backlog/resolveInsertPosition.ts
|
|
14383
14485
|
import chalk77 from "chalk";
|
|
14384
|
-
import { count as count3, eq as
|
|
14486
|
+
import { count as count3, eq as eq26 } from "drizzle-orm";
|
|
14385
14487
|
async function resolveInsertPosition(orm, itemId2, position) {
|
|
14386
|
-
const [row] = await orm.select({ cnt: count3() }).from(planPhases).where(
|
|
14488
|
+
const [row] = await orm.select({ cnt: count3() }).from(planPhases).where(eq26(planPhases.itemId, itemId2));
|
|
14387
14489
|
const phaseCount = row?.cnt ?? 0;
|
|
14388
14490
|
if (position === void 0) return phaseCount;
|
|
14389
14491
|
const pos = Number.parseInt(position, 10);
|
|
@@ -14678,9 +14780,9 @@ function hasCycle(adjacency, fromId, toId) {
|
|
|
14678
14780
|
}
|
|
14679
14781
|
|
|
14680
14782
|
// src/commands/backlog/loadDependencyGraph.ts
|
|
14681
|
-
import { eq as
|
|
14783
|
+
import { eq as eq27 } from "drizzle-orm";
|
|
14682
14784
|
async function loadDependencyGraph(orm) {
|
|
14683
|
-
const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(
|
|
14785
|
+
const rows = await orm.select({ itemId: links.itemId, targetId: links.targetId }).from(links).where(eq27(links.type, "depends-on"));
|
|
14684
14786
|
const graph = /* @__PURE__ */ new Map();
|
|
14685
14787
|
for (const { itemId: itemId2, targetId } of rows) {
|
|
14686
14788
|
const bucket = graph.get(itemId2);
|
|
@@ -14747,7 +14849,7 @@ async function link(fromId, toId, opts) {
|
|
|
14747
14849
|
|
|
14748
14850
|
// src/commands/backlog/unlink.ts
|
|
14749
14851
|
import chalk85 from "chalk";
|
|
14750
|
-
import { and as and10, eq as
|
|
14852
|
+
import { and as and10, eq as eq28 } from "drizzle-orm";
|
|
14751
14853
|
async function unlink(fromId, toId) {
|
|
14752
14854
|
const fromNum = parseItemId(fromId);
|
|
14753
14855
|
const toNum = parseItemId(toId);
|
|
@@ -14771,7 +14873,7 @@ async function unlink(fromId, toId) {
|
|
|
14771
14873
|
);
|
|
14772
14874
|
return;
|
|
14773
14875
|
}
|
|
14774
|
-
await orm.delete(links).where(and10(
|
|
14876
|
+
await orm.delete(links).where(and10(eq28(links.itemId, fromNum), eq28(links.targetId, toNum)));
|
|
14775
14877
|
console.log(
|
|
14776
14878
|
chalk85.green(
|
|
14777
14879
|
`Removed link from ${formatItemId(fromNum)} to ${formatItemId(toNum)}.`
|
|
@@ -14791,7 +14893,7 @@ function registerLinkCommands(cmd) {
|
|
|
14791
14893
|
|
|
14792
14894
|
// src/commands/backlog/move-repo/index.ts
|
|
14793
14895
|
import chalk87 from "chalk";
|
|
14794
|
-
import { eq as
|
|
14896
|
+
import { eq as eq30 } from "drizzle-orm";
|
|
14795
14897
|
|
|
14796
14898
|
// src/commands/backlog/move-repo/confirmMove.ts
|
|
14797
14899
|
import chalk86 from "chalk";
|
|
@@ -14806,9 +14908,9 @@ async function confirmMove(cnt, oldOrigin, newOrigin) {
|
|
|
14806
14908
|
}
|
|
14807
14909
|
|
|
14808
14910
|
// src/commands/backlog/move-repo/countByOrigin.ts
|
|
14809
|
-
import { count as count4, eq as
|
|
14911
|
+
import { count as count4, eq as eq29 } from "drizzle-orm";
|
|
14810
14912
|
async function countByOrigin(orm, origin) {
|
|
14811
|
-
const [{ cnt }] = await orm.select({ cnt: count4() }).from(items).where(
|
|
14913
|
+
const [{ cnt }] = await orm.select({ cnt: count4() }).from(items).where(eq29(items.origin, origin));
|
|
14812
14914
|
return cnt;
|
|
14813
14915
|
}
|
|
14814
14916
|
|
|
@@ -14851,7 +14953,7 @@ async function moveRepo(oldOriginRaw, newOriginRaw, options2 = {}) {
|
|
|
14851
14953
|
console.log(chalk87.yellow("Move cancelled; no changes made."));
|
|
14852
14954
|
return;
|
|
14853
14955
|
}
|
|
14854
|
-
await orm.update(items).set({ origin: newOrigin }).where(
|
|
14956
|
+
await orm.update(items).set({ origin: newOrigin }).where(eq30(items.origin, oldOrigin));
|
|
14855
14957
|
console.log(
|
|
14856
14958
|
chalk87.green(
|
|
14857
14959
|
`Moved ${pluralItems(cnt)} from "${oldOrigin}" to "${newOrigin}".`
|
|
@@ -15251,10 +15353,10 @@ async function start(id) {
|
|
|
15251
15353
|
|
|
15252
15354
|
// src/commands/backlog/stop/index.ts
|
|
15253
15355
|
import chalk99 from "chalk";
|
|
15254
|
-
import { and as and11, eq as
|
|
15356
|
+
import { and as and11, eq as eq31 } from "drizzle-orm";
|
|
15255
15357
|
async function stop() {
|
|
15256
15358
|
const { orm } = await getReady();
|
|
15257
|
-
const stopped = await orm.update(items).set({ status: "todo", currentPhase: 1 }).where(and11(
|
|
15359
|
+
const stopped = await orm.update(items).set({ status: "todo", currentPhase: 1 }).where(and11(eq31(items.status, "in-progress"), eq31(items.origin, getOrigin()))).returning({ id: items.id, name: items.name });
|
|
15258
15360
|
if (stopped.length === 0) {
|
|
15259
15361
|
console.log(chalk99.yellow("No in-progress items to stop."));
|
|
15260
15362
|
return;
|
|
@@ -15371,13 +15473,13 @@ async function findSubtask(id, index3) {
|
|
|
15371
15473
|
}
|
|
15372
15474
|
|
|
15373
15475
|
// src/commands/backlog/updateSubtask.ts
|
|
15374
|
-
import { and as and12, eq as
|
|
15476
|
+
import { and as and12, eq as eq32 } from "drizzle-orm";
|
|
15375
15477
|
async function updateSubtask(orm, itemId2, idx, fields) {
|
|
15376
15478
|
const set = {};
|
|
15377
15479
|
if (fields.title !== void 0) set.title = fields.title;
|
|
15378
15480
|
if (fields.description !== void 0) set.description = fields.description;
|
|
15379
15481
|
if (fields.status !== void 0) set.status = fields.status;
|
|
15380
|
-
const [row] = await orm.update(itemSubtasks).set(set).where(and12(
|
|
15482
|
+
const [row] = await orm.update(itemSubtasks).set(set).where(and12(eq32(itemSubtasks.itemId, itemId2), eq32(itemSubtasks.idx, idx))).returning({ title: itemSubtasks.title });
|
|
15381
15483
|
return row?.title;
|
|
15382
15484
|
}
|
|
15383
15485
|
|
|
@@ -15432,16 +15534,16 @@ async function editSubtask(id, index3, options2) {
|
|
|
15432
15534
|
import chalk105 from "chalk";
|
|
15433
15535
|
|
|
15434
15536
|
// src/commands/backlog/deleteSubtask.ts
|
|
15435
|
-
import { and as and13, asc as asc8, eq as
|
|
15537
|
+
import { and as and13, asc as asc8, eq as eq33 } from "drizzle-orm";
|
|
15436
15538
|
async function deleteSubtask(orm, itemId2, idx) {
|
|
15437
|
-
const [row] = await orm.delete(itemSubtasks).where(and13(
|
|
15539
|
+
const [row] = await orm.delete(itemSubtasks).where(and13(eq33(itemSubtasks.itemId, itemId2), eq33(itemSubtasks.idx, idx))).returning({ title: itemSubtasks.title });
|
|
15438
15540
|
if (!row) return void 0;
|
|
15439
|
-
const remaining = await orm.select({ idx: itemSubtasks.idx }).from(itemSubtasks).where(
|
|
15541
|
+
const remaining = await orm.select({ idx: itemSubtasks.idx }).from(itemSubtasks).where(eq33(itemSubtasks.itemId, itemId2)).orderBy(asc8(itemSubtasks.idx));
|
|
15440
15542
|
for (let i = 0; i < remaining.length; i++) {
|
|
15441
15543
|
const oldIdx = remaining[i].idx;
|
|
15442
15544
|
if (oldIdx === i) continue;
|
|
15443
15545
|
await orm.update(itemSubtasks).set({ idx: i }).where(
|
|
15444
|
-
and13(
|
|
15546
|
+
and13(eq33(itemSubtasks.itemId, itemId2), eq33(itemSubtasks.idx, oldIdx))
|
|
15445
15547
|
);
|
|
15446
15548
|
}
|
|
15447
15549
|
return row.title;
|
|
@@ -15510,20 +15612,20 @@ function registerSubtaskCommands(cmd) {
|
|
|
15510
15612
|
|
|
15511
15613
|
// src/commands/backlog/movePhase.ts
|
|
15512
15614
|
import chalk107 from "chalk";
|
|
15513
|
-
import { count as count6, eq as
|
|
15615
|
+
import { count as count6, eq as eq36 } from "drizzle-orm";
|
|
15514
15616
|
|
|
15515
15617
|
// src/commands/backlog/reorderPhaseRows.ts
|
|
15516
|
-
import { and as and15, asc as asc10, eq as
|
|
15618
|
+
import { and as and15, asc as asc10, eq as eq35 } from "drizzle-orm";
|
|
15517
15619
|
|
|
15518
15620
|
// src/commands/backlog/reindexPhases.ts
|
|
15519
|
-
import { and as and14, asc as asc9, count as count5, eq as
|
|
15621
|
+
import { and as and14, asc as asc9, count as count5, eq as eq34 } from "drizzle-orm";
|
|
15520
15622
|
async function reindexPhases(db, itemId2) {
|
|
15521
|
-
const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(
|
|
15623
|
+
const remaining = await db.select({ idx: planPhases.idx }).from(planPhases).where(eq34(planPhases.itemId, itemId2)).orderBy(asc9(planPhases.idx));
|
|
15522
15624
|
for (let i = 0; i < remaining.length; i++) {
|
|
15523
15625
|
const oldIdx = remaining[i].idx;
|
|
15524
15626
|
if (oldIdx === i) continue;
|
|
15525
|
-
await db.update(planTasks).set({ phaseIdx: i }).where(and14(
|
|
15526
|
-
await db.update(planPhases).set({ idx: i }).where(and14(
|
|
15627
|
+
await db.update(planTasks).set({ phaseIdx: i }).where(and14(eq34(planTasks.itemId, itemId2), eq34(planTasks.phaseIdx, oldIdx)));
|
|
15628
|
+
await db.update(planPhases).set({ idx: i }).where(and14(eq34(planPhases.itemId, itemId2), eq34(planPhases.idx, oldIdx)));
|
|
15527
15629
|
}
|
|
15528
15630
|
}
|
|
15529
15631
|
async function adjustCurrentPhase(db, item, removedIdx) {
|
|
@@ -15531,24 +15633,24 @@ async function adjustCurrentPhase(db, item, removedIdx) {
|
|
|
15531
15633
|
if (currentPhase === void 0) return;
|
|
15532
15634
|
const currentIdx = currentPhase - 1;
|
|
15533
15635
|
if (removedIdx < currentIdx) {
|
|
15534
|
-
await db.update(items).set({ currentPhase: currentPhase - 1 }).where(
|
|
15636
|
+
await db.update(items).set({ currentPhase: currentPhase - 1 }).where(eq34(items.id, item.id));
|
|
15535
15637
|
return;
|
|
15536
15638
|
}
|
|
15537
15639
|
if (removedIdx !== currentIdx) return;
|
|
15538
|
-
const [row] = await db.select({ cnt: count5() }).from(planPhases).where(
|
|
15640
|
+
const [row] = await db.select({ cnt: count5() }).from(planPhases).where(eq34(planPhases.itemId, item.id));
|
|
15539
15641
|
const cnt = row?.cnt ?? 0;
|
|
15540
|
-
await db.update(items).set({ currentPhase: cnt === 0 ? null : Math.min(currentPhase, cnt) }).where(
|
|
15642
|
+
await db.update(items).set({ currentPhase: cnt === 0 ? null : Math.min(currentPhase, cnt) }).where(eq34(items.id, item.id));
|
|
15541
15643
|
}
|
|
15542
15644
|
|
|
15543
15645
|
// src/commands/backlog/reorderPhaseRows.ts
|
|
15544
15646
|
async function reorderPhaseRows(orm, itemId2, fromIdx, toIdx) {
|
|
15545
15647
|
await orm.transaction(async (tx) => {
|
|
15546
|
-
const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(and15(
|
|
15547
|
-
const tasks = await tx.select({ idx: planTasks.idx, task: planTasks.task }).from(planTasks).where(and15(
|
|
15648
|
+
const [phaseRow] = await tx.select({ name: planPhases.name, manualChecks: planPhases.manualChecks }).from(planPhases).where(and15(eq35(planPhases.itemId, itemId2), eq35(planPhases.idx, fromIdx)));
|
|
15649
|
+
const tasks = await tx.select({ idx: planTasks.idx, task: planTasks.task }).from(planTasks).where(and15(eq35(planTasks.itemId, itemId2), eq35(planTasks.phaseIdx, fromIdx))).orderBy(asc10(planTasks.idx));
|
|
15548
15650
|
await tx.delete(planTasks).where(
|
|
15549
|
-
and15(
|
|
15651
|
+
and15(eq35(planTasks.itemId, itemId2), eq35(planTasks.phaseIdx, fromIdx))
|
|
15550
15652
|
);
|
|
15551
|
-
await tx.delete(planPhases).where(and15(
|
|
15653
|
+
await tx.delete(planPhases).where(and15(eq35(planPhases.itemId, itemId2), eq35(planPhases.idx, fromIdx)));
|
|
15552
15654
|
await reindexPhases(tx, itemId2);
|
|
15553
15655
|
await shiftPhasesUp(tx, itemId2, toIdx);
|
|
15554
15656
|
await tx.insert(planPhases).values({
|
|
@@ -15589,7 +15691,7 @@ async function movePhase(id, from, to) {
|
|
|
15589
15691
|
if (!found) return;
|
|
15590
15692
|
const { orm, item } = found;
|
|
15591
15693
|
const itemId2 = item.id;
|
|
15592
|
-
const [row] = await orm.select({ cnt: count6() }).from(planPhases).where(
|
|
15694
|
+
const [row] = await orm.select({ cnt: count6() }).from(planPhases).where(eq36(planPhases.itemId, itemId2));
|
|
15593
15695
|
const phaseCount = row?.cnt ?? 0;
|
|
15594
15696
|
const fromIdx = toIndex2(from, phaseCount);
|
|
15595
15697
|
if (fromIdx === void 0) return;
|
|
@@ -15605,22 +15707,22 @@ async function movePhase(id, from, to) {
|
|
|
15605
15707
|
import chalk109 from "chalk";
|
|
15606
15708
|
|
|
15607
15709
|
// src/commands/backlog/applyPhaseUpdate.ts
|
|
15608
|
-
import { and as and16, eq as
|
|
15710
|
+
import { and as and16, eq as eq37 } from "drizzle-orm";
|
|
15609
15711
|
async function applyPhaseUpdate(orm, itemId2, phaseIdx, fields) {
|
|
15610
15712
|
await orm.transaction(async (tx) => {
|
|
15611
15713
|
if (fields.name) {
|
|
15612
15714
|
await tx.update(planPhases).set({ name: fields.name }).where(
|
|
15613
|
-
and16(
|
|
15715
|
+
and16(eq37(planPhases.itemId, itemId2), eq37(planPhases.idx, phaseIdx))
|
|
15614
15716
|
);
|
|
15615
15717
|
}
|
|
15616
15718
|
if (fields.manualCheck) {
|
|
15617
15719
|
await tx.update(planPhases).set({ manualChecks: JSON.stringify(fields.manualCheck) }).where(
|
|
15618
|
-
and16(
|
|
15720
|
+
and16(eq37(planPhases.itemId, itemId2), eq37(planPhases.idx, phaseIdx))
|
|
15619
15721
|
);
|
|
15620
15722
|
}
|
|
15621
15723
|
if (fields.task) {
|
|
15622
15724
|
await tx.delete(planTasks).where(
|
|
15623
|
-
and16(
|
|
15725
|
+
and16(eq37(planTasks.itemId, itemId2), eq37(planTasks.phaseIdx, phaseIdx))
|
|
15624
15726
|
);
|
|
15625
15727
|
if (fields.task.length) {
|
|
15626
15728
|
await tx.insert(planTasks).values(
|
|
@@ -15638,14 +15740,14 @@ async function applyPhaseUpdate(orm, itemId2, phaseIdx, fields) {
|
|
|
15638
15740
|
|
|
15639
15741
|
// src/commands/backlog/findPhase.ts
|
|
15640
15742
|
import chalk108 from "chalk";
|
|
15641
|
-
import { and as and17, count as count7, eq as
|
|
15743
|
+
import { and as and17, count as count7, eq as eq38 } from "drizzle-orm";
|
|
15642
15744
|
async function findPhase(id, phase) {
|
|
15643
15745
|
const found = await findOneItem(id);
|
|
15644
15746
|
if (!found) return void 0;
|
|
15645
15747
|
const { orm, item } = found;
|
|
15646
15748
|
const itemId2 = item.id;
|
|
15647
15749
|
const phaseIdx = Number.parseInt(phase, 10) - 1;
|
|
15648
|
-
const [row] = await orm.select({ cnt: count7() }).from(planPhases).where(and17(
|
|
15750
|
+
const [row] = await orm.select({ cnt: count7() }).from(planPhases).where(and17(eq38(planPhases.itemId, itemId2), eq38(planPhases.idx, phaseIdx)));
|
|
15649
15751
|
if (!row || row.cnt === 0) {
|
|
15650
15752
|
console.log(
|
|
15651
15753
|
chalk108.red(
|
|
@@ -15818,16 +15920,16 @@ function registerUpdatePhaseCommand(cmd) {
|
|
|
15818
15920
|
|
|
15819
15921
|
// src/commands/backlog/removePhase.ts
|
|
15820
15922
|
import chalk110 from "chalk";
|
|
15821
|
-
import { and as and18, eq as
|
|
15923
|
+
import { and as and18, eq as eq39 } from "drizzle-orm";
|
|
15822
15924
|
async function removePhase(id, phase) {
|
|
15823
15925
|
const found = await findPhase(id, phase);
|
|
15824
15926
|
if (!found) return;
|
|
15825
15927
|
const { item, orm, itemId: itemId2, phaseIdx } = found;
|
|
15826
15928
|
await orm.transaction(async (tx) => {
|
|
15827
15929
|
await tx.delete(planTasks).where(
|
|
15828
|
-
and18(
|
|
15930
|
+
and18(eq39(planTasks.itemId, itemId2), eq39(planTasks.phaseIdx, phaseIdx))
|
|
15829
15931
|
);
|
|
15830
|
-
await tx.delete(planPhases).where(and18(
|
|
15932
|
+
await tx.delete(planPhases).where(and18(eq39(planPhases.itemId, itemId2), eq39(planPhases.idx, phaseIdx)));
|
|
15831
15933
|
await reindexPhases(tx, itemId2);
|
|
15832
15934
|
await adjustCurrentPhase(tx, item, phaseIdx);
|
|
15833
15935
|
});
|
|
@@ -15840,13 +15942,13 @@ async function removePhase(id, phase) {
|
|
|
15840
15942
|
|
|
15841
15943
|
// src/commands/backlog/update/update.ts
|
|
15842
15944
|
import chalk114 from "chalk";
|
|
15843
|
-
import { eq as
|
|
15945
|
+
import { eq as eq40 } from "drizzle-orm";
|
|
15844
15946
|
|
|
15845
15947
|
// src/commands/backlog/update/buildUpdateValues.ts
|
|
15846
15948
|
import chalk111 from "chalk";
|
|
15847
15949
|
function buildUpdateValues(options2) {
|
|
15848
|
-
const { name, desc:
|
|
15849
|
-
if (!name && !
|
|
15950
|
+
const { name, desc: desc7, type, ac, origin } = options2;
|
|
15951
|
+
if (!name && !desc7 && !type && !ac && !origin) {
|
|
15850
15952
|
console.log(chalk111.red("Nothing to update. Provide at least one flag."));
|
|
15851
15953
|
process.exitCode = 1;
|
|
15852
15954
|
return void 0;
|
|
@@ -15862,8 +15964,8 @@ function buildUpdateValues(options2) {
|
|
|
15862
15964
|
set.name = name;
|
|
15863
15965
|
fieldNames.push("name");
|
|
15864
15966
|
}
|
|
15865
|
-
if (
|
|
15866
|
-
set.description =
|
|
15967
|
+
if (desc7) {
|
|
15968
|
+
set.description = desc7.replaceAll(String.raw`\n`, "\n");
|
|
15867
15969
|
fieldNames.push("description");
|
|
15868
15970
|
}
|
|
15869
15971
|
if (type) {
|
|
@@ -15951,7 +16053,7 @@ async function update(id, options2) {
|
|
|
15951
16053
|
if (!built) return;
|
|
15952
16054
|
const { orm } = found;
|
|
15953
16055
|
const itemId2 = found.item.id;
|
|
15954
|
-
await orm.update(items).set(built.set).where(
|
|
16056
|
+
await orm.update(items).set(built.set).where(eq40(items.id, itemId2));
|
|
15955
16057
|
console.log(
|
|
15956
16058
|
chalk114.green(`Updated ${built.fields} on item ${formatItemId(itemId2)}.`)
|
|
15957
16059
|
);
|
|
@@ -15972,7 +16074,7 @@ function readPlanUpdate(source) {
|
|
|
15972
16074
|
}
|
|
15973
16075
|
|
|
15974
16076
|
// src/commands/backlog/updatePlan/replacePlan.ts
|
|
15975
|
-
import { eq as
|
|
16077
|
+
import { eq as eq41 } from "drizzle-orm";
|
|
15976
16078
|
|
|
15977
16079
|
// src/commands/backlog/updatePlan/clampCurrentPhase.ts
|
|
15978
16080
|
function clampCurrentPhase(currentPhase, phaseCount) {
|
|
@@ -15983,8 +16085,8 @@ function clampCurrentPhase(currentPhase, phaseCount) {
|
|
|
15983
16085
|
// src/commands/backlog/updatePlan/replacePlan.ts
|
|
15984
16086
|
async function replacePlan(orm, itemId2, phases, currentPhase) {
|
|
15985
16087
|
await orm.transaction(async (tx) => {
|
|
15986
|
-
await tx.delete(planTasks).where(
|
|
15987
|
-
await tx.delete(planPhases).where(
|
|
16088
|
+
await tx.delete(planTasks).where(eq41(planTasks.itemId, itemId2));
|
|
16089
|
+
await tx.delete(planPhases).where(eq41(planPhases.itemId, itemId2));
|
|
15988
16090
|
if (phases.length > 0) {
|
|
15989
16091
|
await tx.insert(planPhases).values(
|
|
15990
16092
|
phases.map((phase, idx) => ({
|
|
@@ -16002,7 +16104,7 @@ async function replacePlan(orm, itemId2, phases, currentPhase) {
|
|
|
16002
16104
|
if (currentPhase === void 0) return;
|
|
16003
16105
|
const clamped = clampCurrentPhase(currentPhase, phases.length);
|
|
16004
16106
|
if (clamped === currentPhase) return;
|
|
16005
|
-
await tx.update(items).set({ currentPhase: clamped }).where(
|
|
16107
|
+
await tx.update(items).set({ currentPhase: clamped }).where(eq41(items.id, itemId2));
|
|
16006
16108
|
});
|
|
16007
16109
|
}
|
|
16008
16110
|
|
|
@@ -22767,9 +22869,9 @@ function registerGithub(program2) {
|
|
|
22767
22869
|
}
|
|
22768
22870
|
|
|
22769
22871
|
// src/commands/handover/countPendingHandovers.ts
|
|
22770
|
-
import { and as and19, eq as
|
|
22872
|
+
import { and as and19, eq as eq42, isNull, sql as sql21 } from "drizzle-orm";
|
|
22771
22873
|
async function countPendingHandovers(orm, origin) {
|
|
22772
|
-
const [row] = await orm.select({ count:
|
|
22874
|
+
const [row] = await orm.select({ count: sql21`count(*)::int` }).from(handovers).where(and19(eq42(handovers.origin, origin), isNull(handovers.recalledAt)));
|
|
22773
22875
|
return row?.count ?? 0;
|
|
22774
22876
|
}
|
|
22775
22877
|
|
|
@@ -22909,13 +23011,13 @@ async function load2(options2 = {}) {
|
|
|
22909
23011
|
}
|
|
22910
23012
|
|
|
22911
23013
|
// src/commands/handover/listPendingHandovers.ts
|
|
22912
|
-
import { and as and20, desc as
|
|
23014
|
+
import { and as and20, desc as desc5, eq as eq43, isNull as isNull2 } from "drizzle-orm";
|
|
22913
23015
|
async function listPendingHandovers(orm, origin) {
|
|
22914
23016
|
return orm.select({
|
|
22915
23017
|
id: handovers.id,
|
|
22916
23018
|
summary: handovers.summary,
|
|
22917
23019
|
createdAt: handovers.createdAt
|
|
22918
|
-
}).from(handovers).where(and20(
|
|
23020
|
+
}).from(handovers).where(and20(eq43(handovers.origin, origin), isNull2(handovers.recalledAt))).orderBy(desc5(handovers.createdAt), desc5(handovers.id));
|
|
22919
23021
|
}
|
|
22920
23022
|
|
|
22921
23023
|
// src/commands/handover/printPendingHandovers.ts
|
|
@@ -22928,17 +23030,17 @@ async function printPendingHandovers() {
|
|
|
22928
23030
|
}
|
|
22929
23031
|
|
|
22930
23032
|
// src/commands/handover/recallHandover.ts
|
|
22931
|
-
import { and as and21, desc as
|
|
23033
|
+
import { and as and21, desc as desc6, eq as eq44, isNull as isNull3 } from "drizzle-orm";
|
|
22932
23034
|
async function recallHandover(orm, origin, id) {
|
|
22933
23035
|
const [row] = await orm.select().from(handovers).where(
|
|
22934
23036
|
and21(
|
|
22935
|
-
|
|
23037
|
+
eq44(handovers.origin, origin),
|
|
22936
23038
|
isNull3(handovers.recalledAt),
|
|
22937
|
-
...id === void 0 ? [] : [
|
|
23039
|
+
...id === void 0 ? [] : [eq44(handovers.id, id)]
|
|
22938
23040
|
)
|
|
22939
|
-
).orderBy(
|
|
23041
|
+
).orderBy(desc6(handovers.createdAt), desc6(handovers.id)).limit(1);
|
|
22940
23042
|
if (!row) return void 0;
|
|
22941
|
-
await orm.update(handovers).set({ recalledAt: /* @__PURE__ */ new Date() }).where(
|
|
23043
|
+
await orm.update(handovers).set({ recalledAt: /* @__PURE__ */ new Date() }).where(eq44(handovers.id, row.id));
|
|
22942
23044
|
return row.content;
|
|
22943
23045
|
}
|
|
22944
23046
|
|
|
@@ -26610,155 +26712,6 @@ async function raise(options2, command) {
|
|
|
26610
26712
|
await placePr(existing, title, body, resolved);
|
|
26611
26713
|
}
|
|
26612
26714
|
|
|
26613
|
-
// src/commands/prs/readTime.ts
|
|
26614
|
-
import { readFileSync as readFileSync45 } from "fs";
|
|
26615
|
-
|
|
26616
|
-
// src/commands/prs/countReadingWords.ts
|
|
26617
|
-
var FENCE_PATTERN = /^\s*(```|~~~)/;
|
|
26618
|
-
var IMAGE_PATTERN = /!\[[^\]]*\]\([^)]*\)/g;
|
|
26619
|
-
var HTML_TAG_PATTERN = /<[^\s>][^>]*>/g;
|
|
26620
|
-
var URL_PATTERN3 = /https?:\/\/\S+/g;
|
|
26621
|
-
var SINGLE_WORD_PATTERNS = [IMAGE_PATTERN, HTML_TAG_PATTERN, URL_PATTERN3];
|
|
26622
|
-
function countReadingWords(body) {
|
|
26623
|
-
let prose = 0;
|
|
26624
|
-
let code = 0;
|
|
26625
|
-
let inCode = false;
|
|
26626
|
-
for (const line of body.split("\n")) {
|
|
26627
|
-
if (FENCE_PATTERN.test(line)) {
|
|
26628
|
-
inCode = !inCode;
|
|
26629
|
-
} else if (inCode) {
|
|
26630
|
-
code += countTokens(line);
|
|
26631
|
-
} else {
|
|
26632
|
-
prose += countProseWords(line);
|
|
26633
|
-
}
|
|
26634
|
-
}
|
|
26635
|
-
return { prose, code };
|
|
26636
|
-
}
|
|
26637
|
-
function countProseWords(line) {
|
|
26638
|
-
let singles = 0;
|
|
26639
|
-
let remaining = line;
|
|
26640
|
-
for (const pattern2 of SINGLE_WORD_PATTERNS) {
|
|
26641
|
-
remaining = remaining.replace(pattern2, () => {
|
|
26642
|
-
singles += 1;
|
|
26643
|
-
return " ";
|
|
26644
|
-
});
|
|
26645
|
-
}
|
|
26646
|
-
return singles + countTokens(remaining);
|
|
26647
|
-
}
|
|
26648
|
-
function countTokens(text18) {
|
|
26649
|
-
return text18.split(/\s+/).filter((token) => /[A-Za-z0-9]/.test(token)).length;
|
|
26650
|
-
}
|
|
26651
|
-
|
|
26652
|
-
// src/commands/prs/formatReadDuration.ts
|
|
26653
|
-
function formatReadDuration(seconds) {
|
|
26654
|
-
if (seconds < 60) return `${seconds}s`;
|
|
26655
|
-
const minutes = Math.floor(seconds / 60);
|
|
26656
|
-
const remainder = seconds % 60;
|
|
26657
|
-
return remainder === 0 ? `${minutes}m` : `${minutes}m ${remainder}s`;
|
|
26658
|
-
}
|
|
26659
|
-
|
|
26660
|
-
// src/commands/prs/parseReadBudget.ts
|
|
26661
|
-
var BUDGET_PATTERN = /^(?:(\d+)m)?(?:(\d+)s)?$/;
|
|
26662
|
-
function parseReadBudget(value) {
|
|
26663
|
-
const match = BUDGET_PATTERN.exec(value.trim());
|
|
26664
|
-
const minutes = match?.[1];
|
|
26665
|
-
const seconds = match?.[2];
|
|
26666
|
-
if (minutes === void 0 && seconds === void 0) {
|
|
26667
|
-
throw new Error(
|
|
26668
|
-
`Invalid budget "${value}". Use a duration like 45s, 1m30s or 2m.`
|
|
26669
|
-
);
|
|
26670
|
-
}
|
|
26671
|
-
const total = Number(minutes ?? 0) * 60 + Number(seconds ?? 0);
|
|
26672
|
-
if (total === 0) {
|
|
26673
|
-
throw new Error(`Invalid budget "${value}". Value must be at least 1s.`);
|
|
26674
|
-
}
|
|
26675
|
-
return total;
|
|
26676
|
-
}
|
|
26677
|
-
|
|
26678
|
-
// src/commands/prs/resolveReadTimeTarget.ts
|
|
26679
|
-
var PR_URL_PATTERN = /^(https:\/\/github\.com\/[^/\s]+\/[^/\s]+)\/pull\/(\d+)(?:[/?#].*)?$/;
|
|
26680
|
-
function resolveReadTimeTarget(target) {
|
|
26681
|
-
const trimmed = target.trim();
|
|
26682
|
-
if (trimmed === "-") return { kind: "stdin" };
|
|
26683
|
-
if (/^\d+$/.test(trimmed)) {
|
|
26684
|
-
return { kind: "pr", number: Number(trimmed), repo: null };
|
|
26685
|
-
}
|
|
26686
|
-
const url = PR_URL_PATTERN.exec(trimmed);
|
|
26687
|
-
if (url) {
|
|
26688
|
-
const repo = parseGitHubUrl(url[1]);
|
|
26689
|
-
if (repo) return { kind: "pr", number: Number(url[2]), repo };
|
|
26690
|
-
}
|
|
26691
|
-
return { kind: "file", path: target };
|
|
26692
|
-
}
|
|
26693
|
-
|
|
26694
|
-
// src/commands/prs/fetchPrBody.ts
|
|
26695
|
-
import { execSync as execSync51 } from "child_process";
|
|
26696
|
-
function fetchPrBody(number, repo) {
|
|
26697
|
-
const { org, repo: name } = repo ?? getRepoInfo();
|
|
26698
|
-
try {
|
|
26699
|
-
const raw = execSync51(`gh pr view ${number} --json body -R ${org}/${name}`, {
|
|
26700
|
-
encoding: "utf8"
|
|
26701
|
-
});
|
|
26702
|
-
return JSON.parse(raw).body ?? "";
|
|
26703
|
-
} catch (error) {
|
|
26704
|
-
if (isGhNotInstalled(error)) {
|
|
26705
|
-
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
26706
|
-
console.error("Install it from https://cli.github.com/");
|
|
26707
|
-
process.exit(1);
|
|
26708
|
-
}
|
|
26709
|
-
if (isNotFound(error)) {
|
|
26710
|
-
console.error(`Error: Pull request ${org}/${name}#${number} not found.`);
|
|
26711
|
-
process.exit(1);
|
|
26712
|
-
}
|
|
26713
|
-
throw error;
|
|
26714
|
-
}
|
|
26715
|
-
}
|
|
26716
|
-
|
|
26717
|
-
// src/commands/prs/readTime.ts
|
|
26718
|
-
var DEFAULT_WORDS_PER_MINUTE = 80;
|
|
26719
|
-
var DEFAULT_BUDGET_SECONDS = 60;
|
|
26720
|
-
var CODE_WORDS_PER_PROSE_WORD = 2;
|
|
26721
|
-
async function readTime(target, options2 = {}) {
|
|
26722
|
-
const budgetSeconds = resolveBudget(options2.budget);
|
|
26723
|
-
const body = await loadBody(resolveReadTimeTarget(target));
|
|
26724
|
-
const { prose, code } = countReadingWords(body);
|
|
26725
|
-
const words = prose + code;
|
|
26726
|
-
const wordsPerMinute = loadConfig().prs?.readingWordsPerMinute ?? DEFAULT_WORDS_PER_MINUTE;
|
|
26727
|
-
const seconds = Math.round(
|
|
26728
|
-
(prose + code * CODE_WORDS_PER_PROSE_WORD) / wordsPerMinute * 60
|
|
26729
|
-
);
|
|
26730
|
-
const label2 = words === 1 ? "word" : "words";
|
|
26731
|
-
const verdict = seconds > budgetSeconds ? ` \xB7 over the ~${formatReadDuration(budgetSeconds)} budget` : "";
|
|
26732
|
-
console.log(
|
|
26733
|
-
`${words} ${label2} \xB7 ~${formatReadDuration(seconds)} read${verdict}`
|
|
26734
|
-
);
|
|
26735
|
-
}
|
|
26736
|
-
function resolveBudget(budget) {
|
|
26737
|
-
if (budget === void 0) return DEFAULT_BUDGET_SECONDS;
|
|
26738
|
-
try {
|
|
26739
|
-
return parseReadBudget(budget);
|
|
26740
|
-
} catch (error) {
|
|
26741
|
-
console.error(`Error: ${error.message}`);
|
|
26742
|
-
process.exit(1);
|
|
26743
|
-
}
|
|
26744
|
-
}
|
|
26745
|
-
async function loadBody(target) {
|
|
26746
|
-
if (target.kind === "stdin") return readBodyArgument("-");
|
|
26747
|
-
if (target.kind === "file") return readDraftFile(target.path);
|
|
26748
|
-
return fetchPrBody(target.number, target.repo);
|
|
26749
|
-
}
|
|
26750
|
-
function readDraftFile(path90) {
|
|
26751
|
-
try {
|
|
26752
|
-
return readFileSync45(path90, "utf8");
|
|
26753
|
-
} catch {
|
|
26754
|
-
console.error(`Error: Could not read \`${path90}\`.`);
|
|
26755
|
-
console.error(
|
|
26756
|
-
"Pass a pull request number, a GitHub pull request URL, - for stdin, or a path to a file."
|
|
26757
|
-
);
|
|
26758
|
-
process.exit(1);
|
|
26759
|
-
}
|
|
26760
|
-
}
|
|
26761
|
-
|
|
26762
26715
|
// src/commands/prs/reply.ts
|
|
26763
26716
|
function validateBody2(body) {
|
|
26764
26717
|
const lowerBody = body.toLowerCase();
|
|
@@ -26786,7 +26739,7 @@ async function reply(commentId, body) {
|
|
|
26786
26739
|
}
|
|
26787
26740
|
|
|
26788
26741
|
// src/commands/prs/wontfix.ts
|
|
26789
|
-
import { execSync as
|
|
26742
|
+
import { execSync as execSync51 } from "child_process";
|
|
26790
26743
|
function validateReason(reason4) {
|
|
26791
26744
|
const lowerReason = reason4.toLowerCase();
|
|
26792
26745
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
@@ -26803,7 +26756,7 @@ function validateShaReferences(reason4) {
|
|
|
26803
26756
|
const invalidShas = [];
|
|
26804
26757
|
for (const sha of shas) {
|
|
26805
26758
|
try {
|
|
26806
|
-
|
|
26759
|
+
execSync51(`git cat-file -t ${sha}`, { stdio: "pipe" });
|
|
26807
26760
|
} catch {
|
|
26808
26761
|
invalidShas.push(sha);
|
|
26809
26762
|
}
|
|
@@ -27070,17 +27023,189 @@ function registerPrsRaise(prsCommand) {
|
|
|
27070
27023
|
configHelp(raiseCommand, prsRaiseConfigHelp);
|
|
27071
27024
|
}
|
|
27072
27025
|
|
|
27073
|
-
// src/commands/
|
|
27074
|
-
|
|
27075
|
-
|
|
27076
|
-
|
|
27026
|
+
// src/commands/readTime/readTime.ts
|
|
27027
|
+
import { readFileSync as readFileSync45 } from "fs";
|
|
27028
|
+
|
|
27029
|
+
// src/commands/readTime/countReadingWords.ts
|
|
27030
|
+
var FENCE_PATTERN = /^\s*(```|~~~)/;
|
|
27031
|
+
var IMAGE_PATTERN = /!\[[^\]]*\]\([^)]*\)/g;
|
|
27032
|
+
var HTML_TAG_PATTERN = /<[^\s>][^>]*>/g;
|
|
27033
|
+
var URL_PATTERN3 = /https?:\/\/\S+/g;
|
|
27034
|
+
var SINGLE_WORD_PATTERNS = [IMAGE_PATTERN, HTML_TAG_PATTERN, URL_PATTERN3];
|
|
27035
|
+
function countReadingWords(body) {
|
|
27036
|
+
let prose = 0;
|
|
27037
|
+
let code = 0;
|
|
27038
|
+
let inCode = false;
|
|
27039
|
+
for (const line of body.split("\n")) {
|
|
27040
|
+
if (FENCE_PATTERN.test(line)) {
|
|
27041
|
+
inCode = !inCode;
|
|
27042
|
+
} else if (inCode) {
|
|
27043
|
+
code += countTokens(line);
|
|
27044
|
+
} else {
|
|
27045
|
+
prose += countProseWords(line);
|
|
27046
|
+
}
|
|
27047
|
+
}
|
|
27048
|
+
return { prose, code };
|
|
27049
|
+
}
|
|
27050
|
+
function countProseWords(line) {
|
|
27051
|
+
let singles = 0;
|
|
27052
|
+
let remaining = line;
|
|
27053
|
+
for (const pattern2 of SINGLE_WORD_PATTERNS) {
|
|
27054
|
+
remaining = remaining.replace(pattern2, () => {
|
|
27055
|
+
singles += 1;
|
|
27056
|
+
return " ";
|
|
27057
|
+
});
|
|
27058
|
+
}
|
|
27059
|
+
return singles + countTokens(remaining);
|
|
27060
|
+
}
|
|
27061
|
+
function countTokens(text18) {
|
|
27062
|
+
return text18.split(/\s+/).filter((token) => /[A-Za-z0-9]/.test(token)).length;
|
|
27063
|
+
}
|
|
27064
|
+
|
|
27065
|
+
// src/commands/readTime/estimateReadSeconds.ts
|
|
27066
|
+
var LENGTH_DECAY_FITTED_TO_TIMED_PR_READINGS = 0.17;
|
|
27067
|
+
function estimateReadSeconds(words, wordsPerMinute) {
|
|
27068
|
+
const load3 = words ** (1 + LENGTH_DECAY_FITTED_TO_TIMED_PR_READINGS);
|
|
27069
|
+
return Math.round(load3 / wordsPerMinute * 60);
|
|
27070
|
+
}
|
|
27071
|
+
|
|
27072
|
+
// src/commands/readTime/formatReadDuration.ts
|
|
27073
|
+
function formatReadDuration(seconds) {
|
|
27074
|
+
if (seconds < 60) return `${seconds}s`;
|
|
27075
|
+
const minutes = Math.floor(seconds / 60);
|
|
27076
|
+
const remainder = seconds % 60;
|
|
27077
|
+
return remainder === 0 ? `${minutes}m` : `${minutes}m ${remainder}s`;
|
|
27078
|
+
}
|
|
27079
|
+
|
|
27080
|
+
// src/commands/readTime/parseReadBudget.ts
|
|
27081
|
+
var BUDGET_PATTERN = /^(?:(\d+)m)?(?:(\d+)s)?$/;
|
|
27082
|
+
function parseReadBudget(value) {
|
|
27083
|
+
const match = BUDGET_PATTERN.exec(value.trim());
|
|
27084
|
+
const minutes = match?.[1];
|
|
27085
|
+
const seconds = match?.[2];
|
|
27086
|
+
if (minutes === void 0 && seconds === void 0) {
|
|
27087
|
+
throw new Error(
|
|
27088
|
+
`Invalid budget "${value}". Use a duration like 45s, 1m30s or 2m.`
|
|
27089
|
+
);
|
|
27090
|
+
}
|
|
27091
|
+
const total = Number(minutes ?? 0) * 60 + Number(seconds ?? 0);
|
|
27092
|
+
if (total === 0) {
|
|
27093
|
+
throw new Error(`Invalid budget "${value}". Value must be at least 1s.`);
|
|
27094
|
+
}
|
|
27095
|
+
return total;
|
|
27096
|
+
}
|
|
27097
|
+
|
|
27098
|
+
// src/commands/readTime/resolveReadTimeTarget.ts
|
|
27099
|
+
var PR_URL_PATTERN = /^(https:\/\/github\.com\/[^/\s]+\/[^/\s]+)\/pull\/(\d+)(?:[/?#].*)?$/;
|
|
27100
|
+
function resolveReadTimeTarget(target) {
|
|
27101
|
+
const trimmed = target.trim();
|
|
27102
|
+
if (trimmed === "-") return { kind: "stdin" };
|
|
27103
|
+
if (/^\d+$/.test(trimmed)) {
|
|
27104
|
+
return { kind: "pr", number: Number(trimmed), repo: null };
|
|
27105
|
+
}
|
|
27106
|
+
const url = PR_URL_PATTERN.exec(trimmed);
|
|
27107
|
+
if (url) {
|
|
27108
|
+
const repo = parseGitHubUrl(url[1]);
|
|
27109
|
+
if (repo) return { kind: "pr", number: Number(url[2]), repo };
|
|
27110
|
+
}
|
|
27111
|
+
return { kind: "file", path: target };
|
|
27112
|
+
}
|
|
27113
|
+
|
|
27114
|
+
// src/commands/prs/fetchPrBody.ts
|
|
27115
|
+
import { execSync as execSync52 } from "child_process";
|
|
27116
|
+
function exitGhNotInstalled() {
|
|
27117
|
+
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
27118
|
+
console.error("Install it from https://cli.github.com/");
|
|
27119
|
+
process.exit(1);
|
|
27120
|
+
}
|
|
27121
|
+
function currentRepo() {
|
|
27122
|
+
try {
|
|
27123
|
+
return getRepoInfo();
|
|
27124
|
+
} catch (error) {
|
|
27125
|
+
if (isGhNotInstalled(error)) exitGhNotInstalled();
|
|
27126
|
+
console.error("Error: Could not resolve the current GitHub repository.");
|
|
27127
|
+
console.error(
|
|
27128
|
+
"Pass a pull request URL, - for stdin, or a path to a file instead of a number."
|
|
27129
|
+
);
|
|
27130
|
+
process.exit(1);
|
|
27131
|
+
}
|
|
27132
|
+
}
|
|
27133
|
+
function fetchPrBody(number, repo) {
|
|
27134
|
+
const { org, repo: name } = repo ?? currentRepo();
|
|
27135
|
+
try {
|
|
27136
|
+
const raw = execSync52(`gh pr view ${number} --json body -R ${org}/${name}`, {
|
|
27137
|
+
encoding: "utf8",
|
|
27138
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
27139
|
+
});
|
|
27140
|
+
return JSON.parse(raw).body ?? "";
|
|
27141
|
+
} catch (error) {
|
|
27142
|
+
if (isGhNotInstalled(error)) exitGhNotInstalled();
|
|
27143
|
+
if (isNotFound(error)) {
|
|
27144
|
+
console.error(`Error: Pull request ${org}/${name}#${number} not found.`);
|
|
27145
|
+
process.exit(1);
|
|
27146
|
+
}
|
|
27147
|
+
throw error;
|
|
27148
|
+
}
|
|
27149
|
+
}
|
|
27150
|
+
|
|
27151
|
+
// src/commands/readTime/readTime.ts
|
|
27152
|
+
var DEFAULT_WORDS_PER_MINUTE = 200;
|
|
27153
|
+
var DEFAULT_BUDGET_SECONDS = 60;
|
|
27154
|
+
var CODE_WORDS_PER_PROSE_WORD = 2;
|
|
27155
|
+
async function readTime(target, options2 = {}) {
|
|
27156
|
+
const budgetSeconds = resolveBudget(options2.budget);
|
|
27157
|
+
const body = await loadBody(resolveReadTimeTarget(target));
|
|
27158
|
+
const { prose, code } = countReadingWords(body);
|
|
27159
|
+
const words = prose + code;
|
|
27160
|
+
const config = loadConfig();
|
|
27161
|
+
const wordsPerMinute = config.readTime?.wordsPerMinute ?? config.prs?.readingWordsPerMinute ?? DEFAULT_WORDS_PER_MINUTE;
|
|
27162
|
+
const seconds = estimateReadSeconds(
|
|
27163
|
+
prose + code * CODE_WORDS_PER_PROSE_WORD,
|
|
27164
|
+
wordsPerMinute
|
|
27165
|
+
);
|
|
27166
|
+
const label2 = words === 1 ? "word" : "words";
|
|
27167
|
+
const verdict = seconds > budgetSeconds ? ` \xB7 over the ~${formatReadDuration(budgetSeconds)} budget` : "";
|
|
27168
|
+
console.log(
|
|
27169
|
+
`${words} ${label2} \xB7 ~${formatReadDuration(seconds)} read${verdict}`
|
|
27170
|
+
);
|
|
27171
|
+
}
|
|
27172
|
+
function resolveBudget(budget) {
|
|
27173
|
+
if (budget === void 0) return DEFAULT_BUDGET_SECONDS;
|
|
27174
|
+
try {
|
|
27175
|
+
return parseReadBudget(budget);
|
|
27176
|
+
} catch (error) {
|
|
27177
|
+
console.error(`Error: ${error.message}`);
|
|
27178
|
+
process.exit(1);
|
|
27179
|
+
}
|
|
27180
|
+
}
|
|
27181
|
+
async function loadBody(target) {
|
|
27182
|
+
if (target.kind === "stdin") return readBodyArgument("-");
|
|
27183
|
+
if (target.kind === "file") return readDraftFile(target.path);
|
|
27184
|
+
return fetchPrBody(target.number, target.repo);
|
|
27185
|
+
}
|
|
27186
|
+
function readDraftFile(path90) {
|
|
27187
|
+
try {
|
|
27188
|
+
return readFileSync45(path90, "utf8");
|
|
27189
|
+
} catch {
|
|
27190
|
+
console.error(`Error: Could not read \`${path90}\`.`);
|
|
27191
|
+
console.error(
|
|
27192
|
+
"Pass a pull request number, a GitHub pull request URL, - for stdin, or a path to a file."
|
|
27193
|
+
);
|
|
27194
|
+
process.exit(1);
|
|
27195
|
+
}
|
|
27196
|
+
}
|
|
27197
|
+
|
|
27198
|
+
// src/commands/registerReadTime.ts
|
|
27199
|
+
function registerReadTime(parent) {
|
|
27200
|
+
const readTimeCommand = parent.command("read-time <target>").description(
|
|
27201
|
+
"Estimate how long a document takes to read (target is a pull request number, a GitHub pull request URL, - for stdin, or a file path)"
|
|
27077
27202
|
).option(
|
|
27078
27203
|
"--budget <duration>",
|
|
27079
27204
|
"read-time budget the estimate is judged against, e.g. 45s, 1m30s, 2m (default 1m)"
|
|
27080
27205
|
).action(async (target, options2) => {
|
|
27081
27206
|
await readTime(target, options2);
|
|
27082
27207
|
});
|
|
27083
|
-
configHelp(readTimeCommand,
|
|
27208
|
+
configHelp(readTimeCommand, readTimeConfigHelp);
|
|
27084
27209
|
}
|
|
27085
27210
|
|
|
27086
27211
|
// src/commands/registerPrs.ts
|
|
@@ -27089,7 +27214,7 @@ function registerPrs(program2) {
|
|
|
27089
27214
|
registerPrsRaise(prsCommand);
|
|
27090
27215
|
registerPrsEdit(prsCommand);
|
|
27091
27216
|
registerPrsComments(prsCommand);
|
|
27092
|
-
|
|
27217
|
+
registerReadTime(prsCommand);
|
|
27093
27218
|
configHelp(prsCommand, prsConfigHelp);
|
|
27094
27219
|
}
|
|
27095
27220
|
|
|
@@ -31435,8 +31560,8 @@ function filterToSql(filter) {
|
|
|
31435
31560
|
// src/commands/seq/fetchSeqData.ts
|
|
31436
31561
|
function buildDataParams(filter, count8, from, to) {
|
|
31437
31562
|
const sqlFilter = filterToSql(filter);
|
|
31438
|
-
const
|
|
31439
|
-
const params = new URLSearchParams({ q:
|
|
31563
|
+
const sql28 = `select @Timestamp, @Level, @Exception, @Message from stream where ${sqlFilter} order by @Timestamp desc limit ${count8}`;
|
|
31564
|
+
const params = new URLSearchParams({ q: sql28 });
|
|
31440
31565
|
if (from) params.set("rangeStartUtc", from);
|
|
31441
31566
|
if (to) params.set("rangeEndUtc", to);
|
|
31442
31567
|
return params;
|
|
@@ -31797,26 +31922,26 @@ import chalk222 from "chalk";
|
|
|
31797
31922
|
// src/commands/sql/loadConnections.ts
|
|
31798
31923
|
function loadConnections3() {
|
|
31799
31924
|
const raw = loadGlobalConfigRaw();
|
|
31800
|
-
const
|
|
31801
|
-
return
|
|
31925
|
+
const sql28 = raw.sql;
|
|
31926
|
+
return sql28?.connections ?? [];
|
|
31802
31927
|
}
|
|
31803
31928
|
function saveConnections3(connections) {
|
|
31804
31929
|
const raw = loadGlobalConfigRaw();
|
|
31805
|
-
const
|
|
31806
|
-
|
|
31807
|
-
raw.sql =
|
|
31930
|
+
const sql28 = raw.sql ?? {};
|
|
31931
|
+
sql28.connections = connections;
|
|
31932
|
+
raw.sql = sql28;
|
|
31808
31933
|
saveGlobalConfig(raw);
|
|
31809
31934
|
}
|
|
31810
31935
|
function getDefaultConnection2() {
|
|
31811
31936
|
const raw = loadGlobalConfigRaw();
|
|
31812
|
-
const
|
|
31813
|
-
return
|
|
31937
|
+
const sql28 = raw.sql;
|
|
31938
|
+
return sql28?.defaultConnection;
|
|
31814
31939
|
}
|
|
31815
31940
|
function setDefaultConnection2(name) {
|
|
31816
31941
|
const raw = loadGlobalConfigRaw();
|
|
31817
|
-
const
|
|
31818
|
-
|
|
31819
|
-
raw.sql =
|
|
31942
|
+
const sql28 = raw.sql ?? {};
|
|
31943
|
+
sql28.defaultConnection = name;
|
|
31944
|
+
raw.sql = sql28;
|
|
31820
31945
|
saveGlobalConfig(raw);
|
|
31821
31946
|
}
|
|
31822
31947
|
|
|
@@ -31887,9 +32012,9 @@ function resolveConnection3(name) {
|
|
|
31887
32012
|
}
|
|
31888
32013
|
|
|
31889
32014
|
// src/commands/sql/sqlConnect.ts
|
|
31890
|
-
import
|
|
32015
|
+
import sql22 from "mssql";
|
|
31891
32016
|
async function sqlConnect(conn) {
|
|
31892
|
-
return await
|
|
32017
|
+
return await sql22.connect({
|
|
31893
32018
|
server: conn.server,
|
|
31894
32019
|
port: conn.port,
|
|
31895
32020
|
user: conn.user,
|
|
@@ -31952,11 +32077,11 @@ var MUTATION_PATTERN = new RegExp(
|
|
|
31952
32077
|
`\\b(${MUTATION_KEYWORDS.join("|")})\\b`,
|
|
31953
32078
|
"i"
|
|
31954
32079
|
);
|
|
31955
|
-
function stripComments(
|
|
31956
|
-
return
|
|
32080
|
+
function stripComments(sql28) {
|
|
32081
|
+
return sql28.replace(/\/\*[\s\S]*?\*\//g, " ").replace(/--[^\n]*/g, " ");
|
|
31957
32082
|
}
|
|
31958
|
-
function isMutation(
|
|
31959
|
-
const stripped = stripComments(
|
|
32083
|
+
function isMutation(sql28) {
|
|
32084
|
+
const stripped = stripComments(sql28);
|
|
31960
32085
|
if (MUTATION_PATTERN.test(stripped)) return true;
|
|
31961
32086
|
return /\bSELECT\b[\s\S]+\bINTO\s+\w/i.test(stripped);
|
|
31962
32087
|
}
|
|
@@ -35970,13 +36095,13 @@ function drainSessions(sessions, notify2) {
|
|
|
35970
36095
|
}
|
|
35971
36096
|
|
|
35972
36097
|
// src/shared/db/recordPhaseActiveMs.ts
|
|
35973
|
-
import { sql as
|
|
36098
|
+
import { sql as sql23 } from "drizzle-orm";
|
|
35974
36099
|
async function recordPhaseActiveMs(db, itemId2, phaseIdx, activeMs) {
|
|
35975
36100
|
if (activeMs <= 0) return;
|
|
35976
36101
|
await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, activeMs }).onConflictDoUpdate({
|
|
35977
36102
|
target: [phaseUsage.itemId, phaseUsage.phaseIdx],
|
|
35978
36103
|
set: {
|
|
35979
|
-
activeMs:
|
|
36104
|
+
activeMs: sql23`${phaseUsage.activeMs} + excluded.active_ms`
|
|
35980
36105
|
}
|
|
35981
36106
|
});
|
|
35982
36107
|
}
|
|
@@ -36414,10 +36539,10 @@ function applyReviewPause(session, activity2) {
|
|
|
36414
36539
|
}
|
|
36415
36540
|
|
|
36416
36541
|
// src/shared/db/getPhaseActiveMs.ts
|
|
36417
|
-
import { and as and22, eq as
|
|
36542
|
+
import { and as and22, eq as eq45 } from "drizzle-orm";
|
|
36418
36543
|
async function getPhaseActiveMs(db, itemId2, phaseIdx) {
|
|
36419
36544
|
const [row] = await db.select({ activeMs: phaseUsage.activeMs }).from(phaseUsage).where(
|
|
36420
|
-
and22(
|
|
36545
|
+
and22(eq45(phaseUsage.itemId, itemId2), eq45(phaseUsage.phaseIdx, phaseIdx))
|
|
36421
36546
|
);
|
|
36422
36547
|
return row?.activeMs ?? 0;
|
|
36423
36548
|
}
|
|
@@ -37186,7 +37311,7 @@ function makeSessionSpawner(sessions, clients, counter, onStatusChange, notify2)
|
|
|
37186
37311
|
}
|
|
37187
37312
|
|
|
37188
37313
|
// src/shared/db/recordPhaseCycleContext.ts
|
|
37189
|
-
import { sql as
|
|
37314
|
+
import { sql as sql24 } from "drizzle-orm";
|
|
37190
37315
|
async function recordPhaseCycleContext(db, itemId2, phaseIdx, window, resetsAt, pct) {
|
|
37191
37316
|
if (pct <= 0) return;
|
|
37192
37317
|
await db.insert(phaseCycleContext).values({ itemId: itemId2, phaseIdx, window, resetsAt, peakContextPct: pct }).onConflictDoUpdate({
|
|
@@ -37197,24 +37322,24 @@ async function recordPhaseCycleContext(db, itemId2, phaseIdx, window, resetsAt,
|
|
|
37197
37322
|
phaseCycleContext.resetsAt
|
|
37198
37323
|
],
|
|
37199
37324
|
set: {
|
|
37200
|
-
peakContextPct:
|
|
37325
|
+
peakContextPct: sql24`GREATEST(${phaseCycleContext.peakContextPct}, ${pct})`
|
|
37201
37326
|
}
|
|
37202
37327
|
});
|
|
37203
37328
|
}
|
|
37204
37329
|
|
|
37205
37330
|
// src/shared/db/recordPhasePeakContext.ts
|
|
37206
|
-
import { sql as
|
|
37331
|
+
import { sql as sql25 } from "drizzle-orm";
|
|
37207
37332
|
async function recordPhasePeakContext(db, itemId2, phaseIdx, pct) {
|
|
37208
37333
|
await db.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, peakContextPct: pct }).onConflictDoUpdate({
|
|
37209
37334
|
target: [phaseUsage.itemId, phaseUsage.phaseIdx],
|
|
37210
37335
|
set: {
|
|
37211
|
-
peakContextPct:
|
|
37336
|
+
peakContextPct: sql25`GREATEST(${phaseUsage.peakContextPct}, ${pct})`
|
|
37212
37337
|
}
|
|
37213
37338
|
});
|
|
37214
37339
|
}
|
|
37215
37340
|
|
|
37216
37341
|
// src/shared/db/recordPhaseTranscriptUsage.ts
|
|
37217
|
-
import { sql as
|
|
37342
|
+
import { sql as sql26 } from "drizzle-orm";
|
|
37218
37343
|
async function recordPhaseTranscriptUsage(db, itemId2, phaseIdx, responses) {
|
|
37219
37344
|
const byId = /* @__PURE__ */ new Map();
|
|
37220
37345
|
for (const r of responses) byId.set(r.messageId, r);
|
|
@@ -37234,8 +37359,8 @@ async function recordPhaseTranscriptUsage(db, itemId2, phaseIdx, responses) {
|
|
|
37234
37359
|
await tx.insert(phaseUsage).values({ itemId: itemId2, phaseIdx, tokensUp, tokensDown }).onConflictDoUpdate({
|
|
37235
37360
|
target: [phaseUsage.itemId, phaseUsage.phaseIdx],
|
|
37236
37361
|
set: {
|
|
37237
|
-
tokensUp:
|
|
37238
|
-
tokensDown:
|
|
37362
|
+
tokensUp: sql26`${phaseUsage.tokensUp} + ${tokensUp}`,
|
|
37363
|
+
tokensDown: sql26`${phaseUsage.tokensDown} + ${tokensDown}`
|
|
37239
37364
|
}
|
|
37240
37365
|
});
|
|
37241
37366
|
return { tokensUp, tokensDown };
|
|
@@ -37243,17 +37368,17 @@ async function recordPhaseTranscriptUsage(db, itemId2, phaseIdx, responses) {
|
|
|
37243
37368
|
}
|
|
37244
37369
|
|
|
37245
37370
|
// src/shared/db/recordWindowTokens.ts
|
|
37246
|
-
import { and as and23, eq as
|
|
37371
|
+
import { and as and23, eq as eq46, sql as sql27 } from "drizzle-orm";
|
|
37247
37372
|
async function recordWindowTokens(db, window, resetsAt, tokensUp, tokensDown) {
|
|
37248
37373
|
if (tokensUp <= 0 && tokensDown <= 0) return;
|
|
37249
37374
|
await db.update(usagePeaks).set({
|
|
37250
|
-
tokensUp:
|
|
37251
|
-
tokensDown:
|
|
37375
|
+
tokensUp: sql27`${usagePeaks.tokensUp} + ${tokensUp}`,
|
|
37376
|
+
tokensDown: sql27`${usagePeaks.tokensDown} + ${tokensDown}`
|
|
37252
37377
|
}).where(
|
|
37253
37378
|
and23(
|
|
37254
|
-
|
|
37255
|
-
|
|
37256
|
-
|
|
37379
|
+
eq46(usagePeaks.window, window),
|
|
37380
|
+
eq46(usagePeaks.resetsAt, resetsAt),
|
|
37381
|
+
eq46(usagePeaks.segment, 0)
|
|
37257
37382
|
)
|
|
37258
37383
|
);
|
|
37259
37384
|
}
|
|
@@ -41209,6 +41334,7 @@ registerJira(program);
|
|
|
41209
41334
|
registerMermaid(program);
|
|
41210
41335
|
registerMiro(program);
|
|
41211
41336
|
registerPrs(program);
|
|
41337
|
+
registerReadTime(program);
|
|
41212
41338
|
registerRoam(program);
|
|
41213
41339
|
registerBacklog(program);
|
|
41214
41340
|
registerBranch(program);
|