@curdx/flow 2.3.1 → 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.
|
@@ -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",
|
|
@@ -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,
|
|
@@ -403,6 +448,97 @@ export function buildDoctorReport({
|
|
|
403
448
|
}
|
|
404
449
|
}
|
|
405
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
|
+
|
|
406
542
|
const localProjectSection = createSection("Local project:");
|
|
407
543
|
if (projectState?.exists) {
|
|
408
544
|
pushSectionLine(localProjectSection, "ok", `.flow/ ${cwd}`);
|