@brainervirus/workit-cursor 8.32.0 → 8.34.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/.cursor-plugin/plugin.json +1 -1
- package/dist/cursor-session-start.js +62 -3
- package/dist/mcp-server.js +43 -8
- package/dist/workit-hook.js +61 -3
- package/package.json +3 -3
|
@@ -17849,7 +17849,8 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
17849
17849
|
loaded: list2(value.loaded),
|
|
17850
17850
|
raw: list2(value.raw),
|
|
17851
17851
|
stops: list2(value.stops),
|
|
17852
|
-
judges: list2(value.judges)
|
|
17852
|
+
judges: list2(value.judges),
|
|
17853
|
+
model: typeof value.model === "string" ? value.model : null
|
|
17853
17854
|
};
|
|
17854
17855
|
} catch {
|
|
17855
17856
|
return { ...EMPTY };
|
|
@@ -17888,10 +17889,28 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
17888
17889
|
const rest = marker.judges.filter((id) => id !== agentId);
|
|
17889
17890
|
return { ...marker, judges: running ? [...rest, agentId] : rest };
|
|
17890
17891
|
});
|
|
17892
|
+
}, modelSwitchedOnDisk = (input) => {
|
|
17893
|
+
const session = input.session.id;
|
|
17894
|
+
const model = input.model;
|
|
17895
|
+
const file = session && model ? sessionMarkerFile(input, session) : null;
|
|
17896
|
+
if (!file || !model)
|
|
17897
|
+
return false;
|
|
17898
|
+
const previous = readSessionMarker(file).model;
|
|
17899
|
+
if (previous === model)
|
|
17900
|
+
return false;
|
|
17901
|
+
updateSessionMarker(file, (marker) => ({ ...marker, model }));
|
|
17902
|
+
return previous !== null;
|
|
17891
17903
|
};
|
|
17892
17904
|
var init_session_marker = __esm(() => {
|
|
17893
17905
|
init_paths();
|
|
17894
|
-
EMPTY = {
|
|
17906
|
+
EMPTY = {
|
|
17907
|
+
nudged: [],
|
|
17908
|
+
loaded: [],
|
|
17909
|
+
raw: [],
|
|
17910
|
+
stops: [],
|
|
17911
|
+
judges: [],
|
|
17912
|
+
model: null
|
|
17913
|
+
};
|
|
17895
17914
|
});
|
|
17896
17915
|
|
|
17897
17916
|
// packages/workit-core/src/hooks/shell-words.ts
|
|
@@ -22643,7 +22662,9 @@ function decide(input, deps) {
|
|
|
22643
22662
|
if (!usable2(descriptor.context.perTurn))
|
|
22644
22663
|
return NONE6;
|
|
22645
22664
|
const current = turnContextText(input, descriptor);
|
|
22646
|
-
const
|
|
22665
|
+
const turn = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
|
|
22666
|
+
const text = input.model && modelSwitchedOnDisk(input) ? [invariantBootstrap(), turn].filter(Boolean).join(`
|
|
22667
|
+
`) : turn;
|
|
22647
22668
|
return withContextLine(text ? { kind: "context", text } : NONE6, promptNudge(input, event.prompt));
|
|
22648
22669
|
}
|
|
22649
22670
|
case "shell.pre": {
|
|
@@ -22819,6 +22840,7 @@ var init_handle = __esm(() => {
|
|
|
22819
22840
|
init_spawn_gate();
|
|
22820
22841
|
init_standing();
|
|
22821
22842
|
init_session_marker();
|
|
22843
|
+
init_methods();
|
|
22822
22844
|
init_stop();
|
|
22823
22845
|
init_live_session();
|
|
22824
22846
|
init_write_gate();
|
|
@@ -23493,6 +23515,7 @@ var init_codex = __esm(() => {
|
|
|
23493
23515
|
},
|
|
23494
23516
|
permissionMode: input.permission_mode,
|
|
23495
23517
|
transcriptPath: input.transcript_path,
|
|
23518
|
+
model: input.model,
|
|
23496
23519
|
event: protocolEvent(input)
|
|
23497
23520
|
}
|
|
23498
23521
|
};
|
|
@@ -23801,6 +23824,39 @@ var init_cursor = __esm(() => {
|
|
|
23801
23824
|
};
|
|
23802
23825
|
});
|
|
23803
23826
|
|
|
23827
|
+
// packages/workit-core/src/hooks/hook-runs.ts
|
|
23828
|
+
import { mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
|
|
23829
|
+
import path34 from "node:path";
|
|
23830
|
+
var HOOK_RUNS_DIR = "hook-runs", safe = (part) => part.replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 64), runFile = (dir, host, event) => path34.join(dir, HOOK_RUNS_DIR, `${safe(host)}__${safe(event)}.json`), recordHookRun = (host, event, env = process.env, now = Date.now) => {
|
|
23831
|
+
if (!event)
|
|
23832
|
+
return;
|
|
23833
|
+
try {
|
|
23834
|
+
const dir = stateDir2(env);
|
|
23835
|
+
const file = runFile(dir, host, event);
|
|
23836
|
+
let count = 0;
|
|
23837
|
+
try {
|
|
23838
|
+
const previous = JSON.parse(readFileSync6(file, "utf8"));
|
|
23839
|
+
if (typeof previous.count === "number" && Number.isFinite(previous.count))
|
|
23840
|
+
count = previous.count;
|
|
23841
|
+
} catch {}
|
|
23842
|
+
const row = {
|
|
23843
|
+
host,
|
|
23844
|
+
event,
|
|
23845
|
+
at: new Date(now()).toISOString(),
|
|
23846
|
+
count: count + 1
|
|
23847
|
+
};
|
|
23848
|
+
try {
|
|
23849
|
+
writeFileSync3(file, JSON.stringify(row));
|
|
23850
|
+
} catch {
|
|
23851
|
+
mkdirSync4(path34.dirname(file), { recursive: true });
|
|
23852
|
+
writeFileSync3(file, JSON.stringify(row));
|
|
23853
|
+
}
|
|
23854
|
+
} catch {}
|
|
23855
|
+
};
|
|
23856
|
+
var init_hook_runs = __esm(() => {
|
|
23857
|
+
init_heavy_words();
|
|
23858
|
+
});
|
|
23859
|
+
|
|
23804
23860
|
// packages/workit-core/src/hooks/run.ts
|
|
23805
23861
|
async function runHookProcess(host, stdin, stdout, stderr = process.stderr, env = process.env, deadline = {}) {
|
|
23806
23862
|
const adapter = typeof host === "string" ? HOOK_ADAPTERS[host] : host;
|
|
@@ -23812,6 +23868,8 @@ async function runHookProcess(host, stdin, stdout, stderr = process.stderr, env
|
|
|
23812
23868
|
} catch {
|
|
23813
23869
|
raw = undefined;
|
|
23814
23870
|
}
|
|
23871
|
+
const native = raw?.hook_event_name;
|
|
23872
|
+
recordHookRun(typeof host === "string" ? host : adapter.descriptor.host, typeof native === "string" ? native : null, env);
|
|
23815
23873
|
const result = text === null ? skippedHook(adapter, undefined, env, budget, "input") : await dispatchWithin(adapter, raw, env, budget, deadline.dispatch);
|
|
23816
23874
|
if (result.skipped)
|
|
23817
23875
|
stderr.write(`[workit] ${result.error}
|
|
@@ -23829,6 +23887,7 @@ var init_run = __esm(async () => {
|
|
|
23829
23887
|
init_claude_code();
|
|
23830
23888
|
init_codex();
|
|
23831
23889
|
init_cursor();
|
|
23890
|
+
init_hook_runs();
|
|
23832
23891
|
HOOK_ADAPTERS = {
|
|
23833
23892
|
claude_code: claudeCodeAdapter,
|
|
23834
23893
|
codex_cli: codexAdapter,
|
package/dist/mcp-server.js
CHANGED
|
@@ -16256,10 +16256,11 @@ function invalidOperation(family, input, error) {
|
|
|
16256
16256
|
const request = isObject2(input) ? input : {};
|
|
16257
16257
|
const action = typeof request.action === "string" ? request.action : "";
|
|
16258
16258
|
const actions = OPERATION_ACTIONS[family];
|
|
16259
|
-
if (!actions.includes(action))
|
|
16260
|
-
|
|
16261
|
-
|
|
16262
|
-
});
|
|
16259
|
+
if (!actions.includes(action)) {
|
|
16260
|
+
const common = actions.find((name) => READ_ONLY_ACTIONS.has(name)) ?? actions[0];
|
|
16261
|
+
const example = JSON.stringify(exampleOperation(family, common));
|
|
16262
|
+
return failure2("invalid_input", `${family}: action must be one of ${oneOf(actions)}. Example: ${example}`, { fields: [{ path: "action", reason: `got ${JSON.stringify(request.action ?? null)}` }] });
|
|
16263
|
+
}
|
|
16263
16264
|
const own = actionKeys(family, action);
|
|
16264
16265
|
const fields = [];
|
|
16265
16266
|
const sentence = [];
|
|
@@ -27734,6 +27735,38 @@ function run2(root, executable, args, env = {}) {
|
|
|
27734
27735
|
};
|
|
27735
27736
|
}
|
|
27736
27737
|
|
|
27738
|
+
// packages/workit-core/src/hooks/repeat-failure.ts
|
|
27739
|
+
import { createHash as createHash11 } from "node:crypto";
|
|
27740
|
+
var SAME_CALL_FAILED = "Same call failed twice:";
|
|
27741
|
+
var MAX_SESSIONS = 256;
|
|
27742
|
+
var stable = (value) => JSON.stringify(value, (_key, entry) => entry && typeof entry === "object" && !Array.isArray(entry) ? Object.fromEntries(Object.entries(entry).toSorted(([a], [b]) => a.localeCompare(b))) : entry) ?? "null";
|
|
27743
|
+
var hashOf = (call) => createHash11("sha256").update(call).digest("hex").slice(0, 32);
|
|
27744
|
+
var record3 = (memo, sessionId, call, failed, next) => {
|
|
27745
|
+
const previous = memo.get(sessionId);
|
|
27746
|
+
memo.delete(sessionId);
|
|
27747
|
+
if (!failed)
|
|
27748
|
+
return null;
|
|
27749
|
+
const hash = hashOf(call);
|
|
27750
|
+
memo.set(sessionId, hash);
|
|
27751
|
+
if (memo.size > MAX_SESSIONS)
|
|
27752
|
+
memo.delete(memo.keys().next().value);
|
|
27753
|
+
return previous === hash && next ? `${SAME_CALL_FAILED} ${next}` : null;
|
|
27754
|
+
};
|
|
27755
|
+
var isFailure = (result) => result !== null && typeof result === "object" && result.ok === false && typeof result.error === "string";
|
|
27756
|
+
var correctedPayload = (tool, result) => {
|
|
27757
|
+
const example = /Example: (\{.*\})\s*$/.exec(result.error)?.[1];
|
|
27758
|
+
if (example)
|
|
27759
|
+
return `${tool} ${example}`;
|
|
27760
|
+
const guidance = result.details?.guidance;
|
|
27761
|
+
return typeof guidance === "string" && guidance ? guidance : null;
|
|
27762
|
+
};
|
|
27763
|
+
var escalateToolFailure = (memo, sessionId, tool, input, result) => {
|
|
27764
|
+
const failed = isFailure(result);
|
|
27765
|
+
const line = record3(memo, sessionId, `tool\x00${tool}\x00${stable(input)}`, failed, failed ? correctedPayload(tool, result) : null);
|
|
27766
|
+
return line && failed ? { ...result, error: `${result.error}
|
|
27767
|
+
${line}` } : result;
|
|
27768
|
+
};
|
|
27769
|
+
|
|
27737
27770
|
// packages/workit-mcp/src/server.ts
|
|
27738
27771
|
class McpCapabilityUnavailableError extends Error {
|
|
27739
27772
|
capability;
|
|
@@ -27829,8 +27862,8 @@ var sanitizeFailure = (result, workspaceRoot) => {
|
|
|
27829
27862
|
};
|
|
27830
27863
|
return sanitize(result);
|
|
27831
27864
|
};
|
|
27832
|
-
var resultForClient = (result, workspaceRoot) => {
|
|
27833
|
-
const safe = sanitizeFailure(result, workspaceRoot);
|
|
27865
|
+
var resultForClient = (result, workspaceRoot, escalate = (safe) => safe) => {
|
|
27866
|
+
const safe = escalate(sanitizeFailure(result, workspaceRoot));
|
|
27834
27867
|
return {
|
|
27835
27868
|
content: [{ type: "text", text: JSON.stringify(safe) }],
|
|
27836
27869
|
structuredContent: safe,
|
|
@@ -27867,6 +27900,7 @@ function createMcpServer(host, contextProvider) {
|
|
|
27867
27900
|
path23.join(here, "..", "..", "workit-core", "src", "core", "task-engine.ts")
|
|
27868
27901
|
].filter((file) => existsSync9(file)));
|
|
27869
27902
|
let staleWarned = false;
|
|
27903
|
+
const failures = new Map;
|
|
27870
27904
|
const server = new Server({ name: "workit", version: VERSION }, { capabilities: { tools: {}, resources: {} } });
|
|
27871
27905
|
server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
27872
27906
|
let attested = false;
|
|
@@ -27989,9 +28023,10 @@ function createMcpServer(host, contextProvider) {
|
|
|
27989
28023
|
details: { operation: family }
|
|
27990
28024
|
}, workspaceRoot);
|
|
27991
28025
|
}
|
|
28026
|
+
const escalate = (safe) => escalateToolFailure(failures, context.caller.actor || "mcp", toolName, request.params.arguments, safe);
|
|
27992
28027
|
const parsed = parseAdvertisedOperation(family, request.params.arguments, host);
|
|
27993
28028
|
if (!parsed.ok)
|
|
27994
|
-
return resultForClient(parsed, workspaceRoot);
|
|
28029
|
+
return resultForClient(parsed, workspaceRoot, escalate);
|
|
27995
28030
|
if (context.callerAttested !== true && requiresCallerIdentity(parsed.data))
|
|
27996
28031
|
return resultForClient({
|
|
27997
28032
|
ok: false,
|
|
@@ -28003,7 +28038,7 @@ function createMcpServer(host, contextProvider) {
|
|
|
28003
28038
|
const core = new WorkitCore(new TaskStore(context.root), context);
|
|
28004
28039
|
const run = core[family];
|
|
28005
28040
|
const result = run.call(core, parsed.data);
|
|
28006
|
-
return resultForClient(family === "task" && context.callerAttested !== true ? briefInspectResult(parsed.data, result) : result, workspaceRoot);
|
|
28041
|
+
return resultForClient(family === "task" && context.callerAttested !== true ? briefInspectResult(parsed.data, result) : result, workspaceRoot, escalate);
|
|
28007
28042
|
} catch (error) {
|
|
28008
28043
|
return thrownResult(toolName, error, workspaceRoot);
|
|
28009
28044
|
}
|
package/dist/workit-hook.js
CHANGED
|
@@ -17905,7 +17905,14 @@ var sessionMarkerFile = (input, session, store) => {
|
|
|
17905
17905
|
return path21.join(dir, "hooks", `skills-${name}.json`);
|
|
17906
17906
|
};
|
|
17907
17907
|
var list2 = (value) => Array.isArray(value) ? value.filter((item) => typeof item === "string") : [];
|
|
17908
|
-
var EMPTY = {
|
|
17908
|
+
var EMPTY = {
|
|
17909
|
+
nudged: [],
|
|
17910
|
+
loaded: [],
|
|
17911
|
+
raw: [],
|
|
17912
|
+
stops: [],
|
|
17913
|
+
judges: [],
|
|
17914
|
+
model: null
|
|
17915
|
+
};
|
|
17909
17916
|
var readRaw = (file) => {
|
|
17910
17917
|
try {
|
|
17911
17918
|
return fs16.readFileSync(file, "utf8");
|
|
@@ -17921,7 +17928,8 @@ var parseMarker = (text) => {
|
|
|
17921
17928
|
loaded: list2(value.loaded),
|
|
17922
17929
|
raw: list2(value.raw),
|
|
17923
17930
|
stops: list2(value.stops),
|
|
17924
|
-
judges: list2(value.judges)
|
|
17931
|
+
judges: list2(value.judges),
|
|
17932
|
+
model: typeof value.model === "string" ? value.model : null
|
|
17925
17933
|
};
|
|
17926
17934
|
} catch {
|
|
17927
17935
|
return { ...EMPTY };
|
|
@@ -17966,6 +17974,18 @@ var trackJudge = (input, agentId, running) => {
|
|
|
17966
17974
|
return { ...marker, judges: running ? [...rest, agentId] : rest };
|
|
17967
17975
|
});
|
|
17968
17976
|
};
|
|
17977
|
+
var modelSwitchedOnDisk = (input) => {
|
|
17978
|
+
const session = input.session.id;
|
|
17979
|
+
const model = input.model;
|
|
17980
|
+
const file = session && model ? sessionMarkerFile(input, session) : null;
|
|
17981
|
+
if (!file || !model)
|
|
17982
|
+
return false;
|
|
17983
|
+
const previous = readSessionMarker(file).model;
|
|
17984
|
+
if (previous === model)
|
|
17985
|
+
return false;
|
|
17986
|
+
updateSessionMarker(file, (marker) => ({ ...marker, model }));
|
|
17987
|
+
return previous !== null;
|
|
17988
|
+
};
|
|
17969
17989
|
|
|
17970
17990
|
// packages/workit-core/src/hooks/shell-words.ts
|
|
17971
17991
|
class ShellAnalysisError extends Error {
|
|
@@ -22908,7 +22928,9 @@ function decide(input, deps) {
|
|
|
22908
22928
|
if (!usable2(descriptor.context.perTurn))
|
|
22909
22929
|
return NONE6;
|
|
22910
22930
|
const current = turnContextText(input, descriptor);
|
|
22911
|
-
const
|
|
22931
|
+
const turn = resendsOnChange(descriptor) ? changedTurnContext(input, current) : current;
|
|
22932
|
+
const text = input.model && modelSwitchedOnDisk(input) ? [invariantBootstrap(), turn].filter(Boolean).join(`
|
|
22933
|
+
`) : turn;
|
|
22912
22934
|
return withContextLine(text ? { kind: "context", text } : NONE6, promptNudge(input, event.prompt));
|
|
22913
22935
|
}
|
|
22914
22936
|
case "shell.pre": {
|
|
@@ -23717,6 +23739,7 @@ var codexAdapter = {
|
|
|
23717
23739
|
},
|
|
23718
23740
|
permissionMode: input.permission_mode,
|
|
23719
23741
|
transcriptPath: input.transcript_path,
|
|
23742
|
+
model: input.model,
|
|
23720
23743
|
event: protocolEvent(input)
|
|
23721
23744
|
}
|
|
23722
23745
|
};
|
|
@@ -24028,6 +24051,39 @@ var cursorAdapter = {
|
|
|
24028
24051
|
addendum: (input) => input.session.id ? `<workit-session>Cursor puts no session id in the shell: start workit commands that record authorship with the global flag \`--session ${input.session.id}\` (e.g. \`workit --session ${input.session.id} git commit -m …\`; a leading env assignment can miss the \`Shell(workit)\` allowlist). A verifier uses its own id, never this one.</workit-session>` : null
|
|
24029
24052
|
};
|
|
24030
24053
|
|
|
24054
|
+
// packages/workit-core/src/hooks/hook-runs.ts
|
|
24055
|
+
import { mkdirSync as mkdirSync4, readdirSync as readdirSync4, readFileSync as readFileSync6, writeFileSync as writeFileSync3 } from "node:fs";
|
|
24056
|
+
import path34 from "node:path";
|
|
24057
|
+
var HOOK_RUNS_DIR = "hook-runs";
|
|
24058
|
+
var safe = (part) => part.replace(/[^A-Za-z0-9_.-]/g, "-").slice(0, 64);
|
|
24059
|
+
var runFile = (dir, host, event) => path34.join(dir, HOOK_RUNS_DIR, `${safe(host)}__${safe(event)}.json`);
|
|
24060
|
+
var recordHookRun = (host, event, env = process.env, now = Date.now) => {
|
|
24061
|
+
if (!event)
|
|
24062
|
+
return;
|
|
24063
|
+
try {
|
|
24064
|
+
const dir = stateDir2(env);
|
|
24065
|
+
const file = runFile(dir, host, event);
|
|
24066
|
+
let count = 0;
|
|
24067
|
+
try {
|
|
24068
|
+
const previous = JSON.parse(readFileSync6(file, "utf8"));
|
|
24069
|
+
if (typeof previous.count === "number" && Number.isFinite(previous.count))
|
|
24070
|
+
count = previous.count;
|
|
24071
|
+
} catch {}
|
|
24072
|
+
const row = {
|
|
24073
|
+
host,
|
|
24074
|
+
event,
|
|
24075
|
+
at: new Date(now()).toISOString(),
|
|
24076
|
+
count: count + 1
|
|
24077
|
+
};
|
|
24078
|
+
try {
|
|
24079
|
+
writeFileSync3(file, JSON.stringify(row));
|
|
24080
|
+
} catch {
|
|
24081
|
+
mkdirSync4(path34.dirname(file), { recursive: true });
|
|
24082
|
+
writeFileSync3(file, JSON.stringify(row));
|
|
24083
|
+
}
|
|
24084
|
+
} catch {}
|
|
24085
|
+
};
|
|
24086
|
+
|
|
24031
24087
|
// packages/workit-core/src/hooks/run.ts
|
|
24032
24088
|
var HOOK_ADAPTERS = {
|
|
24033
24089
|
claude_code: claudeCodeAdapter,
|
|
@@ -24045,6 +24101,8 @@ async function runHookProcess(host, stdin, stdout, stderr = process.stderr, env
|
|
|
24045
24101
|
} catch {
|
|
24046
24102
|
raw = undefined;
|
|
24047
24103
|
}
|
|
24104
|
+
const native = raw?.hook_event_name;
|
|
24105
|
+
recordHookRun(typeof host === "string" ? host : adapter.descriptor.host, typeof native === "string" ? native : null, env);
|
|
24048
24106
|
const result = text === null ? skippedHook(adapter, undefined, env, budget, "input") : await dispatchWithin(adapter, raw, env, budget, deadline.dispatch);
|
|
24049
24107
|
if (result.skipped)
|
|
24050
24108
|
stderr.write(`[workit] ${result.error}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.34.0",
|
|
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
|
"devDependencies": {
|
|
49
|
-
"@brainervirus/workit-core": "^8.
|
|
50
|
-
"@brainervirus/workit-mcp": "^8.
|
|
49
|
+
"@brainervirus/workit-core": "^8.34.0",
|
|
50
|
+
"@brainervirus/workit-mcp": "^8.34.0"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=24"
|