memorix 1.2.0 → 1.2.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (212) hide show
  1. package/CHANGELOG.md +30 -1
  2. package/README.md +18 -4
  3. package/README.zh-CN.md +18 -4
  4. package/TEAM.md +86 -86
  5. package/dist/cli/index.js +15919 -14055
  6. package/dist/cli/index.js.map +1 -1
  7. package/dist/index.js +1997 -1021
  8. package/dist/index.js.map +1 -1
  9. package/dist/maintenance-runner.d.ts +1 -1
  10. package/dist/maintenance-runner.js +8481 -8005
  11. package/dist/maintenance-runner.js.map +1 -1
  12. package/dist/memcode-runtime/CHANGELOG.md +30 -1
  13. package/dist/sdk.d.ts +7 -2
  14. package/dist/sdk.js +2022 -1024
  15. package/dist/sdk.js.map +1 -1
  16. package/dist/types.d.ts +49 -1
  17. package/dist/types.js.map +1 -1
  18. package/docs/1.2.2-MEMORY-CONTROL-PLANE.md +434 -0
  19. package/docs/AGENT_OPERATOR_PLAYBOOK.md +4 -0
  20. package/docs/API_REFERENCE.md +27 -5
  21. package/docs/DESIGN_DECISIONS.md +357 -357
  22. package/docs/DEVELOPMENT.md +4 -0
  23. package/docs/README.md +1 -1
  24. package/docs/SETUP.md +7 -1
  25. package/docs/dev-log/progress.txt +91 -11
  26. package/docs/knowledge/workflows/memorix-release.md +57 -0
  27. package/package.json +1 -1
  28. package/plugins/codex/memorix/.codex-plugin/plugin.json +1 -1
  29. package/src/audit/index.ts +156 -156
  30. package/src/cli/command-guide.ts +192 -0
  31. package/src/cli/commands/audit-list.ts +89 -89
  32. package/src/cli/commands/audit.ts +9 -4
  33. package/src/cli/commands/background.ts +659 -659
  34. package/src/cli/commands/cleanup.ts +5 -1
  35. package/src/cli/commands/codegraph.ts +17 -8
  36. package/src/cli/commands/context.ts +3 -2
  37. package/src/cli/commands/doctor.ts +4 -2
  38. package/src/cli/commands/explain.ts +9 -3
  39. package/src/cli/commands/formation.ts +48 -48
  40. package/src/cli/commands/git-hook-install.ts +111 -111
  41. package/src/cli/commands/handoff.ts +75 -61
  42. package/src/cli/commands/hooks-status.ts +63 -63
  43. package/src/cli/commands/identity.ts +116 -0
  44. package/src/cli/commands/ingest-commit.ts +153 -153
  45. package/src/cli/commands/ingest-image.ts +71 -69
  46. package/src/cli/commands/ingest-log.ts +180 -180
  47. package/src/cli/commands/ingest.ts +44 -44
  48. package/src/cli/commands/integrate-shared.ts +15 -15
  49. package/src/cli/commands/knowledge.ts +40 -0
  50. package/src/cli/commands/lock.ts +93 -92
  51. package/src/cli/commands/memory.ts +58 -21
  52. package/src/cli/commands/message.ts +123 -118
  53. package/src/cli/commands/operator-shared.ts +98 -3
  54. package/src/cli/commands/poll.ts +74 -64
  55. package/src/cli/commands/purge-all-memory.ts +85 -85
  56. package/src/cli/commands/purge-project-memory.ts +83 -83
  57. package/src/cli/commands/reasoning.ts +135 -121
  58. package/src/cli/commands/retention.ts +9 -4
  59. package/src/cli/commands/serve-http.ts +22 -43
  60. package/src/cli/commands/serve-shared.ts +118 -118
  61. package/src/cli/commands/session.ts +29 -3
  62. package/src/cli/commands/setup.ts +9 -3
  63. package/src/cli/commands/skills.ts +124 -119
  64. package/src/cli/commands/status.ts +4 -3
  65. package/src/cli/commands/task.ts +193 -184
  66. package/src/cli/commands/team.ts +14 -10
  67. package/src/cli/commands/transfer.ts +108 -55
  68. package/src/cli/commands/uninstall-project-artifacts.ts +85 -85
  69. package/src/cli/identity.ts +89 -0
  70. package/src/cli/index.ts +96 -19
  71. package/src/cli/invocation.ts +115 -0
  72. package/src/cli/tui/ChatView.tsx +234 -234
  73. package/src/cli/tui/CommandBar.tsx +312 -312
  74. package/src/cli/tui/ContextRail.tsx +118 -118
  75. package/src/cli/tui/HeaderBar.tsx +72 -72
  76. package/src/cli/tui/LogoBanner.tsx +51 -51
  77. package/src/cli/tui/Sidebar.tsx +179 -179
  78. package/src/cli/tui/chat-service.ts +41 -18
  79. package/src/cli/tui/data.ts +23 -44
  80. package/src/cli/tui/index.ts +41 -41
  81. package/src/cli/tui/markdown-render.tsx +371 -371
  82. package/src/cli/tui/operator-context.ts +60 -0
  83. package/src/cli/tui/use-mouse.ts +157 -157
  84. package/src/cli/tui/useNavigation.ts +56 -56
  85. package/src/cli/tui/views/MemoryView.tsx +10 -8
  86. package/src/cli/update-checker.ts +211 -211
  87. package/src/cli/version.ts +7 -7
  88. package/src/cli/workbench.ts +1 -1
  89. package/src/codegraph/auto-context.ts +34 -17
  90. package/src/codegraph/context-pack.ts +1 -0
  91. package/src/codegraph/current-facts.ts +19 -1
  92. package/src/codegraph/project-context.ts +2 -0
  93. package/src/codegraph/task-lens.ts +49 -5
  94. package/src/compact/engine.ts +26 -10
  95. package/src/compact/index-format.ts +25 -2
  96. package/src/compact/token-budget.ts +74 -74
  97. package/src/dashboard/project-classification.ts +64 -64
  98. package/src/dashboard/server.ts +58 -52
  99. package/src/embedding/fastembed-provider.ts +142 -142
  100. package/src/embedding/transformers-provider.ts +111 -111
  101. package/src/git/extractor.ts +209 -209
  102. package/src/git/hooks-path.ts +85 -85
  103. package/src/hooks/admission.ts +117 -0
  104. package/src/hooks/handler.ts +98 -91
  105. package/src/hooks/pattern-detector.ts +173 -173
  106. package/src/hooks/significance-filter.ts +250 -250
  107. package/src/knowledge/claims.ts +51 -1
  108. package/src/knowledge/context-assembly.ts +97 -0
  109. package/src/knowledge/types.ts +1 -0
  110. package/src/knowledge/workflows.ts +34 -3
  111. package/src/knowledge/workset.ts +179 -10
  112. package/src/llm/memory-manager.ts +328 -328
  113. package/src/llm/provider.ts +885 -885
  114. package/src/llm/quality.ts +248 -248
  115. package/src/memory/admission.ts +57 -0
  116. package/src/memory/attribution-guard.ts +249 -249
  117. package/src/memory/auto-relations.ts +21 -0
  118. package/src/memory/consolidation.ts +13 -2
  119. package/src/memory/disclosure-policy.ts +140 -135
  120. package/src/memory/entity-extractor.ts +197 -197
  121. package/src/memory/export-import.ts +11 -3
  122. package/src/memory/formation/evaluate.ts +217 -217
  123. package/src/memory/formation/extract.ts +361 -361
  124. package/src/memory/formation/index.ts +417 -417
  125. package/src/memory/formation/resolve.ts +344 -344
  126. package/src/memory/formation/types.ts +315 -315
  127. package/src/memory/freshness.ts +122 -122
  128. package/src/memory/graph-context.ts +8 -2
  129. package/src/memory/graph-scope.ts +46 -0
  130. package/src/memory/graph.ts +197 -197
  131. package/src/memory/observations.ts +162 -4
  132. package/src/memory/quality-audit.ts +2 -0
  133. package/src/memory/refs.ts +94 -94
  134. package/src/memory/retention.ts +22 -2
  135. package/src/memory/secret-filter.ts +79 -79
  136. package/src/memory/session.ts +5 -2
  137. package/src/memory/visibility.ts +80 -0
  138. package/src/multimodal/image-loader.ts +143 -143
  139. package/src/orchestrate/adapters/claude-stream.ts +192 -192
  140. package/src/orchestrate/adapters/claude.ts +111 -111
  141. package/src/orchestrate/adapters/codex-stream.ts +134 -134
  142. package/src/orchestrate/adapters/codex.ts +41 -41
  143. package/src/orchestrate/adapters/gemini-stream.ts +166 -166
  144. package/src/orchestrate/adapters/gemini.ts +42 -42
  145. package/src/orchestrate/adapters/index.ts +73 -73
  146. package/src/orchestrate/adapters/opencode-stream.ts +143 -143
  147. package/src/orchestrate/adapters/opencode.ts +47 -47
  148. package/src/orchestrate/adapters/spawn-helper.ts +286 -286
  149. package/src/orchestrate/adapters/types.ts +77 -77
  150. package/src/orchestrate/capability-router.ts +284 -284
  151. package/src/orchestrate/context-compact.ts +188 -188
  152. package/src/orchestrate/cost-tracker.ts +219 -219
  153. package/src/orchestrate/error-recovery.ts +191 -191
  154. package/src/orchestrate/evidence.ts +140 -140
  155. package/src/orchestrate/ledger.ts +110 -110
  156. package/src/orchestrate/memorix-bridge.ts +378 -340
  157. package/src/orchestrate/output-budget.ts +80 -80
  158. package/src/orchestrate/permission.ts +152 -152
  159. package/src/orchestrate/pipeline-trace.ts +131 -131
  160. package/src/orchestrate/prompt-builder.ts +155 -155
  161. package/src/orchestrate/ring-buffer.ts +37 -37
  162. package/src/orchestrate/task-graph.ts +389 -389
  163. package/src/orchestrate/verify-gate.ts +33 -10
  164. package/src/orchestrate/worktree.ts +232 -232
  165. package/src/project/aliases.ts +374 -374
  166. package/src/project/detector.ts +268 -268
  167. package/src/rules/adapters/claude-code.ts +99 -99
  168. package/src/rules/adapters/codex.ts +97 -97
  169. package/src/rules/adapters/copilot.ts +124 -124
  170. package/src/rules/adapters/cursor.ts +114 -114
  171. package/src/rules/adapters/kiro.ts +126 -126
  172. package/src/rules/adapters/trae.ts +56 -56
  173. package/src/rules/adapters/windsurf.ts +83 -83
  174. package/src/rules/syncer.ts +235 -235
  175. package/src/runtime/control-plane-maintenance.ts +1 -0
  176. package/src/runtime/isolated-maintenance.ts +1 -0
  177. package/src/runtime/lifecycle.ts +18 -0
  178. package/src/runtime/maintenance-jobs.ts +1 -0
  179. package/src/runtime/maintenance-runner.ts +2 -0
  180. package/src/runtime/project-maintenance.ts +89 -0
  181. package/src/sdk.ts +334 -304
  182. package/src/search/intent-detector.ts +289 -289
  183. package/src/search/query-expansion.ts +52 -52
  184. package/src/server/formation-timeout.ts +27 -27
  185. package/src/server.ts +334 -93
  186. package/src/skills/mini-skills.ts +386 -386
  187. package/src/store/chat-store.ts +119 -119
  188. package/src/store/graph-store.ts +249 -249
  189. package/src/store/mini-skill-store.ts +349 -349
  190. package/src/store/orama-store.ts +61 -6
  191. package/src/store/persistence-json.ts +212 -212
  192. package/src/store/persistence.ts +291 -291
  193. package/src/store/project-affinity.ts +195 -195
  194. package/src/store/sqlite-db.ts +23 -1
  195. package/src/store/sqlite-store.ts +12 -2
  196. package/src/team/event-bus.ts +76 -76
  197. package/src/team/file-locks.ts +173 -173
  198. package/src/team/handoff.ts +168 -161
  199. package/src/team/messages.ts +203 -203
  200. package/src/team/poll.ts +132 -132
  201. package/src/team/tasks.ts +211 -211
  202. package/src/types.ts +51 -0
  203. package/src/wiki/generator.ts +2 -0
  204. package/src/workspace/mcp-adapters/codex.ts +191 -191
  205. package/src/workspace/mcp-adapters/copilot.ts +105 -105
  206. package/src/workspace/mcp-adapters/cursor.ts +53 -53
  207. package/src/workspace/mcp-adapters/kiro.ts +64 -64
  208. package/src/workspace/mcp-adapters/opencode.ts +123 -123
  209. package/src/workspace/mcp-adapters/trae.ts +134 -134
  210. package/src/workspace/mcp-adapters/windsurf.ts +91 -91
  211. package/src/workspace/sanitizer.ts +60 -60
  212. package/src/workspace/workflow-sync.ts +131 -131
@@ -6,6 +6,7 @@
6
6
  */
7
7
 
8
8
  import * as fs from 'node:fs';
9
+ import { getTuiOperatorContext } from './operator-context.js';
9
10
 
10
11
  export interface ProjectInfo {
11
12
  name: string;
@@ -98,9 +99,7 @@ function truncate(text: string, max = 60): string {
98
99
 
99
100
  export async function getProjectInfo(): Promise<ProjectInfo | null> {
100
101
  try {
101
- const { detectProject } = await import('../../project/detector.js');
102
- const proj = detectProject(process.cwd());
103
- if (!proj) return null;
102
+ const { project: proj } = await getTuiOperatorContext();
104
103
  return {
105
104
  name: proj.name,
106
105
  id: proj.id,
@@ -127,12 +126,8 @@ export async function getHealthInfo(projectId?: string): Promise<HealthInfo> {
127
126
  };
128
127
 
129
128
  try {
130
- const { detectProject } = await import('../../project/detector.js');
131
- const { getProjectDataDir } = await import('../../store/persistence.js');
132
129
  const { initObservationStore: initStore, getObservationStore: getStore } = await import('../../store/obs-store.js');
133
-
134
- const proj = detectProject(process.cwd());
135
- if (!proj) return defaults;
130
+ const { project: proj, dataDir, reader } = await getTuiOperatorContext();
136
131
 
137
132
  // Load .env BEFORE any process.env reads or provider initialization (#46)
138
133
  try {
@@ -141,11 +136,11 @@ export async function getHealthInfo(projectId?: string): Promise<HealthInfo> {
141
136
  } catch { /* best-effort */ }
142
137
 
143
138
  const effectiveProjectId = projectId || proj.id;
144
- const dataDir = await getProjectDataDir(effectiveProjectId);
139
+ if (effectiveProjectId !== proj.id) return defaults;
145
140
  await initStore(dataDir);
146
141
  const allObs = (await getStore().loadAll()) as any[];
147
- // Filter by project flat storage shares one observations.json across projects
148
- const obs = allObs.filter((o: any) => o.projectId === effectiveProjectId);
142
+ const { filterReadableObservations } = await import('../../memory/visibility.js');
143
+ const obs = filterReadableObservations(allObs, reader);
149
144
  const active = obs.filter((o: any) => (o.status ?? 'active') === 'active');
150
145
 
151
146
  defaults.totalMemories = obs.length;
@@ -219,19 +214,15 @@ export async function getHealthInfo(projectId?: string): Promise<HealthInfo> {
219
214
 
220
215
  export async function getRecentMemories(limit = 8, projectId?: string): Promise<MemoryItem[]> {
221
216
  try {
222
- const { detectProject } = await import('../../project/detector.js');
223
- const { getProjectDataDir } = await import('../../store/persistence.js');
224
217
  const { initObservationStore: initStore, getObservationStore: getStore } = await import('../../store/obs-store.js');
225
-
226
- const proj = detectProject(process.cwd());
227
- if (!proj) return [];
218
+ const { project: proj, dataDir, reader } = await getTuiOperatorContext();
228
219
 
229
220
  const effectiveProjectId = projectId || proj.id;
230
- const dataDir = await getProjectDataDir(effectiveProjectId);
221
+ if (effectiveProjectId !== proj.id) return [];
231
222
  await initStore(dataDir);
232
223
  const allObs = (await getStore().loadAll()) as any[];
233
- // Filter by project flat storage shares one observations.json
234
- const projectObs = allObs.filter((o: any) => o.projectId === effectiveProjectId);
224
+ const { filterReadableObservations } = await import('../../memory/visibility.js');
225
+ const projectObs = filterReadableObservations(allObs, reader);
235
226
  const active = projectObs.filter((o: any) => (o.status ?? 'active') === 'active');
236
227
  const filtered = active.filter((o: any) => !/^(Ran:|Command:|Executed:)\s/i.test(o.title || ''));
237
228
 
@@ -251,18 +242,13 @@ export async function getRecentMemories(limit = 8, projectId?: string): Promise<
251
242
  export async function searchMemories(query: string, limit = 10): Promise<SearchResult[]> {
252
243
  try {
253
244
  const { searchObservations } = await import('../../store/orama-store.js');
254
- const { getProjectDataDir } = await import('../../store/persistence.js');
255
- const { detectProject } = await import('../../project/detector.js');
256
245
  const { initObservations, prepareSearchIndex } = await import('../../memory/observations.js');
246
+ const { project: proj, dataDir, reader } = await getTuiOperatorContext();
257
247
 
258
- const proj = detectProject(process.cwd());
259
- if (!proj) return [];
260
-
261
- const dataDir = await getProjectDataDir(proj.id);
262
248
  await initObservations(dataDir);
263
249
  await prepareSearchIndex();
264
250
 
265
- const results = await searchObservations({ query, limit, projectId: proj.id });
251
+ const results = await searchObservations({ query, limit, projectId: proj.id, reader });
266
252
 
267
253
  const typeIcons: Record<string, string> = {
268
254
  gotcha: '!',
@@ -290,15 +276,10 @@ export async function searchMemories(query: string, limit = 10): Promise<SearchR
290
276
 
291
277
  export async function storeQuickMemory(text: string): Promise<{ id: number; title: string } | null> {
292
278
  try {
293
- const { detectProject } = await import('../../project/detector.js');
294
- const { getProjectDataDir } = await import('../../store/persistence.js');
295
279
  const { initObservations, storeObservation } = await import('../../memory/observations.js');
296
280
  const { initObservationStore } = await import('../../store/obs-store.js');
281
+ const { project: proj, dataDir, reader, identity } = await getTuiOperatorContext();
297
282
 
298
- const proj = detectProject(process.cwd());
299
- if (!proj) return null;
300
-
301
- const dataDir = await getProjectDataDir(proj.id);
302
283
  await initObservationStore(dataDir);
303
284
  await initObservations(dataDir);
304
285
 
@@ -310,6 +291,8 @@ export async function storeQuickMemory(text: string): Promise<{ id: number; titl
310
291
  facts: [],
311
292
  projectId: proj.id,
312
293
  sourceDetail: 'explicit',
294
+ visibilityReader: reader,
295
+ ...(identity ? { createdByAgentId: identity.agentId } : {}),
313
296
  });
314
297
 
315
298
  return { id: result.observation.id, title: text.slice(0, 100) };
@@ -484,23 +467,21 @@ export async function getKnowledgeGraph(
484
467
  projectId?: string,
485
468
  ): Promise<import('../../wiki/types.js').ProjectKnowledgeGraph | null> {
486
469
  try {
487
- const { detectProject } = await import('../../project/detector.js');
488
- const { getProjectDataDir } = await import('../../store/persistence.js');
489
470
  const { initObservationStore } = await import('../../store/obs-store.js');
490
471
  const { initObservations, getAllObservations } = await import('../../memory/observations.js');
472
+ const { filterReadableObservations } = await import('../../memory/visibility.js');
491
473
  const { initMiniSkillStore, getMiniSkillStore } = await import('../../store/mini-skill-store.js');
492
474
  const { generateKnowledgeGraph } = await import('../../wiki/knowledge-graph.js');
493
475
 
494
- const proj = detectProject(process.cwd());
495
- if (!proj) return null;
476
+ const { project: proj, dataDir, reader } = await getTuiOperatorContext();
496
477
 
497
478
  const effectiveProjectId = projectId || proj.id;
498
- const dataDir = await getProjectDataDir(effectiveProjectId);
479
+ if (effectiveProjectId !== proj.id) return null;
499
480
  await initObservationStore(dataDir);
500
481
  await initObservations(dataDir);
501
482
  await initMiniSkillStore(dataDir);
502
483
 
503
- const observations = getAllObservations();
484
+ const observations = filterReadableObservations(getAllObservations(), reader);
504
485
  const skills = await getMiniSkillStore().loadByProject(effectiveProjectId);
505
486
 
506
487
  return generateKnowledgeGraph({
@@ -515,23 +496,21 @@ export async function getKnowledgeGraph(
515
496
 
516
497
  export async function getKnowledgeBase(projectId?: string): Promise<import('../../wiki/types.js').ProjectKnowledgeOverview | null> {
517
498
  try {
518
- const { detectProject } = await import('../../project/detector.js');
519
- const { getProjectDataDir } = await import('../../store/persistence.js');
520
499
  const { initObservationStore } = await import('../../store/obs-store.js');
521
500
  const { initObservations, getAllObservations } = await import('../../memory/observations.js');
501
+ const { filterReadableObservations } = await import('../../memory/visibility.js');
522
502
  const { initMiniSkillStore, getMiniSkillStore } = await import('../../store/mini-skill-store.js');
523
503
  const { generateKnowledgeBase } = await import('../../wiki/generator.js');
524
504
 
525
- const proj = detectProject(process.cwd());
526
- if (!proj) return null;
505
+ const { project: proj, dataDir, reader } = await getTuiOperatorContext();
527
506
 
528
507
  const effectiveProjectId = projectId || proj.id;
529
- const dataDir = await getProjectDataDir(effectiveProjectId);
508
+ if (effectiveProjectId !== proj.id) return null;
530
509
  await initObservationStore(dataDir);
531
510
  await initObservations(dataDir);
532
511
  await initMiniSkillStore(dataDir);
533
512
 
534
- const observations = getAllObservations();
513
+ const observations = filterReadableObservations(getAllObservations(), reader);
535
514
  const skills = await getMiniSkillStore().loadByProject(effectiveProjectId);
536
515
 
537
516
  return generateKnowledgeBase({
@@ -1,41 +1,41 @@
1
- /**
2
- * Memorix TUI entry point.
3
- *
4
- * Renders the Ink-based workbench in alternate screen buffer.
5
- * All commands are now Ink-native — no interactive fallback needed.
6
- */
7
-
8
- import { getCliVersion } from '../version.js';
9
-
10
- const ALT_ON = '\x1b[?1049h\x1b[?25l';
11
- const ALT_OFF = '\x1b[?25h\x1b[?1049l';
12
-
13
- /**
14
- * Start the fullscreen Ink-based workbench.
15
- * All views are Ink-native. No alternate-screen exit/re-enter loop.
16
- */
17
- export async function startWorkbench(): Promise<void> {
18
- const { render } = await import('ink');
19
- const React = await import('react');
20
- const { WorkbenchApp } = await import('./App.js');
21
-
22
- process.stdout.write(ALT_ON);
23
-
24
- const app = React.createElement(WorkbenchApp, {
25
- version: getCliVersion(),
26
- onExitForInteractive: () => {
27
- // No-op: all commands are now Ink-native.
28
- // Kept for interface compatibility but never called.
29
- },
30
- });
31
-
32
- const instance = render(app, { exitOnCtrlC: false });
33
-
34
- try {
35
- await instance.waitUntilExit();
36
- } catch {
37
- // Ignore unmount-triggered exits.
38
- }
39
-
40
- process.stdout.write(ALT_OFF);
41
- }
1
+ /**
2
+ * Memorix TUI entry point.
3
+ *
4
+ * Renders the Ink-based workbench in alternate screen buffer.
5
+ * All commands are now Ink-native — no interactive fallback needed.
6
+ */
7
+
8
+ import { getCliVersion } from '../version.js';
9
+
10
+ const ALT_ON = '\x1b[?1049h\x1b[?25l';
11
+ const ALT_OFF = '\x1b[?25h\x1b[?1049l';
12
+
13
+ /**
14
+ * Start the fullscreen Ink-based workbench.
15
+ * All views are Ink-native. No alternate-screen exit/re-enter loop.
16
+ */
17
+ export async function startWorkbench(): Promise<void> {
18
+ const { render } = await import('ink');
19
+ const React = await import('react');
20
+ const { WorkbenchApp } = await import('./App.js');
21
+
22
+ process.stdout.write(ALT_ON);
23
+
24
+ const app = React.createElement(WorkbenchApp, {
25
+ version: getCliVersion(),
26
+ onExitForInteractive: () => {
27
+ // No-op: all commands are now Ink-native.
28
+ // Kept for interface compatibility but never called.
29
+ },
30
+ });
31
+
32
+ const instance = render(app, { exitOnCtrlC: false });
33
+
34
+ try {
35
+ await instance.waitUntilExit();
36
+ } catch {
37
+ // Ignore unmount-triggered exits.
38
+ }
39
+
40
+ process.stdout.write(ALT_OFF);
41
+ }