@brainervirus/workit-cli 8.6.1 → 8.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.js +407 -28
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -65,7 +65,7 @@ var package_default;
|
|
|
65
65
|
var init_package = __esm(() => {
|
|
66
66
|
package_default = {
|
|
67
67
|
name: "@brainervirus/workit-cli",
|
|
68
|
-
version: "8.
|
|
68
|
+
version: "8.7.0",
|
|
69
69
|
private: false,
|
|
70
70
|
description: "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
71
71
|
keywords: [
|
|
@@ -103,9 +103,9 @@ var init_package = __esm(() => {
|
|
|
103
103
|
},
|
|
104
104
|
dependencies: {
|
|
105
105
|
"@babel/parser": "7.29.9",
|
|
106
|
-
"@brainervirus/workit-core": "^8.
|
|
107
|
-
"@brainervirus/workit-cursor": "^8.
|
|
108
|
-
"@brainervirus/workit-opencode": "^8.
|
|
106
|
+
"@brainervirus/workit-core": "^8.7.0",
|
|
107
|
+
"@brainervirus/workit-cursor": "^8.7.0",
|
|
108
|
+
"@brainervirus/workit-opencode": "^8.7.0",
|
|
109
109
|
"@inkjs/ui": "2.0.0",
|
|
110
110
|
ink: "7.1.1",
|
|
111
111
|
react: "19.2.8"
|
|
@@ -63932,11 +63932,13 @@ var init_skill_manifests = __esm(() => {
|
|
|
63932
63932
|
|
|
63933
63933
|
// packages/workit-cli/src/admin/doctor.ts
|
|
63934
63934
|
import { spawnSync as spawnSync13 } from "node:child_process";
|
|
63935
|
+
import { createHash as createHash13 } from "node:crypto";
|
|
63935
63936
|
import {
|
|
63936
63937
|
existsSync as existsSync13,
|
|
63937
63938
|
lstatSync as lstatSync4,
|
|
63938
63939
|
mkdirSync as mkdirSync5,
|
|
63939
63940
|
mkdtempSync,
|
|
63941
|
+
readdirSync as readdirSync7,
|
|
63940
63942
|
readFileSync as readFileSync13,
|
|
63941
63943
|
readlinkSync as readlinkSync2,
|
|
63942
63944
|
rmSync as rmSync2,
|
|
@@ -63977,8 +63979,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
63977
63979
|
cursorMcp: options.cursorMcp ?? path30.join(home, ".cursor", "mcp.json"),
|
|
63978
63980
|
cursorPluginDir: options.cursorPluginDir ?? path30.join(home, ".cursor", "plugins", "local", "workit"),
|
|
63979
63981
|
cursorHeartbeatDir: options.stateDir ?? (env.WORKFLOW_TOOLKIT_STATE || resolveStateDir()),
|
|
63982
|
+
codexHome: options.codexHome,
|
|
63983
|
+
piAgentDir: options.piAgentDir,
|
|
63980
63984
|
env,
|
|
63981
|
-
installer: options.installer ?? false
|
|
63985
|
+
installer: options.installer ?? false,
|
|
63986
|
+
cliVersion: options.cliVersion ?? null
|
|
63982
63987
|
};
|
|
63983
63988
|
}, readJson = (p) => {
|
|
63984
63989
|
try {
|
|
@@ -64199,6 +64204,368 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
64199
64204
|
detail: `codex CLI ${installed} differs from the qualified ${qualified}`,
|
|
64200
64205
|
fix: `Reinstall the qualified Codex CLI ${qualified} or record fresh qualification evidence`
|
|
64201
64206
|
};
|
|
64207
|
+
}, readTomlTables = (file) => {
|
|
64208
|
+
const tables = new Map;
|
|
64209
|
+
let text;
|
|
64210
|
+
try {
|
|
64211
|
+
text = readFileSync13(file, "utf8");
|
|
64212
|
+
} catch {
|
|
64213
|
+
return tables;
|
|
64214
|
+
}
|
|
64215
|
+
const unquote = (raw) => {
|
|
64216
|
+
const s = raw.trim();
|
|
64217
|
+
if (s.startsWith('"')) {
|
|
64218
|
+
try {
|
|
64219
|
+
return JSON.parse(s);
|
|
64220
|
+
} catch {
|
|
64221
|
+
return s.slice(1, -1);
|
|
64222
|
+
}
|
|
64223
|
+
}
|
|
64224
|
+
return s.startsWith("'") ? s.slice(1, -1) : s;
|
|
64225
|
+
};
|
|
64226
|
+
const SEGMENT = /\s*("(?:[^"\\]|\\.)*"|'[^']*'|[A-Za-z0-9_-]+)\s*(?:\.|$)/y;
|
|
64227
|
+
let current = {};
|
|
64228
|
+
tables.set("", current);
|
|
64229
|
+
for (const line of text.split(/\r?\n/)) {
|
|
64230
|
+
const header = /^\s*\[([^[\]]+)\]\s*(?:#.*)?$/.exec(line);
|
|
64231
|
+
if (header) {
|
|
64232
|
+
const parts = [];
|
|
64233
|
+
SEGMENT.lastIndex = 0;
|
|
64234
|
+
let match;
|
|
64235
|
+
while (SEGMENT.lastIndex < header[1].length && (match = SEGMENT.exec(header[1])))
|
|
64236
|
+
parts.push(unquote(match[1]));
|
|
64237
|
+
const key = parts.join("\x00");
|
|
64238
|
+
current = tables.get(key) ?? {};
|
|
64239
|
+
tables.set(key, current);
|
|
64240
|
+
continue;
|
|
64241
|
+
}
|
|
64242
|
+
const pair = /^\s*("(?:[^"\\]|\\.)*"|[A-Za-z0-9_-]+)\s*=\s*(.+?)\s*$/.exec(line);
|
|
64243
|
+
if (!pair)
|
|
64244
|
+
continue;
|
|
64245
|
+
const value = pair[2].replace(/\s+#[^"']*$/, "");
|
|
64246
|
+
current[unquote(pair[1])] = value === "true" ? true : value === "false" ? false : unquote(value);
|
|
64247
|
+
}
|
|
64248
|
+
return tables;
|
|
64249
|
+
}, codexHomeOf = (res) => res.codexHome ?? (res.env.CODEX_HOME || path30.join(res.home, ".codex")), codexWorkitInstalls = (res) => {
|
|
64250
|
+
const codexHome = codexHomeOf(res);
|
|
64251
|
+
const configFile = path30.join(codexHome, "config.toml");
|
|
64252
|
+
const installs = [];
|
|
64253
|
+
for (const [key, table] of readTomlTables(configFile)) {
|
|
64254
|
+
const [kind, id, ...rest] = key.split("\x00");
|
|
64255
|
+
if (kind !== "plugins" || rest.length > 0 || !id?.startsWith("workit@") || table.enabled === false)
|
|
64256
|
+
continue;
|
|
64257
|
+
const marketplace = id.slice("workit@".length);
|
|
64258
|
+
const dir = path30.join(codexHome, "plugins", "cache", marketplace, "workit");
|
|
64259
|
+
let versions = [];
|
|
64260
|
+
try {
|
|
64261
|
+
versions = readdirSync7(dir).filter((v) => existsSync13(path30.join(dir, v, ".codex-plugin")));
|
|
64262
|
+
} catch {
|
|
64263
|
+
versions = [];
|
|
64264
|
+
}
|
|
64265
|
+
const version = versions.reduce((best, v) => best && semverAtLeast(best, v) ? best : v, null);
|
|
64266
|
+
installs.push({
|
|
64267
|
+
id,
|
|
64268
|
+
marketplace,
|
|
64269
|
+
root: version ? path30.join(dir, version) : null,
|
|
64270
|
+
version,
|
|
64271
|
+
configFile
|
|
64272
|
+
});
|
|
64273
|
+
}
|
|
64274
|
+
return installs;
|
|
64275
|
+
}, snakeEvent = (event) => event.replace(/([a-z0-9])([A-Z])/g, "$1_$2").toLowerCase(), codexHookHash = (event, matcher, handler) => {
|
|
64276
|
+
if (handler.type !== "command" || typeof handler.command !== "string" || Object.keys(handler).some((k) => !["type", "command", "timeout", "async"].includes(k)))
|
|
64277
|
+
return null;
|
|
64278
|
+
const hook = {
|
|
64279
|
+
async: handler.async ?? false,
|
|
64280
|
+
command: handler.command,
|
|
64281
|
+
timeout: handler.timeout ?? 600,
|
|
64282
|
+
type: "command"
|
|
64283
|
+
};
|
|
64284
|
+
const body = {
|
|
64285
|
+
event_name: snakeEvent(event),
|
|
64286
|
+
hooks: [hook],
|
|
64287
|
+
...typeof matcher === "string" ? { matcher } : {}
|
|
64288
|
+
};
|
|
64289
|
+
return `sha256:${createHash13("sha256").update(JSON.stringify(body)).digest("hex")}`;
|
|
64290
|
+
}, codexHookTrust = (install, tables) => {
|
|
64291
|
+
const hooks = install.root ? readJson(path30.join(install.root, "hooks", "hooks.json"))?.hooks : null;
|
|
64292
|
+
if (!hooks || typeof hooks !== "object")
|
|
64293
|
+
return null;
|
|
64294
|
+
const result = [];
|
|
64295
|
+
for (const [event, groups] of Object.entries(hooks)) {
|
|
64296
|
+
if (!Array.isArray(groups))
|
|
64297
|
+
continue;
|
|
64298
|
+
groups.forEach((group, g) => {
|
|
64299
|
+
const handlers = Array.isArray(group?.hooks) ? group.hooks : [];
|
|
64300
|
+
handlers.forEach((handler, h) => {
|
|
64301
|
+
const key = `${install.id}:hooks/hooks.json:${snakeEvent(event)}:${g}:${h}`;
|
|
64302
|
+
const hash = codexHookHash(event, group?.matcher, handler ?? {});
|
|
64303
|
+
const state = tables.get(["hooks", "state", key].join("\x00"));
|
|
64304
|
+
const trusted = typeof state?.trusted_hash === "string" ? state.trusted_hash : null;
|
|
64305
|
+
result.push({
|
|
64306
|
+
key,
|
|
64307
|
+
hash,
|
|
64308
|
+
state: state?.enabled === false ? "disabled" : trusted === null ? "untrusted" : hash !== null && trusted !== hash ? "modified" : "trusted"
|
|
64309
|
+
});
|
|
64310
|
+
});
|
|
64311
|
+
});
|
|
64312
|
+
}
|
|
64313
|
+
return result;
|
|
64314
|
+
}, checkCodexHooks = (res) => {
|
|
64315
|
+
const installs = codexWorkitInstalls(res);
|
|
64316
|
+
if (installs.length === 0)
|
|
64317
|
+
return {
|
|
64318
|
+
id: "codex_hooks",
|
|
64319
|
+
status: "pass",
|
|
64320
|
+
detail: "no Workit Codex plugin enabled — skipping"
|
|
64321
|
+
};
|
|
64322
|
+
const tables = readTomlTables(installs[0].configFile);
|
|
64323
|
+
const problems = [];
|
|
64324
|
+
const toTrust = [];
|
|
64325
|
+
let reinstall;
|
|
64326
|
+
let total = 0;
|
|
64327
|
+
for (const install of installs) {
|
|
64328
|
+
const trust = codexHookTrust(install, tables);
|
|
64329
|
+
if (!trust) {
|
|
64330
|
+
problems.push(`${install.id}: no installed hooks/hooks.json`);
|
|
64331
|
+
reinstall ??= `codex plugin remove ${install.id} && codex plugin add ${install.id}`;
|
|
64332
|
+
continue;
|
|
64333
|
+
}
|
|
64334
|
+
total += trust.length;
|
|
64335
|
+
for (const state of ["untrusted", "modified", "disabled"]) {
|
|
64336
|
+
const hit = trust.filter((t) => t.state === state);
|
|
64337
|
+
if (hit.length > 0)
|
|
64338
|
+
problems.push(`${install.id}: ${hit.length} of ${trust.length} hooks ${state === "modified" ? "changed since trusted" : state} (${hit.map((t) => t.key.split(":")[2]).join(", ")})`);
|
|
64339
|
+
}
|
|
64340
|
+
toTrust.push(...trust.filter((t) => t.state !== "trusted"));
|
|
64341
|
+
}
|
|
64342
|
+
if (problems.length === 0)
|
|
64343
|
+
return {
|
|
64344
|
+
id: "codex_hooks",
|
|
64345
|
+
status: "pass",
|
|
64346
|
+
detail: `${total} Workit Codex plugin hooks trusted (${installs.map((i) => `${i.id} ${i.version ?? "?"}`).join(", ")})`
|
|
64347
|
+
};
|
|
64348
|
+
const disabled = toTrust.some((t) => t.state === "disabled");
|
|
64349
|
+
const fix = reinstall ?? `Start \`codex\`, run \`/hooks\` and ${disabled ? "enable and " : ""}trust the workit hooks (or pick "Trust all and continue" when Codex asks you to review hooks at startup)`;
|
|
64350
|
+
return {
|
|
64351
|
+
id: "codex_hooks",
|
|
64352
|
+
status: "warn",
|
|
64353
|
+
detail: `Codex skips untrusted plugin hooks — ${problems.join("; ")}`,
|
|
64354
|
+
fix
|
|
64355
|
+
};
|
|
64356
|
+
}, CODEX_AGENT_MARKER = "# Generated by Workit", AGENT_MARKER_VERSION, agentMarkerVersion = (text) => AGENT_MARKER_VERSION.exec(text)?.[1] ?? null, stampCodexAgent = (content, version) => version && content.startsWith(CODEX_AGENT_MARKER) && !agentMarkerVersion(content) ? `${CODEX_AGENT_MARKER} ${version}${content.slice(CODEX_AGENT_MARKER.length)}` : content, checkCodexAgents = (res) => {
|
|
64357
|
+
const installs = codexWorkitInstalls(res);
|
|
64358
|
+
if (installs.length === 0)
|
|
64359
|
+
return {
|
|
64360
|
+
id: "codex_agents",
|
|
64361
|
+
status: "pass",
|
|
64362
|
+
detail: "no Workit Codex plugin enabled — skipping"
|
|
64363
|
+
};
|
|
64364
|
+
const agentsDir = path30.join(codexHomeOf(res), "agents");
|
|
64365
|
+
const problems = [];
|
|
64366
|
+
const fixes = [];
|
|
64367
|
+
const installed = [];
|
|
64368
|
+
for (const install of installs) {
|
|
64369
|
+
const source = install.root ? path30.join(install.root, "agents") : null;
|
|
64370
|
+
const files = source && existsSync13(source) ? readdirSync7(source).filter((f) => f.endsWith(".toml")).toSorted() : [];
|
|
64371
|
+
if (files.length === 0) {
|
|
64372
|
+
problems.push(`${install.id} ${install.version ?? "?"} bundles no agents`);
|
|
64373
|
+
fixes.push(`codex plugin remove ${install.id} && codex plugin add ${install.id}`);
|
|
64374
|
+
continue;
|
|
64375
|
+
}
|
|
64376
|
+
const install_fix = `node ${JSON.stringify(path30.join(install.root, "dist", "launch-mcp.js"))} --install-agents, then start a new Codex session`;
|
|
64377
|
+
const pluginVersion = readJson(path30.join(install.root, "package.json"))?.version;
|
|
64378
|
+
const version = typeof pluginVersion === "string" ? pluginVersion.match(SEMVER)?.[0] ?? null : null;
|
|
64379
|
+
let stale = false;
|
|
64380
|
+
for (const file of files) {
|
|
64381
|
+
const dest = path30.join(agentsDir, file);
|
|
64382
|
+
const want = stampCodexAgent(readFileSync13(path30.join(source, file), "utf8"), version);
|
|
64383
|
+
let link = false;
|
|
64384
|
+
try {
|
|
64385
|
+
link = lstatSync4(dest).isSymbolicLink();
|
|
64386
|
+
} catch {
|
|
64387
|
+
link = false;
|
|
64388
|
+
}
|
|
64389
|
+
if (link) {
|
|
64390
|
+
problems.push(`${dest} is a symlink (the plugin never writes through one)`);
|
|
64391
|
+
fixes.push(`replace the symlink ${dest} with a file or remove it, then run ${install_fix}`);
|
|
64392
|
+
continue;
|
|
64393
|
+
}
|
|
64394
|
+
let have = null;
|
|
64395
|
+
try {
|
|
64396
|
+
have = readFileSync13(dest, "utf8");
|
|
64397
|
+
} catch {
|
|
64398
|
+
have = null;
|
|
64399
|
+
}
|
|
64400
|
+
const written = have === null ? null : agentMarkerVersion(have);
|
|
64401
|
+
const newer = written !== null && version !== null && versionBehind(version, written);
|
|
64402
|
+
if (have === want || newer) {
|
|
64403
|
+
installed.push(file.replace(/\.toml$/, ""));
|
|
64404
|
+
continue;
|
|
64405
|
+
}
|
|
64406
|
+
if (have !== null && !have.startsWith(CODEX_AGENT_MARKER)) {
|
|
64407
|
+
problems.push(`${dest} is a user agent that shadows ${install.id}'s ${file}`);
|
|
64408
|
+
fixes.push(`rename or remove ${dest}, then run ${install_fix}`);
|
|
64409
|
+
continue;
|
|
64410
|
+
}
|
|
64411
|
+
problems.push(`${dest} ${have === null ? "missing" : "outdated"}`);
|
|
64412
|
+
stale = true;
|
|
64413
|
+
}
|
|
64414
|
+
if (stale)
|
|
64415
|
+
fixes.push(install_fix);
|
|
64416
|
+
}
|
|
64417
|
+
if (problems.length === 0)
|
|
64418
|
+
return {
|
|
64419
|
+
id: "codex_agents",
|
|
64420
|
+
status: "pass",
|
|
64421
|
+
detail: `Codex agents ${installed.join(", ")} installed in ${agentsDir}`
|
|
64422
|
+
};
|
|
64423
|
+
return {
|
|
64424
|
+
id: "codex_agents",
|
|
64425
|
+
status: "warn",
|
|
64426
|
+
detail: problems.join("; "),
|
|
64427
|
+
fix: [...new Set(fixes)].join("; ")
|
|
64428
|
+
};
|
|
64429
|
+
}, PI_PACKAGE = "@brainervirus/workit-pi", PI_EXTENSION = "dist/workit.js", piAgentDirOf = (res) => {
|
|
64430
|
+
if (res.piAgentDir)
|
|
64431
|
+
return res.piAgentDir;
|
|
64432
|
+
const env = res.env.PI_CODING_AGENT_DIR;
|
|
64433
|
+
if (env)
|
|
64434
|
+
return env === "~" ? res.home : env.replace(/^~(?=\/|\\)/, res.home);
|
|
64435
|
+
return path30.join(res.home, ".pi", "agent");
|
|
64436
|
+
}, npmSpecName = (spec) => {
|
|
64437
|
+
const at = spec.indexOf("@", spec.startsWith("@") ? 1 : 0);
|
|
64438
|
+
return at > 0 ? { name: spec.slice(0, at), pinned: true } : { name: spec, pinned: false };
|
|
64439
|
+
}, findPiEntry = (res, agentDir) => {
|
|
64440
|
+
const scopes = [
|
|
64441
|
+
{
|
|
64442
|
+
scope: "project",
|
|
64443
|
+
settingsFile: path30.join(res.cwd, ".pi", "settings.json"),
|
|
64444
|
+
base: path30.join(res.cwd, ".pi")
|
|
64445
|
+
},
|
|
64446
|
+
{ scope: "user", settingsFile: path30.join(agentDir, "settings.json"), base: agentDir }
|
|
64447
|
+
];
|
|
64448
|
+
for (const { scope, settingsFile, base } of scopes) {
|
|
64449
|
+
const packages = readJson(settingsFile)?.packages;
|
|
64450
|
+
if (!Array.isArray(packages))
|
|
64451
|
+
continue;
|
|
64452
|
+
for (const entry of packages) {
|
|
64453
|
+
const source = typeof entry === "string" ? entry : typeof entry?.source === "string" ? entry.source : null;
|
|
64454
|
+
if (!source)
|
|
64455
|
+
continue;
|
|
64456
|
+
const filter = typeof entry === "object" ? entry : null;
|
|
64457
|
+
if (source.startsWith("npm:")) {
|
|
64458
|
+
const spec = npmSpecName(source.slice(4).trim());
|
|
64459
|
+
if (spec.name !== PI_PACKAGE)
|
|
64460
|
+
continue;
|
|
64461
|
+
const root = path30.join(base, "npm", "node_modules", ...PI_PACKAGE.split("/"));
|
|
64462
|
+
return { scope, settingsFile, source, filter, root, npm: { pinned: spec.pinned } };
|
|
64463
|
+
}
|
|
64464
|
+
if (/^(git:|https?:|ssh:|git@)/.test(source))
|
|
64465
|
+
continue;
|
|
64466
|
+
const local = path30.resolve(base, source.replace(/^~(?=\/|\\)/, res.home));
|
|
64467
|
+
if (readJson(path30.join(local, "package.json"))?.name === PI_PACKAGE)
|
|
64468
|
+
return { scope, settingsFile, source, filter, root: local, npm: null };
|
|
64469
|
+
}
|
|
64470
|
+
}
|
|
64471
|
+
return null;
|
|
64472
|
+
}, globToRegExp3 = (pattern) => new RegExp(`^${pattern.replace(/^\.\//, "").replace(/[.+^${}()|[\]\\]/g, "\\$&").split(/\*\*\/?/).map((part) => part.replace(/\*/g, "[^/]*").replace(/\?/g, "[^/]")).join(".*")}$`), piFilterKeeps = (patterns, file) => {
|
|
64473
|
+
if (!Array.isArray(patterns))
|
|
64474
|
+
return true;
|
|
64475
|
+
const list = patterns.filter((p) => typeof p === "string");
|
|
64476
|
+
if (list.length === 0)
|
|
64477
|
+
return false;
|
|
64478
|
+
const name = path30.posix.basename(file);
|
|
64479
|
+
const matches = (p) => {
|
|
64480
|
+
const re = globToRegExp3(p);
|
|
64481
|
+
return re.test(file) || re.test(name);
|
|
64482
|
+
};
|
|
64483
|
+
const exact = (p) => p.replace(/^\.\//, "") === file;
|
|
64484
|
+
const includes = list.filter((p) => !/^[!+-]/.test(p));
|
|
64485
|
+
let kept = includes.length === 0 || includes.some(matches);
|
|
64486
|
+
if (list.some((p) => p.startsWith("!") && matches(p.slice(1))))
|
|
64487
|
+
kept = false;
|
|
64488
|
+
if (list.some((p) => p.startsWith("+") && exact(p.slice(1))))
|
|
64489
|
+
kept = true;
|
|
64490
|
+
if (list.some((p) => p.startsWith("-") && exact(p.slice(1))))
|
|
64491
|
+
kept = false;
|
|
64492
|
+
return kept;
|
|
64493
|
+
}, checkPiExtension = (res) => {
|
|
64494
|
+
const agentDir = piAgentDirOf(res);
|
|
64495
|
+
if (!existsSync13(agentDir) && !commandOnPath("pi", res.env))
|
|
64496
|
+
return { id: "pi_extension", status: "pass", detail: "Pi not installed — skipping" };
|
|
64497
|
+
const installCmd = (scope, spec = `npm:${PI_PACKAGE}`) => `pi install ${spec}${scope === "project" ? " -l" : ""}`;
|
|
64498
|
+
const entry = findPiEntry(res, agentDir);
|
|
64499
|
+
if (!entry)
|
|
64500
|
+
return {
|
|
64501
|
+
id: "pi_extension",
|
|
64502
|
+
status: "warn",
|
|
64503
|
+
detail: `Workit Pi extension missing: no ${PI_PACKAGE} entry in ${path30.join(agentDir, "settings.json")} or ${path30.join(res.cwd, ".pi", "settings.json")}`,
|
|
64504
|
+
fix: installCmd("user")
|
|
64505
|
+
};
|
|
64506
|
+
const where = `${entry.source} (${entry.scope}, ${entry.settingsFile})`;
|
|
64507
|
+
const rebuild = `cd ${JSON.stringify(entry.root)} && bun run build`;
|
|
64508
|
+
const pkg = readJson(path30.join(entry.root, "package.json"));
|
|
64509
|
+
if (!pkg)
|
|
64510
|
+
return {
|
|
64511
|
+
id: "pi_extension",
|
|
64512
|
+
status: "warn",
|
|
64513
|
+
detail: `Workit Pi extension missing: ${where} is listed but not installed at ${entry.root}`,
|
|
64514
|
+
fix: entry.npm ? installCmd(entry.scope, entry.source) : `restore ${entry.root}, then ${rebuild}`
|
|
64515
|
+
};
|
|
64516
|
+
const declared = Array.isArray(pkg.pi?.extensions) ? pkg.pi.extensions.some((e) => typeof e === "string" && e.replace(/^\.\//, "") === PI_EXTENSION) : false;
|
|
64517
|
+
if (!declared || !existsSync13(path30.join(entry.root, PI_EXTENSION)))
|
|
64518
|
+
return {
|
|
64519
|
+
id: "pi_extension",
|
|
64520
|
+
status: "warn",
|
|
64521
|
+
detail: `Workit Pi extension not loading: ${entry.root} ${declared ? `has no ${PI_EXTENSION}` : `does not declare ${PI_EXTENSION} in its pi manifest`}`,
|
|
64522
|
+
fix: entry.npm ? `pi remove ${entry.source}${entry.scope === "project" ? " -l" : ""} && ${installCmd(entry.scope, entry.source)}` : rebuild
|
|
64523
|
+
};
|
|
64524
|
+
const filter = entry.filter?.extensions;
|
|
64525
|
+
if (!piFilterKeeps(filter, PI_EXTENSION))
|
|
64526
|
+
return {
|
|
64527
|
+
id: "pi_extension",
|
|
64528
|
+
status: "warn",
|
|
64529
|
+
detail: `Workit Pi extension not loading: the ${PI_PACKAGE} entry in ${entry.settingsFile} filters out ${PI_EXTENSION} (extensions: ${JSON.stringify(filter)})`,
|
|
64530
|
+
fix: `remove the "extensions" filter from the ${PI_PACKAGE} entry in ${entry.settingsFile}, or enable the extension with \`pi config${entry.scope === "project" ? " -l" : ""}\``
|
|
64531
|
+
};
|
|
64532
|
+
const version = typeof pkg.version === "string" ? pkg.version : null;
|
|
64533
|
+
const cli = res.cliVersion;
|
|
64534
|
+
if (version && cli && versionBehind(version, cli)) {
|
|
64535
|
+
if (!entry.npm)
|
|
64536
|
+
return {
|
|
64537
|
+
id: "pi_extension",
|
|
64538
|
+
status: "pass",
|
|
64539
|
+
detail: `Workit Pi extension ${version} from local checkout ${where} loads ${PI_EXTENSION} (older than workit ${cli}; a checkout is not compared with the registry)`
|
|
64540
|
+
};
|
|
64541
|
+
const latest = registryLatestVersion(res, PI_PACKAGE);
|
|
64542
|
+
const registryProbed = latest !== null && !res.env.WORKIT_DOCTOR_STALE_REGISTRY_VERSION;
|
|
64543
|
+
if (!latest)
|
|
64544
|
+
return {
|
|
64545
|
+
id: "pi_extension",
|
|
64546
|
+
status: "pass",
|
|
64547
|
+
detail: `Workit Pi extension ${version} from ${where} loads ${PI_EXTENSION}; registry unreachable, so whether a version newer than ${version} is published is unknown (workit ${cli})`
|
|
64548
|
+
};
|
|
64549
|
+
if (versionBehind(version, latest))
|
|
64550
|
+
return {
|
|
64551
|
+
id: "pi_extension",
|
|
64552
|
+
status: "warn",
|
|
64553
|
+
detail: `Workit Pi extension stale: ${PI_PACKAGE} ${version} from ${where} is older than workit ${cli} (published ${latest})`,
|
|
64554
|
+
fix: entry.npm.pinned ? installCmd(entry.scope, `npm:${PI_PACKAGE}@${latest}`) : `pi update npm:${PI_PACKAGE}`,
|
|
64555
|
+
registryProbed
|
|
64556
|
+
};
|
|
64557
|
+
return {
|
|
64558
|
+
id: "pi_extension",
|
|
64559
|
+
status: "pass",
|
|
64560
|
+
detail: `Workit Pi extension ${version} from ${where} is the newest published (workit ${cli})`,
|
|
64561
|
+
registryProbed
|
|
64562
|
+
};
|
|
64563
|
+
}
|
|
64564
|
+
return {
|
|
64565
|
+
id: "pi_extension",
|
|
64566
|
+
status: "pass",
|
|
64567
|
+
detail: `Workit Pi extension ${version ?? "?"} from ${where} loads ${PI_EXTENSION}`
|
|
64568
|
+
};
|
|
64202
64569
|
}, assetPathsFor = (host, dev) => {
|
|
64203
64570
|
const pkg = path30.join(dev, "packages", `workit-${host}`);
|
|
64204
64571
|
switch (host) {
|
|
@@ -65306,6 +65673,7 @@ var init_doctor = __esm(() => {
|
|
|
65306
65673
|
init_host_install();
|
|
65307
65674
|
init_skill_manifests();
|
|
65308
65675
|
OPENCODE_V1_FIX = `OpenCode < ${SUPPORT_MATRIX.opencode.minimum} cannot load Workit 3 (V2 plugin API only): upgrade OpenCode to ${SUPPORT_MATRIX.opencode.minimum}+, or stay on Workit 2.x by pinning "@brainervirus/workit-opencode@2" in the opencode.json "plugin" array`;
|
|
65676
|
+
AGENT_MARKER_VERSION = /^# Generated by Workit (\d+\.\d+\.\d+)\b/;
|
|
65309
65677
|
canonicalCursorHook = cursorHooksEntry().command;
|
|
65310
65678
|
SILENT_HOOK_GRACE_MS = 2 * 60000;
|
|
65311
65679
|
SEMVER = /\d+\.\d+\.\d+/;
|
|
@@ -65314,6 +65682,9 @@ var init_doctor = __esm(() => {
|
|
|
65314
65682
|
checkRuntime,
|
|
65315
65683
|
checkVersions,
|
|
65316
65684
|
checkCodexPin,
|
|
65685
|
+
checkCodexHooks,
|
|
65686
|
+
checkCodexAgents,
|
|
65687
|
+
checkPiExtension,
|
|
65317
65688
|
checkOpencodeVersion,
|
|
65318
65689
|
checkClaudePlugin,
|
|
65319
65690
|
checkWorkitOnPath,
|
|
@@ -65353,7 +65724,7 @@ import {
|
|
|
65353
65724
|
mkdirSync as mkdirSync6,
|
|
65354
65725
|
mkdtempSync as mkdtempSync2,
|
|
65355
65726
|
readFileSync as readFileSync14,
|
|
65356
|
-
readdirSync as
|
|
65727
|
+
readdirSync as readdirSync8,
|
|
65357
65728
|
realpathSync as realpathSync7,
|
|
65358
65729
|
renameSync as renameSync3,
|
|
65359
65730
|
rmSync as rmSync3,
|
|
@@ -66239,7 +66610,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
66239
66610
|
}
|
|
66240
66611
|
}, samePluginContent = (src, dest, pluginDir, relative = "") => {
|
|
66241
66612
|
try {
|
|
66242
|
-
for (const entry of
|
|
66613
|
+
for (const entry of readdirSync8(src, { withFileTypes: true })) {
|
|
66243
66614
|
if (entry.name === "node_modules")
|
|
66244
66615
|
continue;
|
|
66245
66616
|
const source = path31.join(src, entry.name);
|
|
@@ -66254,7 +66625,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
66254
66625
|
return false;
|
|
66255
66626
|
}
|
|
66256
66627
|
}
|
|
66257
|
-
for (const entry of
|
|
66628
|
+
for (const entry of readdirSync8(dest, { withFileTypes: true })) {
|
|
66258
66629
|
if (existsSync14(path31.join(src, entry.name)))
|
|
66259
66630
|
continue;
|
|
66260
66631
|
const rel = path31.join(relative, entry.name);
|
|
@@ -66273,7 +66644,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
66273
66644
|
const sourceRules = path31.join(src, "rules");
|
|
66274
66645
|
const installedRules = path31.join(dest, "rules");
|
|
66275
66646
|
try {
|
|
66276
|
-
for (const entry of
|
|
66647
|
+
for (const entry of readdirSync8(installedRules, { withFileTypes: true })) {
|
|
66277
66648
|
if (!entry.isFile() || !entry.name.endsWith(".mdc"))
|
|
66278
66649
|
continue;
|
|
66279
66650
|
if (existsSync14(path31.join(sourceRules, entry.name)))
|
|
@@ -66290,7 +66661,7 @@ var TOKEN_PLACEHOLDER3 = "YOUR_TOKEN_HERE", YT_OVERRIDES, VCS_OVERRIDES, SETUP_O
|
|
|
66290
66661
|
if (existsSync14(legacyRules)) {
|
|
66291
66662
|
try {
|
|
66292
66663
|
mkdirSync6(path31.join(res.cursorPluginDir, "rules"), { recursive: true });
|
|
66293
|
-
for (const entry of
|
|
66664
|
+
for (const entry of readdirSync8(legacyRules, { withFileTypes: true })) {
|
|
66294
66665
|
if (!entry.isFile() || !entry.name.endsWith(".mdc"))
|
|
66295
66666
|
continue;
|
|
66296
66667
|
const target = path31.join(res.cursorPluginDir, "rules", entry.name);
|
|
@@ -70082,7 +70453,7 @@ import {
|
|
|
70082
70453
|
} from "node:fs";
|
|
70083
70454
|
import path35 from "node:path";
|
|
70084
70455
|
import os12 from "node:os";
|
|
70085
|
-
import { createHash as
|
|
70456
|
+
import { createHash as createHash14 } from "node:crypto";
|
|
70086
70457
|
function previewUpgrade(hosts = HOSTS2, deps = {}) {
|
|
70087
70458
|
const paths = upgradePaths(deps);
|
|
70088
70459
|
const { home, configDir } = paths;
|
|
@@ -70210,7 +70581,7 @@ function applyUpgrade(plan, deps = {}) {
|
|
|
70210
70581
|
mkdirSync8(backup, { recursive: true, mode: 448 });
|
|
70211
70582
|
for (const file of Object.keys(plan.configDigests))
|
|
70212
70583
|
if (existsSync17(file)) {
|
|
70213
|
-
const target = path35.join(backup,
|
|
70584
|
+
const target = path35.join(backup, createHash14("sha256").update(file).digest("hex").slice(0, 12) + "-" + path35.basename(file));
|
|
70214
70585
|
copyFileSync5(file, target);
|
|
70215
70586
|
chmodSync(target, 384);
|
|
70216
70587
|
}
|
|
@@ -70465,7 +70836,7 @@ var HOSTS2, upgradePaths = (deps) => {
|
|
|
70465
70836
|
if (!data || typeof data !== "object" || Array.isArray(data))
|
|
70466
70837
|
throw new Error(`${file} is not a configuration object`);
|
|
70467
70838
|
return data;
|
|
70468
|
-
}, digest2 = (file) => existsSync17(file) ?
|
|
70839
|
+
}, digest2 = (file) => existsSync17(file) ? createHash14("sha256").update(readFileSync17(file)).digest("hex") : "missing", runner = (deps) => deps.run ?? ((command, args) => {
|
|
70469
70840
|
const { home, env } = upgradePaths(deps);
|
|
70470
70841
|
let step;
|
|
70471
70842
|
try {
|
|
@@ -70951,7 +71322,12 @@ async function run5(argv, io) {
|
|
|
70951
71322
|
}
|
|
70952
71323
|
fixLock = clearStaleMetadataLock(root, { force });
|
|
70953
71324
|
}
|
|
70954
|
-
const report = runDoctor({
|
|
71325
|
+
const report = runDoctor({
|
|
71326
|
+
host: "cli",
|
|
71327
|
+
cwd: io.cwd,
|
|
71328
|
+
workspaceRoot: root,
|
|
71329
|
+
cliVersion: package_default.version
|
|
71330
|
+
});
|
|
70955
71331
|
let knowledge = null;
|
|
70956
71332
|
try {
|
|
70957
71333
|
knowledge = lintKnowledge(root);
|
|
@@ -70984,7 +71360,9 @@ async function run5(argv, io) {
|
|
|
70984
71360
|
io.stdout(`${mark} ${check.id} — ${check.detail}
|
|
70985
71361
|
`);
|
|
70986
71362
|
if (check.fix)
|
|
70987
|
-
io.stdout(` fix: ${check.fix
|
|
71363
|
+
io.stdout(` fix: ${check.fix.replaceAll(`
|
|
71364
|
+
`, `
|
|
71365
|
+
`)}
|
|
70988
71366
|
`);
|
|
70989
71367
|
}
|
|
70990
71368
|
io.stdout(`${knowledgeLine}
|
|
@@ -70994,6 +71372,7 @@ async function run5(argv, io) {
|
|
|
70994
71372
|
return report.exitCode;
|
|
70995
71373
|
}
|
|
70996
71374
|
var init_doctor2 = __esm(() => {
|
|
71375
|
+
init_package();
|
|
70997
71376
|
init_doctor();
|
|
70998
71377
|
init_knowledge();
|
|
70999
71378
|
init_store_lock();
|
|
@@ -71107,7 +71486,7 @@ var init_redact = __esm(() => {
|
|
|
71107
71486
|
|
|
71108
71487
|
// packages/workit-core/src/checks.ts
|
|
71109
71488
|
import { spawn, spawnSync as spawnSync16 } from "node:child_process";
|
|
71110
|
-
import { createHash as
|
|
71489
|
+
import { createHash as createHash15 } from "node:crypto";
|
|
71111
71490
|
import fs18 from "node:fs";
|
|
71112
71491
|
import os13 from "node:os";
|
|
71113
71492
|
import path37 from "node:path";
|
|
@@ -71441,7 +71820,7 @@ function pruneCheckLogs(storeRoot, options = {}) {
|
|
|
71441
71820
|
}
|
|
71442
71821
|
return report;
|
|
71443
71822
|
}
|
|
71444
|
-
var MAX_LOG_BYTES, TAIL_LINES = 80, MAX_LINE_CHARS = 400, redactLog = (text) => redactText(text), PEM_BEGIN, PEM_END, BATCH_BYTES, MAX_PENDING, PEM_BODY, MAX_KEY_LINES = 4096, MAX_KEY_BYTES, CMD_META, DRAIN_MS = 250, FORWARDED_SIGNALS, logDigest = (log) => `sha256:${
|
|
71823
|
+
var MAX_LOG_BYTES, TAIL_LINES = 80, MAX_LINE_CHARS = 400, redactLog = (text) => redactText(text), PEM_BEGIN, PEM_END, BATCH_BYTES, MAX_PENDING, PEM_BODY, MAX_KEY_LINES = 4096, MAX_KEY_BYTES, CMD_META, DRAIN_MS = 250, FORWARDED_SIGNALS, logDigest = (log) => `sha256:${createHash15("sha256").update(log).digest("hex")}`, LOG_RETENTION;
|
|
71445
71824
|
var init_checks3 = __esm(() => {
|
|
71446
71825
|
init_redact();
|
|
71447
71826
|
MAX_LOG_BYTES = 2 * 1024 * 1024;
|
|
@@ -73426,7 +73805,7 @@ var init_pr_ops = __esm(() => {
|
|
|
73426
73805
|
|
|
73427
73806
|
// packages/workit-core/src/stack.ts
|
|
73428
73807
|
import { spawnSync as spawnSync19 } from "node:child_process";
|
|
73429
|
-
import { createHash as
|
|
73808
|
+
import { createHash as createHash16, randomBytes as randomBytes3 } from "node:crypto";
|
|
73430
73809
|
import fs20 from "node:fs";
|
|
73431
73810
|
import path41 from "node:path";
|
|
73432
73811
|
function worktreeOf(cwd, branch) {
|
|
@@ -74593,7 +74972,7 @@ var STACK_VERSION = 1, stackFail = (code, error, unblock, data) => ({
|
|
|
74593
74972
|
return dir.ok ? path41.join(dir.data, "worktrees") : null;
|
|
74594
74973
|
}, firstLine = (run) => (`${run.stderr}
|
|
74595
74974
|
${run.stdout}`.split(`
|
|
74596
|
-
`).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${
|
|
74975
|
+
`).find((line) => line.trim()) ?? "git failed").trim().slice(0, 300), stackFileName = (name) => `${name.replace(/[^A-Za-z0-9._-]+/gu, "_").replace(/^[._]+/u, "").slice(0, 48).toLowerCase()}-${createHash16("sha256").update(name).digest("hex").slice(0, 12)}.json`, fileFor, validName = (name) => {
|
|
74597
74976
|
if (name.length === 0 || name.length > 200)
|
|
74598
74977
|
return false;
|
|
74599
74978
|
for (let index = 0;index < name.length; index += 1) {
|
|
@@ -77522,7 +77901,7 @@ var init_stack2 = __esm(() => {
|
|
|
77522
77901
|
|
|
77523
77902
|
// packages/workit-core/src/fanout.ts
|
|
77524
77903
|
import { spawnSync as spawnSync23 } from "node:child_process";
|
|
77525
|
-
import { createHash as
|
|
77904
|
+
import { createHash as createHash17 } from "node:crypto";
|
|
77526
77905
|
import fs23 from "node:fs";
|
|
77527
77906
|
import path45 from "node:path";
|
|
77528
77907
|
function expandBraces(pattern, limit = 64) {
|
|
@@ -78003,7 +78382,7 @@ function sliceHash(slice) {
|
|
|
78003
78382
|
context: _context,
|
|
78004
78383
|
...definition
|
|
78005
78384
|
} = slice;
|
|
78006
|
-
return
|
|
78385
|
+
return createHash17("sha256").update(JSON.stringify(definition)).digest("hex").slice(0, 16);
|
|
78007
78386
|
}
|
|
78008
78387
|
function fanoutsDir(cwd) {
|
|
78009
78388
|
const root = storeRoot(cwd);
|
|
@@ -78708,13 +79087,13 @@ var init_fanout_worktree = __esm(() => {
|
|
|
78708
79087
|
});
|
|
78709
79088
|
|
|
78710
79089
|
// packages/workit-core/src/fanout-brief.ts
|
|
78711
|
-
import { createHash as
|
|
79090
|
+
import { createHash as createHash18 } from "node:crypto";
|
|
78712
79091
|
function workerSession(lead, slice, attempt) {
|
|
78713
79092
|
const tail = attempt === null ? "" : `+try${attempt}`;
|
|
78714
79093
|
const full = `${lead}-w-${slice}${tail}`;
|
|
78715
79094
|
if (full.length <= SESSION_MAX)
|
|
78716
79095
|
return full;
|
|
78717
|
-
const digest =
|
|
79096
|
+
const digest = createHash18("sha256").update(slice).digest("hex").slice(0, 12);
|
|
78718
79097
|
return `${lead.slice(0, SESSION_MAX - 3 - digest.length - tail.length)}-w-${digest}${tail}`;
|
|
78719
79098
|
}
|
|
78720
79099
|
function renderBrief(cwd, plan, input) {
|
|
@@ -95900,7 +96279,7 @@ var init_rules = __esm(() => {
|
|
|
95900
96279
|
|
|
95901
96280
|
// packages/workit-cli/src/test-audit/audit.ts
|
|
95902
96281
|
import { spawnSync as spawnSync25 } from "node:child_process";
|
|
95903
|
-
import { existsSync as existsSync19, readdirSync as
|
|
96282
|
+
import { existsSync as existsSync19, readdirSync as readdirSync9, readFileSync as readFileSync22, statSync as statSync7 } from "node:fs";
|
|
95904
96283
|
import path49 from "node:path";
|
|
95905
96284
|
function git7(cwd, args) {
|
|
95906
96285
|
const run = spawnSync25("git", ["-c", "core.quotepath=off", ...args], {
|
|
@@ -95911,7 +96290,7 @@ function git7(cwd, args) {
|
|
|
95911
96290
|
return { ok: run.status === 0, stdout: run.stdout ?? "" };
|
|
95912
96291
|
}
|
|
95913
96292
|
function walkTests(dir, out) {
|
|
95914
|
-
for (const entry of
|
|
96293
|
+
for (const entry of readdirSync9(dir, { withFileTypes: true })) {
|
|
95915
96294
|
if (entry.name.startsWith(".") && entry.name !== "." && entry.isDirectory())
|
|
95916
96295
|
continue;
|
|
95917
96296
|
const full = path49.join(dir, entry.name);
|
|
@@ -96047,7 +96426,7 @@ import {
|
|
|
96047
96426
|
lstatSync as lstatSync7,
|
|
96048
96427
|
mkdirSync as mkdirSync9,
|
|
96049
96428
|
mkdtempSync as mkdtempSync4,
|
|
96050
|
-
readdirSync as
|
|
96429
|
+
readdirSync as readdirSync10,
|
|
96051
96430
|
readFileSync as readFileSync23,
|
|
96052
96431
|
readlinkSync as readlinkSync3,
|
|
96053
96432
|
realpathSync as realpathSync9,
|
|
@@ -96144,7 +96523,7 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
|
|
|
96144
96523
|
let removed = 0;
|
|
96145
96524
|
let names = [];
|
|
96146
96525
|
try {
|
|
96147
|
-
names =
|
|
96526
|
+
names = readdirSync10(tmp);
|
|
96148
96527
|
} catch {
|
|
96149
96528
|
return 0;
|
|
96150
96529
|
}
|
|
@@ -96173,7 +96552,7 @@ function removeStaleCopies(tmp = os14.tmpdir()) {
|
|
|
96173
96552
|
}
|
|
96174
96553
|
function linkNodeModules(origin, target, repo, copy) {
|
|
96175
96554
|
mkdirSync9(target, { recursive: true });
|
|
96176
|
-
for (const entry of
|
|
96555
|
+
for (const entry of readdirSync10(origin, { withFileTypes: true })) {
|
|
96177
96556
|
const from = path50.join(origin, entry.name);
|
|
96178
96557
|
const to = path50.join(target, entry.name);
|
|
96179
96558
|
if (entry.name.startsWith("@") && entry.isDirectory()) {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cli",
|
|
3
|
-
"version": "8.
|
|
3
|
+
"version": "8.7.0",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit CLI — setup wizard, doctor, and task control for agentic coding workflows",
|
|
6
6
|
"keywords": [
|
|
@@ -38,9 +38,9 @@
|
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
40
|
"@babel/parser": "7.29.9",
|
|
41
|
-
"@brainervirus/workit-core": "^8.
|
|
42
|
-
"@brainervirus/workit-cursor": "^8.
|
|
43
|
-
"@brainervirus/workit-opencode": "^8.
|
|
41
|
+
"@brainervirus/workit-core": "^8.7.0",
|
|
42
|
+
"@brainervirus/workit-cursor": "^8.7.0",
|
|
43
|
+
"@brainervirus/workit-opencode": "^8.7.0",
|
|
44
44
|
"@inkjs/ui": "2.0.0",
|
|
45
45
|
"ink": "7.1.1",
|
|
46
46
|
"react": "19.2.8"
|