@pugi/cli 0.1.0-beta.4 → 0.1.0-beta.40

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 (249) hide show
  1. package/THIRD_PARTY_NOTICES.md +40 -0
  2. package/assets/pugi-mascot.ansi +15 -25
  3. package/bin/run.js +33 -1
  4. package/dist/commands/jobs-watch.js +201 -0
  5. package/dist/commands/jobs.js +15 -0
  6. package/dist/commands/smoke.js +133 -0
  7. package/dist/core/agent-progress/cleanup.js +134 -0
  8. package/dist/core/agent-progress/schema.js +144 -0
  9. package/dist/core/agent-progress/writer.js +101 -0
  10. package/dist/core/artifact-chain/dispatcher.js +148 -0
  11. package/dist/core/artifact-chain/exporter.js +164 -0
  12. package/dist/core/artifact-chain/state.js +243 -0
  13. package/dist/core/artifact-chain/steps.js +169 -0
  14. package/dist/core/auth/ensure-authenticated.js +129 -0
  15. package/dist/core/auth/env-provider.js +238 -0
  16. package/dist/core/auto-update/channels.js +122 -0
  17. package/dist/core/auto-update/checker.js +241 -0
  18. package/dist/core/auto-update/state.js +235 -0
  19. package/dist/core/bare-mode/index.js +107 -0
  20. package/dist/core/bash-classifier.js +108 -1
  21. package/dist/core/checkpoint/resumer.js +149 -0
  22. package/dist/core/checkpoint/rewinder.js +291 -0
  23. package/dist/core/codegraph/decision-store.js +248 -0
  24. package/dist/core/codegraph/detect-repo.js +459 -0
  25. package/dist/core/codegraph/install.js +134 -0
  26. package/dist/core/codegraph/offer-hook.js +220 -0
  27. package/dist/core/compact/auto-trigger.js +96 -0
  28. package/dist/core/compact/buffer-rewriter.js +115 -0
  29. package/dist/core/compact/summarizer.js +208 -0
  30. package/dist/core/compact/token-counter.js +108 -0
  31. package/dist/core/consensus/diff-capture.js +73 -0
  32. package/dist/core/context/index.js +7 -0
  33. package/dist/core/context/markdown-traverse.js +255 -0
  34. package/dist/core/cost/rate-card.js +129 -0
  35. package/dist/core/cost/tracker.js +221 -0
  36. package/dist/core/denial-tracking/index.js +8 -0
  37. package/dist/core/denial-tracking/state.js +264 -0
  38. package/dist/core/diagnostics/probe-runner.js +93 -0
  39. package/dist/core/diagnostics/probes/api.js +46 -0
  40. package/dist/core/diagnostics/probes/auth.js +86 -0
  41. package/dist/core/diagnostics/probes/bare-mode.js +42 -0
  42. package/dist/core/diagnostics/probes/cli-version.js +127 -0
  43. package/dist/core/diagnostics/probes/config.js +72 -0
  44. package/dist/core/diagnostics/probes/denial-tracking.js +57 -0
  45. package/dist/core/diagnostics/probes/disk.js +81 -0
  46. package/dist/core/diagnostics/probes/git.js +65 -0
  47. package/dist/core/diagnostics/probes/mcp.js +75 -0
  48. package/dist/core/diagnostics/probes/node.js +59 -0
  49. package/dist/core/diagnostics/probes/pnpm.js +36 -0
  50. package/dist/core/diagnostics/probes/pugi-md.js +89 -0
  51. package/dist/core/diagnostics/probes/session.js +74 -0
  52. package/dist/core/diagnostics/probes/status-snapshot.js +488 -0
  53. package/dist/core/diagnostics/probes/workspace.js +63 -0
  54. package/dist/core/diagnostics/types.js +70 -0
  55. package/dist/core/dispatch/cache-cleanup.js +197 -0
  56. package/dist/core/dispatch/cache-handoff.js +295 -0
  57. package/dist/core/edits/dispatch.js +218 -2
  58. package/dist/core/edits/journal.js +199 -0
  59. package/dist/core/edits/layer-d-ast.js +557 -14
  60. package/dist/core/edits/verify-hook.js +273 -0
  61. package/dist/core/edits/worktree.js +322 -0
  62. package/dist/core/engine/anvil-client.js +115 -5
  63. package/dist/core/engine/budgets.js +98 -0
  64. package/dist/core/engine/context-prefix.js +155 -0
  65. package/dist/core/engine/intent.js +260 -0
  66. package/dist/core/engine/native-pugi.js +860 -211
  67. package/dist/core/engine/prompts.js +88 -2
  68. package/dist/core/engine/strip-internal-fields.js +124 -0
  69. package/dist/core/engine/tool-bridge.js +992 -36
  70. package/dist/core/feedback/queue.js +177 -0
  71. package/dist/core/feedback/submitter.js +145 -0
  72. package/dist/core/file-cache.js +113 -1
  73. package/dist/core/hooks/events.js +44 -0
  74. package/dist/core/hooks/index.js +15 -0
  75. package/dist/core/hooks/registry.js +213 -0
  76. package/dist/core/hooks/runner.js +236 -0
  77. package/dist/core/hooks/v2/event-emitter.js +115 -0
  78. package/dist/core/hooks/v2/executor.js +282 -0
  79. package/dist/core/hooks/v2/index.js +25 -0
  80. package/dist/core/hooks/v2/lifecycle.js +104 -0
  81. package/dist/core/hooks/v2/loader.js +216 -0
  82. package/dist/core/hooks/v2/matcher.js +125 -0
  83. package/dist/core/hooks/v2/trust.js +143 -0
  84. package/dist/core/hooks/v2/types.js +86 -0
  85. package/dist/core/lsp/cache.js +105 -0
  86. package/dist/core/lsp/client.js +776 -0
  87. package/dist/core/lsp/language-detect.js +66 -0
  88. package/dist/core/lsp/post-edit-diagnostics.js +171 -0
  89. package/dist/core/mcp/client.js +75 -6
  90. package/dist/core/mcp/http-server.js +553 -0
  91. package/dist/core/mcp/orchestrator-tools.js +662 -0
  92. package/dist/core/mcp/permission.js +190 -0
  93. package/dist/core/mcp/registry.js +24 -2
  94. package/dist/core/mcp/server-tools.js +219 -0
  95. package/dist/core/mcp/server.js +397 -0
  96. package/dist/core/memory/dual-write.js +416 -0
  97. package/dist/core/memory/phase1-kinds.js +20 -0
  98. package/dist/core/memory-sync/queue.js +158 -0
  99. package/dist/core/onboarding/ensure-initialized.js +133 -0
  100. package/dist/core/onboarding/marker.js +111 -0
  101. package/dist/core/onboarding/telemetry-state.js +108 -0
  102. package/dist/core/output-style/presets.js +176 -0
  103. package/dist/core/output-style/state.js +185 -0
  104. package/dist/core/permissions/auto-classifier.js +124 -0
  105. package/dist/core/permissions/circuit-breaker.js +83 -0
  106. package/dist/core/permissions/gate.js +278 -0
  107. package/dist/core/permissions/index.js +20 -0
  108. package/dist/core/permissions/mode.js +174 -0
  109. package/dist/core/permissions/state.js +241 -0
  110. package/dist/core/permissions/tool-class.js +93 -0
  111. package/dist/core/prd-check/parser.js +215 -0
  112. package/dist/core/prd-check/reporter.js +127 -0
  113. package/dist/core/prd-check/session-review.js +557 -0
  114. package/dist/core/prd-check/verifiers.js +223 -0
  115. package/dist/core/pugi-md/context-injector.js +76 -0
  116. package/dist/core/pugi-md/walk-up.js +207 -0
  117. package/dist/core/release-notes/parser.js +241 -0
  118. package/dist/core/release-notes/state.js +116 -0
  119. package/dist/core/repl/history.js +11 -1
  120. package/dist/core/repl/model-pricing.js +135 -0
  121. package/dist/core/repl/session.js +1899 -38
  122. package/dist/core/repl/slash-commands.js +406 -21
  123. package/dist/core/repl/store/session-store.js +31 -2
  124. package/dist/core/repl/workspace-context.js +22 -0
  125. package/dist/core/repo-map/build.js +125 -0
  126. package/dist/core/repo-map/cache.js +185 -0
  127. package/dist/core/repo-map/extractor.js +254 -0
  128. package/dist/core/repo-map/formatter.js +145 -0
  129. package/dist/core/repo-map/scanner.js +211 -0
  130. package/dist/core/retry-budget/budget.js +284 -0
  131. package/dist/core/retry-budget/index.js +5 -0
  132. package/dist/core/session.js +92 -0
  133. package/dist/core/settings.js +80 -0
  134. package/dist/core/share/formatter.js +271 -0
  135. package/dist/core/share/redactor.js +221 -0
  136. package/dist/core/share/uploader.js +267 -0
  137. package/dist/core/skills/defaults.js +457 -0
  138. package/dist/core/smoke/headless-driver.js +174 -0
  139. package/dist/core/smoke/orchestrator.js +194 -0
  140. package/dist/core/smoke/runner.js +238 -0
  141. package/dist/core/smoke/scenario-parser.js +316 -0
  142. package/dist/core/subagents/dispatcher-real.js +600 -0
  143. package/dist/core/subagents/dispatcher.js +113 -24
  144. package/dist/core/subagents/index.js +18 -5
  145. package/dist/core/subagents/isolation-matrix.js +213 -0
  146. package/dist/core/subagents/spawn.js +19 -4
  147. package/dist/core/telemetry/emitter.js +229 -0
  148. package/dist/core/telemetry/queue.js +251 -0
  149. package/dist/core/theme/context.js +91 -0
  150. package/dist/core/theme/presets.js +228 -0
  151. package/dist/core/theme/state.js +181 -0
  152. package/dist/core/todos/invariant.js +10 -0
  153. package/dist/core/todos/state.js +177 -0
  154. package/dist/core/transport/version-interceptor.js +166 -0
  155. package/dist/core/vim/keymap.js +288 -0
  156. package/dist/core/vim/state.js +92 -0
  157. package/dist/index.js +28 -0
  158. package/dist/runtime/bootstrap.js +190 -0
  159. package/dist/runtime/cli.js +3073 -321
  160. package/dist/runtime/commands/cancel.js +231 -0
  161. package/dist/runtime/commands/chain.js +489 -0
  162. package/dist/runtime/commands/codegraph-status.js +227 -0
  163. package/dist/runtime/commands/compact.js +297 -0
  164. package/dist/runtime/commands/cost.js +199 -0
  165. package/dist/runtime/commands/delegate.js +242 -11
  166. package/dist/runtime/commands/dispatch.js +126 -0
  167. package/dist/runtime/commands/doctor.js +390 -0
  168. package/dist/runtime/commands/feedback.js +184 -0
  169. package/dist/runtime/commands/hooks.js +184 -0
  170. package/dist/runtime/commands/lsp.js +368 -0
  171. package/dist/runtime/commands/mcp.js +879 -0
  172. package/dist/runtime/commands/memory.js +508 -0
  173. package/dist/runtime/commands/model.js +237 -0
  174. package/dist/runtime/commands/onboarding.js +275 -0
  175. package/dist/runtime/commands/patch.js +128 -0
  176. package/dist/runtime/commands/permissions.js +112 -0
  177. package/dist/runtime/commands/plan.js +143 -0
  178. package/dist/runtime/commands/prd-check.js +285 -0
  179. package/dist/runtime/commands/redo-blob-store.js +92 -0
  180. package/dist/runtime/commands/redo.js +361 -0
  181. package/dist/runtime/commands/release-notes.js +229 -0
  182. package/dist/runtime/commands/repo-map.js +95 -0
  183. package/dist/runtime/commands/report.js +299 -0
  184. package/dist/runtime/commands/resume.js +118 -0
  185. package/dist/runtime/commands/review-consensus.js +17 -2
  186. package/dist/runtime/commands/rewind.js +333 -0
  187. package/dist/runtime/commands/sessions.js +163 -0
  188. package/dist/runtime/commands/share.js +316 -0
  189. package/dist/runtime/commands/status.js +186 -0
  190. package/dist/runtime/commands/stickers.js +82 -0
  191. package/dist/runtime/commands/style.js +194 -0
  192. package/dist/runtime/commands/theme.js +196 -0
  193. package/dist/runtime/commands/undo.js +32 -0
  194. package/dist/runtime/commands/update.js +289 -0
  195. package/dist/runtime/commands/vim.js +140 -0
  196. package/dist/runtime/commands/worktree.js +177 -0
  197. package/dist/runtime/headless-repl.js +195 -0
  198. package/dist/runtime/headless.js +543 -0
  199. package/dist/runtime/load-hooks-or-exit.js +71 -0
  200. package/dist/runtime/plan-decompose.js +531 -0
  201. package/dist/runtime/version.js +65 -0
  202. package/dist/tools/agent-tool.js +229 -0
  203. package/dist/tools/apply-patch.js +556 -0
  204. package/dist/tools/ask-user-question.js +213 -0
  205. package/dist/tools/ask-user.js +115 -0
  206. package/dist/tools/file-tools.js +85 -14
  207. package/dist/tools/lsp-tools.js +189 -0
  208. package/dist/tools/mcp-tool.js +260 -0
  209. package/dist/tools/multi-edit.js +361 -0
  210. package/dist/tools/registry.js +46 -0
  211. package/dist/tools/skill-tool.js +96 -0
  212. package/dist/tools/tasks.js +208 -0
  213. package/dist/tools/todo-write.js +184 -0
  214. package/dist/tools/web-fetch.js +147 -2
  215. package/dist/tools/web-search.js +458 -0
  216. package/dist/tui/agent-progress-card.js +111 -0
  217. package/dist/tui/agent-tree.js +10 -0
  218. package/dist/tui/ask-modal.js +2 -2
  219. package/dist/tui/ask-user-question-prompt.js +192 -0
  220. package/dist/tui/compact-banner.js +81 -0
  221. package/dist/tui/conversation-pane.js +82 -8
  222. package/dist/tui/cost-table.js +111 -0
  223. package/dist/tui/doctor-table.js +46 -0
  224. package/dist/tui/feedback-prompt.js +156 -0
  225. package/dist/tui/input-box.js +69 -2
  226. package/dist/tui/markdown-render.js +4 -4
  227. package/dist/tui/onboarding-wizard.js +240 -0
  228. package/dist/tui/permissions-picker.js +86 -0
  229. package/dist/tui/render.js +35 -0
  230. package/dist/tui/repl-render.js +303 -13
  231. package/dist/tui/repl-splash.js +2 -2
  232. package/dist/tui/repl.js +72 -14
  233. package/dist/tui/splash.js +1 -1
  234. package/dist/tui/status-bar.js +94 -16
  235. package/dist/tui/status-table.js +7 -0
  236. package/dist/tui/stickers-art.js +136 -0
  237. package/dist/tui/style-table.js +28 -0
  238. package/dist/tui/theme-table.js +29 -0
  239. package/dist/tui/tool-stream-pane.js +52 -3
  240. package/dist/tui/update-banner.js +20 -2
  241. package/dist/tui/vim-input.js +267 -0
  242. package/docs/examples/codegraph.mcp.json +10 -0
  243. package/package.json +12 -6
  244. package/test/scenarios/codegen-create-file.scenario.txt +13 -0
  245. package/test/scenarios/compact-force.scenario.txt +11 -0
  246. package/test/scenarios/identity.scenario.txt +11 -0
  247. package/test/scenarios/persona-handoff.scenario.txt +11 -0
  248. package/test/scenarios/walkback.scenario.txt +12 -0
  249. package/dist/core/engine/compaction-hook.js +0 -154
@@ -1,4 +1,6 @@
1
1
  import { getJobRegistry, summarizeJobsForPrompt, } from '../jobs/registry.js';
2
+ import { compileStyleBlock } from '../output-style/presets.js';
3
+ import { resolveOutputStyle } from '../output-style/state.js';
2
4
  /**
3
5
  * System prompts for each engine command. Each prompt:
4
6
  * - Anchors the model in Pugi's local-first contract (ADR-0037).
@@ -22,10 +24,62 @@ import { getJobRegistry, summarizeJobsForPrompt, } from '../jobs/registry.js';
22
24
  const COMMON_LOCAL_FIRST_PREAMBLE = [
23
25
  'You are the Pugi CLI agent running locally inside the operator\'s repository.',
24
26
  'The local filesystem is the source of truth. Every change you make is committed locally; nothing is uploaded by default (ADR-0037 local-first).',
27
+ // R1 fix (2026-05-26, PR #413 r1, Fix 5 Option B): only advertise the
28
+ // tools currently wired in `tool-bridge.ts::WIRED_TOOLS`. α7.7 ships
29
+ // apply_patch / lsp_* / worktree_* as CLI-only surfaces (`pugi patch`,
30
+ // `pugi lsp`, `pugi worktree`); wiring them into the engine loop is
31
+ // deferred to β2 (apply_patch), β4 (LSP tools), β7 (worktree tools)
32
+ // per the consolidated sprint plan. Advertising them in the system
33
+ // prompt without a matching executor entry caused Mira to attempt
34
+ // calls that returned `unknown_tool` — broken eval surface.
25
35
  'You have a tool registry: read, write, edit, grep, glob, bash. Call tools to inspect and modify the workspace.',
26
36
  'Cite file paths relative to the workspace root. Keep edits minimal and reversible.',
27
37
  'When you are done, return a single final text answer that the operator can read on the CLI.',
28
38
  ].join(' ');
39
+ /**
40
+ * β5a P1+P6 (2026-05-26): prompt v2 — intent marker contract +
41
+ * definitional examples + jargon ban. Fixes the dominant Pugi loss
42
+ * mode in the α7.X Phase 2 comparative eval: tool-use on pure
43
+ * knowledge questions ("What is grep?" → bash man grep).
44
+ *
45
+ * The CLI-side `classifyIntent` (apps/pugi-cli/src/core/engine/intent.ts)
46
+ * wraps definitional questions with `<intent kind="definitional">` on
47
+ * the user message before send. The rules below teach the model what
48
+ * to do with that marker.
49
+ *
50
+ * Voice constraint: same banned-jargon list as the cabinet Mira
51
+ * persona (брифую / диспатчу / шипаю and the English jargon list
52
+ * from BANNED_WORDS in mira.system-prompt.ts). Repeated here verbatim
53
+ * so the CLI surface has its own enforcement copy; the cabinet copy
54
+ * is the source of truth and ships through the runtime persona
55
+ * prompt for the cabinet UI. CLI runs DO NOT load the cabinet
56
+ * persona prompt — engine prompts are the only place to enforce
57
+ * voice for `pugi explain` / `pugi code` callers.
58
+ */
59
+ const INTENT_MARKER_CONTRACT = [
60
+ '# Intent contract',
61
+ 'When the operator\'s message starts with `<intent kind="definitional">`, treat it as a knowledge question:',
62
+ ' - Answer in prose. Do NOT call any tools.',
63
+ ' - Cite a file from the repo only when it directly supports the explanation.',
64
+ ' - Keep the answer to one short paragraph unless the operator asked for depth.',
65
+ 'When no intent marker is present OR the marker is operational, use tools as needed.',
66
+ '',
67
+ '# Definitional examples',
68
+ 'Operator: `<intent kind="definitional">What is grep?</intent>`',
69
+ 'You: `grep is a Unix command-line tool that searches plain text for lines matching a regular expression. It reads stdin or files and prints matching lines to stdout.`',
70
+ '(No tool calls. One paragraph. No file paths because grep is a generic Unix tool, not a repo artefact.)',
71
+ '',
72
+ 'Operator: `<intent kind="definitional">что такое pgvector?</intent>`',
73
+ 'You: `pgvector - это расширение PostgreSQL для хранения и поиска векторных эмбеддингов. Поддерживает ivfflat и hnsw индексы; используется для RAG и semantic search.`',
74
+ '(One short paragraph. No tools.)',
75
+ ].join('\n');
76
+ const JARGON_BAN = [
77
+ '# Voice',
78
+ 'Brand voice: terse, operator-grade, English in code paths / Russian-Ukrainian permitted in chat answers.',
79
+ 'Banned words (CLI customer-facing output): "брифую", "диспатчу", "шипаю", "journey", "explore", "delight", "magical", "friendly", "let me help", "I\'d be happy to", "pug-tastic". Use neutral verbs instead: brief / dispatch / ship / build / read / write.',
80
+ 'No em-dashes. No emoji. No "AI assistant" framing.',
81
+ ].join('\n');
82
+ const PROMPT_V2_APPENDIX = [INTENT_MARKER_CONTRACT, JARGON_BAN].join('\n\n');
29
83
  const PLAN_TOOLS_NOTE = 'PLAN MODE IS READ-ONLY. You may call read, grep, glob. Calls to write, edit, or bash will be refused and end the run. Produce a written plan, not changes.';
30
84
  const EDIT_FLOW_RULES = [
31
85
  'Before calling edit on a file you have not yet read this session, call read first — the edit tool fails otherwise.',
@@ -34,10 +88,42 @@ const EDIT_FLOW_RULES = [
34
88
  ].join(' ');
35
89
  export function systemPromptFor(kind) {
36
90
  const base = baseSystemPromptFor(kind);
91
+ // β5a P1+P6: append the prompt-v2 intent-marker contract +
92
+ // definitional examples + jargon ban to every command kind. The
93
+ // appendix is shared across kinds because the marker contract +
94
+ // voice gate are command-agnostic — a definitional question lands
95
+ // the same way under `pugi explain` and `pugi code`.
96
+ const withV2 = `${base}\n\n${PROMPT_V2_APPENDIX}`;
97
+ // Leak L18 (2026-05-27): output-style preset block. Compiled from
98
+ // workspace > user > default precedence. The `default` preset
99
+ // returns an empty block (no override over the base voice), so the
100
+ // injection is a no-op for the most-common case. Wrapped in
101
+ // try/catch — every IO failure inside `resolveOutputStyle` already
102
+ // degrades to the default slug, but defence in depth keeps the
103
+ // engine prompt assembly from ever crashing on a hand-edited
104
+ // config.json.
105
+ const styleBlock = formatOutputStyleBlock();
106
+ const withStyle = styleBlock ? `${withV2}\n\n${styleBlock}` : withV2;
37
107
  const snapshot = formatBackgroundJobsSnapshot(getJobRegistrySafely());
38
108
  if (!snapshot)
39
- return base;
40
- return `${base}\n\n${snapshot}`;
109
+ return withStyle;
110
+ return `${withStyle}\n\n${snapshot}`;
111
+ }
112
+ /**
113
+ * Resolve the active output-style preset for the current process
114
+ * and compile it into a prompt block. Returns empty string for the
115
+ * default preset OR for any unexpected IO failure so the engine
116
+ * prompt assembly path is unaffected by a missing / malformed
117
+ * config.json.
118
+ */
119
+ function formatOutputStyleBlock() {
120
+ try {
121
+ const resolved = resolveOutputStyle({ workspaceRoot: process.cwd() });
122
+ return compileStyleBlock(resolved.slug);
123
+ }
124
+ catch {
125
+ return '';
126
+ }
41
127
  }
42
128
  function baseSystemPromptFor(kind) {
43
129
  switch (kind) {
@@ -0,0 +1,124 @@
1
+ /**
2
+ * α7 L3 (2026-05-27) — leak-parity: underscore-prefix internal-fields filter.
3
+ *
4
+ * The convention (observed in the leaked Claude Code BashTool surface and
5
+ * codified in `docs/research/2026-05-27-pugi-gap-analysis-3-repos.md` §1)
6
+ * is that tool-argument fields whose names start with a leading underscore
7
+ * are INTERNAL — populated by the dispatcher at call time (sessionId,
8
+ * tenantId, correlation handles, hook context, ask-modal bridge handles)
9
+ * but never advertised to the model. The model schema MUST omit them so:
10
+ *
11
+ * 1. No token cost — internal context never burns model budget.
12
+ * 2. No fabrication risk — the model cannot hallucinate values for
13
+ * sessionId / tenantId / etc. because the field is invisible.
14
+ * 3. No leak surface — implementation detail stays implementation detail.
15
+ *
16
+ * The dispatcher (see `tool-bridge.ts::buildExecutor`) does NOT strip these
17
+ * fields at call time. It passes the full args record (including any
18
+ * `_internal*` keys an upstream layer injected) straight to the tool
19
+ * handler. Only the schema surface that the engine adapter ships to the
20
+ * model is filtered.
21
+ *
22
+ * This module is intentionally narrow: it accepts a JSON Schema fragment
23
+ * and returns a deep clone with `_`-prefixed keys removed from every
24
+ * `properties` map encountered while walking, and with `required` filtered
25
+ * to drop any references to those keys. It descends into nested object
26
+ * schemas and into the `items` schema of arrays. It is JSON-Schema-version
27
+ * agnostic (works on draft-07, 2019-09, 2020-12 alike) because it only
28
+ * inspects `properties`/`required`/`items` and leaves the rest of the
29
+ * fragment alone.
30
+ *
31
+ * Edge cases handled:
32
+ * - `_` alone (single underscore) is treated as internal and stripped.
33
+ * - Nested object schemas inside `properties` get the same treatment
34
+ * (a sub-property whose name starts with `_` is removed too).
35
+ * - Array `items` are walked. Tuple schemas (`items` as array) are
36
+ * walked element-by-element.
37
+ * - `oneOf`/`anyOf`/`allOf` branches are walked.
38
+ * - Non-object inputs (null, primitives, arrays passed as the root)
39
+ * are returned as-is — defensive no-op, never throws.
40
+ *
41
+ * Contract notes:
42
+ * - Pure function. Input is never mutated.
43
+ * - Output is a deep clone — every nested object/array is freshly
44
+ * allocated so callers can mutate safely.
45
+ * - JSON-only — does not preserve symbols, getters, or class instances
46
+ * because JSON Schema is plain-data by spec.
47
+ */
48
+ const INTERNAL_PREFIX = '_';
49
+ /**
50
+ * Returns true when the field name should be stripped from the model-
51
+ * facing schema. Leading underscore is the contract — single `_` is also
52
+ * stripped (no escape hatch). Empty-string keys (which are technically
53
+ * valid JSON) are left alone so we do not silently drop them.
54
+ */
55
+ export function isInternalFieldName(name) {
56
+ return name.length > 0 && name.startsWith(INTERNAL_PREFIX);
57
+ }
58
+ /**
59
+ * Strip `_`-prefixed properties from a JSON Schema fragment. Recursively
60
+ * walks nested object schemas and array `items`. Returns a deep clone;
61
+ * the input is never mutated.
62
+ *
63
+ * Pass-through behaviour:
64
+ * - Non-object / null / array inputs round-trip unchanged (as deep
65
+ * clones where applicable).
66
+ * - Fragments with no `properties` key are returned as deep clones
67
+ * after walking `items`/`oneOf`/`anyOf`/`allOf`.
68
+ */
69
+ export function stripInternalFields(schema) {
70
+ if (schema === null || typeof schema !== 'object')
71
+ return schema;
72
+ if (Array.isArray(schema)) {
73
+ return schema.map((item) => stripInternalFields(item));
74
+ }
75
+ return walkObject(schema);
76
+ }
77
+ function walkObject(node) {
78
+ const out = {};
79
+ for (const [key, value] of Object.entries(node)) {
80
+ if (key === 'properties' && value !== null && typeof value === 'object' && !Array.isArray(value)) {
81
+ out[key] = walkProperties(value);
82
+ continue;
83
+ }
84
+ if (key === 'required' && Array.isArray(value)) {
85
+ out[key] = value.filter((entry) => typeof entry === 'string' && !isInternalFieldName(entry));
86
+ continue;
87
+ }
88
+ if (key === 'items') {
89
+ out[key] = stripInternalFields(value);
90
+ continue;
91
+ }
92
+ if (key === 'oneOf' || key === 'anyOf' || key === 'allOf') {
93
+ if (Array.isArray(value)) {
94
+ out[key] = value.map((branch) => stripInternalFields(branch));
95
+ continue;
96
+ }
97
+ }
98
+ // Default: deep-clone any nested objects/arrays so the caller can
99
+ // mutate freely without touching the input. Primitives pass through.
100
+ out[key] = cloneJson(value);
101
+ }
102
+ return out;
103
+ }
104
+ function walkProperties(props) {
105
+ const out = {};
106
+ for (const [propName, propSchema] of Object.entries(props)) {
107
+ if (isInternalFieldName(propName))
108
+ continue;
109
+ out[propName] = stripInternalFields(propSchema);
110
+ }
111
+ return out;
112
+ }
113
+ function cloneJson(value) {
114
+ if (value === null || typeof value !== 'object')
115
+ return value;
116
+ if (Array.isArray(value))
117
+ return value.map((item) => cloneJson(item));
118
+ const out = {};
119
+ for (const [key, val] of Object.entries(value)) {
120
+ out[key] = cloneJson(val);
121
+ }
122
+ return out;
123
+ }
124
+ //# sourceMappingURL=strip-internal-fields.js.map