@wrongstack/tools 0.303.0 → 0.305.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/dist/builtin.d.ts +6 -0
- package/dist/builtin.js +715 -378
- package/dist/codebase-index/codebase-index-tool.d.ts +6 -0
- package/dist/codebase-index/index.js +112 -89
- package/dist/codebase-index/indexer.d.ts +6 -0
- package/dist/codebase-index/project-server.js +112 -89
- package/dist/codebase-index/schema.d.ts +11 -0
- package/dist/codebase-index/worker.js +112 -89
- package/dist/index.d.ts +3 -3
- package/dist/index.js +777 -397
- package/dist/kanban-contract-actions.d.ts +7 -0
- package/dist/kanban-task-inputs.d.ts +15 -2
- package/dist/kanban-tool-schema.d.ts +2 -2
- package/dist/kanban-tool-types.d.ts +32 -11
- package/dist/kanban.js +366 -245
- package/dist/pack.js +714 -378
- package/dist/plan.js +601 -289
- package/dist/read.js +112 -89
- package/dist/session-kanban.d.ts +86 -1
- package/dist/session-kanban.js +216 -46
- package/dist/task.js +601 -289
- package/dist/todo.js +601 -289
- package/dist/tool-tier.js +714 -378
- package/package.json +3 -3
package/dist/read.js
CHANGED
|
@@ -2867,6 +2867,86 @@ import {
|
|
|
2867
2867
|
isFrugalPerf
|
|
2868
2868
|
} from "@wrongstack/core/utils";
|
|
2869
2869
|
|
|
2870
|
+
// src/codebase-index/content-hash.ts
|
|
2871
|
+
var PRIME64_1 = 0x9e3779b185ebca87n;
|
|
2872
|
+
var PRIME64_2 = 0xc2b2ae3d27d4eb4fn;
|
|
2873
|
+
var PRIME64_3 = 0x165667b19e3779f9n;
|
|
2874
|
+
var PRIME64_4 = 0x85ebca77c2b2ae63n;
|
|
2875
|
+
var PRIME64_5 = 0x27d4eb2f165667c5n;
|
|
2876
|
+
var MASK64 = 0xffffffffffffffffn;
|
|
2877
|
+
function mul64(a, b) {
|
|
2878
|
+
return (a & MASK64) * (b & MASK64) & MASK64;
|
|
2879
|
+
}
|
|
2880
|
+
function rotl64(x, n) {
|
|
2881
|
+
const v = x & MASK64;
|
|
2882
|
+
return (v << BigInt(n) | v >> BigInt(64 - n)) & MASK64;
|
|
2883
|
+
}
|
|
2884
|
+
function readU64LE(buf, off) {
|
|
2885
|
+
let v = 0n;
|
|
2886
|
+
for (let i = 7; i >= 0; i--) {
|
|
2887
|
+
v = v << 8n | BigInt(buf[off + i] ?? 0);
|
|
2888
|
+
}
|
|
2889
|
+
return v & MASK64;
|
|
2890
|
+
}
|
|
2891
|
+
function readU32LE(buf, off) {
|
|
2892
|
+
return (BigInt(buf[off] ?? 0) | BigInt(buf[off + 1] ?? 0) << 8n | BigInt(buf[off + 2] ?? 0) << 16n | BigInt(buf[off + 3] ?? 0) << 24n) & MASK64;
|
|
2893
|
+
}
|
|
2894
|
+
function xxh64Round(acc, lane) {
|
|
2895
|
+
return mul64(rotl64(acc + mul64(lane, PRIME64_2) & MASK64, 31), PRIME64_1);
|
|
2896
|
+
}
|
|
2897
|
+
function xxh64MergeRound(acc, val) {
|
|
2898
|
+
return mul64(acc ^ xxh64Round(0n, val), PRIME64_1) + PRIME64_4 & MASK64;
|
|
2899
|
+
}
|
|
2900
|
+
function xxhash64Hex(buf, explicitLen) {
|
|
2901
|
+
const length = explicitLen ?? buf.length;
|
|
2902
|
+
let h;
|
|
2903
|
+
let off = 0;
|
|
2904
|
+
if (length >= 32) {
|
|
2905
|
+
let v1 = PRIME64_1 + PRIME64_2 & MASK64;
|
|
2906
|
+
let v2 = PRIME64_2;
|
|
2907
|
+
let v3 = 0n;
|
|
2908
|
+
let v4 = 0n - PRIME64_1 & MASK64;
|
|
2909
|
+
const end32 = length - 32;
|
|
2910
|
+
while (off <= end32) {
|
|
2911
|
+
v1 = xxh64Round(v1, readU64LE(buf, off));
|
|
2912
|
+
v2 = xxh64Round(v2, readU64LE(buf, off + 8));
|
|
2913
|
+
v3 = xxh64Round(v3, readU64LE(buf, off + 16));
|
|
2914
|
+
v4 = xxh64Round(v4, readU64LE(buf, off + 24));
|
|
2915
|
+
off += 32;
|
|
2916
|
+
}
|
|
2917
|
+
h = rotl64(v1, 1) + rotl64(v2, 7) + rotl64(v3, 12) + rotl64(v4, 18) & MASK64;
|
|
2918
|
+
h = xxh64MergeRound(h, v1);
|
|
2919
|
+
h = xxh64MergeRound(h, v2);
|
|
2920
|
+
h = xxh64MergeRound(h, v3);
|
|
2921
|
+
h = xxh64MergeRound(h, v4);
|
|
2922
|
+
} else {
|
|
2923
|
+
h = PRIME64_5;
|
|
2924
|
+
}
|
|
2925
|
+
h = h + BigInt(length) & MASK64;
|
|
2926
|
+
while (off + 8 <= length) {
|
|
2927
|
+
const k1 = xxh64Round(0n, readU64LE(buf, off));
|
|
2928
|
+
h = mul64(rotl64(h ^ k1, 27), PRIME64_1) + PRIME64_4 & MASK64;
|
|
2929
|
+
off += 8;
|
|
2930
|
+
}
|
|
2931
|
+
if (off + 4 <= length) {
|
|
2932
|
+
h = mul64(rotl64(h ^ mul64(readU32LE(buf, off), PRIME64_1), 23), PRIME64_2) + PRIME64_3 & MASK64;
|
|
2933
|
+
off += 4;
|
|
2934
|
+
}
|
|
2935
|
+
while (off < length) {
|
|
2936
|
+
h = mul64(rotl64(h ^ mul64(BigInt(buf[off] ?? 0), PRIME64_5), 11), PRIME64_1) & MASK64;
|
|
2937
|
+
off += 1;
|
|
2938
|
+
}
|
|
2939
|
+
h = (h ^ h >> 33n) & MASK64;
|
|
2940
|
+
h = mul64(h, PRIME64_2);
|
|
2941
|
+
h = (h ^ h >> 29n) & MASK64;
|
|
2942
|
+
h = mul64(h, PRIME64_3);
|
|
2943
|
+
h = (h ^ h >> 32n) & MASK64;
|
|
2944
|
+
return h.toString(16).padStart(16, "0");
|
|
2945
|
+
}
|
|
2946
|
+
function xxhash64String(content) {
|
|
2947
|
+
return xxhash64Hex(new TextEncoder().encode(content));
|
|
2948
|
+
}
|
|
2949
|
+
|
|
2870
2950
|
// src/codebase-index/gitignore.ts
|
|
2871
2951
|
import * as fs from "node:fs/promises";
|
|
2872
2952
|
import * as path2 from "node:path";
|
|
@@ -3898,86 +3978,6 @@ function getParserPool() {
|
|
|
3898
3978
|
return _pool;
|
|
3899
3979
|
}
|
|
3900
3980
|
|
|
3901
|
-
// src/codebase-index/content-hash.ts
|
|
3902
|
-
var PRIME64_1 = 0x9e3779b185ebca87n;
|
|
3903
|
-
var PRIME64_2 = 0xc2b2ae3d27d4eb4fn;
|
|
3904
|
-
var PRIME64_3 = 0x165667b19e3779f9n;
|
|
3905
|
-
var PRIME64_4 = 0x85ebca77c2b2ae63n;
|
|
3906
|
-
var PRIME64_5 = 0x27d4eb2f165667c5n;
|
|
3907
|
-
var MASK64 = 0xffffffffffffffffn;
|
|
3908
|
-
function mul64(a, b) {
|
|
3909
|
-
return (a & MASK64) * (b & MASK64) & MASK64;
|
|
3910
|
-
}
|
|
3911
|
-
function rotl64(x, n) {
|
|
3912
|
-
const v = x & MASK64;
|
|
3913
|
-
return (v << BigInt(n) | v >> BigInt(64 - n)) & MASK64;
|
|
3914
|
-
}
|
|
3915
|
-
function readU64LE(buf, off) {
|
|
3916
|
-
let v = 0n;
|
|
3917
|
-
for (let i = 7; i >= 0; i--) {
|
|
3918
|
-
v = v << 8n | BigInt(buf[off + i] ?? 0);
|
|
3919
|
-
}
|
|
3920
|
-
return v & MASK64;
|
|
3921
|
-
}
|
|
3922
|
-
function readU32LE(buf, off) {
|
|
3923
|
-
return (BigInt(buf[off] ?? 0) | BigInt(buf[off + 1] ?? 0) << 8n | BigInt(buf[off + 2] ?? 0) << 16n | BigInt(buf[off + 3] ?? 0) << 24n) & MASK64;
|
|
3924
|
-
}
|
|
3925
|
-
function xxh64Round(acc, lane) {
|
|
3926
|
-
return mul64(rotl64(acc + mul64(lane, PRIME64_2) & MASK64, 31), PRIME64_1);
|
|
3927
|
-
}
|
|
3928
|
-
function xxh64MergeRound(acc, val) {
|
|
3929
|
-
return mul64(acc ^ xxh64Round(0n, val), PRIME64_1) + PRIME64_4 & MASK64;
|
|
3930
|
-
}
|
|
3931
|
-
function xxhash64Hex(buf, explicitLen) {
|
|
3932
|
-
const length = explicitLen ?? buf.length;
|
|
3933
|
-
let h;
|
|
3934
|
-
let off = 0;
|
|
3935
|
-
if (length >= 32) {
|
|
3936
|
-
let v1 = PRIME64_1 + PRIME64_2 & MASK64;
|
|
3937
|
-
let v2 = PRIME64_2;
|
|
3938
|
-
let v3 = 0n;
|
|
3939
|
-
let v4 = 0n - PRIME64_1 & MASK64;
|
|
3940
|
-
const end32 = length - 32;
|
|
3941
|
-
while (off <= end32) {
|
|
3942
|
-
v1 = xxh64Round(v1, readU64LE(buf, off));
|
|
3943
|
-
v2 = xxh64Round(v2, readU64LE(buf, off + 8));
|
|
3944
|
-
v3 = xxh64Round(v3, readU64LE(buf, off + 16));
|
|
3945
|
-
v4 = xxh64Round(v4, readU64LE(buf, off + 24));
|
|
3946
|
-
off += 32;
|
|
3947
|
-
}
|
|
3948
|
-
h = rotl64(v1, 1) + rotl64(v2, 7) + rotl64(v3, 12) + rotl64(v4, 18) & MASK64;
|
|
3949
|
-
h = xxh64MergeRound(h, v1);
|
|
3950
|
-
h = xxh64MergeRound(h, v2);
|
|
3951
|
-
h = xxh64MergeRound(h, v3);
|
|
3952
|
-
h = xxh64MergeRound(h, v4);
|
|
3953
|
-
} else {
|
|
3954
|
-
h = PRIME64_5;
|
|
3955
|
-
}
|
|
3956
|
-
h = h + BigInt(length) & MASK64;
|
|
3957
|
-
while (off + 8 <= length) {
|
|
3958
|
-
const k1 = xxh64Round(0n, readU64LE(buf, off));
|
|
3959
|
-
h = mul64(rotl64(h ^ k1, 27), PRIME64_1) + PRIME64_4 & MASK64;
|
|
3960
|
-
off += 8;
|
|
3961
|
-
}
|
|
3962
|
-
if (off + 4 <= length) {
|
|
3963
|
-
h = mul64(rotl64(h ^ mul64(readU32LE(buf, off), PRIME64_1), 23), PRIME64_2) + PRIME64_3 & MASK64;
|
|
3964
|
-
off += 4;
|
|
3965
|
-
}
|
|
3966
|
-
while (off < length) {
|
|
3967
|
-
h = mul64(rotl64(h ^ mul64(BigInt(buf[off] ?? 0), PRIME64_5), 11), PRIME64_1) & MASK64;
|
|
3968
|
-
off += 1;
|
|
3969
|
-
}
|
|
3970
|
-
h = (h ^ h >> 33n) & MASK64;
|
|
3971
|
-
h = mul64(h, PRIME64_2);
|
|
3972
|
-
h = (h ^ h >> 29n) & MASK64;
|
|
3973
|
-
h = mul64(h, PRIME64_3);
|
|
3974
|
-
h = (h ^ h >> 32n) & MASK64;
|
|
3975
|
-
return h.toString(16).padStart(16, "0");
|
|
3976
|
-
}
|
|
3977
|
-
function xxhash64String(content) {
|
|
3978
|
-
return xxhash64Hex(new TextEncoder().encode(content));
|
|
3979
|
-
}
|
|
3980
|
-
|
|
3981
3981
|
// src/codebase-index/writer.ts
|
|
3982
3982
|
import { expectDefined as expectDefined4 } from "@wrongstack/core/utils";
|
|
3983
3983
|
import * as fs8 from "node:fs";
|
|
@@ -5495,7 +5495,7 @@ var IndexStore = class _IndexStore {
|
|
|
5495
5495
|
const ftsSchema = this.stmt(
|
|
5496
5496
|
"SELECT sql FROM sqlite_master WHERE type='table' AND name='symbols_fts'"
|
|
5497
5497
|
).get();
|
|
5498
|
-
if (ftsSchema?.sql
|
|
5498
|
+
if (ftsSchema?.sql?.includes("unicode61")) {
|
|
5499
5499
|
this.db.exec("DROP TABLE IF EXISTS symbols_fts");
|
|
5500
5500
|
}
|
|
5501
5501
|
this.db.exec(SYMBOLS_FTS_SQL);
|
|
@@ -5988,9 +5988,13 @@ var IndexStore = class _IndexStore {
|
|
|
5988
5988
|
sim: cosineSimilarity(queryVec, decodeVector(r.vector))
|
|
5989
5989
|
})).sort((a, b) => b.sim - a.sim);
|
|
5990
5990
|
const bm25Rank = /* @__PURE__ */ new Map();
|
|
5991
|
-
bm25Rows.forEach((r, i) =>
|
|
5991
|
+
bm25Rows.forEach((r, i) => {
|
|
5992
|
+
bm25Rank.set(r.id, i);
|
|
5993
|
+
});
|
|
5992
5994
|
const vecRank = /* @__PURE__ */ new Map();
|
|
5993
|
-
vecScores.forEach((r, i) =>
|
|
5995
|
+
vecScores.forEach((r, i) => {
|
|
5996
|
+
vecRank.set(r.id, i);
|
|
5997
|
+
});
|
|
5994
5998
|
const fused = reciprocalRankFusion(bm25Rank, vecRank, 60);
|
|
5995
5999
|
const fusedScore = new Map(fused);
|
|
5996
6000
|
const sorted = [...bm25Rows].sort(
|
|
@@ -6600,6 +6604,9 @@ var YIELD_EVERY_N = 50;
|
|
|
6600
6604
|
function resolveParallelBatch() {
|
|
6601
6605
|
return indexParallelBatchSize(availableParallelism());
|
|
6602
6606
|
}
|
|
6607
|
+
function shouldUseParserWorkerPool(candidateFileCount, parseBatchCount) {
|
|
6608
|
+
return !isFrugalPerf() && candidateFileCount >= WORKER_POOL_THRESHOLD && parseBatchCount > 1;
|
|
6609
|
+
}
|
|
6603
6610
|
function yieldEventLoop() {
|
|
6604
6611
|
return new Promise((resolve4) => setImmediate(resolve4));
|
|
6605
6612
|
}
|
|
@@ -6776,11 +6783,7 @@ async function resolveProjectRelations(store, projectRoot, opts) {
|
|
|
6776
6783
|
const structure = await detectModuleRoots(projectRoot, indexedFiles);
|
|
6777
6784
|
if (opts.signal?.aborted) return;
|
|
6778
6785
|
store.setFilePackages(assignPackageLabels(structure, indexedFiles));
|
|
6779
|
-
const resolver = new ModuleResolver(
|
|
6780
|
-
structure,
|
|
6781
|
-
indexedFiles,
|
|
6782
|
-
store.getNamespaceDeclarations()
|
|
6783
|
-
);
|
|
6786
|
+
const resolver = new ModuleResolver(structure, indexedFiles, store.getNamespaceDeclarations());
|
|
6784
6787
|
const pending2 = store.getUnresolvedImports(opts.onlyFiles);
|
|
6785
6788
|
const resolutions = [];
|
|
6786
6789
|
for (const entry of pending2) {
|
|
@@ -6805,6 +6808,10 @@ async function runIndexerWithStore(store, opts) {
|
|
|
6805
6808
|
const errors = [];
|
|
6806
6809
|
const langStats = {};
|
|
6807
6810
|
let filesIndexed = 0;
|
|
6811
|
+
let filesParsed = 0;
|
|
6812
|
+
let filesSkipped = 0;
|
|
6813
|
+
let filesEmpty = 0;
|
|
6814
|
+
let filesFailed = 0;
|
|
6808
6815
|
let symbolsIndexed = 0;
|
|
6809
6816
|
const isGitIgnored = await loadGitignoreMatcher(projectRoot);
|
|
6810
6817
|
let files;
|
|
@@ -6846,12 +6853,14 @@ async function runIndexerWithStore(store, opts) {
|
|
|
6846
6853
|
langStats[meta.lang] = (langStats[meta.lang] ?? 0) + meta.symbolCount;
|
|
6847
6854
|
symbolsIndexed += meta.symbolCount;
|
|
6848
6855
|
filesIndexed++;
|
|
6856
|
+
filesSkipped++;
|
|
6849
6857
|
filesPreSkipped++;
|
|
6850
6858
|
return false;
|
|
6851
6859
|
});
|
|
6852
6860
|
if (filesPreSkipped > 0) opts.onProgress?.(filesPreSkipped, totalFilesForProgress);
|
|
6853
6861
|
}
|
|
6854
6862
|
const parallelBatch = resolveParallelBatch();
|
|
6863
|
+
const parserPoolCandidateCount = files.length;
|
|
6855
6864
|
let filesSinceLastYield = 0;
|
|
6856
6865
|
for (let batchStart = 0; batchStart < files.length; batchStart += parallelBatch) {
|
|
6857
6866
|
const batchEnd = Math.min(batchStart + parallelBatch, files.length);
|
|
@@ -6944,7 +6953,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
6944
6953
|
});
|
|
6945
6954
|
}
|
|
6946
6955
|
if (toParse.length > 0) {
|
|
6947
|
-
let pool = toParse.length
|
|
6956
|
+
let pool = shouldUseParserWorkerPool(parserPoolCandidateCount, toParse.length) ? getParserPool() : null;
|
|
6948
6957
|
if (pool) {
|
|
6949
6958
|
try {
|
|
6950
6959
|
await pool.ensureReady();
|
|
@@ -6994,12 +7003,14 @@ async function runIndexerWithStore(store, opts) {
|
|
|
6994
7003
|
const err = settled.reason;
|
|
6995
7004
|
if (err instanceof Error && isAbortError(err)) throw err;
|
|
6996
7005
|
errors.push(`batch error: ${file}: ${err instanceof Error ? err.message : String(err)}`);
|
|
7006
|
+
filesFailed++;
|
|
6997
7007
|
continue;
|
|
6998
7008
|
}
|
|
6999
7009
|
const result = settled.value;
|
|
7000
7010
|
if (result.error) {
|
|
7001
7011
|
if (result.missing) store.deleteFile(file);
|
|
7002
7012
|
errors.push(`${file}: ${result.error}`);
|
|
7013
|
+
filesFailed++;
|
|
7003
7014
|
continue;
|
|
7004
7015
|
}
|
|
7005
7016
|
const { stat: stat3, lang, parsed } = result;
|
|
@@ -7007,6 +7018,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7007
7018
|
langStats[lang] = (langStats[lang] ?? 0) + result.skippedMeta.symbolCount;
|
|
7008
7019
|
symbolsIndexed += result.skippedMeta.symbolCount;
|
|
7009
7020
|
filesIndexed++;
|
|
7021
|
+
filesSkipped++;
|
|
7010
7022
|
const stored = existingMeta.get(file);
|
|
7011
7023
|
if (stored && stored.mtimeMs !== result.skippedMeta.mtimeMs) {
|
|
7012
7024
|
store.upsertFile({
|
|
@@ -7031,6 +7043,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7031
7043
|
contentHash: result.contentHash ?? ""
|
|
7032
7044
|
});
|
|
7033
7045
|
filesIndexed++;
|
|
7046
|
+
filesEmpty++;
|
|
7034
7047
|
}
|
|
7035
7048
|
continue;
|
|
7036
7049
|
}
|
|
@@ -7044,6 +7057,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7044
7057
|
contentHash: result.contentHash ?? ""
|
|
7045
7058
|
});
|
|
7046
7059
|
filesIndexed++;
|
|
7060
|
+
filesEmpty++;
|
|
7047
7061
|
continue;
|
|
7048
7062
|
}
|
|
7049
7063
|
batchEntries.push({
|
|
@@ -7065,6 +7079,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7065
7079
|
symbolsIndexed += count;
|
|
7066
7080
|
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + count;
|
|
7067
7081
|
filesIndexed++;
|
|
7082
|
+
filesParsed++;
|
|
7068
7083
|
}
|
|
7069
7084
|
} catch (err) {
|
|
7070
7085
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -7077,6 +7092,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7077
7092
|
symbolsIndexed += symbolsWithIds.length;
|
|
7078
7093
|
langStats[entry.lang] = (langStats[entry.lang] ?? 0) + symbolsWithIds.length;
|
|
7079
7094
|
filesIndexed++;
|
|
7095
|
+
filesParsed++;
|
|
7080
7096
|
if (entry.refs.length > 0 && symbolsWithIds.length > 0) {
|
|
7081
7097
|
const fallbackBatch = assignRefsToSymbols2(entry.refs, symbolsWithIds);
|
|
7082
7098
|
if (fallbackBatch.length > 0) store.insertRefsBatch(fallbackBatch);
|
|
@@ -7094,6 +7110,7 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7094
7110
|
contentHash: entry.contentHash
|
|
7095
7111
|
});
|
|
7096
7112
|
} catch (innerErr) {
|
|
7113
|
+
filesFailed++;
|
|
7097
7114
|
errors.push(
|
|
7098
7115
|
`fallback write failed: ${entry.file}: ${innerErr instanceof Error ? innerErr.message : String(innerErr)}`
|
|
7099
7116
|
);
|
|
@@ -7126,6 +7143,12 @@ async function runIndexerWithStore(store, opts) {
|
|
|
7126
7143
|
const durationMs = Date.now() - startMs;
|
|
7127
7144
|
return {
|
|
7128
7145
|
filesIndexed,
|
|
7146
|
+
fileOutcomes: {
|
|
7147
|
+
parsed: filesParsed,
|
|
7148
|
+
skipped: filesSkipped,
|
|
7149
|
+
empty: filesEmpty,
|
|
7150
|
+
failed: filesFailed
|
|
7151
|
+
},
|
|
7129
7152
|
symbolsIndexed,
|
|
7130
7153
|
langStats,
|
|
7131
7154
|
durationMs,
|
package/dist/session-kanban.d.ts
CHANGED
|
@@ -3,7 +3,16 @@ import { type PlanFile, type PlanItem, type TaskFile } from '@wrongstack/core/st
|
|
|
3
3
|
import type { SerializedTaskGraph } from '@wrongstack/core/types';
|
|
4
4
|
import { type TaskItem } from '@wrongstack/core/utils';
|
|
5
5
|
import { type KanbanBoard, type KanbanColumn, type KanbanTask } from '@wrongstack/kanban';
|
|
6
|
-
/**
|
|
6
|
+
/**
|
|
7
|
+
* The canonical workflow shared by WebUI and TUI session boards.
|
|
8
|
+
*
|
|
9
|
+
* Session boards now use the same 5 standard columns as every other board
|
|
10
|
+
* (backlog, todo, in-progress, review, done). Formerly this shipped a 4-column
|
|
11
|
+
* variant without a backlog; the column lock unified the layout. Existing
|
|
12
|
+
* 4-column session_mirror boards are migrated automatically on the next session
|
|
13
|
+
* touch: `ensureSessionKanbanBoard`'s `sameColumns` check detects the drift
|
|
14
|
+
* and `updateBoard` reconciles to DEFAULT_COLUMNS.
|
|
15
|
+
*/
|
|
7
16
|
export declare const SESSION_KANBAN_COLUMNS: KanbanColumn[];
|
|
8
17
|
/** Create (or migrate) the single Kanban board owned by a session. */
|
|
9
18
|
export declare function ensureSessionKanbanBoard(projectRoot: string | undefined, sessionId: string): Promise<KanbanBoard | null>;
|
|
@@ -17,12 +26,35 @@ export declare function cleanupSessionKanbanBoard(projectRoot: string | undefine
|
|
|
17
26
|
export declare function cleanupSessionKanbanBoardIfEmpty(projectRoot: string | undefined, sessionId: string): Promise<string[]>;
|
|
18
27
|
/** Prune stale empty session boards while preserving manual and live boards. */
|
|
19
28
|
export declare function cleanupEmptySessionKanbanBoards(projectRoot: string | undefined, activeSessionId?: string): Promise<string[]>;
|
|
29
|
+
/**
|
|
30
|
+
* Consume the last unreported mirror failure for a session, if any.
|
|
31
|
+
*
|
|
32
|
+
* The mirror is asynchronous and fire-and-forget, so a rejected projection
|
|
33
|
+
* used to be observable only in stdout. The `todo` tool drains this on its
|
|
34
|
+
* next call and reports it as a warning, so a board that stopped tracking the
|
|
35
|
+
* session is surfaced where the divergence actually matters.
|
|
36
|
+
*/
|
|
37
|
+
export declare function takeSessionMirrorFailure(projectRoot: string | undefined, sessionId: string): string | undefined;
|
|
20
38
|
export declare function todoListToSerializedGraph(todos: readonly TodoItem[], sessionId: string): SerializedTaskGraph;
|
|
21
39
|
export declare function taskFileToSerializedGraph(tasks: readonly TaskItem[], sessionId: string): SerializedTaskGraph;
|
|
22
40
|
export declare function planFileToSerializedGraph(items: readonly PlanItem[], sessionId: string): SerializedTaskGraph;
|
|
23
41
|
export declare function projectSessionTodosToKanban(projectRoot: string | undefined, todos: readonly TodoItem[], sessionId: string): Promise<KanbanBoard | null>;
|
|
24
42
|
export declare function projectSessionTasksToKanban(projectRoot: string | undefined, tasks: readonly TaskItem[], sessionId: string): Promise<KanbanBoard | null>;
|
|
25
43
|
export declare function projectSessionPlanToKanban(projectRoot: string | undefined, items: readonly PlanItem[], sessionId: string): Promise<KanbanBoard | null>;
|
|
44
|
+
/**
|
|
45
|
+
* The todo rows that still need the session mirror: those not already cards on
|
|
46
|
+
* the board this session is working on.
|
|
47
|
+
*
|
|
48
|
+
* The rule used to be all-or-nothing — the mirror was skipped only when EVERY
|
|
49
|
+
* row was bound. A single unbound row (a todo the model added to the chat list
|
|
50
|
+
* without also filing a card) sent the whole list to a separate session board,
|
|
51
|
+
* so work already tracked on the real board showed up a second time there, and
|
|
52
|
+
* one stray row was enough to leave a session board holding a single card.
|
|
53
|
+
*
|
|
54
|
+
* With no active board every row still needs mirroring, since there is no other
|
|
55
|
+
* place the work is being recorded.
|
|
56
|
+
*/
|
|
57
|
+
export declare function todosNeedingSessionMirror(todos: readonly TodoItem[], activeBoardId: string | undefined): readonly TodoItem[];
|
|
26
58
|
export declare function mirrorSessionTodosToKanban(projectRoot: string | undefined, todos: readonly TodoItem[], sessionId: string): void;
|
|
27
59
|
export declare function mirrorSessionTasksToKanban(projectRoot: string | undefined, tasks: readonly TaskItem[], sessionId: string): void;
|
|
28
60
|
export declare function mirrorSessionPlanToKanban(projectRoot: string | undefined, items: readonly PlanItem[], sessionId: string): void;
|
|
@@ -33,6 +65,38 @@ export declare function mirrorSessionPlanToKanban(projectRoot: string | undefine
|
|
|
33
65
|
*/
|
|
34
66
|
export declare function attachSessionKanbanMirror(context: Context): () => void;
|
|
35
67
|
/** Fully hydrate a session board before a host announces the session as ready. */
|
|
68
|
+
/**
|
|
69
|
+
* Re-bind the card this session was working, after a restart or `--resume`.
|
|
70
|
+
*
|
|
71
|
+
* The binding produced by `start_task` lives in `ctx.meta.kanban`, which is
|
|
72
|
+
* conversation state — nothing restores it. So a resumed session came back
|
|
73
|
+
* with `currentKanbanTaskId` undefined while its card still sat in Running on
|
|
74
|
+
* the board: file events lost their task attribution, and under
|
|
75
|
+
* `tools.kanbanGovernance` the run was un-bound until the model happened to
|
|
76
|
+
* call `start_task` again.
|
|
77
|
+
*
|
|
78
|
+
* Board presence is the durable anchor. It is keyed `<sessionId>:<agentId>`,
|
|
79
|
+
* carries the `taskId`, and is stored on the board record itself, so it
|
|
80
|
+
* survives the restart even though its derived `active` flag does not (the TTL
|
|
81
|
+
* is two minutes). Matching it against the cards that are actually still
|
|
82
|
+
* running gives an unambiguous answer without guessing from free-text agent
|
|
83
|
+
* ids.
|
|
84
|
+
*
|
|
85
|
+
* Deliberately conservative — it prefers to do nothing over binding the wrong
|
|
86
|
+
* card:
|
|
87
|
+
* - never overrides a binding this process already has;
|
|
88
|
+
* - only considers cards whose assignment is still `running`;
|
|
89
|
+
* - skips an expired lease, because `recover_stale` may have reassigned that
|
|
90
|
+
* card to another worker;
|
|
91
|
+
* - picks the most recently seen entry when a session touched several.
|
|
92
|
+
*
|
|
93
|
+
* Session mirrors and archived boards are excluded by the snapshot's default
|
|
94
|
+
* board-kind filter, which is correct: `start_task` never targets them.
|
|
95
|
+
*/
|
|
96
|
+
export declare function rebindSessionKanbanTask(context: Context): Promise<{
|
|
97
|
+
boardId: string;
|
|
98
|
+
taskId: string;
|
|
99
|
+
} | null>;
|
|
36
100
|
export declare function hydrateSessionKanban(context: Context): Promise<KanbanBoard | null>;
|
|
37
101
|
export interface SessionKanbanSourceUpdate {
|
|
38
102
|
source: 'todo' | 'task' | 'plan' | null;
|
|
@@ -40,6 +104,27 @@ export interface SessionKanbanSourceUpdate {
|
|
|
40
104
|
tasks?: TaskFile | undefined;
|
|
41
105
|
plan?: PlanFile | undefined;
|
|
42
106
|
}
|
|
107
|
+
/**
|
|
108
|
+
* Human-readable titles of the unfinished dependencies blocking a card.
|
|
109
|
+
*
|
|
110
|
+
* The board recomputes readiness on every mutation; this only carries the
|
|
111
|
+
* result to the todo surface. Titles rather than ids, because the row is read
|
|
112
|
+
* by a model and a human, neither of whom can resolve a raw card id.
|
|
113
|
+
*/
|
|
114
|
+
export declare function blockingTitles(board: KanbanBoard, task: KanbanTask): string[];
|
|
115
|
+
/**
|
|
116
|
+
* Order cards the way they must actually be worked: dependencies first, then
|
|
117
|
+
* priority, then the board's own column/manual order.
|
|
118
|
+
*
|
|
119
|
+
* The managed projection previously sorted by `createdAt` — the moment a card
|
|
120
|
+
* happened to be created — which is unrelated to execution order, so the list
|
|
121
|
+
* the model reads top-to-bottom was not a plan. A dependency-respecting
|
|
122
|
+
* (Kahn) order makes the visible sequence the executable sequence; ties fall
|
|
123
|
+
* back to the deterministic board order so the list never reshuffles on its
|
|
124
|
+
* own. Any card left over by a dependency cycle is appended rather than
|
|
125
|
+
* dropped — the board rejects cycles, but a projection must never lose work.
|
|
126
|
+
*/
|
|
127
|
+
export declare function orderTasksForTodos(board: KanbanBoard, tasks: readonly KanbanTask[]): KanbanTask[];
|
|
43
128
|
/**
|
|
44
129
|
* Replace the tactical todo list from the current cards on a session-owned board.
|
|
45
130
|
*
|