ankui 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (270) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +369 -0
  3. package/dist/cli.d.ts +2 -0
  4. package/dist/cli.js +192 -0
  5. package/dist/cli.js.map +1 -0
  6. package/dist/commands/access.d.ts +6 -0
  7. package/dist/commands/access.js +12 -0
  8. package/dist/commands/access.js.map +1 -0
  9. package/dist/commands/caps.d.ts +6 -0
  10. package/dist/commands/caps.js +12 -0
  11. package/dist/commands/caps.js.map +1 -0
  12. package/dist/commands/discover.d.ts +10 -0
  13. package/dist/commands/discover.js +120 -0
  14. package/dist/commands/discover.js.map +1 -0
  15. package/dist/commands/doctor.d.ts +6 -0
  16. package/dist/commands/doctor.js +12 -0
  17. package/dist/commands/doctor.js.map +1 -0
  18. package/dist/commands/launch-tui.d.ts +15 -0
  19. package/dist/commands/launch-tui.js +53 -0
  20. package/dist/commands/launch-tui.js.map +1 -0
  21. package/dist/commands/list.d.ts +11 -0
  22. package/dist/commands/list.js +51 -0
  23. package/dist/commands/list.js.map +1 -0
  24. package/dist/commands/mcp.d.ts +6 -0
  25. package/dist/commands/mcp.js +12 -0
  26. package/dist/commands/mcp.js.map +1 -0
  27. package/dist/commands/scan-all.d.ts +15 -0
  28. package/dist/commands/scan-all.js +32 -0
  29. package/dist/commands/scan-all.js.map +1 -0
  30. package/dist/commands/show.d.ts +7 -0
  31. package/dist/commands/show.js +12 -0
  32. package/dist/commands/show.js.map +1 -0
  33. package/dist/commands/watch.d.ts +32 -0
  34. package/dist/commands/watch.js +205 -0
  35. package/dist/commands/watch.js.map +1 -0
  36. package/dist/config/ankui-config.d.ts +13 -0
  37. package/dist/config/ankui-config.js +123 -0
  38. package/dist/config/ankui-config.js.map +1 -0
  39. package/dist/scanner/access-review.d.ts +2 -0
  40. package/dist/scanner/access-review.js +183 -0
  41. package/dist/scanner/access-review.js.map +1 -0
  42. package/dist/scanner/adapters/claude.d.ts +2 -0
  43. package/dist/scanner/adapters/claude.js +298 -0
  44. package/dist/scanner/adapters/claude.js.map +1 -0
  45. package/dist/scanner/adapters/codex.d.ts +2 -0
  46. package/dist/scanner/adapters/codex.js +152 -0
  47. package/dist/scanner/adapters/codex.js.map +1 -0
  48. package/dist/scanner/adapters/cursor.d.ts +2 -0
  49. package/dist/scanner/adapters/cursor.js +114 -0
  50. package/dist/scanner/adapters/cursor.js.map +1 -0
  51. package/dist/scanner/adapters/gemini.d.ts +2 -0
  52. package/dist/scanner/adapters/gemini.js +191 -0
  53. package/dist/scanner/adapters/gemini.js.map +1 -0
  54. package/dist/scanner/adapters/index.d.ts +35 -0
  55. package/dist/scanner/adapters/index.js +85 -0
  56. package/dist/scanner/adapters/index.js.map +1 -0
  57. package/dist/scanner/adapters/opencode.d.ts +2 -0
  58. package/dist/scanner/adapters/opencode.js +231 -0
  59. package/dist/scanner/adapters/opencode.js.map +1 -0
  60. package/dist/scanner/adapters/shared.d.ts +57 -0
  61. package/dist/scanner/adapters/shared.js +239 -0
  62. package/dist/scanner/adapters/shared.js.map +1 -0
  63. package/dist/scanner/adapters/skills-sh.d.ts +2 -0
  64. package/dist/scanner/adapters/skills-sh.js +71 -0
  65. package/dist/scanner/adapters/skills-sh.js.map +1 -0
  66. package/dist/scanner/capability-map.d.ts +8 -0
  67. package/dist/scanner/capability-map.js +57 -0
  68. package/dist/scanner/capability-map.js.map +1 -0
  69. package/dist/scanner/discovery.d.ts +31 -0
  70. package/dist/scanner/discovery.js +320 -0
  71. package/dist/scanner/discovery.js.map +1 -0
  72. package/dist/scanner/filesystem-crawler.d.ts +32 -0
  73. package/dist/scanner/filesystem-crawler.js +210 -0
  74. package/dist/scanner/filesystem-crawler.js.map +1 -0
  75. package/dist/scanner/index.d.ts +8 -0
  76. package/dist/scanner/index.js +120 -0
  77. package/dist/scanner/index.js.map +1 -0
  78. package/dist/scanner/multi-project.d.ts +31 -0
  79. package/dist/scanner/multi-project.js +227 -0
  80. package/dist/scanner/multi-project.js.map +1 -0
  81. package/dist/scanner/parallel.d.ts +4 -0
  82. package/dist/scanner/parallel.js +41 -0
  83. package/dist/scanner/parallel.js.map +1 -0
  84. package/dist/scanner/parsing.d.ts +9 -0
  85. package/dist/scanner/parsing.js +135 -0
  86. package/dist/scanner/parsing.js.map +1 -0
  87. package/dist/scanner/paths.d.ts +8 -0
  88. package/dist/scanner/paths.js +39 -0
  89. package/dist/scanner/paths.js.map +1 -0
  90. package/dist/scanner/preview.d.ts +12 -0
  91. package/dist/scanner/preview.js +29 -0
  92. package/dist/scanner/preview.js.map +1 -0
  93. package/dist/scanner/project-discovery.d.ts +11 -0
  94. package/dist/scanner/project-discovery.js +35 -0
  95. package/dist/scanner/project-discovery.js.map +1 -0
  96. package/dist/scanner/ripgrep.d.ts +11 -0
  97. package/dist/scanner/ripgrep.js +61 -0
  98. package/dist/scanner/ripgrep.js.map +1 -0
  99. package/dist/scanner/safety.d.ts +37 -0
  100. package/dist/scanner/safety.js +330 -0
  101. package/dist/scanner/safety.js.map +1 -0
  102. package/dist/scanner/skill-naming.d.ts +5 -0
  103. package/dist/scanner/skill-naming.js +53 -0
  104. package/dist/scanner/skill-naming.js.map +1 -0
  105. package/dist/scanner/watcher.d.ts +15 -0
  106. package/dist/scanner/watcher.js +71 -0
  107. package/dist/scanner/watcher.js.map +1 -0
  108. package/dist/tui/App.d.ts +33 -0
  109. package/dist/tui/App.js +201 -0
  110. package/dist/tui/App.js.map +1 -0
  111. package/dist/tui/components/Breadcrumb.d.ts +10 -0
  112. package/dist/tui/components/Breadcrumb.js +11 -0
  113. package/dist/tui/components/Breadcrumb.js.map +1 -0
  114. package/dist/tui/components/DisclosureRow.d.ts +17 -0
  115. package/dist/tui/components/DisclosureRow.js +13 -0
  116. package/dist/tui/components/DisclosureRow.js.map +1 -0
  117. package/dist/tui/components/DotLeaderRow.d.ts +17 -0
  118. package/dist/tui/components/DotLeaderRow.js +34 -0
  119. package/dist/tui/components/DotLeaderRow.js.map +1 -0
  120. package/dist/tui/components/EmptyStateWhisper.d.ts +5 -0
  121. package/dist/tui/components/EmptyStateWhisper.js +6 -0
  122. package/dist/tui/components/EmptyStateWhisper.js.map +1 -0
  123. package/dist/tui/components/Frame.d.ts +15 -0
  124. package/dist/tui/components/Frame.js +21 -0
  125. package/dist/tui/components/Frame.js.map +1 -0
  126. package/dist/tui/components/IdleWhisper.d.ts +11 -0
  127. package/dist/tui/components/IdleWhisper.js +12 -0
  128. package/dist/tui/components/IdleWhisper.js.map +1 -0
  129. package/dist/tui/components/KeyHint.d.ts +13 -0
  130. package/dist/tui/components/KeyHint.js +12 -0
  131. package/dist/tui/components/KeyHint.js.map +1 -0
  132. package/dist/tui/components/LoadingSplash.d.ts +12 -0
  133. package/dist/tui/components/LoadingSplash.js +55 -0
  134. package/dist/tui/components/LoadingSplash.js.map +1 -0
  135. package/dist/tui/components/ProgressBar.d.ts +16 -0
  136. package/dist/tui/components/ProgressBar.js +24 -0
  137. package/dist/tui/components/ProgressBar.js.map +1 -0
  138. package/dist/tui/components/SearchBox.d.ts +5 -0
  139. package/dist/tui/components/SearchBox.js +6 -0
  140. package/dist/tui/components/SearchBox.js.map +1 -0
  141. package/dist/tui/components/SectionHeader.d.ts +12 -0
  142. package/dist/tui/components/SectionHeader.js +14 -0
  143. package/dist/tui/components/SectionHeader.js.map +1 -0
  144. package/dist/tui/components/SkillViewport.d.ts +8 -0
  145. package/dist/tui/components/SkillViewport.js +42 -0
  146. package/dist/tui/components/SkillViewport.js.map +1 -0
  147. package/dist/tui/components/Spinner.d.ts +11 -0
  148. package/dist/tui/components/Spinner.js +13 -0
  149. package/dist/tui/components/Spinner.js.map +1 -0
  150. package/dist/tui/components/StatusPill.d.ts +13 -0
  151. package/dist/tui/components/StatusPill.js +15 -0
  152. package/dist/tui/components/StatusPill.js.map +1 -0
  153. package/dist/tui/components/TabBar.d.ts +20 -0
  154. package/dist/tui/components/TabBar.js +26 -0
  155. package/dist/tui/components/TabBar.js.map +1 -0
  156. package/dist/tui/components/TextInput.d.ts +13 -0
  157. package/dist/tui/components/TextInput.js +34 -0
  158. package/dist/tui/components/TextInput.js.map +1 -0
  159. package/dist/tui/hooks/use-idle-whisper.d.ts +17 -0
  160. package/dist/tui/hooks/use-idle-whisper.js +66 -0
  161. package/dist/tui/hooks/use-idle-whisper.js.map +1 -0
  162. package/dist/tui/hooks/use-rotating-message.d.ts +20 -0
  163. package/dist/tui/hooks/use-rotating-message.js +34 -0
  164. package/dist/tui/hooks/use-rotating-message.js.map +1 -0
  165. package/dist/tui/input/use-keys.d.ts +16 -0
  166. package/dist/tui/input/use-keys.js +32 -0
  167. package/dist/tui/input/use-keys.js.map +1 -0
  168. package/dist/tui/messages.d.ts +18 -0
  169. package/dist/tui/messages.js +59 -0
  170. package/dist/tui/messages.js.map +1 -0
  171. package/dist/tui/render.d.ts +12 -0
  172. package/dist/tui/render.js +112 -0
  173. package/dist/tui/render.js.map +1 -0
  174. package/dist/tui/screens/AccessTab.d.ts +6 -0
  175. package/dist/tui/screens/AccessTab.js +26 -0
  176. package/dist/tui/screens/AccessTab.js.map +1 -0
  177. package/dist/tui/screens/DoctorTab.d.ts +6 -0
  178. package/dist/tui/screens/DoctorTab.js +24 -0
  179. package/dist/tui/screens/DoctorTab.js.map +1 -0
  180. package/dist/tui/screens/FirstRunScan.d.ts +11 -0
  181. package/dist/tui/screens/FirstRunScan.js +128 -0
  182. package/dist/tui/screens/FirstRunScan.js.map +1 -0
  183. package/dist/tui/screens/McpsTab.d.ts +6 -0
  184. package/dist/tui/screens/McpsTab.js +30 -0
  185. package/dist/tui/screens/McpsTab.js.map +1 -0
  186. package/dist/tui/screens/Overview.d.ts +6 -0
  187. package/dist/tui/screens/Overview.js +75 -0
  188. package/dist/tui/screens/Overview.js.map +1 -0
  189. package/dist/tui/screens/ProjectDrillIn.d.ts +10 -0
  190. package/dist/tui/screens/ProjectDrillIn.js +16 -0
  191. package/dist/tui/screens/ProjectDrillIn.js.map +1 -0
  192. package/dist/tui/screens/Settings.d.ts +8 -0
  193. package/dist/tui/screens/Settings.js +71 -0
  194. package/dist/tui/screens/Settings.js.map +1 -0
  195. package/dist/tui/screens/ToolTab.d.ts +9 -0
  196. package/dist/tui/screens/ToolTab.js +38 -0
  197. package/dist/tui/screens/ToolTab.js.map +1 -0
  198. package/dist/tui/screens/UserScopeDrillIn.d.ts +13 -0
  199. package/dist/tui/screens/UserScopeDrillIn.js +15 -0
  200. package/dist/tui/screens/UserScopeDrillIn.js.map +1 -0
  201. package/dist/tui/state/navigation.d.ts +1 -0
  202. package/dist/tui/state/navigation.js +11 -0
  203. package/dist/tui/state/navigation.js.map +1 -0
  204. package/dist/tui/state/settings-state.d.ts +22 -0
  205. package/dist/tui/state/settings-state.js +30 -0
  206. package/dist/tui/state/settings-state.js.map +1 -0
  207. package/dist/tui/state/tui-state.d.ts +57 -0
  208. package/dist/tui/state/tui-state.js +86 -0
  209. package/dist/tui/state/tui-state.js.map +1 -0
  210. package/dist/tui/theme/borders.d.ts +20 -0
  211. package/dist/tui/theme/borders.js +25 -0
  212. package/dist/tui/theme/borders.js.map +1 -0
  213. package/dist/tui/theme/colors.d.ts +14 -0
  214. package/dist/tui/theme/colors.js +18 -0
  215. package/dist/tui/theme/colors.js.map +1 -0
  216. package/dist/tui/theme/icons.d.ts +30 -0
  217. package/dist/tui/theme/icons.js +51 -0
  218. package/dist/tui/theme/icons.js.map +1 -0
  219. package/dist/tui/util/doctor-grouping.d.ts +27 -0
  220. package/dist/tui/util/doctor-grouping.js +67 -0
  221. package/dist/tui/util/doctor-grouping.js.map +1 -0
  222. package/dist/tui/util/finding-grouping.d.ts +24 -0
  223. package/dist/tui/util/finding-grouping.js +43 -0
  224. package/dist/tui/util/finding-grouping.js.map +1 -0
  225. package/dist/tui/util/mcp-grouping.d.ts +27 -0
  226. package/dist/tui/util/mcp-grouping.js +96 -0
  227. package/dist/tui/util/mcp-grouping.js.map +1 -0
  228. package/dist/tui/util/scan-history.d.ts +15 -0
  229. package/dist/tui/util/scan-history.js +43 -0
  230. package/dist/tui/util/scan-history.js.map +1 -0
  231. package/dist/tui/util/skill-filter.d.ts +2 -0
  232. package/dist/tui/util/skill-filter.js +7 -0
  233. package/dist/tui/util/skill-filter.js.map +1 -0
  234. package/dist/tui/util/skill-grouping.d.ts +3 -0
  235. package/dist/tui/util/skill-grouping.js +31 -0
  236. package/dist/tui/util/skill-grouping.js.map +1 -0
  237. package/dist/types.d.ts +151 -0
  238. package/dist/types.js +143 -0
  239. package/dist/types.js.map +1 -0
  240. package/dist/utils/errors.d.ts +1 -0
  241. package/dist/utils/errors.js +7 -0
  242. package/dist/utils/errors.js.map +1 -0
  243. package/dist/utils/format-access.d.ts +3 -0
  244. package/dist/utils/format-access.js +94 -0
  245. package/dist/utils/format-access.js.map +1 -0
  246. package/dist/utils/format-caps.d.ts +3 -0
  247. package/dist/utils/format-caps.js +139 -0
  248. package/dist/utils/format-caps.js.map +1 -0
  249. package/dist/utils/format-doctor.d.ts +3 -0
  250. package/dist/utils/format-doctor.js +105 -0
  251. package/dist/utils/format-doctor.js.map +1 -0
  252. package/dist/utils/format-list.d.ts +7 -0
  253. package/dist/utils/format-list.js +112 -0
  254. package/dist/utils/format-list.js.map +1 -0
  255. package/dist/utils/format-mcp.d.ts +3 -0
  256. package/dist/utils/format-mcp.js +130 -0
  257. package/dist/utils/format-mcp.js.map +1 -0
  258. package/dist/utils/format-multi-project.d.ts +3 -0
  259. package/dist/utils/format-multi-project.js +100 -0
  260. package/dist/utils/format-multi-project.js.map +1 -0
  261. package/dist/utils/format-show.d.ts +6 -0
  262. package/dist/utils/format-show.js +143 -0
  263. package/dist/utils/format-show.js.map +1 -0
  264. package/dist/utils/format.d.ts +3 -0
  265. package/dist/utils/format.js +53 -0
  266. package/dist/utils/format.js.map +1 -0
  267. package/dist/utils/paths.d.ts +1 -0
  268. package/dist/utils/paths.js +10 -0
  269. package/dist/utils/paths.js.map +1 -0
  270. package/package.json +61 -0
@@ -0,0 +1,112 @@
1
+ import React, { useEffect, useState } from "react";
2
+ import { render } from "ink";
3
+ import { App } from "./App.js";
4
+ import { LoadingSplash } from "./components/LoadingSplash.js";
5
+ /**
6
+ * Mount the TUI. Accepts one of four shapes:
7
+ *
8
+ * 1. `{ loadScan }` (Phase 8g): mount {@link LoadingSplash} immediately, swap
9
+ * to {@link App} when the promise resolves. Use this on the CLI launch
10
+ * path so the developer sees the FROM-themed loading rotation instead of a
11
+ * blank terminal during the multi-project scan.
12
+ * 2. `MultiProjectScanResult` (legacy): mount {@link App} directly with the
13
+ * pre-loaded result. Used by tests and callers that have already scanned.
14
+ * 3. {@link DataSource} (Phase 10): mount {@link App} with a subscribable
15
+ * data source for watch-mode rescans.
16
+ * 4. `{ mode: "firstRun", … }` (Phase 8f): mount {@link App} in first-run
17
+ * mode with no scan data — App renders the FirstRunScan wizard.
18
+ */
19
+ /** ANSI escapes for the alternate screen buffer — the same trick vim/htop use. */
20
+ const ENTER_ALT_BUFFER = "\x1B[?1049h\x1B[2J\x1B[H";
21
+ const EXIT_ALT_BUFFER = "\x1B[?1049l";
22
+ async function withAltScreenBuffer(mount) {
23
+ process.stdout.write(ENTER_ALT_BUFFER);
24
+ const restore = () => {
25
+ process.stdout.write(EXIT_ALT_BUFFER);
26
+ };
27
+ const onSigint = () => {
28
+ restore();
29
+ process.exit(130);
30
+ };
31
+ process.on("SIGINT", onSigint);
32
+ try {
33
+ await mount();
34
+ }
35
+ finally {
36
+ process.off("SIGINT", onSigint);
37
+ restore();
38
+ }
39
+ }
40
+ export async function renderTui(input) {
41
+ if (isFirstRunOptions(input)) {
42
+ await withAltScreenBuffer(async () => {
43
+ const instance = render(React.createElement(App, {
44
+ mode: "firstRun",
45
+ result: null,
46
+ homeDir: input.homeDir,
47
+ onConfigChange: input.onConfigChange
48
+ }));
49
+ await instance.waitUntilExit();
50
+ });
51
+ return;
52
+ }
53
+ if (isLoadScanOptions(input)) {
54
+ await withAltScreenBuffer(async () => {
55
+ const instance = render(React.createElement(LauncherShell, { loadScan: input.loadScan }));
56
+ await instance.waitUntilExit();
57
+ });
58
+ return;
59
+ }
60
+ const props = "initial" in input
61
+ ? { dataSource: input }
62
+ : { result: input };
63
+ await withAltScreenBuffer(async () => {
64
+ const instance = render(React.createElement(App, props));
65
+ await instance.waitUntilExit();
66
+ });
67
+ }
68
+ function isFirstRunOptions(input) {
69
+ return (typeof input === "object" &&
70
+ input !== null &&
71
+ input.mode === "firstRun");
72
+ }
73
+ function isLoadScanOptions(input) {
74
+ return (typeof input === "object" &&
75
+ input !== null &&
76
+ typeof input.loadScan === "function");
77
+ }
78
+ function LauncherShell({ loadScan }) {
79
+ const [result, setResult] = useState(null);
80
+ const [error, setError] = useState(null);
81
+ useEffect(() => {
82
+ let cancelled = false;
83
+ loadScan()
84
+ .then((r) => {
85
+ if (!cancelled)
86
+ setResult(r);
87
+ })
88
+ .catch((err) => {
89
+ if (!cancelled) {
90
+ setError(err instanceof Error ? err.message : String(err));
91
+ }
92
+ });
93
+ return () => {
94
+ cancelled = true;
95
+ };
96
+ }, [loadScan]);
97
+ const handleRefresh = React.useCallback(async () => {
98
+ const next = await loadScan();
99
+ setResult(next);
100
+ }, [loadScan]);
101
+ if (error !== null) {
102
+ return React.createElement(LoadingSplash, {
103
+ active: false,
104
+ completed: false
105
+ });
106
+ }
107
+ if (result === null) {
108
+ return React.createElement(LoadingSplash, { active: true });
109
+ }
110
+ return React.createElement(App, { result, onRefresh: handleRefresh });
111
+ }
112
+ //# sourceMappingURL=render.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render.js","sourceRoot":"","sources":["../../src/tui/render.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import type { MultiProjectScanResult } from "../../types.js";
3
+ export interface AccessTabProps {
4
+ result: MultiProjectScanResult;
5
+ }
6
+ export declare function AccessTab({ result }: AccessTabProps): React.ReactElement;
@@ -0,0 +1,26 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from "react";
3
+ import { Box, Text } from "ink";
4
+ import { EmptyStateWhisper } from "../components/EmptyStateWhisper.js";
5
+ import { SectionHeader } from "../components/SectionHeader.js";
6
+ import { EMPTY_STATE_WHISPERS } from "../messages.js";
7
+ import { relativizeHome } from "../../utils/paths.js";
8
+ import { aggregateFindings } from "../util/finding-grouping.js";
9
+ export function AccessTab({ result }) {
10
+ const sections = aggregateFindings(result);
11
+ if (sections.length === 0) {
12
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { label: "ACCESS" }), _jsx(Text, { dimColor: true, children: "No findings." }), _jsx(Box, { marginTop: 1, children: _jsx(EmptyStateWhisper, { text: EMPTY_STATE_WHISPERS.noFindings }) })] }));
13
+ }
14
+ const total = sections.reduce((n, s) => n + s.findings.length, 0);
15
+ const breakdown = sections
16
+ .map((s) => `${s.findings.length} ${s.category}`)
17
+ .join(" · ");
18
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { label: "ACCESS" }), _jsxs(Text, { children: [total, " findings (", breakdown, ")"] }), sections.map((section) => (_jsx(FindingSectionBlock, { section: section, homeDir: result.homeDir }, section.category)))] }));
19
+ }
20
+ function FindingSectionBlock({ section, homeDir }) {
21
+ return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(SectionHeader, { label: section.label.toUpperCase() }), section.findings.map((finding) => (_jsx(FindingBlock, { finding: finding, homeDir: homeDir }, finding.id)))] }));
22
+ }
23
+ function FindingBlock({ finding, homeDir }) {
24
+ return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: ` • ${finding.title}` }), _jsx(Text, { dimColor: true, children: ` Scope: ${finding.scope} · Tools: ${finding.toolIds.join(", ")}` }), _jsx(Text, { dimColor: true, children: finding.sourcePaths.length === 1 ? " Source:" : " Sources:" }), finding.sourcePaths.map((sourcePath) => (_jsx(Fragment, { children: _jsx(Text, { dimColor: true, children: ` ${relativizeHome(sourcePath, homeDir)}` }) }, sourcePath))), _jsx(Text, { children: ` Recommendation: ${finding.recommendation}` })] }));
25
+ }
26
+ //# sourceMappingURL=AccessTab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AccessTab.js","sourceRoot":"","sources":["../../../src/tui/screens/AccessTab.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import type { MultiProjectScanResult } from "../../types.js";
3
+ export interface DoctorTabProps {
4
+ result: MultiProjectScanResult;
5
+ }
6
+ export declare function DoctorTab({ result }: DoctorTabProps): React.ReactElement;
@@ -0,0 +1,24 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from "react";
3
+ import { Box, Text } from "ink";
4
+ import { EmptyStateWhisper } from "../components/EmptyStateWhisper.js";
5
+ import { SectionHeader } from "../components/SectionHeader.js";
6
+ import { EMPTY_STATE_WHISPERS } from "../messages.js";
7
+ import { relativizeHome } from "../../utils/paths.js";
8
+ import { buildDoctorBoard, groupWarningsByReason } from "../util/doctor-grouping.js";
9
+ export function DoctorTab({ result }) {
10
+ const board = buildDoctorBoard(result);
11
+ const warningGroups = groupWarningsByReason(result);
12
+ const detectedCount = board.filter((row) => row.detected).length;
13
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { label: "DOCTOR" }), _jsxs(Text, { children: [board.length, " tools \u00B7 ", detectedCount, " detected \u00B7 ", result.warnings.length, " warnings"] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(SectionHeader, { label: "TOOLS" }), board.map((row) => (_jsx(ToolBoardRow, { row: row }, row.toolId)))] }), _jsx(Box, { marginTop: 1, flexDirection: "column", children: warningGroups.length === 0 ? (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "No warnings." }), _jsx(Box, { marginTop: 1, children: _jsx(EmptyStateWhisper, { text: EMPTY_STATE_WHISPERS.noWarnings }) })] })) : (_jsx(WarningsSection, { groups: warningGroups, homeDir: result.homeDir })) })] }));
14
+ }
15
+ function ToolBoardRow({ row }) {
16
+ if (!row.detected) {
17
+ return (_jsxs(Box, { children: [_jsx(Text, { children: " - " }), _jsx(Text, { bold: true, children: row.name }), _jsx(Text, { children: " " }), _jsx(Text, { dimColor: true, children: "not detected" })] }));
18
+ }
19
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { children: " ✓ " }), _jsx(Text, { bold: true, children: row.name })] }), row.userPaths.length > 0 && (_jsxs(Fragment, { children: [_jsx(Text, { dimColor: true, children: " user:" }), row.userPaths.map((path) => (_jsx(Text, { children: ` ${path}` }, `u:${path}`)))] })), row.projectPaths.length > 0 && (_jsxs(Fragment, { children: [_jsx(Text, { dimColor: true, children: " project:" }), row.projectPaths.map((path) => (_jsx(Text, { children: ` ${path}` }, `p:${path}`)))] }))] }));
20
+ }
21
+ function WarningsSection({ groups, homeDir }) {
22
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { label: "WARNINGS" }), groups.map((group) => (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { bold: true, children: [group.reason, " (", group.warnings.length, ")"] }), group.warnings.map((warning) => (_jsxs(Text, { dimColor: true, children: [" ", warning.path ? relativizeHome(warning.path, homeDir) : warning.message] }, warning.id)))] }, group.reason)))] }));
23
+ }
24
+ //# sourceMappingURL=DoctorTab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DoctorTab.js","sourceRoot":"","sources":["../../../src/tui/screens/DoctorTab.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,11 @@
1
+ import React from "react";
2
+ import { type CrawlOptions, type CrawlResult } from "../../scanner/filesystem-crawler.js";
3
+ export interface FirstRunScanProps {
4
+ mode: "firstRun" | "rescan";
5
+ homeDir: string;
6
+ onConfirm: (devRoots: string[]) => void;
7
+ onCancel: () => void;
8
+ /** Injected crawler for tests. Defaults to the production `crawlForProjects`. */
9
+ crawlImpl?: (options: CrawlOptions) => Promise<CrawlResult>;
10
+ }
11
+ export declare function FirstRunScan({ homeDir, onConfirm, onCancel, crawlImpl }: FirstRunScanProps): React.ReactElement;
@@ -0,0 +1,128 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useMemo, useReducer, useRef, useState } from "react";
3
+ import { Box, Text, useInput } from "ink";
4
+ import { crawlForProjects as defaultCrawl } from "../../scanner/filesystem-crawler.js";
5
+ import { groupProjectsByParent } from "../../scanner/project-discovery.js";
6
+ import { relativizeHome } from "../../utils/paths.js";
7
+ import { Spinner } from "../components/Spinner.js";
8
+ import { ProgressBar } from "../components/ProgressBar.js";
9
+ import { SectionHeader } from "../components/SectionHeader.js";
10
+ import { DotLeaderRow } from "../components/DotLeaderRow.js";
11
+ import { useKeys } from "../input/use-keys.js";
12
+ import { SPLASH_DOT, STATUS_DOT, ACTIVE_PREFIX } from "../theme/icons.js";
13
+ import { ACCENT } from "../theme/colors.js";
14
+ function selectionReducer(state, action) {
15
+ switch (action.type) {
16
+ case "seed":
17
+ return { cursor: 0, candidates: action.candidates };
18
+ case "moveCursor": {
19
+ if (state.candidates.length === 0)
20
+ return state;
21
+ const max = state.candidates.length - 1;
22
+ const step = action.direction === "down" ? 1 : -1;
23
+ const next = Math.max(0, Math.min(max, state.cursor + step));
24
+ return { ...state, cursor: next };
25
+ }
26
+ case "toggleCursor": {
27
+ const list = state.candidates.map((c, idx) => idx === state.cursor ? { ...c, selected: !c.selected } : c);
28
+ return { ...state, candidates: list };
29
+ }
30
+ default:
31
+ return state;
32
+ }
33
+ }
34
+ export function FirstRunScan({ homeDir, onConfirm, onCancel, crawlImpl }) {
35
+ const [phase, setPhase] = useState("crawling");
36
+ const [livePathsVisited, setLivePathsVisited] = useState(0);
37
+ const [liveProjectCount, setLiveProjectCount] = useState(0);
38
+ const [spinnerFrame, setSpinnerFrame] = useState(0);
39
+ const abortRef = useRef(null);
40
+ const [selection, dispatch] = useReducer(selectionReducer, {
41
+ cursor: 0,
42
+ candidates: []
43
+ });
44
+ const crawl = crawlImpl ?? defaultCrawl;
45
+ // Spinner timer.
46
+ useEffect(() => {
47
+ if (phase !== "crawling")
48
+ return;
49
+ const t = setInterval(() => setSpinnerFrame((f) => f + 1), 100);
50
+ return () => clearInterval(t);
51
+ }, [phase]);
52
+ // Crawl effect — exactly once on mount.
53
+ useEffect(() => {
54
+ const controller = new AbortController();
55
+ abortRef.current = controller;
56
+ let cancelled = false;
57
+ let projectCount = 0;
58
+ crawl({
59
+ rootDir: homeDir,
60
+ signal: controller.signal,
61
+ onProject: (project) => {
62
+ projectCount += 1;
63
+ if (!cancelled)
64
+ setLiveProjectCount(projectCount);
65
+ void project;
66
+ }
67
+ })
68
+ .then((result) => {
69
+ if (cancelled)
70
+ return;
71
+ setLivePathsVisited(result.stats.pathsVisited);
72
+ const grouped = groupProjectsByParent(result.projects);
73
+ if (grouped.length === 0) {
74
+ setPhase("empty");
75
+ return;
76
+ }
77
+ const seeded = grouped.map((c) => ({
78
+ ...c,
79
+ selected: c.defaultOn
80
+ }));
81
+ dispatch({ type: "seed", candidates: seeded });
82
+ setPhase("selecting");
83
+ })
84
+ .catch(() => {
85
+ // Crawler tolerates internal failures via warnings; an actual rejection
86
+ // is a programmer bug. Surface as empty so the user can still cancel.
87
+ if (!cancelled)
88
+ setPhase("empty");
89
+ });
90
+ return () => {
91
+ cancelled = true;
92
+ controller.abort();
93
+ };
94
+ // eslint-disable-next-line react-hooks/exhaustive-deps
95
+ }, []);
96
+ useKeys({
97
+ onArrowUp: () => dispatch({ type: "moveCursor", direction: "up" }),
98
+ onArrowDown: () => dispatch({ type: "moveCursor", direction: "down" }),
99
+ onEnter: () => {
100
+ if (phase === "selecting") {
101
+ const picked = selection.candidates.filter((c) => c.selected).map((c) => c.parentPath);
102
+ onConfirm(picked);
103
+ }
104
+ else if (phase === "empty") {
105
+ onConfirm([]);
106
+ }
107
+ },
108
+ onEscape: () => {
109
+ abortRef.current?.abort();
110
+ onCancel();
111
+ }
112
+ });
113
+ // Space toggles current selection.
114
+ useInput((input, key) => {
115
+ if (input === " " && !key.ctrl && !key.meta && phase === "selecting") {
116
+ dispatch({ type: "toggleCursor" });
117
+ }
118
+ });
119
+ const totalSelected = useMemo(() => selection.candidates.filter((c) => c.selected).length, [selection.candidates]);
120
+ return (_jsxs(Box, { flexDirection: "column", paddingX: 2, children: [_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { children: `${SPLASH_DOT} ankui` }), _jsx(Text, { dimColor: true, children: " anghkooey" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "─".repeat(42) }) }), _jsx(Text, { children: "remember what your agents can access" })] }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Spinner, { frame: spinnerFrame, label: phase === "crawling" ? "Remembering..." : "Remembered." }), _jsxs(Box, { marginTop: 1, children: [_jsx(ProgressBar, { value: phase === "crawling" ? 0.4 : 1, width: 28 }), _jsx(Text, { children: ` ${phase === "crawling" ? "" : "100%"}` })] }), _jsx(Text, { dimColor: true, children: `${livePathsVisited.toLocaleString("en-US")} paths visited · ${liveProjectCount} projects found` })] }), phase === "empty" && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "No projects found. Press Enter to continue with an empty config, or Esc to cancel." }) })), phase === "selecting" && (_jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(SectionHeader, { label: "FOUND DEV ROOTS" }), selection.candidates.map((candidate, idx) => {
121
+ const isActive = idx === selection.cursor;
122
+ const glyph = candidate.selected ? STATUS_DOT : "○";
123
+ const display = relativizeHome(candidate.parentPath, homeDir);
124
+ const meta = `${candidate.projectCount} ${candidate.projectCount === 1 ? "project" : "projects"}`;
125
+ return (_jsxs(Box, { children: [_jsx(Text, { color: isActive ? ACCENT : undefined, children: isActive ? `${ACTIVE_PREFIX} ` : " " }), _jsx(Text, { color: candidate.selected ? ACCENT : undefined, children: `${glyph} ` }), _jsx(Box, { width: 60, children: _jsx(DotLeaderRow, { label: display, metadata: meta, width: 60 }) })] }, candidate.parentPath));
126
+ }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: `space toggle · enter accept ${totalSelected} root${totalSelected === 1 ? "" : "s"} · esc cancel` }) })] }))] }));
127
+ }
128
+ //# sourceMappingURL=FirstRunScan.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FirstRunScan.js","sourceRoot":"","sources":["../../../src/tui/screens/FirstRunScan.tsx"],"names":[],"mappings":";AAAA,OAAc,EAAE,SAAS,EAAE,OAAO,EAAE,UAAU,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AAChF,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,KAAK,CAAC;AAE1C,OAAO,EACL,gBAAgB,IAAI,YAAY,EAIjC,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EACL,qBAAqB,EAEtB,MAAM,oCAAoC,CAAC;AAC5C,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,OAAO,EAAE,MAAM,0BAA0B,CAAC;AACnD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,aAAa,EAAE,MAAM,mBAAmB,CAAC;AAC1E,OAAO,EAAE,MAAM,EAAE,MAAM,oBAAoB,CAAC;AA2B5C,SAAS,gBAAgB,CAAC,KAAqB,EAAE,MAAuB;IACtE,QAAQ,MAAM,CAAC,IAAI,EAAE,CAAC;QACpB,KAAK,MAAM;YACT,OAAO,EAAE,MAAM,EAAE,CAAC,EAAE,UAAU,EAAE,MAAM,CAAC,UAAU,EAAE,CAAC;QACtD,KAAK,YAAY,CAAC,CAAC,CAAC;YAClB,IAAI,KAAK,CAAC,UAAU,CAAC,MAAM,KAAK,CAAC;gBAAE,OAAO,KAAK,CAAC;YAChD,MAAM,GAAG,GAAG,KAAK,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAC;YACxC,MAAM,IAAI,GAAG,MAAM,CAAC,SAAS,KAAK,MAAM,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;YAClD,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC,CAAC;YAC7D,OAAO,EAAE,GAAG,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;QACpC,CAAC;QACD,KAAK,cAAc,CAAC,CAAC,CAAC;YACpB,MAAM,IAAI,GAAG,KAAK,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,CAC3C,GAAG,KAAK,KAAK,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,QAAQ,EAAE,CAAC,CAAC,CAAC,QAAQ,EAAE,CAAC,CAAC,CAAC,CAAC,CAC3D,CAAC;YACF,OAAO,EAAE,GAAG,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QACxC,CAAC;QACD;YACE,OAAO,KAAK,CAAC;IACjB,CAAC;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,EAC3B,OAAO,EACP,SAAS,EACT,QAAQ,EACR,SAAS,EACS;IAClB,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,CAAQ,UAAU,CAAC,CAAC;IACtD,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,CAAC,gBAAgB,EAAE,mBAAmB,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IAC5D,MAAM,CAAC,YAAY,EAAE,eAAe,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAAC;IACpD,MAAM,QAAQ,GAAG,MAAM,CAAyB,IAAI,CAAC,CAAC;IAEtD,MAAM,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,UAAU,CAAC,gBAAgB,EAAE;QACzD,MAAM,EAAE,CAAC;QACT,UAAU,EAAE,EAAE;KACf,CAAC,CAAC;IAEH,MAAM,KAAK,GAAG,SAAS,IAAI,YAAY,CAAC;IAExC,iBAAiB;IACjB,SAAS,CAAC,GAAG,EAAE;QACb,IAAI,KAAK,KAAK,UAAU;YAAE,OAAO;QACjC,MAAM,CAAC,GAAG,WAAW,CAAC,GAAG,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAChE,OAAO,GAAG,EAAE,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC;IAChC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEZ,wCAAwC;IACxC,SAAS,CAAC,GAAG,EAAE;QACb,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;QACzC,QAAQ,CAAC,OAAO,GAAG,UAAU,CAAC;QAC9B,IAAI,SAAS,GAAG,KAAK,CAAC;QAEtB,IAAI,YAAY,GAAG,CAAC,CAAC;QACrB,KAAK,CAAC;YACJ,OAAO,EAAE,OAAO;YAChB,MAAM,EAAE,UAAU,CAAC,MAAM;YACzB,SAAS,EAAE,CAAC,OAAqB,EAAE,EAAE;gBACnC,YAAY,IAAI,CAAC,CAAC;gBAClB,IAAI,CAAC,SAAS;oBAAE,mBAAmB,CAAC,YAAY,CAAC,CAAC;gBAClD,KAAK,OAAO,CAAC;YACf,CAAC;SACF,CAAC;aACC,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE;YACf,IAAI,SAAS;gBAAE,OAAO;YACtB,mBAAmB,CAAC,MAAM,CAAC,KAAK,CAAC,YAAY,CAAC,CAAC;YAC/C,MAAM,OAAO,GAAG,qBAAqB,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC;YACvD,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;gBACzB,QAAQ,CAAC,OAAO,CAAC,CAAC;gBAClB,OAAO;YACT,CAAC;YACD,MAAM,MAAM,GAA0B,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;gBACxD,GAAG,CAAC;gBACJ,QAAQ,EAAE,CAAC,CAAC,SAAS;aACtB,CAAC,CAAC,CAAC;YACJ,QAAQ,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,CAAC,CAAC;YAC/C,QAAQ,CAAC,WAAW,CAAC,CAAC;QACxB,CAAC,CAAC;aACD,KAAK,CAAC,GAAG,EAAE;YACV,wEAAwE;YACxE,sEAAsE;YACtE,IAAI,CAAC,SAAS;gBAAE,QAAQ,CAAC,OAAO,CAAC,CAAC;QACpC,CAAC,CAAC,CAAC;QAEL,OAAO,GAAG,EAAE;YACV,SAAS,GAAG,IAAI,CAAC;YACjB,UAAU,CAAC,KAAK,EAAE,CAAC;QACrB,CAAC,CAAC;QACF,uDAAuD;IACzD,CAAC,EAAE,EAAE,CAAC,CAAC;IAEP,OAAO,CAAC;QACN,SAAS,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;QAClE,WAAW,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,EAAE,CAAC;QACtE,OAAO,EAAE,GAAG,EAAE;YACZ,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;gBAC1B,MAAM,MAAM,GAAG,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;gBACvF,SAAS,CAAC,MAAM,CAAC,CAAC;YACpB,CAAC;iBAAM,IAAI,KAAK,KAAK,OAAO,EAAE,CAAC;gBAC7B,SAAS,CAAC,EAAE,CAAC,CAAC;YAChB,CAAC;QACH,CAAC;QACD,QAAQ,EAAE,GAAG,EAAE;YACb,QAAQ,CAAC,OAAO,EAAE,KAAK,EAAE,CAAC;YAC1B,QAAQ,EAAE,CAAC;QACb,CAAC;KACF,CAAC,CAAC;IAEH,mCAAmC;IACnC,QAAQ,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,EAAE;QACtB,IAAI,KAAK,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,GAAG,CAAC,IAAI,IAAI,KAAK,KAAK,WAAW,EAAE,CAAC;YACrE,QAAQ,CAAC,EAAE,IAAI,EAAE,cAAc,EAAE,CAAC,CAAC;QACrC,CAAC;IACH,CAAC,CAAC,CAAC;IAEH,MAAM,aAAa,GAAG,OAAO,CAC3B,GAAG,EAAE,CAAC,SAAS,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,MAAM,EAC3D,CAAC,SAAS,CAAC,UAAU,CAAC,CACvB,CAAC;IAEF,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,QAAQ,EAAE,CAAC,aACrC,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,SAAS,EAAE,CAAC,aACtC,KAAC,IAAI,cAAE,GAAG,UAAU,SAAS,GAAQ,EACrC,KAAC,IAAI,IAAC,QAAQ,kBAAE,cAAc,GAAQ,EACtC,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,IAAI,IAAC,QAAQ,kBAAE,GAAG,CAAC,MAAM,CAAC,EAAE,CAAC,GAAQ,GAClC,EACN,KAAC,IAAI,cAAE,sCAAsC,GAAQ,IACjD,EAEN,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,SAAS,EAAE,CAAC,aACtC,KAAC,OAAO,IACN,KAAK,EAAE,YAAY,EACnB,KAAK,EAAE,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,aAAa,GAC9D,EACF,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,aACf,KAAC,WAAW,IAAC,KAAK,EAAE,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,KAAK,EAAE,EAAE,GAAI,EACjE,KAAC,IAAI,cAAE,KAAK,KAAK,KAAK,UAAU,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,EAAE,GAAQ,IACpD,EACN,KAAC,IAAI,IAAC,QAAQ,kBACX,GAAG,gBAAgB,CAAC,cAAc,CAAC,OAAO,CAAC,oBAAoB,gBAAgB,iBAAiB,GAC5F,IACH,EAEL,KAAK,KAAK,OAAO,IAAI,CACpB,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,IAAI,IAAC,QAAQ,yGAEP,GACH,CACP,EAEA,KAAK,KAAK,WAAW,IAAI,CACxB,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,EAAC,SAAS,EAAE,CAAC,aACtC,KAAC,aAAa,IAAC,KAAK,EAAC,iBAAiB,GAAG,EACxC,SAAS,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,GAAG,EAAE,EAAE;wBAC3C,MAAM,QAAQ,GAAG,GAAG,KAAK,SAAS,CAAC,MAAM,CAAC;wBAC1C,MAAM,KAAK,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG,CAAC;wBACpD,MAAM,OAAO,GAAG,cAAc,CAAC,SAAS,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;wBAC9D,MAAM,IAAI,GAAG,GAAG,SAAS,CAAC,YAAY,IAAI,SAAS,CAAC,YAAY,KAAK,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC;wBAClG,OAAO,CACL,MAAC,GAAG,eACF,KAAC,IAAI,IAAC,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,YACvC,QAAQ,CAAC,CAAC,CAAC,GAAG,aAAa,GAAG,CAAC,CAAC,CAAC,IAAI,GACjC,EACP,KAAC,IAAI,IAAC,KAAK,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,SAAS,YAAG,GAAG,KAAK,GAAG,GAAQ,EAC1E,KAAC,GAAG,IAAC,KAAK,EAAE,EAAE,YACZ,KAAC,YAAY,IAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,EAAE,GAAI,GACvD,KAPE,SAAS,CAAC,UAAU,CAQxB,CACP,CAAC;oBACJ,CAAC,CAAC,EACF,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,IAAI,IAAC,QAAQ,kBACX,+BAA+B,aAAa,QAAQ,aAAa,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,eAAe,GAC7F,GACH,IACF,CACP,IACG,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import type { MultiProjectScanResult } from "../../types.js";
3
+ export interface McpsTabProps {
4
+ result: MultiProjectScanResult;
5
+ }
6
+ export declare function McpsTab({ result }: McpsTabProps): React.ReactElement;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Fragment } from "react";
3
+ import { Box, Text } from "ink";
4
+ import { EmptyStateWhisper } from "../components/EmptyStateWhisper.js";
5
+ import { SectionHeader } from "../components/SectionHeader.js";
6
+ import { DotLeaderRow } from "../components/DotLeaderRow.js";
7
+ import { StatusPill } from "../components/StatusPill.js";
8
+ import { EMPTY_STATE_WHISPERS } from "../messages.js";
9
+ import { relativizeHome } from "../../utils/paths.js";
10
+ import { aggregateMcps, formatCapabilityTag } from "../util/mcp-grouping.js";
11
+ const ROW_WIDTH = 60;
12
+ export function McpsTab({ result }) {
13
+ const groups = aggregateMcps(result);
14
+ if (groups.length === 0) {
15
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { label: "MCPS" }), _jsx(Text, { dimColor: true, children: "No MCP servers configured." }), _jsx(Box, { marginTop: 1, children: _jsx(EmptyStateWhisper, { text: EMPTY_STATE_WHISPERS.noMcps }) })] }));
16
+ }
17
+ const totalConfigs = groups.reduce((n, g) => n + g.configurations.length, 0);
18
+ const toolSet = new Set();
19
+ for (const group of groups) {
20
+ for (const config of group.configurations)
21
+ toolSet.add(config.toolId);
22
+ }
23
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { label: "MCPS" }), _jsxs(Text, { children: [groups.length, " unique \u00B7 ", totalConfigs, " configurations \u00B7 ", toolSet.size, " tools"] }), groups.map((group) => (_jsx(McpGroupBlock, { group: group, homeDir: result.homeDir }, group.name.toLowerCase())))] }));
24
+ }
25
+ function McpGroupBlock({ group, homeDir }) {
26
+ const tag = formatCapabilityTag(group);
27
+ const uncatalogued = tag === "(uncatalogued)";
28
+ return (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { bold: true, children: group.name }), _jsx(Text, { children: " " }), uncatalogued ? (_jsx(Text, { dimColor: true, italic: true, children: "(uncatalogued)" })) : (_jsx(StatusPill, { label: group.capabilityCategories.join(", "), accessLevel: group.accessLevel }))] }), group.configurations.map((config, idx) => (_jsx(Fragment, { children: _jsx(DotLeaderRow, { label: ` ${config.toolId}`, metadata: relativizeHome(config.sourcePath, homeDir), width: ROW_WIDTH }) }, `${idx}:${config.toolId}:${config.sourcePath}`))), group.duplicatedAcrossTools && (_jsxs(Text, { color: "red", children: [" ", "\u26A0 Configured in ", new Set(group.configurations.map((c) => c.toolId)).size, " tools"] })), group.secretEnvKeys.length > 0 && (_jsxs(Text, { color: "red", children: [" ", "\u26A0 Secret-bearing env keys: ", group.secretEnvKeys.join(", ")] }))] }));
29
+ }
30
+ //# sourceMappingURL=McpsTab.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"McpsTab.js","sourceRoot":"","sources":["../../../src/tui/screens/McpsTab.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import React from "react";
2
+ import type { MultiProjectScanResult } from "../../types.js";
3
+ export interface OverviewProps {
4
+ result: MultiProjectScanResult;
5
+ }
6
+ export declare function Overview({ result }: OverviewProps): React.ReactElement;
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { SectionHeader } from "../components/SectionHeader.js";
4
+ import { DotLeaderRow } from "../components/DotLeaderRow.js";
5
+ const ROW_WIDTH = 60;
6
+ export function Overview({ result }) {
7
+ const totals = computeOverviewTotals(result);
8
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { label: "OVERVIEW" }), _jsxs(Text, { children: [totals.detectedCount, " detected \u00B7 ", totals.skills, " skills \u00B7 ", totals.mcpConfigs, " MCP configs (", totals.uniqueMcps, " unique) \u00B7 ", totals.findings, " findings"] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(SectionHeader, { label: "PER TOOL" }), result.userScope.tools.map((tool) => (_jsx(DotLeaderRow, { label: tool.name, metadata: formatToolMetadata(tool, result), width: ROW_WIDTH }, tool.id)))] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(SectionHeader, { label: "CROSS TOOL" }), _jsxs(Text, { children: [totals.uniqueMcps, " unique MCPs \u00B7 ", totals.duplicatedMcps, " duplicated cross-tool"] }), _jsxs(Text, { children: [totals.findings, " access findings \u00B7 ", totals.warnings, " scanner warnings"] })] })] }));
9
+ }
10
+ function formatToolMetadata(tool, result) {
11
+ if (!tool.detected)
12
+ return "not detected";
13
+ const userScopeSkills = tool.skills.length;
14
+ const projectSkills = result.projects.reduce((sum, project) => {
15
+ const projectTool = project.scan.tools.find((t) => t.id === tool.id);
16
+ return sum + (projectTool?.skills.length ?? 0);
17
+ }, 0);
18
+ const mcpCount = tool.stats.mcpServers;
19
+ if (userScopeSkills === 0 && projectSkills === 0 && mcpCount === 0) {
20
+ return "detected, no skills";
21
+ }
22
+ return `${userScopeSkills} user · ${projectSkills} project · ${mcpCount} MCPs`;
23
+ }
24
+ function computeOverviewTotals(result) {
25
+ const { userScope, projects, warnings } = result;
26
+ const userTools = userScope.tools;
27
+ const detectedCount = userTools.filter((t) => t.detected).length;
28
+ const userScopeSkills = userTools.reduce((n, t) => n + t.skills.length, 0);
29
+ const projectSkills = projects.reduce((n, p) => n + p.scan.tools.reduce((m, t) => m + t.skills.length, 0), 0);
30
+ const mcpNames = new Set();
31
+ const mcpAppearances = new Map();
32
+ let mcpConfigs = 0;
33
+ for (const tool of userTools) {
34
+ for (const skill of tool.skills) {
35
+ if (skill.kind !== "mcp_server")
36
+ continue;
37
+ const key = skill.name.toLowerCase();
38
+ mcpNames.add(key);
39
+ mcpConfigs += 1;
40
+ const set = mcpAppearances.get(key) ?? new Set();
41
+ set.add(tool.id);
42
+ mcpAppearances.set(key, set);
43
+ }
44
+ }
45
+ for (const project of projects) {
46
+ for (const tool of project.scan.tools) {
47
+ for (const skill of tool.skills) {
48
+ if (skill.kind !== "mcp_server")
49
+ continue;
50
+ const key = skill.name.toLowerCase();
51
+ mcpNames.add(key);
52
+ mcpConfigs += 1;
53
+ const set = mcpAppearances.get(key) ?? new Set();
54
+ set.add(tool.id);
55
+ mcpAppearances.set(key, set);
56
+ }
57
+ }
58
+ }
59
+ let duplicated = 0;
60
+ for (const set of mcpAppearances.values()) {
61
+ if (set.size >= 2)
62
+ duplicated += 1;
63
+ }
64
+ const findings = userScope.findings.length;
65
+ return {
66
+ detectedCount,
67
+ skills: userScopeSkills + projectSkills,
68
+ mcpConfigs,
69
+ uniqueMcps: mcpNames.size,
70
+ duplicatedMcps: duplicated,
71
+ findings,
72
+ warnings: warnings.length
73
+ };
74
+ }
75
+ //# sourceMappingURL=Overview.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Overview.js","sourceRoot":"","sources":["../../../src/tui/screens/Overview.tsx"],"names":[],"mappings":"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"}
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { MultiProjectScanResult, ToolId } from "../../types.js";
3
+ export interface ProjectDrillInProps {
4
+ toolId: ToolId;
5
+ projectPath: string;
6
+ result: MultiProjectScanResult;
7
+ /** Cursor into the grouped skill list. */
8
+ cursor?: number;
9
+ }
10
+ export declare function ProjectDrillIn({ toolId, projectPath, result, cursor }: ProjectDrillInProps): React.ReactElement;
@@ -0,0 +1,16 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from "ink";
3
+ import { Breadcrumb } from "../components/Breadcrumb.js";
4
+ import { EmptyStateWhisper } from "../components/EmptyStateWhisper.js";
5
+ import { SkillViewport } from "../components/SkillViewport.js";
6
+ import { EMPTY_STATE_WHISPERS } from "../messages.js";
7
+ export function ProjectDrillIn({ toolId, projectPath, result, cursor = 0 }) {
8
+ const project = result.projects.find((p) => p.projectPath === projectPath);
9
+ if (!project) {
10
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Breadcrumb, { parts: ["ankui", toolId, "?"] }), _jsx(Text, { dimColor: true, children: "Project not found." })] }));
11
+ }
12
+ const projectTool = project.scan.tools.find((t) => t.id === toolId);
13
+ const skills = projectTool?.skills ?? [];
14
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Breadcrumb, { parts: ["ankui", toolId, project.displayPath] }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: project.displayPath }), _jsx(Text, { dimColor: true, children: project.projectPath })] }), skills.length === 0 ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "No skills for this tool in this project." }), _jsx(Box, { marginTop: 1, children: _jsx(EmptyStateWhisper, { text: EMPTY_STATE_WHISPERS.noProjectSkills }) })] })) : (_jsx(Box, { marginTop: 1, children: _jsx(SkillViewport, { skills: skills, cursor: cursor }) }))] }));
15
+ }
16
+ //# sourceMappingURL=ProjectDrillIn.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ProjectDrillIn.js","sourceRoot":"","sources":["../../../src/tui/screens/ProjectDrillIn.tsx"],"names":[],"mappings":";AACA,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAGhC,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AAUtD,MAAM,UAAU,cAAc,CAAC,EAC7B,MAAM,EACN,WAAW,EACX,MAAM,EACN,MAAM,GAAG,CAAC,EACU;IACpB,MAAM,OAAO,GAAG,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,WAAW,KAAK,WAAW,CAAC,CAAC;IAC3E,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,UAAU,IAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,GAAG,CAAC,GAAI,EAC7C,KAAC,IAAI,IAAC,QAAQ,yCAA0B,IACpC,CACP,CAAC;IACJ,CAAC;IAED,MAAM,WAAW,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,EAAE,KAAK,MAAM,CAAC,CAAC;IACpE,MAAM,MAAM,GAAG,WAAW,EAAE,MAAM,IAAI,EAAE,CAAC;IAEzC,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,UAAU,IAAC,KAAK,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,WAAW,CAAC,GAAI,EAE7D,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACvC,KAAC,IAAI,cAAE,OAAO,CAAC,WAAW,GAAQ,EAClC,KAAC,IAAI,IAAC,QAAQ,kBAAE,OAAO,CAAC,WAAW,GAAQ,IACvC,EAEL,MAAM,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,CACrB,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACvC,KAAC,IAAI,IAAC,QAAQ,+DAAgD,EAC9D,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,iBAAiB,IAAC,IAAI,EAAE,oBAAoB,CAAC,eAAe,GAAI,GAC7D,IACF,CACP,CAAC,CAAC,CAAC,CACF,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,aAAa,IAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAI,GAC7C,CACP,IACG,CACP,CAAC;AACJ,CAAC"}
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import type { MultiProjectScanResult } from "../../types.js";
3
+ export interface SettingsProps {
4
+ result: MultiProjectScanResult;
5
+ onConfigChange: (devRoots: string[]) => Promise<void>;
6
+ onRescan: () => void;
7
+ }
8
+ export declare function Settings({ result, onConfigChange, onRescan }: SettingsProps): React.ReactElement;
@@ -0,0 +1,71 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useReducer, useState } from "react";
3
+ import { Box, Text, useInput } from "ink";
4
+ import { mergeDevRoots } from "../../config/ankui-config.js";
5
+ import { relativizeHome } from "../../utils/paths.js";
6
+ import { SectionHeader } from "../components/SectionHeader.js";
7
+ import { DotLeaderRow } from "../components/DotLeaderRow.js";
8
+ import { TextInput } from "../components/TextInput.js";
9
+ import { useKeys } from "../input/use-keys.js";
10
+ import { INITIAL_SETTINGS_STATE, settingsReducer } from "../state/settings-state.js";
11
+ import { ACTIVE_PREFIX } from "../theme/icons.js";
12
+ import { ACCENT } from "../theme/colors.js";
13
+ import { formatLastScan } from "../util/scan-history.js";
14
+ export function Settings({ result, onConfigChange, onRescan }) {
15
+ const [state, dispatch] = useReducer(settingsReducer, INITIAL_SETTINGS_STATE);
16
+ const [addBuffer, setAddBuffer] = useState("");
17
+ const devRoots = result.devRoots;
18
+ const rootCount = devRoots.length;
19
+ useKeys({
20
+ onArrowUp: () => {
21
+ if (!state.addMode) {
22
+ dispatch({ type: "moveCursor", direction: "up", rootCount });
23
+ }
24
+ },
25
+ onArrowDown: () => {
26
+ if (!state.addMode) {
27
+ dispatch({ type: "moveCursor", direction: "down", rootCount });
28
+ }
29
+ }
30
+ });
31
+ useInput((input, key) => {
32
+ if (state.addMode) {
33
+ // TextInput owns key handling while in add-mode. We listen only for Esc.
34
+ if (key.escape)
35
+ dispatch({ type: "cancelAddMode" });
36
+ return;
37
+ }
38
+ if (input === "a") {
39
+ setAddBuffer("");
40
+ dispatch({ type: "enterAddMode" });
41
+ return;
42
+ }
43
+ if (input === "d" && rootCount > 0) {
44
+ const next = devRoots.filter((_, idx) => idx !== state.cursor);
45
+ dispatch({ type: "removeAtCursor", newRootCount: next.length });
46
+ void onConfigChange(next);
47
+ return;
48
+ }
49
+ if (input === "r") {
50
+ onRescan();
51
+ }
52
+ });
53
+ const totalSkills = result.totals.userScopeSkills + result.totals.skillsAcrossProjects;
54
+ const lastScanLine = formatLastScan({
55
+ scannedAt: result.scannedAt,
56
+ totalSkills
57
+ });
58
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionHeader, { label: "DEV ROOTS" }), rootCount === 0 ? (_jsx(Text, { dimColor: true, children: "No dev roots registered. Press [a] to add one, or [r] to scan filesystem." })) : (devRoots.map((root, idx) => {
59
+ const isActive = idx === state.cursor && !state.addMode;
60
+ const display = relativizeHome(root, result.homeDir);
61
+ return (_jsxs(Box, { children: [_jsx(Text, { color: isActive ? ACCENT : undefined, children: isActive ? `${ACTIVE_PREFIX} ` : " " }), _jsx(Box, { width: 60, children: _jsx(DotLeaderRow, { label: display, metadata: "", width: 60, active: isActive }) })] }, root));
62
+ })), state.addMode && (_jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "add root " }), _jsx(TextInput, { value: addBuffer, onChange: setAddBuffer, placeholder: "/Users/.../code", onSubmit: async (value) => {
63
+ const trimmed = value.trim();
64
+ dispatch({ type: "cancelAddMode" });
65
+ if (!trimmed)
66
+ return;
67
+ const next = mergeDevRoots(devRoots, [trimmed]);
68
+ await onConfigChange(next);
69
+ } })] })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "[ a ] add root [ d ] remove selected [ r ] re-scan filesystem" }) }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(SectionHeader, { label: "SCAN HISTORY" }), _jsx(Text, { children: `last scan · ${lastScanLine}` })] })] }));
70
+ }
71
+ //# sourceMappingURL=Settings.js.map