@kenkaiiii/gg-editor 0.1.0 → 0.3.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.
@@ -0,0 +1,212 @@
1
+ /**
2
+ * Interactive doctor runner.
3
+ *
4
+ * Used by `ggeditor doctor` and the first-run onboarding flow. Walks
5
+ * through actionable items one at a time:
6
+ *
7
+ * - If the item carries an `installable` hint → ask "Install X? [Y/n]".
8
+ * Yes → spawn the package manager, stream output live, re-run the
9
+ * doctor, move on. No → print the manual `fix` text, move on.
10
+ * - If the item only has a `fix` string (manual setup, env var, etc.)
11
+ * → print it and move on.
12
+ *
13
+ * The banner matches `runStatus`'s look ("GG Editor — Doctor", orange-
14
+ * bold, two-space indent, dim subline) so the three CLI commands
15
+ * (auth/login/doctor) feel like one product.
16
+ */
17
+ import { spawn } from "node:child_process";
18
+ import { createInterface } from "node:readline/promises";
19
+ import chalk from "chalk";
20
+ import { runDoctor } from "./doctor.js";
21
+ import { renderDoctorReport } from "./doctor-render.js";
22
+ const EDITOR_PRIMARY = "#f97316"; // matches login.ts / runStatus
23
+ const EDITOR_ACCENT = "#ec4899";
24
+ const orange = chalk.hex(EDITOR_PRIMARY);
25
+ const pink = chalk.hex(EDITOR_ACCENT);
26
+ /**
27
+ * Top-level entry. Renders the orange banner, then either
28
+ * - prints the inventory (`all` or `nonInteractive`), OR
29
+ * - walks the user through fixable items one at a time.
30
+ */
31
+ export async function runDoctorInteractive(opts = {}) {
32
+ printBanner(opts.onboarding === true);
33
+ // Non-interactive paths: just print and exit.
34
+ if (opts.all || opts.nonInteractive || !process.stdin.isTTY) {
35
+ const report = runDoctor();
36
+ process.stdout.write(renderDoctorReport(report, { all: opts.all, onboarding: opts.onboarding }));
37
+ return;
38
+ }
39
+ await walkActionableItems(opts.onboarding === true);
40
+ }
41
+ // ── Internals ──────────────────────────────────────────────
42
+ function printBanner(onboarding) {
43
+ const title = onboarding ? "Welcome to GG Editor" : "GG Editor — Doctor";
44
+ process.stdout.write(orange.bold(`\n ${title}\n`));
45
+ process.stdout.write(chalk.dim(onboarding
46
+ ? " One-time environment check. Re-run any time with `ggeditor doctor`.\n\n"
47
+ : " Environment check. Re-run any time.\n\n"));
48
+ }
49
+ /**
50
+ * Walk through actionable items in priority order. After each step we
51
+ * re-run the doctor so that successful installs unblock follow-on
52
+ * checks (e.g. installing ffmpeg flips both ffmpeg + ffprobe to OK).
53
+ */
54
+ async function walkActionableItems(onboarding) {
55
+ const rl = createInterface({ input: process.stdin, output: process.stdout });
56
+ // Track items the user has explicitly skipped this session so we
57
+ // don't re-ask about them after every successful install.
58
+ const skipped = new Set();
59
+ try {
60
+ for (let step = 0; step < 20; step++) {
61
+ const report = runDoctor();
62
+ const next = pickNextActionable(report, skipped);
63
+ const total = countActionable(report);
64
+ const done = countDone(report);
65
+ if (!next) {
66
+ // Either everything's clean or we've skipped everything we can.
67
+ const cleanEverything = total === done;
68
+ if (cleanEverything) {
69
+ process.stdout.write(" " +
70
+ chalk.green("✓ Nothing to fix. You're all good.") +
71
+ chalk.dim(" Run `ggeditor` to start.\n\n"));
72
+ }
73
+ else {
74
+ process.stdout.write(" " +
75
+ chalk.dim(`Skipped the rest. ${done}/${total} ready. Re-run \`ggeditor doctor\` any time.\n\n`));
76
+ }
77
+ return;
78
+ }
79
+ const proceed = await handleItem(next, rl, onboarding);
80
+ if (proceed === "skip")
81
+ skipped.add(next.id);
82
+ if (proceed === "quit") {
83
+ process.stdout.write(chalk.dim(" Done. Re-run `ggeditor doctor` any time.\n\n"));
84
+ return;
85
+ }
86
+ }
87
+ // Safety bound — should never hit this.
88
+ process.stdout.write(chalk.dim(" Reached step limit. Re-run `ggeditor doctor` to continue.\n\n"));
89
+ }
90
+ finally {
91
+ rl.close();
92
+ }
93
+ }
94
+ async function handleItem(item, rl, onboarding) {
95
+ // Header: severity + label + detail.
96
+ const tag = item.severity === "required" || item.severity === "block"
97
+ ? chalk.red("required")
98
+ : item.status === "warn"
99
+ ? chalk.yellow("needs attention")
100
+ : chalk.yellow("optional");
101
+ process.stdout.write(` ${tag} · ${chalk.bold(item.label)} ${chalk.dim("— " + item.detail)}\n`);
102
+ process.stdout.write(" " + chalk.dim(item.unlocks) + "\n\n");
103
+ if (item.installable) {
104
+ return await offerInstall(item, item.installable, rl);
105
+ }
106
+ // No structured installer — print the manual fix and move on.
107
+ if (item.fix) {
108
+ process.stdout.write(chalk.dim(" Fix:\n"));
109
+ for (const line of item.fix.split("\n")) {
110
+ process.stdout.write(" " + chalk.cyan(line) + "\n");
111
+ }
112
+ process.stdout.write("\n");
113
+ }
114
+ // For required items without a structured installer (e.g. auth →
115
+ // `ggeditor login`) we ask whether to keep going. For optional ones
116
+ // we just move on.
117
+ if (item.severity === "required" || item.severity === "block") {
118
+ const ans = await askYN(rl, " Continue to the next item?", "y");
119
+ return ans ? "next" : "quit";
120
+ }
121
+ // Onboarding mode: pause briefly so the user reads the fix.
122
+ if (onboarding) {
123
+ const ans = await askYN(rl, " Continue?", "y");
124
+ return ans ? "next" : "quit";
125
+ }
126
+ return "next";
127
+ }
128
+ async function offerInstall(item, hint, rl) {
129
+ const cmdline = `${hint.command} ${hint.args.join(" ")}`;
130
+ process.stdout.write(` ${chalk.bold(hint.label)}\n`);
131
+ process.stdout.write(` ${pink(cmdline)}\n`);
132
+ if (hint.needsSudo) {
133
+ process.stdout.write(chalk.dim(" (will prompt for your sudo password)\n"));
134
+ }
135
+ process.stdout.write("\n");
136
+ const yes = await askYN(rl, " Install now?", "y");
137
+ if (!yes) {
138
+ if (item.fix) {
139
+ process.stdout.write(chalk.dim(" Skipping. To install later:\n"));
140
+ for (const line of item.fix.split("\n")) {
141
+ process.stdout.write(" " + chalk.cyan(line) + "\n");
142
+ }
143
+ process.stdout.write("\n");
144
+ }
145
+ return "skip";
146
+ }
147
+ const exitCode = await spawnInstall(hint);
148
+ if (exitCode === 0) {
149
+ process.stdout.write(chalk.green(" ✓ Installed.\n\n"));
150
+ return "next";
151
+ }
152
+ process.stdout.write(chalk.red(` ✗ Install failed (exit ${exitCode}).`) + chalk.dim(" You can fix it manually:\n"));
153
+ if (item.fix) {
154
+ for (const line of item.fix.split("\n")) {
155
+ process.stdout.write(" " + chalk.cyan(line) + "\n");
156
+ }
157
+ }
158
+ process.stdout.write("\n");
159
+ return "skip";
160
+ }
161
+ function spawnInstall(hint) {
162
+ return new Promise((resolve) => {
163
+ const child = spawn(hint.command, hint.args, {
164
+ stdio: "inherit", // user sees + interacts with the manager directly
165
+ });
166
+ child.on("close", (code) => resolve(code ?? 1));
167
+ child.on("error", () => resolve(1));
168
+ });
169
+ }
170
+ async function askYN(rl, prompt, defaultAns) {
171
+ const suffix = defaultAns === "y" ? chalk.dim(" [Y/n] ") : chalk.dim(" [y/N] ");
172
+ const raw = (await rl.question(prompt + suffix)).trim().toLowerCase();
173
+ if (raw === "")
174
+ return defaultAns === "y";
175
+ if (raw === "y" || raw === "yes")
176
+ return true;
177
+ if (raw === "n" || raw === "no")
178
+ return false;
179
+ // Anything else: re-ask once, then fall through to default.
180
+ const retry = (await rl.question(chalk.dim(' Please answer "y" or "n": '))).trim().toLowerCase();
181
+ if (retry === "y" || retry === "yes")
182
+ return true;
183
+ if (retry === "n" || retry === "no")
184
+ return false;
185
+ return defaultAns === "y";
186
+ }
187
+ /**
188
+ * Pick the next actionable item (block > required > optional-warn >
189
+ * optional-missing) that the user hasn't already skipped this session.
190
+ * Info-severity items are never returned.
191
+ */
192
+ function pickNextActionable(report, skipped) {
193
+ const tiers = [
194
+ { severity: "block", statuses: ["missing", "warn"] },
195
+ { severity: "required", statuses: ["missing", "warn"] },
196
+ { severity: "optional", statuses: ["warn"] },
197
+ { severity: "optional", statuses: ["missing"] },
198
+ ];
199
+ for (const tier of tiers) {
200
+ const hit = report.checks.find((c) => c.severity === tier.severity && tier.statuses.includes(c.status) && !skipped.has(c.id));
201
+ if (hit)
202
+ return hit;
203
+ }
204
+ return undefined;
205
+ }
206
+ function countActionable(report) {
207
+ return report.checks.filter((c) => c.severity !== "info").length;
208
+ }
209
+ function countDone(report) {
210
+ return report.checks.filter((c) => c.severity !== "info" && c.status === "ok").length;
211
+ }
212
+ //# sourceMappingURL=doctor-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor-runner.js","sourceRoot":"","sources":["../../src/core/doctor-runner.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * First-run doctor — environment probes + actionable install hints.
3
+ *
4
+ * The CLI runs onboarding on first launch (no `~/.gg/auth.json` and no
5
+ * `~/.gg/onboarded-ggeditor` marker). The same checks are exposed via
6
+ * `ggeditor doctor` so users can re-run it any time.
7
+ *
8
+ * Design rules:
9
+ * - Probes only. We never auto-install anything — surprise sudo
10
+ * prompts are worse than a missing dep.
11
+ * - Each check carries `severity`:
12
+ * block — nothing meaningful works without it (none currently;
13
+ * the agent can run with zero deps)
14
+ * required — most tools need it (ffmpeg / ffprobe)
15
+ * optional — unlocks a feature group (openai-key, resolve,
16
+ * premiere, whisper-cpp, whisperx)
17
+ * info — purely informational (auth status)
18
+ * - Each check tells the user EXACTLY what to do to fix it, including
19
+ * the platform-appropriate install command.
20
+ * - Pure module — no I/O writes, no side effects beyond `spawnSync` /
21
+ * `existsSync` probes.
22
+ */
23
+ export type CheckSeverity = "block" | "required" | "optional" | "info";
24
+ export type CheckStatus = "ok" | "missing" | "warn";
25
+ /**
26
+ * Structured install hint. When present, the doctor's interactive flow
27
+ * can offer to spawn this command after a Y/N confirmation — no copy-
28
+ * paste, no shell injection (we never run a string through `sh -c`).
29
+ *
30
+ * Only attached to checks where the install is a single packaged step
31
+ * (Homebrew formula, winget id, apt package). Items that need manual
32
+ * sign-up (API keys), license acceptance (Resolve installer), or
33
+ * multi-step setup (whisperx + HF_TOKEN) carry a `fix` string instead.
34
+ */
35
+ export interface InstallableHint {
36
+ /** Human label shown in the prompt: "Install ffmpeg via Homebrew". */
37
+ label: string;
38
+ /** Executable on PATH. */
39
+ command: string;
40
+ /** Argument vector — NEVER a shell string. */
41
+ args: string[];
42
+ /**
43
+ * What managed manager this uses. Used by the renderer to decide
44
+ * whether to show "requires sudo" copy.
45
+ */
46
+ manager: "homebrew" | "winget" | "apt" | "pip" | "npm";
47
+ /** True when the command requires elevated privileges (sudo / admin). */
48
+ needsSudo?: boolean;
49
+ }
50
+ export interface DoctorCheck {
51
+ /** Stable id (also the check key). */
52
+ id: string;
53
+ /** ≤30 char display label. */
54
+ label: string;
55
+ status: CheckStatus;
56
+ severity: CheckSeverity;
57
+ /** One-line current state ("v6.1.1 found", "not on PATH", …). */
58
+ detail: string;
59
+ /**
60
+ * What this check unlocks for the user. Always present, even when
61
+ * status=ok — gives the user the mental model.
62
+ */
63
+ unlocks: string;
64
+ /**
65
+ * Fix instruction. Multi-line ok. Empty when status=ok.
66
+ * Platform-appropriate (macOS / linux / win32).
67
+ */
68
+ fix?: string;
69
+ /**
70
+ * Optional structured install command for items where a single
71
+ * package-manager invocation does the job. The CLI offers Y/N
72
+ * confirmation and spawns it directly.
73
+ */
74
+ installable?: InstallableHint;
75
+ }
76
+ export interface DoctorReport {
77
+ checks: DoctorCheck[];
78
+ /** True when no `severity=required` check is missing. */
79
+ ready: boolean;
80
+ /** Where the marker file was/should be written. */
81
+ markerPath: string;
82
+ /** Whether onboarding has been completed before. */
83
+ onboarded: boolean;
84
+ }
85
+ export declare function onboardedMarkerPath(home?: string): string;
86
+ export declare function isOnboarded(home?: string): boolean;
87
+ /**
88
+ * Run every check. Synchronous and quick — only spawns short-lived
89
+ * `--version` style probes.
90
+ */
91
+ export declare function runDoctor(home?: string): DoctorReport;
92
+ //# sourceMappingURL=doctor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"doctor.d.ts","sourceRoot":"","sources":["../../src/core/doctor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;GAqBG;AASH,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,UAAU,GAAG,UAAU,GAAG,MAAM,CAAC;AAEvE,MAAM,MAAM,WAAW,GAAG,IAAI,GAAG,SAAS,GAAG,MAAM,CAAC;AAEpD;;;;;;;;;GASG;AACH,MAAM,WAAW,eAAe;IAC9B,sEAAsE;IACtE,KAAK,EAAE,MAAM,CAAC;IACd,0BAA0B;IAC1B,OAAO,EAAE,MAAM,CAAC;IAChB,8CAA8C;IAC9C,IAAI,EAAE,MAAM,EAAE,CAAC;IACf;;;OAGG;IACH,OAAO,EAAE,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,KAAK,GAAG,KAAK,CAAC;IACvD,yEAAyE;IACzE,SAAS,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,WAAW,WAAW;IAC1B,sCAAsC;IACtC,EAAE,EAAE,MAAM,CAAC;IACX,8BAA8B;IAC9B,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,WAAW,CAAC;IACpB,QAAQ,EAAE,aAAa,CAAC;IACxB,iEAAiE;IACjE,MAAM,EAAE,MAAM,CAAC;IACf;;;OAGG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;;OAIG;IACH,WAAW,CAAC,EAAE,eAAe,CAAC;CAC/B;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,yDAAyD;IACzD,KAAK,EAAE,OAAO,CAAC;IACf,mDAAmD;IACnD,UAAU,EAAE,MAAM,CAAC;IACnB,oDAAoD;IACpD,SAAS,EAAE,OAAO,CAAC;CACpB;AAID,wBAAgB,mBAAmB,CAAC,IAAI,GAAE,MAAkB,GAAG,MAAM,CAEpE;AAED,wBAAgB,WAAW,CAAC,IAAI,GAAE,MAAkB,GAAG,OAAO,CAM7D;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,IAAI,GAAE,MAAkB,GAAG,YAAY,CAqBhE"}