@planu/cli 4.3.7 → 4.3.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/dist/engine/auto-updater/config-patcher.d.ts +1 -0
- package/dist/engine/auto-updater/config-patcher.js +3 -0
- package/dist/engine/config-health/index.d.ts +1 -0
- package/dist/engine/config-health/index.js +7 -1
- package/dist/engine/config-health/mcp-config-checker.d.ts +3 -0
- package/dist/engine/config-health/mcp-config-checker.js +144 -0
- package/dist/engine/mcp-config/mcp-config-writer.d.ts +1 -1
- package/dist/engine/mcp-config/mcp-config-writer.js +6 -3
- package/dist/hosts/codex/config-scaffold.js +1 -0
- package/dist/tools/check-config-health.js +2 -2
- package/dist/tools/validate.js +31 -4
- package/dist/transports/transport-factory.js +31 -0
- package/dist/types/analysis-detection.d.ts +10 -2
- package/dist/types/auto-update.d.ts +1 -0
- package/package.json +9 -9
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
// SPEC-443
|
|
4
4
|
// Creates a backup before any modification. Restores backup on write failure.
|
|
5
5
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
6
|
+
export const PLANU_MCP_STARTUP_TIMEOUT_SEC = 120;
|
|
6
7
|
// ---------------------------------------------------------------------------
|
|
7
8
|
// Helpers
|
|
8
9
|
// ---------------------------------------------------------------------------
|
|
@@ -64,6 +65,7 @@ export function buildNpxLatestEntry() {
|
|
|
64
65
|
return {
|
|
65
66
|
command: 'npx',
|
|
66
67
|
args: ['-y', '@planu/cli@latest'],
|
|
68
|
+
startup_timeout_sec: PLANU_MCP_STARTUP_TIMEOUT_SEC,
|
|
67
69
|
};
|
|
68
70
|
}
|
|
69
71
|
/**
|
|
@@ -73,6 +75,7 @@ export function buildPinnedEntry(version) {
|
|
|
73
75
|
return {
|
|
74
76
|
command: 'npx',
|
|
75
77
|
args: ['-y', `@planu/cli@${version}`],
|
|
78
|
+
startup_timeout_sec: PLANU_MCP_STARTUP_TIMEOUT_SEC,
|
|
76
79
|
};
|
|
77
80
|
}
|
|
78
81
|
//# sourceMappingURL=config-patcher.js.map
|
|
@@ -6,6 +6,7 @@ export { checkCargoFeatures, checkClippyConfig, checkRustToolchain } from './rus
|
|
|
6
6
|
export { checkCiCommands, checkBuildArtifacts, checkVersionConsistency } from './ci-checker.js';
|
|
7
7
|
export { checkOtelVersionCompatibility } from './otel-checker.js';
|
|
8
8
|
export { checkBuildToolHealth } from './build-tool-checker.js';
|
|
9
|
+
export { checkMcpConfig } from './mcp-config-checker.js';
|
|
9
10
|
export type { ConfigHealthCheckGroup as CheckGroup } from '../../types/index.js';
|
|
10
11
|
/**
|
|
11
12
|
* Run all requested config health checkers in parallel and return a
|
|
@@ -7,6 +7,7 @@ import { checkCargoFeatures, checkClippyConfig, checkRustToolchain } from './rus
|
|
|
7
7
|
import { checkCiCommands, checkBuildArtifacts, checkVersionConsistency } from './ci-checker.js';
|
|
8
8
|
import { checkOtelVersionCompatibility } from './otel-checker.js';
|
|
9
9
|
import { checkBuildToolHealth } from './build-tool-checker.js';
|
|
10
|
+
import { checkMcpConfig } from './mcp-config-checker.js';
|
|
10
11
|
// Re-export individual checkers for direct use
|
|
11
12
|
export { checkTsCoverage, checkVitestSchema, checkJestSchema, checkPackageScripts, checkHuskyScripts, } from './ts-checker.js';
|
|
12
13
|
export { checkPyprojectSchema, checkMypyConfig, checkPythonScripts, checkRequirementsConflicts, } from './python-checker.js';
|
|
@@ -15,6 +16,7 @@ export { checkCargoFeatures, checkClippyConfig, checkRustToolchain } from './rus
|
|
|
15
16
|
export { checkCiCommands, checkBuildArtifacts, checkVersionConsistency } from './ci-checker.js';
|
|
16
17
|
export { checkOtelVersionCompatibility } from './otel-checker.js';
|
|
17
18
|
export { checkBuildToolHealth } from './build-tool-checker.js';
|
|
19
|
+
export { checkMcpConfig } from './mcp-config-checker.js';
|
|
18
20
|
const SEVERITY_ORDER = {
|
|
19
21
|
error: 0,
|
|
20
22
|
warning: 1,
|
|
@@ -32,7 +34,8 @@ const SEVERITY_ORDER = {
|
|
|
32
34
|
* @param minSeverity - Minimum severity level to include in findings (default: info).
|
|
33
35
|
*/
|
|
34
36
|
export async function runConfigHealthChecks(projectPath, checks, minSeverity = 'info') {
|
|
35
|
-
const activeChecks = checks ??
|
|
37
|
+
const activeChecks = checks ??
|
|
38
|
+
['ts', 'python', 'go', 'rust', 'ci', 'build-tool', 'mcp'];
|
|
36
39
|
const minOrder = SEVERITY_ORDER[minSeverity];
|
|
37
40
|
const checkPromises = [];
|
|
38
41
|
if (activeChecks.includes('ts')) {
|
|
@@ -56,6 +59,9 @@ export async function runConfigHealthChecks(projectPath, checks, minSeverity = '
|
|
|
56
59
|
if (activeChecks.includes('build-tool')) {
|
|
57
60
|
checkPromises.push(checkBuildToolHealth(projectPath));
|
|
58
61
|
}
|
|
62
|
+
if (activeChecks.includes('mcp')) {
|
|
63
|
+
checkPromises.push(checkMcpConfig(projectPath));
|
|
64
|
+
}
|
|
59
65
|
const results = await Promise.all(checkPromises);
|
|
60
66
|
const allFindings = results.flat();
|
|
61
67
|
// Filter by minimum severity
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
// engine/config-health/mcp-config-checker.ts — SPEC-1062
|
|
2
|
+
// Detects unsafe Planu MCP host configuration.
|
|
3
|
+
import { access, readFile } from 'node:fs/promises';
|
|
4
|
+
import { homedir } from 'node:os';
|
|
5
|
+
import { isAbsolute, join } from 'node:path';
|
|
6
|
+
async function fileExists(path) {
|
|
7
|
+
try {
|
|
8
|
+
await access(path);
|
|
9
|
+
return true;
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return false;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
async function readIfExists(path) {
|
|
16
|
+
try {
|
|
17
|
+
return await readFile(path, 'utf-8');
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function replacementFix() {
|
|
24
|
+
return 'Replace the Planu MCP entry with command "npx", args ["-y", "@planu/cli@latest"], and startup_timeout_sec 120 where the host supports it.';
|
|
25
|
+
}
|
|
26
|
+
async function checkPathTarget(configFile, target) {
|
|
27
|
+
if (!target || !isAbsolute(target) || (await fileExists(target))) {
|
|
28
|
+
return [];
|
|
29
|
+
}
|
|
30
|
+
return [
|
|
31
|
+
{
|
|
32
|
+
severity: 'error',
|
|
33
|
+
checker: 'mcp-config',
|
|
34
|
+
file: configFile,
|
|
35
|
+
message: `Planu MCP config points to missing path '${target}'`,
|
|
36
|
+
fix: replacementFix(),
|
|
37
|
+
},
|
|
38
|
+
];
|
|
39
|
+
}
|
|
40
|
+
async function checkJsonConfig(basePath, relativePath, displayPath = relativePath) {
|
|
41
|
+
const filePath = join(basePath, relativePath);
|
|
42
|
+
const raw = await readIfExists(filePath);
|
|
43
|
+
if (!raw) {
|
|
44
|
+
return [];
|
|
45
|
+
}
|
|
46
|
+
let parsed;
|
|
47
|
+
try {
|
|
48
|
+
parsed = JSON.parse(raw);
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return [];
|
|
52
|
+
}
|
|
53
|
+
const planu = parsed.mcpServers?.planu;
|
|
54
|
+
if (!planu) {
|
|
55
|
+
return [];
|
|
56
|
+
}
|
|
57
|
+
const findings = await checkPathTarget(displayPath, planu.command);
|
|
58
|
+
for (const arg of planu.args ?? []) {
|
|
59
|
+
findings.push(...(await checkPathTarget(displayPath, arg)));
|
|
60
|
+
}
|
|
61
|
+
return findings;
|
|
62
|
+
}
|
|
63
|
+
function extractPlanuCodexServer(content) {
|
|
64
|
+
const serverBlocks = content.split(/\n(?=\[\[mcp\.servers\]\])/);
|
|
65
|
+
return serverBlocks.find((block) => /name\s*=\s*"planu"/.test(block)) ?? null;
|
|
66
|
+
}
|
|
67
|
+
function extractTomlString(block, key) {
|
|
68
|
+
const match = new RegExp(`^${key}\\s*=\\s*"([^"]+)"`, 'm').exec(block);
|
|
69
|
+
return match?.[1];
|
|
70
|
+
}
|
|
71
|
+
function extractTomlStringArray(block, key) {
|
|
72
|
+
const match = new RegExp(`^${key}\\s*=\\s*\\[([^\\]]*)\\]`, 'm').exec(block);
|
|
73
|
+
const body = match?.[1];
|
|
74
|
+
if (!body) {
|
|
75
|
+
return [];
|
|
76
|
+
}
|
|
77
|
+
return [...body.matchAll(/"([^"]+)"/g)].map((item) => item[1] ?? '');
|
|
78
|
+
}
|
|
79
|
+
async function checkCodexConfig(basePath, relativePath, displayPath = relativePath) {
|
|
80
|
+
const raw = await readIfExists(join(basePath, relativePath));
|
|
81
|
+
if (!raw) {
|
|
82
|
+
return [];
|
|
83
|
+
}
|
|
84
|
+
const planuBlock = extractPlanuCodexServer(raw);
|
|
85
|
+
if (!planuBlock) {
|
|
86
|
+
return [];
|
|
87
|
+
}
|
|
88
|
+
const findings = await checkPathTarget(displayPath, extractTomlString(planuBlock, 'command'));
|
|
89
|
+
for (const arg of extractTomlStringArray(planuBlock, 'args')) {
|
|
90
|
+
findings.push(...(await checkPathTarget(displayPath, arg)));
|
|
91
|
+
}
|
|
92
|
+
const timeoutMatch = /^startup_timeout_sec\s*=\s*(\d+)/m.exec(planuBlock);
|
|
93
|
+
const timeout = timeoutMatch?.[1] ? Number(timeoutMatch[1]) : 0;
|
|
94
|
+
if (timeout < 60) {
|
|
95
|
+
findings.push({
|
|
96
|
+
severity: 'warning',
|
|
97
|
+
checker: 'mcp-config',
|
|
98
|
+
file: displayPath,
|
|
99
|
+
message: 'Planu Codex MCP config is missing startup_timeout_sec >= 60',
|
|
100
|
+
fix: 'Add startup_timeout_sec = 120 to the planu MCP server block.',
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
return findings;
|
|
104
|
+
}
|
|
105
|
+
function uniqueLocations(locations) {
|
|
106
|
+
const seen = new Set();
|
|
107
|
+
return locations.filter((location) => {
|
|
108
|
+
const key = join(location.basePath, location.relativePath);
|
|
109
|
+
if (seen.has(key)) {
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
seen.add(key);
|
|
113
|
+
return true;
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
export async function checkMcpConfig(projectPath, options = {}) {
|
|
117
|
+
const homePath = options.homePath ?? homedir();
|
|
118
|
+
const jsonLocations = uniqueLocations([
|
|
119
|
+
{ basePath: projectPath, relativePath: '.mcp.json' },
|
|
120
|
+
{ basePath: projectPath, relativePath: '.claude.json' },
|
|
121
|
+
{ basePath: projectPath, relativePath: '.claude/claude.json' },
|
|
122
|
+
{ basePath: homePath, relativePath: '.claude.json', displayPath: '~/.claude.json' },
|
|
123
|
+
{
|
|
124
|
+
basePath: homePath,
|
|
125
|
+
relativePath: '.claude/claude.json',
|
|
126
|
+
displayPath: '~/.claude/claude.json',
|
|
127
|
+
},
|
|
128
|
+
]);
|
|
129
|
+
const codexLocations = uniqueLocations([
|
|
130
|
+
{ basePath: projectPath, relativePath: '.openai/config.toml' },
|
|
131
|
+
{ basePath: homePath, relativePath: '.codex/config.toml', displayPath: '~/.codex/config.toml' },
|
|
132
|
+
{
|
|
133
|
+
basePath: homePath,
|
|
134
|
+
relativePath: '.openai/config.toml',
|
|
135
|
+
displayPath: '~/.openai/config.toml',
|
|
136
|
+
},
|
|
137
|
+
]);
|
|
138
|
+
const results = await Promise.all([
|
|
139
|
+
...jsonLocations.map((location) => checkJsonConfig(location.basePath, location.relativePath, location.displayPath)),
|
|
140
|
+
...codexLocations.map((location) => checkCodexConfig(location.basePath, location.relativePath, location.displayPath)),
|
|
141
|
+
]);
|
|
142
|
+
return results.flat();
|
|
143
|
+
}
|
|
144
|
+
//# sourceMappingURL=mcp-config-checker.js.map
|
|
@@ -9,7 +9,7 @@ import type { CheckAndFixResult } from '../../types/mcp-config.js';
|
|
|
9
9
|
*/
|
|
10
10
|
export declare function findMcpConfigPath(projectPath: string): Promise<string>;
|
|
11
11
|
/**
|
|
12
|
-
* Returns true if the config already has a `planu` entry pointing to @planu/cli@latest.
|
|
12
|
+
* Returns true if the config already has a safe `planu` entry pointing to @planu/cli@latest.
|
|
13
13
|
*/
|
|
14
14
|
export declare function isPlanuDirectlyConfigured(configPath: string): Promise<boolean>;
|
|
15
15
|
/**
|
|
@@ -4,11 +4,13 @@
|
|
|
4
4
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
5
5
|
import { join } from 'node:path';
|
|
6
6
|
import { homedir } from 'node:os';
|
|
7
|
+
import { buildNpxLatestEntry } from '../auto-updater/config-patcher.js';
|
|
7
8
|
// ---------------------------------------------------------------------------
|
|
8
9
|
// Constants
|
|
9
10
|
// ---------------------------------------------------------------------------
|
|
10
11
|
const PLANU_KEY = 'planu';
|
|
11
12
|
const PLANU_LATEST_ARG = '@planu/cli@latest';
|
|
13
|
+
const MIN_STARTUP_TIMEOUT_SEC = 60;
|
|
12
14
|
// ---------------------------------------------------------------------------
|
|
13
15
|
// Helpers
|
|
14
16
|
// ---------------------------------------------------------------------------
|
|
@@ -60,7 +62,7 @@ export async function findMcpConfigPath(projectPath) {
|
|
|
60
62
|
return join(projectPath, '.mcp.json');
|
|
61
63
|
}
|
|
62
64
|
/**
|
|
63
|
-
* Returns true if the config already has a `planu` entry pointing to @planu/cli@latest.
|
|
65
|
+
* Returns true if the config already has a safe `planu` entry pointing to @planu/cli@latest.
|
|
64
66
|
*/
|
|
65
67
|
export async function isPlanuDirectlyConfigured(configPath) {
|
|
66
68
|
const config = await readJsonFile(configPath);
|
|
@@ -76,7 +78,8 @@ export async function isPlanuDirectlyConfigured(configPath) {
|
|
|
76
78
|
if (!Array.isArray(args)) {
|
|
77
79
|
return false;
|
|
78
80
|
}
|
|
79
|
-
return args.includes(PLANU_LATEST_ARG)
|
|
81
|
+
return (args.includes(PLANU_LATEST_ARG) &&
|
|
82
|
+
(planuEntry.startup_timeout_sec ?? 0) >= MIN_STARTUP_TIMEOUT_SEC);
|
|
80
83
|
}
|
|
81
84
|
/**
|
|
82
85
|
* Merges the direct Planu entry into the config file.
|
|
@@ -85,7 +88,7 @@ export async function isPlanuDirectlyConfigured(configPath) {
|
|
|
85
88
|
export async function injectDirectPlanuEntry(configPath, currentVersion, latestVersion) {
|
|
86
89
|
const existing = await readJsonFile(configPath);
|
|
87
90
|
const mcpServers = existing.mcpServers !== undefined ? { ...existing.mcpServers } : {};
|
|
88
|
-
mcpServers[PLANU_KEY] =
|
|
91
|
+
mcpServers[PLANU_KEY] = buildNpxLatestEntry();
|
|
89
92
|
const updated = {
|
|
90
93
|
...existing,
|
|
91
94
|
mcpServers,
|
|
@@ -9,10 +9,10 @@ import { safeTracked } from './safe-handler.js';
|
|
|
9
9
|
const checkConfigHealthSchema = {
|
|
10
10
|
projectPath: z.string().max(4096).describe('Absolute path to the project root to check'),
|
|
11
11
|
checks: z
|
|
12
|
-
.array(z.enum(['ts', 'python', 'go', 'rust', 'ci', 'build-tool']))
|
|
12
|
+
.array(z.enum(['ts', 'python', 'go', 'rust', 'ci', 'build-tool', 'mcp']))
|
|
13
13
|
.max(100)
|
|
14
14
|
.optional()
|
|
15
|
-
.describe('Subset of check groups to run. Default: all groups (ts, python, go, rust, ci, build-tool)'),
|
|
15
|
+
.describe('Subset of check groups to run. Default: all groups (ts, python, go, rust, ci, build-tool, mcp)'),
|
|
16
16
|
severity: z
|
|
17
17
|
.enum(['error', 'warning', 'info'])
|
|
18
18
|
.optional()
|
package/dist/tools/validate.js
CHANGED
|
@@ -392,9 +392,31 @@ function buildCompactValidateText(args) {
|
|
|
392
392
|
}
|
|
393
393
|
/** Allowlist: alphanumerics, spaces, and safe shell chars. Rejects ; | $ ` & < > */
|
|
394
394
|
const SAFE_COMMAND_RE = /^[\w\s./:@~=-]+$/;
|
|
395
|
+
const LINT_CHECK_TIMEOUT_MS = 50_000;
|
|
395
396
|
function isSafeCommand(cmd) {
|
|
396
397
|
return SAFE_COMMAND_RE.test(cmd);
|
|
397
398
|
}
|
|
399
|
+
function isCommandTimeout(err) {
|
|
400
|
+
if (!(err instanceof Error)) {
|
|
401
|
+
return false;
|
|
402
|
+
}
|
|
403
|
+
const errorWithProcessFields = err;
|
|
404
|
+
return (errorWithProcessFields.signal === 'SIGTERM' ||
|
|
405
|
+
errorWithProcessFields.killed === true ||
|
|
406
|
+
errorWithProcessFields.code === 'ETIMEDOUT' ||
|
|
407
|
+
/timed out|timeout/i.test(err.message));
|
|
408
|
+
}
|
|
409
|
+
function commandOutput(err) {
|
|
410
|
+
if (!(err instanceof Error)) {
|
|
411
|
+
return String(err);
|
|
412
|
+
}
|
|
413
|
+
const errorWithOutput = err;
|
|
414
|
+
return [errorWithOutput.stdout, errorWithOutput.stderr]
|
|
415
|
+
.filter((chunk) => chunk !== undefined)
|
|
416
|
+
.map((chunk) => String(chunk))
|
|
417
|
+
.join('\n')
|
|
418
|
+
.trim();
|
|
419
|
+
}
|
|
398
420
|
function runLintCheck(projectPath, lintCommand) {
|
|
399
421
|
if (lintCommand !== null && !isSafeCommand(lintCommand)) {
|
|
400
422
|
console.warn(`[Planu] validate: lintCommand contains unsafe characters — skipping execution`);
|
|
@@ -413,14 +435,19 @@ function runLintCheck(projectPath, lintCommand) {
|
|
|
413
435
|
execSync(command, {
|
|
414
436
|
cwd: projectPath,
|
|
415
437
|
stdio: ['pipe', 'pipe', 'pipe'],
|
|
416
|
-
timeout:
|
|
438
|
+
timeout: LINT_CHECK_TIMEOUT_MS,
|
|
417
439
|
});
|
|
418
440
|
return { passed: true, command, issueCount: 0, output: '' };
|
|
419
441
|
}
|
|
420
442
|
catch (err) {
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
443
|
+
if (isCommandTimeout(err)) {
|
|
444
|
+
const seconds = Math.round(LINT_CHECK_TIMEOUT_MS / 1000);
|
|
445
|
+
const output = `Lint command timed out after ${String(seconds)}s and was skipped so validate can complete within MCP request limits. ` +
|
|
446
|
+
`Run \`${command}\` manually before marking the spec done.`;
|
|
447
|
+
console.warn(`[Planu] lintCheck timed out: command="${command}" cwd="${projectPath}" timeoutMs=${String(LINT_CHECK_TIMEOUT_MS)}`);
|
|
448
|
+
return { passed: true, command, issueCount: 0, output };
|
|
449
|
+
}
|
|
450
|
+
const raw = commandOutput(err);
|
|
424
451
|
// SPEC-787: never truncate — preserve full output so caller sees real issues
|
|
425
452
|
const output = raw.trim();
|
|
426
453
|
const issueCount = parseLintIssueCount(output);
|
|
@@ -41,10 +41,41 @@ export async function selectTransport(server, args, serverFactory) {
|
|
|
41
41
|
if (config.transport === 'stdio') {
|
|
42
42
|
const transport = new StdioServerTransport();
|
|
43
43
|
await server.connect(transport);
|
|
44
|
+
installStdioShutdownHandlers(server);
|
|
44
45
|
return;
|
|
45
46
|
}
|
|
46
47
|
// HTTP mode: each session needs its own McpServer instance
|
|
47
48
|
const factory = serverFactory ?? (() => server);
|
|
48
49
|
await createHttpTransport(factory, config);
|
|
49
50
|
}
|
|
51
|
+
function installStdioShutdownHandlers(server) {
|
|
52
|
+
let shuttingDown = false;
|
|
53
|
+
const shutdown = (reason) => {
|
|
54
|
+
if (shuttingDown) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
shuttingDown = true;
|
|
58
|
+
console.error(`[Planu] MCP stdio ${reason}; shutting down.`);
|
|
59
|
+
void server
|
|
60
|
+
.close()
|
|
61
|
+
.catch((err) => {
|
|
62
|
+
console.error('[Planu] Error while closing MCP server:', err);
|
|
63
|
+
})
|
|
64
|
+
.finally(() => {
|
|
65
|
+
process.exit(0);
|
|
66
|
+
});
|
|
67
|
+
};
|
|
68
|
+
process.stdin.once('end', () => {
|
|
69
|
+
shutdown('stdin ended');
|
|
70
|
+
});
|
|
71
|
+
process.stdin.once('close', () => {
|
|
72
|
+
shutdown('stdin closed');
|
|
73
|
+
});
|
|
74
|
+
process.once('SIGTERM', () => {
|
|
75
|
+
shutdown('received SIGTERM');
|
|
76
|
+
});
|
|
77
|
+
process.once('SIGINT', () => {
|
|
78
|
+
shutdown('received SIGINT');
|
|
79
|
+
});
|
|
80
|
+
}
|
|
50
81
|
//# sourceMappingURL=transport-factory.js.map
|
|
@@ -63,7 +63,7 @@ export interface SloConfig {
|
|
|
63
63
|
errorRatePercent: number;
|
|
64
64
|
windowMinutes: number;
|
|
65
65
|
}
|
|
66
|
-
export type ConfigHealthChecker = 'ts-coverage' | 'vitest-schema' | 'jest-schema' | 'husky-scripts' | 'package-scripts' | 'pyproject-schema' | 'mypy-config' | 'go-mod-imports' | 'golangci-config' | 'cargo-features' | 'clippy-config' | 'ci-commands' | 'build-artifacts' | 'otel-version' | 'build-tool';
|
|
66
|
+
export type ConfigHealthChecker = 'ts-coverage' | 'vitest-schema' | 'jest-schema' | 'husky-scripts' | 'package-scripts' | 'pyproject-schema' | 'mypy-config' | 'go-mod-imports' | 'golangci-config' | 'cargo-features' | 'clippy-config' | 'ci-commands' | 'build-artifacts' | 'mcp-config' | 'otel-version' | 'build-tool';
|
|
67
67
|
export interface ConfigHealthFinding {
|
|
68
68
|
severity: 'error' | 'warning' | 'info';
|
|
69
69
|
checker: ConfigHealthChecker;
|
|
@@ -72,6 +72,14 @@ export interface ConfigHealthFinding {
|
|
|
72
72
|
message: string;
|
|
73
73
|
fix: string;
|
|
74
74
|
}
|
|
75
|
+
export interface JsonMcpHealthEntry {
|
|
76
|
+
command?: string;
|
|
77
|
+
args?: string[];
|
|
78
|
+
startup_timeout_sec?: number;
|
|
79
|
+
}
|
|
80
|
+
export interface McpConfigCheckOptions {
|
|
81
|
+
homePath?: string;
|
|
82
|
+
}
|
|
75
83
|
export interface ConfigHealthReport {
|
|
76
84
|
summary: {
|
|
77
85
|
errors: number;
|
|
@@ -81,6 +89,6 @@ export interface ConfigHealthReport {
|
|
|
81
89
|
findings: ConfigHealthFinding[];
|
|
82
90
|
blocksDoR: boolean;
|
|
83
91
|
}
|
|
84
|
-
export type ConfigHealthCheckGroup = 'ts' | 'python' | 'go' | 'rust' | 'ci' | 'build-tool';
|
|
92
|
+
export type ConfigHealthCheckGroup = 'ts' | 'python' | 'go' | 'rust' | 'ci' | 'build-tool' | 'mcp';
|
|
85
93
|
export type ConfigHealthSeverityFilter = 'error' | 'warning' | 'info';
|
|
86
94
|
//# sourceMappingURL=analysis-detection.d.ts.map
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@planu/cli",
|
|
3
|
-
"version": "4.3.
|
|
3
|
+
"version": "4.3.8",
|
|
4
4
|
"description": "Planu — MCP Server for Spec Driven Development with native Rust acceleration for hot paths. Cross-platform (Linux/macOS/Windows, x64/arm64, glibc/musl).",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -32,14 +32,14 @@
|
|
|
32
32
|
"packageName": "@planu/core"
|
|
33
33
|
},
|
|
34
34
|
"optionalDependencies": {
|
|
35
|
-
"@planu/core-darwin-arm64": "4.3.
|
|
36
|
-
"@planu/core-darwin-x64": "4.3.
|
|
37
|
-
"@planu/core-linux-arm64-gnu": "4.3.
|
|
38
|
-
"@planu/core-linux-arm64-musl": "4.3.
|
|
39
|
-
"@planu/core-linux-x64-gnu": "4.3.
|
|
40
|
-
"@planu/core-linux-x64-musl": "4.3.
|
|
41
|
-
"@planu/core-win32-arm64-msvc": "4.3.
|
|
42
|
-
"@planu/core-win32-x64-msvc": "4.3.
|
|
35
|
+
"@planu/core-darwin-arm64": "4.3.8",
|
|
36
|
+
"@planu/core-darwin-x64": "4.3.8",
|
|
37
|
+
"@planu/core-linux-arm64-gnu": "4.3.8",
|
|
38
|
+
"@planu/core-linux-arm64-musl": "4.3.8",
|
|
39
|
+
"@planu/core-linux-x64-gnu": "4.3.8",
|
|
40
|
+
"@planu/core-linux-x64-musl": "4.3.8",
|
|
41
|
+
"@planu/core-win32-arm64-msvc": "4.3.8",
|
|
42
|
+
"@planu/core-win32-x64-msvc": "4.3.8"
|
|
43
43
|
},
|
|
44
44
|
"engines": {
|
|
45
45
|
"node": ">=24.0.0"
|