@curdx/flow 2.3.2 → 2.3.3
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.3"
|
|
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.3",
|
|
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.3",
|
|
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 = [
|
|
@@ -208,6 +209,12 @@ function createCurdxFlowPluginOptionsState() {
|
|
|
208
209
|
return {
|
|
209
210
|
pluginId: CURDX_FLOW_PLUGIN_ID,
|
|
210
211
|
definitions,
|
|
212
|
+
user: {
|
|
213
|
+
pluginConfigsPresent: false,
|
|
214
|
+
pluginConfigPresent: false,
|
|
215
|
+
optionsPresent: false,
|
|
216
|
+
overrides: {},
|
|
217
|
+
},
|
|
211
218
|
project: {
|
|
212
219
|
pluginConfigsPresent: false,
|
|
213
220
|
pluginConfigPresent: false,
|
|
@@ -221,11 +228,34 @@ function createCurdxFlowPluginOptionsState() {
|
|
|
221
228
|
overrides: {},
|
|
222
229
|
},
|
|
223
230
|
repoEffective,
|
|
231
|
+
machineEffective: structuredClone(repoEffective),
|
|
224
232
|
};
|
|
225
233
|
}
|
|
226
234
|
|
|
227
235
|
function pluginScopeLabel(scope) {
|
|
228
|
-
|
|
236
|
+
if (scope === "local") return "settings.local.json";
|
|
237
|
+
if (scope === "user") return "~/.claude/settings.json";
|
|
238
|
+
return "settings.json";
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
function invalidSettingKindForScope(scope) {
|
|
242
|
+
if (scope === "local") return "invalid-local-setting";
|
|
243
|
+
if (scope === "user") return "invalid-user-setting";
|
|
244
|
+
return "invalid-project-setting";
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function applyCurdxFlowPluginOptionOverride(pluginOptionsState, key, value, scope) {
|
|
248
|
+
pluginOptionsState.machineEffective[key] = {
|
|
249
|
+
value,
|
|
250
|
+
source: scope,
|
|
251
|
+
};
|
|
252
|
+
|
|
253
|
+
if (scope === "project" || scope === "local") {
|
|
254
|
+
pluginOptionsState.repoEffective[key] = {
|
|
255
|
+
value,
|
|
256
|
+
source: scope,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
229
259
|
}
|
|
230
260
|
|
|
231
261
|
function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope = "project") {
|
|
@@ -233,14 +263,18 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
233
263
|
return;
|
|
234
264
|
}
|
|
235
265
|
|
|
236
|
-
const target = scope === "local"
|
|
266
|
+
const target = scope === "local"
|
|
267
|
+
? pluginOptionsState.local
|
|
268
|
+
: scope === "user"
|
|
269
|
+
? pluginOptionsState.user
|
|
270
|
+
: pluginOptionsState.project;
|
|
237
271
|
const settingsLabel = pluginScopeLabel(scope);
|
|
238
272
|
target.pluginConfigsPresent = true;
|
|
239
273
|
|
|
240
274
|
if (!isNonArrayObject(parsed.pluginConfigs)) {
|
|
241
275
|
pushScopedWarning(
|
|
242
276
|
warnings,
|
|
243
|
-
scope
|
|
277
|
+
invalidSettingKindForScope(scope),
|
|
244
278
|
`pluginConfigs in ${settingsLabel} must be an object keyed by plugin@marketplace id`,
|
|
245
279
|
scope
|
|
246
280
|
);
|
|
@@ -257,7 +291,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
257
291
|
if (!isNonArrayObject(pluginConfig)) {
|
|
258
292
|
pushScopedWarning(
|
|
259
293
|
warnings,
|
|
260
|
-
scope
|
|
294
|
+
invalidSettingKindForScope(scope),
|
|
261
295
|
`pluginConfigs[${CURDX_FLOW_PLUGIN_ID}] in ${settingsLabel} must be an object when set`,
|
|
262
296
|
scope
|
|
263
297
|
);
|
|
@@ -273,7 +307,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
273
307
|
if (!isNonArrayObject(pluginConfig.options)) {
|
|
274
308
|
pushScopedWarning(
|
|
275
309
|
warnings,
|
|
276
|
-
scope
|
|
310
|
+
invalidSettingKindForScope(scope),
|
|
277
311
|
`pluginConfigs[${CURDX_FLOW_PLUGIN_ID}].options in ${settingsLabel} must be an object when set`,
|
|
278
312
|
scope
|
|
279
313
|
);
|
|
@@ -296,7 +330,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
296
330
|
if (definition.type === "boolean" && typeof value !== "boolean") {
|
|
297
331
|
pushScopedWarning(
|
|
298
332
|
warnings,
|
|
299
|
-
scope
|
|
333
|
+
invalidSettingKindForScope(scope),
|
|
300
334
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be boolean`,
|
|
301
335
|
scope
|
|
302
336
|
);
|
|
@@ -307,7 +341,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
307
341
|
if (typeof value !== "number" || Number.isNaN(value)) {
|
|
308
342
|
pushScopedWarning(
|
|
309
343
|
warnings,
|
|
310
|
-
scope
|
|
344
|
+
invalidSettingKindForScope(scope),
|
|
311
345
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be a number`,
|
|
312
346
|
scope
|
|
313
347
|
);
|
|
@@ -317,7 +351,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
317
351
|
if (definition.integer && !Number.isInteger(value)) {
|
|
318
352
|
pushScopedWarning(
|
|
319
353
|
warnings,
|
|
320
|
-
scope
|
|
354
|
+
invalidSettingKindForScope(scope),
|
|
321
355
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be an integer`,
|
|
322
356
|
scope
|
|
323
357
|
);
|
|
@@ -327,7 +361,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
327
361
|
if (typeof definition.min === "number" && value < definition.min) {
|
|
328
362
|
pushScopedWarning(
|
|
329
363
|
warnings,
|
|
330
|
-
scope
|
|
364
|
+
invalidSettingKindForScope(scope),
|
|
331
365
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be between ${definition.min} and ${definition.max}`,
|
|
332
366
|
scope
|
|
333
367
|
);
|
|
@@ -337,7 +371,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
337
371
|
if (typeof definition.max === "number" && value > definition.max) {
|
|
338
372
|
pushScopedWarning(
|
|
339
373
|
warnings,
|
|
340
|
-
scope
|
|
374
|
+
invalidSettingKindForScope(scope),
|
|
341
375
|
`CurDX-Flow plugin option ${key} in ${settingsLabel} must be between ${definition.min} and ${definition.max}`,
|
|
342
376
|
scope
|
|
343
377
|
);
|
|
@@ -346,10 +380,7 @@ function auditCurdxFlowPluginOptions(parsed, warnings, pluginOptionsState, scope
|
|
|
346
380
|
}
|
|
347
381
|
|
|
348
382
|
target.overrides[key] = value;
|
|
349
|
-
pluginOptionsState
|
|
350
|
-
value,
|
|
351
|
-
source: scope,
|
|
352
|
-
};
|
|
383
|
+
applyCurdxFlowPluginOptionOverride(pluginOptionsState, key, value, scope);
|
|
353
384
|
}
|
|
354
385
|
}
|
|
355
386
|
|
|
@@ -544,10 +575,15 @@ function auditLocalClaudeSettings(parsed, warnings) {
|
|
|
544
575
|
}
|
|
545
576
|
}
|
|
546
577
|
|
|
547
|
-
export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
578
|
+
export async function readProjectClaudeSettings(cwd = process.cwd(), { homeDir = os.homedir() } = {}) {
|
|
548
579
|
const settingsPath = path.join(cwd, ".claude", "settings.json");
|
|
549
580
|
const localSettingsPath = path.join(cwd, ".claude", "settings.local.json");
|
|
581
|
+
const userSettingsPath = path.join(homeDir, ".claude", "settings.json");
|
|
550
582
|
const state = {
|
|
583
|
+
userExists: false,
|
|
584
|
+
userInvalid: false,
|
|
585
|
+
userParseError: null,
|
|
586
|
+
userWarnings: [],
|
|
551
587
|
exists: false,
|
|
552
588
|
localExists: false,
|
|
553
589
|
invalid: false,
|
|
@@ -559,6 +595,13 @@ export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
|
559
595
|
pluginOptions: createCurdxFlowPluginOptionsState(),
|
|
560
596
|
};
|
|
561
597
|
|
|
598
|
+
try {
|
|
599
|
+
const userStat = await fs.stat(userSettingsPath);
|
|
600
|
+
state.userExists = userStat.isFile();
|
|
601
|
+
} catch {
|
|
602
|
+
state.userExists = false;
|
|
603
|
+
}
|
|
604
|
+
|
|
562
605
|
try {
|
|
563
606
|
const localStat = await fs.stat(localSettingsPath);
|
|
564
607
|
state.localExists = localStat.isFile();
|
|
@@ -566,6 +609,16 @@ export async function readProjectClaudeSettings(cwd = process.cwd()) {
|
|
|
566
609
|
state.localExists = false;
|
|
567
610
|
}
|
|
568
611
|
|
|
612
|
+
if (state.userExists) {
|
|
613
|
+
try {
|
|
614
|
+
const userParsed = JSON.parse(await fs.readFile(userSettingsPath, "utf-8"));
|
|
615
|
+
auditCurdxFlowPluginOptions(userParsed, state.userWarnings, state.pluginOptions, "user");
|
|
616
|
+
} catch (error) {
|
|
617
|
+
state.userInvalid = true;
|
|
618
|
+
state.userParseError = error?.message || String(error);
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
|
|
569
622
|
let parsed;
|
|
570
623
|
try {
|
|
571
624
|
const stat = await fs.stat(settingsPath);
|
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,
|
|
@@ -626,6 +685,33 @@ export function buildDoctorReport({
|
|
|
626
685
|
}
|
|
627
686
|
|
|
628
687
|
const projectSettingsSection = createSection("Project Claude settings:");
|
|
688
|
+
const userSettingsSection = createSection("User Claude settings:");
|
|
689
|
+
|
|
690
|
+
if (projectClaudeSettings?.userExists) {
|
|
691
|
+
if (projectClaudeSettings.userInvalid) {
|
|
692
|
+
pushSectionLine(
|
|
693
|
+
userSettingsSection,
|
|
694
|
+
"err",
|
|
695
|
+
"~/.claude/settings.json invalid JSON",
|
|
696
|
+
[projectClaudeSettings.userParseError]
|
|
697
|
+
);
|
|
698
|
+
} else if ((projectClaudeSettings.userWarnings || []).length > 0) {
|
|
699
|
+
pushSectionLine(userSettingsSection, "warn", "~/.claude/settings.json affects CurDX-Flow globally");
|
|
700
|
+
for (const warning of projectClaudeSettings.userWarnings) {
|
|
701
|
+
pushSectionLine(
|
|
702
|
+
userSettingsSection,
|
|
703
|
+
"warn",
|
|
704
|
+
warning.message,
|
|
705
|
+
projectSettingsWarningDetails(warning)
|
|
706
|
+
);
|
|
707
|
+
}
|
|
708
|
+
} else {
|
|
709
|
+
pushSectionLine(userSettingsSection, "ok", "~/.claude/settings.json present");
|
|
710
|
+
}
|
|
711
|
+
} else {
|
|
712
|
+
pushSectionLine(userSettingsSection, "info", "~/.claude/settings.json not present");
|
|
713
|
+
}
|
|
714
|
+
|
|
629
715
|
if (projectClaudeSettings?.exists) {
|
|
630
716
|
if (projectClaudeSettings.invalid) {
|
|
631
717
|
pushSectionLine(
|