@brainervirus/workit-core 2.9.0 → 4.0.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.
@@ -1,115 +0,0 @@
1
- // Host presence detection uses actual CLI executables or supported desktop app
2
- // locations; configuration directories alone never count as installation.
3
- // Existing registrations reuse uninstall planning so the two paths agree.
4
- import { readdirSync } from "node:fs";
5
- import os from "node:os";
6
- import path from "node:path";
7
- import { commandOnPath } from "./doctor";
8
- import { planUninstall, type UninstallPaths } from "./uninstall";
9
- import {
10
- findHostExecutable,
11
- installedHostApp,
12
- isClaudeWorkitInstalled,
13
- isCodexWorkitInstalled,
14
- isPiWorkitInstalled,
15
- type HostId,
16
- } from "./host-install";
17
- export type { HostId } from "./host-install";
18
-
19
- export type HostDetection = {
20
- /** Host executable or supported desktop application is present. */
21
- detected: boolean;
22
- /** A Workit registration is present for this host. */
23
- configured: boolean;
24
- };
25
-
26
- export type DetectHostsOptions = UninstallPaths;
27
-
28
- const HOSTS: HostId[] = ["opencode", "cursor", "codex", "pi", "claude-code"];
29
-
30
- /** Hosts the setup wizard configures through their native install paths. */
31
- export const WIZARD_HOSTS: HostId[] = [...HOSTS];
32
-
33
- /** Detected hosts the wizard can preselect (presence ∩ wizard-managed). */
34
- export function preselectedPlatforms(detection: Record<HostId, HostDetection>): string[] {
35
- return WIZARD_HOSTS.filter((host) => detection[host].detected);
36
- }
37
-
38
- export function emptyDetection(): Record<HostId, HostDetection> {
39
- return {
40
- opencode: { detected: false, configured: false },
41
- cursor: { detected: false, configured: false },
42
- codex: { detected: false, configured: false },
43
- pi: { detected: false, configured: false },
44
- "claude-code": { detected: false, configured: false },
45
- };
46
- }
47
-
48
- export function detectHosts(options: DetectHostsOptions = {}): Record<HostId, HostDetection> {
49
- const env = options.env ?? process.env;
50
- // Same chain as uninstall path resolution: explicit home > env.HOME > homedir.
51
- const home = options.home ?? env.HOME ?? os.homedir();
52
- // Pure reader: classifies the installed state, never writes.
53
- const plan = planUninstall({ ...options, home, env });
54
- const configuredByHost = new Map(plan.hosts.map((h) => [h.host, h.installed]));
55
- const found = emptyDetection();
56
- for (const host of HOSTS) {
57
- // Claude Code ships as the `claude` CLI (the desktop app embeds it).
58
- const executable = host === "claude-code" ? "claude" : host;
59
- const detected =
60
- findHostExecutable(executable, { home, env }) !== null || installedHostApp(host, home, env);
61
- found[host] = {
62
- detected,
63
- configured:
64
- host === "codex"
65
- ? isCodexWorkitInstalled(home, env) || (configuredByHost.get(host) ?? false)
66
- : host === "pi"
67
- ? isPiWorkitInstalled(home, env) || (configuredByHost.get(host) ?? false)
68
- : host === "claude-code"
69
- ? isClaudeWorkitInstalled(home, env)
70
- : (configuredByHost.get(host) ?? false),
71
- };
72
- }
73
- return found;
74
- }
75
-
76
- /**
77
- * CLI lookup beyond ambient PATH: version-manager shims (fnm multishells,
78
- * nvm, asdf) and ~/.local/bin vanish from bare non-interactive PATHs, so a
79
- * tool installed under one is still present. Presence-only statSync reads —
80
- * never a subprocess probe.
81
- */
82
- export function cliFound(name: string, env: NodeJS.ProcessEnv, home: string): boolean {
83
- if (commandOnPath(name, env)) return true;
84
- const extra = managerBinDirs(home);
85
- if (extra.length === 0) return false;
86
- return commandOnPath(name, {
87
- ...env,
88
- PATH: [...(env.PATH ?? "").split(path.delimiter), ...extra].join(path.delimiter),
89
- });
90
- }
91
-
92
- const managerBinDirs = (home: string): string[] => {
93
- const dirs = [path.join(home, ".local", "bin"), path.join(home, ".asdf", "shims")];
94
- // One-level layouts: fnm node-versions/<v>/installation/bin, nvm node/<v>/bin.
95
- const versioned: Array<[base: string, tail: string]> = [
96
- [path.join(home, ".local", "share", "fnm", "node-versions"), path.join("installation", "bin")],
97
- [path.join(home, ".nvm", "versions", "node"), "bin"],
98
- ];
99
- for (const [base, tail] of versioned) {
100
- let entries: Array<{ name: string; isDirectory: () => boolean }>;
101
- try {
102
- entries = readdirSync(base, { withFileTypes: true });
103
- } catch {
104
- continue;
105
- }
106
- for (const entry of entries) {
107
- try {
108
- if (entry.isDirectory()) dirs.push(path.join(base, entry.name, tail));
109
- } catch {
110
- /* keep scanning */
111
- }
112
- }
113
- }
114
- return dirs;
115
- };