@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.
@@ -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.0"
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.0",
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.0",
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
@@ -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;
@@ -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}`);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@curdx/flow",
3
- "version": "2.3.0",
3
+ "version": "2.3.2",
4
4
  "description": "Skill-first discipline layer and CLI installer for Claude Code",
5
5
  "type": "module",
6
6
  "bin": {