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.
- package/LICENSE +21 -0
- package/README.md +369 -0
- package/dist/cli.d.ts +2 -0
- package/dist/cli.js +192 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/access.d.ts +6 -0
- package/dist/commands/access.js +12 -0
- package/dist/commands/access.js.map +1 -0
- package/dist/commands/caps.d.ts +6 -0
- package/dist/commands/caps.js +12 -0
- package/dist/commands/caps.js.map +1 -0
- package/dist/commands/discover.d.ts +10 -0
- package/dist/commands/discover.js +120 -0
- package/dist/commands/discover.js.map +1 -0
- package/dist/commands/doctor.d.ts +6 -0
- package/dist/commands/doctor.js +12 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/launch-tui.d.ts +15 -0
- package/dist/commands/launch-tui.js +53 -0
- package/dist/commands/launch-tui.js.map +1 -0
- package/dist/commands/list.d.ts +11 -0
- package/dist/commands/list.js +51 -0
- package/dist/commands/list.js.map +1 -0
- package/dist/commands/mcp.d.ts +6 -0
- package/dist/commands/mcp.js +12 -0
- package/dist/commands/mcp.js.map +1 -0
- package/dist/commands/scan-all.d.ts +15 -0
- package/dist/commands/scan-all.js +32 -0
- package/dist/commands/scan-all.js.map +1 -0
- package/dist/commands/show.d.ts +7 -0
- package/dist/commands/show.js +12 -0
- package/dist/commands/show.js.map +1 -0
- package/dist/commands/watch.d.ts +32 -0
- package/dist/commands/watch.js +205 -0
- package/dist/commands/watch.js.map +1 -0
- package/dist/config/ankui-config.d.ts +13 -0
- package/dist/config/ankui-config.js +123 -0
- package/dist/config/ankui-config.js.map +1 -0
- package/dist/scanner/access-review.d.ts +2 -0
- package/dist/scanner/access-review.js +183 -0
- package/dist/scanner/access-review.js.map +1 -0
- package/dist/scanner/adapters/claude.d.ts +2 -0
- package/dist/scanner/adapters/claude.js +298 -0
- package/dist/scanner/adapters/claude.js.map +1 -0
- package/dist/scanner/adapters/codex.d.ts +2 -0
- package/dist/scanner/adapters/codex.js +152 -0
- package/dist/scanner/adapters/codex.js.map +1 -0
- package/dist/scanner/adapters/cursor.d.ts +2 -0
- package/dist/scanner/adapters/cursor.js +114 -0
- package/dist/scanner/adapters/cursor.js.map +1 -0
- package/dist/scanner/adapters/gemini.d.ts +2 -0
- package/dist/scanner/adapters/gemini.js +191 -0
- package/dist/scanner/adapters/gemini.js.map +1 -0
- package/dist/scanner/adapters/index.d.ts +35 -0
- package/dist/scanner/adapters/index.js +85 -0
- package/dist/scanner/adapters/index.js.map +1 -0
- package/dist/scanner/adapters/opencode.d.ts +2 -0
- package/dist/scanner/adapters/opencode.js +231 -0
- package/dist/scanner/adapters/opencode.js.map +1 -0
- package/dist/scanner/adapters/shared.d.ts +57 -0
- package/dist/scanner/adapters/shared.js +239 -0
- package/dist/scanner/adapters/shared.js.map +1 -0
- package/dist/scanner/adapters/skills-sh.d.ts +2 -0
- package/dist/scanner/adapters/skills-sh.js +71 -0
- package/dist/scanner/adapters/skills-sh.js.map +1 -0
- package/dist/scanner/capability-map.d.ts +8 -0
- package/dist/scanner/capability-map.js +57 -0
- package/dist/scanner/capability-map.js.map +1 -0
- package/dist/scanner/discovery.d.ts +31 -0
- package/dist/scanner/discovery.js +320 -0
- package/dist/scanner/discovery.js.map +1 -0
- package/dist/scanner/filesystem-crawler.d.ts +32 -0
- package/dist/scanner/filesystem-crawler.js +210 -0
- package/dist/scanner/filesystem-crawler.js.map +1 -0
- package/dist/scanner/index.d.ts +8 -0
- package/dist/scanner/index.js +120 -0
- package/dist/scanner/index.js.map +1 -0
- package/dist/scanner/multi-project.d.ts +31 -0
- package/dist/scanner/multi-project.js +227 -0
- package/dist/scanner/multi-project.js.map +1 -0
- package/dist/scanner/parallel.d.ts +4 -0
- package/dist/scanner/parallel.js +41 -0
- package/dist/scanner/parallel.js.map +1 -0
- package/dist/scanner/parsing.d.ts +9 -0
- package/dist/scanner/parsing.js +135 -0
- package/dist/scanner/parsing.js.map +1 -0
- package/dist/scanner/paths.d.ts +8 -0
- package/dist/scanner/paths.js +39 -0
- package/dist/scanner/paths.js.map +1 -0
- package/dist/scanner/preview.d.ts +12 -0
- package/dist/scanner/preview.js +29 -0
- package/dist/scanner/preview.js.map +1 -0
- package/dist/scanner/project-discovery.d.ts +11 -0
- package/dist/scanner/project-discovery.js +35 -0
- package/dist/scanner/project-discovery.js.map +1 -0
- package/dist/scanner/ripgrep.d.ts +11 -0
- package/dist/scanner/ripgrep.js +61 -0
- package/dist/scanner/ripgrep.js.map +1 -0
- package/dist/scanner/safety.d.ts +37 -0
- package/dist/scanner/safety.js +330 -0
- package/dist/scanner/safety.js.map +1 -0
- package/dist/scanner/skill-naming.d.ts +5 -0
- package/dist/scanner/skill-naming.js +53 -0
- package/dist/scanner/skill-naming.js.map +1 -0
- package/dist/scanner/watcher.d.ts +15 -0
- package/dist/scanner/watcher.js +71 -0
- package/dist/scanner/watcher.js.map +1 -0
- package/dist/tui/App.d.ts +33 -0
- package/dist/tui/App.js +201 -0
- package/dist/tui/App.js.map +1 -0
- package/dist/tui/components/Breadcrumb.d.ts +10 -0
- package/dist/tui/components/Breadcrumb.js +11 -0
- package/dist/tui/components/Breadcrumb.js.map +1 -0
- package/dist/tui/components/DisclosureRow.d.ts +17 -0
- package/dist/tui/components/DisclosureRow.js +13 -0
- package/dist/tui/components/DisclosureRow.js.map +1 -0
- package/dist/tui/components/DotLeaderRow.d.ts +17 -0
- package/dist/tui/components/DotLeaderRow.js +34 -0
- package/dist/tui/components/DotLeaderRow.js.map +1 -0
- package/dist/tui/components/EmptyStateWhisper.d.ts +5 -0
- package/dist/tui/components/EmptyStateWhisper.js +6 -0
- package/dist/tui/components/EmptyStateWhisper.js.map +1 -0
- package/dist/tui/components/Frame.d.ts +15 -0
- package/dist/tui/components/Frame.js +21 -0
- package/dist/tui/components/Frame.js.map +1 -0
- package/dist/tui/components/IdleWhisper.d.ts +11 -0
- package/dist/tui/components/IdleWhisper.js +12 -0
- package/dist/tui/components/IdleWhisper.js.map +1 -0
- package/dist/tui/components/KeyHint.d.ts +13 -0
- package/dist/tui/components/KeyHint.js +12 -0
- package/dist/tui/components/KeyHint.js.map +1 -0
- package/dist/tui/components/LoadingSplash.d.ts +12 -0
- package/dist/tui/components/LoadingSplash.js +55 -0
- package/dist/tui/components/LoadingSplash.js.map +1 -0
- package/dist/tui/components/ProgressBar.d.ts +16 -0
- package/dist/tui/components/ProgressBar.js +24 -0
- package/dist/tui/components/ProgressBar.js.map +1 -0
- package/dist/tui/components/SearchBox.d.ts +5 -0
- package/dist/tui/components/SearchBox.js +6 -0
- package/dist/tui/components/SearchBox.js.map +1 -0
- package/dist/tui/components/SectionHeader.d.ts +12 -0
- package/dist/tui/components/SectionHeader.js +14 -0
- package/dist/tui/components/SectionHeader.js.map +1 -0
- package/dist/tui/components/SkillViewport.d.ts +8 -0
- package/dist/tui/components/SkillViewport.js +42 -0
- package/dist/tui/components/SkillViewport.js.map +1 -0
- package/dist/tui/components/Spinner.d.ts +11 -0
- package/dist/tui/components/Spinner.js +13 -0
- package/dist/tui/components/Spinner.js.map +1 -0
- package/dist/tui/components/StatusPill.d.ts +13 -0
- package/dist/tui/components/StatusPill.js +15 -0
- package/dist/tui/components/StatusPill.js.map +1 -0
- package/dist/tui/components/TabBar.d.ts +20 -0
- package/dist/tui/components/TabBar.js +26 -0
- package/dist/tui/components/TabBar.js.map +1 -0
- package/dist/tui/components/TextInput.d.ts +13 -0
- package/dist/tui/components/TextInput.js +34 -0
- package/dist/tui/components/TextInput.js.map +1 -0
- package/dist/tui/hooks/use-idle-whisper.d.ts +17 -0
- package/dist/tui/hooks/use-idle-whisper.js +66 -0
- package/dist/tui/hooks/use-idle-whisper.js.map +1 -0
- package/dist/tui/hooks/use-rotating-message.d.ts +20 -0
- package/dist/tui/hooks/use-rotating-message.js +34 -0
- package/dist/tui/hooks/use-rotating-message.js.map +1 -0
- package/dist/tui/input/use-keys.d.ts +16 -0
- package/dist/tui/input/use-keys.js +32 -0
- package/dist/tui/input/use-keys.js.map +1 -0
- package/dist/tui/messages.d.ts +18 -0
- package/dist/tui/messages.js +59 -0
- package/dist/tui/messages.js.map +1 -0
- package/dist/tui/render.d.ts +12 -0
- package/dist/tui/render.js +112 -0
- package/dist/tui/render.js.map +1 -0
- package/dist/tui/screens/AccessTab.d.ts +6 -0
- package/dist/tui/screens/AccessTab.js +26 -0
- package/dist/tui/screens/AccessTab.js.map +1 -0
- package/dist/tui/screens/DoctorTab.d.ts +6 -0
- package/dist/tui/screens/DoctorTab.js +24 -0
- package/dist/tui/screens/DoctorTab.js.map +1 -0
- package/dist/tui/screens/FirstRunScan.d.ts +11 -0
- package/dist/tui/screens/FirstRunScan.js +128 -0
- package/dist/tui/screens/FirstRunScan.js.map +1 -0
- package/dist/tui/screens/McpsTab.d.ts +6 -0
- package/dist/tui/screens/McpsTab.js +30 -0
- package/dist/tui/screens/McpsTab.js.map +1 -0
- package/dist/tui/screens/Overview.d.ts +6 -0
- package/dist/tui/screens/Overview.js +75 -0
- package/dist/tui/screens/Overview.js.map +1 -0
- package/dist/tui/screens/ProjectDrillIn.d.ts +10 -0
- package/dist/tui/screens/ProjectDrillIn.js +16 -0
- package/dist/tui/screens/ProjectDrillIn.js.map +1 -0
- package/dist/tui/screens/Settings.d.ts +8 -0
- package/dist/tui/screens/Settings.js +71 -0
- package/dist/tui/screens/Settings.js.map +1 -0
- package/dist/tui/screens/ToolTab.d.ts +9 -0
- package/dist/tui/screens/ToolTab.js +38 -0
- package/dist/tui/screens/ToolTab.js.map +1 -0
- package/dist/tui/screens/UserScopeDrillIn.d.ts +13 -0
- package/dist/tui/screens/UserScopeDrillIn.js +15 -0
- package/dist/tui/screens/UserScopeDrillIn.js.map +1 -0
- package/dist/tui/state/navigation.d.ts +1 -0
- package/dist/tui/state/navigation.js +11 -0
- package/dist/tui/state/navigation.js.map +1 -0
- package/dist/tui/state/settings-state.d.ts +22 -0
- package/dist/tui/state/settings-state.js +30 -0
- package/dist/tui/state/settings-state.js.map +1 -0
- package/dist/tui/state/tui-state.d.ts +57 -0
- package/dist/tui/state/tui-state.js +86 -0
- package/dist/tui/state/tui-state.js.map +1 -0
- package/dist/tui/theme/borders.d.ts +20 -0
- package/dist/tui/theme/borders.js +25 -0
- package/dist/tui/theme/borders.js.map +1 -0
- package/dist/tui/theme/colors.d.ts +14 -0
- package/dist/tui/theme/colors.js +18 -0
- package/dist/tui/theme/colors.js.map +1 -0
- package/dist/tui/theme/icons.d.ts +30 -0
- package/dist/tui/theme/icons.js +51 -0
- package/dist/tui/theme/icons.js.map +1 -0
- package/dist/tui/util/doctor-grouping.d.ts +27 -0
- package/dist/tui/util/doctor-grouping.js +67 -0
- package/dist/tui/util/doctor-grouping.js.map +1 -0
- package/dist/tui/util/finding-grouping.d.ts +24 -0
- package/dist/tui/util/finding-grouping.js +43 -0
- package/dist/tui/util/finding-grouping.js.map +1 -0
- package/dist/tui/util/mcp-grouping.d.ts +27 -0
- package/dist/tui/util/mcp-grouping.js +96 -0
- package/dist/tui/util/mcp-grouping.js.map +1 -0
- package/dist/tui/util/scan-history.d.ts +15 -0
- package/dist/tui/util/scan-history.js +43 -0
- package/dist/tui/util/scan-history.js.map +1 -0
- package/dist/tui/util/skill-filter.d.ts +2 -0
- package/dist/tui/util/skill-filter.js +7 -0
- package/dist/tui/util/skill-filter.js.map +1 -0
- package/dist/tui/util/skill-grouping.d.ts +3 -0
- package/dist/tui/util/skill-grouping.js +31 -0
- package/dist/tui/util/skill-grouping.js.map +1 -0
- package/dist/types.d.ts +151 -0
- package/dist/types.js +143 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/errors.d.ts +1 -0
- package/dist/utils/errors.js +7 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/format-access.d.ts +3 -0
- package/dist/utils/format-access.js +94 -0
- package/dist/utils/format-access.js.map +1 -0
- package/dist/utils/format-caps.d.ts +3 -0
- package/dist/utils/format-caps.js +139 -0
- package/dist/utils/format-caps.js.map +1 -0
- package/dist/utils/format-doctor.d.ts +3 -0
- package/dist/utils/format-doctor.js +105 -0
- package/dist/utils/format-doctor.js.map +1 -0
- package/dist/utils/format-list.d.ts +7 -0
- package/dist/utils/format-list.js +112 -0
- package/dist/utils/format-list.js.map +1 -0
- package/dist/utils/format-mcp.d.ts +3 -0
- package/dist/utils/format-mcp.js +130 -0
- package/dist/utils/format-mcp.js.map +1 -0
- package/dist/utils/format-multi-project.d.ts +3 -0
- package/dist/utils/format-multi-project.js +100 -0
- package/dist/utils/format-multi-project.js.map +1 -0
- package/dist/utils/format-show.d.ts +6 -0
- package/dist/utils/format-show.js +143 -0
- package/dist/utils/format-show.js.map +1 -0
- package/dist/utils/format.d.ts +3 -0
- package/dist/utils/format.js +53 -0
- package/dist/utils/format.js.map +1 -0
- package/dist/utils/paths.d.ts +1 -0
- package/dist/utils/paths.js +10 -0
- package/dist/utils/paths.js.map +1 -0
- 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,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":";AAAA,OAAc,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACxC,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,KAAK,CAAC;AAGhC,OAAO,EAAE,iBAAiB,EAAE,MAAM,oCAAoC,CAAC;AACvE,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,oBAAoB,EAAE,MAAM,gBAAgB,CAAC;AACtD,OAAO,EAAE,cAAc,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EACL,iBAAiB,EAElB,MAAM,6BAA6B,CAAC;AAMrC,MAAM,UAAU,SAAS,CAAC,EAAE,MAAM,EAAkB;IAClD,MAAM,QAAQ,GAAG,iBAAiB,CAAC,MAAM,CAAC,CAAC;IAE3C,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1B,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,aAAa,IAAC,KAAK,EAAC,QAAQ,GAAG,EAChC,KAAC,IAAI,IAAC,QAAQ,mCAAoB,EAClC,KAAC,GAAG,IAAC,SAAS,EAAE,CAAC,YACf,KAAC,iBAAiB,IAAC,IAAI,EAAE,oBAAoB,CAAC,UAAU,GAAI,GACxD,IACF,CACP,CAAC;IACJ,CAAC;IAED,MAAM,KAAK,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IAClE,MAAM,SAAS,GAAG,QAAQ;SACvB,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,MAAM,IAAI,CAAC,CAAC,QAAQ,EAAE,CAAC;SAChD,IAAI,CAAC,KAAK,CAAC,CAAC;IAEf,OAAO,CACL,MAAC,GAAG,IAAC,aAAa,EAAC,QAAQ,aACzB,KAAC,aAAa,IAAC,KAAK,EAAC,QAAQ,GAAG,EAChC,MAAC,IAAI,eACF,KAAK,iBAAa,SAAS,SACvB,EAEN,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CACzB,KAAC,mBAAmB,IAElB,OAAO,EAAE,OAAO,EAChB,OAAO,EAAE,MAAM,CAAC,OAAO,IAFlB,OAAO,CAAC,QAAQ,CAGrB,CACH,CAAC,IACE,CACP,CAAC;AACJ,CAAC;AAOD,SAAS,mBAAmB,CAAC,EAC3B,OAAO,EACP,OAAO,EACkB;IACzB,OAAO,CACL,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACvC,KAAC,aAAa,IAAC,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,WAAW,EAAE,GAAI,EACpD,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CACjC,KAAC,YAAY,IAAkB,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,IAA9C,OAAO,CAAC,EAAE,CAAwC,CACtE,CAAC,IACE,CACP,CAAC;AACJ,CAAC;AAOD,SAAS,YAAY,CAAC,EAAE,OAAO,EAAE,OAAO,EAAqB;IAC3D,OAAO,CACL,MAAC,GAAG,IAAC,SAAS,EAAE,CAAC,EAAE,aAAa,EAAC,QAAQ,aACvC,KAAC,IAAI,cAAE,OAAO,OAAO,CAAC,KAAK,EAAE,GAAQ,EACrC,KAAC,IAAI,IAAC,QAAQ,kBAAE,cAAc,OAAO,CAAC,KAAK,aAAa,OAAO,CAAC,OAAO,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,GAAQ,EAC5F,KAAC,IAAI,IAAC,QAAQ,kBACX,OAAO,CAAC,WAAW,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,cAAc,GAC7D,EACN,OAAO,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,EAAE,EAAE,CAAC,CACvC,KAAC,QAAQ,cACP,KAAC,IAAI,IAAC,QAAQ,kBAAE,SAAS,cAAc,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,GAAQ,IADzD,UAAU,CAEd,CACZ,CAAC,EACF,KAAC,IAAI,cAAE,uBAAuB,OAAO,CAAC,cAAc,EAAE,GAAQ,IAC1D,CACP,CAAC;AACJ,CAAC"}
|
|
@@ -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":"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"}
|
|
@@ -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,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
|