@brainervirus/workit-cursor 8.27.0 → 8.28.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 +173 -4
- package/dist/mcp-server.js +121 -7
- package/dist/workit-hook.js +175 -2
- package/hooks/launch.mjs +30 -0
- package/package.json +3 -3
|
@@ -8122,7 +8122,33 @@ var nonBlank, branchPreset, commitPreset, workspaceBranchPolicySchema, workspace
|
|
|
8122
8122
|
workspace: found?.workspace ?? null,
|
|
8123
8123
|
from: found?.source?.name ?? null
|
|
8124
8124
|
};
|
|
8125
|
-
}, workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false,
|
|
8125
|
+
}, workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false, modelSources = (cwd, dir) => {
|
|
8126
|
+
const out = [];
|
|
8127
|
+
try {
|
|
8128
|
+
const matched = matchRuntimeWorkspace(cwd, dir);
|
|
8129
|
+
if (matched) {
|
|
8130
|
+
const enclosing = matched.matches.filter((entry) => entry !== matched.selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
8131
|
+
for (const entry of [matched.selected, ...enclosing])
|
|
8132
|
+
out.push({
|
|
8133
|
+
name: entry.name,
|
|
8134
|
+
global: false,
|
|
8135
|
+
models: entry.models,
|
|
8136
|
+
tierModels: entry.fanout?.tierModels
|
|
8137
|
+
});
|
|
8138
|
+
}
|
|
8139
|
+
} catch {}
|
|
8140
|
+
try {
|
|
8141
|
+
const document = JSON.parse(readFileSync2(path4.join(dir ?? configDir(), "workspaces.json"), "utf8"));
|
|
8142
|
+
if (document && typeof document === "object" && !Array.isArray(document))
|
|
8143
|
+
out.push({
|
|
8144
|
+
name: "global",
|
|
8145
|
+
global: true,
|
|
8146
|
+
models: document.models,
|
|
8147
|
+
tierModels: undefined
|
|
8148
|
+
});
|
|
8149
|
+
} catch {}
|
|
8150
|
+
return out;
|
|
8151
|
+
}, hostFamily = (host) => host.replace(/_(?:cli|desktop)$/u, ""), TIER_KEYS, TIER_HOSTS, resolveRuntimeWorkspaceCandidate = (cwd, kind) => {
|
|
8126
8152
|
const matched = matchRuntimeWorkspace(cwd);
|
|
8127
8153
|
if (!matched)
|
|
8128
8154
|
return null;
|
|
@@ -9713,11 +9739,15 @@ var withReleaseTracks = (workspaceRoot, policy) => {
|
|
|
9713
9739
|
source,
|
|
9714
9740
|
fingerprint
|
|
9715
9741
|
};
|
|
9742
|
+
}, rethrowRuntimeBug = (error) => {
|
|
9743
|
+
if (error instanceof TypeError || error instanceof ReferenceError)
|
|
9744
|
+
throw error;
|
|
9716
9745
|
}, validateBranchNameFor = (workspaceRoot, name) => {
|
|
9717
9746
|
try {
|
|
9718
9747
|
const resolved = branchPolicySnapshotFor(workspaceRoot);
|
|
9719
9748
|
return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
|
|
9720
9749
|
} catch (error) {
|
|
9750
|
+
rethrowRuntimeBug(error);
|
|
9721
9751
|
const correction = "repair or select a valid workspace policy before creating a branch";
|
|
9722
9752
|
return {
|
|
9723
9753
|
ok: false,
|
|
@@ -11497,7 +11527,7 @@ var str2 = (d, values) => ({
|
|
|
11497
11527
|
description: typeof description === "string" ? description : "",
|
|
11498
11528
|
paths: ["."],
|
|
11499
11529
|
exclusions: []
|
|
11500
|
-
}), LISTS, ENVELOPE;
|
|
11530
|
+
}), LISTS, ENVELOPE, NOT_FLAT;
|
|
11501
11531
|
var init_operation_input = __esm(() => {
|
|
11502
11532
|
init_task_contract();
|
|
11503
11533
|
scopeFields = {
|
|
@@ -11619,6 +11649,7 @@ var init_operation_input = __esm(() => {
|
|
|
11619
11649
|
"expectedWorkspaceRevision",
|
|
11620
11650
|
"judgment"
|
|
11621
11651
|
]);
|
|
11652
|
+
NOT_FLAT = new Set(["schemaVersion", "action", "workspaceId"]);
|
|
11622
11653
|
});
|
|
11623
11654
|
|
|
11624
11655
|
// packages/workit-core/src/core/policy/judgment.ts
|
|
@@ -22037,6 +22068,131 @@ var init_standing = __esm(() => {
|
|
|
22037
22068
|
sinceStart = new Map;
|
|
22038
22069
|
});
|
|
22039
22070
|
|
|
22071
|
+
// packages/workit-core/src/core/role-models.ts
|
|
22072
|
+
var MODEL_ROLES, MODEL_TIERS, HOST_KEYS, BUILT_IN_CLAUDE_CODE, isObject7 = (value) => !!value && typeof value === "object" && !Array.isArray(value), name = (value) => typeof value === "string" && value.trim() ? value.trim() : null, leaf = (value) => {
|
|
22073
|
+
const text = name(value);
|
|
22074
|
+
if (text)
|
|
22075
|
+
return { model: text, effort: null };
|
|
22076
|
+
if (!isObject7(value))
|
|
22077
|
+
return null;
|
|
22078
|
+
const model = name(value.model);
|
|
22079
|
+
return model ? { model, effort: name(value.effort) } : null;
|
|
22080
|
+
}, isFlat = (value) => typeof value === "string" || isObject7(value) && Object.hasOwn(value, "model"), forHost = (value, host) => {
|
|
22081
|
+
if (isFlat(value))
|
|
22082
|
+
return leaf(value);
|
|
22083
|
+
if (!isObject7(value))
|
|
22084
|
+
return null;
|
|
22085
|
+
const family = host ? hostFamily(host) : null;
|
|
22086
|
+
for (const key of [family, host, "default"])
|
|
22087
|
+
if (key && Object.hasOwn(value, key)) {
|
|
22088
|
+
const found = leaf(value[key]);
|
|
22089
|
+
if (found)
|
|
22090
|
+
return found;
|
|
22091
|
+
}
|
|
22092
|
+
return null;
|
|
22093
|
+
}, rolesOf = (models) => isObject7(models) && isObject7(models.roles) ? models.roles : null, resolveRoleModel = (cwd, selector, host, dir) => {
|
|
22094
|
+
const { role, tier } = selector;
|
|
22095
|
+
for (const source of modelSources(cwd, dir)) {
|
|
22096
|
+
const where = source.global ? "the global default" : `workspace ${source.name}`;
|
|
22097
|
+
const roles = rolesOf(source.models);
|
|
22098
|
+
const configured = roles && Object.hasOwn(roles, role) ? roles[role] : undefined;
|
|
22099
|
+
const fromRoles = role === "implementer" ? tier && isObject7(configured) && Object.hasOwn(configured, tier) ? forHost(configured[tier], host) : null : forHost(configured, host);
|
|
22100
|
+
if (fromRoles)
|
|
22101
|
+
return { ...fromRoles, source: `models.roles in ${where}` };
|
|
22102
|
+
if (role === "implementer" && tier && isObject7(source.tierModels)) {
|
|
22103
|
+
const fromTiers = Object.hasOwn(source.tierModels, tier) ? forHost(source.tierModels[tier], host) : null;
|
|
22104
|
+
if (fromTiers)
|
|
22105
|
+
return { model: fromTiers.model, effort: null, source: `fanout.tierModels in ${where}` };
|
|
22106
|
+
}
|
|
22107
|
+
}
|
|
22108
|
+
if (host && hostFamily(host) === "claude_code") {
|
|
22109
|
+
const builtIn = BUILT_IN_CLAUDE_CODE[tier ? `${role}.${tier}` : role];
|
|
22110
|
+
if (builtIn)
|
|
22111
|
+
return { model: builtIn.model, effort: builtIn.effort ?? null, source: "built-in" };
|
|
22112
|
+
}
|
|
22113
|
+
return null;
|
|
22114
|
+
}, ROLE_SELECTORS, roleForAgentType = (agentType, prompt) => {
|
|
22115
|
+
switch (agentType) {
|
|
22116
|
+
case "workit:implementer": {
|
|
22117
|
+
const tier = typeof prompt === "string" ? /^[ \t]*TIER:[ \t]*(mundane|standard|hard)\b/im.exec(prompt)?.[1] : undefined;
|
|
22118
|
+
return { role: "implementer", tier: tier?.toLowerCase() ?? "standard" };
|
|
22119
|
+
}
|
|
22120
|
+
case "workit:verifier":
|
|
22121
|
+
return { role: "verifier" };
|
|
22122
|
+
case "workit:reviewer":
|
|
22123
|
+
return { role: "reviewer" };
|
|
22124
|
+
case "Explore":
|
|
22125
|
+
return { role: "explore" };
|
|
22126
|
+
default:
|
|
22127
|
+
return null;
|
|
22128
|
+
}
|
|
22129
|
+
};
|
|
22130
|
+
var init_role_models = __esm(() => {
|
|
22131
|
+
init_workspaces();
|
|
22132
|
+
MODEL_ROLES = [
|
|
22133
|
+
"decider",
|
|
22134
|
+
"implementer",
|
|
22135
|
+
"verifier",
|
|
22136
|
+
"reviewer",
|
|
22137
|
+
"explore",
|
|
22138
|
+
"babysit"
|
|
22139
|
+
];
|
|
22140
|
+
MODEL_TIERS = ["mundane", "standard", "hard"];
|
|
22141
|
+
HOST_KEYS = new Set([
|
|
22142
|
+
"claude_code",
|
|
22143
|
+
"opencode",
|
|
22144
|
+
"codex",
|
|
22145
|
+
"codex_cli",
|
|
22146
|
+
"codex_desktop",
|
|
22147
|
+
"cursor",
|
|
22148
|
+
"pi",
|
|
22149
|
+
"default"
|
|
22150
|
+
]);
|
|
22151
|
+
BUILT_IN_CLAUDE_CODE = {
|
|
22152
|
+
decider: { model: "fable", effort: "high" },
|
|
22153
|
+
"implementer.hard": { model: "opus" },
|
|
22154
|
+
"implementer.standard": { model: "sonnet" },
|
|
22155
|
+
"implementer.mundane": { model: "haiku" },
|
|
22156
|
+
verifier: { model: "sonnet" },
|
|
22157
|
+
reviewer: { model: "opus" },
|
|
22158
|
+
explore: { model: "haiku" },
|
|
22159
|
+
babysit: { model: "haiku" }
|
|
22160
|
+
};
|
|
22161
|
+
ROLE_SELECTORS = MODEL_ROLES.flatMap((role) => role === "implementer" ? MODEL_TIERS.map((tier) => ({ role, tier })) : [{ role }]);
|
|
22162
|
+
});
|
|
22163
|
+
|
|
22164
|
+
// packages/workit-core/src/hooks/agent-model.ts
|
|
22165
|
+
var MODELS, EFFORTS, given = (value) => typeof value === "string" && value.trim() !== "", agentModelDecision = (cwd, tool, env = process.env) => {
|
|
22166
|
+
if (given(tool.model) || env.CLAUDE_CODE_SUBAGENT_MODEL_FORCE)
|
|
22167
|
+
return { kind: "none" };
|
|
22168
|
+
const selector = roleForAgentType(tool.subagent_type, tool.prompt);
|
|
22169
|
+
if (!selector)
|
|
22170
|
+
return { kind: "none" };
|
|
22171
|
+
const resolved = resolveRoleModel(cwd, selector, "claude_code");
|
|
22172
|
+
if (!resolved)
|
|
22173
|
+
return { kind: "none" };
|
|
22174
|
+
const model = resolved.model.toLowerCase();
|
|
22175
|
+
const effort = resolved.effort?.toLowerCase() ?? null;
|
|
22176
|
+
const input = { ...tool };
|
|
22177
|
+
if (MODELS.has(model))
|
|
22178
|
+
input.model = model;
|
|
22179
|
+
if (effort && EFFORTS.has(effort) && !given(tool.effort))
|
|
22180
|
+
input.effort = effort;
|
|
22181
|
+
if (input.model === undefined && input.effort === tool.effort)
|
|
22182
|
+
return { kind: "none" };
|
|
22183
|
+
const label = selector.tier ? `${selector.role} ${selector.tier}` : selector.role;
|
|
22184
|
+
return {
|
|
22185
|
+
kind: "update-input",
|
|
22186
|
+
input,
|
|
22187
|
+
text: `Workit set ${String(input.model ?? "no model")}${input.effort !== tool.effort ? ` (effort ${String(input.effort)})` : ""} on this ${String(tool.subagent_type)} spawn: role ${label}, ${resolved.source}. Name a model to choose another.`
|
|
22188
|
+
};
|
|
22189
|
+
};
|
|
22190
|
+
var init_agent_model = __esm(() => {
|
|
22191
|
+
init_role_models();
|
|
22192
|
+
MODELS = new Set(["sonnet", "opus", "haiku", "fable"]);
|
|
22193
|
+
EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
22194
|
+
});
|
|
22195
|
+
|
|
22040
22196
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
22041
22197
|
var DIR, COMPOUND, GLOBALS_FIRST, VERB_FIRST, VERBS, FILTERS, clean = (s) => !s.unanalyzable && !s.group && !s.subs?.length && !s.stdin?.length && !s.redirects.length, stdinOnly = (words) => {
|
|
22042
22198
|
const [name, ...args] = words;
|
|
@@ -22345,7 +22501,7 @@ function handleHook(input, deps) {
|
|
|
22345
22501
|
const delta = standingDelta(input);
|
|
22346
22502
|
if (!delta)
|
|
22347
22503
|
return decision;
|
|
22348
|
-
return decision.kind === "rewrite" ? { ...decision, text: [decision.text, delta].filter(Boolean).join(`
|
|
22504
|
+
return decision.kind === "rewrite" || decision.kind === "update-input" ? { ...decision, text: [decision.text, delta].filter(Boolean).join(`
|
|
22349
22505
|
`) } : withContextLine(decision, delta);
|
|
22350
22506
|
}
|
|
22351
22507
|
function decide(input, deps) {
|
|
@@ -22458,6 +22614,8 @@ function decide(input, deps) {
|
|
|
22458
22614
|
case "tool.pre":
|
|
22459
22615
|
if (event.skill)
|
|
22460
22616
|
recordSkillLoad(input, event.skill, "tool");
|
|
22617
|
+
if (event.agentInput && input.host === "claude_code")
|
|
22618
|
+
return agentModelDecision(input.cwd, event.agentInput);
|
|
22461
22619
|
return NONE6;
|
|
22462
22620
|
case "stop":
|
|
22463
22621
|
return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE6;
|
|
@@ -22530,6 +22688,7 @@ var init_handle = __esm(() => {
|
|
|
22530
22688
|
init_branch();
|
|
22531
22689
|
init_workspaces();
|
|
22532
22690
|
init_host_session();
|
|
22691
|
+
init_agent_model();
|
|
22533
22692
|
init_cwd_normalize();
|
|
22534
22693
|
init_delivery_verbs();
|
|
22535
22694
|
init_heavy_gate();
|
|
@@ -22680,6 +22839,16 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
22680
22839
|
skill: isRecord6(value.tool_input) ? optionalText(value.tool_input.skill) : null
|
|
22681
22840
|
}
|
|
22682
22841
|
};
|
|
22842
|
+
if (value.tool_name === "Agent" || value.tool_name === "Task")
|
|
22843
|
+
return {
|
|
22844
|
+
ok: true,
|
|
22845
|
+
event: {
|
|
22846
|
+
kind: "tool.pre",
|
|
22847
|
+
tool: value.tool_name,
|
|
22848
|
+
toolUseId,
|
|
22849
|
+
...isRecord6(value.tool_input) ? { agentInput: value.tool_input } : {}
|
|
22850
|
+
}
|
|
22851
|
+
};
|
|
22683
22852
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
22684
22853
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
22685
22854
|
const command = toolCommand(value);
|
|
@@ -22748,7 +22917,7 @@ var CLAUDE_CODE_DESCRIPTOR, EVENTS, SOURCES, SHELL_TOOLS, dialectOf2 = (value) =
|
|
|
22748
22917
|
};
|
|
22749
22918
|
}
|
|
22750
22919
|
}, CONTEXT_EVENTS, render = (decision, native) => {
|
|
22751
|
-
if (decision.kind === "rewrite" && native === "PreToolUse" && decision.input)
|
|
22920
|
+
if ((decision.kind === "rewrite" || decision.kind === "update-input") && native === "PreToolUse" && decision.input)
|
|
22752
22921
|
return {
|
|
22753
22922
|
json: {
|
|
22754
22923
|
hookSpecificOutput: {
|
package/dist/mcp-server.js
CHANGED
|
@@ -15951,8 +15951,9 @@ function flatOperationJsonSchema(family, options = {}) {
|
|
|
15951
15951
|
};
|
|
15952
15952
|
const properties = { action: { type: "string", enum: actions } };
|
|
15953
15953
|
const readField = (name) => name === "taskId" || (family === "task" ? READ_TASK_FIELDS.has(name) : family === "policy");
|
|
15954
|
+
const taken = (name) => family === "policy" || actions.some((action) => actionKeys(family, action)?.accepted.has(name));
|
|
15954
15955
|
for (const [name, field] of Object.entries({ ...COMMON, ...spec.fields }))
|
|
15955
|
-
if (options.readOnly ? readField(name) :
|
|
15956
|
+
if (options.readOnly ? readField(name) : taken(name))
|
|
15956
15957
|
properties[name] = property(field);
|
|
15957
15958
|
return {
|
|
15958
15959
|
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -16194,6 +16195,98 @@ function normalizeOperationInput(family, input, host) {
|
|
|
16194
16195
|
request[key] = strings(request[key]);
|
|
16195
16196
|
return request;
|
|
16196
16197
|
}
|
|
16198
|
+
var defOf = (node) => node._zod.def;
|
|
16199
|
+
var unwrap = (node) => {
|
|
16200
|
+
let inner = node;
|
|
16201
|
+
while (["optional", "nullable"].includes(defOf(inner).type) && defOf(inner).innerType)
|
|
16202
|
+
inner = defOf(inner).innerType;
|
|
16203
|
+
return inner;
|
|
16204
|
+
};
|
|
16205
|
+
var FLAT_NAME = {
|
|
16206
|
+
authorityRefs: "refs",
|
|
16207
|
+
contentRefs: "refs",
|
|
16208
|
+
statedChoice: "choice",
|
|
16209
|
+
reviewContext: "reviewSession"
|
|
16210
|
+
};
|
|
16211
|
+
var NOT_FLAT = new Set(["schemaVersion", "action", "workspaceId"]);
|
|
16212
|
+
function actionKeys(family, action) {
|
|
16213
|
+
const options = advertisedOperationSchemas[family].options;
|
|
16214
|
+
const option = options.find((item) => {
|
|
16215
|
+
const literal = defOf(item).shape?.action;
|
|
16216
|
+
return literal._zod.def.values.includes(action);
|
|
16217
|
+
});
|
|
16218
|
+
if (!option)
|
|
16219
|
+
return null;
|
|
16220
|
+
const out = { accepted: new Set, required: [] };
|
|
16221
|
+
const walk = (shape, nested) => {
|
|
16222
|
+
for (const [key, node] of Object.entries(shape)) {
|
|
16223
|
+
if (NOT_FLAT.has(key) || nested && key === "taskId")
|
|
16224
|
+
continue;
|
|
16225
|
+
const inner = unwrap(node);
|
|
16226
|
+
const type = defOf(inner).type;
|
|
16227
|
+
const needed = !["optional", "nullable"].includes(defOf(node).type);
|
|
16228
|
+
if (key === "scope")
|
|
16229
|
+
for (const name of ["scope", "paths", "exclusions"])
|
|
16230
|
+
out.accepted.add(name);
|
|
16231
|
+
else if (type === "object" && key !== "bundle" && !(key in FLAT_NAME)) {
|
|
16232
|
+
out.accepted.add(key);
|
|
16233
|
+
walk(defOf(inner).shape, true);
|
|
16234
|
+
} else {
|
|
16235
|
+
const name = FLAT_NAME[key] ?? key;
|
|
16236
|
+
out.accepted.add(name);
|
|
16237
|
+
if (needed && type !== "array" && type !== "record" && !out.required.includes(name))
|
|
16238
|
+
out.required.push(name);
|
|
16239
|
+
}
|
|
16240
|
+
}
|
|
16241
|
+
};
|
|
16242
|
+
walk(defOf(option).shape, false);
|
|
16243
|
+
return out;
|
|
16244
|
+
}
|
|
16245
|
+
function exampleOperation(family, action) {
|
|
16246
|
+
const keys = actionKeys(family, action);
|
|
16247
|
+
const example = { action };
|
|
16248
|
+
for (const name of keys?.required ?? [])
|
|
16249
|
+
example[name] = FLAT[family].fields[name]?.enum?.[0] ?? `<${name}>`;
|
|
16250
|
+
if (family === "policy" && !keys?.required.length && action !== "explain")
|
|
16251
|
+
example.riskTier = "normal";
|
|
16252
|
+
return example;
|
|
16253
|
+
}
|
|
16254
|
+
var oneOf = (values) => values.join(" | ");
|
|
16255
|
+
function invalidOperation(family, input, error) {
|
|
16256
|
+
const request = isObject2(input) ? input : {};
|
|
16257
|
+
const action = typeof request.action === "string" ? request.action : "";
|
|
16258
|
+
const actions = OPERATION_ACTIONS[family];
|
|
16259
|
+
if (!actions.includes(action))
|
|
16260
|
+
return failure2("invalid_input", `${family}: action must be one of ${oneOf(actions)}`, {
|
|
16261
|
+
fields: [{ path: "action", reason: `got ${JSON.stringify(request.action ?? null)}` }]
|
|
16262
|
+
});
|
|
16263
|
+
const own = actionKeys(family, action);
|
|
16264
|
+
const fields = [];
|
|
16265
|
+
const sentence = [];
|
|
16266
|
+
for (const issue of error.issues) {
|
|
16267
|
+
if (issue.code === "unrecognized_keys") {
|
|
16268
|
+
for (const key of issue.keys) {
|
|
16269
|
+
const home = actions.find((other) => other !== action && actionKeys(family, other).accepted.has(key));
|
|
16270
|
+
const reason = home ? `${key} belongs to action: ${home}` : `${key} is not a field of ${family} ${action}; it takes ${[...own.accepted].join(", ")}`;
|
|
16271
|
+
fields.push({ path: key, reason });
|
|
16272
|
+
sentence.push(reason);
|
|
16273
|
+
}
|
|
16274
|
+
continue;
|
|
16275
|
+
}
|
|
16276
|
+
const last = String(issue.path.at(-1) ?? "");
|
|
16277
|
+
const name = Object.entries(FLAT_NAME).find(([contract]) => contract === last)?.[1] ?? last;
|
|
16278
|
+
const missing = issue.code === "invalid_type" && /received undefined/.test(issue.message);
|
|
16279
|
+
const absent = own.required.filter((key) => !(key in request));
|
|
16280
|
+
const needs = missing && issue.path.length === 1 && absent.length ? absent.join(", ") : name;
|
|
16281
|
+
const reason = missing ? `${family} ${action} requires ${needs}` : `${name}: ${issue.message}`;
|
|
16282
|
+
fields.push({ path: issue.path.join("."), reason });
|
|
16283
|
+
sentence.push(reason);
|
|
16284
|
+
}
|
|
16285
|
+
const example = JSON.stringify(exampleOperation(family, action));
|
|
16286
|
+
return failure2("invalid_input", `${[...new Set(sentence)].join("; ")}. Example: ${example}`, {
|
|
16287
|
+
fields
|
|
16288
|
+
});
|
|
16289
|
+
}
|
|
16197
16290
|
function parseAdvertisedOperation(family, input, host) {
|
|
16198
16291
|
const request = normalizeOperationInput(family, input, host);
|
|
16199
16292
|
if (isObject2(request) && request.schemaVersion !== SCHEMA_VERSION)
|
|
@@ -16201,12 +16294,7 @@ function parseAdvertisedOperation(family, input, host) {
|
|
|
16201
16294
|
const parsed = advertisedOperationSchemas[family].safeParse(request);
|
|
16202
16295
|
if (parsed.success)
|
|
16203
16296
|
return success(null, null, request);
|
|
16204
|
-
return
|
|
16205
|
-
fields: parsed.error.issues.map((issue) => ({
|
|
16206
|
-
path: issue.code === "unrecognized_keys" ? issue.keys.join(".") : issue.path.join("."),
|
|
16207
|
-
reason: issue.message
|
|
16208
|
-
}))
|
|
16209
|
-
});
|
|
16297
|
+
return invalidOperation(family, request, parsed.error);
|
|
16210
16298
|
}
|
|
16211
16299
|
// packages/workit-core/src/core/task-store.ts
|
|
16212
16300
|
import { createHash as createHash8, randomUUID as randomUUID3 } from "node:crypto";
|
|
@@ -28185,6 +28273,32 @@ var WRITE_TOOLS = new Set([
|
|
|
28185
28273
|
]);
|
|
28186
28274
|
// packages/workit-core/src/hooks/standing.ts
|
|
28187
28275
|
var sinceStart = new Map;
|
|
28276
|
+
// packages/workit-core/src/core/role-models.ts
|
|
28277
|
+
var MODEL_ROLES = [
|
|
28278
|
+
"decider",
|
|
28279
|
+
"implementer",
|
|
28280
|
+
"verifier",
|
|
28281
|
+
"reviewer",
|
|
28282
|
+
"explore",
|
|
28283
|
+
"babysit"
|
|
28284
|
+
];
|
|
28285
|
+
var MODEL_TIERS = ["mundane", "standard", "hard"];
|
|
28286
|
+
var HOST_KEYS = new Set([
|
|
28287
|
+
"claude_code",
|
|
28288
|
+
"opencode",
|
|
28289
|
+
"codex",
|
|
28290
|
+
"codex_cli",
|
|
28291
|
+
"codex_desktop",
|
|
28292
|
+
"cursor",
|
|
28293
|
+
"pi",
|
|
28294
|
+
"default"
|
|
28295
|
+
]);
|
|
28296
|
+
var ROLE_SELECTORS = MODEL_ROLES.flatMap((role) => role === "implementer" ? MODEL_TIERS.map((tier) => ({ role, tier })) : [{ role }]);
|
|
28297
|
+
|
|
28298
|
+
// packages/workit-core/src/hooks/agent-model.ts
|
|
28299
|
+
var MODELS = new Set(["sonnet", "opus", "haiku", "fable"]);
|
|
28300
|
+
var EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
28301
|
+
|
|
28188
28302
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
28189
28303
|
var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
|
|
28190
28304
|
var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
|
package/dist/workit-hook.js
CHANGED
|
@@ -8205,6 +8205,34 @@ var workspaceTrailers = (cwd, dir) => {
|
|
|
8205
8205
|
};
|
|
8206
8206
|
};
|
|
8207
8207
|
var workspaceLiveSessionGuard = (cwd, dir) => inheritedSetting(cwd, dir, (entry) => typeof entry.liveSessionGuard === "boolean")?.source?.liveSessionGuard !== false;
|
|
8208
|
+
var modelSources = (cwd, dir) => {
|
|
8209
|
+
const out = [];
|
|
8210
|
+
try {
|
|
8211
|
+
const matched = matchRuntimeWorkspace(cwd, dir);
|
|
8212
|
+
if (matched) {
|
|
8213
|
+
const enclosing = matched.matches.filter((entry) => entry !== matched.selected).toSorted((a, b) => globSpecificity(b.glob) - globSpecificity(a.glob));
|
|
8214
|
+
for (const entry of [matched.selected, ...enclosing])
|
|
8215
|
+
out.push({
|
|
8216
|
+
name: entry.name,
|
|
8217
|
+
global: false,
|
|
8218
|
+
models: entry.models,
|
|
8219
|
+
tierModels: entry.fanout?.tierModels
|
|
8220
|
+
});
|
|
8221
|
+
}
|
|
8222
|
+
} catch {}
|
|
8223
|
+
try {
|
|
8224
|
+
const document = JSON.parse(readFileSync2(path4.join(dir ?? configDir(), "workspaces.json"), "utf8"));
|
|
8225
|
+
if (document && typeof document === "object" && !Array.isArray(document))
|
|
8226
|
+
out.push({
|
|
8227
|
+
name: "global",
|
|
8228
|
+
global: true,
|
|
8229
|
+
models: document.models,
|
|
8230
|
+
tierModels: undefined
|
|
8231
|
+
});
|
|
8232
|
+
} catch {}
|
|
8233
|
+
return out;
|
|
8234
|
+
};
|
|
8235
|
+
var hostFamily = (host) => host.replace(/_(?:cli|desktop)$/u, "");
|
|
8208
8236
|
var TIER_KEYS = new Set(["mundane", "standard", "hard"]);
|
|
8209
8237
|
var TIER_HOSTS = new Set([
|
|
8210
8238
|
"claude_code",
|
|
@@ -9700,11 +9728,16 @@ var branchPolicySnapshotFor = (workspaceRoot) => {
|
|
|
9700
9728
|
fingerprint
|
|
9701
9729
|
};
|
|
9702
9730
|
};
|
|
9731
|
+
var rethrowRuntimeBug = (error) => {
|
|
9732
|
+
if (error instanceof TypeError || error instanceof ReferenceError)
|
|
9733
|
+
throw error;
|
|
9734
|
+
};
|
|
9703
9735
|
var validateBranchNameFor = (workspaceRoot, name) => {
|
|
9704
9736
|
try {
|
|
9705
9737
|
const resolved = branchPolicySnapshotFor(workspaceRoot);
|
|
9706
9738
|
return validateBranchNamePolicy(name, resolved.namePolicy, resolved.source);
|
|
9707
9739
|
} catch (error) {
|
|
9740
|
+
rethrowRuntimeBug(error);
|
|
9708
9741
|
const correction = "repair or select a valid workspace policy before creating a branch";
|
|
9709
9742
|
return {
|
|
9710
9743
|
ok: false,
|
|
@@ -11608,6 +11641,7 @@ function normalizeOperationInput(family, input, host) {
|
|
|
11608
11641
|
request[key] = strings(request[key]);
|
|
11609
11642
|
return request;
|
|
11610
11643
|
}
|
|
11644
|
+
var NOT_FLAT = new Set(["schemaVersion", "action", "workspaceId"]);
|
|
11611
11645
|
|
|
11612
11646
|
// packages/workit-core/src/core/policy/judgment.ts
|
|
11613
11647
|
var isObject3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
@@ -22250,6 +22284,133 @@ var standingDelta = (input) => {
|
|
|
22250
22284
|
}
|
|
22251
22285
|
};
|
|
22252
22286
|
var sinceStart = new Map;
|
|
22287
|
+
// packages/workit-core/src/core/role-models.ts
|
|
22288
|
+
var MODEL_ROLES = [
|
|
22289
|
+
"decider",
|
|
22290
|
+
"implementer",
|
|
22291
|
+
"verifier",
|
|
22292
|
+
"reviewer",
|
|
22293
|
+
"explore",
|
|
22294
|
+
"babysit"
|
|
22295
|
+
];
|
|
22296
|
+
var MODEL_TIERS = ["mundane", "standard", "hard"];
|
|
22297
|
+
var HOST_KEYS = new Set([
|
|
22298
|
+
"claude_code",
|
|
22299
|
+
"opencode",
|
|
22300
|
+
"codex",
|
|
22301
|
+
"codex_cli",
|
|
22302
|
+
"codex_desktop",
|
|
22303
|
+
"cursor",
|
|
22304
|
+
"pi",
|
|
22305
|
+
"default"
|
|
22306
|
+
]);
|
|
22307
|
+
var BUILT_IN_CLAUDE_CODE = {
|
|
22308
|
+
decider: { model: "fable", effort: "high" },
|
|
22309
|
+
"implementer.hard": { model: "opus" },
|
|
22310
|
+
"implementer.standard": { model: "sonnet" },
|
|
22311
|
+
"implementer.mundane": { model: "haiku" },
|
|
22312
|
+
verifier: { model: "sonnet" },
|
|
22313
|
+
reviewer: { model: "opus" },
|
|
22314
|
+
explore: { model: "haiku" },
|
|
22315
|
+
babysit: { model: "haiku" }
|
|
22316
|
+
};
|
|
22317
|
+
var isObject7 = (value) => !!value && typeof value === "object" && !Array.isArray(value);
|
|
22318
|
+
var name = (value) => typeof value === "string" && value.trim() ? value.trim() : null;
|
|
22319
|
+
var leaf = (value) => {
|
|
22320
|
+
const text = name(value);
|
|
22321
|
+
if (text)
|
|
22322
|
+
return { model: text, effort: null };
|
|
22323
|
+
if (!isObject7(value))
|
|
22324
|
+
return null;
|
|
22325
|
+
const model = name(value.model);
|
|
22326
|
+
return model ? { model, effort: name(value.effort) } : null;
|
|
22327
|
+
};
|
|
22328
|
+
var isFlat = (value) => typeof value === "string" || isObject7(value) && Object.hasOwn(value, "model");
|
|
22329
|
+
var forHost = (value, host) => {
|
|
22330
|
+
if (isFlat(value))
|
|
22331
|
+
return leaf(value);
|
|
22332
|
+
if (!isObject7(value))
|
|
22333
|
+
return null;
|
|
22334
|
+
const family = host ? hostFamily(host) : null;
|
|
22335
|
+
for (const key of [family, host, "default"])
|
|
22336
|
+
if (key && Object.hasOwn(value, key)) {
|
|
22337
|
+
const found = leaf(value[key]);
|
|
22338
|
+
if (found)
|
|
22339
|
+
return found;
|
|
22340
|
+
}
|
|
22341
|
+
return null;
|
|
22342
|
+
};
|
|
22343
|
+
var rolesOf = (models) => isObject7(models) && isObject7(models.roles) ? models.roles : null;
|
|
22344
|
+
var resolveRoleModel = (cwd, selector, host, dir) => {
|
|
22345
|
+
const { role, tier } = selector;
|
|
22346
|
+
for (const source of modelSources(cwd, dir)) {
|
|
22347
|
+
const where = source.global ? "the global default" : `workspace ${source.name}`;
|
|
22348
|
+
const roles = rolesOf(source.models);
|
|
22349
|
+
const configured = roles && Object.hasOwn(roles, role) ? roles[role] : undefined;
|
|
22350
|
+
const fromRoles = role === "implementer" ? tier && isObject7(configured) && Object.hasOwn(configured, tier) ? forHost(configured[tier], host) : null : forHost(configured, host);
|
|
22351
|
+
if (fromRoles)
|
|
22352
|
+
return { ...fromRoles, source: `models.roles in ${where}` };
|
|
22353
|
+
if (role === "implementer" && tier && isObject7(source.tierModels)) {
|
|
22354
|
+
const fromTiers = Object.hasOwn(source.tierModels, tier) ? forHost(source.tierModels[tier], host) : null;
|
|
22355
|
+
if (fromTiers)
|
|
22356
|
+
return { model: fromTiers.model, effort: null, source: `fanout.tierModels in ${where}` };
|
|
22357
|
+
}
|
|
22358
|
+
}
|
|
22359
|
+
if (host && hostFamily(host) === "claude_code") {
|
|
22360
|
+
const builtIn = BUILT_IN_CLAUDE_CODE[tier ? `${role}.${tier}` : role];
|
|
22361
|
+
if (builtIn)
|
|
22362
|
+
return { model: builtIn.model, effort: builtIn.effort ?? null, source: "built-in" };
|
|
22363
|
+
}
|
|
22364
|
+
return null;
|
|
22365
|
+
};
|
|
22366
|
+
var ROLE_SELECTORS = MODEL_ROLES.flatMap((role) => role === "implementer" ? MODEL_TIERS.map((tier) => ({ role, tier })) : [{ role }]);
|
|
22367
|
+
var roleForAgentType = (agentType, prompt) => {
|
|
22368
|
+
switch (agentType) {
|
|
22369
|
+
case "workit:implementer": {
|
|
22370
|
+
const tier = typeof prompt === "string" ? /^[ \t]*TIER:[ \t]*(mundane|standard|hard)\b/im.exec(prompt)?.[1] : undefined;
|
|
22371
|
+
return { role: "implementer", tier: tier?.toLowerCase() ?? "standard" };
|
|
22372
|
+
}
|
|
22373
|
+
case "workit:verifier":
|
|
22374
|
+
return { role: "verifier" };
|
|
22375
|
+
case "workit:reviewer":
|
|
22376
|
+
return { role: "reviewer" };
|
|
22377
|
+
case "Explore":
|
|
22378
|
+
return { role: "explore" };
|
|
22379
|
+
default:
|
|
22380
|
+
return null;
|
|
22381
|
+
}
|
|
22382
|
+
};
|
|
22383
|
+
|
|
22384
|
+
// packages/workit-core/src/hooks/agent-model.ts
|
|
22385
|
+
var MODELS = new Set(["sonnet", "opus", "haiku", "fable"]);
|
|
22386
|
+
var EFFORTS = new Set(["low", "medium", "high", "xhigh", "max"]);
|
|
22387
|
+
var given = (value) => typeof value === "string" && value.trim() !== "";
|
|
22388
|
+
var agentModelDecision = (cwd, tool, env = process.env) => {
|
|
22389
|
+
if (given(tool.model) || env.CLAUDE_CODE_SUBAGENT_MODEL_FORCE)
|
|
22390
|
+
return { kind: "none" };
|
|
22391
|
+
const selector = roleForAgentType(tool.subagent_type, tool.prompt);
|
|
22392
|
+
if (!selector)
|
|
22393
|
+
return { kind: "none" };
|
|
22394
|
+
const resolved = resolveRoleModel(cwd, selector, "claude_code");
|
|
22395
|
+
if (!resolved)
|
|
22396
|
+
return { kind: "none" };
|
|
22397
|
+
const model = resolved.model.toLowerCase();
|
|
22398
|
+
const effort = resolved.effort?.toLowerCase() ?? null;
|
|
22399
|
+
const input = { ...tool };
|
|
22400
|
+
if (MODELS.has(model))
|
|
22401
|
+
input.model = model;
|
|
22402
|
+
if (effort && EFFORTS.has(effort) && !given(tool.effort))
|
|
22403
|
+
input.effort = effort;
|
|
22404
|
+
if (input.model === undefined && input.effort === tool.effort)
|
|
22405
|
+
return { kind: "none" };
|
|
22406
|
+
const label = selector.tier ? `${selector.role} ${selector.tier}` : selector.role;
|
|
22407
|
+
return {
|
|
22408
|
+
kind: "update-input",
|
|
22409
|
+
input,
|
|
22410
|
+
text: `Workit set ${String(input.model ?? "no model")}${input.effort !== tool.effort ? ` (effort ${String(input.effort)})` : ""} on this ${String(tool.subagent_type)} spawn: role ${label}, ${resolved.source}. Name a model to choose another.`
|
|
22411
|
+
};
|
|
22412
|
+
};
|
|
22413
|
+
|
|
22253
22414
|
// packages/workit-core/src/hooks/cwd-normalize.ts
|
|
22254
22415
|
var DIR = String.raw`[A-Za-z0-9_./@%+:,=-]+|'[^'$${"`"}\\\n]*'|"[^"$${"`"}\\!\n]*"`;
|
|
22255
22416
|
var COMPOUND = new RegExp(String.raw`^\s*cd\s+(${DIR})\s*&&\s*(workit(?:\s[\s\S]*)?)$`, "u");
|
|
@@ -22613,7 +22774,7 @@ function handleHook(input, deps) {
|
|
|
22613
22774
|
const delta = standingDelta(input);
|
|
22614
22775
|
if (!delta)
|
|
22615
22776
|
return decision;
|
|
22616
|
-
return decision.kind === "rewrite" ? { ...decision, text: [decision.text, delta].filter(Boolean).join(`
|
|
22777
|
+
return decision.kind === "rewrite" || decision.kind === "update-input" ? { ...decision, text: [decision.text, delta].filter(Boolean).join(`
|
|
22617
22778
|
`) } : withContextLine(decision, delta);
|
|
22618
22779
|
}
|
|
22619
22780
|
function decide(input, deps) {
|
|
@@ -22726,6 +22887,8 @@ function decide(input, deps) {
|
|
|
22726
22887
|
case "tool.pre":
|
|
22727
22888
|
if (event.skill)
|
|
22728
22889
|
recordSkillLoad(input, event.skill, "tool");
|
|
22890
|
+
if (event.agentInput && input.host === "claude_code")
|
|
22891
|
+
return agentModelDecision(input.cwd, event.agentInput);
|
|
22729
22892
|
return NONE6;
|
|
22730
22893
|
case "stop":
|
|
22731
22894
|
return usable2(descriptor.events.stop.support) ? stopDecision(input, descriptor) : NONE6;
|
|
@@ -23009,6 +23172,16 @@ var eventOf = (name, value) => {
|
|
|
23009
23172
|
skill: isRecord6(value.tool_input) ? optionalText(value.tool_input.skill) : null
|
|
23010
23173
|
}
|
|
23011
23174
|
};
|
|
23175
|
+
if (value.tool_name === "Agent" || value.tool_name === "Task")
|
|
23176
|
+
return {
|
|
23177
|
+
ok: true,
|
|
23178
|
+
event: {
|
|
23179
|
+
kind: "tool.pre",
|
|
23180
|
+
tool: value.tool_name,
|
|
23181
|
+
toolUseId,
|
|
23182
|
+
...isRecord6(value.tool_input) ? { agentInput: value.tool_input } : {}
|
|
23183
|
+
}
|
|
23184
|
+
};
|
|
23012
23185
|
if (!SHELL_TOOLS.has(value.tool_name))
|
|
23013
23186
|
return { ok: true, event: { kind: "tool.pre", tool: value.tool_name, toolUseId } };
|
|
23014
23187
|
const command = toolCommand(value);
|
|
@@ -23085,7 +23258,7 @@ var CONTEXT_EVENTS = new Set([
|
|
|
23085
23258
|
"PostToolUse"
|
|
23086
23259
|
]);
|
|
23087
23260
|
var render = (decision, native) => {
|
|
23088
|
-
if (decision.kind === "rewrite" && native === "PreToolUse" && decision.input)
|
|
23261
|
+
if ((decision.kind === "rewrite" || decision.kind === "update-input") && native === "PreToolUse" && decision.input)
|
|
23089
23262
|
return {
|
|
23090
23263
|
json: {
|
|
23091
23264
|
hookSpecificOutput: {
|
package/hooks/launch.mjs
CHANGED
|
@@ -15,6 +15,7 @@
|
|
|
15
15
|
// failure, npx network error, timeout, a crash) prints `{}`, exits 0 and
|
|
16
16
|
// writes one `[workit] Cursor hook unavailable: …` line on stderr. Only a
|
|
17
17
|
// policy deny from the hook itself (exit 2 with Cursor's deny JSON) blocks.
|
|
18
|
+
import { readFileSync } from "node:fs";
|
|
18
19
|
import path from "node:path";
|
|
19
20
|
import { fileURLToPath } from "node:url";
|
|
20
21
|
import {
|
|
@@ -25,6 +26,35 @@ import {
|
|
|
25
26
|
|
|
26
27
|
const pluginRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..");
|
|
27
28
|
|
|
29
|
+
// A Node below this package's own engines minimum cannot run the bundled hook:
|
|
30
|
+
// fail open with one clear line. WORKIT_HOOK_NODE_VERSION stands in for
|
|
31
|
+
// process.versions.node (tests). An unreadable minimum never blocks.
|
|
32
|
+
const nodeFloorMessage = () => {
|
|
33
|
+
try {
|
|
34
|
+
const found = process.env.WORKIT_HOOK_NODE_VERSION || process.versions.node;
|
|
35
|
+
const range = JSON.parse(readFileSync(path.join(pluginRoot, "package.json"), "utf8")).engines
|
|
36
|
+
?.node;
|
|
37
|
+
const min = /^>=\s*(\d+)(?:\.(\d+))?(?:\.(\d+))?/.exec(String(range ?? ""));
|
|
38
|
+
if (!min) return null;
|
|
39
|
+
const need = [min[1], min[2] ?? 0, min[3] ?? 0].map(Number);
|
|
40
|
+
const have = String(found).replace(/^v/, "").split(".").map(Number);
|
|
41
|
+
for (let i = 0; i < 3; i++) {
|
|
42
|
+
const a = have[i] ?? 0;
|
|
43
|
+
if (Number.isNaN(a)) return null;
|
|
44
|
+
if (a !== need[i]) return a < need[i] ? `needs Node ${range}, found ${found}` : null;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
} catch {
|
|
48
|
+
return null;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
const tooOld = nodeFloorMessage();
|
|
52
|
+
if (tooOld && process.argv[2] !== "--probe") {
|
|
53
|
+
process.stderr.write(`[workit] Cursor hook unavailable: ${tooOld}\n`);
|
|
54
|
+
process.stdout.write("{}\n");
|
|
55
|
+
process.exit(0);
|
|
56
|
+
}
|
|
57
|
+
|
|
28
58
|
// `--probe <bin>` (for `workit doctor`): run one no-op preCompact through
|
|
29
59
|
// the candidate Cursor would use and print which one it was and how it went.
|
|
30
60
|
if (process.argv[2] === "--probe") {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.28.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
|
"devDependencies": {
|
|
49
|
-
"@brainervirus/workit-core": "^8.
|
|
50
|
-
"@brainervirus/workit-mcp": "^8.
|
|
49
|
+
"@brainervirus/workit-core": "^8.28.1",
|
|
50
|
+
"@brainervirus/workit-mcp": "^8.28.1"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=24"
|