@uwmd/core 1.1.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 (214) hide show
  1. package/README.md +79 -0
  2. package/dist/agents/bancroft.d.ts +32 -0
  3. package/dist/agents/bancroft.d.ts.map +1 -0
  4. package/dist/agents/bancroft.js +292 -0
  5. package/dist/agents/bancroft.js.map +1 -0
  6. package/dist/agents/schemas.d.ts +13 -0
  7. package/dist/agents/schemas.d.ts.map +1 -0
  8. package/dist/agents/schemas.js +74 -0
  9. package/dist/agents/schemas.js.map +1 -0
  10. package/dist/bindings.d.ts +85 -0
  11. package/dist/bindings.d.ts.map +1 -0
  12. package/dist/bindings.js +293 -0
  13. package/dist/bindings.js.map +1 -0
  14. package/dist/browser.d.ts +66 -0
  15. package/dist/browser.d.ts.map +1 -0
  16. package/dist/browser.js +53 -0
  17. package/dist/browser.js.map +1 -0
  18. package/dist/calc/builtins.d.ts +4 -0
  19. package/dist/calc/builtins.d.ts.map +1 -0
  20. package/dist/calc/builtins.js +329 -0
  21. package/dist/calc/builtins.js.map +1 -0
  22. package/dist/calc/dependencies.d.ts +34 -0
  23. package/dist/calc/dependencies.d.ts.map +1 -0
  24. package/dist/calc/dependencies.js +101 -0
  25. package/dist/calc/dependencies.js.map +1 -0
  26. package/dist/calc/errors.d.ts +8 -0
  27. package/dist/calc/errors.d.ts.map +1 -0
  28. package/dist/calc/errors.js +18 -0
  29. package/dist/calc/errors.js.map +1 -0
  30. package/dist/calc/evaluator.d.ts +5 -0
  31. package/dist/calc/evaluator.d.ts.map +1 -0
  32. package/dist/calc/evaluator.js +184 -0
  33. package/dist/calc/evaluator.js.map +1 -0
  34. package/dist/calc/index.d.ts +14 -0
  35. package/dist/calc/index.d.ts.map +1 -0
  36. package/dist/calc/index.js +56 -0
  37. package/dist/calc/index.js.map +1 -0
  38. package/dist/calc/parser.d.ts +32 -0
  39. package/dist/calc/parser.d.ts.map +1 -0
  40. package/dist/calc/parser.js +361 -0
  41. package/dist/calc/parser.js.map +1 -0
  42. package/dist/cascade.d.ts +85 -0
  43. package/dist/cascade.d.ts.map +1 -0
  44. package/dist/cascade.js +172 -0
  45. package/dist/cascade.js.map +1 -0
  46. package/dist/cli.d.ts +3 -0
  47. package/dist/cli.d.ts.map +1 -0
  48. package/dist/cli.js +843 -0
  49. package/dist/cli.js.map +1 -0
  50. package/dist/codec.d.ts +25 -0
  51. package/dist/codec.d.ts.map +1 -0
  52. package/dist/codec.js +62 -0
  53. package/dist/codec.js.map +1 -0
  54. package/dist/codecs.d.ts +6 -0
  55. package/dist/codecs.d.ts.map +1 -0
  56. package/dist/codecs.js +12 -0
  57. package/dist/codecs.js.map +1 -0
  58. package/dist/compactor.d.ts +11 -0
  59. package/dist/compactor.d.ts.map +1 -0
  60. package/dist/compactor.js +84 -0
  61. package/dist/compactor.js.map +1 -0
  62. package/dist/context-profiles.d.ts +30 -0
  63. package/dist/context-profiles.d.ts.map +1 -0
  64. package/dist/context-profiles.js +225 -0
  65. package/dist/context-profiles.js.map +1 -0
  66. package/dist/context.d.ts +55 -0
  67. package/dist/context.d.ts.map +1 -0
  68. package/dist/context.js +288 -0
  69. package/dist/context.js.map +1 -0
  70. package/dist/dcf.d.ts +9 -0
  71. package/dist/dcf.d.ts.map +1 -0
  72. package/dist/dcf.js +75 -0
  73. package/dist/dcf.js.map +1 -0
  74. package/dist/debt.d.ts +11 -0
  75. package/dist/debt.d.ts.map +1 -0
  76. package/dist/debt.js +85 -0
  77. package/dist/debt.js.map +1 -0
  78. package/dist/defaults.d.ts +104 -0
  79. package/dist/defaults.d.ts.map +1 -0
  80. package/dist/defaults.js +622 -0
  81. package/dist/defaults.js.map +1 -0
  82. package/dist/derived.d.ts +17 -0
  83. package/dist/derived.d.ts.map +1 -0
  84. package/dist/derived.js +29 -0
  85. package/dist/derived.js.map +1 -0
  86. package/dist/editor.d.ts +76 -0
  87. package/dist/editor.d.ts.map +1 -0
  88. package/dist/editor.js +502 -0
  89. package/dist/editor.js.map +1 -0
  90. package/dist/envelope.d.ts +51 -0
  91. package/dist/envelope.d.ts.map +1 -0
  92. package/dist/envelope.js +213 -0
  93. package/dist/envelope.js.map +1 -0
  94. package/dist/format.d.ts +60 -0
  95. package/dist/format.d.ts.map +1 -0
  96. package/dist/format.js +115 -0
  97. package/dist/format.js.map +1 -0
  98. package/dist/gaps.d.ts +62 -0
  99. package/dist/gaps.d.ts.map +1 -0
  100. package/dist/gaps.js +272 -0
  101. package/dist/gaps.js.map +1 -0
  102. package/dist/index.d.ts +75 -0
  103. package/dist/index.d.ts.map +1 -0
  104. package/dist/index.js +47 -0
  105. package/dist/index.js.map +1 -0
  106. package/dist/init.d.ts +15 -0
  107. package/dist/init.d.ts.map +1 -0
  108. package/dist/init.js +169 -0
  109. package/dist/init.js.map +1 -0
  110. package/dist/integrity-canonical.d.ts +17 -0
  111. package/dist/integrity-canonical.d.ts.map +1 -0
  112. package/dist/integrity-canonical.js +124 -0
  113. package/dist/integrity-canonical.js.map +1 -0
  114. package/dist/integrity.d.ts +54 -0
  115. package/dist/integrity.d.ts.map +1 -0
  116. package/dist/integrity.js +244 -0
  117. package/dist/integrity.js.map +1 -0
  118. package/dist/modules.d.ts +24 -0
  119. package/dist/modules.d.ts.map +1 -0
  120. package/dist/modules.js +322 -0
  121. package/dist/modules.js.map +1 -0
  122. package/dist/negotiation.d.ts +19 -0
  123. package/dist/negotiation.d.ts.map +1 -0
  124. package/dist/negotiation.js +70 -0
  125. package/dist/negotiation.js.map +1 -0
  126. package/dist/opstatement.d.ts +9 -0
  127. package/dist/opstatement.d.ts.map +1 -0
  128. package/dist/opstatement.js +80 -0
  129. package/dist/opstatement.js.map +1 -0
  130. package/dist/packs/excel-emit.d.ts +21 -0
  131. package/dist/packs/excel-emit.d.ts.map +1 -0
  132. package/dist/packs/excel-emit.js +135 -0
  133. package/dist/packs/excel-emit.js.map +1 -0
  134. package/dist/packs/index.d.ts +13 -0
  135. package/dist/packs/index.d.ts.map +1 -0
  136. package/dist/packs/index.js +37 -0
  137. package/dist/packs/index.js.map +1 -0
  138. package/dist/packs/industrial.d.ts +3 -0
  139. package/dist/packs/industrial.d.ts.map +1 -0
  140. package/dist/packs/industrial.js +115 -0
  141. package/dist/packs/industrial.js.map +1 -0
  142. package/dist/packs/multifamily.d.ts +3 -0
  143. package/dist/packs/multifamily.d.ts.map +1 -0
  144. package/dist/packs/multifamily.js +82 -0
  145. package/dist/packs/multifamily.js.map +1 -0
  146. package/dist/packs/office.d.ts +3 -0
  147. package/dist/packs/office.d.ts.map +1 -0
  148. package/dist/packs/office.js +108 -0
  149. package/dist/packs/office.js.map +1 -0
  150. package/dist/packs/retail.d.ts +3 -0
  151. package/dist/packs/retail.d.ts.map +1 -0
  152. package/dist/packs/retail.js +115 -0
  153. package/dist/packs/retail.js.map +1 -0
  154. package/dist/packs/self-storage.d.ts +3 -0
  155. package/dist/packs/self-storage.d.ts.map +1 -0
  156. package/dist/packs/self-storage.js +105 -0
  157. package/dist/packs/self-storage.js.map +1 -0
  158. package/dist/parser.d.ts +15 -0
  159. package/dist/parser.d.ts.map +1 -0
  160. package/dist/parser.js +430 -0
  161. package/dist/parser.js.map +1 -0
  162. package/dist/protocol.d.ts +389 -0
  163. package/dist/protocol.d.ts.map +1 -0
  164. package/dist/protocol.js +714 -0
  165. package/dist/protocol.js.map +1 -0
  166. package/dist/refinement.d.ts +63 -0
  167. package/dist/refinement.d.ts.map +1 -0
  168. package/dist/refinement.js +242 -0
  169. package/dist/refinement.js.map +1 -0
  170. package/dist/renderer.d.ts +22 -0
  171. package/dist/renderer.d.ts.map +1 -0
  172. package/dist/renderer.js +478 -0
  173. package/dist/renderer.js.map +1 -0
  174. package/dist/rentroll.d.ts +21 -0
  175. package/dist/rentroll.d.ts.map +1 -0
  176. package/dist/rentroll.js +256 -0
  177. package/dist/rentroll.js.map +1 -0
  178. package/dist/report.d.ts +29 -0
  179. package/dist/report.d.ts.map +1 -0
  180. package/dist/report.js +924 -0
  181. package/dist/report.js.map +1 -0
  182. package/dist/runner.d.ts +43 -0
  183. package/dist/runner.d.ts.map +1 -0
  184. package/dist/runner.js +288 -0
  185. package/dist/runner.js.map +1 -0
  186. package/dist/sourcesuses.d.ts +14 -0
  187. package/dist/sourcesuses.d.ts.map +1 -0
  188. package/dist/sourcesuses.js +63 -0
  189. package/dist/sourcesuses.js.map +1 -0
  190. package/dist/types.d.ts +248 -0
  191. package/dist/types.d.ts.map +1 -0
  192. package/dist/types.js +15 -0
  193. package/dist/types.js.map +1 -0
  194. package/dist/uwcsv.d.ts +23 -0
  195. package/dist/uwcsv.d.ts.map +1 -0
  196. package/dist/uwcsv.js +877 -0
  197. package/dist/uwcsv.js.map +1 -0
  198. package/dist/uwjson.d.ts +19 -0
  199. package/dist/uwjson.d.ts.map +1 -0
  200. package/dist/uwjson.js +61 -0
  201. package/dist/uwjson.js.map +1 -0
  202. package/dist/uwxml.d.ts +18 -0
  203. package/dist/uwxml.d.ts.map +1 -0
  204. package/dist/uwxml.js +297 -0
  205. package/dist/uwxml.js.map +1 -0
  206. package/dist/validator.d.ts +35 -0
  207. package/dist/validator.d.ts.map +1 -0
  208. package/dist/validator.js +553 -0
  209. package/dist/validator.js.map +1 -0
  210. package/dist/valuation.d.ts +9 -0
  211. package/dist/valuation.d.ts.map +1 -0
  212. package/dist/valuation.js +34 -0
  213. package/dist/valuation.js.map +1 -0
  214. package/package.json +58 -0
package/dist/cli.js ADDED
@@ -0,0 +1,843 @@
1
+ #!/usr/bin/env node
2
+ // uwmd CLI — command-line interface for .uw.md files
3
+ // Commands: parse, validate, compact, diff, init, summary, render
4
+ // Usage: uwmd <command> <file> [options]
5
+ import { readFileSync, writeFileSync, existsSync } from 'node:fs';
6
+ import { resolve, basename } from 'node:path';
7
+ import { parseUWFile } from './parser.js';
8
+ import { validateUWFile } from './validator.js';
9
+ import { compact, diff } from './compactor.js';
10
+ import { generateBlankUWFile } from './init.js';
11
+ import { render } from './renderer.js';
12
+ import { renderReportHtml } from './report.js';
13
+ import { stringifyUWJsonWithDigest } from './uwjson.js';
14
+ import { toUWEnvelope } from './envelope.js';
15
+ import { CORE_CODEC_REGISTRY } from './codecs.js';
16
+ import { writeAgentBlock } from './runner.js';
17
+ import { applyEdit } from './editor.js';
18
+ import { verifyChain, verifyProvenance } from './integrity.js';
19
+ import { evaluateCalc } from './calc/index.js';
20
+ import { buildAgentContext, buildAgentPrompt, isContextReady, BANCROFT_LAYERS } from './context.js';
21
+ import { runBancroftAgent } from './agents/bancroft.js';
22
+ import { buildContext } from './context-profiles.js';
23
+ import { rankGaps } from './refinement.js';
24
+ import { resolveValue } from './cascade.js';
25
+ import { getAssetClassDefaults } from './defaults.js';
26
+ import { MULTIFAMILY_PACK, getPackForAssetClass } from './packs/index.js';
27
+ const [, , command, ...args] = process.argv;
28
+ function readFile(path) {
29
+ const resolved = resolve(path);
30
+ if (!existsSync(resolved)) {
31
+ console.error(`Error: file not found: ${resolved}`);
32
+ process.exit(1);
33
+ }
34
+ return readFileSync(resolved, 'utf-8');
35
+ }
36
+ function loadInstitutionConfig(path) {
37
+ if (!path)
38
+ return undefined;
39
+ try {
40
+ return JSON.parse(readFileSync(resolve(path), 'utf-8'));
41
+ }
42
+ catch {
43
+ console.error(`Warning: could not load institution config: ${path}`);
44
+ return undefined;
45
+ }
46
+ }
47
+ function parseFlags(rawArgs) {
48
+ const flags = {};
49
+ for (let i = 0; i < rawArgs.length; i++) {
50
+ const arg = rawArgs[i];
51
+ if (arg.startsWith('--')) {
52
+ const body = arg.slice(2);
53
+ const eqIdx = body.indexOf('=');
54
+ if (eqIdx >= 0) {
55
+ flags[body.slice(0, eqIdx)] = body.slice(eqIdx + 1);
56
+ continue;
57
+ }
58
+ const key = body;
59
+ const next = rawArgs[i + 1];
60
+ if (next && !next.startsWith('--')) {
61
+ flags[key] = next;
62
+ i++;
63
+ }
64
+ else {
65
+ flags[key] = true;
66
+ }
67
+ }
68
+ }
69
+ return flags;
70
+ }
71
+ // ─── Commands ─────────────────────────────────────────────────────────────────
72
+ function cmdParse(file, flags) {
73
+ const content = readFile(file);
74
+ const parsed = parseUWFile(content, { strict: flags['strict'] === true });
75
+ const output = {
76
+ frontmatter: parsed.frontmatter,
77
+ sections: Object.fromEntries(Object.entries(parsed.sections).map(([id, block]) => [id, block])),
78
+ prose: parsed.prose,
79
+ pipeline_log: parsed.pipeline_log.map(b => b.content),
80
+ superseded_blocks: Object.fromEntries(Object.entries(parsed.superseded).map(([id, blocks]) => [id, blocks.map(b => b.content)])),
81
+ };
82
+ console.log(JSON.stringify(output, null, flags['compact'] ? 0 : 2));
83
+ }
84
+ function cmdValidate(file, flags) {
85
+ const content = readFile(file);
86
+ const parsed = parseUWFile(content);
87
+ const instCfg = loadInstitutionConfig(flags['institution']);
88
+ const result = validateUWFile(parsed, instCfg?.thresholds);
89
+ if (flags['json']) {
90
+ console.log(JSON.stringify(result, null, 2));
91
+ return;
92
+ }
93
+ const statusEmoji = {
94
+ clean: '✓',
95
+ warnings: '⚠',
96
+ errors: '✗',
97
+ blocking: '🚫',
98
+ }[result.overall_status];
99
+ console.log(`\n${statusEmoji} ${result.overall_status.toUpperCase()} — ${basename(file)}\n`);
100
+ console.log('Stage Readiness:');
101
+ for (const [stage, ready] of Object.entries(result.stage_readiness)) {
102
+ console.log(` ${ready ? '✓' : '✗'} ${stage}`);
103
+ }
104
+ if (result.issues.length > 0) {
105
+ console.log(`\nIssues (${result.issues.length}):`);
106
+ for (const issue of result.issues) {
107
+ const prefix = issue.severity === 'error' ? '[ERROR]' : issue.severity === 'warning' ? '[WARN] ' : '[INFO] ';
108
+ const location = issue.section ? ` [${issue.section}${issue.field ? `.${issue.field}` : ''}]` : '';
109
+ console.log(` ${prefix}${location} ${issue.message}`);
110
+ }
111
+ }
112
+ else {
113
+ console.log('\nNo issues found.');
114
+ }
115
+ console.log('');
116
+ process.exit(result.errors.length > 0 ? 1 : 0);
117
+ }
118
+ async function cmdVerify(file, flags) {
119
+ const content = readFile(file);
120
+ const parsed = parseUWFile(content);
121
+ // No flag set → run all three.
122
+ const onlyValidate = flags['validate'] === true;
123
+ const onlyIntegrity = flags['integrity'] === true;
124
+ const onlyPolicy = flags['policy'] === true;
125
+ const runAll = !onlyValidate && !onlyIntegrity && !onlyPolicy;
126
+ const sections = {};
127
+ let hadError = false;
128
+ if (runAll || onlyValidate) {
129
+ const v = validateUWFile(parsed);
130
+ sections['validation'] = v;
131
+ if (v.errors.length > 0)
132
+ hadError = true;
133
+ }
134
+ let chain;
135
+ let prov;
136
+ if (runAll || onlyIntegrity) {
137
+ chain = await verifyChain(parsed);
138
+ sections['integrity'] = chain;
139
+ if (!chain.ok)
140
+ hadError = true;
141
+ }
142
+ if (runAll || onlyPolicy) {
143
+ prov = verifyProvenance(parsed);
144
+ sections['provenance'] = prov;
145
+ if (!prov.ok)
146
+ hadError = true;
147
+ }
148
+ if (flags['json']) {
149
+ console.log(JSON.stringify({ ok: !hadError, ...sections }, null, 2));
150
+ }
151
+ else {
152
+ console.log(`\n${hadError ? '✗' : '✓'} ${hadError ? 'FAIL' : 'OK'} — ${basename(file)}\n`);
153
+ if ('validation' in sections) {
154
+ const v = sections['validation'];
155
+ console.log(`Validation: ${v.overall_status} (${v.issues.length} issue${v.issues.length === 1 ? '' : 's'})`);
156
+ for (const issue of v.issues) {
157
+ const tag = issue.severity === 'error' ? '[ERROR]' : issue.severity === 'warning' ? '[WARN] ' : '[INFO] ';
158
+ const loc = issue.section ? ` [${issue.section}${issue.field ? `.${issue.field}` : ''}]` : '';
159
+ console.log(` ${tag}${loc} ${issue.code ? `${issue.code}: ` : ''}${issue.message}`);
160
+ }
161
+ }
162
+ if (chain) {
163
+ console.log(`\nIntegrity: ${chain.ok ? 'ok' : 'FAIL'} — chains_with_hashes=${chain.chains_with_hashes}, chains_verified=${chain.chains_verified}`);
164
+ for (const issue of chain.issues) {
165
+ console.log(` [${issue.severity.toUpperCase()}] ${issue.code}${issue.section ? ` [${issue.section}]` : ''}: ${issue.message}`);
166
+ }
167
+ }
168
+ if (prov) {
169
+ console.log(`\nProvenance: ${prov.ok ? 'ok' : 'FAIL'} (${prov.issues.length} issue${prov.issues.length === 1 ? '' : 's'})`);
170
+ for (const issue of prov.issues) {
171
+ console.log(` [${issue.severity.toUpperCase()}] ${issue.code}${issue.section ? ` [${issue.section}]` : ''}: ${issue.message}`);
172
+ }
173
+ }
174
+ console.log('');
175
+ }
176
+ process.exit(hadError ? 1 : 0);
177
+ }
178
+ function cmdCompact(file, flags) {
179
+ const content = readFile(file);
180
+ const parsed = parseUWFile(content);
181
+ const totalSuperseded = Object.values(parsed.superseded).reduce((n, arr) => n + arr.length, 0);
182
+ if (totalSuperseded === 0) {
183
+ console.log(`No superseded blocks found in ${basename(file)} — nothing to compact.`);
184
+ return;
185
+ }
186
+ const compacted = compact(parsed);
187
+ if (flags['dry-run']) {
188
+ console.log(`Would remove ${totalSuperseded} superseded block(s) from ${basename(file)}.`);
189
+ console.log(`Original: ${content.length} bytes → Compacted: ${compacted.length} bytes`);
190
+ return;
191
+ }
192
+ const outPath = flags['output']
193
+ ? resolve(flags['output'])
194
+ : resolve(file);
195
+ writeFileSync(outPath, compacted, 'utf-8');
196
+ console.log(`Compacted ${totalSuperseded} superseded block(s). Wrote to: ${outPath}`);
197
+ }
198
+ function cmdDiff(fileA, fileB) {
199
+ const contentA = readFile(fileA);
200
+ const contentB = readFile(fileB);
201
+ const parsedA = parseUWFile(contentA);
202
+ const parsedB = parseUWFile(contentB);
203
+ const diffs = diff(parsedA, parsedB);
204
+ const changed = diffs.filter(d => d.status !== 'unchanged');
205
+ if (changed.length === 0) {
206
+ console.log('No differences found between sections.');
207
+ return;
208
+ }
209
+ console.log(`\nDiff: ${basename(fileA)} → ${basename(fileB)}\n`);
210
+ for (const d of changed) {
211
+ const icon = d.status === 'added' ? '+' : d.status === 'removed' ? '-' : '~';
212
+ console.log(` ${icon} ${d.sectionId} (${d.status})`);
213
+ if (d.changedFields?.length) {
214
+ for (const f of d.changedFields) {
215
+ console.log(` · ${f}`);
216
+ }
217
+ }
218
+ }
219
+ console.log('');
220
+ }
221
+ function cmdInit(flags) {
222
+ const content = generateBlankUWFile({
223
+ dealName: flags['name'],
224
+ address: flags['address'],
225
+ city: flags['city'],
226
+ state: flags['state'],
227
+ zip: flags['zip'],
228
+ assetClass: flags['asset-class'],
229
+ dealStage: flags['stage'],
230
+ tier: flags['tier'] ?? 'screener',
231
+ });
232
+ const filename = flags['output']
233
+ ?? (flags['name'] ? `${flags['name'].replace(/\s+/g, '-').toLowerCase()}.uw.md` : 'new-deal.uw.md');
234
+ const outPath = resolve(filename);
235
+ writeFileSync(outPath, content, 'utf-8');
236
+ console.log(`Created: ${outPath}`);
237
+ }
238
+ function cmdSummary(file) {
239
+ const content = readFile(file);
240
+ const parsed = parseUWFile(content);
241
+ const fm = parsed.frontmatter;
242
+ const qm = fm.quick_metrics ?? {};
243
+ const fmt = (v, unit = '') => v == null ? 'n/a' : `${typeof v === 'number' ? v.toLocaleString() : v}${unit}`;
244
+ const pctFmt = (v) => v == null ? 'n/a' : `${(v * 100).toFixed(2)}%`;
245
+ console.log(`
246
+ ─────────────────────────────────────────────────
247
+ ${fm.deal_name ?? 'Untitled'}
248
+ ${fm.property_address ?? ''}, ${fm.city ?? ''} ${fm.state ?? ''}
249
+ ${fm.asset_class ?? ''} · ${fm.deal_stage ?? 'draft'}
250
+ ─────────────────────────────────────────────────
251
+ Purchase Price: $${fmt(qm.purchase_price)}
252
+ Loan Amount: $${fmt(qm.loan_amount)}
253
+ Equity Required: $${fmt(qm.equity_required)}
254
+ UW NOI: $${fmt(qm.noi_underwritten)}
255
+ DSCR: ${fmt(qm.dscr)}x
256
+ LTV: ${pctFmt(qm.ltv)}
257
+ Cap Rate: ${pctFmt(qm.cap_rate)}
258
+ Debt Yield: ${pctFmt(qm.debt_yield)}
259
+ Projected IRR: ${pctFmt(qm.irr_projected)}
260
+ ─────────────────────────────────────────────────
261
+ Deal ID: ${fm.deal_id ?? 'n/a'}
262
+ Flags: ${(fm.flags ?? []).length > 0 ? (fm.flags ?? []).join(', ') : 'none'}
263
+ Blocking: ${(fm.blocking_flags ?? []).length > 0 ? (fm.blocking_flags ?? []).join(', ') : 'none'}
264
+ ─────────────────────────────────────────────────
265
+ Sections present: ${Object.keys(parsed.sections).length}
266
+ Pipeline log entries: ${parsed.pipeline_log.length}
267
+ Superseded blocks: ${Object.values(parsed.superseded).reduce((n, a) => n + a.length, 0)}
268
+ ─────────────────────────────────────────────────
269
+ `);
270
+ }
271
+ async function cmdExport(file, flags) {
272
+ const content = readFile(file);
273
+ const parsed = parseUWFile(content);
274
+ const text = await stringifyUWJsonWithDigest(parsed, {
275
+ // --no-superseded or --compact drops the append-only history.
276
+ includeSuperseded: !(flags['no-superseded'] === true || flags['compact'] === true),
277
+ });
278
+ // Default output path: swap the .uw.md suffix for .uw.json (fall back to appending).
279
+ const outPath = flags['output']
280
+ ? resolve(flags['output'])
281
+ : resolve(file.endsWith('.uw.md') ? `${file.slice(0, -'.uw.md'.length)}.uw.json` : `${file}.uw.json`);
282
+ if (flags['stdout']) {
283
+ process.stdout.write(text);
284
+ return;
285
+ }
286
+ writeFileSync(outPath, text, 'utf-8');
287
+ console.log(`Exported ${basename(file)} → ${basename(outPath)} (.uw.json sibling)`);
288
+ }
289
+ const FORMAT_ALIASES = {
290
+ json: 'uw-json',
291
+ xml: 'uw-xml',
292
+ csv: 'uw-csv-bundle',
293
+ 'csv-bundle': 'uw-csv-bundle',
294
+ 'uw-json': 'uw-json',
295
+ 'uw-xml': 'uw-xml',
296
+ 'uw-csv-bundle': 'uw-csv-bundle',
297
+ };
298
+ async function loadEnvelope(file) {
299
+ if (file.toLowerCase().endsWith('.uw.md')) {
300
+ return toUWEnvelope(parseUWFile(readFile(file)));
301
+ }
302
+ const sourceCodec = CORE_CODEC_REGISTRY.findByFileName(file);
303
+ if (!sourceCodec || !sourceCodec.descriptor.directions.includes('read')) {
304
+ throw new Error(`Cannot detect a readable UW representation for ${file}.`);
305
+ }
306
+ const input = sourceCodec.descriptor.id === 'uw-csv-bundle'
307
+ ? new Uint8Array(readFileSync(resolve(file)))
308
+ : readFile(file);
309
+ return sourceCodec.decode(input);
310
+ }
311
+ function replaceUWExtension(file, extension) {
312
+ const lower = file.toLowerCase();
313
+ for (const suffix of ['.uw.csv.zip', '.uw.md', '.uw.json', '.uw.xml']) {
314
+ if (lower.endsWith(suffix))
315
+ return `${file.slice(0, -suffix.length)}${extension}`;
316
+ }
317
+ return `${file}${extension}`;
318
+ }
319
+ async function cmdConvert(file, flags) {
320
+ const requested = flags['to'];
321
+ if (typeof requested !== 'string') {
322
+ throw new Error('convert requires --to uw-json|uw-xml|uw-csv-bundle.');
323
+ }
324
+ const targetId = FORMAT_ALIASES[requested.toLowerCase()] ?? requested;
325
+ const target = CORE_CODEC_REGISTRY.get(targetId);
326
+ if (!target.descriptor.directions.includes('write')) {
327
+ throw new Error(`Representation ${targetId} is not writable.`);
328
+ }
329
+ const envelope = await loadEnvelope(file);
330
+ const encoded = await target.encode(envelope);
331
+ if (typeof encoded !== 'string' && !(encoded instanceof Uint8Array)) {
332
+ throw new Error(`Representation ${targetId} did not produce file-compatible output.`);
333
+ }
334
+ if (flags['stdout']) {
335
+ process.stdout.write(typeof encoded === 'string' ? encoded : Buffer.from(encoded));
336
+ return;
337
+ }
338
+ const extension = target.descriptor.file_extensions[0];
339
+ const outPath = flags['output']
340
+ ? resolve(flags['output'])
341
+ : resolve(replaceUWExtension(file, extension));
342
+ writeFileSync(outPath, encoded, 'utf-8');
343
+ console.log(`Converted ${basename(file)} → ${basename(outPath)} (${targetId})`);
344
+ }
345
+ function cmdFormats(flags) {
346
+ const descriptors = CORE_CODEC_REGISTRY.list();
347
+ if (flags['json']) {
348
+ console.log(JSON.stringify(descriptors, null, 2));
349
+ return;
350
+ }
351
+ console.log('Registered UW representations:\n');
352
+ for (const descriptor of descriptors) {
353
+ console.log(` ${descriptor.id.padEnd(16)} ${descriptor.fidelity.padEnd(6)} ${descriptor.directions.join('/').padEnd(10)} ${descriptor.media_types[0]}`);
354
+ }
355
+ }
356
+ function cmdReport(file, flags) {
357
+ const content = readFile(file);
358
+ const parsed = parseUWFile(content);
359
+ const result = renderReportHtml(parsed, {
360
+ tier: flags['tier'],
361
+ preparedBy: flags['prepared-by'],
362
+ });
363
+ // Default output path: swap the .uw.md suffix for .report.html.
364
+ const outPath = flags['output']
365
+ ? resolve(flags['output'])
366
+ : resolve(file.endsWith('.uw.md') ? `${file.slice(0, -'.uw.md'.length)}.report.html` : `${file}.report.html`);
367
+ if (flags['stdout']) {
368
+ process.stdout.write(result.html);
369
+ return;
370
+ }
371
+ writeFileSync(outPath, result.html, 'utf-8');
372
+ const label = result.tier === 'analyst' ? 'credit memo' : 'lender package';
373
+ console.log(`Rendered ${label} → ${basename(outPath)} (${result.sectionsRendered.length} sections${result.sectionsSkipped.length ? `, skipped: ${result.sectionsSkipped.join(', ')}` : ''})`);
374
+ console.log('For PDF output, use @uwmd/report (uwmd-report) or print this HTML from a browser.');
375
+ }
376
+ function cmdScope(file, flags) {
377
+ const content = readFile(file);
378
+ const parsed = parseUWFile(content);
379
+ const assetClass = flags['asset-class'] ??
380
+ parsed.frontmatter.asset_class ??
381
+ 'multifamily';
382
+ const table = getAssetClassDefaults(assetClass);
383
+ if (!table) {
384
+ console.error(`No published asset-class default table for '${assetClass}'.`);
385
+ process.exit(1);
386
+ }
387
+ const out = {};
388
+ for (const path of Object.keys(table.fields)) {
389
+ const r = resolveValue(path, parsed);
390
+ out[path] = {
391
+ value: r.value,
392
+ step: r.step,
393
+ range: r.range,
394
+ resolved_from: r.resolved_from,
395
+ };
396
+ }
397
+ const payload = {
398
+ deal_id: parsed.frontmatter.deal_id ?? null,
399
+ deal_stage_target: 'scope',
400
+ asset_class: assetClass,
401
+ defaults_table: `${assetClass}@${table.version}`,
402
+ resolved: out,
403
+ };
404
+ const text = JSON.stringify(payload, null, 2);
405
+ if (flags['output']) {
406
+ writeFileSync(resolve(flags['output']), text, 'utf-8');
407
+ console.log(`Scope view written to ${flags['output']} (${Object.keys(out).length} fields)`);
408
+ }
409
+ else {
410
+ process.stdout.write(`${text}\n`);
411
+ }
412
+ }
413
+ function cmdRefine(file, flags) {
414
+ const content = readFile(file);
415
+ const parsed = parseUWFile(content);
416
+ const targets = flags['targets']
417
+ ? flags['targets'].split(',').map(s => s.trim()).filter(Boolean)
418
+ : undefined;
419
+ const top = flags['top'] ? Number.parseInt(flags['top'], 10) : 10;
420
+ const assetClass = flags['asset-class'] ??
421
+ parsed.frontmatter.asset_class ??
422
+ 'multifamily';
423
+ const pack = getPackForAssetClass(assetClass) ?? MULTIFAMILY_PACK;
424
+ const result = rankGaps(parsed, { targets, top, packs: [pack] });
425
+ if (flags['json']) {
426
+ process.stdout.write(`${JSON.stringify(result, null, 2)}\n`);
427
+ return;
428
+ }
429
+ console.log('\n─── Refinement ranking ───');
430
+ console.log(`graph_size=${result.diagnostics.graph_size} resolved=${result.diagnostics.resolved} perturbations=${result.diagnostics.perturbations}`);
431
+ if (result.diagnostics.non_monotonic.length > 0) {
432
+ console.log(`⚠ Non-monotonic outputs: ${result.diagnostics.non_monotonic.map(w => w.output_id).join(', ')}`);
433
+ }
434
+ console.log('');
435
+ if (result.by_voi.length === 0) {
436
+ console.log('No gap-driven inputs found among the requested targets.');
437
+ return;
438
+ }
439
+ result.by_voi.forEach((g, i) => {
440
+ console.log(`${i + 1}. ${g.field_path}`);
441
+ console.log(` range: [${g.prior_range.low} … ${g.prior_range.central} … ${g.prior_range.high}]`);
442
+ console.log(` voi: ${g.total_voi.toFixed(4)}`);
443
+ console.log(` touches: ${g.affected_outputs.map(o => o.output_id).join(', ')}`);
444
+ if (g.question_template)
445
+ console.log(` ask: "${g.question_template}"`);
446
+ console.log('');
447
+ });
448
+ }
449
+ // ─── Router ───────────────────────────────────────────────────────────────────
450
+ const flags = parseFlags(args);
451
+ // Positional args: non-flag tokens that don't immediately follow a --flag
452
+ const positional = [];
453
+ for (let _i = 0; _i < args.length; _i++) {
454
+ const a = args[_i];
455
+ if (a.startsWith('--')) {
456
+ _i++;
457
+ continue;
458
+ } // skip flag + its value
459
+ positional.push(a);
460
+ }
461
+ // Top-level async wrapper so `run --live` can use await
462
+ (async () => {
463
+ switch (command) {
464
+ case 'parse':
465
+ if (!positional[0]) {
466
+ console.error('Usage: uwmd parse <file> [--compact] [--strict]');
467
+ process.exit(1);
468
+ }
469
+ cmdParse(positional[0], flags);
470
+ break;
471
+ case 'validate':
472
+ if (!positional[0]) {
473
+ console.error('Usage: uwmd validate <file> [--stage <stage>] [--institution <config.json>] [--json]');
474
+ process.exit(1);
475
+ }
476
+ cmdValidate(positional[0], flags);
477
+ break;
478
+ case 'verify':
479
+ if (!positional[0]) {
480
+ console.error('Usage: uwmd verify <file> [--validate] [--integrity] [--policy] [--json]');
481
+ process.exit(1);
482
+ }
483
+ await cmdVerify(positional[0], flags);
484
+ break;
485
+ case 'compact':
486
+ if (!positional[0]) {
487
+ console.error('Usage: uwmd compact <file> [--output <file>] [--dry-run]');
488
+ process.exit(1);
489
+ }
490
+ cmdCompact(positional[0], flags);
491
+ break;
492
+ case 'diff':
493
+ if (!positional[0] || !positional[1]) {
494
+ console.error('Usage: uwmd diff <file-a> <file-b>');
495
+ process.exit(1);
496
+ }
497
+ cmdDiff(positional[0], positional[1]);
498
+ break;
499
+ case 'render': {
500
+ if (!positional[0]) {
501
+ console.error('Usage: uwmd render <file> [--format <json|csv|chat|summary>] [--profile <summary|live|compact|full|relevant>] [--sections a,b,c] [--max-tokens 12000] [--no-meta] [--output <file>] [--tier screener|analyst]');
502
+ process.exit(1);
503
+ }
504
+ const content = readFile(positional[0]);
505
+ const parsedFile = parseUWFile(content);
506
+ if (flags['profile']) {
507
+ const profile = flags['profile'];
508
+ const validProfiles = ['summary', 'live', 'compact', 'full', 'relevant'];
509
+ if (!validProfiles.includes(profile)) {
510
+ console.error(`Unknown profile '${profile}'. Valid: ${validProfiles.join(' | ')}`);
511
+ process.exit(1);
512
+ }
513
+ const sectionsList = flags['sections']
514
+ ? flags['sections'].split(',').map(s => s.trim()).filter(Boolean)
515
+ : undefined;
516
+ const result = buildContext(parsedFile, profile, {
517
+ sections: sectionsList,
518
+ maxTokens: flags['max-tokens'] ? Number.parseInt(flags['max-tokens'], 10) : undefined,
519
+ includeMeta: flags['no-meta'] ? false : undefined,
520
+ });
521
+ if (flags['output']) {
522
+ writeFileSync(resolve(flags['output']), result.content, 'utf-8');
523
+ console.log(`Rendered profile=${profile} → ${flags['output']}${result.truncated ? ' [TRUNCATED]' : ''} (~${result.tokenEstimate} tokens)`);
524
+ }
525
+ else {
526
+ process.stdout.write(`${result.content}\n`);
527
+ process.stderr.write(`(~${result.tokenEstimate} tokens, profile=${profile}${result.truncated ? ', truncated' : ''})\n`);
528
+ }
529
+ break;
530
+ }
531
+ const fmt = flags['format'] ?? 'summary';
532
+ const result = render(parsedFile, {
533
+ format: fmt,
534
+ tier: flags['tier'],
535
+ maxTokens: flags['max-tokens'] ? Number.parseInt(flags['max-tokens'], 10) : undefined,
536
+ });
537
+ if (flags['output']) {
538
+ writeFileSync(resolve(flags['output']), result.content, 'utf-8');
539
+ console.log(`Rendered ${fmt} → ${flags['output']}${result.truncated ? ' [TRUNCATED]' : ''}${result.estimatedTokens ? ` (~${result.estimatedTokens} tokens)` : ''}`);
540
+ }
541
+ else {
542
+ process.stdout.write(`${result.content}\n`);
543
+ if (result.estimatedTokens)
544
+ process.stderr.write(`(~${result.estimatedTokens} tokens)\n`);
545
+ }
546
+ break;
547
+ }
548
+ case 'run': {
549
+ if (!positional[0]) {
550
+ console.error('Usage: uwmd run <file> --agent <agent_id> [--section <id>] [--context-only] [--output <file>]');
551
+ process.exit(1);
552
+ }
553
+ const agentId = flags['agent'];
554
+ if (!agentId) {
555
+ console.error('--agent <agent_id> is required (e.g. --agent L1, --agent L2-CRE-11)');
556
+ process.exit(1);
557
+ }
558
+ const fileContent = readFile(positional[0]);
559
+ const parsedFile = parseUWFile(fileContent);
560
+ const ctx = buildAgentContext(parsedFile, agentId);
561
+ if (flags['context-only']) {
562
+ // Print the context that would be sent to the agent — useful for debugging
563
+ console.log(`\n─── Agent: ${agentId} — ${ctx.layer.name} ───`);
564
+ console.log(`Reads: ${ctx.layer.reads.join(', ') || 'all'}`);
565
+ console.log(`Writes: ${ctx.layer.writes.join(', ')}`);
566
+ console.log(`Sections available: ${Object.keys(ctx.sections).join(', ')}`);
567
+ console.log(`Missing required: ${ctx.missingRequired.join(', ') || 'none'}`);
568
+ console.log(`Missing optional: ${ctx.missingOptional.join(', ') || 'none'}`);
569
+ console.log(`Blocking flags: ${ctx.blockingFlags.join(', ') || 'none'}`);
570
+ console.log(`Context ready: ${isContextReady(ctx)}`);
571
+ console.log(`\n── Chat context (~${ctx.chatTokenEstimate} tokens) ──\n`);
572
+ console.log(ctx.chatContext);
573
+ break;
574
+ }
575
+ if (flags['prompt']) {
576
+ // Print the system prompt + user message that would be sent to Claude
577
+ const { systemPrompt, userMessage, outputSchemaDescription } = buildAgentPrompt(ctx);
578
+ console.log('=== SYSTEM PROMPT ===\n');
579
+ console.log(systemPrompt);
580
+ console.log('\n=== OUTPUT SCHEMA ===\n');
581
+ console.log(outputSchemaDescription);
582
+ console.log('\n=== USER MESSAGE ===\n');
583
+ console.log(userMessage);
584
+ break;
585
+ }
586
+ if (!isContextReady(ctx)) {
587
+ console.error(`Cannot run ${agentId}: context not ready.`);
588
+ if (ctx.blockingFlags.length)
589
+ console.error(` Blocking flags: ${ctx.blockingFlags.join(', ')}`);
590
+ if (ctx.missingRequired.length)
591
+ console.error(` Missing required sections: ${ctx.missingRequired.join(', ')}`);
592
+ process.exit(1);
593
+ }
594
+ // --live: actually call Claude and write the result back to the file
595
+ if (flags['live']) {
596
+ const apiKey = flags['api-key'] ?? process.env['ANTHROPIC_API_KEY'];
597
+ if (!apiKey) {
598
+ console.error('--live requires ANTHROPIC_API_KEY env var or --api-key flag');
599
+ process.exit(1);
600
+ }
601
+ console.log(`\nRunning ${agentId} — ${ctx.layer.name} against ${basename(positional[0])}`);
602
+ console.log(`Model: ${flags['model'] ?? 'claude-sonnet-4-6'}\n`);
603
+ const result = await runBancroftAgent(fileContent, agentId, {
604
+ apiKey,
605
+ model: flags['model'],
606
+ userInstructions: flags['instructions'],
607
+ onProgress: (evt) => {
608
+ const icons = { context_built: '●', calling_claude: '⟳', parsing_output: '◆', writing_block: '✎', complete: '✓', error: '✗' };
609
+ console.log(` ${icons[evt.stage] ?? '·'} [${evt.stage}] ${evt.message}`);
610
+ },
611
+ });
612
+ if (!result.success) {
613
+ console.error(`\nAgent failed: ${result.error}`);
614
+ process.exit(1);
615
+ }
616
+ const outPath = flags['output'] ? resolve(flags['output']) : resolve(positional[0]);
617
+ writeFileSync(outPath, result.updatedContent, 'utf-8');
618
+ console.log(`\nWrote: ${result.sectionsWritten.join(', ')} → ${basename(outPath)}`);
619
+ console.log(`Tokens: ${result.tokensUsed.input} in / ${result.tokensUsed.output} out | ${result.durationMs}ms`);
620
+ break;
621
+ }
622
+ // When --section and --json-input are provided, write a block from stdin
623
+ const sectionId = flags['section'];
624
+ if (sectionId && flags['json-input']) {
625
+ const jsonInput = readFile(flags['json-input']);
626
+ let blockContent;
627
+ try {
628
+ blockContent = JSON.parse(jsonInput);
629
+ }
630
+ catch (err) {
631
+ console.error(`Could not parse JSON input: ${err}`);
632
+ process.exit(1);
633
+ }
634
+ const runResult = writeAgentBlock(fileContent, parsedFile, { sectionId, content: blockContent }, {
635
+ agentId,
636
+ actor: 'system',
637
+ inputSections: ctx.layer.reads,
638
+ });
639
+ const outPath = flags['output'] ? resolve(flags['output']) : resolve(positional[0]);
640
+ writeFileSync(outPath, runResult.content, 'utf-8');
641
+ console.log(`Wrote ${sectionId} v${runResult.newBlock.version} to ${basename(outPath)} [${runResult.supersededPriorBlock ? 'superseded prior' : 'new section'}]`);
642
+ break;
643
+ }
644
+ // No JSON input — print instructions for how to use this command
645
+ console.log(`\nAgent: ${agentId} — ${ctx.layer.name}`);
646
+ console.log(`Context ready: ${isContextReady(ctx)}\n`);
647
+ console.log('To write a block from agent output:');
648
+ console.log(` uwmd run ${positional[0]} --agent ${agentId} --section <id> --json-input <output.json> [--output <file>]`);
649
+ console.log('\nTo inspect the context this agent would receive:');
650
+ console.log(` uwmd run ${positional[0]} --agent ${agentId} --context-only`);
651
+ console.log('\nTo view the system + user prompts:');
652
+ console.log(` uwmd run ${positional[0]} --agent ${agentId} --prompt`);
653
+ break;
654
+ }
655
+ case 'layers':
656
+ console.log('\nBancroft agent layers:\n');
657
+ for (const layer of BANCROFT_LAYERS) {
658
+ console.log(` ${layer.id.padEnd(4)} ${layer.name.padEnd(22)} reads: ${(layer.reads[0] === '*' ? ['all'] : layer.reads).slice(0, 3).join(', ')}${layer.reads.length > 3 ? '…' : ''}`);
659
+ }
660
+ console.log('');
661
+ break;
662
+ case 'edit': {
663
+ if (!positional[0] || !positional[1]) {
664
+ console.error('Usage: uwmd edit <file> <operation.json> [--actor <name>] [--source <pattern>] [--agent-id <id>] [--agent-version <v>] [--confidence high|medium|low] [--output <file>]');
665
+ process.exit(1);
666
+ }
667
+ const fileContent = readFile(positional[0]);
668
+ const opJson = readFile(positional[1]);
669
+ let op;
670
+ try {
671
+ op = JSON.parse(opJson);
672
+ }
673
+ catch (err) {
674
+ console.error(`Could not parse operation JSON: ${err}`);
675
+ process.exit(1);
676
+ }
677
+ const parsedFile = parseUWFile(fileContent);
678
+ const ctx = {
679
+ actor: flags['actor'] ?? 'system',
680
+ source: flags['source'] ?? 'manual',
681
+ agentId: flags['agent-id'] ?? null,
682
+ agentVersion: flags['agent-version'] ?? null,
683
+ confidence: flags['confidence'] ?? 'medium',
684
+ };
685
+ const result = applyEdit(fileContent, parsedFile, op, ctx);
686
+ if (!result.ok || !result.content) {
687
+ console.error(`Edit rejected: [${result.error?.code}] ${result.error?.message}`);
688
+ if (result.error?.pointer)
689
+ console.error(` pointer: ${result.error.pointer}`);
690
+ process.exit(1);
691
+ }
692
+ const outPath = flags['output'] ? resolve(flags['output']) : resolve(positional[0]);
693
+ writeFileSync(outPath, result.content, 'utf-8');
694
+ const summary = op.kind === 'frontmatter_set'
695
+ ? `frontmatter.${op.path}`
696
+ : op.kind === 'pipeline_log_append'
697
+ ? 'pipeline_log entry'
698
+ : `${op.section_id} v${result.newVersion}${result.supersededPriorBlock ? ' [superseded prior]' : ''}`;
699
+ console.log(`Applied ${op.kind} (${summary}) → ${basename(outPath)}`);
700
+ break;
701
+ }
702
+ case 'calc': {
703
+ if (!positional[0] || !positional[1]) {
704
+ console.error('Usage: uwmd calc <file> <calc.json|formula>');
705
+ process.exit(1);
706
+ }
707
+ const fileContent = readFile(positional[0]);
708
+ const parsed = parseUWFile(fileContent);
709
+ // Second arg is either a JSON file path or a raw formula string.
710
+ let decls;
711
+ const argTwo = positional[1];
712
+ if (existsSync(resolve(argTwo))) {
713
+ const raw = JSON.parse(readFileSync(resolve(argTwo), 'utf-8'));
714
+ decls = Array.isArray(raw) ? raw : [raw];
715
+ }
716
+ else {
717
+ decls = [{ id: 'inline', label: 'inline', formula: argTwo, deterministic: true }];
718
+ }
719
+ const ctx = {
720
+ parsed,
721
+ prior_results: {},
722
+ locale: 'en-US',
723
+ };
724
+ const results = decls.map((d) => evaluateCalc(d, ctx));
725
+ for (const r of results) {
726
+ if (r.ok) {
727
+ console.log(`${r.calc_id} = ${r.display ?? String(r.value)}`);
728
+ }
729
+ else {
730
+ console.log(`${r.calc_id} ERROR [${r.error?.code}] ${r.error?.message}`);
731
+ }
732
+ }
733
+ if (flags['json']) {
734
+ console.log(JSON.stringify(results, null, 2));
735
+ }
736
+ if (results.some((r) => !r.ok))
737
+ process.exit(1);
738
+ break;
739
+ }
740
+ case 'init':
741
+ cmdInit(flags);
742
+ break;
743
+ case 'summary':
744
+ if (!positional[0]) {
745
+ console.error('Usage: uwmd summary <file>');
746
+ process.exit(1);
747
+ }
748
+ cmdSummary(positional[0]);
749
+ break;
750
+ case 'export':
751
+ if (!positional[0]) {
752
+ console.error('Usage: uwmd export <file.uw.md> [--output <file.uw.json>] [--no-superseded] [--stdout]');
753
+ process.exit(1);
754
+ }
755
+ await cmdExport(positional[0], flags);
756
+ break;
757
+ case 'formats':
758
+ cmdFormats(flags);
759
+ break;
760
+ case 'convert':
761
+ if (!positional[0]) {
762
+ console.error('Usage: uwmd convert <file.uw.md|file.uw.json|file.uw.xml|file.uw.csv.zip> --to uw-json|uw-xml|uw-csv-bundle [--output <file>] [--stdout]');
763
+ process.exit(1);
764
+ }
765
+ await cmdConvert(positional[0], flags);
766
+ break;
767
+ case 'report':
768
+ if (!positional[0]) {
769
+ console.error('Usage: uwmd report <file.uw.md> [--tier screener|analyst] [--prepared-by <name>] [--output <file.html>] [--stdout]');
770
+ process.exit(1);
771
+ }
772
+ cmdReport(positional[0], flags);
773
+ break;
774
+ case 'scope':
775
+ if (!positional[0]) {
776
+ console.error('Usage: uwmd scope <file> [--asset-class multifamily] [--output <file>]');
777
+ process.exit(1);
778
+ }
779
+ cmdScope(positional[0], flags);
780
+ break;
781
+ case 'refine':
782
+ if (!positional[0]) {
783
+ console.error('Usage: uwmd refine <file> [--targets dscr,debt_yield] [--top 5] [--json]');
784
+ process.exit(1);
785
+ }
786
+ cmdRefine(positional[0], flags);
787
+ break;
788
+ default:
789
+ console.log(`
790
+ uwmd — .uw.md underwriting file toolkit
791
+
792
+ Commands:
793
+ parse <file> Parse file and output JSON
794
+ validate <file> Run all validation checks
795
+ verify <file> Validate + verify integrity (hashes) + provenance (actor/policy)
796
+ render <file> Render to output format (see --format)
797
+ run <file> Invoke a Bancroft agent (see --agent)
798
+ edit <file> <op.json> Apply an EditOperation (Tier-2)
799
+ compact <file> Strip superseded blocks
800
+ diff <file-a> <file-b> Compare two files section by section
801
+ calc <file> <calc.json> Evaluate a calc declaration or inline formula (Tier-3)
802
+ init Generate a blank .uw.md file
803
+ summary <file> Print quick metrics to terminal
804
+ export <file> Export a digested UW JSON 1.0 document
805
+ formats List registered machine representations
806
+ convert <file> Convert Markdown/JSON/XML/CSV bundle representations
807
+ report <file> Render the lender package / credit memo HTML (§7.1/§7.2)
808
+ scope <file> Resolve every required input via the fallback cascade and emit a triage view
809
+ refine <file> Rank gaps by value-of-information for stated calc targets
810
+ layers List Bancroft agent layers
811
+
812
+ Options:
813
+ --output <path> Output file path (compact, init)
814
+ --dry-run Show what would change without writing (compact)
815
+ --json JSON output (validate)
816
+ --strict Throw on parse errors instead of collecting
817
+ --format <f> Render format: json|csv|chat|summary (render, default: summary)
818
+ --no-superseded Drop append-only history from the .uw.json export (export)
819
+ --stdout Write export/convert output to stdout
820
+ --to <format> Target representation: uw-json|uw-xml|uw-csv-bundle (convert)
821
+ --max-tokens <n> Max tokens for chat render (default: 12000)
822
+ --institution <f> Path to .uw.institution.json config (validate)
823
+ --name <n> Deal name (init)
824
+ --address <a> Property address (init)
825
+ --city <c> City (init)
826
+ --state <s> State code (init)
827
+ --asset-class <ac> Asset class (init)
828
+ --stage <s> Deal stage (init)
829
+ --tier <t> screener | analyst (init)
830
+ --live Actually call Claude (run command)
831
+ --api-key <k> Anthropic API key (run --live, or use ANTHROPIC_API_KEY)
832
+ --model <m> Claude model (run --live, default: claude-sonnet-4-6)
833
+ --instructions <i> Extra instructions for the agent (run --live)
834
+ --actor <n> Actor name for the edit (edit, default: system)
835
+ --source <s> _meta.source pattern (edit, default: manual)
836
+ --agent-id <id> Agent identifier when source is agent/* (edit)
837
+ --agent-version <v>Agent semver (edit)
838
+ --confidence <c> Confidence level for new block: high|medium|low (edit)
839
+ `);
840
+ process.exit(0);
841
+ }
842
+ })().catch(err => { console.error(err); process.exit(1); });
843
+ //# sourceMappingURL=cli.js.map