@moda-ai/cli 1.55.27 → 1.55.28

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 (43) hide show
  1. package/dist/{agent-vp9prdma.js → agent-0w9rtdsw.js} +22 -16
  2. package/dist/cli-3n0gz4d0.js +17971 -0
  3. package/dist/{cli-q9nsvqqg.js → cli-3retgh64.js} +85 -1165
  4. package/dist/{cli-ccjfwzqx.js → cli-97jew729.js} +1 -1
  5. package/dist/{cli-r5pp7qny.js → cli-99p2jh28.js} +42 -456
  6. package/dist/{cli-0pkc8jhg.js → cli-9bgk5jeh.js} +8 -6
  7. package/dist/{cli-hmjdgxrn.js → cli-9bgnvth2.js} +5 -3
  8. package/dist/cli-9kfxkxmk.js +6994 -0
  9. package/dist/cli-cesds766.js +1516 -0
  10. package/dist/{cli-v8q7sd7g.js → cli-dr6x0zv7.js} +1 -1
  11. package/dist/{cli-pg5qfmqc.js → cli-e23g9ywa.js} +1 -1
  12. package/dist/{cli-vdkwkkp5.js → cli-fwpqghgc.js} +4 -2
  13. package/dist/cli-k4tqwah4.js +89 -0
  14. package/dist/{cli-6et4z504.js → cli-mg5zv4s6.js} +15 -13
  15. package/dist/{cli-wzds257x.js → cli-n2hng3gv.js} +6 -4
  16. package/dist/cli-ns5fcnkz.js +341 -0
  17. package/dist/{cli-4h0ave0f.js → cli-ny48a8de.js} +12 -9
  18. package/dist/{cli-qwgct6x7.js → cli-qpq84eee.js} +5220 -32716
  19. package/dist/cli-xq45etg1.js +1098 -0
  20. package/dist/cli-yf39avhh.js +754 -0
  21. package/dist/cli-yz8xcpyk.js +12 -0
  22. package/dist/cli.js +77 -153
  23. package/dist/doctor-k3ta6h2y.js +24 -0
  24. package/dist/{fix-75s9s0d6.js → fix-w5z02wt4.js} +8 -9
  25. package/dist/{harness-a7tb9a23.js → harness-7azmzytc.js} +15 -7
  26. package/dist/{harness-github-actions-6v4wydb8.js → harness-github-actions-bv2qrnqb.js} +11 -5
  27. package/dist/harness-report-rd9c4e9v.js +54 -0
  28. package/dist/{index-91bpc8g8.js → index-d1z50dsq.js} +41 -33
  29. package/dist/{production-intelligence-7nqddd4f.js → production-intelligence-wvsgjxth.js} +9 -6
  30. package/dist/{profile-commands-49yn02yg.js → profile-commands-h8evz9x0.js} +7 -4
  31. package/dist/prompt-attribution-1nytc9wj.js +148 -0
  32. package/dist/prompt-source-mcp.js +14 -7
  33. package/dist/prompt-sources-07tczyqa.js +19 -0
  34. package/dist/{prompts-17wyrst6.js → prompts-6y9wfq5z.js} +8 -7
  35. package/dist/prompts-ab-ezrd63sk.js +70 -0
  36. package/dist/prompts-propose-2k3enm2q.js +255 -0
  37. package/dist/{provision-bc3h68e1.js → provision-y5bdbh4y.js} +9 -6
  38. package/dist/{registry-fpzhjznj.js → registry-96aze5wp.js} +7 -4
  39. package/dist/{skills-ccdbqghj.js → skills-j8865009.js} +7 -5
  40. package/dist/{sync-hkxce5zh.js → sync-ew7yvy0e.js} +16 -10
  41. package/package.json +1 -1
  42. package/skills/integration/index.json +2 -2
  43. package/dist/doctor-wmre3mr7.js +0 -22
@@ -0,0 +1,1098 @@
1
+ // ../../packages/messaging/messaging.json
2
+ var messaging_default = {
3
+ $schema: "./messaging.schema.json",
4
+ version: "1.0.4",
5
+ locked_on: "2026-10-15",
6
+ approved_by: "mo",
7
+ source: "Branding project docs/positioning.md v1.0.4",
8
+ canonical_strings: {
9
+ version: "1.0.4",
10
+ locked_on: "2026-10-15",
11
+ approved_by: "mo",
12
+ strings: [
13
+ {
14
+ id: "one_liner",
15
+ source: "approved",
16
+ text: "Infrastructure for catching AI agent mistakes in production and turning them into evals.",
17
+ match: "exact",
18
+ surfaces: [
19
+ "apps/moda-frontend/components/landing/hero.tsx (subhead under the H1)",
20
+ "apps/moda-frontend/lib/site.ts SITE_DESCRIPTION",
21
+ "apps/moda-frontend/lib/agent-md.ts MODA_POSITIONING",
22
+ "apps/moda-frontend/components/seo/json-ld.tsx Organization, WebSite and SoftwareApplication description",
23
+ "apps/moda-frontend/content/llms/homepage.md",
24
+ "apps/moda-frontend/app/llms.txt/route.ts (summary line)",
25
+ "docs/index.mdx frontmatter description",
26
+ "AGENTS.md and .cursorrules (What Moda Is)",
27
+ "Y Combinator company page one-liner"
28
+ ]
29
+ },
30
+ {
31
+ id: "category_label",
32
+ source: "approved",
33
+ text: "Production monitoring and evals for AI agents.",
34
+ match: "contains_ci",
35
+ surfaces: [
36
+ "apps/moda-frontend/app/llms.txt/route.ts (after the tagline)",
37
+ "every bio, package description and directory listing below"
38
+ ]
39
+ },
40
+ {
41
+ id: "tagline_h1",
42
+ source: "approved",
43
+ text: "Your agents should never make the same mistake twice.",
44
+ match: "exact",
45
+ surfaces: [
46
+ "apps/moda-frontend/components/landing/hero.tsx H1",
47
+ "apps/moda-frontend/lib/agent-md.ts MODA_TAGLINE",
48
+ "Y Combinator company page headline"
49
+ ]
50
+ },
51
+ {
52
+ id: "page_title",
53
+ source: "approved",
54
+ text: "Moda - Production monitoring and evals for AI agents",
55
+ match: "exact",
56
+ max_chars: 60,
57
+ surfaces: [
58
+ "apps/moda-frontend/app/layout.tsx metadata.title default",
59
+ "apps/moda-frontend/app/page.tsx metadata.title",
60
+ "og:title and twitter:title on the homepage"
61
+ ]
62
+ },
63
+ {
64
+ id: "faq_lead",
65
+ source: "approved",
66
+ text: "Moda helps engineers make sure their agents never make the same mistake twice.",
67
+ match: "contains_ci",
68
+ surfaces: [
69
+ "apps/moda-frontend/lib/faq.ts first answer (also emitted as FAQPage JSON-LD)",
70
+ "apps/moda-frontend/content/llms/faq.md"
71
+ ]
72
+ },
73
+ {
74
+ id: "meta_description",
75
+ source: "derived",
76
+ text: "Production monitoring and evals for AI agents. Infrastructure for catching AI agent mistakes in production and turning them into evals.",
77
+ match: "exact",
78
+ max_chars: 160,
79
+ surfaces: [
80
+ "apps/moda-frontend/app/layout.tsx metadata.description, openGraph.description, twitter.description",
81
+ "apps/moda-frontend/app/page.tsx metadata.description, openGraph.description, twitter.description"
82
+ ]
83
+ },
84
+ {
85
+ id: "bio",
86
+ source: "derived",
87
+ text: "Production monitoring and evals for AI agents. Your agents should never make the same mistake twice.",
88
+ match: "exact",
89
+ max_chars: 120,
90
+ surfaces: [
91
+ "X @modaflows bio",
92
+ "GitHub org ModaLabs bio",
93
+ "LinkedIn tagline",
94
+ "Crunchbase short description",
95
+ "Product Hunt, G2 and other directory taglines"
96
+ ]
97
+ },
98
+ {
99
+ id: "long_description",
100
+ source: "derived",
101
+ text: "Infrastructure for catching AI agent mistakes in production and turning them into evals. Agents fail in ways that never throw an error: frustrated users, failing tools, lazy or hallucinated answers. Moda reads every production trace end to end, detects these failures, and groups them into problems ranked by how many users they affect, with the traces behind each one. When your team changes a prompt, tool, or model, Moda replays the change against those traces and shows whether it worked, then keeps monitoring after release. Works with any model provider through SDKs, OpenTelemetry, or HTTP. Backed by Y Combinator.",
102
+ match: "exact",
103
+ surfaces: [
104
+ "LinkedIn About",
105
+ "Crunchbase full description",
106
+ "Y Combinator company page description",
107
+ "GitHub org profile README (profile/README.md)",
108
+ "press boilerplate"
109
+ ]
110
+ },
111
+ {
112
+ id: "sdk_node_description",
113
+ source: "derived",
114
+ text: "Official Node.js/TypeScript SDK for Moda: production monitoring and evals for AI agents.",
115
+ match: "exact",
116
+ surfaces: [
117
+ "npm moda-ai package.json description",
118
+ "GitHub repo description for the Node SDK"
119
+ ]
120
+ },
121
+ {
122
+ id: "sdk_python_description",
123
+ source: "derived",
124
+ text: "Official Python SDK for Moda: production monitoring and evals for AI agents.",
125
+ match: "exact",
126
+ surfaces: [
127
+ "PyPI moda-ai summary (pyproject.toml description)",
128
+ "GitHub moda-python repo description"
129
+ ]
130
+ },
131
+ {
132
+ id: "cli_description",
133
+ source: "derived",
134
+ text: "CLI for Moda: production monitoring and evals for AI agents.",
135
+ match: "exact",
136
+ surfaces: [
137
+ "apps/moda-cli/package.json description",
138
+ "apps/moda-cli/README.md first line",
139
+ "apps/moda-cli/src/tui.ts banner subtitle"
140
+ ]
141
+ },
142
+ {
143
+ id: "mcp_intro",
144
+ source: "derived",
145
+ text: "Moda is production monitoring and evals for AI agents: it ingests your agents' production traces (one trace = one complete agent run) and turns them into ranked problems, detections, and replayable tests.",
146
+ match: "exact",
147
+ surfaces: [
148
+ "apps/moda-mcp/src/instructions.ts SERVER_INSTRUCTIONS first sentence"
149
+ ]
150
+ },
151
+ {
152
+ id: "init_prompt_line",
153
+ source: "derived",
154
+ text: "Add the Moda SDK to this project so its AI agents are monitored in production.",
155
+ match: "exact",
156
+ surfaces: [
157
+ "apps/moda-cli/src/init/sdk-integration.ts prompt header",
158
+ "apps/moda-onboarding-evals/prompts/integrate.md first line"
159
+ ]
160
+ },
161
+ {
162
+ id: "schema_service_type",
163
+ source: "derived",
164
+ text: "Production monitoring and evals for AI agents",
165
+ match: "exact",
166
+ surfaces: [
167
+ "apps/moda-frontend/components/seo/json-ld.tsx Service serviceType"
168
+ ]
169
+ },
170
+ {
171
+ id: "directory_categories",
172
+ source: "derived",
173
+ text: "AI agent monitoring; LLM evaluation",
174
+ match: "exact",
175
+ surfaces: [
176
+ "G2, Product Hunt, There's An AI For That and other directory category fields: primary first, secondary second"
177
+ ]
178
+ },
179
+ {
180
+ id: "pillar_1",
181
+ source: "derived",
182
+ title: "Monitor every trace",
183
+ text: "Moda reads every production trace end to end and detects the failures that never throw an error: frustrated users, failing tools, lazy answers, and hallucinations. Add your own checks in plain language and get alerts in Slack, Linear, or email.",
184
+ match: "exact",
185
+ surfaces: [
186
+ "apps/moda-frontend/components/landing/features.tsx pillar 1",
187
+ "sales one-pager"
188
+ ]
189
+ },
190
+ {
191
+ id: "pillar_2",
192
+ source: "derived",
193
+ title: "Find the mistakes they keep repeating",
194
+ text: "Failures are grouped into problems ranked by how many users they affect. Each problem shows the traces behind it, the turn where it went wrong, and related problems that share the cause.",
195
+ match: "exact",
196
+ surfaces: [
197
+ "apps/moda-frontend/components/landing/features.tsx pillar 2",
198
+ "sales one-pager"
199
+ ]
200
+ },
201
+ {
202
+ id: "pillar_3",
203
+ source: "derived",
204
+ title: "Test every change before it ships",
205
+ text: "Replay a prompt, model, or tool change against the exact traces that failed and compare pass rate, latency, and tokens. Your team reviews the result and decides what ships.",
206
+ match: "exact",
207
+ surfaces: [
208
+ "apps/moda-frontend/components/landing/features.tsx pillar 3",
209
+ "sales one-pager"
210
+ ]
211
+ }
212
+ ]
213
+ },
214
+ banned_phrases: {
215
+ version: "1.0.4",
216
+ phrases: [
217
+ {
218
+ id: "fixes_your_agent",
219
+ pattern: "\\b(fix(es|ing)?|repair(s|ing)?) your (ai )?agents?\\b|\\b(moda|we) (fix|fixes|repair|repairs)\\b",
220
+ reason: "Moda never claims to fix agents itself; engineers fix their agents",
221
+ replacement: "test your fix against real traces",
222
+ allowed_contexts: [
223
+ "competitor_comparison"
224
+ ]
225
+ },
226
+ {
227
+ id: "auto_fix",
228
+ pattern: "\\bauto[- ]?fix(es|ing|ed)?\\b|\\bautonomous(ly)? fix|\\bself[- ]heal(ing|s)?\\b|\\bfixes itself\\b",
229
+ reason: "Implies Moda changes agents without engineers",
230
+ replacement: "test your change against real traces",
231
+ allowed_contexts: [
232
+ "competitor_comparison"
233
+ ]
234
+ },
235
+ {
236
+ id: "moda_opens_prs",
237
+ pattern: "\\b(moda|we) (can |will |then |also )?(opens?|creates?|files?|raises?|submits?|sends?|delivers?|drafts?|proposes?|suggests?|writes?) (a |an |the )?(draft |candidate |proposed |verified )?(fix(es)?|pull requests?|PRs?|code changes?|code fix(es)?|patch(es)?)\\b|\\b(moda|we) (can |will |then |also )?(drafts?|proposes?|suggests?) (a |an |the )?(candidate |proposed )?changes?\\b|\\bfix PRs?\\b|\\bdraft (pull requests?|PRs?)\\b",
238
+ reason: "Moda does not open pull requests, write fix PRs or propose code fixes; engineers make the change",
239
+ replacement: "engineers make the change; Moda tests it against real traces",
240
+ allowed_contexts: [
241
+ "competitor_comparison",
242
+ "negation"
243
+ ]
244
+ },
245
+ {
246
+ id: "learns",
247
+ pattern: "\\blearns\\b|\\blearn(s|ing)? from (production|every|each|your)\\b|\\bagents? that learn\\b|\\bself[- ]learning\\b",
248
+ reason: "Learning claims are visionary framing; Moda monitors, finds and tests. Navigation text such as 'Learn more' is allowed because it does not match",
249
+ replacement: "find the mistakes your agents keep repeating",
250
+ allowed_contexts: [
251
+ "competitor_comparison"
252
+ ]
253
+ },
254
+ {
255
+ id: "continual_learning",
256
+ pattern: "\\bcontinual[- ]learning\\b|\\blearning (layer|loop)\\b",
257
+ reason: "Undefined buzzword to buyers; rejected Oct 14",
258
+ replacement: "production monitoring and evals for AI agents",
259
+ allowed_contexts: [
260
+ "competitor_comparison",
261
+ "keyword_list"
262
+ ]
263
+ },
264
+ {
265
+ id: "self_improving",
266
+ pattern: "\\bself[- ]improv(e|es|ing|ement)\\b|\\bimprov(e|es) (itself|themselves)\\b|\\bget(s)? better (on (its|their) own|automatically|every week)\\b",
267
+ reason: "Implies autonomy and roadmap capability",
268
+ replacement: "test every change against real traces",
269
+ allowed_contexts: [
270
+ "competitor_comparison"
271
+ ]
272
+ },
273
+ {
274
+ id: "roadmap_training",
275
+ pattern: "\\bfine[- ]?tun(e|es|ed|ing)\\b|\\btrain(s|ing|ed)? (your|the|their) (models?|agents?)\\b|\\bmodel training\\b|\\bweight updates?\\b",
276
+ reason: "Not shipped",
277
+ replacement: "(none)",
278
+ allowed_contexts: [
279
+ "negation",
280
+ "internal",
281
+ "keyword_list"
282
+ ]
283
+ },
284
+ {
285
+ id: "roadmap_rl",
286
+ pattern: "\\breinforcement learning\\b|\\bRLHF\\b|\\bRLAIF\\b|\\bRL\\b|\\breward models?\\b|\\bprocess reward\\b|\\bPRM\\b",
287
+ case_sensitive: true,
288
+ reason: "Not shipped. Case-sensitive so the word 'url' does not match",
289
+ replacement: "(none)",
290
+ allowed_contexts: [
291
+ "negation",
292
+ "internal"
293
+ ]
294
+ },
295
+ {
296
+ id: "roadmap_inference",
297
+ pattern: "\\bhosted inference\\b|\\binference (platform|hosting|fleet)\\b|\\b(we|moda) (serve|host)s? (your )?models?\\b",
298
+ reason: "Not shipped",
299
+ replacement: "(none)",
300
+ allowed_contexts: [
301
+ "negation",
302
+ "internal"
303
+ ]
304
+ },
305
+ {
306
+ id: "visionary_framing",
307
+ pattern: "\\blatent space\\b|\\boutside the (model )?weights\\b|\\bthe future of\\b|\\bnext[- ]gen(eration)?\\b|\\brevolutioni[sz]",
308
+ reason: "Describe only what ships today",
309
+ replacement: "(describe the shipped feature)",
310
+ allowed_contexts: [
311
+ "competitor_comparison"
312
+ ]
313
+ },
314
+ {
315
+ id: "layer_category",
316
+ pattern: "\\b(learning|intelligence|reliability|data) layer\\b|\\blayer for ai agents\\b",
317
+ reason: "Vague infrastructure noun from retired positioning",
318
+ replacement: "production monitoring and evals for AI agents",
319
+ allowed_contexts: []
320
+ },
321
+ {
322
+ id: "observability_category",
323
+ pattern: "\\b(llm|ai|agent|ai agent) observability\\b|\\bobservability (platform|for ai|for agents|for llm)|\\bobservability-grade\\b",
324
+ reason: "mo rejected observability as the pitch; names a data layer, not the job",
325
+ replacement: "production monitoring and evals for AI agents",
326
+ allowed_contexts: [
327
+ "competitor_comparison",
328
+ "keyword_list",
329
+ "skill_trigger"
330
+ ]
331
+ },
332
+ {
333
+ id: "product_analytics",
334
+ pattern: "\\b(ai |llm )?product analytics\\b|\\bconversation analytics\\b|\\banalytics platform\\b|\\banalytics and observability\\b",
335
+ reason: "mo rejected product analytics; signals a PM buyer",
336
+ replacement: "production monitoring and evals for AI agents",
337
+ allowed_contexts: [
338
+ "competitor_comparison",
339
+ "keyword_list",
340
+ "skill_trigger"
341
+ ]
342
+ },
343
+ {
344
+ id: "llm_native_software",
345
+ pattern: "\\bllm-native software\\b|\\bchatbots, copilots, agents\\b",
346
+ reason: "Dated audience framing",
347
+ replacement: "AI agents in production",
348
+ allowed_contexts: []
349
+ },
350
+ {
351
+ id: "harness_category",
352
+ pattern: "\\bharness engineering\\b|\\bautonomous harness\\b|\\bobservability for agent harnesses\\b",
353
+ reason: "Retired by PR #5696. 'Agent harness' remains allowed as a technical noun in docs and product UI",
354
+ replacement: "production monitoring and evals for AI agents",
355
+ allowed_contexts: [
356
+ "internal"
357
+ ]
358
+ },
359
+ {
360
+ id: "reliability_category",
361
+ pattern: "\\breliability platform\\b|\\bmonitoring (&|and) reliability\\b",
362
+ reason: "Rejected category label; the approved label is fixed",
363
+ replacement: "production monitoring and evals for AI agents",
364
+ allowed_contexts: []
365
+ },
366
+ {
367
+ id: "agent_improvement_category",
368
+ pattern: "\\bagent improvement (platform|layer)\\b|\\bdata engine for\\b",
369
+ reason: "Rejected category labels",
370
+ replacement: "production monitoring and evals for AI agents",
371
+ allowed_contexts: []
372
+ },
373
+ {
374
+ id: "sentry_for_ai",
375
+ pattern: "\\bsentry for ai\\b",
376
+ reason: "Raindrop's tagline",
377
+ replacement: "(none)",
378
+ allowed_contexts: [
379
+ "competitor_comparison"
380
+ ]
381
+ },
382
+ {
383
+ id: "gateway",
384
+ pattern: "\\bllm api gateway\\b|\\bgateway and pub-sub\\b",
385
+ reason: "Product from an earlier era",
386
+ replacement: "(none)",
387
+ allowed_contexts: []
388
+ }
389
+ ]
390
+ },
391
+ terminology: {
392
+ version: "1.0.4",
393
+ terms: [
394
+ {
395
+ use: "trace",
396
+ avoid: [
397
+ "conversation (as the product noun)",
398
+ "session"
399
+ ],
400
+ note: "Renamed in MODA-1793. API and tool parameter names may keep conversation_id"
401
+ },
402
+ {
403
+ use: "AI agents",
404
+ avoid: [
405
+ "LLM apps",
406
+ "chatbots",
407
+ "copilots"
408
+ ],
409
+ note: "Audience noun in all marketing copy"
410
+ },
411
+ {
412
+ use: "problem",
413
+ avoid: [
414
+ "issue",
415
+ "incident"
416
+ ],
417
+ note: "A group of recurring failures ranked by users affected"
418
+ },
419
+ {
420
+ use: "signal",
421
+ avoid: [
422
+ "metric",
423
+ "classifier"
424
+ ],
425
+ note: "A detection that runs on every trace"
426
+ },
427
+ {
428
+ use: "custom signal",
429
+ avoid: [
430
+ "custom metric",
431
+ "judge"
432
+ ],
433
+ note: "A plain-language check the team writes"
434
+ },
435
+ {
436
+ use: "use case",
437
+ avoid: [
438
+ "cluster (in marketing copy)"
439
+ ],
440
+ note: "Cluster stays fine in docs and the product UI"
441
+ },
442
+ {
443
+ use: "replay",
444
+ avoid: [
445
+ "simulation"
446
+ ],
447
+ note: "Running a change against real traces"
448
+ },
449
+ {
450
+ use: "monitor",
451
+ avoid: [],
452
+ note: "Product feature name and the category verb"
453
+ },
454
+ {
455
+ use: "evals",
456
+ avoid: [
457
+ "evaluations (in headlines)"
458
+ ],
459
+ note: "Category word; 'evaluation' is fine in body text"
460
+ },
461
+ {
462
+ use: "fix (engineer as the actor)",
463
+ avoid: [
464
+ "Moda fixes",
465
+ "auto-fix",
466
+ "fix PRs",
467
+ "Moda opens a PR",
468
+ "Moda drafts a fix"
469
+ ],
470
+ note: "A change engineers make. Moda tests it against real traces; it does not draft fixes or open pull requests"
471
+ }
472
+ ]
473
+ }
474
+ };
475
+
476
+ // ../../packages/messaging/src/matchers.ts
477
+ var normalizeCopy = (text) => text.replace(/\s+/g, " ").trim();
478
+ function matchCanonical(text, entry) {
479
+ const copy = normalizeCopy(text);
480
+ const canonical = normalizeCopy(entry.text);
481
+ if (entry.match === "exact" && copy === canonical)
482
+ return "exact";
483
+ return copy.toLowerCase().includes(canonical.toLowerCase()) ? "contains" : null;
484
+ }
485
+ var ALLOW_MARKER = /messaging-allow:\s*([a-z_]+)/g;
486
+ var escapeRegExp = (text) => text.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
487
+ var terminologyPattern = (avoid) => {
488
+ const words = normalizeCopy(avoid.replace(/\([^)]*\)/g, " "));
489
+ return words ? new RegExp(`(?<![\\w-])${escapeRegExp(words)}(?![\\w-])`, "gi") : null;
490
+ };
491
+ var distinct = (values) => [...new Set(values)];
492
+ function createMatchers(messaging) {
493
+ const banned = messaging.banned_phrases.phrases.map((phrase) => ({
494
+ phrase,
495
+ regex: new RegExp(phrase.pattern, phrase.case_sensitive ? "g" : "gi")
496
+ }));
497
+ const terms = messaging.terminology.terms.flatMap((term) => term.avoid.flatMap((avoid) => {
498
+ const regex = terminologyPattern(avoid);
499
+ return regex ? [{ term, avoid, regex }] : [];
500
+ }));
501
+ return {
502
+ findBannedPhrases(text) {
503
+ const markers = new Set([...text.matchAll(ALLOW_MARKER)].map((match) => match[1]));
504
+ return banned.flatMap(({ phrase, regex }) => {
505
+ const matches = distinct([...text.matchAll(regex)].map((match) => match[0]));
506
+ if (matches.length === 0)
507
+ return [];
508
+ const allowedBy = phrase.allowed_contexts.find((context) => markers.has(context)) ?? null;
509
+ return [{ phrase, matches, allowedBy }];
510
+ });
511
+ },
512
+ findTerminology(text) {
513
+ return terms.flatMap(({ term, avoid, regex }) => {
514
+ const matches = distinct([...text.matchAll(regex)].map((match) => match[0]));
515
+ return matches.length ? [{ term, avoid, matches }] : [];
516
+ });
517
+ },
518
+ findCanonical(text) {
519
+ return messaging.canonical_strings.strings.flatMap((entry) => {
520
+ const relation = matchCanonical(text, entry);
521
+ return relation ? [{ entry, relation }] : [];
522
+ });
523
+ }
524
+ };
525
+ }
526
+ // ../../packages/messaging/messaging.schema.json
527
+ var messaging_schema_default = {
528
+ $schema: "https://json-schema.org/draft/2020-12/schema",
529
+ $id: "https://moda.dev/schemas/messaging.schema.json",
530
+ title: "Moda locked positioning",
531
+ description: "packages/messaging/messaging.json: the canonical-strings, banned-phrases and terminology blocks of the locked positioning doc. Regenerate with `bun run --cwd packages/messaging sync -- <positioning.md>`.",
532
+ type: "object",
533
+ required: ["version", "locked_on", "approved_by", "source", "canonical_strings", "banned_phrases", "terminology"],
534
+ additionalProperties: false,
535
+ properties: {
536
+ $schema: { type: "string" },
537
+ version: { $ref: "#/$defs/version" },
538
+ locked_on: { $ref: "#/$defs/date" },
539
+ approved_by: { type: "string", minLength: 1 },
540
+ source: { type: "string", minLength: 1 },
541
+ canonical_strings: {
542
+ type: "object",
543
+ required: ["version", "locked_on", "approved_by", "strings"],
544
+ additionalProperties: false,
545
+ properties: {
546
+ version: { $ref: "#/$defs/version" },
547
+ locked_on: { $ref: "#/$defs/date" },
548
+ approved_by: { type: "string", minLength: 1 },
549
+ strings: { type: "array", items: { $ref: "#/$defs/canonicalString" } }
550
+ }
551
+ },
552
+ banned_phrases: {
553
+ type: "object",
554
+ required: ["version", "phrases"],
555
+ additionalProperties: false,
556
+ properties: {
557
+ version: { $ref: "#/$defs/version" },
558
+ phrases: { type: "array", items: { $ref: "#/$defs/bannedPhrase" } }
559
+ }
560
+ },
561
+ terminology: {
562
+ type: "object",
563
+ required: ["version", "terms"],
564
+ additionalProperties: false,
565
+ properties: {
566
+ version: { $ref: "#/$defs/version" },
567
+ terms: { type: "array", items: { $ref: "#/$defs/term" } }
568
+ }
569
+ }
570
+ },
571
+ $defs: {
572
+ version: { type: "string", pattern: "^\\d+\\.\\d+\\.\\d+$" },
573
+ date: { type: "string", pattern: "^\\d{4}-\\d{2}-\\d{2}$" },
574
+ canonicalString: {
575
+ type: "object",
576
+ required: ["id", "source", "text", "match", "surfaces"],
577
+ additionalProperties: false,
578
+ properties: {
579
+ id: { type: "string", pattern: "^[a-z0-9_]+$" },
580
+ source: { enum: ["approved", "derived"] },
581
+ title: { type: "string", minLength: 1 },
582
+ text: { type: "string", minLength: 1 },
583
+ match: {
584
+ enum: ["exact", "contains_ci"],
585
+ description: "exact: the live text equals text after trimming whitespace. contains_ci: the live text contains text, case-insensitively."
586
+ },
587
+ surfaces: { type: "array", items: { type: "string", minLength: 1 } },
588
+ max_chars: { type: "integer", minimum: 1 }
589
+ }
590
+ },
591
+ bannedPhrase: {
592
+ type: "object",
593
+ required: ["id", "pattern", "reason", "replacement", "allowed_contexts"],
594
+ additionalProperties: false,
595
+ properties: {
596
+ id: { type: "string", pattern: "^[a-z0-9_]+$" },
597
+ pattern: { type: "string", minLength: 1, description: "A JavaScript regular expression, case-insensitive unless case_sensitive is true." },
598
+ case_sensitive: { type: "boolean" },
599
+ reason: { type: "string", minLength: 1 },
600
+ replacement: { type: "string", minLength: 1 },
601
+ allowed_contexts: {
602
+ type: "array",
603
+ items: { enum: ["competitor_comparison", "keyword_list", "skill_trigger", "negation", "internal"] }
604
+ }
605
+ }
606
+ },
607
+ term: {
608
+ type: "object",
609
+ required: ["use", "avoid", "note"],
610
+ additionalProperties: false,
611
+ properties: {
612
+ use: { type: "string", minLength: 1 },
613
+ avoid: { type: "array", items: { type: "string", minLength: 1 } },
614
+ note: { type: "string" }
615
+ }
616
+ }
617
+ }
618
+ };
619
+
620
+ // ../../packages/messaging/src/schema.ts
621
+ var KNOWN_KEYWORDS = new Set([
622
+ "$schema",
623
+ "$id",
624
+ "$defs",
625
+ "$ref",
626
+ "title",
627
+ "description",
628
+ "type",
629
+ "required",
630
+ "additionalProperties",
631
+ "properties",
632
+ "items",
633
+ "enum",
634
+ "pattern",
635
+ "minLength",
636
+ "minimum"
637
+ ]);
638
+ var root = messaging_schema_default;
639
+ var withArticle = (word) => `${/^[aeiou]/.test(word) ? "an" : "a"} ${word}`;
640
+ var typeOf = (value) => value === null ? "null" : Array.isArray(value) ? "array" : Number.isInteger(value) ? "integer" : typeof value;
641
+ function check(value, node, path, problems) {
642
+ const unknown = Object.keys(node).filter((keyword) => !KNOWN_KEYWORDS.has(keyword));
643
+ if (unknown.length) {
644
+ problems.push(`messaging.schema.json uses unsupported keywords at ${path}: ${unknown.join(", ")}`);
645
+ return;
646
+ }
647
+ if (node.$ref) {
648
+ const name = node.$ref.replace(/^#\/\$defs\//, "");
649
+ const target = root.$defs[name];
650
+ if (!target)
651
+ problems.push(`messaging.schema.json has no definition ${node.$ref}`);
652
+ else
653
+ check(value, target, path, problems);
654
+ return;
655
+ }
656
+ if (node.enum && !node.enum.includes(value)) {
657
+ problems.push(`${path} is ${JSON.stringify(value)}, expected one of ${node.enum.map((entry) => JSON.stringify(entry)).join(", ")}`);
658
+ return;
659
+ }
660
+ if (node.type) {
661
+ const actual = typeOf(value);
662
+ if (actual !== node.type) {
663
+ problems.push(`${path} is ${actual === "undefined" ? "missing" : withArticle(actual)}, expected ${withArticle(node.type)}`);
664
+ return;
665
+ }
666
+ }
667
+ if (typeof value === "string") {
668
+ if (node.minLength !== undefined && value.length < node.minLength)
669
+ problems.push(`${path} must not be empty`);
670
+ if (node.pattern && !new RegExp(node.pattern, "u").test(value))
671
+ problems.push(`${path} "${value}" does not match ${node.pattern}`);
672
+ }
673
+ if (typeof value === "number" && node.minimum !== undefined && value < node.minimum)
674
+ problems.push(`${path} is below ${node.minimum}`);
675
+ if (Array.isArray(value) && node.items) {
676
+ value.forEach((entry, index) => check(entry, node.items, `${path}[${index}]`, problems));
677
+ }
678
+ if (value && typeof value === "object" && !Array.isArray(value)) {
679
+ const record = value;
680
+ for (const key of node.required ?? []) {
681
+ if (!(key in record))
682
+ problems.push(`${path}.${key} is missing`);
683
+ }
684
+ for (const [key, entry] of Object.entries(record)) {
685
+ const child = node.properties?.[key];
686
+ if (child)
687
+ check(entry, child, `${path}.${key}`, problems);
688
+ else if (node.additionalProperties === false)
689
+ problems.push(`${path}.${key} is not allowed`);
690
+ }
691
+ }
692
+ }
693
+ function schemaProblems(value) {
694
+ const problems = [];
695
+ check(value, root, "messaging", problems);
696
+ return problems;
697
+ }
698
+
699
+ // ../../packages/messaging/src/positioning-doc.ts
700
+ function validateMessaging(messaging) {
701
+ const shape = schemaProblems(messaging);
702
+ if (shape.length > 0)
703
+ return shape;
704
+ const problems = [];
705
+ const { canonical_strings: canonical, banned_phrases: banned, terminology } = messaging;
706
+ for (const [name, version] of [
707
+ ["canonical_strings", canonical.version],
708
+ ["banned_phrases", banned.version],
709
+ ["terminology", terminology.version]
710
+ ]) {
711
+ if (version !== messaging.version)
712
+ problems.push(`${name}.version is ${version}, expected ${messaging.version}`);
713
+ }
714
+ const ids = new Set;
715
+ for (const entry of canonical.strings) {
716
+ const label = `canonical string ${entry.id}`;
717
+ if (ids.has(entry.id))
718
+ problems.push(`${label} is listed twice`);
719
+ ids.add(entry.id);
720
+ if (entry.max_chars !== undefined && entry.text.length > entry.max_chars) {
721
+ problems.push(`${label} is ${entry.text.length} characters, over its max_chars of ${entry.max_chars}`);
722
+ }
723
+ }
724
+ const phraseIds = new Set;
725
+ for (const phrase of banned.phrases) {
726
+ const label = `banned phrase ${phrase.id}`;
727
+ if (phraseIds.has(phrase.id))
728
+ problems.push(`${label} is listed twice`);
729
+ phraseIds.add(phrase.id);
730
+ try {
731
+ if (new RegExp(phrase.pattern, phrase.case_sensitive ? "" : "i").test(""))
732
+ problems.push(`${label} matches empty text`);
733
+ } catch (error) {
734
+ problems.push(`${label} has an invalid pattern: ${error instanceof Error ? error.message : String(error)}`);
735
+ }
736
+ }
737
+ return problems;
738
+ }
739
+
740
+ // ../../packages/messaging/src/index.ts
741
+ var problems = validateMessaging(messaging_default);
742
+ if (problems.length > 0)
743
+ throw new Error(`packages/messaging/messaging.json is invalid:
744
+ - ${problems.join(`
745
+ - `)}`);
746
+ var messaging = messaging_default;
747
+ var CANONICAL_STRINGS = messaging.canonical_strings.strings;
748
+ var BANNED_PHRASES = messaging.banned_phrases.phrases;
749
+ var TERMINOLOGY = messaging.terminology.terms;
750
+ var matchers2 = createMatchers(messaging);
751
+ function canonicalString(id) {
752
+ const entry = CANONICAL_STRINGS.find((candidate) => candidate.id === id);
753
+ if (!entry)
754
+ throw new Error(`Unknown canonical string "${id}"`);
755
+ return entry;
756
+ }
757
+ function canonicalText(id) {
758
+ return canonicalString(id).text;
759
+ }
760
+
761
+ // src/tui.ts
762
+ var STATUS_LABELS = {
763
+ pass: "ok",
764
+ warning: "warn",
765
+ fail: "fail",
766
+ skipped: "skip",
767
+ info: "info",
768
+ run: "run"
769
+ };
770
+ var STATUS_COLORS = {
771
+ pass: 32,
772
+ warning: 33,
773
+ fail: 31,
774
+ skipped: 90,
775
+ info: 36,
776
+ run: 35
777
+ };
778
+ function renderDoctorHuman(report, options = {}) {
779
+ const ui = createTerminalUi(options);
780
+ const lines = [];
781
+ lines.push(ui.title("Moda doctor"));
782
+ lines.push(ui.dim(`Profile ${report.profile.active}${report.profile.requested ? ` (requested ${report.profile.requested})` : ""}`));
783
+ lines.push("");
784
+ lines.push(renderSummary(ui, report));
785
+ for (const [group, checks] of groupChecks(report.checks)) {
786
+ lines.push("");
787
+ lines.push(ui.section(titleCase(group)));
788
+ for (const check2 of checks) {
789
+ lines.push(renderCheck(ui, check2));
790
+ }
791
+ }
792
+ if (report.nextCommands.length > 0) {
793
+ lines.push("");
794
+ lines.push(ui.section("Next commands"));
795
+ for (const command of report.nextCommands.slice(0, 8)) {
796
+ lines.push(` ${ui.command(command)}`);
797
+ }
798
+ }
799
+ if (report.fixPlan.length > 0) {
800
+ lines.push("");
801
+ lines.push(ui.section("Repair plan"));
802
+ for (const item of report.fixPlan.slice(0, 8)) {
803
+ const command = item.command ? ` ${ui.command(item.command)}` : "";
804
+ lines.push(ui.wrap(` - ${item.action}${command}`, 4));
805
+ }
806
+ }
807
+ return lines.join(`
808
+ `);
809
+ }
810
+ function renderStatusHuman(status, options = {}) {
811
+ const ui = createTerminalUi(options);
812
+ const lines = [];
813
+ lines.push(ui.title("Moda status"));
814
+ lines.push(`${ui.status(status.status)} ${ui.dim(`profile ${status.activeProfile}`)}`);
815
+ lines.push("");
816
+ lines.push(`Auth ${status.auth.apiKey}`);
817
+ lines.push(`Tenant ${status.workspace.tenantId ?? "missing"}`);
818
+ lines.push(`Base URL ${status.workspace.baseUrl}`);
819
+ lines.push(`Ingest URL ${status.workspace.ingestUrl}`);
820
+ lines.push("");
821
+ lines.push(ui.section("Readiness"));
822
+ lines.push(` Data flow ${ui.status(status.dataFlow)}`);
823
+ lines.push(` Harness ${ui.status(status.harness)}`);
824
+ lines.push(` Harness sync ${ui.status(status.harnessSync)}`);
825
+ lines.push(` Shadow Spark ${ui.status(status.shadowSpark)}`);
826
+ lines.push(` Prompts ${ui.status(status.prompts)}`);
827
+ lines.push(` Skills ${ui.status(status.skills)}`);
828
+ if (status.topBrokenCheck) {
829
+ lines.push("");
830
+ lines.push(ui.section("Attention"));
831
+ lines.push(` ${ui.status(status.topBrokenCheck.status)} ${status.topBrokenCheck.id}`);
832
+ if (status.topBrokenCheck.nextCommand) {
833
+ lines.push(` Next: ${ui.command(status.topBrokenCheck.nextCommand)}`);
834
+ }
835
+ }
836
+ if (status.nextCommands.length > 0) {
837
+ lines.push("");
838
+ lines.push(ui.section("Next commands"));
839
+ for (const command of status.nextCommands) {
840
+ lines.push(` ${ui.command(command)}`);
841
+ }
842
+ }
843
+ return lines.join(`
844
+ `);
845
+ }
846
+ function renderInitBanner() {
847
+ const ui = createTerminalUi();
848
+ return [
849
+ ui.title("Moda init"),
850
+ ui.dim(canonicalText("cli_description")),
851
+ "",
852
+ ` ${ui.status("info")} Human-in-the-loop setup. Secrets stay untouched and file changes are shown before completion.`
853
+ ].join(`
854
+ `);
855
+ }
856
+ function renderInitStep(index, total, title, detail) {
857
+ const ui = createTerminalUi();
858
+ return [
859
+ "",
860
+ ui.section(`Step ${index}/${total}: ${title}`),
861
+ ...detail ? [ui.wrap(` ${detail}`, 2)] : []
862
+ ].join(`
863
+ `);
864
+ }
865
+ function renderInitNote(message, status = "pass") {
866
+ const ui = createTerminalUi();
867
+ return ` ${ui.status(status)} ${message}`;
868
+ }
869
+ function renderInitSetupPlan(actions, options = {}) {
870
+ const ui = createTerminalUi();
871
+ const lines = actions.map((action) => {
872
+ const status = action.approved ? "pass" : action.status === "manual" ? "skipped" : "warning";
873
+ const suffix = action.reason ? ` - ${action.reason}` : "";
874
+ return ui.wrap(` ${ui.status(status)} ${action.title}${suffix}`, 4);
875
+ });
876
+ if (options.snapshotConsent) {
877
+ lines.push("");
878
+ lines.push(ui.wrap(` ${ui.status("info")} Moda will analyze a snapshot of this repo on Moda's servers to map your agent harness. Secrets and .env files are excluded and the snapshot is deleted after the run.`, 4));
879
+ }
880
+ return lines.join(`
881
+ `);
882
+ }
883
+ function renderInitComplete(options) {
884
+ const ui = createTerminalUi();
885
+ const dashboardUrl = options.dashboardUrl ?? "https://moda.dev";
886
+ const lines = [];
887
+ lines.push("");
888
+ lines.push(ui.title(`Workspace ready: ${options.tenantName}`));
889
+ lines.push(`Dashboard: ${ui.command(dashboardUrl)}`);
890
+ lines.push("");
891
+ if (options.harness?.state === "pending") {
892
+ lines.push(ui.wrap(` ${ui.status("run")} Harness analysis is still running on Moda. Run ${ui.command("moda harness pull --sync")} in a few minutes.`, 4));
893
+ } else if (options.harness?.state === "skipped") {
894
+ const what = {
895
+ unavailable: "Harness analysis did not start and was skipped.",
896
+ lost: "Lost track of the harness analysis; it may still be running on Moda.",
897
+ unfetched: "Harness analysis finished on Moda but could not be fetched.",
898
+ failed: "Harness analysis failed on Moda and was skipped."
899
+ }[options.harness.reason ?? "unavailable"];
900
+ lines.push(ui.wrap(` ${ui.status("warning")} ${what} Run ${ui.command(options.harness.next ?? "moda harness pull --sync")} later.`, 4));
901
+ } else if (options.harness) {
902
+ lines.push(ui.wrap(` ${ui.status("pass")} Harness mapped: ${options.harness.agents ?? 0} agent(s), ${options.harness.artifacts ?? 0} artifact(s). View: ${ui.command(dashboardUrl)}`, 4));
903
+ }
904
+ lines.push("");
905
+ lines.push(ui.section("Files changed"));
906
+ if (options.filesChanged.length === 0) {
907
+ lines.push(` ${ui.dim("none")}`);
908
+ } else {
909
+ for (const file of options.filesChanged)
910
+ lines.push(` ${file}`);
911
+ }
912
+ if (options.skipped.length > 0) {
913
+ lines.push("");
914
+ lines.push(ui.section("Skipped or manual"));
915
+ for (const action of options.skipped) {
916
+ lines.push(ui.wrap(` - ${action.title}: ${action.reason ?? action.status}`, 4));
917
+ }
918
+ }
919
+ lines.push("");
920
+ lines.push(ui.section("Next steps"));
921
+ if (options.telemetrySeen === true) {
922
+ lines.push(` Telemetry is flowing. Explore it: ${ui.command("moda overview")}`);
923
+ } else {
924
+ lines.push(` Run your app to send telemetry, then: ${ui.command("moda doctor --online")}`);
925
+ }
926
+ lines.push(` Ask about your agents: ${ui.command('moda ask "what did my agents do today?"')}`);
927
+ return lines.join(`
928
+ `);
929
+ }
930
+ function renderHarnessProgress(message, status = "run") {
931
+ const ui = createTerminalUi();
932
+ return `${ui.status(status)} ${message}`;
933
+ }
934
+ function renderHarnessAnalystPreflight(options) {
935
+ const ui = createTerminalUi();
936
+ const lines = [];
937
+ lines.push(ui.section("Analyst read plan"));
938
+ lines.push(` ${ui.status("info")} Moda Analyst will inspect this project before any sync.`);
939
+ lines.push(` Root ${options.root}`);
940
+ lines.push(ui.wrap(` Reads ${options.willRead.join(", ")}`, 12));
941
+ lines.push(ui.wrap(` Skips ${options.willSkip.join(", ")}`, 12));
942
+ lines.push(` Writes ${ui.dim("none during investigation; Moda writes .moda artifacts after validation")}`);
943
+ return lines.join(`
944
+ `);
945
+ }
946
+ function renderHarnessAnalystLaunch(options) {
947
+ const ui = createTerminalUi();
948
+ const lines = [];
949
+ lines.push(ui.section("Moda Analyst"));
950
+ lines.push(` ${ui.status("run")} Launching ${options.adapterName}`);
951
+ lines.push(` Command ${options.command}`);
952
+ lines.push(` Mode read-only repository access`);
953
+ lines.push(` Root ${options.root}`);
954
+ lines.push(` Output ${options.reportPath}`);
955
+ lines.push(` Boundary Agent can inspect files and print a cited report. Moda validates it, then writes .moda artifacts.`);
956
+ return lines.join(`
957
+ `);
958
+ }
959
+ function renderHarnessAnalystChoice(options) {
960
+ const ui = createTerminalUi();
961
+ return [
962
+ ui.section("Detected coding agents"),
963
+ ` ${ui.status("info")} ${options.detected.join(", ")}`,
964
+ " Pick the agent that should investigate this repo in read-only mode."
965
+ ].join(`
966
+ `);
967
+ }
968
+ function renderHarnessAnalyzeHumanResult(data) {
969
+ const ui = createTerminalUi();
970
+ const value = asRecord(data);
971
+ const summary = asRecord(value?.summary);
972
+ const validation = asRecord(value?.validation);
973
+ const written = Array.isArray(value?.written) ? value.written.map(String) : [];
974
+ const lines = [];
975
+ lines.push(ui.title("Moda Analyst report"));
976
+ lines.push(`${ui.status(validation?.status === "fail" ? "fail" : "pass")} validation ${String(validation?.status ?? "unknown")}`);
977
+ if (summary) {
978
+ lines.push("");
979
+ lines.push(ui.section("Harness summary"));
980
+ lines.push(` Runtime agents ${numberOrZero(summary.runtimeAgentCount)}`);
981
+ lines.push(` Agent families ${numberOrZero(summary.agentFamilyCount)}`);
982
+ lines.push(` Artifacts ${numberOrZero(summary.artifactCount ?? summary.sharedArtifactCount)}`);
983
+ lines.push(` Providers ${numberOrZero(summary.modelProviderCount)}`);
984
+ lines.push(` Unknowns ${numberOrZero(summary.unknownCount)}`);
985
+ }
986
+ if (written.length > 0) {
987
+ lines.push("");
988
+ lines.push(ui.section("Artifacts"));
989
+ for (const file of written.slice(0, 12))
990
+ lines.push(` ${file}`);
991
+ }
992
+ lines.push("");
993
+ lines.push(ui.section("Next commands"));
994
+ lines.push(` ${ui.command("moda harness approve --yes")}`);
995
+ lines.push(` ${ui.command("moda harness sync --from-report .moda/harness-report.json --dry-run")}`);
996
+ return lines.join(`
997
+ `);
998
+ }
999
+ function renderSummary(ui, report) {
1000
+ return [
1001
+ ui.status(report.summary.status),
1002
+ `pass ${report.summary.pass}`,
1003
+ `warn ${report.summary.warning}`,
1004
+ `fail ${report.summary.fail}`,
1005
+ `skip ${report.summary.skipped}`
1006
+ ].join(" ");
1007
+ }
1008
+ function renderCheck(ui, check2) {
1009
+ const lines = [];
1010
+ lines.push(` ${ui.status(check2.status)} ${check2.title}`);
1011
+ for (const evidence of check2.evidence.slice(0, 4)) {
1012
+ lines.push(ui.wrap(` ${evidence.label}: ${evidence.value}`, 6));
1013
+ }
1014
+ if (check2.remediation) {
1015
+ lines.push(ui.wrap(` fix: ${check2.remediation}`, 6));
1016
+ }
1017
+ if (check2.nextCommand) {
1018
+ lines.push(` next: ${ui.command(check2.nextCommand)}`);
1019
+ }
1020
+ return lines.join(`
1021
+ `);
1022
+ }
1023
+ function groupChecks(checks) {
1024
+ const groups = new Map;
1025
+ for (const check2 of checks) {
1026
+ const existing = groups.get(check2.group) ?? [];
1027
+ existing.push(check2);
1028
+ groups.set(check2.group, existing);
1029
+ }
1030
+ return [...groups.entries()];
1031
+ }
1032
+ function titleCase(value) {
1033
+ return value.split(/[-_\s]+/).filter(Boolean).map((part) => `${part.slice(0, 1).toUpperCase()}${part.slice(1)}`).join(" ");
1034
+ }
1035
+ function numberOrZero(value) {
1036
+ return typeof value === "number" && Number.isFinite(value) ? String(value) : "0";
1037
+ }
1038
+ function asRecord(value) {
1039
+ return isRecord(value) ? value : undefined;
1040
+ }
1041
+ function isRecord(value) {
1042
+ return typeof value === "object" && value !== null && !Array.isArray(value);
1043
+ }
1044
+ function createTerminalUi(options = {}) {
1045
+ const width = Math.max(48, Math.min(options.width ?? process.stdout.columns ?? 96, 140));
1046
+ const color = options.color ?? Boolean(process.stdout.isTTY && process.env.NO_COLOR !== "1");
1047
+ const paint = (code, text) => color ? `\x1B[${code}m${text}\x1B[0m` : text;
1048
+ return {
1049
+ title: (text) => paint(1, text),
1050
+ section: (text) => paint(36, text),
1051
+ status: (status) => paint(STATUS_COLORS[status], `[${STATUS_LABELS[status]}]`),
1052
+ command: (text) => paint(36, text),
1053
+ dim: (text) => paint(90, text),
1054
+ wrap: (text, indent) => wrapLine(text, width, indent)
1055
+ };
1056
+ }
1057
+ function wrapLine(text, width, indent) {
1058
+ if (text.length <= width)
1059
+ return text;
1060
+ const lead = text.match(/^\s*/)?.[0] ?? "";
1061
+ const words = text.trimStart().split(/\s+/);
1062
+ const lines = [];
1063
+ let current = "";
1064
+ for (const word of words) {
1065
+ const next = current ? `${current} ${word}` : `${lead}${word}`;
1066
+ if (next.length > width && current) {
1067
+ lines.push(current);
1068
+ current = `${" ".repeat(indent)}${word}`;
1069
+ } else {
1070
+ current = next;
1071
+ }
1072
+ }
1073
+ if (current)
1074
+ lines.push(current);
1075
+ return lines.join(`
1076
+ `);
1077
+ }
1078
+
1079
+ // src/harness-sync-state.ts
1080
+ import { existsSync, readFileSync } from "node:fs";
1081
+ import { join } from "node:path";
1082
+ var SYNC_STATE_PATH = join(".moda", "sync-state.json");
1083
+ function readHarnessSyncState(rootDir) {
1084
+ const path = join(rootDir, SYNC_STATE_PATH);
1085
+ if (!existsSync(path))
1086
+ return;
1087
+ try {
1088
+ const parsed = JSON.parse(readFileSync(path, "utf8"));
1089
+ if (!parsed || typeof parsed !== "object" || Array.isArray(parsed) || parsed.schemaVersion !== "sync_state.v0.1") {
1090
+ return;
1091
+ }
1092
+ return parsed;
1093
+ } catch {
1094
+ return;
1095
+ }
1096
+ }
1097
+
1098
+ export { canonicalText, renderDoctorHuman, renderStatusHuman, renderInitBanner, renderInitStep, renderInitNote, renderInitSetupPlan, renderInitComplete, renderHarnessProgress, renderHarnessAnalystPreflight, renderHarnessAnalystLaunch, renderHarnessAnalystChoice, renderHarnessAnalyzeHumanResult, SYNC_STATE_PATH, readHarnessSyncState };