@curdx/flow 3.0.0 → 3.2.0

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.
Files changed (219) hide show
  1. package/CHANGELOG.md +33 -82
  2. package/LICENSE +1 -1
  3. package/README.md +28 -129
  4. package/dist/index.mjs +1165 -0
  5. package/package.json +33 -44
  6. package/.claude-plugin/marketplace.json +0 -48
  7. package/.claude-plugin/plugin.json +0 -52
  8. package/agent-preamble/preamble.md +0 -314
  9. package/agents/flow-adversary.md +0 -203
  10. package/agents/flow-architect.md +0 -198
  11. package/agents/flow-brownfield-analyst.md +0 -143
  12. package/agents/flow-debugger.md +0 -321
  13. package/agents/flow-edge-hunter.md +0 -289
  14. package/agents/flow-executor.md +0 -269
  15. package/agents/flow-orchestrator.md +0 -145
  16. package/agents/flow-planner.md +0 -247
  17. package/agents/flow-product-designer.md +0 -159
  18. package/agents/flow-qa-engineer.md +0 -282
  19. package/agents/flow-researcher.md +0 -166
  20. package/agents/flow-reviewer.md +0 -304
  21. package/agents/flow-security-auditor.md +0 -401
  22. package/agents/flow-triage-analyst.md +0 -272
  23. package/agents/flow-ui-researcher.md +0 -230
  24. package/agents/flow-ux-designer.md +0 -221
  25. package/agents/flow-verifier.md +0 -350
  26. package/bin/curdx-flow +0 -5
  27. package/bin/curdx-flow-state +0 -104
  28. package/bin/curdx-flow.js +0 -54
  29. package/cli/README.md +0 -104
  30. package/cli/doctor-workflow.js +0 -483
  31. package/cli/doctor.js +0 -73
  32. package/cli/help.js +0 -59
  33. package/cli/install-bundled-mcps.js +0 -37
  34. package/cli/install-companions.js +0 -19
  35. package/cli/install-context7-config.js +0 -80
  36. package/cli/install-curdx-plugin.js +0 -96
  37. package/cli/install-language.js +0 -35
  38. package/cli/install-next-steps.js +0 -29
  39. package/cli/install-options.js +0 -9
  40. package/cli/install-paths.js +0 -52
  41. package/cli/install-recommended-plugins.js +0 -104
  42. package/cli/install-required-plugins.js +0 -57
  43. package/cli/install-self-update.js +0 -62
  44. package/cli/install-workflow.js +0 -209
  45. package/cli/install.js +0 -101
  46. package/cli/lib/claude-commands.js +0 -41
  47. package/cli/lib/claude-ops.js +0 -47
  48. package/cli/lib/claude.js +0 -183
  49. package/cli/lib/config.js +0 -24
  50. package/cli/lib/doctor-claude-settings.js +0 -1186
  51. package/cli/lib/doctor-report.js +0 -978
  52. package/cli/lib/doctor-runtime-environment.js +0 -196
  53. package/cli/lib/frontmatter.js +0 -44
  54. package/cli/lib/json-schema.js +0 -57
  55. package/cli/lib/logging.js +0 -25
  56. package/cli/lib/process.js +0 -60
  57. package/cli/lib/prompts.js +0 -135
  58. package/cli/lib/runtime.js +0 -107
  59. package/cli/lib/semver.js +0 -109
  60. package/cli/lib/version.js +0 -12
  61. package/cli/protocols-body.md +0 -22
  62. package/cli/protocols.js +0 -162
  63. package/cli/registry.js +0 -123
  64. package/cli/router.js +0 -49
  65. package/cli/uninstall-actions.js +0 -360
  66. package/cli/uninstall-workflow.js +0 -146
  67. package/cli/uninstall.js +0 -42
  68. package/cli/upgrade-workflow.js +0 -80
  69. package/cli/upgrade.js +0 -91
  70. package/cli/utils.js +0 -40
  71. package/gates/adversarial-review-gate.md +0 -219
  72. package/gates/coverage-audit-gate.md +0 -182
  73. package/gates/devex-gate.md +0 -254
  74. package/gates/edge-case-gate.md +0 -194
  75. package/gates/karpathy-gate.md +0 -130
  76. package/gates/security-gate.md +0 -218
  77. package/gates/tdd-gate.md +0 -182
  78. package/gates/test-quality-gate.md +0 -59
  79. package/gates/verification-gate.md +0 -179
  80. package/hooks/hooks.json +0 -130
  81. package/hooks/scripts/common.sh +0 -237
  82. package/hooks/scripts/config-change-guard.sh +0 -94
  83. package/hooks/scripts/flow-context-watch.sh +0 -94
  84. package/hooks/scripts/inject-karpathy.sh +0 -53
  85. package/hooks/scripts/quick-mode-guard.sh +0 -69
  86. package/hooks/scripts/session-start.sh +0 -94
  87. package/hooks/scripts/session-title.sh +0 -87
  88. package/hooks/scripts/stop-watcher.sh +0 -231
  89. package/hooks/scripts/subagent-artifact-guard.sh +0 -92
  90. package/hooks/scripts/subagent-statusline.sh +0 -111
  91. package/hooks/scripts/task-lifecycle-guard.sh +0 -106
  92. package/hooks/scripts/teammate-idle-guard.sh +0 -83
  93. package/knowledge/artifact-output-discipline.md +0 -24
  94. package/knowledge/artifact-summary-contracts.md +0 -50
  95. package/knowledge/atomic-commits.md +0 -262
  96. package/knowledge/claude-code-runtime-contracts.md +0 -240
  97. package/knowledge/epic-decomposition.md +0 -307
  98. package/knowledge/execution-strategies.md +0 -303
  99. package/knowledge/karpathy-guidelines.md +0 -219
  100. package/knowledge/planning-reviews.md +0 -211
  101. package/knowledge/poc-first-workflow.md +0 -223
  102. package/knowledge/review-feedback-intake.md +0 -57
  103. package/knowledge/spec-driven-development.md +0 -180
  104. package/knowledge/systematic-debugging.md +0 -378
  105. package/knowledge/two-stage-review.md +0 -249
  106. package/knowledge/wave-execution.md +0 -403
  107. package/monitors/monitors.json +0 -8
  108. package/monitors/scripts/flow-state-monitor.sh +0 -102
  109. package/output-styles/curdx-evidence-first.md +0 -34
  110. package/output-styles/curdx-fast-mode.md +0 -42
  111. package/output-styles/curdx-spec-mode.md +0 -46
  112. package/schemas/agent-frontmatter.schema.json +0 -66
  113. package/schemas/config.schema.json +0 -134
  114. package/schemas/gate-frontmatter.schema.json +0 -30
  115. package/schemas/hooks.schema.json +0 -115
  116. package/schemas/output-style-frontmatter.schema.json +0 -22
  117. package/schemas/plugin-manifest.schema.json +0 -436
  118. package/schemas/plugin-settings.schema.json +0 -29
  119. package/schemas/skill-frontmatter.schema.json +0 -177
  120. package/schemas/spec-frontmatter.schema.json +0 -42
  121. package/schemas/spec-state.schema.json +0 -165
  122. package/settings.json +0 -8
  123. package/skills/brownfield-index/SKILL.md +0 -53
  124. package/skills/brownfield-index/references/applicability.md +0 -12
  125. package/skills/brownfield-index/references/handoff.md +0 -8
  126. package/skills/brownfield-index/references/index-contract.md +0 -10
  127. package/skills/browser-qa/SKILL.md +0 -39
  128. package/skills/browser-qa/references/handoff.md +0 -6
  129. package/skills/browser-qa/references/prerequisites.md +0 -10
  130. package/skills/browser-qa/references/qa-contract.md +0 -20
  131. package/skills/cancel/SKILL.md +0 -41
  132. package/skills/cancel/references/destructive-mode.md +0 -17
  133. package/skills/cancel/references/reporting.md +0 -18
  134. package/skills/cancel/references/state-recovery.md +0 -30
  135. package/skills/cancel/references/target-resolution.md +0 -7
  136. package/skills/debug/SKILL.md +0 -45
  137. package/skills/debug/references/context-gathering.md +0 -11
  138. package/skills/debug/references/failure-guard.md +0 -25
  139. package/skills/debug/references/intake.md +0 -12
  140. package/skills/debug/references/phase-workflow.md +0 -34
  141. package/skills/debug/references/reporting.md +0 -20
  142. package/skills/epic/SKILL.md +0 -39
  143. package/skills/epic/references/epic-artifacts.md +0 -20
  144. package/skills/epic/references/epic-intake.md +0 -9
  145. package/skills/epic/references/slice-handoff.md +0 -16
  146. package/skills/fast/SKILL.md +0 -62
  147. package/skills/fast/references/applicability.md +0 -25
  148. package/skills/fast/references/clarification.md +0 -20
  149. package/skills/fast/references/execution-contract.md +0 -56
  150. package/skills/help/SKILL.md +0 -55
  151. package/skills/help/references/dispatch.md +0 -20
  152. package/skills/help/references/overview.md +0 -39
  153. package/skills/help/references/troubleshoot.md +0 -47
  154. package/skills/help/references/workflow.md +0 -37
  155. package/skills/implement/SKILL.md +0 -104
  156. package/skills/implement/references/error-recovery.md +0 -36
  157. package/skills/implement/references/linear-execution.md +0 -43
  158. package/skills/implement/references/native-task-sync.md +0 -107
  159. package/skills/implement/references/preflight.md +0 -43
  160. package/skills/implement/references/progress-contract.md +0 -36
  161. package/skills/implement/references/state-init.md +0 -36
  162. package/skills/implement/references/stop-hook-execution.md +0 -50
  163. package/skills/implement/references/strategy-router.md +0 -38
  164. package/skills/implement/references/subagent-execution.md +0 -57
  165. package/skills/implement/references/wave-execution.md +0 -180
  166. package/skills/init/SKILL.md +0 -49
  167. package/skills/init/references/gitignore-and-health.md +0 -26
  168. package/skills/init/references/next-steps.md +0 -22
  169. package/skills/init/references/preflight.md +0 -15
  170. package/skills/init/references/scaffold-contract.md +0 -27
  171. package/skills/review/SKILL.md +0 -82
  172. package/skills/review/references/optional-passes.md +0 -48
  173. package/skills/review/references/preflight.md +0 -38
  174. package/skills/review/references/report-contract.md +0 -49
  175. package/skills/review/references/reporting.md +0 -20
  176. package/skills/review/references/stage-execution.md +0 -32
  177. package/skills/security-audit/SKILL.md +0 -47
  178. package/skills/security-audit/references/audit-contract.md +0 -21
  179. package/skills/security-audit/references/gate-handoff.md +0 -8
  180. package/skills/security-audit/references/scope-and-depth.md +0 -9
  181. package/skills/spec/SKILL.md +0 -100
  182. package/skills/spec/references/artifact-landing.md +0 -31
  183. package/skills/spec/references/phase-execution.md +0 -50
  184. package/skills/spec/references/planning-review.md +0 -31
  185. package/skills/spec/references/preflight-and-routing.md +0 -46
  186. package/skills/spec/references/reporting.md +0 -21
  187. package/skills/start/SKILL.md +0 -84
  188. package/skills/start/references/branch-routing.md +0 -51
  189. package/skills/start/references/mode-semantics.md +0 -12
  190. package/skills/start/references/preflight.md +0 -13
  191. package/skills/start/references/reporting.md +0 -20
  192. package/skills/start/references/state-seeding.md +0 -44
  193. package/skills/start/references/workflow-handoff.md +0 -26
  194. package/skills/status/SKILL.md +0 -41
  195. package/skills/status/references/gather-contract.md +0 -30
  196. package/skills/status/references/health-rules.md +0 -27
  197. package/skills/status/references/output-contract.md +0 -25
  198. package/skills/status/references/preflight.md +0 -10
  199. package/skills/status/references/recovery-hints.md +0 -18
  200. package/skills/ui-sketch/SKILL.md +0 -39
  201. package/skills/ui-sketch/references/brief-intake.md +0 -10
  202. package/skills/ui-sketch/references/iteration-handoff.md +0 -5
  203. package/skills/ui-sketch/references/variant-contract.md +0 -15
  204. package/skills/verify/SKILL.md +0 -56
  205. package/skills/verify/references/evidence-workflow.md +0 -39
  206. package/skills/verify/references/output-contract.md +0 -23
  207. package/skills/verify/references/preflight.md +0 -11
  208. package/skills/verify/references/report-handoff.md +0 -35
  209. package/skills/verify/references/strict-mode.md +0 -12
  210. package/templates/CONTEXT.md.tmpl +0 -53
  211. package/templates/PROJECT.md.tmpl +0 -59
  212. package/templates/ROADMAP.md.tmpl +0 -50
  213. package/templates/STATE.md.tmpl +0 -49
  214. package/templates/config.json.tmpl +0 -51
  215. package/templates/design.md.tmpl +0 -83
  216. package/templates/progress.md.tmpl +0 -77
  217. package/templates/requirements.md.tmpl +0 -76
  218. package/templates/research.md.tmpl +0 -83
  219. package/templates/tasks.md.tmpl +0 -107
@@ -1,978 +0,0 @@
1
- import { BUNDLED_MCPS, REQUIRED_PLUGINS, RECOMMENDED_PLUGINS } from "../registry.js";
2
- import {
3
- findDuplicateMcps,
4
- findPluginByRegistryEntry,
5
- hasMarketplace,
6
- } from "./claude.js";
7
- import { isVersionAtLeast } from "./semver.js";
8
-
9
- export const MIN_CLAUDE_VERSION = "2.1.110";
10
-
11
- function pluginErrorDetails(plugin) {
12
- return Array.isArray(plugin?.errors) ? plugin.errors : [];
13
- }
14
-
15
- function projectSettingsWarningDetails(warning) {
16
- if (warning?.scope === "managed") {
17
- if (warning.kind === "unknown-plugin-option") {
18
- return [
19
- "file-based managed settings override user, project, and local CurDX-Flow plugin options 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 (warning.kind === "invalid-managed-setting") {
25
- return [
26
- "file-based managed settings are evaluated before user/project/local settings",
27
- "fix the managed JSON value shape so CurDX-Flow runtime projection matches the intended policy",
28
- ];
29
- }
30
-
31
- return [
32
- "file-based managed settings override user, project, and local CurDX-Flow plugin options on this machine",
33
- "fix the managed policy or move the value to a supported CurDX-Flow plugin option key",
34
- ];
35
- }
36
-
37
- if (warning?.scope === "user") {
38
- if (warning.kind === "unknown-plugin-option") {
39
- return [
40
- "~/.claude/settings.json applies across all your repositories on this machine",
41
- "remove the unknown key or rename it to a supported CurDX-Flow plugin option under pluginConfigs[curdx-flow@curdx-flow-marketplace].options",
42
- ];
43
- }
44
-
45
- if (
46
- warning.kind === "invalid-user-setting" &&
47
- (
48
- warning.message?.includes("pluginConfigs[curdx-flow@curdx-flow-marketplace]") ||
49
- warning.message?.includes("CurDX-Flow plugin option")
50
- )
51
- ) {
52
- return [
53
- "CurDX-Flow plugin options in ~/.claude/settings.json apply across all local repositories unless a project or local override wins",
54
- "use boolean values for autonomous_blocking and daily_dependency_check, and keep monitor_interval_seconds as an integer between 3 and 60",
55
- ];
56
- }
57
-
58
- return [
59
- "~/.claude/settings.json applies across all your repositories on this machine",
60
- "fix or remove the user-scoped override if CurDX-Flow behaves unexpectedly everywhere",
61
- ];
62
- }
63
-
64
- if (warning?.scope === "local") {
65
- if (warning.kind === "unknown-plugin-option") {
66
- return [
67
- "settings.local.json overrides shared project settings on this machine",
68
- "remove the unknown key or rename it to a supported CurDX-Flow plugin option under pluginConfigs[curdx-flow@curdx-flow-marketplace].options",
69
- ];
70
- }
71
-
72
- if (
73
- warning.kind === "invalid-local-setting" &&
74
- (
75
- warning.message?.includes("pluginConfigs[curdx-flow@curdx-flow-marketplace]") ||
76
- warning.message?.includes("CurDX-Flow plugin option")
77
- )
78
- ) {
79
- return [
80
- "CurDX-Flow plugin options in settings.local.json override project and user settings on this machine",
81
- "use boolean values for autonomous_blocking and daily_dependency_check, and keep monitor_interval_seconds as an integer between 3 and 60",
82
- ];
83
- }
84
-
85
- if (warning.kind === "invalid-local-setting") {
86
- return [
87
- "settings.local.json is the highest-precedence repo-scoped settings surface on this machine",
88
- "fix the JSON shape or remove the local override if Claude behaves unexpectedly",
89
- ];
90
- }
91
-
92
- if (warning.kind === "required-plugin-disabled") {
93
- return [
94
- "settings.local.json overrides project and user plugin preferences on this machine",
95
- "remove the false entry or enable the required companion plugin locally",
96
- ];
97
- }
98
-
99
- if (warning.kind === "flow-runtime-blocker") {
100
- return [
101
- "settings.local.json has higher precedence than .claude/settings.json and can break CurDX-Flow only on this machine",
102
- "remove the local blocker or add explicit exceptions for curdx-flow workflows",
103
- ];
104
- }
105
-
106
- return [
107
- "settings.local.json overrides shared project settings on this machine",
108
- "fix or remove the local override if Claude behaves differently from the rest of the team",
109
- ];
110
- }
111
-
112
- if (warning.kind === "unknown-plugin-option") {
113
- return [
114
- "CurDX-Flow only reads keys declared in the plugin manifest",
115
- "remove the unknown key or rename it under pluginConfigs[curdx-flow@curdx-flow-marketplace].options",
116
- ];
117
- }
118
-
119
- if (
120
- warning.kind === "invalid-project-setting" &&
121
- (
122
- warning.message?.includes("pluginConfigs[curdx-flow@curdx-flow-marketplace]") ||
123
- warning.message?.includes("CurDX-Flow plugin option")
124
- )
125
- ) {
126
- return [
127
- "CurDX-Flow non-sensitive plugin options belong under pluginConfigs[curdx-flow@curdx-flow-marketplace].options in .claude/settings.json",
128
- "use boolean values for autonomous_blocking and daily_dependency_check, and keep monitor_interval_seconds as an integer between 3 and 60",
129
- ];
130
- }
131
-
132
- if (!warning?.kind) {
133
- return [
134
- "project settings are shared with collaborators",
135
- "prefer deny rules for .env/secrets and avoid bypassPermissions defaults",
136
- ];
137
- }
138
-
139
- if (warning.kind === "ignored-project-setting" || warning.kind === "managed-only-setting") {
140
- return [
141
- "Claude Code will ignore this at project scope or only honor it from managed settings",
142
- "move it to user settings, settings.local.json, or managed settings as appropriate",
143
- ];
144
- }
145
-
146
- if (warning.kind === "invalid-project-setting") {
147
- return [
148
- "Claude Code expects this setting to follow the official settings.json shape",
149
- "fix the value shape or remove the key from shared project settings",
150
- ];
151
- }
152
-
153
- if (warning.kind === "required-plugin-disabled") {
154
- return [
155
- "project enabledPlugins has higher precedence than user plugin preferences",
156
- "remove the false entry or enable the required companion plugin for this project",
157
- ];
158
- }
159
-
160
- if (
161
- warning.kind === "shared-script-setting" ||
162
- warning.kind === "shared-env-setting" ||
163
- warning.kind === "shared-mcp-auto-approve" ||
164
- warning.kind === "shared-hook-policy" ||
165
- warning.kind === "shared-sandbox-policy"
166
- ) {
167
- return [
168
- "project settings are shared with collaborators",
169
- "avoid shared settings that run scripts, inject env vars, narrow hooks, or change sandbox behavior for every collaborator",
170
- ];
171
- }
172
-
173
- if (warning.kind === "skill-shell-disabled") {
174
- return [
175
- "Claude Code replaces inline skill shell output with a disabled placeholder when this policy is active",
176
- "keep it only if your team intentionally bans dynamic shell-backed skill content",
177
- ];
178
- }
179
-
180
- if (warning.kind === "low-effort-project-setting") {
181
- return [
182
- "project effortLevel applies to main-thread planning and review turns",
183
- "prefer high/xhigh for CurDX-Flow planning and verification-heavy workflows",
184
- ];
185
- }
186
-
187
- if (warning.kind === "flow-runtime-blocker") {
188
- return [
189
- "CurDX-Flow relies on Claude Code hooks, Agent dispatch, AskUserQuestion, Monitor plus Bash/Read/Edit tooling, and sonnet/opus model aliases",
190
- "move restrictive policy to a narrower scope or add explicit exceptions for curdx-flow workflows",
191
- ];
192
- }
193
-
194
- if (warning.kind === "deprecated-setting") {
195
- return [
196
- "deprecated settings are still accepted for compatibility but should be migrated",
197
- "prefer the current official replacement before the old key is removed",
198
- ];
199
- }
200
-
201
- return [
202
- "project settings are shared with collaborators",
203
- "prefer deny rules for .env/secrets and avoid bypassPermissions defaults",
204
- ];
205
- }
206
-
207
- function formatInlineValue(value) {
208
- if (typeof value === "string") return `"${value}"`;
209
- return String(value);
210
- }
211
-
212
- export function buildDoctorReport({
213
- claudeVersionValue,
214
- nodeVersion,
215
- plugins = [],
216
- marketplaces = [],
217
- mcps = [],
218
- userMcpConfig,
219
- runtimeStatus,
220
- runtimeEnvironment,
221
- cwd,
222
- projectState,
223
- projectMcpConfig,
224
- projectTeamConfig,
225
- projectClaudeSettings,
226
- bundledPluginRuntimeDefaults,
227
- legacyInstallState,
228
- }) {
229
- const lines = [];
230
- const sections = [];
231
- let errors = 0;
232
- let warnings = 0;
233
-
234
- const pushLine = (target, level, text, details = []) => {
235
- target.push({ level, text, details });
236
- if (level === "err") errors++;
237
- if (level === "warn") warnings++;
238
- };
239
- const pushSectionLine = (section, level, text, details = []) => {
240
- pushLine(section.lines, level, text, details);
241
- };
242
- const createSection = (title) => {
243
- const section = { title, lines: [] };
244
- sections.push(section);
245
- return section;
246
- };
247
-
248
- if (claudeVersionValue) {
249
- pushLine(lines, "ok", `claude CLI ${claudeVersionValue}`);
250
- if (!isVersionAtLeast(claudeVersionValue, MIN_CLAUDE_VERSION)) {
251
- pushLine(
252
- lines,
253
- "warn",
254
- `claude CLI ${claudeVersionValue} below recommended ${MIN_CLAUDE_VERSION}`,
255
- [
256
- "curdx-flow uses modern Claude Code plugin dependency resolution, plugin bin/PATH support, plugin-level main-agent routing, and monitor surfaces",
257
- "run: claude update",
258
- ]
259
- );
260
- }
261
- } else {
262
- pushLine(lines, "err", "claude CLI not found (install Claude Code)");
263
- }
264
- pushLine(lines, "ok", `Node ${nodeVersion}`);
265
-
266
- const curdx = plugins.find((plugin) => plugin.name === "curdx-flow");
267
- const bundledVersion =
268
- bundledPluginRuntimeDefaults?.pluginVersion || bundledPluginRuntimeDefaults?.packageVersion || null;
269
- const bundledSourceRepo = bundledPluginRuntimeDefaults?.sourceRepo || {};
270
- if (curdx) {
271
- if (curdx.status === "enabled") {
272
- pushLine(lines, "ok", `curdx-flow v${curdx.version} (enabled)`);
273
- if (bundledVersion && curdx.version && curdx.version !== bundledVersion) {
274
- pushLine(
275
- lines,
276
- "warn",
277
- `curdx-flow source/body v${bundledVersion} differs from installed v${curdx.version}`,
278
- [
279
- "you are not validating the same CurDX-Flow build that Claude currently loads",
280
- "reinstall the plugin from the current source/package, then restart Claude Code",
281
- "run: npx @curdx/flow install --all",
282
- ]
283
- );
284
- }
285
- if (bundledSourceRepo.isGitRepo && bundledSourceRepo.dirty) {
286
- pushLine(
287
- lines,
288
- "warn",
289
- "curdx-flow source repo has uninstalled local changes",
290
- [
291
- "the current CLI/source checkout has git modifications that Claude's installed plugin cannot see until you reinstall it",
292
- "re-run: npx @curdx/flow install --all",
293
- "then fully restart Claude Code before validating plugin behavior",
294
- ]
295
- );
296
- }
297
- } else {
298
- pushLine(
299
- lines,
300
- "err",
301
- `curdx-flow v${curdx.version} (${curdx.status})`,
302
- pluginErrorDetails(curdx)
303
- );
304
- }
305
- } else {
306
- pushLine(lines, "warn", "curdx-flow not installed → run curdx-flow install");
307
- }
308
-
309
- const requiredSection = createSection("Required plugins:");
310
- for (const entry of REQUIRED_PLUGINS) {
311
- const plugin = findPluginByRegistryEntry(plugins, entry);
312
- if (!hasMarketplace(marketplaces, entry)) {
313
- pushSectionLine(
314
- requiredSection,
315
- "warn",
316
- `${entry.marketplaceId.padEnd(22)} marketplace missing`,
317
- [`run: claude plugin marketplace add --scope ${entry.scope} ${entry.marketplaceSource}`]
318
- );
319
- }
320
- if (plugin && plugin.status === "enabled") {
321
- pushSectionLine(requiredSection, "ok", `${entry.name.padEnd(22)} v${plugin.version || "unknown"}`);
322
- } else if (plugin && plugin.status === "failed") {
323
- pushSectionLine(
324
- requiredSection,
325
- "err",
326
- `${entry.name.padEnd(22)} load failed`,
327
- pluginErrorDetails(plugin)
328
- );
329
- } else {
330
- pushSectionLine(
331
- requiredSection,
332
- "warn",
333
- `${entry.name.padEnd(22)} not installed`,
334
- [`run: claude plugin install --scope ${entry.scope} ${entry.installSpec}`]
335
- );
336
- }
337
- }
338
-
339
- const mcpSection = createSection("MCP Servers (required by L2 mandatory tools):");
340
- for (const expected of BUNDLED_MCPS) {
341
- const userLevel = mcps.find((entry) => entry.name === expected.name && entry.plugin === null);
342
- const pluginLevel = mcps.find((entry) => entry.name === expected.name && entry.plugin !== null);
343
-
344
- if (userLevel) {
345
- pushSectionLine(mcpSection, "ok", `${expected.name.padEnd(22)} user-level (standard)`);
346
- } else if (pluginLevel) {
347
- pushSectionLine(
348
- mcpSection,
349
- "warn",
350
- `${expected.name.padEnd(22)} registered via plugin:${pluginLevel.plugin} (legacy)`,
351
- ["run: npx @curdx/flow install --all"]
352
- );
353
- } else if (curdx) {
354
- pushSectionLine(
355
- mcpSection,
356
- "warn",
357
- `${expected.name.padEnd(22)} missing`,
358
- [`run: claude mcp add --scope user ${expected.name} -- ${expected.command} ${expected.args.join(" ")}`]
359
- );
360
- } else {
361
- pushSectionLine(mcpSection, "info", `${expected.name.padEnd(22)} waiting for curdx-flow install`);
362
- }
363
- }
364
-
365
- const recommendedSection = createSection("Recommended plugins:");
366
- let claudeMemEnabled = false;
367
- for (const entry of RECOMMENDED_PLUGINS) {
368
- const plugin = findPluginByRegistryEntry(plugins, entry);
369
- if (!hasMarketplace(marketplaces, entry)) {
370
- pushSectionLine(
371
- recommendedSection,
372
- "warn",
373
- `${entry.marketplaceId.padEnd(22)} marketplace missing`,
374
- [`run: claude plugin marketplace add --scope ${entry.scope} ${entry.marketplaceSource}`]
375
- );
376
- }
377
- if (plugin && plugin.status === "enabled") {
378
- pushSectionLine(recommendedSection, "ok", `${entry.name.padEnd(22)} v${plugin.version}`);
379
- if (entry.postInstall === "claude-mem-runtimes") claudeMemEnabled = true;
380
- } else if (plugin && plugin.status === "failed") {
381
- pushSectionLine(
382
- recommendedSection,
383
- "err",
384
- `${entry.name.padEnd(22)} load failed`,
385
- pluginErrorDetails(plugin)
386
- );
387
- } else {
388
- pushSectionLine(
389
- recommendedSection,
390
- "warn",
391
- `${entry.name.padEnd(22)} not installed`,
392
- [`run: claude plugin install --scope ${entry.scope} ${entry.installSpec}`]
393
- );
394
- }
395
- }
396
-
397
- const duplicates = findDuplicateMcps(mcps, userMcpConfig);
398
- if (duplicates.length > 0) {
399
- const duplicateSection = createSection("Duplicate MCP registrations:");
400
- for (const duplicate of duplicates) {
401
- const details = duplicate.pluginEntry.plugin === "curdx-flow"
402
- ? [
403
- "migration: claude plugin update curdx-flow@curdx-flow-marketplace",
404
- "then restart Claude Code",
405
- ]
406
- : duplicate.pluginEntry.plugin === "context7-plugin" && duplicate.name === "context7"
407
- ? [
408
- "official Context7 plugin already owns the context7 MCP server",
409
- "run: npx @curdx/flow doctor --fix",
410
- `or run manually: claude mcp remove --scope user ${duplicate.name}`,
411
- ]
412
- : [
413
- `remove the duplicate user-level server if plugin:${duplicate.pluginEntry.plugin} should own it`,
414
- `run: claude mcp remove --scope user ${duplicate.name}`,
415
- ];
416
- pushSectionLine(
417
- duplicateSection,
418
- "warn",
419
- `${duplicate.name.padEnd(22)} both user-level AND plugin:${duplicate.pluginEntry.plugin} active`,
420
- details
421
- );
422
- }
423
- }
424
-
425
- if (legacyInstallState?.hasLegacyContext7ApiKey) {
426
- const legacySection = createSection("Legacy installer state:");
427
- pushSectionLine(
428
- legacySection,
429
- "warn",
430
- "legacy Context7 API key stored in curdx-flow install config",
431
- [
432
- `path: ${legacyInstallState.configPath}`,
433
- "this key belonged to the retired user-level Context7 MCP install flow and is no longer used by the official context7-plugin",
434
- "run: npx @curdx/flow doctor --fix",
435
- ]
436
- );
437
- }
438
-
439
- if (claudeMemEnabled && runtimeStatus) {
440
- const runtimeSection = createSection("Runtime (claude-mem dependencies):");
441
- for (const [name, status] of Object.entries(runtimeStatus)) {
442
- if (status.status === "ok") {
443
- pushSectionLine(runtimeSection, "ok", `${name.padEnd(22)} visible on PATH`);
444
- } else if (status.status === "linkable") {
445
- pushSectionLine(
446
- runtimeSection,
447
- "warn",
448
- `${name.padEnd(22)} installed but not on PATH`,
449
- [
450
- `detected at ${status.path}`,
451
- "run: npx @curdx/flow doctor --fix",
452
- ]
453
- );
454
- } else if (status.status === "linked") {
455
- pushSectionLine(runtimeSection, "ok", `${name.padEnd(22)} auto-linked ${status.link} → ${status.path}`);
456
- } else if (status.status === "missing") {
457
- pushSectionLine(
458
- runtimeSection,
459
- "warn",
460
- `${name.padEnd(22)} not installed`,
461
- ["claude-mem will auto-install on next Claude Code session"]
462
- );
463
- } else if (status.status === "path-unwritable") {
464
- const dir = status.path.split("/").slice(0, -1).join("/");
465
- pushSectionLine(
466
- runtimeSection,
467
- "err",
468
- `${name.padEnd(22)} installed but not on PATH`,
469
- [
470
- `add export PATH="${dir}:$PATH" to your shell rc`,
471
- "then rerun: npx @curdx/flow doctor",
472
- ]
473
- );
474
- }
475
- }
476
- }
477
-
478
- if (runtimeEnvironment?.entries?.length > 0) {
479
- const runtimeEnvSection = createSection("Runtime environment:");
480
- for (const entry of runtimeEnvironment.entries) {
481
- pushSectionLine(
482
- runtimeEnvSection,
483
- entry.level || "info",
484
- entry.text,
485
- entry.details || []
486
- );
487
- }
488
- }
489
-
490
- if (bundledPluginRuntimeDefaults?.exists) {
491
- const bundledRuntimeSection = createSection("CurDX-Flow bundled runtime:");
492
-
493
- if (bundledVersion) {
494
- pushSectionLine(
495
- bundledRuntimeSection,
496
- "info",
497
- `Bundled plugin body v${bundledVersion}`
498
- );
499
- }
500
-
501
- if (bundledSourceRepo.isGitRepo) {
502
- const sourceSummary = [
503
- bundledSourceRepo.branch,
504
- bundledSourceRepo.shortSha,
505
- ].filter(Boolean).join(" @ ");
506
- const sourceDetails = [];
507
- if (bundledSourceRepo.exactTag) {
508
- sourceDetails.push(`exact tag: ${bundledSourceRepo.exactTag}`);
509
- }
510
- sourceDetails.push(
511
- bundledSourceRepo.dirty
512
- ? "git worktree dirty: local source changes are not reflected in Claude until reinstall"
513
- : "git worktree clean"
514
- );
515
- pushSectionLine(
516
- bundledRuntimeSection,
517
- bundledSourceRepo.dirty ? "warn" : "info",
518
- `Bundled source repo ${sourceSummary || "git checkout detected"}`,
519
- sourceDetails
520
- );
521
- }
522
-
523
- if (bundledPluginRuntimeDefaults.defaultAgent) {
524
- pushSectionLine(
525
- bundledRuntimeSection,
526
- "ok",
527
- `Main thread agent ${bundledPluginRuntimeDefaults.defaultAgent}`,
528
- [
529
- "plugin-level settings route the main Claude thread through this agent by default",
530
- ]
531
- );
532
- }
533
-
534
- if (bundledPluginRuntimeDefaults.subagentStatusLineCommand) {
535
- pushSectionLine(
536
- bundledRuntimeSection,
537
- "info",
538
- `Subagent status line configured`,
539
- [bundledPluginRuntimeDefaults.subagentStatusLineCommand]
540
- );
541
- }
542
-
543
- if (bundledPluginRuntimeDefaults.monitorCount > 0) {
544
- const monitorSummary = bundledPluginRuntimeDefaults.monitors
545
- .map((monitor) => `${monitor.name} (${monitor.when})`)
546
- .join(", ");
547
- pushSectionLine(
548
- bundledRuntimeSection,
549
- "ok",
550
- `Plugin monitors ${bundledPluginRuntimeDefaults.monitorCount} configured`,
551
- [
552
- monitorSummary,
553
- "official docs: plugin monitors run only in interactive Claude Code sessions where the Monitor tool is available",
554
- ]
555
- );
556
- }
557
-
558
- if ((bundledPluginRuntimeDefaults.userConfig || []).length > 0) {
559
- const optionSummary = bundledPluginRuntimeDefaults.userConfig
560
- .map((option) => {
561
- const suffix = option.default === undefined ? "" : `=${String(option.default)}`;
562
- return `${option.key}${suffix}`;
563
- })
564
- .join(", ");
565
- pushSectionLine(
566
- bundledRuntimeSection,
567
- "info",
568
- `Plugin options ${bundledPluginRuntimeDefaults.userConfig.length} available`,
569
- [
570
- optionSummary,
571
- "non-sensitive values are stored under pluginConfigs[curdx-flow@curdx-flow-marketplace].options in Claude settings.json",
572
- ]
573
- );
574
- }
575
- }
576
-
577
- if (
578
- bundledPluginRuntimeDefaults?.exists ||
579
- projectClaudeSettings?.pluginOptions
580
- ) {
581
- const configuredOptionsSection = createSection("CurDX-Flow configured options:");
582
- const optionState = projectClaudeSettings?.pluginOptions;
583
- const managedOverrides = optionState?.managed?.overrides || {};
584
- const userOverrides = optionState?.user?.overrides || {};
585
- const projectOverrides = optionState?.project?.overrides || {};
586
- const localOverrides = optionState?.local?.overrides || {};
587
- const machineEffective = optionState?.machineEffective || optionState?.repoEffective || {};
588
- const definitions = bundledPluginRuntimeDefaults?.userConfig?.length
589
- ? bundledPluginRuntimeDefaults.userConfig
590
- : (optionState?.definitions || []);
591
-
592
- pushSectionLine(
593
- configuredOptionsSection,
594
- "info",
595
- "Scope precedence file-managed > settings.local.json > settings.json > ~/.claude/settings.json > bundled default",
596
- [
597
- "server-managed settings, MDM/registry policies, and command-line overrides are not inspected here",
598
- "effective values below reflect this machine only",
599
- ]
600
- );
601
-
602
- if (projectClaudeSettings?.managedSettings?.exists) {
603
- if (Object.keys(managedOverrides).length > 0) {
604
- const summary = Object.entries(managedOverrides)
605
- .map(([key, value]) => `${key}=${formatInlineValue(value)}`)
606
- .join(", ");
607
- pushSectionLine(
608
- configuredOptionsSection,
609
- "info",
610
- `file-managed settings ${Object.keys(managedOverrides).length} valid override(s)`,
611
- [
612
- summary,
613
- projectClaudeSettings.managedSettings.rootPath,
614
- ]
615
- );
616
- } else {
617
- pushSectionLine(
618
- configuredOptionsSection,
619
- "info",
620
- "file-managed settings present without valid CurDX-Flow overrides"
621
- );
622
- }
623
- } else {
624
- pushSectionLine(
625
- configuredOptionsSection,
626
- "info",
627
- "file-managed settings not present"
628
- );
629
- }
630
-
631
- if (projectClaudeSettings?.userExists) {
632
- if (Object.keys(userOverrides).length > 0) {
633
- const summary = Object.entries(userOverrides)
634
- .map(([key, value]) => `${key}=${formatInlineValue(value)}`)
635
- .join(", ");
636
- pushSectionLine(
637
- configuredOptionsSection,
638
- "info",
639
- `~/.claude/settings.json ${Object.keys(userOverrides).length} valid override(s)`,
640
- [
641
- summary,
642
- "user-scoped overrides apply across repositories unless project/local settings win",
643
- ]
644
- );
645
- } else {
646
- pushSectionLine(
647
- configuredOptionsSection,
648
- "info",
649
- "~/.claude/settings.json present without valid CurDX-Flow overrides"
650
- );
651
- }
652
- } else {
653
- pushSectionLine(
654
- configuredOptionsSection,
655
- "info",
656
- "~/.claude/settings.json not present"
657
- );
658
- }
659
-
660
- if (Object.keys(projectOverrides).length > 0) {
661
- const summary = Object.entries(projectOverrides)
662
- .map(([key, value]) => `${key}=${formatInlineValue(value)}`)
663
- .join(", ");
664
- pushSectionLine(
665
- configuredOptionsSection,
666
- "info",
667
- `.claude/settings.json ${Object.keys(projectOverrides).length} valid override(s)`,
668
- [
669
- summary,
670
- "path: pluginConfigs[curdx-flow@curdx-flow-marketplace].options",
671
- ]
672
- );
673
- } else {
674
- pushSectionLine(
675
- configuredOptionsSection,
676
- "info",
677
- ".claude/settings.json no CurDX-Flow repo override"
678
- );
679
- }
680
-
681
- if (projectClaudeSettings?.localExists) {
682
- if (Object.keys(localOverrides).length > 0) {
683
- const summary = Object.entries(localOverrides)
684
- .map(([key, value]) => `${key}=${formatInlineValue(value)}`)
685
- .join(", ");
686
- pushSectionLine(
687
- configuredOptionsSection,
688
- "info",
689
- `.claude/settings.local.json ${Object.keys(localOverrides).length} valid override(s)`,
690
- [
691
- summary,
692
- "local overrides have higher precedence than .claude/settings.json on this machine",
693
- ]
694
- );
695
- } else {
696
- pushSectionLine(
697
- configuredOptionsSection,
698
- "info",
699
- ".claude/settings.local.json present without valid CurDX-Flow overrides"
700
- );
701
- }
702
- } else {
703
- pushSectionLine(
704
- configuredOptionsSection,
705
- "info",
706
- ".claude/settings.local.json not present"
707
- );
708
- }
709
-
710
- for (const definition of definitions) {
711
- const effective = machineEffective[definition.key] || {
712
- value: definition.default,
713
- source: "default",
714
- };
715
- const sourceLabel = effective.source === "local"
716
- ? "local"
717
- : effective.source === "project"
718
- ? "project"
719
- : effective.source === "user"
720
- ? "user"
721
- : effective.source === "managed"
722
- ? "managed"
723
- : "bundled default";
724
- pushSectionLine(
725
- configuredOptionsSection,
726
- "ok",
727
- `${definition.key.padEnd(22)} ${formatInlineValue(effective.value)} (${sourceLabel})`
728
- );
729
- }
730
- }
731
-
732
- if ((projectClaudeSettings?.pluginRuntimeProjection || []).length > 0) {
733
- const runtimeProjectionSection = createSection("CurDX-Flow runtime projection:");
734
-
735
- for (const entry of projectClaudeSettings.pluginRuntimeProjection) {
736
- const sourceLabel = entry.source === "local"
737
- ? "local"
738
- : entry.source === "project"
739
- ? "project"
740
- : entry.source === "user"
741
- ? "user"
742
- : entry.source === "managed"
743
- ? "managed"
744
- : "bundled default";
745
- pushSectionLine(
746
- runtimeProjectionSection,
747
- "info",
748
- `${entry.envVar.padEnd(36)} ${formatInlineValue(entry.value)} (${sourceLabel})`,
749
- [
750
- `consumer: ${entry.consumer}`,
751
- entry.summary,
752
- ...(entry.details || []),
753
- ]
754
- );
755
- }
756
- }
757
-
758
- const localProjectSection = createSection("Local project:");
759
- if (projectState?.exists) {
760
- pushSectionLine(localProjectSection, "ok", `.flow/ ${cwd}`);
761
- if (projectState.activeSpec) {
762
- pushSectionLine(localProjectSection, "info", `Active spec ${projectState.activeSpec}`);
763
- } else {
764
- pushSectionLine(localProjectSection, "info", "Active spec (none)");
765
- }
766
- } else {
767
- pushSectionLine(localProjectSection, "info", ".flow/ not a curdx-flow project (run: curdx-flow init)");
768
- }
769
-
770
- const projectMcpSection = createSection("Project MCP config:");
771
- if (projectMcpConfig?.misplacedExists) {
772
- pushSectionLine(
773
- projectMcpSection,
774
- projectMcpConfig.exists ? "warn" : "err",
775
- `.claude/.mcp.json ignored by Claude Code`,
776
- [
777
- "project MCP config must live at repo root as .mcp.json",
778
- "move .claude/.mcp.json → .mcp.json, then reopen /mcp or rerun doctor",
779
- ]
780
- );
781
- }
782
-
783
- if (projectMcpConfig?.exists) {
784
- if (projectMcpConfig.invalid) {
785
- pushSectionLine(
786
- projectMcpSection,
787
- "err",
788
- `.mcp.json invalid JSON`,
789
- [
790
- projectMcpConfig.parseError,
791
- "fix the JSON syntax, then run /mcp or npx @curdx/flow doctor again",
792
- ]
793
- );
794
- } else if (projectMcpConfig.shapeError) {
795
- pushSectionLine(
796
- projectMcpSection,
797
- "err",
798
- `.mcp.json unsupported shape`,
799
- [
800
- projectMcpConfig.shapeError,
801
- 'expected: { "mcpServers": { "<name>": { ... } } }',
802
- ]
803
- );
804
- } else {
805
- pushSectionLine(
806
- projectMcpSection,
807
- "ok",
808
- `.mcp.json ${projectMcpConfig.serverCount} server(s) declared`
809
- );
810
-
811
- for (const warning of projectMcpConfig.relativePathWarnings || []) {
812
- pushSectionLine(
813
- projectMcpSection,
814
- "warn",
815
- `${warning.serverName.padEnd(22)} relative path in ${warning.field}`,
816
- [
817
- `value: ${warning.value}`,
818
- "Claude Code resolves relative MCP paths against the launch directory, not .mcp.json",
819
- "use an absolute path or a PATH executable such as npx / uvx",
820
- "debug: claude --debug mcp",
821
- ]
822
- );
823
- }
824
- }
825
- } else if (!projectMcpConfig?.misplacedExists) {
826
- pushSectionLine(projectMcpSection, "info", ".mcp.json not present");
827
- }
828
-
829
- const projectTeamsSection = createSection("Project agent teams:");
830
- if (projectTeamConfig?.exists) {
831
- pushSectionLine(
832
- projectTeamsSection,
833
- "warn",
834
- `.claude/teams/teams.json ignored by Claude Code`,
835
- [
836
- "official agent-teams docs say project directories do not have a recognized team config surface",
837
- "remove the file or move team configuration to the supported user-level agent-teams runtime",
838
- ]
839
- );
840
- } else {
841
- pushSectionLine(projectTeamsSection, "info", ".claude/teams/teams.json not present");
842
- }
843
-
844
- const projectSettingsSection = createSection("Project Claude settings:");
845
- const managedSettingsSection = createSection("Managed Claude settings:");
846
- const userSettingsSection = createSection("User Claude settings:");
847
-
848
- if (projectClaudeSettings?.managedSettings?.supported) {
849
- if (projectClaudeSettings.managedSettings.exists) {
850
- pushSectionLine(
851
- managedSettingsSection,
852
- "info",
853
- "File-based managed settings detected",
854
- [projectClaudeSettings.managedSettings.rootPath]
855
- );
856
-
857
- for (const file of projectClaudeSettings.managedSettings.files || []) {
858
- if (file.invalid) {
859
- pushSectionLine(
860
- managedSettingsSection,
861
- "err",
862
- `${file.label} invalid JSON`,
863
- [file.parseError]
864
- );
865
- continue;
866
- }
867
-
868
- if (Object.keys(file.overrides || {}).length > 0) {
869
- const summary = Object.entries(file.overrides)
870
- .map(([key, value]) => `${key}=${formatInlineValue(value)}`)
871
- .join(", ");
872
- pushSectionLine(
873
- managedSettingsSection,
874
- "info",
875
- `${file.label} ${Object.keys(file.overrides).length} CurDX-Flow override(s)`,
876
- [summary]
877
- );
878
- } else {
879
- pushSectionLine(
880
- managedSettingsSection,
881
- "ok",
882
- `${file.label} present`
883
- );
884
- }
885
- }
886
-
887
- if ((projectClaudeSettings.managedWarnings || []).length > 0) {
888
- pushSectionLine(managedSettingsSection, "warn", "Managed CurDX-Flow settings need review");
889
- for (const warning of projectClaudeSettings.managedWarnings) {
890
- pushSectionLine(
891
- managedSettingsSection,
892
- "warn",
893
- warning.message,
894
- projectSettingsWarningDetails(warning)
895
- );
896
- }
897
- }
898
- } else {
899
- pushSectionLine(managedSettingsSection, "info", "File-based managed settings not present");
900
- }
901
- } else {
902
- pushSectionLine(managedSettingsSection, "info", "File-based managed settings not supported on this platform");
903
- }
904
-
905
- if (projectClaudeSettings?.userExists) {
906
- if (projectClaudeSettings.userInvalid) {
907
- pushSectionLine(
908
- userSettingsSection,
909
- "err",
910
- "~/.claude/settings.json invalid JSON",
911
- [projectClaudeSettings.userParseError]
912
- );
913
- } else if ((projectClaudeSettings.userWarnings || []).length > 0) {
914
- pushSectionLine(userSettingsSection, "warn", "~/.claude/settings.json affects CurDX-Flow globally");
915
- for (const warning of projectClaudeSettings.userWarnings) {
916
- pushSectionLine(
917
- userSettingsSection,
918
- "warn",
919
- warning.message,
920
- projectSettingsWarningDetails(warning)
921
- );
922
- }
923
- } else {
924
- pushSectionLine(userSettingsSection, "ok", "~/.claude/settings.json present");
925
- }
926
- } else {
927
- pushSectionLine(userSettingsSection, "info", "~/.claude/settings.json not present");
928
- }
929
-
930
- if (projectClaudeSettings?.exists) {
931
- if (projectClaudeSettings.invalid) {
932
- pushSectionLine(
933
- projectSettingsSection,
934
- "err",
935
- `.claude/settings.json invalid JSON`,
936
- [projectClaudeSettings.parseError]
937
- );
938
- } else if ((projectClaudeSettings.warnings || []).length > 0) {
939
- pushSectionLine(projectSettingsSection, "warn", ".claude/settings.json needs review");
940
- for (const warning of projectClaudeSettings.warnings) {
941
- pushSectionLine(
942
- projectSettingsSection,
943
- "warn",
944
- warning.message,
945
- projectSettingsWarningDetails(warning)
946
- );
947
- }
948
- } else {
949
- pushSectionLine(projectSettingsSection, "ok", ".claude/settings.json conservative");
950
- }
951
- } else {
952
- pushSectionLine(projectSettingsSection, "info", ".claude/settings.json not present");
953
- }
954
-
955
- if (projectClaudeSettings?.localExists) {
956
- pushSectionLine(projectSettingsSection, "info", ".claude/settings.local.json present (local overrides)");
957
- if (projectClaudeSettings.localInvalid) {
958
- pushSectionLine(
959
- projectSettingsSection,
960
- "err",
961
- ".claude/settings.local.json invalid JSON",
962
- [projectClaudeSettings.localParseError]
963
- );
964
- } else if ((projectClaudeSettings.localWarnings || []).length > 0) {
965
- pushSectionLine(projectSettingsSection, "warn", ".claude/settings.local.json affects the local runtime");
966
- for (const warning of projectClaudeSettings.localWarnings) {
967
- pushSectionLine(
968
- projectSettingsSection,
969
- "warn",
970
- warning.message,
971
- projectSettingsWarningDetails(warning)
972
- );
973
- }
974
- }
975
- }
976
-
977
- return { lines, sections, errors, warnings };
978
- }