@brainervirus/workit-cursor 0.8.7 → 0.8.8
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/README.md +8 -0
- package/dist/mcp-server.js +262 -110
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -100,6 +100,14 @@ The repository root carries `.cursor-plugin/marketplace.json`, indexing `package
|
|
|
100
100
|
- **Update review** — Git plugin metadata (manifest, rules, skills, assets) is reviewed by Cursor on Marketplace updates, while the npm runtime runs from `@latest` with `--prefer-online`. The selector is shared across `mcp.json`, `hooks-cursor.json`, and `run-cursor-mcp.sh`, and a stale `latest` resolution is prevented by the mandatory `--prefer-online` flag.
|
|
101
101
|
- **Troubleshooting** — `workit doctor` (or the `workflow_doctor` tool) reports installation health including runtime, token, VCS/YouTrack, and log-writability checks; it exits nonzero on failure. An MCP/hook startup failure with no network is an `npx`/registry reachability issue, not a Workit defect.
|
|
102
102
|
|
|
103
|
+
## Auto-load repair
|
|
104
|
+
|
|
105
|
+
Workit self-heals stale Cursor plugin installs so the workflow features never silently drop out of auto-load (CA-01/CA-04):
|
|
106
|
+
|
|
107
|
+
- **Detection** — the doctor's `stale_install` finding reads the installed `~/.cursor/plugins/local/workit` directory and fails on a legacy `--package=` pin in the plugin's own `mcp.json`, a sessionStart hook running a legacy selector, or a local-dist install behind the current/published runtime; each finding carries the exact repair step.
|
|
108
|
+
- **Self-heal** — `install-cursor-plugin.sh` runs `doctor-check.ts cursor --stale` as a pre-check: exit 2 (stale) triggers a refresh of the plugin directory plus a rewrite of the workit MCP/hook entries to the canonical `@latest` + `--prefer-online` selector, preserving unrelated MCP servers; a healthy install is byte-untouched.
|
|
109
|
+
- **Fail-open** — the one network probe (the npm registry version comparison for local-dist installs) never blocks an install: an unreachable registry warns as `registry_unreachable`, never `stale_install` and never a failure. Canonical `@latest` installs skip the probe entirely — the selector resolves fresh at launch, so the installed `package.json` version is metadata, not a freshness signal.
|
|
110
|
+
|
|
103
111
|
## Docs
|
|
104
112
|
|
|
105
113
|
Full usage: https://github.com/BrainerVirus/workit#readme
|
package/dist/mcp-server.js
CHANGED
|
@@ -21835,6 +21835,25 @@ var init_support_matrix = __esm(() => {
|
|
|
21835
21835
|
};
|
|
21836
21836
|
});
|
|
21837
21837
|
|
|
21838
|
+
// packages/workit-core/src/core/package-root.ts
|
|
21839
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
21840
|
+
import path8 from "node:path";
|
|
21841
|
+
import { fileURLToPath } from "node:url";
|
|
21842
|
+
var packageRoot = () => {
|
|
21843
|
+
let dir = path8.dirname(fileURLToPath(import.meta.url));
|
|
21844
|
+
while (true) {
|
|
21845
|
+
const parent = path8.dirname(dir);
|
|
21846
|
+
if (existsSync4(path8.join(dir, "package.json")) || parent === dir)
|
|
21847
|
+
return dir;
|
|
21848
|
+
dir = parent;
|
|
21849
|
+
}
|
|
21850
|
+
}, assetRoot = () => {
|
|
21851
|
+
const root = packageRoot();
|
|
21852
|
+
const assets = path8.join(root, "assets");
|
|
21853
|
+
return existsSync4(assets) ? assets : root;
|
|
21854
|
+
};
|
|
21855
|
+
var init_package_root = () => {};
|
|
21856
|
+
|
|
21838
21857
|
// packages/workit-core/src/core/registration.ts
|
|
21839
21858
|
function isWorkitPlugin(value) {
|
|
21840
21859
|
const s = String(value).replaceAll("\\", "/");
|
|
@@ -21864,18 +21883,18 @@ var named = (s, name) => s === name || s.startsWith(`${name}@`), CURSOR_RUNTIME_
|
|
|
21864
21883
|
var init_registration = () => {};
|
|
21865
21884
|
|
|
21866
21885
|
// packages/workit-core/src/core/skill-manifests.ts
|
|
21867
|
-
import { existsSync as
|
|
21868
|
-
import
|
|
21869
|
-
var CANONICAL_SKILLS, skillManifestNames = (root) =>
|
|
21886
|
+
import { existsSync as existsSync5, readFileSync as readFileSync6, readdirSync as readdirSync4, statSync as statSync2 } from "node:fs";
|
|
21887
|
+
import path9 from "node:path";
|
|
21888
|
+
var CANONICAL_SKILLS, skillManifestNames = (root) => existsSync5(root) ? readdirSync4(root).filter((name) => existsSync5(path9.join(root, name, "SKILL.md"))).sort() : [], validateSkillManifests = (root, expected, label) => {
|
|
21870
21889
|
const actual = skillManifestNames(root);
|
|
21871
21890
|
const missing = expected.filter((name) => !actual.includes(name));
|
|
21872
21891
|
const extra = actual.filter((name) => !expected.includes(name));
|
|
21873
21892
|
return missing.length === 0 && extra.length === 0 ? null : `${label} mismatch at ${root} (missing: ${missing.join(", ") || "none"}; extra: ${extra.join(", ") || "none"})`;
|
|
21874
21893
|
}, validateCursorSkills = (pluginDir) => {
|
|
21875
|
-
const workit = validateSkillManifests(
|
|
21894
|
+
const workit = validateSkillManifests(path9.join(pluginDir, "skills"), CANONICAL_SKILLS.workit, "Cursor Workit skills");
|
|
21876
21895
|
if (workit)
|
|
21877
21896
|
return workit;
|
|
21878
|
-
const vendor =
|
|
21897
|
+
const vendor = path9.join(pluginDir, "vendor/superpowers/skills");
|
|
21879
21898
|
const superpowers = validateSkillManifests(vendor, CANONICAL_SKILLS.superpowers, "Cursor Superpowers skills");
|
|
21880
21899
|
if (superpowers)
|
|
21881
21900
|
return superpowers;
|
|
@@ -21883,7 +21902,7 @@ var CANONICAL_SKILLS, skillManifestNames = (root) => existsSync4(root) ? readdir
|
|
|
21883
21902
|
while (pending.length > 0) {
|
|
21884
21903
|
const dir = pending.pop();
|
|
21885
21904
|
for (const entry of readdirSync4(dir, { withFileTypes: true })) {
|
|
21886
|
-
const file =
|
|
21905
|
+
const file = path9.join(dir, entry.name);
|
|
21887
21906
|
if (entry.isDirectory())
|
|
21888
21907
|
pending.push(file);
|
|
21889
21908
|
else if ((statSync2(file).mode & 73) !== 0 || readFileSync6(file).subarray(0, 2).toString("latin1") === "#!") {
|
|
@@ -21931,15 +21950,15 @@ var init_skill_manifests = __esm(() => {
|
|
|
21931
21950
|
|
|
21932
21951
|
// packages/workit-core/src/core/doctor.ts
|
|
21933
21952
|
import { spawnSync as spawnSync4 } from "node:child_process";
|
|
21934
|
-
import { existsSync as
|
|
21953
|
+
import { existsSync as existsSync6, mkdirSync as mkdirSync4, readFileSync as readFileSync7, statSync as statSync3, unlinkSync as unlinkSync2, writeFileSync as writeFileSync3 } from "node:fs";
|
|
21935
21954
|
import os3 from "node:os";
|
|
21936
|
-
import
|
|
21955
|
+
import path10 from "node:path";
|
|
21937
21956
|
var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
21938
|
-
let dir =
|
|
21957
|
+
let dir = path10.resolve(cwd);
|
|
21939
21958
|
while (true) {
|
|
21940
|
-
if (
|
|
21959
|
+
if (existsSync6(path10.join(dir, "packages", "workit-core", "package.json")))
|
|
21941
21960
|
return dir;
|
|
21942
|
-
const parent =
|
|
21961
|
+
const parent = path10.dirname(dir);
|
|
21943
21962
|
if (parent === dir)
|
|
21944
21963
|
return null;
|
|
21945
21964
|
dir = parent;
|
|
@@ -21947,8 +21966,8 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
21947
21966
|
}, resolve = (options) => {
|
|
21948
21967
|
const env = options.env ?? process.env;
|
|
21949
21968
|
const home = options.home ?? env.HOME ?? os3.homedir();
|
|
21950
|
-
const configDir2 = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ??
|
|
21951
|
-
const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ??
|
|
21969
|
+
const configDir2 = options.configDir ?? env.WORKFLOW_TOOLKIT_CONFIG ?? env.WORKFLOW_TOOLKIT_CONFIG_DIR ?? path10.join(home, ".config", "workit");
|
|
21970
|
+
const stateDir = options.stateDir ?? env.WORKFLOW_TOOLKIT_STATE ?? path10.join(home, ".local", "state", "workit");
|
|
21952
21971
|
const cwd = options.cwd ?? process.cwd();
|
|
21953
21972
|
const dev = options.dev ?? env.WORKFLOW_TOOLKIT_DEV ?? findDevFromCwd(cwd);
|
|
21954
21973
|
return {
|
|
@@ -21958,10 +21977,10 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
21958
21977
|
stateDir,
|
|
21959
21978
|
cwd,
|
|
21960
21979
|
dev,
|
|
21961
|
-
opencodeConfig: options.opencodeConfig ??
|
|
21962
|
-
cursorSettings: options.cursorSettings ??
|
|
21963
|
-
cursorMcp: options.cursorMcp ??
|
|
21964
|
-
cursorPluginDir: options.cursorPluginDir ??
|
|
21980
|
+
opencodeConfig: options.opencodeConfig ?? path10.join(home, ".config", "opencode", "opencode.json"),
|
|
21981
|
+
cursorSettings: options.cursorSettings ?? path10.join(home, ".cursor", "settings.json"),
|
|
21982
|
+
cursorMcp: options.cursorMcp ?? path10.join(home, ".cursor", "mcp.json"),
|
|
21983
|
+
cursorPluginDir: options.cursorPluginDir ?? path10.join(home, ".cursor", "plugins", "local", "workit"),
|
|
21965
21984
|
env,
|
|
21966
21985
|
installer: options.installer ?? false
|
|
21967
21986
|
};
|
|
@@ -21983,13 +22002,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
21983
22002
|
const list = Array.isArray(plugin) ? plugin : typeof plugin === "string" ? [plugin] : [];
|
|
21984
22003
|
return list.map(String).filter(isWorkitPlugin);
|
|
21985
22004
|
}, commandOnPath2 = (name, env) => {
|
|
21986
|
-
const dirs = (env.PATH ?? process.env.PATH ?? "").split(
|
|
22005
|
+
const dirs = (env.PATH ?? process.env.PATH ?? "").split(path10.delimiter);
|
|
21987
22006
|
const names = process.platform === "win32" ? [name, `${name}.exe`] : [name];
|
|
21988
22007
|
for (const dir of dirs) {
|
|
21989
22008
|
if (!dir)
|
|
21990
22009
|
continue;
|
|
21991
22010
|
for (const candidateName of names) {
|
|
21992
|
-
const candidate =
|
|
22011
|
+
const candidate = path10.join(dir, candidateName);
|
|
21993
22012
|
try {
|
|
21994
22013
|
const st = statSync3(candidate);
|
|
21995
22014
|
if (process.platform !== "win32" && (st.mode & 73) === 0)
|
|
@@ -22015,14 +22034,14 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22015
22034
|
}
|
|
22016
22035
|
return true;
|
|
22017
22036
|
}, resolveBun = (env) => {
|
|
22018
|
-
if (env.BUN &&
|
|
22037
|
+
if (env.BUN && existsSync6(env.BUN))
|
|
22019
22038
|
return env.BUN;
|
|
22020
22039
|
for (const candidate of [
|
|
22021
|
-
|
|
22040
|
+
path10.join(os3.homedir(), ".bun/bin/bun"),
|
|
22022
22041
|
"/usr/local/bin/bun",
|
|
22023
22042
|
"/usr/bin/bun"
|
|
22024
22043
|
]) {
|
|
22025
|
-
if (
|
|
22044
|
+
if (existsSync6(candidate))
|
|
22026
22045
|
return candidate;
|
|
22027
22046
|
}
|
|
22028
22047
|
return commandOnPath2("bun", env) ? "bun" : null;
|
|
@@ -22070,7 +22089,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22070
22089
|
detail: "no dev checkout found (WORKFLOW_TOOLKIT_DEV) — skipping version parity"
|
|
22071
22090
|
};
|
|
22072
22091
|
}
|
|
22073
|
-
const corePkg = readJson(
|
|
22092
|
+
const corePkg = readJson(path10.join(res.dev, "packages/workit-core/package.json"));
|
|
22074
22093
|
if (!corePkg) {
|
|
22075
22094
|
return {
|
|
22076
22095
|
id: "versions",
|
|
@@ -22080,7 +22099,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22080
22099
|
}
|
|
22081
22100
|
const refs = new Set;
|
|
22082
22101
|
for (const name of ["workit-opencode", "workit-cursor", "workit-cli"]) {
|
|
22083
|
-
const pkg = readJson(
|
|
22102
|
+
const pkg = readJson(path10.join(res.dev, "packages", name, "package.json"));
|
|
22084
22103
|
const dep = pkg?.dependencies?.["@brainervirus/workit-core"];
|
|
22085
22104
|
if (typeof dep === "string")
|
|
22086
22105
|
refs.add(dep);
|
|
@@ -22096,7 +22115,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22096
22115
|
if (refs.size > 1) {
|
|
22097
22116
|
problems.push(`adapters pin different core versions: ${[...refs].join(", ")}`);
|
|
22098
22117
|
}
|
|
22099
|
-
const opencodePkg = readJson(
|
|
22118
|
+
const opencodePkg = readJson(path10.join(res.dev, "packages/workit-opencode/package.json"));
|
|
22100
22119
|
const sdk = opencodePkg?.dependencies?.["@opencode-ai/plugin"];
|
|
22101
22120
|
const sdkVersion = typeof sdk === "string" ? (sdk.match(/^\d+(?:\.\d+){0,2}/) ?? [])[0] : null;
|
|
22102
22121
|
if (sdkVersion && !semverAtLeast(sdkVersion, SUPPORT_MATRIX.opencode.minimum)) {
|
|
@@ -22116,27 +22135,27 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22116
22135
|
fix: "Align every adapter to the same @brainervirus/workit-core version (rewrite-workspace-deps.ts) or reinstall"
|
|
22117
22136
|
};
|
|
22118
22137
|
}, assetPathsFor = (host, dev) => {
|
|
22119
|
-
const pkg =
|
|
22138
|
+
const pkg = path10.join(dev, "packages", `workit-${host}`);
|
|
22120
22139
|
switch (host) {
|
|
22121
22140
|
case "opencode":
|
|
22122
22141
|
return [
|
|
22123
|
-
|
|
22124
|
-
|
|
22125
|
-
|
|
22126
|
-
|
|
22142
|
+
path10.join(pkg, "assets", "commands", "wk-init.md"),
|
|
22143
|
+
path10.join(pkg, "assets", "skills", "wk-init", "SKILL.md"),
|
|
22144
|
+
path10.join(pkg, "assets", "templates", "spec-template.md"),
|
|
22145
|
+
path10.join(pkg, "assets", "vendor", "superpowers", "skills", "brainstorming", "SKILL.md")
|
|
22127
22146
|
];
|
|
22128
22147
|
case "cursor":
|
|
22129
22148
|
return [
|
|
22130
|
-
|
|
22131
|
-
|
|
22132
|
-
|
|
22149
|
+
path10.join(pkg, "assets", "templates", "spec-template.md"),
|
|
22150
|
+
path10.join(pkg, "mcp.json"),
|
|
22151
|
+
path10.join(pkg, ".cursor-plugin")
|
|
22133
22152
|
];
|
|
22134
22153
|
case "cli":
|
|
22135
|
-
return [
|
|
22154
|
+
return [path10.join(pkg, "assets", "templates", "spec-template.md")];
|
|
22136
22155
|
}
|
|
22137
22156
|
}, hostsFor = (host) => host === "cli" ? ["opencode", "cursor", "cli"] : [host], checkAssets = (res) => {
|
|
22138
22157
|
const dev = res.dev;
|
|
22139
|
-
const missing = dev ? hostsFor(res.host).flatMap((h) => assetPathsFor(h, dev).filter((p) => !
|
|
22158
|
+
const missing = dev ? hostsFor(res.host).flatMap((h) => assetPathsFor(h, dev).filter((p) => !existsSync6(p)).map((p) => `${h}: ${p}`)) : [];
|
|
22140
22159
|
if (res.host === "cursor" || res.host === "cli") {
|
|
22141
22160
|
const cursorError = validateCursorSkills(res.cursorPluginDir);
|
|
22142
22161
|
if (cursorError)
|
|
@@ -22163,17 +22182,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22163
22182
|
fix: "Reinstall or rebuild the workit package (missing assets under packages/workit-<host>)"
|
|
22164
22183
|
};
|
|
22165
22184
|
}, launcherSlotsFor = (host, dev) => {
|
|
22166
|
-
const pkg =
|
|
22185
|
+
const pkg = path10.join(dev, "packages", `workit-${host}`);
|
|
22167
22186
|
switch (host) {
|
|
22168
22187
|
case "opencode":
|
|
22169
|
-
return [[
|
|
22188
|
+
return [[path10.join(pkg, "src", "plugin.ts"), path10.join(pkg, "dist", "plugin.js")]];
|
|
22170
22189
|
case "cursor":
|
|
22171
22190
|
return [
|
|
22172
|
-
[
|
|
22173
|
-
[
|
|
22191
|
+
[path10.join(pkg, "dist", "mcp-server.js")],
|
|
22192
|
+
[path10.join(pkg, "dist", "cursor-session-start.js")]
|
|
22174
22193
|
];
|
|
22175
22194
|
case "cli":
|
|
22176
|
-
return [[
|
|
22195
|
+
return [[path10.join(pkg, "src", "index.tsx"), path10.join(pkg, "dist", "index.js")]];
|
|
22177
22196
|
}
|
|
22178
22197
|
}, validNodeEntry = (entry, runtime, env) => {
|
|
22179
22198
|
try {
|
|
@@ -22187,7 +22206,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22187
22206
|
return false;
|
|
22188
22207
|
}
|
|
22189
22208
|
}, registeredCursorLauncher = (res) => {
|
|
22190
|
-
if (!
|
|
22209
|
+
if (!existsSync6(res.cursorMcp))
|
|
22191
22210
|
return "invalid";
|
|
22192
22211
|
const config2 = readJson(res.cursorMcp);
|
|
22193
22212
|
if (!config2)
|
|
@@ -22200,7 +22219,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22200
22219
|
if (typeof command !== "string" || !Array.isArray(args) || typeof args[0] !== "string") {
|
|
22201
22220
|
return "invalid";
|
|
22202
22221
|
}
|
|
22203
|
-
const executable =
|
|
22222
|
+
const executable = path10.basename(command).toLowerCase();
|
|
22204
22223
|
const canonical = cursorMcpServerEntry("").args;
|
|
22205
22224
|
if (executable === "npx" || executable === "npx.exe" || executable === "npx.cmd") {
|
|
22206
22225
|
if (args.length !== canonical.length || args.some((a, i) => a !== canonical[i]))
|
|
@@ -22212,11 +22231,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22212
22231
|
return {
|
|
22213
22232
|
kind: "node",
|
|
22214
22233
|
runtime: command,
|
|
22215
|
-
entry:
|
|
22234
|
+
entry: path10.isAbsolute(args[0]) ? args[0] : path10.resolve(path10.dirname(res.cursorMcp), args[0])
|
|
22216
22235
|
};
|
|
22217
22236
|
}, canonicalCursorHook, registeredCursorHook = (res) => {
|
|
22218
|
-
const hooksFile =
|
|
22219
|
-
if (!
|
|
22237
|
+
const hooksFile = path10.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
|
|
22238
|
+
if (!existsSync6(hooksFile))
|
|
22220
22239
|
return "invalid";
|
|
22221
22240
|
const config2 = readJson(hooksFile);
|
|
22222
22241
|
if (!config2)
|
|
@@ -22233,9 +22252,9 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22233
22252
|
if (!m)
|
|
22234
22253
|
return false;
|
|
22235
22254
|
const entry = m[1].trim();
|
|
22236
|
-
if (!
|
|
22255
|
+
if (!path10.isAbsolute(entry))
|
|
22237
22256
|
return false;
|
|
22238
|
-
if (
|
|
22257
|
+
if (path10.resolve(entry) !== path10.resolve(path10.join(res.cursorPluginDir, "dist", "cursor-session-start.js"))) {
|
|
22239
22258
|
return false;
|
|
22240
22259
|
}
|
|
22241
22260
|
return validNodeEntry(entry, "node", res.env);
|
|
@@ -22244,7 +22263,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22244
22263
|
const hosts = hostsFor(res.host);
|
|
22245
22264
|
const registered = hosts.includes("cursor") ? registeredCursorLauncher(res) : null;
|
|
22246
22265
|
const runtime = registered && registered !== "invalid" && registered.kind === "node" ? registered.runtime : "node";
|
|
22247
|
-
const missing = hosts.includes("cursor") ? ["dist/mcp-server.js", "dist/cursor-session-start.js"].map((rel) =>
|
|
22266
|
+
const missing = hosts.includes("cursor") ? ["dist/mcp-server.js", "dist/cursor-session-start.js"].map((rel) => path10.join(res.cursorPluginDir, rel)).filter((p) => !validNodeEntry(p, runtime, res.env)).map((p) => `cursor: ${p}`) : [];
|
|
22248
22267
|
if (hosts.includes("cursor")) {
|
|
22249
22268
|
if (registered === "invalid") {
|
|
22250
22269
|
missing.push(`cursor: canonical workit MCP launcher in ${res.cursorMcp}`);
|
|
@@ -22253,13 +22272,13 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22253
22272
|
}
|
|
22254
22273
|
const hook = registeredCursorHook(res);
|
|
22255
22274
|
if (hook === "invalid") {
|
|
22256
|
-
missing.push(`cursor: canonical session-start hook in ${
|
|
22275
|
+
missing.push(`cursor: canonical session-start hook in ${path10.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")}`);
|
|
22257
22276
|
} else if (hook !== null && hook !== canonicalCursorHook && !validLocalDistHook(hook, res)) {
|
|
22258
|
-
missing.push(`cursor: canonical session-start hook in ${
|
|
22277
|
+
missing.push(`cursor: canonical session-start hook in ${path10.join(res.cursorPluginDir, "hooks", "hooks-cursor.json")} (registered ${hook})`);
|
|
22259
22278
|
}
|
|
22260
22279
|
}
|
|
22261
22280
|
if (dev) {
|
|
22262
|
-
missing.push(...hosts.filter((h) => h !== "cursor").flatMap((h) => launcherSlotsFor(h, dev).filter((slot) => !slot.some((p) =>
|
|
22281
|
+
missing.push(...hosts.filter((h) => h !== "cursor").flatMap((h) => launcherSlotsFor(h, dev).filter((slot) => !slot.some((p) => existsSync6(p))).map((slot) => `${h}: ${slot.join(" or ")}`)));
|
|
22263
22282
|
}
|
|
22264
22283
|
if (missing.length > 0) {
|
|
22265
22284
|
return {
|
|
@@ -22276,7 +22295,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22276
22295
|
detail: "no dev checkout found (WORKFLOW_TOOLKIT_DEV) — skipping non-Cursor launcher checks"
|
|
22277
22296
|
};
|
|
22278
22297
|
}
|
|
22279
|
-
return {
|
|
22298
|
+
return {
|
|
22299
|
+
id: "launcher",
|
|
22300
|
+
status: "pass",
|
|
22301
|
+
detail: `${res.host} launcher/hook entries present`
|
|
22302
|
+
};
|
|
22280
22303
|
}, checkUtility = (res) => {
|
|
22281
22304
|
const git = commandOnPath2("git", res.env);
|
|
22282
22305
|
if (!git) {
|
|
@@ -22295,7 +22318,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22295
22318
|
fix: "Install util-linux (flock)"
|
|
22296
22319
|
};
|
|
22297
22320
|
}
|
|
22298
|
-
return {
|
|
22321
|
+
return {
|
|
22322
|
+
id: "utility",
|
|
22323
|
+
status: "pass",
|
|
22324
|
+
detail: "git (and flock where required) on PATH"
|
|
22325
|
+
};
|
|
22299
22326
|
}, staleEntry = (entry) => {
|
|
22300
22327
|
if (!entry)
|
|
22301
22328
|
return "stale";
|
|
@@ -22303,7 +22330,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22303
22330
|
return "stale";
|
|
22304
22331
|
if (entry.startsWith("file:")) {
|
|
22305
22332
|
const target = entry.replace(/^file:\/\//, "").replace(/^file:/, "");
|
|
22306
|
-
return
|
|
22333
|
+
return existsSync6(target) ? "ok" : "missing-file";
|
|
22307
22334
|
}
|
|
22308
22335
|
return "ok";
|
|
22309
22336
|
}, checkStalePin = (res) => {
|
|
@@ -22314,8 +22341,12 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22314
22341
|
detail: "opencode pin not inspected on the cursor host"
|
|
22315
22342
|
};
|
|
22316
22343
|
}
|
|
22317
|
-
if (!
|
|
22318
|
-
return {
|
|
22344
|
+
if (!existsSync6(res.opencodeConfig)) {
|
|
22345
|
+
return {
|
|
22346
|
+
id: "stale_pin",
|
|
22347
|
+
status: "pass",
|
|
22348
|
+
detail: "no opencode config — not registered"
|
|
22349
|
+
};
|
|
22319
22350
|
}
|
|
22320
22351
|
const cfg = readJson(res.opencodeConfig);
|
|
22321
22352
|
const entries = pluginEntries(cfg);
|
|
@@ -22345,17 +22376,125 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22345
22376
|
};
|
|
22346
22377
|
}
|
|
22347
22378
|
return { id: "stale_pin", status: "pass", detail: "opencode pin resolves" };
|
|
22379
|
+
}, pluginMcpSelectors = (res) => {
|
|
22380
|
+
const p = path10.join(res.cursorPluginDir, "mcp.json");
|
|
22381
|
+
if (!existsSync6(p))
|
|
22382
|
+
return null;
|
|
22383
|
+
const cfg = readJson(p);
|
|
22384
|
+
const server = cfg?.mcpServers?.workit;
|
|
22385
|
+
if (!server || typeof server !== "object" || Array.isArray(server))
|
|
22386
|
+
return null;
|
|
22387
|
+
const args = server.args;
|
|
22388
|
+
if (!Array.isArray(args))
|
|
22389
|
+
return null;
|
|
22390
|
+
return args.map(String).filter((a) => a.startsWith("--package="));
|
|
22391
|
+
}, registryLatestVersion = (res) => {
|
|
22392
|
+
const seam = res.env.WORKIT_DOCTOR_STALE_REGISTRY_VERSION;
|
|
22393
|
+
if (typeof seam === "string" && seam) {
|
|
22394
|
+
return /^\d+(?:\.\d+){1,2}$/.test(seam) ? seam : null;
|
|
22395
|
+
}
|
|
22396
|
+
const cmd = res.env.WORKIT_DOCTOR_STALE_REGISTRY_CMD ?? (commandOnPath2("npm", res.env) ? "npm" : null);
|
|
22397
|
+
if (!cmd)
|
|
22398
|
+
return null;
|
|
22399
|
+
try {
|
|
22400
|
+
const r = spawnSync4(cmd, ["view", CURSOR_RUNTIME_PACKAGE, "version"], {
|
|
22401
|
+
encoding: "utf8",
|
|
22402
|
+
timeout: 20000,
|
|
22403
|
+
env: res.env
|
|
22404
|
+
});
|
|
22405
|
+
const v = (r.stdout ?? "").trim().split(/\s+/)[0];
|
|
22406
|
+
return r.status === 0 && /^\d+(?:\.\d+){1,2}$/.test(v) ? v : null;
|
|
22407
|
+
} catch {
|
|
22408
|
+
return null;
|
|
22409
|
+
}
|
|
22410
|
+
}, installedPluginVersion = (res) => {
|
|
22411
|
+
const pkg = readJson(path10.join(res.cursorPluginDir, "package.json"));
|
|
22412
|
+
return typeof pkg?.version === "string" && pkg.version ? pkg.version : null;
|
|
22413
|
+
}, checkStaleInstall = (res) => {
|
|
22414
|
+
if (res.host !== "cursor" && res.host !== "cli") {
|
|
22415
|
+
return {
|
|
22416
|
+
id: "stale_install",
|
|
22417
|
+
status: "pass",
|
|
22418
|
+
detail: "cursor plugin not inspected on the opencode host"
|
|
22419
|
+
};
|
|
22420
|
+
}
|
|
22421
|
+
const canonicalMcp = cursorMcpServerEntry("").args.find((a) => a.startsWith("--package="));
|
|
22422
|
+
const mcpSelectors = pluginMcpSelectors(res);
|
|
22423
|
+
const legacyPin = (mcpSelectors ?? []).find((s) => s !== canonicalMcp && s.includes("@brainervirus/workit-cursor@"));
|
|
22424
|
+
if (legacyPin) {
|
|
22425
|
+
return {
|
|
22426
|
+
id: "stale_install",
|
|
22427
|
+
status: "fail",
|
|
22428
|
+
detail: `stale_install: plugin mcp.json pins a legacy selector ${legacyPin} (canonical: ${canonicalMcp})`,
|
|
22429
|
+
fix: "Re-run install-cursor-plugin.sh — it rewrites the workit MCP entry to the canonical @latest selector"
|
|
22430
|
+
};
|
|
22431
|
+
}
|
|
22432
|
+
const hooksFile = path10.join(res.cursorPluginDir, "hooks", "hooks-cursor.json");
|
|
22433
|
+
const hookCmd = readJson(hooksFile)?.hooks?.sessionStart?.[0]?.command ?? null;
|
|
22434
|
+
const canonicalHook = canonicalCursorHook;
|
|
22435
|
+
const staleHook = hookCmd !== null && hookCmd !== canonicalHook && !hookCmd.startsWith("node ") && hookCmd.includes("@brainervirus/workit-cursor@");
|
|
22436
|
+
if (staleHook) {
|
|
22437
|
+
return {
|
|
22438
|
+
id: "stale_install",
|
|
22439
|
+
status: "fail",
|
|
22440
|
+
detail: `stale_install: sessionStart hook runs a legacy selector (canonical: ${canonicalHook})`,
|
|
22441
|
+
fix: "Re-run install-cursor-plugin.sh — it rewrites the sessionStart hook to the canonical @latest selector"
|
|
22442
|
+
};
|
|
22443
|
+
}
|
|
22444
|
+
const installed = installedPluginVersion(res);
|
|
22445
|
+
const source = res.dev ? readJson(path10.join(res.dev, "packages/workit-cursor/package.json"))?.version ?? null : readJson(path10.join(packageRoot(), "package.json"))?.version ?? null;
|
|
22446
|
+
const localDist = mcpSelectors === null && hookCmd !== null && hookCmd.startsWith("node ");
|
|
22447
|
+
if (localDist && installed !== null && source !== null && !semverAtLeast(installed, source)) {
|
|
22448
|
+
return {
|
|
22449
|
+
id: "stale_install",
|
|
22450
|
+
status: "fail",
|
|
22451
|
+
detail: `stale_install: installed workit-cursor ${installed} is behind the current runtime ${source}`,
|
|
22452
|
+
fix: "Re-run install-cursor-plugin.sh — it refreshes the plugin directory and rewrites the workit MCP/hook entries"
|
|
22453
|
+
};
|
|
22454
|
+
}
|
|
22455
|
+
if (installed !== null && localDist) {
|
|
22456
|
+
const expected = registryLatestVersion(res);
|
|
22457
|
+
if (expected === null) {
|
|
22458
|
+
return {
|
|
22459
|
+
id: "registry_unreachable",
|
|
22460
|
+
status: "warn",
|
|
22461
|
+
registryProbed: true,
|
|
22462
|
+
detail: "registry_unreachable: cannot compare installed workit-cursor against the published runtime",
|
|
22463
|
+
fix: "Retry when the npm registry is reachable, or re-run install-cursor-plugin.sh to refresh the install"
|
|
22464
|
+
};
|
|
22465
|
+
}
|
|
22466
|
+
if (!semverAtLeast(installed, expected)) {
|
|
22467
|
+
return {
|
|
22468
|
+
id: "stale_install",
|
|
22469
|
+
status: "fail",
|
|
22470
|
+
registryProbed: true,
|
|
22471
|
+
detail: `stale_install: local-dist workit-cursor ${installed} is behind the published runtime ${expected}`,
|
|
22472
|
+
fix: "Re-run install-cursor-plugin.sh — it refreshes the plugin directory with the current build"
|
|
22473
|
+
};
|
|
22474
|
+
}
|
|
22475
|
+
return {
|
|
22476
|
+
id: "stale_install",
|
|
22477
|
+
status: "pass",
|
|
22478
|
+
registryProbed: true,
|
|
22479
|
+
detail: `installed local-dist workit-cursor ${installed} matches the published runtime ${expected}`
|
|
22480
|
+
};
|
|
22481
|
+
}
|
|
22482
|
+
return {
|
|
22483
|
+
id: "stale_install",
|
|
22484
|
+
status: "pass",
|
|
22485
|
+
detail: installed === null ? "installed workit-cursor selectors are canonical" : `installed workit-cursor ${installed} is metadata on the canonical @latest install (fresh at launch)`
|
|
22486
|
+
};
|
|
22348
22487
|
}, checkDuplicateRegistration = (res) => {
|
|
22349
22488
|
const problems = [];
|
|
22350
22489
|
const opencodeHost = res.host !== "cursor";
|
|
22351
22490
|
const cursorHost = res.host !== "opencode";
|
|
22352
|
-
if (opencodeHost &&
|
|
22491
|
+
if (opencodeHost && existsSync6(res.opencodeConfig)) {
|
|
22353
22492
|
const cfg = readJson(res.opencodeConfig);
|
|
22354
22493
|
const entries = pluginEntries(cfg);
|
|
22355
22494
|
if (entries.length > 1)
|
|
22356
22495
|
problems.push(`opencode registers ${entries.length} workit plugin entries`);
|
|
22357
22496
|
}
|
|
22358
|
-
if (cursorHost &&
|
|
22497
|
+
if (cursorHost && existsSync6(res.cursorSettings)) {
|
|
22359
22498
|
const settings = readJson(res.cursorSettings);
|
|
22360
22499
|
const enabled = settings?.enabled_plugins;
|
|
22361
22500
|
if (enabled && typeof enabled === "object") {
|
|
@@ -22370,7 +22509,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22370
22509
|
if (dirs.length > 1)
|
|
22371
22510
|
problems.push(`cursor plugin_dirs has ${dirs.length} workit entries`);
|
|
22372
22511
|
}
|
|
22373
|
-
if (cursorHost &&
|
|
22512
|
+
if (cursorHost && existsSync6(res.cursorMcp)) {
|
|
22374
22513
|
const mcp = readJson(res.cursorMcp);
|
|
22375
22514
|
const servers = mcp?.mcpServers ?? {};
|
|
22376
22515
|
if (servers && typeof servers === "object") {
|
|
@@ -22380,7 +22519,11 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22380
22519
|
}
|
|
22381
22520
|
}
|
|
22382
22521
|
if (problems.length === 0) {
|
|
22383
|
-
return {
|
|
22522
|
+
return {
|
|
22523
|
+
id: "duplicate_registration",
|
|
22524
|
+
status: "pass",
|
|
22525
|
+
detail: "no duplicate registrations"
|
|
22526
|
+
};
|
|
22384
22527
|
}
|
|
22385
22528
|
return {
|
|
22386
22529
|
id: "duplicate_registration",
|
|
@@ -22391,21 +22534,25 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22391
22534
|
}, checkMalformedConfig = (res) => {
|
|
22392
22535
|
const files = [];
|
|
22393
22536
|
for (const name of ["config.json", "youtrack.json", "vcs.json", "workspaces.json"]) {
|
|
22394
|
-
const p =
|
|
22395
|
-
if (
|
|
22537
|
+
const p = path10.join(res.configDir, name);
|
|
22538
|
+
if (existsSync6(p))
|
|
22396
22539
|
files.push(p);
|
|
22397
22540
|
}
|
|
22398
22541
|
const opencodeHost = res.host !== "cursor";
|
|
22399
22542
|
const cursorHost = res.host !== "opencode";
|
|
22400
|
-
if (opencodeHost &&
|
|
22543
|
+
if (opencodeHost && existsSync6(res.opencodeConfig))
|
|
22401
22544
|
files.push(res.opencodeConfig);
|
|
22402
|
-
if (cursorHost &&
|
|
22545
|
+
if (cursorHost && existsSync6(res.cursorSettings))
|
|
22403
22546
|
files.push(res.cursorSettings);
|
|
22404
|
-
if (cursorHost &&
|
|
22547
|
+
if (cursorHost && existsSync6(res.cursorMcp))
|
|
22405
22548
|
files.push(res.cursorMcp);
|
|
22406
22549
|
const bad = files.filter((p) => !parsesAsConfigObject(p));
|
|
22407
22550
|
if (bad.length === 0)
|
|
22408
|
-
return {
|
|
22551
|
+
return {
|
|
22552
|
+
id: "malformed_config",
|
|
22553
|
+
status: "pass",
|
|
22554
|
+
detail: "config files parse"
|
|
22555
|
+
};
|
|
22409
22556
|
return {
|
|
22410
22557
|
id: "malformed_config",
|
|
22411
22558
|
status: "fail",
|
|
@@ -22413,12 +22560,20 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22413
22560
|
fix: `Repair the malformed config in ${bad[0]}`
|
|
22414
22561
|
};
|
|
22415
22562
|
}, checkWorkspaceMismatch = (res) => {
|
|
22416
|
-
const file =
|
|
22417
|
-
if (!
|
|
22418
|
-
return {
|
|
22563
|
+
const file = path10.join(res.configDir, "workspaces.json");
|
|
22564
|
+
if (!existsSync6(file))
|
|
22565
|
+
return {
|
|
22566
|
+
id: "workspace_mismatch",
|
|
22567
|
+
status: "pass",
|
|
22568
|
+
detail: "no workspaces configured"
|
|
22569
|
+
};
|
|
22419
22570
|
const ws = readJson(file);
|
|
22420
22571
|
if (!Array.isArray(ws?.workspaces)) {
|
|
22421
|
-
return {
|
|
22572
|
+
return {
|
|
22573
|
+
id: "workspace_mismatch",
|
|
22574
|
+
status: "pass",
|
|
22575
|
+
detail: "no workspaces configured"
|
|
22576
|
+
};
|
|
22422
22577
|
}
|
|
22423
22578
|
const match = resolveWorkspaceFrom(res.cwd, res.configDir);
|
|
22424
22579
|
if (match)
|
|
@@ -22441,29 +22596,33 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22441
22596
|
}
|
|
22442
22597
|
}, checkCredentialMetadata = (res) => {
|
|
22443
22598
|
const tokenPaths = [];
|
|
22444
|
-
const youtrackJson = readJson(
|
|
22599
|
+
const youtrackJson = readJson(path10.join(res.configDir, "youtrack.json"));
|
|
22445
22600
|
if (youtrackJson) {
|
|
22446
|
-
tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile :
|
|
22447
|
-
} else if (
|
|
22448
|
-
tokenPaths.push(
|
|
22601
|
+
tokenPaths.push(typeof youtrackJson.tokenFile === "string" ? youtrackJson.tokenFile : path10.join(res.configDir, "youtrack.token"));
|
|
22602
|
+
} else if (existsSync6(path10.join(res.configDir, "youtrack.token"))) {
|
|
22603
|
+
tokenPaths.push(path10.join(res.configDir, "youtrack.token"));
|
|
22449
22604
|
}
|
|
22450
|
-
const vcsJson = readJson(
|
|
22605
|
+
const vcsJson = readJson(path10.join(res.configDir, "vcs.json"));
|
|
22451
22606
|
for (const key of ["gitlab", "github"]) {
|
|
22452
22607
|
const provider = vcsJson?.[key];
|
|
22453
22608
|
if (provider && typeof provider === "object") {
|
|
22454
22609
|
const tf = provider.tokenFile;
|
|
22455
|
-
tokenPaths.push(typeof tf === "string" ? tf :
|
|
22456
|
-
} else if (!vcsJson &&
|
|
22457
|
-
tokenPaths.push(
|
|
22610
|
+
tokenPaths.push(typeof tf === "string" ? tf : path10.join(res.configDir, `${key}.token`));
|
|
22611
|
+
} else if (!vcsJson && existsSync6(path10.join(res.configDir, `${key}.token`))) {
|
|
22612
|
+
tokenPaths.push(path10.join(res.configDir, `${key}.token`));
|
|
22458
22613
|
}
|
|
22459
22614
|
}
|
|
22460
22615
|
if (tokenPaths.length === 0) {
|
|
22461
|
-
return {
|
|
22616
|
+
return {
|
|
22617
|
+
id: "credential_metadata",
|
|
22618
|
+
status: "pass",
|
|
22619
|
+
detail: "no credentials configured"
|
|
22620
|
+
};
|
|
22462
22621
|
}
|
|
22463
22622
|
const problems = [];
|
|
22464
22623
|
for (const raw of tokenPaths) {
|
|
22465
|
-
const p =
|
|
22466
|
-
if (!
|
|
22624
|
+
const p = path10.isAbsolute(raw) ? raw : path10.resolve(res.configDir, raw);
|
|
22625
|
+
if (!existsSync6(p)) {
|
|
22467
22626
|
problems.push(`${p} is missing`);
|
|
22468
22627
|
continue;
|
|
22469
22628
|
}
|
|
@@ -22489,13 +22648,17 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22489
22648
|
fix: "Create or fix the token file (mode 0600, real value) — see /wk-status for the exact path"
|
|
22490
22649
|
};
|
|
22491
22650
|
}, checkLogWritable = (res) => {
|
|
22492
|
-
const logsDir =
|
|
22493
|
-
const probe =
|
|
22651
|
+
const logsDir = path10.join(res.stateDir, "logs");
|
|
22652
|
+
const probe = path10.join(logsDir, "doctor-probe.tmp");
|
|
22494
22653
|
try {
|
|
22495
22654
|
mkdirSync4(logsDir, { recursive: true, mode: 448 });
|
|
22496
22655
|
writeFileSync3(probe, `{"probe":true}
|
|
22497
22656
|
`, { mode: 384 });
|
|
22498
|
-
return {
|
|
22657
|
+
return {
|
|
22658
|
+
id: "log_writable",
|
|
22659
|
+
status: "pass",
|
|
22660
|
+
detail: "log directory writable"
|
|
22661
|
+
};
|
|
22499
22662
|
} catch (err) {
|
|
22500
22663
|
return {
|
|
22501
22664
|
id: "log_writable",
|
|
@@ -22511,15 +22674,20 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22511
22674
|
}, RUN_CHECKS, INSTALLER_REQUIRED, runDoctor = (options = {}) => {
|
|
22512
22675
|
const res = resolve(options);
|
|
22513
22676
|
const raw = RUN_CHECKS.map((fn) => fn(res));
|
|
22514
|
-
const checks4 = res.installer ? raw.map((c) => c.status === "fail" && !INSTALLER_REQUIRED.has(c.id) ? {
|
|
22677
|
+
const checks4 = res.installer ? raw.map((c) => c.status === "fail" && !INSTALLER_REQUIRED.has(c.id) ? {
|
|
22678
|
+
...c,
|
|
22679
|
+
status: "warn",
|
|
22680
|
+
detail: `${c.detail} (not enforced by installer)`
|
|
22681
|
+
} : c) : raw;
|
|
22515
22682
|
const failed = checks4.filter((c) => c.status === "fail").length;
|
|
22516
22683
|
const warned = checks4.filter((c) => c.status === "warn").length;
|
|
22517
22684
|
const passed = checks4.filter((c) => c.status === "pass").length;
|
|
22518
22685
|
const exitCode = failed > 0 ? 1 : 0;
|
|
22686
|
+
const offline = !raw.some((c) => ("registryProbed" in c) && c.registryProbed);
|
|
22519
22687
|
const report = {
|
|
22520
22688
|
ok: failed === 0,
|
|
22521
22689
|
exitCode,
|
|
22522
|
-
offline
|
|
22690
|
+
offline,
|
|
22523
22691
|
host: res.host,
|
|
22524
22692
|
checked_at: new Date().toISOString(),
|
|
22525
22693
|
summary: { passed, warned, failed, total: checks4.length },
|
|
@@ -22529,7 +22697,7 @@ var TOKEN_PLACEHOLDER2 = "YOUR_TOKEN_HERE", findDevFromCwd = (cwd) => {
|
|
|
22529
22697
|
getDiagnosticLogger()?.info(EVENT.doctor, {
|
|
22530
22698
|
host: res.host,
|
|
22531
22699
|
exit_code: exitCode,
|
|
22532
|
-
offline
|
|
22700
|
+
offline,
|
|
22533
22701
|
failed: checks4.filter((c) => c.status === "fail").map((c) => c.id),
|
|
22534
22702
|
total: checks4.length
|
|
22535
22703
|
});
|
|
@@ -22539,6 +22707,7 @@ var init_doctor = __esm(() => {
|
|
|
22539
22707
|
init_support_matrix();
|
|
22540
22708
|
init_boundary();
|
|
22541
22709
|
init_config();
|
|
22710
|
+
init_package_root();
|
|
22542
22711
|
init_registration();
|
|
22543
22712
|
init_workspaces();
|
|
22544
22713
|
init_skill_manifests();
|
|
@@ -22550,6 +22719,7 @@ var init_doctor = __esm(() => {
|
|
|
22550
22719
|
checkLauncher,
|
|
22551
22720
|
checkUtility,
|
|
22552
22721
|
checkStalePin,
|
|
22722
|
+
checkStaleInstall,
|
|
22553
22723
|
checkDuplicateRegistration,
|
|
22554
22724
|
checkMalformedConfig,
|
|
22555
22725
|
checkWorkspaceMismatch,
|
|
@@ -22562,6 +22732,7 @@ var init_doctor = __esm(() => {
|
|
|
22562
22732
|
"launcher",
|
|
22563
22733
|
"utility",
|
|
22564
22734
|
"stale_pin",
|
|
22735
|
+
"stale_install",
|
|
22565
22736
|
"duplicate_registration",
|
|
22566
22737
|
"malformed_config"
|
|
22567
22738
|
]);
|
|
@@ -22569,7 +22740,7 @@ var init_doctor = __esm(() => {
|
|
|
22569
22740
|
|
|
22570
22741
|
// packages/workit-core/src/core/pr-create.ts
|
|
22571
22742
|
import fs2 from "node:fs";
|
|
22572
|
-
import
|
|
22743
|
+
import path11 from "node:path";
|
|
22573
22744
|
import { spawnSync as spawnSync5 } from "node:child_process";
|
|
22574
22745
|
function parseGhRepo(remote) {
|
|
22575
22746
|
remote = (remote || "").trim().replace(/\/+$/, "");
|
|
@@ -22634,11 +22805,11 @@ ${line}` : line;
|
|
|
22634
22805
|
}
|
|
22635
22806
|
function whichOnPath(tool) {
|
|
22636
22807
|
const names = process.platform === "win32" ? [tool, `${tool}.exe`, `${tool}.cmd`] : [tool];
|
|
22637
|
-
for (const dir of (process.env.PATH ?? "").split(
|
|
22808
|
+
for (const dir of (process.env.PATH ?? "").split(path11.delimiter)) {
|
|
22638
22809
|
if (!dir)
|
|
22639
22810
|
continue;
|
|
22640
22811
|
for (const name of names) {
|
|
22641
|
-
const candidate =
|
|
22812
|
+
const candidate = path11.join(dir, name);
|
|
22642
22813
|
try {
|
|
22643
22814
|
fs2.accessSync(candidate, fs2.constants.X_OK);
|
|
22644
22815
|
return candidate;
|
|
@@ -22740,7 +22911,7 @@ function prCreate(env, cwd) {
|
|
|
22740
22911
|
const draft = String(env.WF_PR_DRAFT ?? "false").toLowerCase() === "true";
|
|
22741
22912
|
let baseUrl = cfg.youtrack_base_url;
|
|
22742
22913
|
if (!baseUrl) {
|
|
22743
|
-
const ytCfg = process.env.WORKFLOW_YOUTRACK_CONFIG ??
|
|
22914
|
+
const ytCfg = process.env.WORKFLOW_YOUTRACK_CONFIG ?? path11.join(path11.dirname(String(cfg.configPath)), "youtrack.json");
|
|
22744
22915
|
try {
|
|
22745
22916
|
const yt = JSON.parse(fs2.readFileSync(ytCfg, "utf8"));
|
|
22746
22917
|
if (yt && typeof yt === "object")
|
|
@@ -22849,25 +23020,6 @@ var init_pr_create = __esm(() => {
|
|
|
22849
23020
|
init_branch();
|
|
22850
23021
|
});
|
|
22851
23022
|
|
|
22852
|
-
// packages/workit-core/src/core/package-root.ts
|
|
22853
|
-
import { existsSync as existsSync6 } from "node:fs";
|
|
22854
|
-
import path11 from "node:path";
|
|
22855
|
-
import { fileURLToPath } from "node:url";
|
|
22856
|
-
var packageRoot = () => {
|
|
22857
|
-
let dir = path11.dirname(fileURLToPath(import.meta.url));
|
|
22858
|
-
while (true) {
|
|
22859
|
-
const parent = path11.dirname(dir);
|
|
22860
|
-
if (existsSync6(path11.join(dir, "package.json")) || parent === dir)
|
|
22861
|
-
return dir;
|
|
22862
|
-
dir = parent;
|
|
22863
|
-
}
|
|
22864
|
-
}, assetRoot = () => {
|
|
22865
|
-
const root = packageRoot();
|
|
22866
|
-
const assets = path11.join(root, "assets");
|
|
22867
|
-
return existsSync6(assets) ? assets : root;
|
|
22868
|
-
};
|
|
22869
|
-
var init_package_root = () => {};
|
|
22870
|
-
|
|
22871
23023
|
// packages/workit-core/src/core/scripts.ts
|
|
22872
23024
|
var PLUGIN_ROOT, ASSET_ROOT, resolveWorkspaceRoot = (explicit) => explicit || process.cwd();
|
|
22873
23025
|
var init_scripts = __esm(() => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@brainervirus/workit-cursor",
|
|
3
|
-
"version": "0.8.
|
|
3
|
+
"version": "0.8.8",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "Workit Cursor plugin: MCP server, hooks, rules, marketplace manifest (thin over @brainervirus/workit-core)",
|
|
6
6
|
"keywords": [
|
|
@@ -39,7 +39,7 @@
|
|
|
39
39
|
"build": "bun scripts/build.ts"
|
|
40
40
|
},
|
|
41
41
|
"dependencies": {
|
|
42
|
-
"@brainervirus/workit-core": "^0.8.
|
|
42
|
+
"@brainervirus/workit-core": "^0.8.8",
|
|
43
43
|
"@modelcontextprotocol/sdk": "^1.12.0",
|
|
44
44
|
"zod": "^3.24.0"
|
|
45
45
|
},
|