@curdx/flow 2.3.0 → 2.3.2
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/.claude-plugin/marketplace.json +2 -2
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +6 -0
- package/README.md +4 -0
- package/cli/doctor-workflow.js +77 -0
- package/cli/install-next-steps.js +2 -0
- package/cli/lib/doctor-claude-settings.js +186 -0
- package/cli/lib/doctor-report.js +194 -0
- package/package.json +1 -1
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
},
|
|
7
7
|
"metadata": {
|
|
8
8
|
"description": "Claude Code Discipline Layer — spec-driven workflow + goal-backward verification + Karpathy 4 principles enforced via gates. Stops Claude from faking \"done\" on non-trivial features.",
|
|
9
|
-
"version": "2.3.
|
|
9
|
+
"version": "2.3.2"
|
|
10
10
|
},
|
|
11
11
|
"allowCrossMarketplaceDependenciesOn": [
|
|
12
12
|
"context7-marketplace"
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
"name": "curdx-flow",
|
|
17
17
|
"source": "./",
|
|
18
18
|
"description": "Claude Code Discipline Layer — spec-driven workflow + goal-backward verification + Karpathy 4 principles enforced via gates. Stops Claude from faking \"done\" on non-trivial features.",
|
|
19
|
-
"version": "2.3.
|
|
19
|
+
"version": "2.3.2",
|
|
20
20
|
"author": {
|
|
21
21
|
"name": "wdx",
|
|
22
22
|
"email": "bydongxin@gmail.com"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "curdx-flow",
|
|
3
|
-
"version": "2.3.
|
|
3
|
+
"version": "2.3.2",
|
|
4
4
|
"description": "Claude Code Discipline Layer — spec-driven workflow + goal-backward verification + Karpathy 4 principles enforced via gates. Stops Claude from faking \"done\" on non-trivial features.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "wdx",
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 2.3.1
|
|
4
|
+
|
|
5
|
+
- expanded `doctor` to report CurDX-Flow’s bundled main-thread agent, monitor surface, and plugin option defaults
|
|
6
|
+
- documented where Claude Code stores CurDX-Flow non-sensitive `userConfig` values in `pluginConfigs`
|
|
7
|
+
- added troubleshooting guidance for stop-hook blocking and plugin monitor behavior
|
|
8
|
+
|
|
3
9
|
## 2.3.0
|
|
4
10
|
|
|
5
11
|
- added a default `flow-orchestrator` main-thread agent through plugin-level `settings.json`
|
package/README.md
CHANGED
|
@@ -22,6 +22,10 @@ Requires modern Claude Code and Node 18+. The install flow registers the plugin,
|
|
|
22
22
|
required reasoning/doc tools, and recommended companion plugins. Run
|
|
23
23
|
`npx @curdx/flow doctor` after install if anything looks off.
|
|
24
24
|
|
|
25
|
+
After restart, CurDX-Flow routes the main thread through `flow-orchestrator`
|
|
26
|
+
by default and starts the bundled `.flow` progress monitor in interactive
|
|
27
|
+
Claude Code sessions.
|
|
28
|
+
|
|
25
29
|
## 11 slash commands, that's it
|
|
26
30
|
|
|
27
31
|
```text
|
package/cli/doctor-workflow.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
2
|
import path from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
3
4
|
|
|
4
5
|
import { readProjectClaudeSettings } from "./lib/doctor-claude-settings.js";
|
|
5
6
|
import { inspectRuntimeEnvironment } from "./lib/doctor-runtime-environment.js";
|
|
@@ -19,6 +20,8 @@ import {
|
|
|
19
20
|
export { readProjectClaudeSettings };
|
|
20
21
|
export { inspectRuntimeEnvironment };
|
|
21
22
|
|
|
23
|
+
const PACKAGE_ROOT = fileURLToPath(new URL("../", import.meta.url));
|
|
24
|
+
|
|
22
25
|
export function createDoctorContext(args = []) {
|
|
23
26
|
return {
|
|
24
27
|
fix: args.includes("--fix"),
|
|
@@ -150,6 +153,78 @@ export async function readProjectTeamConfig(cwd = process.cwd()) {
|
|
|
150
153
|
return state;
|
|
151
154
|
}
|
|
152
155
|
|
|
156
|
+
export async function readBundledPluginRuntimeDefaults(packageRoot = PACKAGE_ROOT) {
|
|
157
|
+
const pluginSettingsPath = path.join(packageRoot, "settings.json");
|
|
158
|
+
const pluginManifestPath = path.join(packageRoot, ".claude-plugin", "plugin.json");
|
|
159
|
+
const monitorsPath = path.join(packageRoot, "monitors", "monitors.json");
|
|
160
|
+
|
|
161
|
+
const state = {
|
|
162
|
+
exists: false,
|
|
163
|
+
defaultAgent: null,
|
|
164
|
+
subagentStatusLineCommand: null,
|
|
165
|
+
monitorCount: 0,
|
|
166
|
+
monitors: [],
|
|
167
|
+
userConfig: [],
|
|
168
|
+
};
|
|
169
|
+
|
|
170
|
+
let pluginSettings = null;
|
|
171
|
+
let pluginManifest = null;
|
|
172
|
+
let monitors = null;
|
|
173
|
+
|
|
174
|
+
try {
|
|
175
|
+
pluginSettings = JSON.parse(await fs.readFile(pluginSettingsPath, "utf-8"));
|
|
176
|
+
} catch {
|
|
177
|
+
pluginSettings = null;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
pluginManifest = JSON.parse(await fs.readFile(pluginManifestPath, "utf-8"));
|
|
182
|
+
} catch {
|
|
183
|
+
pluginManifest = null;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
try {
|
|
187
|
+
monitors = JSON.parse(await fs.readFile(monitorsPath, "utf-8"));
|
|
188
|
+
} catch {
|
|
189
|
+
monitors = null;
|
|
190
|
+
}
|
|
191
|
+
|
|
192
|
+
if (!pluginSettings && !pluginManifest && !monitors) {
|
|
193
|
+
return state;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
state.exists = true;
|
|
197
|
+
state.defaultAgent = typeof pluginSettings?.agent === "string" ? pluginSettings.agent : null;
|
|
198
|
+
state.subagentStatusLineCommand =
|
|
199
|
+
typeof pluginSettings?.subagentStatusLine?.command === "string"
|
|
200
|
+
? pluginSettings.subagentStatusLine.command
|
|
201
|
+
: null;
|
|
202
|
+
|
|
203
|
+
if (Array.isArray(monitors)) {
|
|
204
|
+
state.monitorCount = monitors.length;
|
|
205
|
+
state.monitors = monitors
|
|
206
|
+
.filter((entry) => entry && typeof entry === "object")
|
|
207
|
+
.map((entry) => ({
|
|
208
|
+
name: typeof entry.name === "string" ? entry.name : null,
|
|
209
|
+
when: typeof entry.when === "string" ? entry.when : "always",
|
|
210
|
+
}))
|
|
211
|
+
.filter((entry) => entry.name);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
if (pluginManifest?.userConfig && typeof pluginManifest.userConfig === "object") {
|
|
215
|
+
state.userConfig = Object.entries(pluginManifest.userConfig)
|
|
216
|
+
.filter(([, value]) => value && typeof value === "object" && !Array.isArray(value))
|
|
217
|
+
.map(([key, value]) => ({
|
|
218
|
+
key,
|
|
219
|
+
type: value.type ?? null,
|
|
220
|
+
default: Object.prototype.hasOwnProperty.call(value, "default") ? value.default : undefined,
|
|
221
|
+
sensitive: value.sensitive === true,
|
|
222
|
+
}));
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
return state;
|
|
226
|
+
}
|
|
227
|
+
|
|
153
228
|
export async function readProjectState(cwd = process.cwd()) {
|
|
154
229
|
const flowDir = path.join(cwd, ".flow");
|
|
155
230
|
try {
|
|
@@ -185,6 +260,7 @@ export async function collectDoctorData(
|
|
|
185
260
|
readProjectMcpConfigImpl = readProjectMcpConfig,
|
|
186
261
|
readProjectTeamConfigImpl = readProjectTeamConfig,
|
|
187
262
|
readProjectClaudeSettingsImpl = readProjectClaudeSettings,
|
|
263
|
+
readBundledPluginRuntimeDefaultsImpl = readBundledPluginRuntimeDefaults,
|
|
188
264
|
} = {}
|
|
189
265
|
) {
|
|
190
266
|
const claudeVersionValue = claudeVersionImpl();
|
|
@@ -212,6 +288,7 @@ export async function collectDoctorData(
|
|
|
212
288
|
projectMcpConfig: await readProjectMcpConfigImpl(cwd),
|
|
213
289
|
projectTeamConfig: await readProjectTeamConfigImpl(cwd),
|
|
214
290
|
projectClaudeSettings: await readProjectClaudeSettingsImpl(cwd),
|
|
291
|
+
bundledPluginRuntimeDefaults: await readBundledPluginRuntimeDefaultsImpl(),
|
|
215
292
|
};
|
|
216
293
|
}
|
|
217
294
|
|
|
@@ -11,6 +11,8 @@ export function printNextSteps() {
|
|
|
11
11
|
console.log(`${color.bold("Next steps")}:\n`);
|
|
12
12
|
console.log(` ${color.dim("# Verify health")}`);
|
|
13
13
|
console.log(` ${cliCmd} doctor\n`);
|
|
14
|
+
console.log(` ${color.dim("# Review the bundled main-agent / monitor defaults")}`);
|
|
15
|
+
console.log(` ${color.dim("# (doctor now prints CurDX-Flow runtime defaults and plugin option keys)")}\n`);
|
|
14
16
|
console.log(` ${color.dim("# Inside any project, initialize and start a feature spec")}`);
|
|
15
17
|
console.log(` ${color.cyan("cd ~/your-project")}`);
|
|
16
18
|
console.log(` ${color.cyan("claude")}`);
|
|
@@ -99,6 +99,23 @@ const CURDX_FLOW_REQUIRED_TOOLS = [
|
|
|
99
99
|
"Glob",
|
|
100
100
|
];
|
|
101
101
|
const CURDX_FLOW_PLUGIN_ID = "curdx-flow@curdx-flow-marketplace";
|
|
102
|
+
const CURDX_FLOW_PLUGIN_OPTIONS = {
|
|
103
|
+
autonomous_blocking: {
|
|
104
|
+
type: "boolean",
|
|
105
|
+
default: true,
|
|
106
|
+
},
|
|
107
|
+
daily_dependency_check: {
|
|
108
|
+
type: "boolean",
|
|
109
|
+
default: true,
|
|
110
|
+
},
|
|
111
|
+
monitor_interval_seconds: {
|
|
112
|
+
type: "number",
|
|
113
|
+
default: 8,
|
|
114
|
+
integer: true,
|
|
115
|
+
min: 3,
|
|
116
|
+
max: 60,
|
|
117
|
+
},
|
|
118
|
+
};
|
|
102
119
|
const CURDX_FLOW_REQUIRED_PLUGIN_IDS = ["context7-plugin@context7-marketplace"];
|
|
103
120
|
const HTTP_HOOK_SETTINGS = ["allowedHttpHookUrls", "httpHookAllowedEnvVars"];
|
|
104
121
|
const PERSISTED_EFFORT_LEVELS = ["low", "medium", "high", "xhigh"];
|
|
@@ -171,6 +188,171 @@ function pushScopedWarning(target, kind, message, scope = "project") {
|
|
|
171
188
|
target.push(warning);
|
|
172
189
|
}
|
|
173
190
|
|
|
191
|
+
function createCurdxFlowPluginOptionsState() {
|
|
192
|
+
const definitions = Object.entries(CURDX_FLOW_PLUGIN_OPTIONS).map(([key, value]) => ({
|
|
193
|
+
key,
|
|
194
|
+
type: value.type,
|
|
195
|
+
default: value.default,
|
|
196
|
+
min: value.min,
|
|
197
|
+
max: value.max,
|
|
198
|
+
integer: value.integer === true,
|
|
199
|
+
}));
|
|
200
|
+
|
|
201
|
+
const repoEffective = Object.fromEntries(
|
|
202
|
+
definitions.map((definition) => [
|
|
203
|
+
definition.key,
|
|
204
|
+
{ value: definition.default, source: "default" },
|
|
205
|
+
])
|
|
206
|
+
);
|
|
207
|
+
|
|
208
|
+
return {
|
|
209
|
+
pluginId: CURDX_FLOW_PLUGIN_ID,
|
|
210
|
+
definitions,
|
|
211
|
+
project: {
|
|
212
|
+
pluginConfigsPresent: false,
|
|
213
|
+
pluginConfigPresent: false,
|
|
214
|
+
optionsPresent: false,
|
|
215
|
+
overrides: {},
|
|
216
|
+
},
|
|
217
|
+
local: {
|
|
218
|
+
pluginConfigsPresent: false,
|
|
219
|
+
pluginConfigPresent: false,
|
|
220
|
+
optionsPresent: false,
|
|
221
|
+
overrides: {},
|
|
222
|
+
},
|
|
223
|
+
repoEffective,
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
function pluginScopeLabel(scope) {
|
|
228
|
+
return scope === "local" ? "settings.local.json" : "settings.json";
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope = "project") {
|
|
232
|
+
if (!isNonArrayObject(parsed) || !("pluginConfigs" in parsed)) {
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
const target = scope === "local" ? pluginOptionsState.local : pluginOptionsState.project;
|
|
237
|
+
const settingsLabel = pluginScopeLabel(scope);
|
|
238
|
+
target.pluginConfigsPresent = true;
|
|
239
|
+
|
|
240
|
+
if (!isNonArrayObject(parsed.pluginConfigs)) {
|
|
241
|
+
pushScopedWarning(
|
|
242
|
+
warnings,
|
|
243
|
+
scope === "local" ? "invalid-local-setting" : "invalid-project-setting",
|
|
244
|
+
`pluginConfigs in ${settingsLabel} must be an object keyed by plugin@marketplace id`,
|
|
245
|
+
scope
|
|
246
|
+
);
|
|
247
|
+
return;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
const pluginConfig = parsed.pluginConfigs[CURDX_FLOW_PLUGIN_ID];
|
|
251
|
+
if (pluginConfig === undefined) {
|
|
252
|
+
return;
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
target.pluginConfigPresent = true;
|
|
256
|
+
|
|
257
|
+
if (!isNonArrayObject(pluginConfig)) {
|
|
258
|
+
pushScopedWarning(
|
|
259
|
+
warnings,
|
|
260
|
+
scope === "local" ? "invalid-local-setting" : "invalid-project-setting",
|
|
261
|
+
`pluginConfigs[${CURDX_FLOW_PLUGIN_ID}] in ${settingsLabel} must be an object when set`,
|
|
262
|
+
scope
|
|
263
|
+
);
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
if (!("options" in pluginConfig)) {
|
|
268
|
+
return;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
target.optionsPresent = true;
|
|
272
|
+
|
|
273
|
+
if (!isNonArrayObject(pluginConfig.options)) {
|
|
274
|
+
pushScopedWarning(
|
|
275
|
+
warnings,
|
|
276
|
+
scope === "local" ? "invalid-local-setting" : "invalid-project-setting",
|
|
277
|
+
`pluginConfigs[${CURDX_FLOW_PLUGIN_ID}].options in ${settingsLabel} must be an object when set`,
|
|
278
|
+
scope
|
|
279
|
+
);
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
for (const [key, value] of Object.entries(pluginConfig.options)) {
|
|
284
|
+
const definition = CURDX_FLOW_PLUGIN_OPTIONS[key];
|
|
285
|
+
|
|
286
|
+
if (!definition) {
|
|
287
|
+
pushScopedWarning(
|
|
288
|
+
warnings,
|
|
289
|
+
"unknown-plugin-option",
|
|
290
|
+
`unknown CurDX-Flow plugin option in ${settingsLabel}: ${key}`,
|
|
291
|
+
scope
|
|
292
|
+
);
|
|
293
|
+
continue;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
if (definition.type === "boolean" && typeof value !== "boolean") {
|
|
297
|
+
pushScopedWarning(
|
|
298
|
+
warnings,
|
|
299
|
+
scope === "local" ? "invalid-local-setting" : "invalid-project-setting",
|
|
300
|
+
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be boolean`,
|
|
301
|
+
scope
|
|
302
|
+
);
|
|
303
|
+
continue;
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
if (definition.type === "number") {
|
|
307
|
+
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
308
|
+
pushScopedWarning(
|
|
309
|
+
warnings,
|
|
310
|
+
scope === "local" ? "invalid-local-setting" : "invalid-project-setting",
|
|
311
|
+
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be a number`,
|
|
312
|
+
scope
|
|
313
|
+
);
|
|
314
|
+
continue;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
if (definition.integer && !Number.isInteger(value)) {
|
|
318
|
+
pushScopedWarning(
|
|
319
|
+
warnings,
|
|
320
|
+
scope === "local" ? "invalid-local-setting" : "invalid-project-setting",
|
|
321
|
+
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be an integer`,
|
|
322
|
+
scope
|
|
323
|
+
);
|
|
324
|
+
continue;
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
if (typeof definition.min === "number" && value < definition.min) {
|
|
328
|
+
pushScopedWarning(
|
|
329
|
+
warnings,
|
|
330
|
+
scope === "local" ? "invalid-local-setting" : "invalid-project-setting",
|
|
331
|
+
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be between ${definition.min} and ${definition.max}`,
|
|
332
|
+
scope
|
|
333
|
+
);
|
|
334
|
+
continue;
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (typeof definition.max === "number" && value > definition.max) {
|
|
338
|
+
pushScopedWarning(
|
|
339
|
+
warnings,
|
|
340
|
+
scope === "local" ? "invalid-local-setting" : "invalid-project-setting",
|
|
341
|
+
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be between ${definition.min} and ${definition.max}`,
|
|
342
|
+
scope
|
|
343
|
+
);
|
|
344
|
+
continue;
|
|
345
|
+
}
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
target.overrides[key] = value;
|
|
349
|
+
pluginOptionsState.repoEffective[key] = {
|
|
350
|
+
value,
|
|
351
|
+
source: scope,
|
|
352
|
+
};
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
|
|
174
356
|
function auditLocalClaudeSettings(parsed, warnings) {
|
|
175
357
|
const scope = "local";
|
|
176
358
|
const permissions = parsed?.permissions && typeof parsed.permissions === "object"
|
|
@@ -374,6 +556,7 @@ export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
|
374
556
|
localInvalid: false,
|
|
375
557
|
localParseError: null,
|
|
376
558
|
localWarnings: [],
|
|
559
|
+
pluginOptions: createCurdxFlowPluginOptionsState(),
|
|
377
560
|
};
|
|
378
561
|
|
|
379
562
|
try {
|
|
@@ -407,6 +590,8 @@ export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
|
407
590
|
const allow = Array.isArray(permissions.allow) ? permissions.allow : [];
|
|
408
591
|
const deny = Array.isArray(permissions.deny) ? permissions.deny : [];
|
|
409
592
|
|
|
593
|
+
auditCurdxFlowPluginOptions(parsed, state.warnings, state.pluginOptions, "project");
|
|
594
|
+
|
|
410
595
|
if ("enabledPlugins" in parsed && (
|
|
411
596
|
!parsed.enabledPlugins ||
|
|
412
597
|
typeof parsed.enabledPlugins !== "object" ||
|
|
@@ -726,6 +911,7 @@ export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
|
726
911
|
if (state.localExists) {
|
|
727
912
|
try {
|
|
728
913
|
const localParsed = JSON.parse(await fs.readFile(localSettingsPath, "utf-8"));
|
|
914
|
+
auditCurdxFlowPluginOptions(localParsed, state.localWarnings, state.pluginOptions, "local");
|
|
729
915
|
auditLocalClaudeSettings(localParsed, state.localWarnings);
|
|
730
916
|
} catch (error) {
|
|
731
917
|
state.localInvalid = true;
|
package/cli/lib/doctor-report.js
CHANGED
|
@@ -14,6 +14,26 @@ function pluginErrorDetails(plugin) {
|
|
|
14
14
|
|
|
15
15
|
function projectSettingsWarningDetails(warning) {
|
|
16
16
|
if (warning?.scope === "local") {
|
|
17
|
+
if (warning.kind === "unknown-plugin-option") {
|
|
18
|
+
return [
|
|
19
|
+
"settings.local.json overrides shared project settings on this machine",
|
|
20
|
+
"remove the unknown key or rename it to a supported CurDX-Flow plugin option under pluginConfigs[curdx-flow@curdx-flow-marketplace].options",
|
|
21
|
+
];
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
if (
|
|
25
|
+
warning.kind === "invalid-local-setting" &&
|
|
26
|
+
(
|
|
27
|
+
warning.message?.includes("pluginConfigs[curdx-flow@curdx-flow-marketplace]") ||
|
|
28
|
+
warning.message?.includes("CurDX-Flow plugin option")
|
|
29
|
+
)
|
|
30
|
+
) {
|
|
31
|
+
return [
|
|
32
|
+
"CurDX-Flow plugin options in settings.local.json override project and user settings on this machine",
|
|
33
|
+
"use boolean values for autonomous_blocking and daily_dependency_check, and keep monitor_interval_seconds as an integer between 3 and 60",
|
|
34
|
+
];
|
|
35
|
+
}
|
|
36
|
+
|
|
17
37
|
if (warning.kind === "invalid-local-setting") {
|
|
18
38
|
return [
|
|
19
39
|
"settings.local.json is the highest-precedence repo-scoped settings surface on this machine",
|
|
@@ -41,6 +61,26 @@ function projectSettingsWarningDetails(warning) {
|
|
|
41
61
|
];
|
|
42
62
|
}
|
|
43
63
|
|
|
64
|
+
if (warning.kind === "unknown-plugin-option") {
|
|
65
|
+
return [
|
|
66
|
+
"CurDX-Flow only reads keys declared in the plugin manifest",
|
|
67
|
+
"remove the unknown key or rename it under pluginConfigs[curdx-flow@curdx-flow-marketplace].options",
|
|
68
|
+
];
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
if (
|
|
72
|
+
warning.kind === "invalid-project-setting" &&
|
|
73
|
+
(
|
|
74
|
+
warning.message?.includes("pluginConfigs[curdx-flow@curdx-flow-marketplace]") ||
|
|
75
|
+
warning.message?.includes("CurDX-Flow plugin option")
|
|
76
|
+
)
|
|
77
|
+
) {
|
|
78
|
+
return [
|
|
79
|
+
"CurDX-Flow non-sensitive plugin options belong under pluginConfigs[curdx-flow@curdx-flow-marketplace].options in .claude/settings.json",
|
|
80
|
+
"use boolean values for autonomous_blocking and daily_dependency_check, and keep monitor_interval_seconds as an integer between 3 and 60",
|
|
81
|
+
];
|
|
82
|
+
}
|
|
83
|
+
|
|
44
84
|
if (!warning?.kind) {
|
|
45
85
|
return [
|
|
46
86
|
"project settings are shared with collaborators",
|
|
@@ -116,6 +156,11 @@ function projectSettingsWarningDetails(warning) {
|
|
|
116
156
|
];
|
|
117
157
|
}
|
|
118
158
|
|
|
159
|
+
function formatInlineValue(value) {
|
|
160
|
+
if (typeof value === "string") return `"${value}"`;
|
|
161
|
+
return String(value);
|
|
162
|
+
}
|
|
163
|
+
|
|
119
164
|
export function buildDoctorReport({
|
|
120
165
|
claudeVersionValue,
|
|
121
166
|
nodeVersion,
|
|
@@ -130,6 +175,7 @@ export function buildDoctorReport({
|
|
|
130
175
|
projectMcpConfig,
|
|
131
176
|
projectTeamConfig,
|
|
132
177
|
projectClaudeSettings,
|
|
178
|
+
bundledPluginRuntimeDefaults,
|
|
133
179
|
}) {
|
|
134
180
|
const lines = [];
|
|
135
181
|
const sections = [];
|
|
@@ -345,6 +391,154 @@ export function buildDoctorReport({
|
|
|
345
391
|
}
|
|
346
392
|
}
|
|
347
393
|
|
|
394
|
+
if (bundledPluginRuntimeDefaults?.exists) {
|
|
395
|
+
const bundledRuntimeSection = createSection("CurDX-Flow bundled runtime:");
|
|
396
|
+
|
|
397
|
+
if (bundledPluginRuntimeDefaults.defaultAgent) {
|
|
398
|
+
pushSectionLine(
|
|
399
|
+
bundledRuntimeSection,
|
|
400
|
+
"ok",
|
|
401
|
+
`Main thread agent ${bundledPluginRuntimeDefaults.defaultAgent}`,
|
|
402
|
+
[
|
|
403
|
+
"plugin-level settings route the main Claude thread through this agent by default",
|
|
404
|
+
]
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
if (bundledPluginRuntimeDefaults.subagentStatusLineCommand) {
|
|
409
|
+
pushSectionLine(
|
|
410
|
+
bundledRuntimeSection,
|
|
411
|
+
"info",
|
|
412
|
+
`Subagent status line configured`,
|
|
413
|
+
[bundledPluginRuntimeDefaults.subagentStatusLineCommand]
|
|
414
|
+
);
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
if (bundledPluginRuntimeDefaults.monitorCount > 0) {
|
|
418
|
+
const monitorSummary = bundledPluginRuntimeDefaults.monitors
|
|
419
|
+
.map((monitor) => `${monitor.name} (${monitor.when})`)
|
|
420
|
+
.join(", ");
|
|
421
|
+
pushSectionLine(
|
|
422
|
+
bundledRuntimeSection,
|
|
423
|
+
"ok",
|
|
424
|
+
`Plugin monitors ${bundledPluginRuntimeDefaults.monitorCount} configured`,
|
|
425
|
+
[
|
|
426
|
+
monitorSummary,
|
|
427
|
+
"official docs: plugin monitors run only in interactive Claude Code sessions where the Monitor tool is available",
|
|
428
|
+
]
|
|
429
|
+
);
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
if ((bundledPluginRuntimeDefaults.userConfig || []).length > 0) {
|
|
433
|
+
const optionSummary = bundledPluginRuntimeDefaults.userConfig
|
|
434
|
+
.map((option) => {
|
|
435
|
+
const suffix = option.default === undefined ? "" : `=${String(option.default)}`;
|
|
436
|
+
return `${option.key}${suffix}`;
|
|
437
|
+
})
|
|
438
|
+
.join(", ");
|
|
439
|
+
pushSectionLine(
|
|
440
|
+
bundledRuntimeSection,
|
|
441
|
+
"info",
|
|
442
|
+
`Plugin options ${bundledPluginRuntimeDefaults.userConfig.length} available`,
|
|
443
|
+
[
|
|
444
|
+
optionSummary,
|
|
445
|
+
"non-sensitive values are stored under pluginConfigs[curdx-flow@curdx-flow-marketplace].options in Claude settings.json",
|
|
446
|
+
]
|
|
447
|
+
);
|
|
448
|
+
}
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
if (
|
|
452
|
+
bundledPluginRuntimeDefaults?.exists ||
|
|
453
|
+
projectClaudeSettings?.pluginOptions
|
|
454
|
+
) {
|
|
455
|
+
const configuredOptionsSection = createSection("CurDX-Flow configured options:");
|
|
456
|
+
const optionState = projectClaudeSettings?.pluginOptions;
|
|
457
|
+
const projectOverrides = optionState?.project?.overrides || {};
|
|
458
|
+
const localOverrides = optionState?.local?.overrides || {};
|
|
459
|
+
const repoEffective = optionState?.repoEffective || {};
|
|
460
|
+
const definitions = bundledPluginRuntimeDefaults?.userConfig?.length
|
|
461
|
+
? bundledPluginRuntimeDefaults.userConfig
|
|
462
|
+
: (optionState?.definitions || []);
|
|
463
|
+
|
|
464
|
+
pushSectionLine(
|
|
465
|
+
configuredOptionsSection,
|
|
466
|
+
"info",
|
|
467
|
+
"Scope precedence settings.local.json > settings.json > bundled default",
|
|
468
|
+
[
|
|
469
|
+
"this section audits repo-scoped CurDX-Flow plugin options only",
|
|
470
|
+
"user-level ~/.claude/settings.json is not inspected here",
|
|
471
|
+
]
|
|
472
|
+
);
|
|
473
|
+
|
|
474
|
+
if (Object.keys(projectOverrides).length > 0) {
|
|
475
|
+
const summary = Object.entries(projectOverrides)
|
|
476
|
+
.map(([key, value]) => `${key}=${formatInlineValue(value)}`)
|
|
477
|
+
.join(", ");
|
|
478
|
+
pushSectionLine(
|
|
479
|
+
configuredOptionsSection,
|
|
480
|
+
"info",
|
|
481
|
+
`.claude/settings.json ${Object.keys(projectOverrides).length} valid override(s)`,
|
|
482
|
+
[
|
|
483
|
+
summary,
|
|
484
|
+
"path: pluginConfigs[curdx-flow@curdx-flow-marketplace].options",
|
|
485
|
+
]
|
|
486
|
+
);
|
|
487
|
+
} else {
|
|
488
|
+
pushSectionLine(
|
|
489
|
+
configuredOptionsSection,
|
|
490
|
+
"info",
|
|
491
|
+
".claude/settings.json no CurDX-Flow repo override"
|
|
492
|
+
);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (projectClaudeSettings?.localExists) {
|
|
496
|
+
if (Object.keys(localOverrides).length > 0) {
|
|
497
|
+
const summary = Object.entries(localOverrides)
|
|
498
|
+
.map(([key, value]) => `${key}=${formatInlineValue(value)}`)
|
|
499
|
+
.join(", ");
|
|
500
|
+
pushSectionLine(
|
|
501
|
+
configuredOptionsSection,
|
|
502
|
+
"info",
|
|
503
|
+
`.claude/settings.local.json ${Object.keys(localOverrides).length} valid override(s)`,
|
|
504
|
+
[
|
|
505
|
+
summary,
|
|
506
|
+
"local overrides have higher precedence than .claude/settings.json on this machine",
|
|
507
|
+
]
|
|
508
|
+
);
|
|
509
|
+
} else {
|
|
510
|
+
pushSectionLine(
|
|
511
|
+
configuredOptionsSection,
|
|
512
|
+
"info",
|
|
513
|
+
".claude/settings.local.json present without valid CurDX-Flow overrides"
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
} else {
|
|
517
|
+
pushSectionLine(
|
|
518
|
+
configuredOptionsSection,
|
|
519
|
+
"info",
|
|
520
|
+
".claude/settings.local.json not present"
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
for (const definition of definitions) {
|
|
525
|
+
const effective = repoEffective[definition.key] || {
|
|
526
|
+
value: definition.default,
|
|
527
|
+
source: "default",
|
|
528
|
+
};
|
|
529
|
+
const sourceLabel = effective.source === "local"
|
|
530
|
+
? "local"
|
|
531
|
+
: effective.source === "project"
|
|
532
|
+
? "project"
|
|
533
|
+
: "bundled default";
|
|
534
|
+
pushSectionLine(
|
|
535
|
+
configuredOptionsSection,
|
|
536
|
+
"ok",
|
|
537
|
+
`${definition.key.padEnd(22)} ${formatInlineValue(effective.value)} (${sourceLabel})`
|
|
538
|
+
);
|
|
539
|
+
}
|
|
540
|
+
}
|
|
541
|
+
|
|
348
542
|
const localProjectSection = createSection("Local project:");
|
|
349
543
|
if (projectState?.exists) {
|
|
350
544
|
pushSectionLine(localProjectSection, "ok", `.flow/ ${cwd}`);
|