react-doctor 0.0.44 → 0.0.45
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/README.md +1 -1
- package/dist/cli.js +46 -74
- package/package.json +2 -1
package/README.md
CHANGED
|
@@ -48,7 +48,7 @@ npx -y react-doctor@latest install
|
|
|
48
48
|
|
|
49
49
|
You'll be prompted to pick which detected agents to install for. Pass `--yes` to skip prompts and install for every detected agent.
|
|
50
50
|
|
|
51
|
-
Supports Claude Code, Codex,
|
|
51
|
+
Supports 50+ coding agents via [`agent-install`](https://www.npmjs.com/package/agent-install), including Claude Code, Codex, Cursor, Factory Droid, Gemini CLI, GitHub Copilot, Goose, OpenCode, Pi, Windsurf, Roo Code, Cline, Kilo Code, Warp, Replit, OpenHands, Continue, and many more. Detection is the union of CLI binaries on `$PATH` and config dirs in `$HOME` (`~/.claude`, `~/.cursor`, `~/.codex`, `~/.factory`, `~/.pi`, etc.).
|
|
52
52
|
|
|
53
53
|
## GitHub Actions
|
|
54
54
|
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { createRequire } from "node:module";
|
|
2
|
-
import fs, { accessSync, constants,
|
|
2
|
+
import fs, { accessSync, constants, existsSync, mkdirSync, mkdtempSync, readdirSync, statSync, writeFileSync } from "node:fs";
|
|
3
3
|
import os, { tmpdir } from "node:os";
|
|
4
4
|
import path, { join } from "node:path";
|
|
5
5
|
import { performance } from "node:perf_hooks";
|
|
6
6
|
import { Command } from "commander";
|
|
7
7
|
import { fileURLToPath } from "node:url";
|
|
8
|
+
import { SKILL_MANIFEST_FILE, detectInstalledSkillAgents, getSkillAgentConfig, getSkillAgentTypes, installSkillsFromSource } from "agent-install";
|
|
8
9
|
import pc from "picocolors";
|
|
9
10
|
import basePrompts from "prompts";
|
|
10
11
|
import ora from "ora";
|
|
@@ -43,54 +44,21 @@ const IGNORED_DIRECTORIES = new Set([
|
|
|
43
44
|
"coverage"
|
|
44
45
|
]);
|
|
45
46
|
const CANONICAL_GITHUB_URL = "https://github.com/millionco/react-doctor";
|
|
47
|
+
const SKILL_NAME = "react-doctor";
|
|
46
48
|
const PROXY_OUTPUT_MAX_BYTES = 50 * 1024 * 1024;
|
|
47
49
|
const buildNoReactDependencyError = (directory) => `No React dependency found in ${directory}/package.json. Add "react" to dependencies (or peerDependencies) and re-run.`;
|
|
48
50
|
//#endregion
|
|
49
51
|
//#region src/utils/detect-agents.ts
|
|
50
|
-
const
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
displayName: "Codex",
|
|
60
|
-
skillDir: AGENTS_SKILL_DIR
|
|
61
|
-
},
|
|
62
|
-
copilot: {
|
|
63
|
-
binaries: ["copilot"],
|
|
64
|
-
displayName: "GitHub Copilot",
|
|
65
|
-
skillDir: AGENTS_SKILL_DIR
|
|
66
|
-
},
|
|
67
|
-
gemini: {
|
|
68
|
-
binaries: ["gemini"],
|
|
69
|
-
displayName: "Gemini CLI",
|
|
70
|
-
skillDir: AGENTS_SKILL_DIR
|
|
71
|
-
},
|
|
72
|
-
cursor: {
|
|
73
|
-
binaries: ["cursor", "agent"],
|
|
74
|
-
displayName: "Cursor",
|
|
75
|
-
skillDir: AGENTS_SKILL_DIR
|
|
76
|
-
},
|
|
77
|
-
opencode: {
|
|
78
|
-
binaries: ["opencode"],
|
|
79
|
-
displayName: "OpenCode",
|
|
80
|
-
skillDir: AGENTS_SKILL_DIR
|
|
81
|
-
},
|
|
82
|
-
droid: {
|
|
83
|
-
binaries: ["droid"],
|
|
84
|
-
displayName: "Factory Droid",
|
|
85
|
-
skillDir: ".factory/skills"
|
|
86
|
-
},
|
|
87
|
-
pi: {
|
|
88
|
-
binaries: ["pi", "omegon"],
|
|
89
|
-
displayName: "Pi",
|
|
90
|
-
skillDir: AGENTS_SKILL_DIR
|
|
91
|
-
}
|
|
52
|
+
const PATH_BINARIES = {
|
|
53
|
+
"claude-code": ["claude"],
|
|
54
|
+
codex: ["codex"],
|
|
55
|
+
cursor: ["cursor", "agent"],
|
|
56
|
+
droid: ["droid"],
|
|
57
|
+
"gemini-cli": ["gemini"],
|
|
58
|
+
"github-copilot": ["copilot"],
|
|
59
|
+
opencode: ["opencode"],
|
|
60
|
+
pi: ["pi", "omegon"]
|
|
92
61
|
};
|
|
93
|
-
const ALL_SUPPORTED_AGENTS = Object.keys(SUPPORTED_AGENTS);
|
|
94
62
|
const isCommandAvailable = (command) => {
|
|
95
63
|
const pathDirectories = (process.env.PATH ?? "").split(path.delimiter).filter(Boolean);
|
|
96
64
|
for (const directory of pathDirectories) {
|
|
@@ -104,9 +72,15 @@ const isCommandAvailable = (command) => {
|
|
|
104
72
|
}
|
|
105
73
|
return false;
|
|
106
74
|
};
|
|
107
|
-
const
|
|
108
|
-
const
|
|
109
|
-
const
|
|
75
|
+
const detectPathAvailableAgents = () => {
|
|
76
|
+
const detected = [];
|
|
77
|
+
for (const [agent, binaries] of Object.entries(PATH_BINARIES)) if (binaries.some(isCommandAvailable)) detected.push(agent);
|
|
78
|
+
return detected;
|
|
79
|
+
};
|
|
80
|
+
const detectAvailableAgents = async () => {
|
|
81
|
+
const detected = new Set([...detectPathAvailableAgents(), ...await detectInstalledSkillAgents()]);
|
|
82
|
+
return getSkillAgentTypes().filter((agent) => agent !== "universal" && detected.has(agent));
|
|
83
|
+
};
|
|
110
84
|
//#endregion
|
|
111
85
|
//#region src/utils/highlighter.ts
|
|
112
86
|
const highlighter = {
|
|
@@ -117,19 +91,6 @@ const highlighter = {
|
|
|
117
91
|
dim: pc.dim
|
|
118
92
|
};
|
|
119
93
|
//#endregion
|
|
120
|
-
//#region src/utils/install-skill-for-agent.ts
|
|
121
|
-
const installSkillForAgent = (projectRoot, agent, skillSourceDirectory, skillName, alreadyInstalledDirectories) => {
|
|
122
|
-
const installedSkillDirectory = path.join(projectRoot, toSkillDir(agent), skillName);
|
|
123
|
-
if (alreadyInstalledDirectories?.has(installedSkillDirectory)) return installedSkillDirectory;
|
|
124
|
-
rmSync(installedSkillDirectory, {
|
|
125
|
-
recursive: true,
|
|
126
|
-
force: true
|
|
127
|
-
});
|
|
128
|
-
mkdirSync(path.dirname(installedSkillDirectory), { recursive: true });
|
|
129
|
-
cpSync(skillSourceDirectory, installedSkillDirectory, { recursive: true });
|
|
130
|
-
return installedSkillDirectory;
|
|
131
|
-
};
|
|
132
|
-
//#endregion
|
|
133
94
|
//#region src/utils/logger.ts
|
|
134
95
|
let isSilent$1 = false;
|
|
135
96
|
const setLoggerSilent = (silent) => {
|
|
@@ -274,31 +235,34 @@ const spinner = (text) => ({ start() {
|
|
|
274
235
|
return handle;
|
|
275
236
|
} });
|
|
276
237
|
//#endregion
|
|
238
|
+
//#region src/utils/to-display-name.ts
|
|
239
|
+
const toDisplayName = (agent) => getSkillAgentConfig(agent).displayName;
|
|
240
|
+
//#endregion
|
|
277
241
|
//#region src/install-skill.ts
|
|
278
|
-
const SKILL_NAME = "react-doctor";
|
|
279
242
|
const getSkillSourceDirectory = () => {
|
|
280
243
|
const distDirectory = path.dirname(fileURLToPath(import.meta.url));
|
|
281
244
|
return path.join(distDirectory, "skills", SKILL_NAME);
|
|
282
245
|
};
|
|
283
246
|
const runInstallSkill = async (options = {}) => {
|
|
284
|
-
const projectRoot = process.cwd();
|
|
285
|
-
const sourceDir = getSkillSourceDirectory();
|
|
286
|
-
if (!existsSync(path.join(sourceDir,
|
|
247
|
+
const projectRoot = options.projectRoot ?? process.cwd();
|
|
248
|
+
const sourceDir = options.sourceDir ?? getSkillSourceDirectory();
|
|
249
|
+
if (!existsSync(path.join(sourceDir, SKILL_MANIFEST_FILE))) {
|
|
287
250
|
logger.error(`Could not locate the ${SKILL_NAME} skill bundled with this package.`);
|
|
288
251
|
process.exitCode = 1;
|
|
289
252
|
return;
|
|
290
253
|
}
|
|
291
|
-
const detectedAgents = detectAvailableAgents();
|
|
254
|
+
const detectedAgents = options.detectedAgents ?? await detectAvailableAgents();
|
|
292
255
|
if (detectedAgents.length === 0) {
|
|
293
|
-
logger.error("No supported coding agents detected
|
|
294
|
-
logger.dim("
|
|
256
|
+
logger.error("No supported coding agents detected.");
|
|
257
|
+
logger.dim(" Looked for binaries on PATH (claude, codex, cursor, droid, gemini, copilot, opencode, pi)");
|
|
258
|
+
logger.dim(" and config dirs in $HOME (~/.claude, ~/.cursor, ~/.codex, ~/.gemini, ...).");
|
|
295
259
|
process.exitCode = 1;
|
|
296
260
|
return;
|
|
297
261
|
}
|
|
298
262
|
const selectedAgents = Boolean(options.yes) || !process.stdin.isTTY ? detectedAgents : (await prompts({
|
|
299
263
|
type: "multiselect",
|
|
300
264
|
name: "agents",
|
|
301
|
-
message: `Install the ${highlighter.info(
|
|
265
|
+
message: `Install the ${highlighter.info("react-doctor")} skill for:`,
|
|
302
266
|
choices: detectedAgents.map((agent) => ({
|
|
303
267
|
title: toDisplayName(agent),
|
|
304
268
|
value: agent,
|
|
@@ -315,12 +279,20 @@ const runInstallSkill = async (options = {}) => {
|
|
|
315
279
|
return;
|
|
316
280
|
}
|
|
317
281
|
const installSpinner = spinner(`Installing ${SKILL_NAME} skill...`).start();
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
282
|
+
try {
|
|
283
|
+
const installResult = await installSkillsFromSource({
|
|
284
|
+
source: sourceDir,
|
|
285
|
+
agents: selectedAgents,
|
|
286
|
+
cwd: projectRoot,
|
|
287
|
+
mode: "copy"
|
|
288
|
+
});
|
|
289
|
+
if (installResult.skills.length === 0) throw new Error(`Could not parse ${SKILL_MANIFEST_FILE} for ${SKILL_NAME} (missing or invalid frontmatter).`);
|
|
290
|
+
if (installResult.failed.length > 0) throw new Error(installResult.failed.map((failure) => `${toDisplayName(failure.agent)}: ${failure.error}`).join("\n"));
|
|
291
|
+
installSpinner.succeed(`${SKILL_NAME} skill installed for ${selectedAgents.map(toDisplayName).join(", ")}.`);
|
|
292
|
+
} catch (error) {
|
|
293
|
+
installSpinner.fail(`Failed to install ${SKILL_NAME} skill.`);
|
|
294
|
+
throw error;
|
|
322
295
|
}
|
|
323
|
-
installSpinner.succeed(`${SKILL_NAME} skill installed for ${selectedAgents.map(toDisplayName).join(", ")}.`);
|
|
324
296
|
};
|
|
325
297
|
//#endregion
|
|
326
298
|
//#region src/core/calculate-score-locally.ts
|
|
@@ -3358,7 +3330,7 @@ const promptProjectSelection = async (workspacePackages, rootDirectory) => {
|
|
|
3358
3330
|
};
|
|
3359
3331
|
//#endregion
|
|
3360
3332
|
//#region src/cli.ts
|
|
3361
|
-
const VERSION = "0.0.
|
|
3333
|
+
const VERSION = "0.0.45";
|
|
3362
3334
|
const VALID_FAIL_ON_LEVELS = new Set([
|
|
3363
3335
|
"error",
|
|
3364
3336
|
"warning",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "react-doctor",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.45",
|
|
4
4
|
"description": "Diagnose and fix React codebases for security, performance, correctness, accessibility, bundle-size, and architecture issues",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"accessibility",
|
|
@@ -62,6 +62,7 @@
|
|
|
62
62
|
}
|
|
63
63
|
},
|
|
64
64
|
"dependencies": {
|
|
65
|
+
"agent-install": "0.0.4",
|
|
65
66
|
"commander": "^14.0.3",
|
|
66
67
|
"knip": "^6.3.1",
|
|
67
68
|
"ora": "^9.3.0",
|