@brainervirus/workit-cursor 8.17.1 → 8.18.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 +25 -14
- package/dist/mcp-server.js +26 -15
- package/dist/workit-hook.js +26 -15
- package/package.json +3 -3
- package/skills/workit-bdd/SKILL.md +1 -1
- package/skills/workit-fanout/SKILL.md +2 -2
- package/skills/workit-fanout/references/orchestration.md +21 -0
- package/skills/workit-retro/SKILL.md +2 -2
- package/skills/workit-review/SKILL.md +4 -1
- package/skills/workit-ship/SKILL.md +1 -1
- package/skills/workit-verify-app/SKILL.md +1 -1
|
@@ -11526,7 +11526,7 @@ var RULES, derived2 = (spec) => ({
|
|
|
11526
11526
|
};
|
|
11527
11527
|
const since = Date.parse(task.createdAt);
|
|
11528
11528
|
const branch = ledger.branch();
|
|
11529
|
-
const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
11529
|
+
const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
11530
11530
|
return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
|
|
11531
11531
|
met: false,
|
|
11532
11532
|
reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or, if it is not open, `workit policy assess --judge product-choice=no --why "<reason>"`)',
|
|
@@ -15033,6 +15033,11 @@ var init_workers = __esm(() => {
|
|
|
15033
15033
|
import { spawnSync as spawnSync9 } from "node:child_process";
|
|
15034
15034
|
import fs12 from "node:fs";
|
|
15035
15035
|
import path16 from "node:path";
|
|
15036
|
+
function checksConfigFile(root) {
|
|
15037
|
+
const current = path16.join(root, CHECKS_FILE);
|
|
15038
|
+
const legacy = path16.join(root, LEGACY_CHECKS_FILE);
|
|
15039
|
+
return !fs12.existsSync(current) && fs12.existsSync(legacy) ? legacy : current;
|
|
15040
|
+
}
|
|
15036
15041
|
function repoHeavyCommands(root) {
|
|
15037
15042
|
try {
|
|
15038
15043
|
const value = JSON.parse(fs12.readFileSync(checksConfigFile(root), "utf8"));
|
|
@@ -15124,19 +15129,25 @@ function detectedChecks(root) {
|
|
|
15124
15129
|
function loadCheckConfig(cwd) {
|
|
15125
15130
|
const root = checkRoot(cwd);
|
|
15126
15131
|
const file = checksConfigFile(root);
|
|
15132
|
+
const legacy = path16.basename(file) === LEGACY_CHECKS_FILE;
|
|
15133
|
+
const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
|
|
15134
|
+
const note = legacy ? {
|
|
15135
|
+
note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
|
|
15136
|
+
} : {};
|
|
15127
15137
|
const none = (error = null) => ({
|
|
15128
|
-
source: error ?
|
|
15138
|
+
source: error ? label : "none",
|
|
15129
15139
|
root,
|
|
15130
15140
|
checks: [],
|
|
15131
15141
|
gates: {},
|
|
15132
|
-
error
|
|
15142
|
+
error,
|
|
15143
|
+
...note
|
|
15133
15144
|
});
|
|
15134
15145
|
let text = null;
|
|
15135
15146
|
try {
|
|
15136
15147
|
text = fs12.readFileSync(file, "utf8");
|
|
15137
15148
|
} catch (error) {
|
|
15138
15149
|
if (error.code !== "ENOENT")
|
|
15139
|
-
return none(`cannot read ${
|
|
15150
|
+
return none(`cannot read ${label}: ${error.message}`);
|
|
15140
15151
|
}
|
|
15141
15152
|
if (text === null) {
|
|
15142
15153
|
const checks = detectedChecks(root);
|
|
@@ -15146,33 +15157,33 @@ function loadCheckConfig(cwd) {
|
|
|
15146
15157
|
try {
|
|
15147
15158
|
parsed = JSON.parse(text);
|
|
15148
15159
|
} catch {
|
|
15149
|
-
return none(`${
|
|
15160
|
+
return none(`${label} is not valid JSON`);
|
|
15150
15161
|
}
|
|
15151
15162
|
if (!isRecord3(parsed) || !isRecord3(parsed.checks))
|
|
15152
|
-
return none(`${
|
|
15163
|
+
return none(`${label} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
|
|
15153
15164
|
const checks = [];
|
|
15154
15165
|
for (const [name, value] of Object.entries(parsed.checks)) {
|
|
15155
15166
|
if (!CHECK_NAME.test(name))
|
|
15156
|
-
return none(`${
|
|
15167
|
+
return none(`${label}: check name "${name}" must match ${CHECK_NAME.source}`);
|
|
15157
15168
|
const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
|
|
15158
15169
|
if (argv instanceof Error)
|
|
15159
|
-
return none(`${
|
|
15170
|
+
return none(`${label}: check "${name}": ${argv.message}`);
|
|
15160
15171
|
checks.push({ name, argv, accepts: [argv] });
|
|
15161
15172
|
}
|
|
15162
15173
|
const gates = {};
|
|
15163
15174
|
if (parsed.gates !== undefined) {
|
|
15164
15175
|
if (!isRecord3(parsed.gates))
|
|
15165
|
-
return none(`${
|
|
15176
|
+
return none(`${label}: gates must be {"testing":"<name>"|["<name>",…]}`);
|
|
15166
15177
|
for (const [gate, value] of Object.entries(parsed.gates)) {
|
|
15167
15178
|
if (!GATES.includes(gate))
|
|
15168
|
-
return none(`${
|
|
15179
|
+
return none(`${label}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
|
|
15169
15180
|
const names = typeof value === "string" ? [value] : value;
|
|
15170
15181
|
if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
|
|
15171
|
-
return none(`${
|
|
15182
|
+
return none(`${label}: gates.${gate} must name configured checks`);
|
|
15172
15183
|
gates[gate] = names;
|
|
15173
15184
|
}
|
|
15174
15185
|
}
|
|
15175
|
-
return { source:
|
|
15186
|
+
return { source: label, root, checks, gates, error: null, ...note, resources: parsed.resources };
|
|
15176
15187
|
}
|
|
15177
15188
|
function matchesNamedCheck(config, name, argv, cwd) {
|
|
15178
15189
|
if (config.error || name === null || cwd !== ".")
|
|
@@ -15207,7 +15218,7 @@ function workitAvailable(env = process.env) {
|
|
|
15207
15218
|
function checkCommand(args, version, onPath = workitAvailable()) {
|
|
15208
15219
|
return onPath ? `workit check ${args}` : `npx -y @brainervirus/workit-cli@${version} check ${args}`;
|
|
15209
15220
|
}
|
|
15210
|
-
var SCRIPT_CHECKS, CHECKS_FILE = "workit
|
|
15221
|
+
var SCRIPT_CHECKS, CHECKS_FILE = ".workit/checks.json", LEGACY_CHECKS_FILE = "workit.checks.json", isRecord3 = (value) => typeof value === "object" && value !== null && !Array.isArray(value), SHELL_META, git4 = (cwd, args) => {
|
|
15211
15222
|
const run = spawnSync9("git", args, {
|
|
15212
15223
|
cwd,
|
|
15213
15224
|
encoding: "utf8",
|
|
@@ -15423,7 +15434,7 @@ function evaluateObservedGate(task, requirement, statuses, config) {
|
|
|
15423
15434
|
return {
|
|
15424
15435
|
satisfied: [],
|
|
15425
15436
|
considered,
|
|
15426
|
-
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}
|
|
15437
|
+
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}, e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
|
|
15427
15438
|
};
|
|
15428
15439
|
const satisfied = fresh.filter(named);
|
|
15429
15440
|
if (satisfied.length)
|
package/dist/mcp-server.js
CHANGED
|
@@ -21250,8 +21250,13 @@ import { spawnSync as spawnSync3 } from "node:child_process";
|
|
|
21250
21250
|
import fs9 from "node:fs";
|
|
21251
21251
|
import path9 from "node:path";
|
|
21252
21252
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
21253
|
-
var CHECKS_FILE = "workit
|
|
21254
|
-
var
|
|
21253
|
+
var CHECKS_FILE = ".workit/checks.json";
|
|
21254
|
+
var LEGACY_CHECKS_FILE = "workit.checks.json";
|
|
21255
|
+
function checksConfigFile(root) {
|
|
21256
|
+
const current = path9.join(root, CHECKS_FILE);
|
|
21257
|
+
const legacy = path9.join(root, LEGACY_CHECKS_FILE);
|
|
21258
|
+
return !fs9.existsSync(current) && fs9.existsSync(legacy) ? legacy : current;
|
|
21259
|
+
}
|
|
21255
21260
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
21256
21261
|
var SHELL_META = /[|&;<>()$`\\*?[\]{}~!#]/u;
|
|
21257
21262
|
function splitCommand(command) {
|
|
@@ -21365,19 +21370,25 @@ var GATES = ["testing", "verification"];
|
|
|
21365
21370
|
function loadCheckConfig(cwd) {
|
|
21366
21371
|
const root = checkRoot(cwd);
|
|
21367
21372
|
const file = checksConfigFile(root);
|
|
21373
|
+
const legacy = path9.basename(file) === LEGACY_CHECKS_FILE;
|
|
21374
|
+
const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
|
|
21375
|
+
const note = legacy ? {
|
|
21376
|
+
note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
|
|
21377
|
+
} : {};
|
|
21368
21378
|
const none = (error = null) => ({
|
|
21369
|
-
source: error ?
|
|
21379
|
+
source: error ? label : "none",
|
|
21370
21380
|
root,
|
|
21371
21381
|
checks: [],
|
|
21372
21382
|
gates: {},
|
|
21373
|
-
error
|
|
21383
|
+
error,
|
|
21384
|
+
...note
|
|
21374
21385
|
});
|
|
21375
21386
|
let text = null;
|
|
21376
21387
|
try {
|
|
21377
21388
|
text = fs9.readFileSync(file, "utf8");
|
|
21378
21389
|
} catch (error) {
|
|
21379
21390
|
if (error.code !== "ENOENT")
|
|
21380
|
-
return none(`cannot read ${
|
|
21391
|
+
return none(`cannot read ${label}: ${error.message}`);
|
|
21381
21392
|
}
|
|
21382
21393
|
if (text === null) {
|
|
21383
21394
|
const checks = detectedChecks(root);
|
|
@@ -21387,33 +21398,33 @@ function loadCheckConfig(cwd) {
|
|
|
21387
21398
|
try {
|
|
21388
21399
|
parsed = JSON.parse(text);
|
|
21389
21400
|
} catch {
|
|
21390
|
-
return none(`${
|
|
21401
|
+
return none(`${label} is not valid JSON`);
|
|
21391
21402
|
}
|
|
21392
21403
|
if (!isRecord(parsed) || !isRecord(parsed.checks))
|
|
21393
|
-
return none(`${
|
|
21404
|
+
return none(`${label} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
|
|
21394
21405
|
const checks = [];
|
|
21395
21406
|
for (const [name, value] of Object.entries(parsed.checks)) {
|
|
21396
21407
|
if (!CHECK_NAME.test(name))
|
|
21397
|
-
return none(`${
|
|
21408
|
+
return none(`${label}: check name "${name}" must match ${CHECK_NAME.source}`);
|
|
21398
21409
|
const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
|
|
21399
21410
|
if (argv instanceof Error)
|
|
21400
|
-
return none(`${
|
|
21411
|
+
return none(`${label}: check "${name}": ${argv.message}`);
|
|
21401
21412
|
checks.push({ name, argv, accepts: [argv] });
|
|
21402
21413
|
}
|
|
21403
21414
|
const gates = {};
|
|
21404
21415
|
if (parsed.gates !== undefined) {
|
|
21405
21416
|
if (!isRecord(parsed.gates))
|
|
21406
|
-
return none(`${
|
|
21417
|
+
return none(`${label}: gates must be {"testing":"<name>"|["<name>",…]}`);
|
|
21407
21418
|
for (const [gate, value] of Object.entries(parsed.gates)) {
|
|
21408
21419
|
if (!GATES.includes(gate))
|
|
21409
|
-
return none(`${
|
|
21420
|
+
return none(`${label}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
|
|
21410
21421
|
const names = typeof value === "string" ? [value] : value;
|
|
21411
21422
|
if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
|
|
21412
|
-
return none(`${
|
|
21423
|
+
return none(`${label}: gates.${gate} must name configured checks`);
|
|
21413
21424
|
gates[gate] = names;
|
|
21414
21425
|
}
|
|
21415
21426
|
}
|
|
21416
|
-
return { source:
|
|
21427
|
+
return { source: label, root, checks, gates, error: null, ...note, resources: parsed.resources };
|
|
21417
21428
|
}
|
|
21418
21429
|
var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
|
|
21419
21430
|
function matchesNamedCheck(config, name, argv, cwd) {
|
|
@@ -24072,7 +24083,7 @@ var productDecision = (task, ledger) => {
|
|
|
24072
24083
|
};
|
|
24073
24084
|
const since = Date.parse(task.createdAt);
|
|
24074
24085
|
const branch = ledger.branch();
|
|
24075
|
-
const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
24086
|
+
const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
24076
24087
|
return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
|
|
24077
24088
|
met: false,
|
|
24078
24089
|
reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or, if it is not open, `workit policy assess --judge product-choice=no --why "<reason>"`)',
|
|
@@ -24556,7 +24567,7 @@ function evaluateObservedGate(task, requirement, statuses, config) {
|
|
|
24556
24567
|
return {
|
|
24557
24568
|
satisfied: [],
|
|
24558
24569
|
considered,
|
|
24559
|
-
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}
|
|
24570
|
+
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}, e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
|
|
24560
24571
|
};
|
|
24561
24572
|
const satisfied = fresh.filter(named);
|
|
24562
24573
|
if (satisfied.length)
|
package/dist/workit-hook.js
CHANGED
|
@@ -11528,7 +11528,7 @@ var productDecision = (task, ledger) => {
|
|
|
11528
11528
|
};
|
|
11529
11529
|
const since = Date.parse(task.createdAt);
|
|
11530
11530
|
const branch = ledger.branch();
|
|
11531
|
-
const row = ledger.rows().findLast((item) => item.type === "decision" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
11531
|
+
const row = ledger.rows().findLast((item) => item.type === "decision" && item.by !== "decider" && !item.superseded && (item.branch ?? null) === branch && Date.parse(item.at) >= since);
|
|
11532
11532
|
return row ? { met: true, reason: `ledger decision ${row.id} records the choice`, decisionIds: [] } : {
|
|
11533
11533
|
met: false,
|
|
11534
11534
|
reason: 'open product choice: ask the user, then `workit ledger decision "<choice>" --why "<reason>"` (or, if it is not open, `workit policy assess --judge product-choice=no --why "<reason>"`)',
|
|
@@ -15071,8 +15071,13 @@ import { spawnSync as spawnSync9 } from "node:child_process";
|
|
|
15071
15071
|
import fs12 from "node:fs";
|
|
15072
15072
|
import path16 from "node:path";
|
|
15073
15073
|
var SCRIPT_CHECKS = ["test", "lint", "typecheck", "check"];
|
|
15074
|
-
var CHECKS_FILE = "workit
|
|
15075
|
-
var
|
|
15074
|
+
var CHECKS_FILE = ".workit/checks.json";
|
|
15075
|
+
var LEGACY_CHECKS_FILE = "workit.checks.json";
|
|
15076
|
+
function checksConfigFile(root) {
|
|
15077
|
+
const current = path16.join(root, CHECKS_FILE);
|
|
15078
|
+
const legacy = path16.join(root, LEGACY_CHECKS_FILE);
|
|
15079
|
+
return !fs12.existsSync(current) && fs12.existsSync(legacy) ? legacy : current;
|
|
15080
|
+
}
|
|
15076
15081
|
function repoHeavyCommands(root) {
|
|
15077
15082
|
try {
|
|
15078
15083
|
const value = JSON.parse(fs12.readFileSync(checksConfigFile(root), "utf8"));
|
|
@@ -15196,19 +15201,25 @@ var GATES = ["testing", "verification"];
|
|
|
15196
15201
|
function loadCheckConfig(cwd) {
|
|
15197
15202
|
const root = checkRoot(cwd);
|
|
15198
15203
|
const file = checksConfigFile(root);
|
|
15204
|
+
const legacy = path16.basename(file) === LEGACY_CHECKS_FILE;
|
|
15205
|
+
const label = legacy ? LEGACY_CHECKS_FILE : CHECKS_FILE;
|
|
15206
|
+
const note = legacy ? {
|
|
15207
|
+
note: `${LEGACY_CHECKS_FILE} is deprecated; move it to ${CHECKS_FILE} (workit doctor --fix)`
|
|
15208
|
+
} : {};
|
|
15199
15209
|
const none = (error = null) => ({
|
|
15200
|
-
source: error ?
|
|
15210
|
+
source: error ? label : "none",
|
|
15201
15211
|
root,
|
|
15202
15212
|
checks: [],
|
|
15203
15213
|
gates: {},
|
|
15204
|
-
error
|
|
15214
|
+
error,
|
|
15215
|
+
...note
|
|
15205
15216
|
});
|
|
15206
15217
|
let text = null;
|
|
15207
15218
|
try {
|
|
15208
15219
|
text = fs12.readFileSync(file, "utf8");
|
|
15209
15220
|
} catch (error) {
|
|
15210
15221
|
if (error.code !== "ENOENT")
|
|
15211
|
-
return none(`cannot read ${
|
|
15222
|
+
return none(`cannot read ${label}: ${error.message}`);
|
|
15212
15223
|
}
|
|
15213
15224
|
if (text === null) {
|
|
15214
15225
|
const checks = detectedChecks(root);
|
|
@@ -15218,33 +15229,33 @@ function loadCheckConfig(cwd) {
|
|
|
15218
15229
|
try {
|
|
15219
15230
|
parsed = JSON.parse(text);
|
|
15220
15231
|
} catch {
|
|
15221
|
-
return none(`${
|
|
15232
|
+
return none(`${label} is not valid JSON`);
|
|
15222
15233
|
}
|
|
15223
15234
|
if (!isRecord3(parsed) || !isRecord3(parsed.checks))
|
|
15224
|
-
return none(`${
|
|
15235
|
+
return none(`${label} must be {"checks":{"<name>":"<command>"|["argv",…]}}`);
|
|
15225
15236
|
const checks = [];
|
|
15226
15237
|
for (const [name, value] of Object.entries(parsed.checks)) {
|
|
15227
15238
|
if (!CHECK_NAME.test(name))
|
|
15228
|
-
return none(`${
|
|
15239
|
+
return none(`${label}: check name "${name}" must match ${CHECK_NAME.source}`);
|
|
15229
15240
|
const argv = typeof value === "string" ? splitCommand(value) : Array.isArray(value) && value.length > 0 && value.every((item) => typeof item === "string" && item.length > 0) ? value : new Error("expected a command string or a non-empty argv array");
|
|
15230
15241
|
if (argv instanceof Error)
|
|
15231
|
-
return none(`${
|
|
15242
|
+
return none(`${label}: check "${name}": ${argv.message}`);
|
|
15232
15243
|
checks.push({ name, argv, accepts: [argv] });
|
|
15233
15244
|
}
|
|
15234
15245
|
const gates = {};
|
|
15235
15246
|
if (parsed.gates !== undefined) {
|
|
15236
15247
|
if (!isRecord3(parsed.gates))
|
|
15237
|
-
return none(`${
|
|
15248
|
+
return none(`${label}: gates must be {"testing":"<name>"|["<name>",…]}`);
|
|
15238
15249
|
for (const [gate, value] of Object.entries(parsed.gates)) {
|
|
15239
15250
|
if (!GATES.includes(gate))
|
|
15240
|
-
return none(`${
|
|
15251
|
+
return none(`${label}: unknown gate "${gate}" (use ${GATES.join(" or ")})`);
|
|
15241
15252
|
const names = typeof value === "string" ? [value] : value;
|
|
15242
15253
|
if (!Array.isArray(names) || names.length === 0 || !names.every((name) => typeof name === "string" && checks.some((c) => c.name === name)))
|
|
15243
|
-
return none(`${
|
|
15254
|
+
return none(`${label}: gates.${gate} must name configured checks`);
|
|
15244
15255
|
gates[gate] = names;
|
|
15245
15256
|
}
|
|
15246
15257
|
}
|
|
15247
|
-
return { source:
|
|
15258
|
+
return { source: label, root, checks, gates, error: null, ...note, resources: parsed.resources };
|
|
15248
15259
|
}
|
|
15249
15260
|
var sameArgv = (left, right) => left.length === right.length && left.every((item, index) => item === right[index]);
|
|
15250
15261
|
function matchesNamedCheck(config, name, argv, cwd) {
|
|
@@ -15711,7 +15722,7 @@ function evaluateObservedGate(task, requirement, statuses, config) {
|
|
|
15711
15722
|
return {
|
|
15712
15723
|
satisfied: [],
|
|
15713
15724
|
considered,
|
|
15714
|
-
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}
|
|
15725
|
+
reason: requirement.dimension === "testing" && config.checks.length ? `no \`test\` check is configured; add one (or gates.testing) to ${CHECKS_FILE}, then run ${checkCommand("test", runtimeVersion())}` : `no checks are configured or detected; add ${CHECKS_FILE}, e.g. {"checks":{"test":"<your test command>"}}, then run ${checkCommand("test", runtimeVersion())}`
|
|
15715
15726
|
};
|
|
15716
15727
|
const satisfied = fresh.filter(named);
|
|
15717
15728
|
if (satisfied.length)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.18.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.18.0",
|
|
50
|
+
"@brainervirus/workit-mcp": "^8.18.0"
|
|
51
51
|
},
|
|
52
52
|
"engines": {
|
|
53
53
|
"node": ">=24"
|
|
@@ -22,7 +22,7 @@ description: Turn requirements into Given/When/Then scenarios, agree the test se
|
|
|
22
22
|
then take the next scenario. Any edit makes the observation stale; re-run
|
|
23
23
|
before you claim it. A recorded "tests pass" is a note, and an ad-hoc
|
|
24
24
|
`workit check -- <cmd>` never satisfies the gate: only the configured
|
|
25
|
-
`test` check does. No `test` detected? Create
|
|
25
|
+
`test` check does. No `test` detected? Create `.workit/checks.json`, copying
|
|
26
26
|
in every check the repo already runs: once it exists it replaces the
|
|
27
27
|
detected defaults.
|
|
28
28
|
5. **Mock only at system boundaries:** network, clock, randomness, other
|
|
@@ -37,8 +37,8 @@ units of ~100+ lines each. Coupled work stays with one owner. Workit never spawn
|
|
|
37
37
|
6. **Retry once, then escalate.** A stuck or failed slice gets one retry with
|
|
38
38
|
a fresh brief (`workit fanout brief <slice> --mode resume`, same branch);
|
|
39
39
|
if that fails too, re-slice it, take it over, or report the gap. Never
|
|
40
|
-
chain resumes. A
|
|
41
|
-
|
|
40
|
+
chain resumes. A fix round and recheck that both fail: a decider, not a
|
|
41
|
+
third round; product choices go to the user (`references/orchestration.md`).
|
|
42
42
|
7. **Verify by the workspace `verification` setting** (`workit grant show`).
|
|
43
43
|
A report is a pointer, never evidence. `self`: a verifier that wrote none
|
|
44
44
|
of the slices, or you if you wrote none. `independent` or high risk: a
|
|
@@ -43,6 +43,27 @@ rulings). If the retry fails too, escalate instead of a third worker:
|
|
|
43
43
|
re-slice it smaller (`workit fanout plan` again), take it over yourself, or
|
|
44
44
|
report the gap to the user.
|
|
45
45
|
|
|
46
|
+
## Stuck calls: the decider
|
|
47
|
+
|
|
48
|
+
A failed review gets one fix round and one recheck (the changed hunks only).
|
|
49
|
+
If the recheck fails, or a worker and a reviewer disagree, spawn a decider
|
|
50
|
+
instead of a third round. A decider is a fresh read-only agent, not the author
|
|
51
|
+
or the reviewer. It returns three things: the options, a pick by
|
|
52
|
+
reversibility (the option cheapest to undo wins a tie), and the exact change
|
|
53
|
+
list. The fixer applies that list once, then one focused recheck follows.
|
|
54
|
+
|
|
55
|
+
- Hard call (shared contract, security, migration): two deciders on different
|
|
56
|
+
models (Claude Code: a different `model` on the Agent call; OpenCode: a
|
|
57
|
+
child session on another configured agent; Codex, Cursor and Pi: one
|
|
58
|
+
decider, since the host picks no model). If they disagree, you take the
|
|
59
|
+
more reversible option.
|
|
60
|
+
- Record it: `workit ledger decision "<pick>" --by decider --why "<why it is
|
|
61
|
+
the more reversible>" --view "<decider A: view>" --view "<decider B: view>"`.
|
|
62
|
+
The row is listed as a decider call; it is never the user's answer to a
|
|
63
|
+
product choice and never a verdict.
|
|
64
|
+
- A product choice, or authority nobody here has, is not a decider's call:
|
|
65
|
+
ask the user with your recommendation.
|
|
66
|
+
|
|
46
67
|
## Race a hard slice
|
|
47
68
|
|
|
48
69
|
When a hard slice has an uncertain approach and a cheap VERIFY, run 2-3
|
|
@@ -32,7 +32,7 @@ proposes and stops. Nothing changes until the user approves.
|
|
|
32
32
|
more cited occurrences**; a one-off is not a learning.
|
|
33
33
|
4. **Name the strongest enforcer that works**, strongest first: architecture
|
|
34
34
|
or types (the mistake cannot be written) > lint rule > configured check
|
|
35
|
-
(
|
|
35
|
+
(`.workit/checks.json`, `workit check <name>`) or CI job > test >
|
|
36
36
|
`CODING_STANDARDS.md` (judgment the reviewer reads) > AGENTS.md pointer
|
|
37
37
|
(navigation only). A mechanical rule gets a check, not prose: a check can
|
|
38
38
|
fail, a sentence cannot. From types to test, the proof is that the new
|
|
@@ -60,7 +60,7 @@ Bad: "Agents seem lost in the build. Added 'read the build docs carefully' to
|
|
|
60
60
|
AGENTS.md." One vague occurrence, no citation, a no-op line, auto-applied.
|
|
61
61
|
|
|
62
62
|
Good: "Navigation, 3 occurrences (rulings 12, 19; PR #88 thread): agents sought
|
|
63
|
-
check names in package.json, missing
|
|
63
|
+
check names in package.json, missing `.workit/checks.json`. Enforcer: AGENTS.md
|
|
64
64
|
pointer, +74 bytes, minus the stale Commands paragraph (-210). Unstable check,
|
|
65
65
|
2 occurrences (ledger rows 31, 44: `e2e` red then green on one SHA): a debug
|
|
66
66
|
slice to fix the check. Approve?"
|
|
@@ -36,7 +36,10 @@ independent; `type-check-only` never proves a behavior change.
|
|
|
36
36
|
7. Record the verdict:
|
|
37
37
|
`workit ledger verdict verified|failed|blocked --kind review --branch <b> --how "<what you ran and read>"`
|
|
38
38
|
under your own session (the one the lead or the hook gave you).
|
|
39
|
-
8. **
|
|
39
|
+
8. **Bound the loop.** A failed verdict gets one fix round and one recheck of
|
|
40
|
+
the changed hunks. Still failing, or stuck: no third round; the lead spawns
|
|
41
|
+
a decider (read the `workit-fanout` skill's SKILL.md and follow it). A product choice goes to the user.
|
|
42
|
+
9. **Harness note** (optional): friction caused by the repo or harness, not
|
|
40
43
|
the change (a flaky fixture, a missing check, a misleading doc or skill
|
|
41
44
|
line), goes on its own `Harness note:` line in the report and any PR
|
|
42
45
|
comment, never as a finding or in the verdict, so a later retro finds it.
|
|
@@ -30,7 +30,7 @@ once the verdict is accepted. `merged`: step 7. `null` (closed, not merged):
|
|
|
30
30
|
stop and report. Stop early only for a new consequential choice, a host
|
|
31
31
|
denial, a review comment that needs a product decision, a required update
|
|
32
32
|
that repeats because the base keeps moving, or after 3 failed fix attempts on
|
|
33
|
-
the same check.
|
|
33
|
+
the same check. Fix round and recheck both failed: a decider (read the `workit-fanout` skill's SKILL.md and follow it).
|
|
34
34
|
|
|
35
35
|
1. **Open.** `workit git push`, then `workit pr create --title "<title>"
|
|
36
36
|
--body-file <f|->` plus `--issue <#n|ID>` per issue (`--refs`: no close; idempotent;
|
|
@@ -35,7 +35,7 @@ way a user would.
|
|
|
35
35
|
never run is a guess; do not hand it off.
|
|
36
36
|
4. **Make the driver a named check** when it is cheap and deterministic. A
|
|
37
37
|
named check is one command without shell syntax (put pipes and setup in a
|
|
38
|
-
script). Add `"verify": "<driver>"` to `checks` in
|
|
38
|
+
script). Add `"verify": "<driver>"` to `checks` in `.workit/checks.json`; if
|
|
39
39
|
the file does not exist yet, first copy in every check the repo already runs
|
|
40
40
|
(test, lint, typecheck), because the file replaces the detected defaults.
|
|
41
41
|
`workit check verify` then records it, and it joins the verification gate
|