@curdx/flow 2.3.2 → 2.3.4
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.
|
@@ -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.4"
|
|
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.4",
|
|
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.4",
|
|
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",
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import fs from "node:fs/promises";
|
|
2
|
+
import os from "node:os";
|
|
2
3
|
import path from "node:path";
|
|
3
4
|
|
|
4
5
|
const PROJECT_ONLY_IGNORED_SETTINGS = [
|
|
@@ -120,6 +121,23 @@ const CURDX_FLOW_REQUIRED_PLUGIN_IDS = ["context7-plugin@context7-marketplace"];
|
|
|
120
121
|
const HTTP_HOOK_SETTINGS = ["allowedHttpHookUrls", "httpHookAllowedEnvVars"];
|
|
121
122
|
const PERSISTED_EFFORT_LEVELS = ["low", "medium", "high", "xhigh"];
|
|
122
123
|
const ENV_EFFORT_LEVELS = [...PERSISTED_EFFORT_LEVELS, "max", "auto"];
|
|
124
|
+
const CURDX_FLOW_RUNTIME_CONSUMERS = {
|
|
125
|
+
autonomous_blocking: {
|
|
126
|
+
envVar: "CLAUDE_PLUGIN_OPTION_AUTONOMOUS_BLOCKING",
|
|
127
|
+
consumer: "hooks/scripts/stop-watcher.sh",
|
|
128
|
+
summary: "controls whether the Stop hook blocks Claude from stopping while execute work remains",
|
|
129
|
+
},
|
|
130
|
+
daily_dependency_check: {
|
|
131
|
+
envVar: "CLAUDE_PLUGIN_OPTION_DAILY_DEPENDENCY_CHECK",
|
|
132
|
+
consumer: "hooks/scripts/session-start.sh",
|
|
133
|
+
summary: "controls the once-per-day recommended companion plugin reminder",
|
|
134
|
+
},
|
|
135
|
+
monitor_interval_seconds: {
|
|
136
|
+
envVar: "CLAUDE_PLUGIN_OPTION_MONITOR_INTERVAL_SECONDS",
|
|
137
|
+
consumer: "monitors/scripts/flow-state-monitor.sh",
|
|
138
|
+
summary: "controls the polling interval for the interactive flow-state monitor",
|
|
139
|
+
},
|
|
140
|
+
};
|
|
123
141
|
|
|
124
142
|
function envFlagEnabled(value) {
|
|
125
143
|
if (value === true || value === 1) return true;
|
|
@@ -208,6 +226,12 @@ function createCurdxFlowPluginOptionsState() {
|
|
|
208
226
|
return {
|
|
209
227
|
pluginId: CURDX_FLOW_PLUGIN_ID,
|
|
210
228
|
definitions,
|
|
229
|
+
user: {
|
|
230
|
+
pluginConfigsPresent: false,
|
|
231
|
+
pluginConfigPresent: false,
|
|
232
|
+
optionsPresent: false,
|
|
233
|
+
overrides: {},
|
|
234
|
+
},
|
|
211
235
|
project: {
|
|
212
236
|
pluginConfigsPresent: false,
|
|
213
237
|
pluginConfigPresent: false,
|
|
@@ -221,11 +245,71 @@ function createCurdxFlowPluginOptionsState() {
|
|
|
221
245
|
overrides: {},
|
|
222
246
|
},
|
|
223
247
|
repoEffective,
|
|
248
|
+
machineEffective: structuredClone(repoEffective),
|
|
224
249
|
};
|
|
225
250
|
}
|
|
226
251
|
|
|
227
252
|
function pluginScopeLabel(scope) {
|
|
228
|
-
|
|
253
|
+
if (scope === "local") return "settings.local.json";
|
|
254
|
+
if (scope === "user") return "~/.claude/settings.json";
|
|
255
|
+
return "settings.json";
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
function invalidSettingKindForScope(scope) {
|
|
259
|
+
if (scope === "local") return "invalid-local-setting";
|
|
260
|
+
if (scope === "user") return "invalid-user-setting";
|
|
261
|
+
return "invalid-project-setting";
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function applyCurdxFlowPluginOptionOverride(pluginOptionsState, key, value, scope) {
|
|
265
|
+
pluginOptionsState.machineEffective[key] = {
|
|
266
|
+
value,
|
|
267
|
+
source: scope,
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
if (scope === "project" || scope === "local") {
|
|
271
|
+
pluginOptionsState.repoEffective[key] = {
|
|
272
|
+
value,
|
|
273
|
+
source: scope,
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
function buildCurdxFlowRuntimeProjection(pluginOptionsState) {
|
|
279
|
+
return pluginOptionsState.definitions.map((definition) => {
|
|
280
|
+
const effective = pluginOptionsState.machineEffective[definition.key] || {
|
|
281
|
+
value: definition.default,
|
|
282
|
+
source: "default",
|
|
283
|
+
};
|
|
284
|
+
const runtime = CURDX_FLOW_RUNTIME_CONSUMERS[definition.key] || {};
|
|
285
|
+
const details = [];
|
|
286
|
+
|
|
287
|
+
if (definition.key === "autonomous_blocking") {
|
|
288
|
+
details.push(
|
|
289
|
+
effective.value === false
|
|
290
|
+
? "stop-hook continuation is disabled; Claude may stop at turn end even when execute tasks remain"
|
|
291
|
+
: "stop-hook continuation is enabled; CurDX-Flow can block turn end while execute tasks remain"
|
|
292
|
+
);
|
|
293
|
+
} else if (definition.key === "daily_dependency_check") {
|
|
294
|
+
details.push(
|
|
295
|
+
effective.value === false
|
|
296
|
+
? "SessionStart plugin reminder is disabled on this machine"
|
|
297
|
+
: "SessionStart plugin reminder runs at most once per day on this machine"
|
|
298
|
+
);
|
|
299
|
+
} else if (definition.key === "monitor_interval_seconds") {
|
|
300
|
+
details.push(`flow-state monitor polls every ${effective.value} second(s) when Monitor is available`);
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
return {
|
|
304
|
+
key: definition.key,
|
|
305
|
+
envVar: runtime.envVar || `CLAUDE_PLUGIN_OPTION_${definition.key.toUpperCase()}`,
|
|
306
|
+
consumer: runtime.consumer || "plugin subprocess",
|
|
307
|
+
summary: runtime.summary || "runtime consumer",
|
|
308
|
+
value: effective.value,
|
|
309
|
+
source: effective.source,
|
|
310
|
+
details,
|
|
311
|
+
};
|
|
312
|
+
});
|
|
229
313
|
}
|
|
230
314
|
|
|
231
315
|
function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope = "project") {
|
|
@@ -233,14 +317,18 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
233
317
|
return;
|
|
234
318
|
}
|
|
235
319
|
|
|
236
|
-
const target = scope === "local"
|
|
320
|
+
const target = scope === "local"
|
|
321
|
+
? pluginOptionsState.local
|
|
322
|
+
: scope === "user"
|
|
323
|
+
? pluginOptionsState.user
|
|
324
|
+
: pluginOptionsState.project;
|
|
237
325
|
const settingsLabel = pluginScopeLabel(scope);
|
|
238
326
|
target.pluginConfigsPresent = true;
|
|
239
327
|
|
|
240
328
|
if (!isNonArrayObject(parsed.pluginConfigs)) {
|
|
241
329
|
pushScopedWarning(
|
|
242
330
|
warnings,
|
|
243
|
-
scope
|
|
331
|
+
invalidSettingKindForScope(scope),
|
|
244
332
|
`pluginConfigs in ${settingsLabel} must be an object keyed by plugin@marketplace id`,
|
|
245
333
|
scope
|
|
246
334
|
);
|
|
@@ -257,7 +345,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
257
345
|
if (!isNonArrayObject(pluginConfig)) {
|
|
258
346
|
pushScopedWarning(
|
|
259
347
|
warnings,
|
|
260
|
-
scope
|
|
348
|
+
invalidSettingKindForScope(scope),
|
|
261
349
|
`pluginConfigs[${CURDX_FLOW_PLUGIN_ID}] in ${settingsLabel} must be an object when set`,
|
|
262
350
|
scope
|
|
263
351
|
);
|
|
@@ -273,7 +361,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
273
361
|
if (!isNonArrayObject(pluginConfig.options)) {
|
|
274
362
|
pushScopedWarning(
|
|
275
363
|
warnings,
|
|
276
|
-
scope
|
|
364
|
+
invalidSettingKindForScope(scope),
|
|
277
365
|
`pluginConfigs[${CURDX_FLOW_PLUGIN_ID}].options in ${settingsLabel} must be an object when set`,
|
|
278
366
|
scope
|
|
279
367
|
);
|
|
@@ -296,7 +384,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
296
384
|
if (definition.type === "boolean" && typeof value !== "boolean") {
|
|
297
385
|
pushScopedWarning(
|
|
298
386
|
warnings,
|
|
299
|
-
scope
|
|
387
|
+
invalidSettingKindForScope(scope),
|
|
300
388
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be boolean`,
|
|
301
389
|
scope
|
|
302
390
|
);
|
|
@@ -307,7 +395,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
307
395
|
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
308
396
|
pushScopedWarning(
|
|
309
397
|
warnings,
|
|
310
|
-
scope
|
|
398
|
+
invalidSettingKindForScope(scope),
|
|
311
399
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be a number`,
|
|
312
400
|
scope
|
|
313
401
|
);
|
|
@@ -317,7 +405,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
317
405
|
if (definition.integer && !Number.isInteger(value)) {
|
|
318
406
|
pushScopedWarning(
|
|
319
407
|
warnings,
|
|
320
|
-
scope
|
|
408
|
+
invalidSettingKindForScope(scope),
|
|
321
409
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be an integer`,
|
|
322
410
|
scope
|
|
323
411
|
);
|
|
@@ -327,7 +415,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
327
415
|
if (typeof definition.min === "number" && value < definition.min) {
|
|
328
416
|
pushScopedWarning(
|
|
329
417
|
warnings,
|
|
330
|
-
scope
|
|
418
|
+
invalidSettingKindForScope(scope),
|
|
331
419
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be between ${definition.min} and ${definition.max}`,
|
|
332
420
|
scope
|
|
333
421
|
);
|
|
@@ -337,7 +425,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
337
425
|
if (typeof definition.max === "number" && value > definition.max) {
|
|
338
426
|
pushScopedWarning(
|
|
339
427
|
warnings,
|
|
340
|
-
scope
|
|
428
|
+
invalidSettingKindForScope(scope),
|
|
341
429
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be between ${definition.min} and ${definition.max}`,
|
|
342
430
|
scope
|
|
343
431
|
);
|
|
@@ -346,10 +434,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
346
434
|
}
|
|
347
435
|
|
|
348
436
|
target.overrides[key] = value;
|
|
349
|
-
pluginOptionsState
|
|
350
|
-
value,
|
|
351
|
-
source: scope,
|
|
352
|
-
};
|
|
437
|
+
applyCurdxFlowPluginOptionOverride(pluginOptionsState, key, value, scope);
|
|
353
438
|
}
|
|
354
439
|
}
|
|
355
440
|
|
|
@@ -544,10 +629,15 @@ function auditLocalClaudeSettings(parsed, warnings) {
|
|
|
544
629
|
}
|
|
545
630
|
}
|
|
546
631
|
|
|
547
|
-
export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
632
|
+
export async function readProjectClaudeSettings(cwd = process.cwd(), { homeDir = os.homedir() } = {}) {
|
|
548
633
|
const settingsPath = path.join(cwd, ".claude", "settings.json");
|
|
549
634
|
const localSettingsPath = path.join(cwd, ".claude", "settings.local.json");
|
|
635
|
+
const userSettingsPath = path.join(homeDir, ".claude", "settings.json");
|
|
550
636
|
const state = {
|
|
637
|
+
userExists: false,
|
|
638
|
+
userInvalid: false,
|
|
639
|
+
userParseError: null,
|
|
640
|
+
userWarnings: [],
|
|
551
641
|
exists: false,
|
|
552
642
|
localExists: false,
|
|
553
643
|
invalid: false,
|
|
@@ -557,8 +647,16 @@ export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
|
557
647
|
localParseError: null,
|
|
558
648
|
localWarnings: [],
|
|
559
649
|
pluginOptions: createCurdxFlowPluginOptionsState(),
|
|
650
|
+
pluginRuntimeProjection: [],
|
|
560
651
|
};
|
|
561
652
|
|
|
653
|
+
try {
|
|
654
|
+
const userStat = await fs.stat(userSettingsPath);
|
|
655
|
+
state.userExists = userStat.isFile();
|
|
656
|
+
} catch {
|
|
657
|
+
state.userExists = false;
|
|
658
|
+
}
|
|
659
|
+
|
|
562
660
|
try {
|
|
563
661
|
const localStat = await fs.stat(localSettingsPath);
|
|
564
662
|
state.localExists = localStat.isFile();
|
|
@@ -566,6 +664,16 @@ export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
|
566
664
|
state.localExists = false;
|
|
567
665
|
}
|
|
568
666
|
|
|
667
|
+
if (state.userExists) {
|
|
668
|
+
try {
|
|
669
|
+
const userParsed = JSON.parse(await fs.readFile(userSettingsPath, "utf-8"));
|
|
670
|
+
auditCurdxFlowPluginOptions(userParsed, state.userWarnings, state.pluginOptions, "user");
|
|
671
|
+
} catch (error) {
|
|
672
|
+
state.userInvalid = true;
|
|
673
|
+
state.userParseError = error?.message || String(error);
|
|
674
|
+
}
|
|
675
|
+
}
|
|
676
|
+
|
|
569
677
|
let parsed;
|
|
570
678
|
try {
|
|
571
679
|
const stat = await fs.stat(settingsPath);
|
|
@@ -919,5 +1027,7 @@ export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
|
919
1027
|
}
|
|
920
1028
|
}
|
|
921
1029
|
|
|
1030
|
+
state.pluginRuntimeProjection = buildCurdxFlowRuntimeProjection(state.pluginOptions);
|
|
1031
|
+
|
|
922
1032
|
return state;
|
|
923
1033
|
}
|
package/cli/lib/doctor-report.js
CHANGED
|
@@ -13,6 +13,33 @@ function pluginErrorDetails(plugin) {
|
|
|
13
13
|
}
|
|
14
14
|
|
|
15
15
|
function projectSettingsWarningDetails(warning) {
|
|
16
|
+
if (warning?.scope === "user") {
|
|
17
|
+
if (warning.kind === "unknown-plugin-option") {
|
|
18
|
+
return [
|
|
19
|
+
"~/.claude/settings.json applies across all your repositories 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-user-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 ~/.claude/settings.json apply across all local repositories unless a project or local override wins",
|
|
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
|
+
|
|
37
|
+
return [
|
|
38
|
+
"~/.claude/settings.json applies across all your repositories on this machine",
|
|
39
|
+
"fix or remove the user-scoped override if CurDX-Flow behaves unexpectedly everywhere",
|
|
40
|
+
];
|
|
41
|
+
}
|
|
42
|
+
|
|
16
43
|
if (warning?.scope === "local") {
|
|
17
44
|
if (warning.kind === "unknown-plugin-option") {
|
|
18
45
|
return [
|
|
@@ -454,9 +481,10 @@ export function buildDoctorReport({
|
|
|
454
481
|
) {
|
|
455
482
|
const configuredOptionsSection = createSection("CurDX-Flow configured options:");
|
|
456
483
|
const optionState = projectClaudeSettings?.pluginOptions;
|
|
484
|
+
const userOverrides = optionState?.user?.overrides || {};
|
|
457
485
|
const projectOverrides = optionState?.project?.overrides || {};
|
|
458
486
|
const localOverrides = optionState?.local?.overrides || {};
|
|
459
|
-
const
|
|
487
|
+
const machineEffective = optionState?.machineEffective || optionState?.repoEffective || {};
|
|
460
488
|
const definitions = bundledPluginRuntimeDefaults?.userConfig?.length
|
|
461
489
|
? bundledPluginRuntimeDefaults.userConfig
|
|
462
490
|
: (optionState?.definitions || []);
|
|
@@ -464,13 +492,42 @@ export function buildDoctorReport({
|
|
|
464
492
|
pushSectionLine(
|
|
465
493
|
configuredOptionsSection,
|
|
466
494
|
"info",
|
|
467
|
-
"Scope precedence settings.local.json > settings.json > bundled default",
|
|
495
|
+
"Scope precedence settings.local.json > settings.json > ~/.claude/settings.json > bundled default",
|
|
468
496
|
[
|
|
469
|
-
"
|
|
470
|
-
"
|
|
497
|
+
"managed settings and command-line overrides are not inspected here",
|
|
498
|
+
"effective values below reflect this machine only",
|
|
471
499
|
]
|
|
472
500
|
);
|
|
473
501
|
|
|
502
|
+
if (projectClaudeSettings?.userExists) {
|
|
503
|
+
if (Object.keys(userOverrides).length > 0) {
|
|
504
|
+
const summary = Object.entries(userOverrides)
|
|
505
|
+
.map(([key, value]) => `${key}=${formatInlineValue(value)}`)
|
|
506
|
+
.join(", ");
|
|
507
|
+
pushSectionLine(
|
|
508
|
+
configuredOptionsSection,
|
|
509
|
+
"info",
|
|
510
|
+
`~/.claude/settings.json ${Object.keys(userOverrides).length} valid override(s)`,
|
|
511
|
+
[
|
|
512
|
+
summary,
|
|
513
|
+
"user-scoped overrides apply across repositories unless project/local settings win",
|
|
514
|
+
]
|
|
515
|
+
);
|
|
516
|
+
} else {
|
|
517
|
+
pushSectionLine(
|
|
518
|
+
configuredOptionsSection,
|
|
519
|
+
"info",
|
|
520
|
+
"~/.claude/settings.json present without valid CurDX-Flow overrides"
|
|
521
|
+
);
|
|
522
|
+
}
|
|
523
|
+
} else {
|
|
524
|
+
pushSectionLine(
|
|
525
|
+
configuredOptionsSection,
|
|
526
|
+
"info",
|
|
527
|
+
"~/.claude/settings.json not present"
|
|
528
|
+
);
|
|
529
|
+
}
|
|
530
|
+
|
|
474
531
|
if (Object.keys(projectOverrides).length > 0) {
|
|
475
532
|
const summary = Object.entries(projectOverrides)
|
|
476
533
|
.map(([key, value]) => `${key}=${formatInlineValue(value)}`)
|
|
@@ -522,7 +579,7 @@ export function buildDoctorReport({
|
|
|
522
579
|
}
|
|
523
580
|
|
|
524
581
|
for (const definition of definitions) {
|
|
525
|
-
const effective =
|
|
582
|
+
const effective = machineEffective[definition.key] || {
|
|
526
583
|
value: definition.default,
|
|
527
584
|
source: "default",
|
|
528
585
|
};
|
|
@@ -530,6 +587,8 @@ export function buildDoctorReport({
|
|
|
530
587
|
? "local"
|
|
531
588
|
: effective.source === "project"
|
|
532
589
|
? "project"
|
|
590
|
+
: effective.source === "user"
|
|
591
|
+
? "user"
|
|
533
592
|
: "bundled default";
|
|
534
593
|
pushSectionLine(
|
|
535
594
|
configuredOptionsSection,
|
|
@@ -539,6 +598,30 @@ export function buildDoctorReport({
|
|
|
539
598
|
}
|
|
540
599
|
}
|
|
541
600
|
|
|
601
|
+
if ((projectClaudeSettings?.pluginRuntimeProjection || []).length > 0) {
|
|
602
|
+
const runtimeProjectionSection = createSection("CurDX-Flow runtime projection:");
|
|
603
|
+
|
|
604
|
+
for (const entry of projectClaudeSettings.pluginRuntimeProjection) {
|
|
605
|
+
const sourceLabel = entry.source === "local"
|
|
606
|
+
? "local"
|
|
607
|
+
: entry.source === "project"
|
|
608
|
+
? "project"
|
|
609
|
+
: entry.source === "user"
|
|
610
|
+
? "user"
|
|
611
|
+
: "bundled default";
|
|
612
|
+
pushSectionLine(
|
|
613
|
+
runtimeProjectionSection,
|
|
614
|
+
"info",
|
|
615
|
+
`${entry.envVar.padEnd(36)} ${formatInlineValue(entry.value)} (${sourceLabel})`,
|
|
616
|
+
[
|
|
617
|
+
`consumer: ${entry.consumer}`,
|
|
618
|
+
entry.summary,
|
|
619
|
+
...(entry.details || []),
|
|
620
|
+
]
|
|
621
|
+
);
|
|
622
|
+
}
|
|
623
|
+
}
|
|
624
|
+
|
|
542
625
|
const localProjectSection = createSection("Local project:");
|
|
543
626
|
if (projectState?.exists) {
|
|
544
627
|
pushSectionLine(localProjectSection, "ok", `.flow/ ${cwd}`);
|
|
@@ -626,6 +709,33 @@ export function buildDoctorReport({
|
|
|
626
709
|
}
|
|
627
710
|
|
|
628
711
|
const projectSettingsSection = createSection("Project Claude settings:");
|
|
712
|
+
const userSettingsSection = createSection("User Claude settings:");
|
|
713
|
+
|
|
714
|
+
if (projectClaudeSettings?.userExists) {
|
|
715
|
+
if (projectClaudeSettings.userInvalid) {
|
|
716
|
+
pushSectionLine(
|
|
717
|
+
userSettingsSection,
|
|
718
|
+
"err",
|
|
719
|
+
"~/.claude/settings.json invalid JSON",
|
|
720
|
+
[projectClaudeSettings.userParseError]
|
|
721
|
+
);
|
|
722
|
+
} else if ((projectClaudeSettings.userWarnings || []).length > 0) {
|
|
723
|
+
pushSectionLine(userSettingsSection, "warn", "~/.claude/settings.json affects CurDX-Flow globally");
|
|
724
|
+
for (const warning of projectClaudeSettings.userWarnings) {
|
|
725
|
+
pushSectionLine(
|
|
726
|
+
userSettingsSection,
|
|
727
|
+
"warn",
|
|
728
|
+
warning.message,
|
|
729
|
+
projectSettingsWarningDetails(warning)
|
|
730
|
+
);
|
|
731
|
+
}
|
|
732
|
+
} else {
|
|
733
|
+
pushSectionLine(userSettingsSection, "ok", "~/.claude/settings.json present");
|
|
734
|
+
}
|
|
735
|
+
} else {
|
|
736
|
+
pushSectionLine(userSettingsSection, "info", "~/.claude/settings.json not present");
|
|
737
|
+
}
|
|
738
|
+
|
|
629
739
|
if (projectClaudeSettings?.exists) {
|
|
630
740
|
if (projectClaudeSettings.invalid) {
|
|
631
741
|
pushSectionLine(
|
|
@@ -88,6 +88,7 @@ Guarded artifact targets:
|
|
|
88
88
|
- `autonomous_blocking`: lets users disable stop-hook continuation without editing plugin files.
|
|
89
89
|
- `daily_dependency_check`: silences or enables the once-per-day recommended-plugin reminder.
|
|
90
90
|
- `monitor_interval_seconds`: controls plugin monitor polling cadence.
|
|
91
|
+
- `doctor` should explain both the machine-effective config value and the projected plugin subprocess env var for these knobs, since hook/monitor behavior depends on the env projection rather than direct JSON parsing.
|
|
91
92
|
|
|
92
93
|
## Plugin Dependency Constraints
|
|
93
94
|
|