@getlore/cli 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (148) hide show
  1. package/LICENSE +13 -0
  2. package/README.md +80 -0
  3. package/dist/cli/colors.d.ts +48 -0
  4. package/dist/cli/colors.js +48 -0
  5. package/dist/cli/commands/ask.d.ts +7 -0
  6. package/dist/cli/commands/ask.js +97 -0
  7. package/dist/cli/commands/auth.d.ts +10 -0
  8. package/dist/cli/commands/auth.js +484 -0
  9. package/dist/cli/commands/daemon.d.ts +22 -0
  10. package/dist/cli/commands/daemon.js +244 -0
  11. package/dist/cli/commands/docs.d.ts +7 -0
  12. package/dist/cli/commands/docs.js +188 -0
  13. package/dist/cli/commands/extensions.d.ts +7 -0
  14. package/dist/cli/commands/extensions.js +204 -0
  15. package/dist/cli/commands/misc.d.ts +7 -0
  16. package/dist/cli/commands/misc.js +172 -0
  17. package/dist/cli/commands/pending.d.ts +7 -0
  18. package/dist/cli/commands/pending.js +63 -0
  19. package/dist/cli/commands/projects.d.ts +7 -0
  20. package/dist/cli/commands/projects.js +136 -0
  21. package/dist/cli/commands/search.d.ts +7 -0
  22. package/dist/cli/commands/search.js +102 -0
  23. package/dist/cli/commands/skills.d.ts +24 -0
  24. package/dist/cli/commands/skills.js +447 -0
  25. package/dist/cli/commands/sources.d.ts +7 -0
  26. package/dist/cli/commands/sources.js +121 -0
  27. package/dist/cli/commands/sync.d.ts +31 -0
  28. package/dist/cli/commands/sync.js +768 -0
  29. package/dist/cli/helpers.d.ts +30 -0
  30. package/dist/cli/helpers.js +119 -0
  31. package/dist/core/auth.d.ts +62 -0
  32. package/dist/core/auth.js +330 -0
  33. package/dist/core/config.d.ts +41 -0
  34. package/dist/core/config.js +96 -0
  35. package/dist/core/data-repo.d.ts +31 -0
  36. package/dist/core/data-repo.js +146 -0
  37. package/dist/core/embedder.d.ts +22 -0
  38. package/dist/core/embedder.js +104 -0
  39. package/dist/core/git.d.ts +37 -0
  40. package/dist/core/git.js +140 -0
  41. package/dist/core/index.d.ts +4 -0
  42. package/dist/core/index.js +5 -0
  43. package/dist/core/insight-extractor.d.ts +26 -0
  44. package/dist/core/insight-extractor.js +114 -0
  45. package/dist/core/local-search.d.ts +43 -0
  46. package/dist/core/local-search.js +221 -0
  47. package/dist/core/themes.d.ts +15 -0
  48. package/dist/core/themes.js +77 -0
  49. package/dist/core/types.d.ts +177 -0
  50. package/dist/core/types.js +9 -0
  51. package/dist/core/user-settings.d.ts +15 -0
  52. package/dist/core/user-settings.js +42 -0
  53. package/dist/core/vector-store-lance.d.ts +98 -0
  54. package/dist/core/vector-store-lance.js +384 -0
  55. package/dist/core/vector-store-supabase.d.ts +89 -0
  56. package/dist/core/vector-store-supabase.js +295 -0
  57. package/dist/core/vector-store.d.ts +131 -0
  58. package/dist/core/vector-store.js +503 -0
  59. package/dist/daemon-runner.d.ts +8 -0
  60. package/dist/daemon-runner.js +246 -0
  61. package/dist/extensions/config.d.ts +22 -0
  62. package/dist/extensions/config.js +102 -0
  63. package/dist/extensions/proposals.d.ts +30 -0
  64. package/dist/extensions/proposals.js +178 -0
  65. package/dist/extensions/registry.d.ts +35 -0
  66. package/dist/extensions/registry.js +309 -0
  67. package/dist/extensions/sandbox.d.ts +16 -0
  68. package/dist/extensions/sandbox.js +17 -0
  69. package/dist/extensions/types.d.ts +114 -0
  70. package/dist/extensions/types.js +4 -0
  71. package/dist/extensions/worker.d.ts +1 -0
  72. package/dist/extensions/worker.js +49 -0
  73. package/dist/index.d.ts +17 -0
  74. package/dist/index.js +105 -0
  75. package/dist/mcp/handlers/archive-project.d.ts +51 -0
  76. package/dist/mcp/handlers/archive-project.js +112 -0
  77. package/dist/mcp/handlers/get-quotes.d.ts +27 -0
  78. package/dist/mcp/handlers/get-quotes.js +61 -0
  79. package/dist/mcp/handlers/get-source.d.ts +9 -0
  80. package/dist/mcp/handlers/get-source.js +40 -0
  81. package/dist/mcp/handlers/ingest.d.ts +25 -0
  82. package/dist/mcp/handlers/ingest.js +305 -0
  83. package/dist/mcp/handlers/list-projects.d.ts +4 -0
  84. package/dist/mcp/handlers/list-projects.js +16 -0
  85. package/dist/mcp/handlers/list-sources.d.ts +11 -0
  86. package/dist/mcp/handlers/list-sources.js +20 -0
  87. package/dist/mcp/handlers/research-agent.d.ts +21 -0
  88. package/dist/mcp/handlers/research-agent.js +369 -0
  89. package/dist/mcp/handlers/research.d.ts +22 -0
  90. package/dist/mcp/handlers/research.js +225 -0
  91. package/dist/mcp/handlers/retain.d.ts +18 -0
  92. package/dist/mcp/handlers/retain.js +92 -0
  93. package/dist/mcp/handlers/search.d.ts +52 -0
  94. package/dist/mcp/handlers/search.js +145 -0
  95. package/dist/mcp/handlers/sync.d.ts +47 -0
  96. package/dist/mcp/handlers/sync.js +211 -0
  97. package/dist/mcp/server.d.ts +10 -0
  98. package/dist/mcp/server.js +268 -0
  99. package/dist/mcp/tools.d.ts +16 -0
  100. package/dist/mcp/tools.js +297 -0
  101. package/dist/sync/config.d.ts +26 -0
  102. package/dist/sync/config.js +140 -0
  103. package/dist/sync/discover.d.ts +51 -0
  104. package/dist/sync/discover.js +190 -0
  105. package/dist/sync/index.d.ts +11 -0
  106. package/dist/sync/index.js +11 -0
  107. package/dist/sync/process.d.ts +50 -0
  108. package/dist/sync/process.js +285 -0
  109. package/dist/sync/processors.d.ts +24 -0
  110. package/dist/sync/processors.js +351 -0
  111. package/dist/tui/browse-handlers-ask.d.ts +30 -0
  112. package/dist/tui/browse-handlers-ask.js +372 -0
  113. package/dist/tui/browse-handlers-autocomplete.d.ts +49 -0
  114. package/dist/tui/browse-handlers-autocomplete.js +270 -0
  115. package/dist/tui/browse-handlers-extensions.d.ts +18 -0
  116. package/dist/tui/browse-handlers-extensions.js +107 -0
  117. package/dist/tui/browse-handlers-pending.d.ts +22 -0
  118. package/dist/tui/browse-handlers-pending.js +100 -0
  119. package/dist/tui/browse-handlers-research.d.ts +32 -0
  120. package/dist/tui/browse-handlers-research.js +363 -0
  121. package/dist/tui/browse-handlers-tools.d.ts +42 -0
  122. package/dist/tui/browse-handlers-tools.js +289 -0
  123. package/dist/tui/browse-handlers.d.ts +239 -0
  124. package/dist/tui/browse-handlers.js +1944 -0
  125. package/dist/tui/browse-render-extensions.d.ts +14 -0
  126. package/dist/tui/browse-render-extensions.js +114 -0
  127. package/dist/tui/browse-render-tools.d.ts +18 -0
  128. package/dist/tui/browse-render-tools.js +259 -0
  129. package/dist/tui/browse-render.d.ts +51 -0
  130. package/dist/tui/browse-render.js +599 -0
  131. package/dist/tui/browse-types.d.ts +142 -0
  132. package/dist/tui/browse-types.js +70 -0
  133. package/dist/tui/browse-ui.d.ts +10 -0
  134. package/dist/tui/browse-ui.js +432 -0
  135. package/dist/tui/browse.d.ts +17 -0
  136. package/dist/tui/browse.js +625 -0
  137. package/dist/tui/markdown.d.ts +22 -0
  138. package/dist/tui/markdown.js +223 -0
  139. package/package.json +71 -0
  140. package/plugins/claude-code/.claude-plugin/plugin.json +10 -0
  141. package/plugins/claude-code/.mcp.json +6 -0
  142. package/plugins/claude-code/skills/lore/SKILL.md +63 -0
  143. package/plugins/codex/SKILL.md +36 -0
  144. package/plugins/codex/agents/openai.yaml +10 -0
  145. package/plugins/gemini/GEMINI.md +31 -0
  146. package/plugins/gemini/gemini-extension.json +11 -0
  147. package/skills/generic-agent.md +99 -0
  148. package/skills/openclaw.md +67 -0
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Extension-related rendering functions for the Lore Document Browser TUI
3
+ *
4
+ * Handles rendering of extension lists and details.
5
+ */
6
+ import type { BrowserState, UIComponents } from './browse-types.js';
7
+ /**
8
+ * Render the extensions list
9
+ */
10
+ export declare function renderExtensionsList(ui: UIComponents, state: BrowserState): void;
11
+ /**
12
+ * Render the extension details panel
13
+ */
14
+ export declare function renderExtensionDetails(ui: UIComponents, state: BrowserState): void;
@@ -0,0 +1,114 @@
1
+ /**
2
+ * Extension-related rendering functions for the Lore Document Browser TUI
3
+ *
4
+ * Handles rendering of extension lists and details.
5
+ */
6
+ import { escapeForBlessed, truncate } from './browse-render.js';
7
+ /**
8
+ * Render the extensions list
9
+ */
10
+ export function renderExtensionsList(ui, state) {
11
+ const width = ui.listContent.width - 2;
12
+ const height = ui.listContent.height - 1;
13
+ const lines = [];
14
+ if (state.extensionsList.length === 0) {
15
+ lines.push('');
16
+ lines.push('{blue-fg} No extensions installed{/blue-fg}');
17
+ lines.push('');
18
+ lines.push('{blue-fg} Install with:{/blue-fg}');
19
+ lines.push('{cyan-fg} lore extension install <package>{/cyan-fg}');
20
+ ui.listContent.setContent(lines.join('\n'));
21
+ return;
22
+ }
23
+ const visibleStart = Math.max(0, state.selectedExtensionIndex - Math.floor(height / 3));
24
+ const visibleEnd = Math.min(state.extensionsList.length, visibleStart + height);
25
+ for (let i = visibleStart; i < visibleEnd; i++) {
26
+ const ext = state.extensionsList[i];
27
+ const isSelected = i === state.selectedExtensionIndex;
28
+ const accent = isSelected ? '{cyan-fg}>{/cyan-fg}' : ' ';
29
+ const status = ext.enabled ? '{green-fg}[ok]{/green-fg}' : '{red-fg}[off]{/red-fg}';
30
+ const name = truncate(ext.name, width - 10);
31
+ const version = `v${ext.version}`;
32
+ lines.push(`${accent} ${status} {bold}${escapeForBlessed(name)}{/bold}`);
33
+ lines.push(` {blue-fg}${escapeForBlessed(version)}{/blue-fg}`);
34
+ lines.push('');
35
+ }
36
+ ui.listContent.setContent(lines.join('\n'));
37
+ }
38
+ /**
39
+ * Render the extension details panel
40
+ */
41
+ export function renderExtensionDetails(ui, state) {
42
+ if (state.extensionsList.length === 0) {
43
+ ui.previewContent.setContent('{blue-fg}No extensions{/blue-fg}');
44
+ return;
45
+ }
46
+ const ext = state.extensionsList[state.selectedExtensionIndex];
47
+ if (!ext) {
48
+ ui.previewContent.setContent('{blue-fg}Select an extension{/blue-fg}');
49
+ return;
50
+ }
51
+ const lines = [];
52
+ const previewWidth = ui.previewContent.width - 2;
53
+ // Header
54
+ lines.push(`{bold}${truncate(escapeForBlessed(ext.name), previewWidth)}{/bold}`);
55
+ lines.push(`{blue-fg}v${escapeForBlessed(ext.version)}{/blue-fg}`);
56
+ lines.push('');
57
+ // Status
58
+ const statusColor = ext.enabled ? 'green' : 'red';
59
+ const statusText = ext.enabled ? 'Enabled' : 'Disabled';
60
+ lines.push(`{${statusColor}-fg}Status: ${statusText}{/${statusColor}-fg}`);
61
+ lines.push('');
62
+ // Hooks
63
+ lines.push('{cyan-fg}Hooks:{/cyan-fg}');
64
+ if (ext.hooks.length === 0) {
65
+ lines.push(' {blue-fg}(none){/blue-fg}');
66
+ }
67
+ else {
68
+ for (const hook of ext.hooks) {
69
+ lines.push(` {green-fg}*{/green-fg} ${escapeForBlessed(hook)}`);
70
+ }
71
+ }
72
+ lines.push('');
73
+ // Middleware
74
+ lines.push('{cyan-fg}Middleware:{/cyan-fg}');
75
+ if (ext.middleware.length === 0) {
76
+ lines.push(' {blue-fg}(none){/blue-fg}');
77
+ }
78
+ else {
79
+ for (const mw of ext.middleware) {
80
+ lines.push(` {green-fg}*{/green-fg} ${escapeForBlessed(mw)}`);
81
+ }
82
+ }
83
+ lines.push('');
84
+ // Commands
85
+ lines.push('{cyan-fg}Commands:{/cyan-fg}');
86
+ if (ext.commands.length === 0) {
87
+ lines.push(' {blue-fg}(none){/blue-fg}');
88
+ }
89
+ else {
90
+ for (const cmd of ext.commands) {
91
+ lines.push(` {green-fg}*{/green-fg} ${escapeForBlessed(cmd)}`);
92
+ }
93
+ }
94
+ lines.push('');
95
+ // Permissions
96
+ lines.push('{cyan-fg}Permissions:{/cyan-fg}');
97
+ if (!ext.permissions) {
98
+ lines.push(' {blue-fg}read: true (default){/blue-fg}');
99
+ }
100
+ else {
101
+ const perms = ext.permissions;
102
+ lines.push(` read: ${perms.read !== false ? 'true' : 'false'}`);
103
+ if (perms.proposeCreate)
104
+ lines.push(' proposeCreate: true');
105
+ if (perms.proposeModify)
106
+ lines.push(' proposeModify: true');
107
+ if (perms.proposeDelete)
108
+ lines.push(' proposeDelete: true');
109
+ }
110
+ lines.push('');
111
+ // Instructions
112
+ lines.push('{blue-fg}Press Enter to toggle enabled/disabled{/blue-fg}');
113
+ ui.previewContent.setContent(lines.join('\n'));
114
+ }
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Tool-related rendering functions for the Lore Document Browser TUI
3
+ *
4
+ * Handles rendering of tool lists, forms, and results.
5
+ */
6
+ import type { BrowserState, UIComponents } from './browse-types.js';
7
+ /**
8
+ * Render the tool form overlay
9
+ */
10
+ export declare function renderToolForm(ui: UIComponents, state: BrowserState): void;
11
+ /**
12
+ * Render the tools list
13
+ */
14
+ export declare function renderToolsList(ui: UIComponents, state: BrowserState): void;
15
+ /**
16
+ * Render the tool preview (schema + result)
17
+ */
18
+ export declare function renderToolResult(ui: UIComponents, state: BrowserState): void;
@@ -0,0 +1,259 @@
1
+ /**
2
+ * Tool-related rendering functions for the Lore Document Browser TUI
3
+ *
4
+ * Handles rendering of tool lists, forms, and results.
5
+ */
6
+ import { escapeForBlessed, truncate, formatJsonForPreview } from './browse-render.js';
7
+ function formatToolFormValue(fieldType, value) {
8
+ if (fieldType === 'boolean') {
9
+ return value ? '[x]' : '[ ]';
10
+ }
11
+ const text = value === undefined || value === null ? '' : String(value);
12
+ return `[${escapeForBlessed(text)}]`;
13
+ }
14
+ /**
15
+ * Render the tool form overlay
16
+ */
17
+ export function renderToolForm(ui, state) {
18
+ const width = ui.toolFormContent.width - 2;
19
+ const lines = [];
20
+ if (state.toolFormFields.length === 0) {
21
+ lines.push('{blue-fg}No input fields for this tool.{/blue-fg}');
22
+ lines.push('');
23
+ lines.push('{blue-fg}[Enter: run] [Esc: back]{/blue-fg}');
24
+ ui.toolFormContent.setContent(lines.join('\n'));
25
+ return;
26
+ }
27
+ for (let i = 0; i < state.toolFormFields.length; i++) {
28
+ const field = state.toolFormFields[i];
29
+ const isFocused = i === state.toolFormIndex;
30
+ const name = escapeForBlessed(field.name);
31
+ const reqLabel = field.required ? '{red-fg}(required){/red-fg}' : '{green-fg}(optional){/green-fg}';
32
+ const valueText = formatToolFormValue(field.type, field.value);
33
+ const line = truncate(`${name}: ${valueText} ${reqLabel}`, width);
34
+ lines.push(isFocused ? `{inverse}${line}{/inverse}` : line);
35
+ if (field.description) {
36
+ const hint = truncate(escapeForBlessed(field.description), Math.max(0, width - 2));
37
+ lines.push(` {blue-fg}${hint}{/blue-fg}`);
38
+ }
39
+ lines.push('');
40
+ }
41
+ lines.push('{blue-fg}[Tab: next field] [Shift+Tab: prev] [Enter: run] [Esc: back]{/blue-fg}');
42
+ ui.toolFormContent.setContent(lines.join('\n'));
43
+ }
44
+ /**
45
+ * Render the tools list
46
+ */
47
+ export function renderToolsList(ui, state) {
48
+ const width = ui.listContent.width - 2;
49
+ const height = ui.listContent.height - 1;
50
+ const lines = [];
51
+ if (state.toolsList.length === 0) {
52
+ lines.push('');
53
+ lines.push('{blue-fg} No tools available{/blue-fg}');
54
+ lines.push('');
55
+ lines.push('{blue-fg} Install extensions with tools{/blue-fg}');
56
+ ui.listContent.setContent(lines.join('\n'));
57
+ return;
58
+ }
59
+ const visibleStart = Math.max(0, state.selectedToolIndex - Math.floor(height / 3));
60
+ const visibleEnd = Math.min(state.toolsList.length, visibleStart + height);
61
+ for (let i = visibleStart; i < visibleEnd; i++) {
62
+ const tool = state.toolsList[i];
63
+ const isSelected = i === state.selectedToolIndex;
64
+ const accent = isSelected ? '{cyan-fg}▌{/cyan-fg}' : ' ';
65
+ const name = truncate(tool.name, width - 4);
66
+ const description = truncate(tool.description || '', width - 6);
67
+ lines.push(`${accent} {bold}${escapeForBlessed(name)}{/bold}`);
68
+ if (description) {
69
+ lines.push(`${accent} {cyan-fg}${escapeForBlessed(description)}{/cyan-fg}`);
70
+ }
71
+ lines.push('');
72
+ }
73
+ ui.listContent.setContent(lines.join('\n'));
74
+ }
75
+ /**
76
+ * Format tool result in a human-readable way
77
+ */
78
+ function formatToolResultNicely(result, maxWidth) {
79
+ const lines = [];
80
+ if (result === null || result === undefined) {
81
+ lines.push('{blue-fg}(no result){/blue-fg}');
82
+ return lines;
83
+ }
84
+ if (typeof result !== 'object') {
85
+ lines.push(escapeForBlessed(String(result)));
86
+ return lines;
87
+ }
88
+ const obj = result;
89
+ // Handle running state
90
+ if ('status' in obj && obj.status === 'running') {
91
+ lines.push('{yellow-fg}⏳ Running...{/yellow-fg}');
92
+ if ('message' in obj) {
93
+ lines.push(`{cyan-fg}${escapeForBlessed(String(obj.message))}{/cyan-fg}`);
94
+ }
95
+ return lines;
96
+ }
97
+ // Handle common patterns
98
+ if ('status' in obj && obj.status === 'ok') {
99
+ lines.push(`{green-fg}Status:{/green-fg} ${escapeForBlessed(String(obj.status))}`);
100
+ }
101
+ if ('status' in obj && obj.status === 'error') {
102
+ lines.push(`{red-fg}Status:{/red-fg} error`);
103
+ }
104
+ if ('message' in obj && obj.status !== 'running') {
105
+ lines.push(`{cyan-fg}Message:{/cyan-fg} ${escapeForBlessed(String(obj.message))}`);
106
+ }
107
+ // Handle proposal notification
108
+ if ('proposal_id' in obj) {
109
+ lines.push('');
110
+ lines.push(`{yellow-fg}📋 Proposal created:{/yellow-fg} ${escapeForBlessed(String(obj.proposal_id))}`);
111
+ if ('proposal_note' in obj) {
112
+ lines.push(`{yellow-fg}${escapeForBlessed(String(obj.proposal_note))}{/yellow-fg}`);
113
+ }
114
+ lines.push(`{yellow-fg}Press 'r' to review and approve{/yellow-fg}`);
115
+ }
116
+ // Handle analysis output
117
+ if ('analysis' in obj) {
118
+ lines.push('');
119
+ lines.push('{cyan-fg}Analysis:{/cyan-fg}');
120
+ const analysisText = String(obj.analysis);
121
+ // Wrap long analysis text
122
+ const analysisLines = analysisText.split('\n');
123
+ for (const line of analysisLines.slice(0, 30)) { // Limit to 30 lines
124
+ lines.push(escapeForBlessed(truncate(line, maxWidth)));
125
+ }
126
+ if (analysisLines.length > 30) {
127
+ lines.push('{blue-fg}... (truncated){/blue-fg}');
128
+ }
129
+ }
130
+ if ('total_sources_analyzed' in obj) {
131
+ lines.push(`{cyan-fg}Sources analyzed:{/cyan-fg} ${obj.total_sources_analyzed}`);
132
+ }
133
+ if ('total_speakers' in obj) {
134
+ lines.push(`{cyan-fg}Total speakers:{/cyan-fg} ${obj.total_speakers}`);
135
+ }
136
+ if ('top_pain_point' in obj) {
137
+ lines.push(`{cyan-fg}Top pain point:{/cyan-fg} ${escapeForBlessed(String(obj.top_pain_point))}`);
138
+ }
139
+ if ('verdict' in obj) {
140
+ const verdict = String(obj.verdict);
141
+ const color = verdict === 'SUPPORTED' ? 'green' : verdict === 'CONTRADICTED' ? 'red' : 'yellow';
142
+ lines.push(`{${color}-fg}Verdict:{/${color}-fg} ${verdict}`);
143
+ }
144
+ if ('confidence' in obj) {
145
+ lines.push(`{cyan-fg}Confidence:{/cyan-fg} ${escapeForBlessed(String(obj.confidence))}`);
146
+ }
147
+ if ('coverage_note' in obj && obj.coverage_note) {
148
+ lines.push('');
149
+ lines.push(`{yellow-fg}⚠ ${escapeForBlessed(String(obj.coverage_note))}{/yellow-fg}`);
150
+ }
151
+ if ('features_tested' in obj && Array.isArray(obj.features_tested)) {
152
+ lines.push('');
153
+ lines.push('{cyan-fg}Features tested:{/cyan-fg}');
154
+ for (const feature of obj.features_tested) {
155
+ lines.push(` • ${escapeForBlessed(String(feature))}`);
156
+ }
157
+ }
158
+ if ('pain_points' in obj && Array.isArray(obj.pain_points)) {
159
+ const painPoints = obj.pain_points;
160
+ if (painPoints.length > 0) {
161
+ lines.push('');
162
+ lines.push('{cyan-fg}Pain points:{/cyan-fg}');
163
+ for (const pp of painPoints.slice(0, 5)) {
164
+ lines.push(` • ${escapeForBlessed(pp.category || 'Unknown')} (${pp.frequency || 0}x)`);
165
+ }
166
+ }
167
+ }
168
+ if ('profiles' in obj && Array.isArray(obj.profiles)) {
169
+ const profiles = obj.profiles;
170
+ if (profiles.length > 0) {
171
+ lines.push('');
172
+ lines.push('{cyan-fg}Speakers:{/cyan-fg}');
173
+ for (const p of profiles.slice(0, 5)) {
174
+ lines.push(` • ${escapeForBlessed(p.name || 'Unknown')} (${p.appearances || 0} appearances)`);
175
+ }
176
+ }
177
+ }
178
+ if ('supporting' in obj && Array.isArray(obj.supporting)) {
179
+ const supporting = obj.supporting;
180
+ if (supporting.length > 0) {
181
+ lines.push('');
182
+ lines.push(`{green-fg}Supporting evidence:{/green-fg} ${supporting.length} sources`);
183
+ for (const s of supporting.slice(0, 3)) {
184
+ lines.push(` • ${escapeForBlessed(s.source || 'Unknown source')}`);
185
+ }
186
+ }
187
+ }
188
+ if ('contradicting' in obj && Array.isArray(obj.contradicting)) {
189
+ const contradicting = obj.contradicting;
190
+ if (contradicting.length > 0) {
191
+ lines.push('');
192
+ lines.push(`{red-fg}Contradicting evidence:{/red-fg} ${contradicting.length} sources`);
193
+ for (const c of contradicting.slice(0, 3)) {
194
+ lines.push(` • ${escapeForBlessed(c.source || 'Unknown source')}`);
195
+ }
196
+ }
197
+ }
198
+ // If we didn't format anything special, fall back to JSON
199
+ if (lines.length === 0) {
200
+ const jsonText = formatJsonForPreview(result);
201
+ for (const line of jsonText.split('\n')) {
202
+ lines.push(truncate(escapeForBlessed(line), maxWidth));
203
+ }
204
+ }
205
+ return lines;
206
+ }
207
+ /**
208
+ * Render the tool preview (schema + result)
209
+ */
210
+ export function renderToolResult(ui, state) {
211
+ if (state.toolsList.length === 0) {
212
+ ui.previewContent.setContent('{blue-fg}No tools{/blue-fg}');
213
+ return;
214
+ }
215
+ const tool = state.toolsList[state.selectedToolIndex];
216
+ if (!tool) {
217
+ ui.previewContent.setContent('{blue-fg}Select a tool{/blue-fg}');
218
+ return;
219
+ }
220
+ const lines = [];
221
+ const previewWidth = ui.previewContent.width - 2;
222
+ lines.push(`{bold}${truncate(escapeForBlessed(tool.name), previewWidth)}{/bold}`);
223
+ if (tool.description) {
224
+ lines.push(escapeForBlessed(tool.description));
225
+ }
226
+ lines.push('');
227
+ lines.push('{cyan-fg}Input Schema{/cyan-fg}');
228
+ lines.push('{cyan-fg}─────────────────────────────────{/cyan-fg}');
229
+ const schemaText = formatJsonForPreview(tool.inputSchema);
230
+ for (const line of schemaText.split('\n')) {
231
+ lines.push(truncate(escapeForBlessed(line), previewWidth));
232
+ }
233
+ const matchingResult = state.toolResult && state.toolResult.toolName === tool.name
234
+ ? state.toolResult
235
+ : null;
236
+ if (matchingResult) {
237
+ lines.push('');
238
+ if (matchingResult.ok) {
239
+ lines.push('{green-fg}✓ Result{/green-fg}');
240
+ lines.push('{cyan-fg}─────────────────────────────────{/cyan-fg}');
241
+ const formattedResult = formatToolResultNicely(matchingResult.result, previewWidth);
242
+ lines.push(...formattedResult);
243
+ }
244
+ else {
245
+ lines.push('{red-fg}✗ Error{/red-fg}');
246
+ lines.push('{cyan-fg}─────────────────────────────────{/cyan-fg}');
247
+ lines.push(`{red-fg}${escapeForBlessed(String(matchingResult.result))}{/red-fg}`);
248
+ }
249
+ }
250
+ else if (state.toolRunning) {
251
+ lines.push('');
252
+ lines.push('{yellow-fg}⏳ Running...{/yellow-fg}');
253
+ }
254
+ else {
255
+ lines.push('');
256
+ lines.push('{cyan-fg}Press Enter to run tool{/cyan-fg}');
257
+ }
258
+ ui.previewContent.setContent(lines.join('\n'));
259
+ }
@@ -0,0 +1,51 @@
1
+ /**
2
+ * Rendering functions for the Lore Document Browser TUI
3
+ *
4
+ * Functions for formatting and rendering content to the UI.
5
+ */
6
+ import type { SourceItem, BrowserState, UIComponents, ListItem } from './browse-types.js';
7
+ import type { SourceType } from '../core/types.js';
8
+ /**
9
+ * Format a date for display
10
+ */
11
+ export declare function formatDate(dateStr: string): string;
12
+ /**
13
+ * Truncate a string to a maximum length
14
+ */
15
+ export declare function truncate(str: string, len: number): string;
16
+ /**
17
+ * Escape text for blessed tags - must escape curly braces and handle special chars
18
+ */
19
+ export declare function escapeForBlessed(text: string): string;
20
+ /**
21
+ * Format value as JSON for preview display
22
+ */
23
+ export declare function formatJsonForPreview(value: unknown): string;
24
+ /**
25
+ * Simple markdown to blessed tags converter (no ANSI codes)
26
+ */
27
+ export declare function markdownToBlessed(text: string): string;
28
+ /**
29
+ * Update the status bar
30
+ */
31
+ export declare function updateStatus(ui: UIComponents, state: BrowserState, project?: string, sourceType?: SourceType): void;
32
+ /**
33
+ * Build flattened list items from grouped sources
34
+ */
35
+ export declare function buildListItems(state: BrowserState): ListItem[];
36
+ /**
37
+ * Get the currently selected source (if any)
38
+ */
39
+ export declare function getSelectedSource(state: BrowserState): SourceItem | null;
40
+ /**
41
+ * Render the document list (supports both flat and grouped views)
42
+ */
43
+ export declare function renderList(ui: UIComponents, state: BrowserState): void;
44
+ /**
45
+ * Render the preview pane
46
+ */
47
+ export declare function renderPreview(ui: UIComponents, state: BrowserState): void;
48
+ /**
49
+ * Render the full view pane
50
+ */
51
+ export declare function renderFullView(ui: UIComponents, state: BrowserState): void;