@brainervirus/workit-cursor 8.9.0 → 8.9.1
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/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +172 -46
- package/dist/mcp-server.js +155 -28
- package/dist/workit-hook.js +175 -42
- package/package.json +3 -3
|
@@ -13109,6 +13109,66 @@ class TaskStore {
|
|
|
13109
13109
|
return bound;
|
|
13110
13110
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
13111
13111
|
}
|
|
13112
|
+
taskForWorker(workerId) {
|
|
13113
|
+
if (typeof workerId !== "string" || !validId(workerId))
|
|
13114
|
+
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
13115
|
+
const listed = this.listTaskIndex();
|
|
13116
|
+
if (!listed.ok)
|
|
13117
|
+
return listed;
|
|
13118
|
+
for (const entry of listed.data) {
|
|
13119
|
+
if (entry.status === "closed")
|
|
13120
|
+
continue;
|
|
13121
|
+
const loaded = this.load(entry.id);
|
|
13122
|
+
if (!loaded.ok)
|
|
13123
|
+
return loaded;
|
|
13124
|
+
if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
|
|
13125
|
+
return success(null, null, entry.id);
|
|
13126
|
+
}
|
|
13127
|
+
return success(null, null, null);
|
|
13128
|
+
}
|
|
13129
|
+
originTaskEntry() {
|
|
13130
|
+
const key = this.currentKey();
|
|
13131
|
+
const location = this.location();
|
|
13132
|
+
const branch = key.ok ? key.data.branch : null;
|
|
13133
|
+
if (!branch || !location.ok)
|
|
13134
|
+
return success(null, null, null);
|
|
13135
|
+
const common = path13.dirname(location.data.dir);
|
|
13136
|
+
const heads = path13.join(common, "logs", "refs", "heads");
|
|
13137
|
+
const headLog = location.data.git ? path13.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
13138
|
+
const signatures = [
|
|
13139
|
+
path13.join(heads, branch),
|
|
13140
|
+
headLog,
|
|
13141
|
+
heads,
|
|
13142
|
+
path13.join(common, "packed-refs")
|
|
13143
|
+
].map((file) => fileSignature(file) ?? "-");
|
|
13144
|
+
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
13145
|
+
let resolved = origins.get(slot);
|
|
13146
|
+
if (resolved?.signature !== signatures.join("|")) {
|
|
13147
|
+
const renames = branchRenames(location.data);
|
|
13148
|
+
const aliases = [branch];
|
|
13149
|
+
for (const rename of renames.toReversed())
|
|
13150
|
+
if (rename.to === aliases.at(-1))
|
|
13151
|
+
aliases.push(rename.from);
|
|
13152
|
+
const start = branchStart(location.data, branch, aliases);
|
|
13153
|
+
const slash = start?.indexOf("/") ?? -1;
|
|
13154
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path13.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
13155
|
+
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
13156
|
+
resolved = {
|
|
13157
|
+
signature: signatures.join("|"),
|
|
13158
|
+
name: name && !aliases.includes(name) ? name : null,
|
|
13159
|
+
renames
|
|
13160
|
+
};
|
|
13161
|
+
origins.set(slot, resolved);
|
|
13162
|
+
}
|
|
13163
|
+
const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
|
|
13164
|
+
if (entry && !entry.ok)
|
|
13165
|
+
return entry;
|
|
13166
|
+
return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
|
|
13167
|
+
}
|
|
13168
|
+
readStoreTask(taskId) {
|
|
13169
|
+
const loaded = this.load(taskId);
|
|
13170
|
+
return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
|
|
13171
|
+
}
|
|
13112
13172
|
create(input, provenance, expectedWorkspaceRevision) {
|
|
13113
13173
|
const value = "intent" in input ? input : {
|
|
13114
13174
|
intent: input,
|
|
@@ -13778,12 +13838,12 @@ class TaskStore {
|
|
|
13778
13838
|
return 0;
|
|
13779
13839
|
}
|
|
13780
13840
|
}
|
|
13781
|
-
openTasksForKey(key) {
|
|
13841
|
+
openTasksForKey(key, known) {
|
|
13782
13842
|
const all = this.listStoreIndex();
|
|
13783
13843
|
if (!all.ok)
|
|
13784
13844
|
return all;
|
|
13785
|
-
const location = this.location();
|
|
13786
|
-
const renames = location
|
|
13845
|
+
const location = known ? null : this.location();
|
|
13846
|
+
const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
|
|
13787
13847
|
return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
|
|
13788
13848
|
}
|
|
13789
13849
|
openTaskForKey(key) {
|
|
@@ -14470,7 +14530,34 @@ var MIGRATION_PENDING = "workit migration pending", STORE_FORMAT = 3, STORE_MIN_
|
|
|
14470
14530
|
return left > right;
|
|
14471
14531
|
}
|
|
14472
14532
|
return false;
|
|
14473
|
-
}, validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value),
|
|
14533
|
+
}, validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value), readText = (file) => {
|
|
14534
|
+
try {
|
|
14535
|
+
return fs10.readFileSync(file, "utf8");
|
|
14536
|
+
} catch {
|
|
14537
|
+
return "";
|
|
14538
|
+
}
|
|
14539
|
+
}, REFLOG_LINE, branchStart = (location, branch, aliases) => {
|
|
14540
|
+
const first = readText(path13.join(path13.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
14541
|
+
`, 1)[0];
|
|
14542
|
+
const created = REFLOG_LINE.exec(first);
|
|
14543
|
+
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
14544
|
+
if (!created || !start)
|
|
14545
|
+
return null;
|
|
14546
|
+
if (start !== "HEAD")
|
|
14547
|
+
return start;
|
|
14548
|
+
if (!location.git)
|
|
14549
|
+
return null;
|
|
14550
|
+
for (const line of readText(path13.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
14551
|
+
`)) {
|
|
14552
|
+
const entry = REFLOG_LINE.exec(line);
|
|
14553
|
+
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
14554
|
+
const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
|
|
14555
|
+
if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
|
|
14556
|
+
return moved[1];
|
|
14557
|
+
}
|
|
14558
|
+
return null;
|
|
14559
|
+
}, refExists = (common, ref) => fs10.existsSync(path13.join(common, ref)) || readText(path13.join(common, "packed-refs")).includes(` ${ref}
|
|
14560
|
+
`), origins, sameDirectoryIdentity = (left, right) => {
|
|
14474
14561
|
if (!path13.isAbsolute(left) || !path13.isAbsolute(right))
|
|
14475
14562
|
return false;
|
|
14476
14563
|
const normalizedLeft = path13.resolve(left);
|
|
@@ -14518,6 +14605,8 @@ var init_task_store = __esm(() => {
|
|
|
14518
14605
|
AUTO_COMPACT_BYTES = 2 * 1024 * 1024;
|
|
14519
14606
|
TOMBSTONE = Symbol("tombstone");
|
|
14520
14607
|
STALE_TEMPORARY_MS = 60 * 60000;
|
|
14608
|
+
REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
14609
|
+
origins = new Map;
|
|
14521
14610
|
heldInProcess = new Map;
|
|
14522
14611
|
});
|
|
14523
14612
|
|
|
@@ -15507,38 +15596,29 @@ function reconcileResume(view, observations = []) {
|
|
|
15507
15596
|
reassessmentRequired: view.task.policy?.policyVersion !== POLICY_VERSION
|
|
15508
15597
|
});
|
|
15509
15598
|
}
|
|
15510
|
-
function compactTaskContext(view) {
|
|
15511
|
-
const decisions =
|
|
15512
|
-
|
|
15513
|
-
|
|
15514
|
-
|
|
15515
|
-
|
|
15516
|
-
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
15517
|
-
}).slice(0, COMPACT_MAX_ITEMS).map((entry) => ({
|
|
15518
|
-
id: entry.id,
|
|
15519
|
-
purpose: entry.data.purpose,
|
|
15520
|
-
status: entry.data.response,
|
|
15521
|
-
digest: entry.data.digest,
|
|
15522
|
-
choice: compactDecisionChoice(entry) || `task.inspect taskId=${view.task.id} view=full; decision=${entry.id}`,
|
|
15523
|
-
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference)
|
|
15524
|
-
}));
|
|
15525
|
-
const methods = view.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
15599
|
+
function compactTaskContext(view, inherited = null) {
|
|
15600
|
+
const decisions = compactDecisions([
|
|
15601
|
+
view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
|
|
15602
|
+
inherited
|
|
15603
|
+
]);
|
|
15604
|
+
const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
15526
15605
|
id: method.id,
|
|
15527
15606
|
assurance: method.assurance,
|
|
15528
15607
|
reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
|
|
15529
15608
|
})) : [];
|
|
15609
|
+
const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
|
|
15530
15610
|
const gaps = Array.from(new Set([
|
|
15531
|
-
...
|
|
15532
|
-
...view
|
|
15533
|
-
...view
|
|
15611
|
+
...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
|
|
15612
|
+
...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
|
|
15613
|
+
...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
|
|
15534
15614
|
])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
|
|
15535
15615
|
const context = {
|
|
15536
|
-
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
15537
|
-
status: view
|
|
15616
|
+
objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
|
|
15617
|
+
status: view?.task.status ?? null,
|
|
15538
15618
|
decisions,
|
|
15539
15619
|
methods,
|
|
15540
15620
|
gaps,
|
|
15541
|
-
nextAction: compactText(view
|
|
15621
|
+
nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
|
|
15542
15622
|
};
|
|
15543
15623
|
let encoded = JSON.stringify(context);
|
|
15544
15624
|
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
|
|
@@ -15581,7 +15661,21 @@ var COMPACT_MAX_BYTES = 4096, COMPACT_MAX_ITEMS = 8, COMPACT_TEXT_BYTES = 128, s
|
|
|
15581
15661
|
id: ref.id
|
|
15582
15662
|
};
|
|
15583
15663
|
return { kind: ref.kind };
|
|
15584
|
-
},
|
|
15664
|
+
}, newestDecisionFirst = (left, right) => {
|
|
15665
|
+
const leftAt = compactTimestamp(left.recordedAt);
|
|
15666
|
+
const rightAt = compactTimestamp(right.recordedAt);
|
|
15667
|
+
if (leftAt !== rightAt)
|
|
15668
|
+
return leftAt > rightAt ? -1 : 1;
|
|
15669
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
15670
|
+
}, compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
|
|
15671
|
+
id: entry.id,
|
|
15672
|
+
purpose: entry.data.purpose,
|
|
15673
|
+
status: entry.data.response,
|
|
15674
|
+
digest: entry.data.digest,
|
|
15675
|
+
choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
|
|
15676
|
+
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
|
|
15677
|
+
origin: source.origin ?? undefined
|
|
15678
|
+
})) : []).slice(0, COMPACT_MAX_ITEMS), exportDigest = (bundle) => sha256({
|
|
15585
15679
|
schemaVersion: bundle.schemaVersion,
|
|
15586
15680
|
exportedAt: bundle.exportedAt,
|
|
15587
15681
|
sourceWorkspaceId: bundle.sourceWorkspaceId,
|
|
@@ -15644,6 +15738,17 @@ class WorkitCore {
|
|
|
15644
15738
|
const root = this.contextRootError();
|
|
15645
15739
|
if (!root.ok)
|
|
15646
15740
|
return root;
|
|
15741
|
+
const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
|
|
15742
|
+
const workerId = addressed ? value.workerId : this.context.workerId ?? null;
|
|
15743
|
+
if (workerId !== null) {
|
|
15744
|
+
const owner = this.store.taskForWorker(workerId);
|
|
15745
|
+
if (!owner.ok)
|
|
15746
|
+
return owner;
|
|
15747
|
+
if (owner.data)
|
|
15748
|
+
return success(null, null, { ...request, taskId: owner.data });
|
|
15749
|
+
if (addressed)
|
|
15750
|
+
return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
|
|
15751
|
+
}
|
|
15647
15752
|
const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
|
|
15648
15753
|
const found = this.store.implicitTask({
|
|
15649
15754
|
provenance: provenance(this.context),
|
|
@@ -16350,7 +16455,9 @@ class WorkitCore {
|
|
|
16350
16455
|
if (!resolved.ok)
|
|
16351
16456
|
return resolved;
|
|
16352
16457
|
const value = resolved.data;
|
|
16353
|
-
|
|
16458
|
+
const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
|
|
16459
|
+
const { action, taskId } = value ?? {};
|
|
16460
|
+
return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
|
|
16354
16461
|
}
|
|
16355
16462
|
workerOnce(request) {
|
|
16356
16463
|
const root = this.contextRootError();
|
|
@@ -16555,10 +16662,18 @@ class WorkitCore {
|
|
|
16555
16662
|
requirements
|
|
16556
16663
|
});
|
|
16557
16664
|
}
|
|
16558
|
-
compactContext(taskId, freshness) {
|
|
16665
|
+
compactContext(taskId, freshness, origin = null) {
|
|
16559
16666
|
const root = this.contextRootError();
|
|
16560
16667
|
if (!root.ok)
|
|
16561
16668
|
return root;
|
|
16669
|
+
const base = origin && this.store.readStoreTask(origin.taskId);
|
|
16670
|
+
const inherited = base?.ok && base.data ? {
|
|
16671
|
+
origin: origin.origin,
|
|
16672
|
+
taskId: base.data.id,
|
|
16673
|
+
decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
|
|
16674
|
+
} : null;
|
|
16675
|
+
if (taskId === null)
|
|
16676
|
+
return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
|
|
16562
16677
|
const task = this.store.readTask(taskId);
|
|
16563
16678
|
if (!task.ok)
|
|
16564
16679
|
return task;
|
|
@@ -16568,7 +16683,7 @@ class WorkitCore {
|
|
|
16568
16683
|
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
16569
16684
|
if (!view.ok)
|
|
16570
16685
|
return view;
|
|
16571
|
-
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
16686
|
+
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
|
|
16572
16687
|
}
|
|
16573
16688
|
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
16574
16689
|
const workspace = this.store.readWorkspace();
|
|
@@ -16979,7 +17094,12 @@ var MAX_JUDGED_REFS = 5, workspaceVerification = (root) => {
|
|
|
16979
17094
|
return changed;
|
|
16980
17095
|
const updated = changed.data.task.workers.find((candidate) => candidate.id === input.workerId);
|
|
16981
17096
|
return updated ? success(changed.data.task.revision, changed.data.workspace.revision, updated) : failure2("recovery_required", "worker disappeared during lifecycle observation");
|
|
16982
|
-
}, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS,
|
|
17097
|
+
}, refsWithinScope = (refs, scope) => refs.every((ref) => ref.kind !== "file" || scopeCovers(scope, { description: "", paths: [ref.path], exclusions: [] })), TASK_SCOPED_ACTIONS, WORKER_ADDRESSED_ACTIONS, workerCommand = (taskId, request) => {
|
|
17098
|
+
const { action, workerId, reason, report } = request;
|
|
17099
|
+
const rest = JSON.stringify({ reason, report });
|
|
17100
|
+
const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
|
|
17101
|
+
return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
|
|
17102
|
+
}, CREATING_ACTIONS, REVISION_RETRY_ATTEMPTS = 8, retryPause, filledRevisions = (request) => {
|
|
16983
17103
|
const value = typeof request === "object" && request !== null ? request : {};
|
|
16984
17104
|
return {
|
|
16985
17105
|
task: value.expectedRevision === undefined,
|
|
@@ -17201,6 +17321,7 @@ var init_task_engine = __esm(() => {
|
|
|
17201
17321
|
"worker.cancel",
|
|
17202
17322
|
"state.export"
|
|
17203
17323
|
]);
|
|
17324
|
+
WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
17204
17325
|
CREATING_ACTIONS = new Set([
|
|
17205
17326
|
"task.revise",
|
|
17206
17327
|
"task.progress",
|
|
@@ -17248,18 +17369,23 @@ function sessionCompactContext(store, session, context, selection = "session-bou
|
|
|
17248
17369
|
if (!listed.ok)
|
|
17249
17370
|
return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
|
|
17250
17371
|
const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
|
|
17251
|
-
return
|
|
17372
|
+
return entryCompactContext(store, entry, session, context);
|
|
17252
17373
|
}
|
|
17253
17374
|
function entryCompactContext(store, entry, session, context) {
|
|
17375
|
+
const found = store.originTaskEntry();
|
|
17376
|
+
const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
|
|
17377
|
+
if (!entry && !origin)
|
|
17378
|
+
return null;
|
|
17254
17379
|
const workspace = store.readWorkspace();
|
|
17255
|
-
if (!workspace.ok || !workspace.data)
|
|
17380
|
+
if (!workspace.ok || entry && !workspace.data)
|
|
17256
17381
|
return null;
|
|
17257
17382
|
const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
|
|
17258
17383
|
const key = canonicalJson({
|
|
17259
|
-
task: entry
|
|
17260
|
-
revision: entry
|
|
17261
|
-
file: entry
|
|
17262
|
-
|
|
17384
|
+
task: entry?.id ?? null,
|
|
17385
|
+
revision: entry?.revision ?? null,
|
|
17386
|
+
file: entry?.file ?? null,
|
|
17387
|
+
origin: origin && origin.entry.file,
|
|
17388
|
+
workspace: workspace.data?.revision ?? null,
|
|
17263
17389
|
caller: context.caller,
|
|
17264
17390
|
workerId: context.workerId ?? null,
|
|
17265
17391
|
capabilities: context.capabilities
|
|
@@ -17267,15 +17393,15 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
17267
17393
|
const hit = cache.get(slot);
|
|
17268
17394
|
if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
|
|
17269
17395
|
return hit.value;
|
|
17270
|
-
const task = store.readTask(entry.id);
|
|
17271
|
-
if (!task.ok)
|
|
17396
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
17397
|
+
if (task && !task.ok)
|
|
17272
17398
|
return null;
|
|
17273
17399
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
17274
|
-
const observed = task
|
|
17275
|
-
const files = task
|
|
17400
|
+
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
17401
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
|
|
17276
17402
|
const observedFiles = filesKey(files);
|
|
17277
|
-
const compact = new WorkitCore(store, context).compactContext(entry
|
|
17278
|
-
if (!compact.ok)
|
|
17403
|
+
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
17404
|
+
if (!compact.ok || !compact.data)
|
|
17279
17405
|
return null;
|
|
17280
17406
|
cache.delete(slot);
|
|
17281
17407
|
cache.set(slot, {
|
|
@@ -17294,8 +17420,8 @@ var MIGRATION_PENDING_NOTE = "workit migration pending — run `workit task stat
|
|
|
17294
17420
|
return sessionTaskEntry(entries, session);
|
|
17295
17421
|
const active = entries.filter((entry) => entry.status === "active");
|
|
17296
17422
|
return active.length === 1 ? active[0] : null;
|
|
17297
|
-
}, implicitTaskEntry = (store, entries, session) => {
|
|
17298
|
-
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !entry
|
|
17423
|
+
}, otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle)), implicitTaskEntry = (store, entries, session) => {
|
|
17424
|
+
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
|
|
17299
17425
|
if (open.length === 0)
|
|
17300
17426
|
return null;
|
|
17301
17427
|
const key = store.currentKey();
|
|
@@ -17346,7 +17472,7 @@ ${options.addendum}` : ""}
|
|
|
17346
17472
|
if (!listed.ok)
|
|
17347
17473
|
throw new Error(listed.error);
|
|
17348
17474
|
const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
|
|
17349
|
-
const text =
|
|
17475
|
+
const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
|
|
17350
17476
|
if (text)
|
|
17351
17477
|
compact = `
|
|
17352
17478
|
<workit-task-context>${text}</workit-task-context>`;
|
package/dist/mcp-server.js
CHANGED
|
@@ -19409,6 +19409,38 @@ var isNewerVersion = (candidate, current) => {
|
|
|
19409
19409
|
return false;
|
|
19410
19410
|
};
|
|
19411
19411
|
var validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);
|
|
19412
|
+
var readText = (file) => {
|
|
19413
|
+
try {
|
|
19414
|
+
return fs7.readFileSync(file, "utf8");
|
|
19415
|
+
} catch {
|
|
19416
|
+
return "";
|
|
19417
|
+
}
|
|
19418
|
+
};
|
|
19419
|
+
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
19420
|
+
var branchStart = (location, branch, aliases) => {
|
|
19421
|
+
const first = readText(path6.join(path6.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
19422
|
+
`, 1)[0];
|
|
19423
|
+
const created = REFLOG_LINE.exec(first);
|
|
19424
|
+
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
19425
|
+
if (!created || !start)
|
|
19426
|
+
return null;
|
|
19427
|
+
if (start !== "HEAD")
|
|
19428
|
+
return start;
|
|
19429
|
+
if (!location.git)
|
|
19430
|
+
return null;
|
|
19431
|
+
for (const line of readText(path6.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
19432
|
+
`)) {
|
|
19433
|
+
const entry = REFLOG_LINE.exec(line);
|
|
19434
|
+
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
19435
|
+
const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
|
|
19436
|
+
if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
|
|
19437
|
+
return moved[1];
|
|
19438
|
+
}
|
|
19439
|
+
return null;
|
|
19440
|
+
};
|
|
19441
|
+
var refExists = (common, ref) => fs7.existsSync(path6.join(common, ref)) || readText(path6.join(common, "packed-refs")).includes(` ${ref}
|
|
19442
|
+
`);
|
|
19443
|
+
var origins = new Map;
|
|
19412
19444
|
var sameDirectoryIdentity = (left, right) => {
|
|
19413
19445
|
if (!path6.isAbsolute(left) || !path6.isAbsolute(right))
|
|
19414
19446
|
return false;
|
|
@@ -19661,6 +19693,66 @@ class TaskStore {
|
|
|
19661
19693
|
return bound;
|
|
19662
19694
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
19663
19695
|
}
|
|
19696
|
+
taskForWorker(workerId) {
|
|
19697
|
+
if (typeof workerId !== "string" || !validId(workerId))
|
|
19698
|
+
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
19699
|
+
const listed = this.listTaskIndex();
|
|
19700
|
+
if (!listed.ok)
|
|
19701
|
+
return listed;
|
|
19702
|
+
for (const entry of listed.data) {
|
|
19703
|
+
if (entry.status === "closed")
|
|
19704
|
+
continue;
|
|
19705
|
+
const loaded = this.load(entry.id);
|
|
19706
|
+
if (!loaded.ok)
|
|
19707
|
+
return loaded;
|
|
19708
|
+
if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
|
|
19709
|
+
return success(null, null, entry.id);
|
|
19710
|
+
}
|
|
19711
|
+
return success(null, null, null);
|
|
19712
|
+
}
|
|
19713
|
+
originTaskEntry() {
|
|
19714
|
+
const key = this.currentKey();
|
|
19715
|
+
const location = this.location();
|
|
19716
|
+
const branch = key.ok ? key.data.branch : null;
|
|
19717
|
+
if (!branch || !location.ok)
|
|
19718
|
+
return success(null, null, null);
|
|
19719
|
+
const common = path6.dirname(location.data.dir);
|
|
19720
|
+
const heads = path6.join(common, "logs", "refs", "heads");
|
|
19721
|
+
const headLog = location.data.git ? path6.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
19722
|
+
const signatures = [
|
|
19723
|
+
path6.join(heads, branch),
|
|
19724
|
+
headLog,
|
|
19725
|
+
heads,
|
|
19726
|
+
path6.join(common, "packed-refs")
|
|
19727
|
+
].map((file) => fileSignature(file) ?? "-");
|
|
19728
|
+
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
19729
|
+
let resolved = origins.get(slot);
|
|
19730
|
+
if (resolved?.signature !== signatures.join("|")) {
|
|
19731
|
+
const renames = branchRenames(location.data);
|
|
19732
|
+
const aliases = [branch];
|
|
19733
|
+
for (const rename of renames.toReversed())
|
|
19734
|
+
if (rename.to === aliases.at(-1))
|
|
19735
|
+
aliases.push(rename.from);
|
|
19736
|
+
const start = branchStart(location.data, branch, aliases);
|
|
19737
|
+
const slash = start?.indexOf("/") ?? -1;
|
|
19738
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path6.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
19739
|
+
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
19740
|
+
resolved = {
|
|
19741
|
+
signature: signatures.join("|"),
|
|
19742
|
+
name: name && !aliases.includes(name) ? name : null,
|
|
19743
|
+
renames
|
|
19744
|
+
};
|
|
19745
|
+
origins.set(slot, resolved);
|
|
19746
|
+
}
|
|
19747
|
+
const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
|
|
19748
|
+
if (entry && !entry.ok)
|
|
19749
|
+
return entry;
|
|
19750
|
+
return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
|
|
19751
|
+
}
|
|
19752
|
+
readStoreTask(taskId) {
|
|
19753
|
+
const loaded = this.load(taskId);
|
|
19754
|
+
return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
|
|
19755
|
+
}
|
|
19664
19756
|
create(input, provenance, expectedWorkspaceRevision) {
|
|
19665
19757
|
const value = "intent" in input ? input : {
|
|
19666
19758
|
intent: input,
|
|
@@ -20330,12 +20422,12 @@ class TaskStore {
|
|
|
20330
20422
|
return 0;
|
|
20331
20423
|
}
|
|
20332
20424
|
}
|
|
20333
|
-
openTasksForKey(key) {
|
|
20425
|
+
openTasksForKey(key, known) {
|
|
20334
20426
|
const all = this.listStoreIndex();
|
|
20335
20427
|
if (!all.ok)
|
|
20336
20428
|
return all;
|
|
20337
|
-
const location = this.location();
|
|
20338
|
-
const renames = location
|
|
20429
|
+
const location = known ? null : this.location();
|
|
20430
|
+
const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
|
|
20339
20431
|
return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
|
|
20340
20432
|
}
|
|
20341
20433
|
openTaskForKey(key) {
|
|
@@ -24421,38 +24513,45 @@ var compactReference = (ref) => {
|
|
|
24421
24513
|
};
|
|
24422
24514
|
return { kind: ref.kind };
|
|
24423
24515
|
};
|
|
24424
|
-
|
|
24425
|
-
const
|
|
24426
|
-
|
|
24427
|
-
|
|
24428
|
-
|
|
24429
|
-
|
|
24430
|
-
|
|
24431
|
-
|
|
24432
|
-
|
|
24433
|
-
|
|
24434
|
-
|
|
24435
|
-
|
|
24436
|
-
|
|
24437
|
-
|
|
24438
|
-
|
|
24439
|
-
|
|
24516
|
+
var newestDecisionFirst = (left, right) => {
|
|
24517
|
+
const leftAt = compactTimestamp(left.recordedAt);
|
|
24518
|
+
const rightAt = compactTimestamp(right.recordedAt);
|
|
24519
|
+
if (leftAt !== rightAt)
|
|
24520
|
+
return leftAt > rightAt ? -1 : 1;
|
|
24521
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
24522
|
+
};
|
|
24523
|
+
var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
|
|
24524
|
+
id: entry.id,
|
|
24525
|
+
purpose: entry.data.purpose,
|
|
24526
|
+
status: entry.data.response,
|
|
24527
|
+
digest: entry.data.digest,
|
|
24528
|
+
choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
|
|
24529
|
+
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
|
|
24530
|
+
origin: source.origin ?? undefined
|
|
24531
|
+
})) : []).slice(0, COMPACT_MAX_ITEMS);
|
|
24532
|
+
function compactTaskContext(view, inherited = null) {
|
|
24533
|
+
const decisions = compactDecisions([
|
|
24534
|
+
view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
|
|
24535
|
+
inherited
|
|
24536
|
+
]);
|
|
24537
|
+
const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
24440
24538
|
id: method.id,
|
|
24441
24539
|
assurance: method.assurance,
|
|
24442
24540
|
reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
|
|
24443
24541
|
})) : [];
|
|
24542
|
+
const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
|
|
24444
24543
|
const gaps = Array.from(new Set([
|
|
24445
|
-
...
|
|
24446
|
-
...view
|
|
24447
|
-
...view
|
|
24544
|
+
...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
|
|
24545
|
+
...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
|
|
24546
|
+
...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
|
|
24448
24547
|
])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
|
|
24449
24548
|
const context = {
|
|
24450
|
-
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
24451
|
-
status: view
|
|
24549
|
+
objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
|
|
24550
|
+
status: view?.task.status ?? null,
|
|
24452
24551
|
decisions,
|
|
24453
24552
|
methods,
|
|
24454
24553
|
gaps,
|
|
24455
|
-
nextAction: compactText(view
|
|
24554
|
+
nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
|
|
24456
24555
|
};
|
|
24457
24556
|
let encoded = JSON.stringify(context);
|
|
24458
24557
|
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
|
|
@@ -25057,6 +25156,13 @@ var TASK_SCOPED_ACTIONS = new Set([
|
|
|
25057
25156
|
"worker.cancel",
|
|
25058
25157
|
"state.export"
|
|
25059
25158
|
]);
|
|
25159
|
+
var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
25160
|
+
var workerCommand = (taskId, request) => {
|
|
25161
|
+
const { action, workerId, reason, report } = request;
|
|
25162
|
+
const rest = JSON.stringify({ reason, report });
|
|
25163
|
+
const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
|
|
25164
|
+
return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
|
|
25165
|
+
};
|
|
25060
25166
|
var CREATING_ACTIONS = new Set([
|
|
25061
25167
|
"task.revise",
|
|
25062
25168
|
"task.progress",
|
|
@@ -25307,6 +25413,17 @@ class WorkitCore {
|
|
|
25307
25413
|
const root = this.contextRootError();
|
|
25308
25414
|
if (!root.ok)
|
|
25309
25415
|
return root;
|
|
25416
|
+
const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
|
|
25417
|
+
const workerId = addressed ? value.workerId : this.context.workerId ?? null;
|
|
25418
|
+
if (workerId !== null) {
|
|
25419
|
+
const owner = this.store.taskForWorker(workerId);
|
|
25420
|
+
if (!owner.ok)
|
|
25421
|
+
return owner;
|
|
25422
|
+
if (owner.data)
|
|
25423
|
+
return success(null, null, { ...request, taskId: owner.data });
|
|
25424
|
+
if (addressed)
|
|
25425
|
+
return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
|
|
25426
|
+
}
|
|
25310
25427
|
const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
|
|
25311
25428
|
const found = this.store.implicitTask({
|
|
25312
25429
|
provenance: provenance(this.context),
|
|
@@ -26013,7 +26130,9 @@ class WorkitCore {
|
|
|
26013
26130
|
if (!resolved.ok)
|
|
26014
26131
|
return resolved;
|
|
26015
26132
|
const value = resolved.data;
|
|
26016
|
-
|
|
26133
|
+
const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
|
|
26134
|
+
const { action, taskId } = value ?? {};
|
|
26135
|
+
return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
|
|
26017
26136
|
}
|
|
26018
26137
|
workerOnce(request) {
|
|
26019
26138
|
const root = this.contextRootError();
|
|
@@ -26218,10 +26337,18 @@ class WorkitCore {
|
|
|
26218
26337
|
requirements
|
|
26219
26338
|
});
|
|
26220
26339
|
}
|
|
26221
|
-
compactContext(taskId, freshness) {
|
|
26340
|
+
compactContext(taskId, freshness, origin = null) {
|
|
26222
26341
|
const root = this.contextRootError();
|
|
26223
26342
|
if (!root.ok)
|
|
26224
26343
|
return root;
|
|
26344
|
+
const base = origin && this.store.readStoreTask(origin.taskId);
|
|
26345
|
+
const inherited = base?.ok && base.data ? {
|
|
26346
|
+
origin: origin.origin,
|
|
26347
|
+
taskId: base.data.id,
|
|
26348
|
+
decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
|
|
26349
|
+
} : null;
|
|
26350
|
+
if (taskId === null)
|
|
26351
|
+
return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
|
|
26225
26352
|
const task = this.store.readTask(taskId);
|
|
26226
26353
|
if (!task.ok)
|
|
26227
26354
|
return task;
|
|
@@ -26231,7 +26358,7 @@ class WorkitCore {
|
|
|
26231
26358
|
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
26232
26359
|
if (!view.ok)
|
|
26233
26360
|
return view;
|
|
26234
|
-
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
26361
|
+
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
|
|
26235
26362
|
}
|
|
26236
26363
|
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
26237
26364
|
const workspace = this.store.readWorkspace();
|
package/dist/workit-hook.js
CHANGED
|
@@ -12972,6 +12972,38 @@ var isNewerVersion = (candidate, current) => {
|
|
|
12972
12972
|
return false;
|
|
12973
12973
|
};
|
|
12974
12974
|
var validId = (value) => /^[0-9a-f]{8}-[0-9a-f]{4}-[1-5][0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(value);
|
|
12975
|
+
var readText = (file) => {
|
|
12976
|
+
try {
|
|
12977
|
+
return fs10.readFileSync(file, "utf8");
|
|
12978
|
+
} catch {
|
|
12979
|
+
return "";
|
|
12980
|
+
}
|
|
12981
|
+
};
|
|
12982
|
+
var REFLOG_LINE = /^[0-9a-f]+ ([0-9a-f]+) .* (\d+) [+-]\d{4}\t(.*)$/u;
|
|
12983
|
+
var branchStart = (location, branch, aliases) => {
|
|
12984
|
+
const first = readText(path13.join(path13.dirname(location.dir), "logs", "refs", "heads", branch)).split(`
|
|
12985
|
+
`, 1)[0];
|
|
12986
|
+
const created = REFLOG_LINE.exec(first);
|
|
12987
|
+
const start = created && /^branch: Created from (\S+)$/u.exec(created[3])?.[1];
|
|
12988
|
+
if (!created || !start)
|
|
12989
|
+
return null;
|
|
12990
|
+
if (start !== "HEAD")
|
|
12991
|
+
return start;
|
|
12992
|
+
if (!location.git)
|
|
12993
|
+
return null;
|
|
12994
|
+
for (const line of readText(path13.join(location.git.gitDir, "logs", "HEAD")).split(`
|
|
12995
|
+
`)) {
|
|
12996
|
+
const entry = REFLOG_LINE.exec(line);
|
|
12997
|
+
const moved = entry && /^checkout: moving from (\S+) to (\S+)$/u.exec(entry[3]);
|
|
12998
|
+
const seconds = entry ? Number(entry[2]) - Number(created[2]) : -1;
|
|
12999
|
+
if (moved && entry[1] === created[1] && seconds >= 0 && seconds <= 1 && aliases.includes(moved[2]))
|
|
13000
|
+
return moved[1];
|
|
13001
|
+
}
|
|
13002
|
+
return null;
|
|
13003
|
+
};
|
|
13004
|
+
var refExists = (common, ref) => fs10.existsSync(path13.join(common, ref)) || readText(path13.join(common, "packed-refs")).includes(` ${ref}
|
|
13005
|
+
`);
|
|
13006
|
+
var origins = new Map;
|
|
12975
13007
|
var sameDirectoryIdentity = (left, right) => {
|
|
12976
13008
|
if (!path13.isAbsolute(left) || !path13.isAbsolute(right))
|
|
12977
13009
|
return false;
|
|
@@ -13224,6 +13256,66 @@ class TaskStore {
|
|
|
13224
13256
|
return bound;
|
|
13225
13257
|
return success(null, null, { key: key.data, bound: bound.data });
|
|
13226
13258
|
}
|
|
13259
|
+
taskForWorker(workerId) {
|
|
13260
|
+
if (typeof workerId !== "string" || !validId(workerId))
|
|
13261
|
+
return failure2("invalid_input", "needs a worker id: `workit worker cancel|report <workerId>`");
|
|
13262
|
+
const listed = this.listTaskIndex();
|
|
13263
|
+
if (!listed.ok)
|
|
13264
|
+
return listed;
|
|
13265
|
+
for (const entry of listed.data) {
|
|
13266
|
+
if (entry.status === "closed")
|
|
13267
|
+
continue;
|
|
13268
|
+
const loaded = this.load(entry.id);
|
|
13269
|
+
if (!loaded.ok)
|
|
13270
|
+
return loaded;
|
|
13271
|
+
if (loaded.data?.record.workers.some((worker) => worker.id === workerId))
|
|
13272
|
+
return success(null, null, entry.id);
|
|
13273
|
+
}
|
|
13274
|
+
return success(null, null, null);
|
|
13275
|
+
}
|
|
13276
|
+
originTaskEntry() {
|
|
13277
|
+
const key = this.currentKey();
|
|
13278
|
+
const location = this.location();
|
|
13279
|
+
const branch = key.ok ? key.data.branch : null;
|
|
13280
|
+
if (!branch || !location.ok)
|
|
13281
|
+
return success(null, null, null);
|
|
13282
|
+
const common = path13.dirname(location.data.dir);
|
|
13283
|
+
const heads = path13.join(common, "logs", "refs", "heads");
|
|
13284
|
+
const headLog = location.data.git ? path13.join(location.data.git.gitDir, "logs", "HEAD") : "";
|
|
13285
|
+
const signatures = [
|
|
13286
|
+
path13.join(heads, branch),
|
|
13287
|
+
headLog,
|
|
13288
|
+
heads,
|
|
13289
|
+
path13.join(common, "packed-refs")
|
|
13290
|
+
].map((file) => fileSignature(file) ?? "-");
|
|
13291
|
+
const slot = `${location.data.dir}\x00${headLog}\x00${branch}`;
|
|
13292
|
+
let resolved = origins.get(slot);
|
|
13293
|
+
if (resolved?.signature !== signatures.join("|")) {
|
|
13294
|
+
const renames = branchRenames(location.data);
|
|
13295
|
+
const aliases = [branch];
|
|
13296
|
+
for (const rename of renames.toReversed())
|
|
13297
|
+
if (rename.to === aliases.at(-1))
|
|
13298
|
+
aliases.push(rename.from);
|
|
13299
|
+
const start = branchStart(location.data, branch, aliases);
|
|
13300
|
+
const slash = start?.indexOf("/") ?? -1;
|
|
13301
|
+
const remote = slash > 0 && !refExists(common, `refs/heads/${start}`) && (refExists(common, `refs/remotes/${start}`) || readText(path13.join(common, "config")).includes(`[remote "${start.slice(0, slash)}"]`));
|
|
13302
|
+
const name = start && (remote ? start.slice(slash + 1) : start);
|
|
13303
|
+
resolved = {
|
|
13304
|
+
signature: signatures.join("|"),
|
|
13305
|
+
name: name && !aliases.includes(name) ? name : null,
|
|
13306
|
+
renames
|
|
13307
|
+
};
|
|
13308
|
+
origins.set(slot, resolved);
|
|
13309
|
+
}
|
|
13310
|
+
const entry = resolved.name ? this.openTasksForKey(resolved.name, resolved.renames) : null;
|
|
13311
|
+
if (entry && !entry.ok)
|
|
13312
|
+
return entry;
|
|
13313
|
+
return success(null, null, entry?.data[0] ? { origin: `branch ${resolved.name}`, entry: entry.data[0] } : null);
|
|
13314
|
+
}
|
|
13315
|
+
readStoreTask(taskId) {
|
|
13316
|
+
const loaded = this.load(taskId);
|
|
13317
|
+
return loaded.ok ? success(null, null, loaded.data?.record ?? null) : loaded;
|
|
13318
|
+
}
|
|
13227
13319
|
create(input, provenance, expectedWorkspaceRevision) {
|
|
13228
13320
|
const value = "intent" in input ? input : {
|
|
13229
13321
|
intent: input,
|
|
@@ -13893,12 +13985,12 @@ class TaskStore {
|
|
|
13893
13985
|
return 0;
|
|
13894
13986
|
}
|
|
13895
13987
|
}
|
|
13896
|
-
openTasksForKey(key) {
|
|
13988
|
+
openTasksForKey(key, known) {
|
|
13897
13989
|
const all = this.listStoreIndex();
|
|
13898
13990
|
if (!all.ok)
|
|
13899
13991
|
return all;
|
|
13900
|
-
const location = this.location();
|
|
13901
|
-
const renames = location
|
|
13992
|
+
const location = known ? null : this.location();
|
|
13993
|
+
const renames = known ?? (location?.ok ? branchRenames(location.data) : []);
|
|
13902
13994
|
return success(null, null, all.data.filter((entry) => entry.key !== null && entry.status !== "closed").filter((entry) => (renames.length ? followRenames(entry.key, entry.boundAt ?? entry.createdAt, renames) : entry.key) === key).toSorted(oldestFirst));
|
|
13903
13995
|
}
|
|
13904
13996
|
openTaskForKey(key) {
|
|
@@ -15573,38 +15665,45 @@ var compactReference = (ref) => {
|
|
|
15573
15665
|
};
|
|
15574
15666
|
return { kind: ref.kind };
|
|
15575
15667
|
};
|
|
15576
|
-
|
|
15577
|
-
const
|
|
15578
|
-
|
|
15579
|
-
|
|
15580
|
-
|
|
15581
|
-
|
|
15582
|
-
|
|
15583
|
-
|
|
15584
|
-
|
|
15585
|
-
|
|
15586
|
-
|
|
15587
|
-
|
|
15588
|
-
|
|
15589
|
-
|
|
15590
|
-
|
|
15591
|
-
|
|
15668
|
+
var newestDecisionFirst = (left, right) => {
|
|
15669
|
+
const leftAt = compactTimestamp(left.recordedAt);
|
|
15670
|
+
const rightAt = compactTimestamp(right.recordedAt);
|
|
15671
|
+
if (leftAt !== rightAt)
|
|
15672
|
+
return leftAt > rightAt ? -1 : 1;
|
|
15673
|
+
return left.id < right.id ? -1 : left.id > right.id ? 1 : 0;
|
|
15674
|
+
};
|
|
15675
|
+
var compactDecisions = (sources) => sources.flatMap((source) => source ? source.decisions.toSorted(newestDecisionFirst).map((entry) => ({
|
|
15676
|
+
id: entry.id,
|
|
15677
|
+
purpose: entry.data.purpose,
|
|
15678
|
+
status: entry.data.response,
|
|
15679
|
+
digest: entry.data.digest,
|
|
15680
|
+
choice: compactDecisionChoice(entry) || `task.inspect taskId=${source.taskId} view=full; decision=${entry.id}`,
|
|
15681
|
+
references: entry.data.binding.contentRefs.slice(0, 1).map(compactReference),
|
|
15682
|
+
origin: source.origin ?? undefined
|
|
15683
|
+
})) : []).slice(0, COMPACT_MAX_ITEMS);
|
|
15684
|
+
function compactTaskContext(view, inherited = null) {
|
|
15685
|
+
const decisions = compactDecisions([
|
|
15686
|
+
view && { origin: null, taskId: view.task.id, decisions: view.task.decisions },
|
|
15687
|
+
inherited
|
|
15688
|
+
]);
|
|
15689
|
+
const methods = view?.task.policy ? selectMethods(view.task.policy, view.capabilities).map((method) => ({
|
|
15592
15690
|
id: method.id,
|
|
15593
15691
|
assurance: method.assurance,
|
|
15594
15692
|
reason: compactText(method.reason, COMPACT_TEXT_BYTES) ?? ""
|
|
15595
15693
|
})) : [];
|
|
15694
|
+
const stale = view?.evidence.filter((entry) => entry.status === "stale").length;
|
|
15596
15695
|
const gaps = Array.from(new Set([
|
|
15597
|
-
...
|
|
15598
|
-
...view
|
|
15599
|
-
...view
|
|
15696
|
+
...stale ? [`stale evidence: ${stale}; workit task inspect --task ${view.task.id} --view full`] : [],
|
|
15697
|
+
...(view?.requirements ?? []).filter((entry) => entry.status !== "satisfied").map((entry) => entry.reason),
|
|
15698
|
+
...(view?.task.progress.blockers ?? []).map((entry) => entry.reason)
|
|
15600
15699
|
])).toSorted().slice(0, COMPACT_MAX_ITEMS).map((gap) => compactText(gap, COMPACT_TEXT_BYTES));
|
|
15601
15700
|
const context = {
|
|
15602
|
-
objective: compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "",
|
|
15603
|
-
status: view
|
|
15701
|
+
objective: view ? compactText(view.task.intent.data.objective, COMPACT_TEXT_BYTES) ?? "" : "no task on this branch yet",
|
|
15702
|
+
status: view?.task.status ?? null,
|
|
15604
15703
|
decisions,
|
|
15605
15704
|
methods,
|
|
15606
15705
|
gaps,
|
|
15607
|
-
nextAction: compactText(view
|
|
15706
|
+
nextAction: compactText(view?.task.progress.nextAction ?? null, COMPACT_TEXT_BYTES)
|
|
15608
15707
|
};
|
|
15609
15708
|
let encoded = JSON.stringify(context);
|
|
15610
15709
|
while (compactBytes(encoded) > COMPACT_MAX_BYTES && (context.decisions.length || context.methods.length || context.gaps.length)) {
|
|
@@ -15793,6 +15892,13 @@ var TASK_SCOPED_ACTIONS = new Set([
|
|
|
15793
15892
|
"worker.cancel",
|
|
15794
15893
|
"state.export"
|
|
15795
15894
|
]);
|
|
15895
|
+
var WORKER_ADDRESSED_ACTIONS = new Set(["worker.cancel", "worker.report"]);
|
|
15896
|
+
var workerCommand = (taskId, request) => {
|
|
15897
|
+
const { action, workerId, reason, report } = request;
|
|
15898
|
+
const rest = JSON.stringify({ reason, report });
|
|
15899
|
+
const payload = rest === "{}" ? "" : ` --payload '${rest.replace(/'/gu, "'\\''")}'`;
|
|
15900
|
+
return `workit worker ${String(action)} ${String(workerId)} --task ${taskId}${payload}`;
|
|
15901
|
+
};
|
|
15796
15902
|
var CREATING_ACTIONS = new Set([
|
|
15797
15903
|
"task.revise",
|
|
15798
15904
|
"task.progress",
|
|
@@ -16043,6 +16149,17 @@ class WorkitCore {
|
|
|
16043
16149
|
const root = this.contextRootError();
|
|
16044
16150
|
if (!root.ok)
|
|
16045
16151
|
return root;
|
|
16152
|
+
const addressed = WORKER_ADDRESSED_ACTIONS.has(action);
|
|
16153
|
+
const workerId = addressed ? value.workerId : this.context.workerId ?? null;
|
|
16154
|
+
if (workerId !== null) {
|
|
16155
|
+
const owner = this.store.taskForWorker(workerId);
|
|
16156
|
+
if (!owner.ok)
|
|
16157
|
+
return owner;
|
|
16158
|
+
if (owner.data)
|
|
16159
|
+
return success(null, null, { ...request, taskId: owner.data });
|
|
16160
|
+
if (addressed)
|
|
16161
|
+
return failure2("not_found", `worker ${workerId} is on no open task; find its task with \`workit task status --all\`, then run \`${workerCommand("<task-id>", request)}\``);
|
|
16162
|
+
}
|
|
16046
16163
|
const create = CREATING_ACTIONS.has(action) && (this.context.workerId ?? null) === null;
|
|
16047
16164
|
const found = this.store.implicitTask({
|
|
16048
16165
|
provenance: provenance(this.context),
|
|
@@ -16749,7 +16866,9 @@ class WorkitCore {
|
|
|
16749
16866
|
if (!resolved.ok)
|
|
16750
16867
|
return resolved;
|
|
16751
16868
|
const value = resolved.data;
|
|
16752
|
-
|
|
16869
|
+
const result = this.retryOmittedRevisions(value, () => this.workerOnce(value));
|
|
16870
|
+
const { action, taskId } = value ?? {};
|
|
16871
|
+
return result.ok || request?.taskId !== undefined || typeof taskId !== "string" || !WORKER_ADDRESSED_ACTIONS.has(`worker.${String(action)}`) ? result : { ...result, error: `${result.error}; run \`${workerCommand(taskId, value)}\`` };
|
|
16753
16872
|
}
|
|
16754
16873
|
workerOnce(request) {
|
|
16755
16874
|
const root = this.contextRootError();
|
|
@@ -16954,10 +17073,18 @@ class WorkitCore {
|
|
|
16954
17073
|
requirements
|
|
16955
17074
|
});
|
|
16956
17075
|
}
|
|
16957
|
-
compactContext(taskId, freshness) {
|
|
17076
|
+
compactContext(taskId, freshness, origin = null) {
|
|
16958
17077
|
const root = this.contextRootError();
|
|
16959
17078
|
if (!root.ok)
|
|
16960
17079
|
return root;
|
|
17080
|
+
const base = origin && this.store.readStoreTask(origin.taskId);
|
|
17081
|
+
const inherited = base?.ok && base.data ? {
|
|
17082
|
+
origin: origin.origin,
|
|
17083
|
+
taskId: base.data.id,
|
|
17084
|
+
decisions: base.data.decisions.filter((entry) => entry.data.revoked === null)
|
|
17085
|
+
} : null;
|
|
17086
|
+
if (taskId === null)
|
|
17087
|
+
return success(null, null, inherited?.decisions.length ? compactTaskContext(null, inherited) : "");
|
|
16961
17088
|
const task = this.store.readTask(taskId);
|
|
16962
17089
|
if (!task.ok)
|
|
16963
17090
|
return task;
|
|
@@ -16967,7 +17094,7 @@ class WorkitCore {
|
|
|
16967
17094
|
const view = this.view(task.data, false, freshness ?? signalFreshness(this.store.root));
|
|
16968
17095
|
if (!view.ok)
|
|
16969
17096
|
return view;
|
|
16970
|
-
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data));
|
|
17097
|
+
return success(view.revision, view.workspaceRevision, compactTaskContext(view.data, inherited));
|
|
16971
17098
|
}
|
|
16972
17099
|
view(task, capture = true, freshness = treeFreshness(this.store.root)) {
|
|
16973
17100
|
const workspace = this.store.readWorkspace();
|
|
@@ -17288,8 +17415,9 @@ var currentTaskEntry = (entries, session, selection) => {
|
|
|
17288
17415
|
const active = entries.filter((entry) => entry.status === "active");
|
|
17289
17416
|
return active.length === 1 ? active[0] : null;
|
|
17290
17417
|
};
|
|
17418
|
+
var otherConversation = (entry, session) => entry.sessions.some((item) => item.host !== "workit_cli" && !(item.host === session.host && item.handle === session.handle));
|
|
17291
17419
|
var implicitTaskEntry = (store, entries, session) => {
|
|
17292
|
-
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !entry
|
|
17420
|
+
const open = entries.filter((entry) => entry.key !== null && entry.status !== "closed" && !otherConversation(entry, session));
|
|
17293
17421
|
if (open.length === 0)
|
|
17294
17422
|
return null;
|
|
17295
17423
|
const key = store.currentKey();
|
|
@@ -17330,18 +17458,23 @@ function sessionCompactContext(store, session, context, selection = "session-bou
|
|
|
17330
17458
|
if (!listed.ok)
|
|
17331
17459
|
return migrationPending(listed) ? MIGRATION_PENDING_NOTE : null;
|
|
17332
17460
|
const entry = currentTaskEntry(listed.data, session, selection) ?? implicitTaskEntry(store, listed.data, session);
|
|
17333
|
-
return
|
|
17461
|
+
return entryCompactContext(store, entry, session, context);
|
|
17334
17462
|
}
|
|
17335
17463
|
function entryCompactContext(store, entry, session, context) {
|
|
17464
|
+
const found = store.originTaskEntry();
|
|
17465
|
+
const origin = found.ok && found.data && found.data.entry.id !== entry?.id && !otherConversation(found.data.entry, session) ? found.data : null;
|
|
17466
|
+
if (!entry && !origin)
|
|
17467
|
+
return null;
|
|
17336
17468
|
const workspace = store.readWorkspace();
|
|
17337
|
-
if (!workspace.ok || !workspace.data)
|
|
17469
|
+
if (!workspace.ok || entry && !workspace.data)
|
|
17338
17470
|
return null;
|
|
17339
17471
|
const slot = `${store.root}\x00${session.host}\x00${session.handle}`;
|
|
17340
17472
|
const key = canonicalJson({
|
|
17341
|
-
task: entry
|
|
17342
|
-
revision: entry
|
|
17343
|
-
file: entry
|
|
17344
|
-
|
|
17473
|
+
task: entry?.id ?? null,
|
|
17474
|
+
revision: entry?.revision ?? null,
|
|
17475
|
+
file: entry?.file ?? null,
|
|
17476
|
+
origin: origin && origin.entry.file,
|
|
17477
|
+
workspace: workspace.data?.revision ?? null,
|
|
17345
17478
|
caller: context.caller,
|
|
17346
17479
|
workerId: context.workerId ?? null,
|
|
17347
17480
|
capabilities: context.capabilities
|
|
@@ -17349,15 +17482,15 @@ function entryCompactContext(store, entry, session, context) {
|
|
|
17349
17482
|
const hit = cache.get(slot);
|
|
17350
17483
|
if (hit && hit.key === key && hit.filesKey !== null && hit.filesKey === filesKey(hit.files) && (hit.signal === null || hit.signal === recentSignal(store.root)))
|
|
17351
17484
|
return hit.value;
|
|
17352
|
-
const task = store.readTask(entry.id);
|
|
17353
|
-
if (!task.ok)
|
|
17485
|
+
const task = entry ? store.readTask(entry.id) : null;
|
|
17486
|
+
if (task && !task.ok)
|
|
17354
17487
|
return null;
|
|
17355
17488
|
const freshness = { mode: "signal", current: () => recentSignal(store.root) };
|
|
17356
|
-
const observed = task
|
|
17357
|
-
const files = task
|
|
17489
|
+
const observed = task?.data.evidence.some(isObservedCheck) ?? false;
|
|
17490
|
+
const files = (task?.data.decisions ?? []).flatMap((decision) => decision.data.binding.contentRefs.flatMap((ref) => ref.kind === "file" ? [path16.resolve(store.root, ref.path)] : []));
|
|
17358
17491
|
const observedFiles = filesKey(files);
|
|
17359
|
-
const compact = new WorkitCore(store, context).compactContext(entry
|
|
17360
|
-
if (!compact.ok)
|
|
17492
|
+
const compact = new WorkitCore(store, context).compactContext(entry?.id ?? null, freshness, origin && { origin: origin.origin, taskId: origin.entry.id });
|
|
17493
|
+
if (!compact.ok || !compact.data)
|
|
17361
17494
|
return null;
|
|
17362
17495
|
cache.delete(slot);
|
|
17363
17496
|
cache.set(slot, {
|
|
@@ -17396,7 +17529,7 @@ ${options.addendum}` : ""}
|
|
|
17396
17529
|
if (!listed.ok)
|
|
17397
17530
|
throw new Error(listed.error);
|
|
17398
17531
|
const entry = currentTaskEntry(listed.data, session, descriptor.context.task) ?? implicitTaskEntry(store, listed.data, session);
|
|
17399
|
-
const text =
|
|
17532
|
+
const text = entryCompactContext(store, entry, session, hookOperationContext(input, descriptor));
|
|
17400
17533
|
if (text)
|
|
17401
17534
|
compact = `
|
|
17402
17535
|
<workit-task-context>${text}</workit-task-context>`;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "8.9.
|
|
3
|
+
"version": "8.9.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Cursor plugin — shared MCP transport, native hooks, and sixteen method skills",
|
|
6
6
|
"keywords": [
|
|
@@ -46,8 +46,8 @@
|
|
|
46
46
|
"build": "bun scripts/build.ts"
|
|
47
47
|
},
|
|
48
48
|
"dependencies": {
|
|
49
|
-
"@brainervirus/workit-core": "^8.9.
|
|
50
|
-
"@brainervirus/workit-mcp": "^8.9.
|
|
49
|
+
"@brainervirus/workit-core": "^8.9.1",
|
|
50
|
+
"@brainervirus/workit-mcp": "^8.9.1"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=24"
|