veryfront 0.1.1193 → 0.1.1195

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 (54) hide show
  1. package/esm/cli/app/actions.d.ts +27 -10
  2. package/esm/cli/app/actions.d.ts.map +1 -1
  3. package/esm/cli/app/actions.js +115 -89
  4. package/esm/cli/app/components/list-select.d.ts.map +1 -1
  5. package/esm/cli/app/components/list-select.js +7 -4
  6. package/esm/cli/app/index.d.ts +2 -4
  7. package/esm/cli/app/index.d.ts.map +1 -1
  8. package/esm/cli/app/index.js +3 -5
  9. package/esm/cli/app/key-reducer.d.ts +74 -0
  10. package/esm/cli/app/key-reducer.d.ts.map +1 -0
  11. package/esm/cli/app/key-reducer.js +229 -0
  12. package/esm/cli/app/operations/project-creation.d.ts.map +1 -1
  13. package/esm/cli/app/operations/project-creation.js +2 -8
  14. package/esm/cli/app/shell.d.ts +4 -1
  15. package/esm/cli/app/shell.d.ts.map +1 -1
  16. package/esm/cli/app/shell.js +135 -322
  17. package/esm/cli/app/startup.d.ts +33 -4
  18. package/esm/cli/app/startup.d.ts.map +1 -1
  19. package/esm/cli/app/startup.js +63 -24
  20. package/esm/cli/app/state.d.ts +29 -33
  21. package/esm/cli/app/state.d.ts.map +1 -1
  22. package/esm/cli/app/state.js +40 -70
  23. package/esm/cli/app/types.d.ts +3 -2
  24. package/esm/cli/app/types.d.ts.map +1 -1
  25. package/esm/cli/app/utils.d.ts +1 -2
  26. package/esm/cli/app/utils.d.ts.map +1 -1
  27. package/esm/cli/app/utils.js +0 -29
  28. package/esm/cli/app/views/dashboard.d.ts.map +1 -1
  29. package/esm/cli/app/views/dashboard.js +22 -43
  30. package/esm/cli/app/views/help.js +1 -1
  31. package/esm/cli/commands/start/command.d.ts +13 -0
  32. package/esm/cli/commands/start/command.d.ts.map +1 -1
  33. package/esm/cli/commands/start/command.js +142 -83
  34. package/esm/cli/ui/layout.d.ts +10 -0
  35. package/esm/cli/ui/layout.d.ts.map +1 -1
  36. package/esm/cli/ui/layout.js +14 -2
  37. package/esm/deno.js +1 -1
  38. package/esm/src/agent/hosted/veryfront-cloud-agent-service.d.ts.map +1 -1
  39. package/esm/src/agent/hosted/veryfront-cloud-agent-service.js +1 -2
  40. package/esm/src/html/hydration-script-builder/hydration-runtime.generated.js +1 -1
  41. package/esm/src/platform/compat/http/pinned-fetch.d.ts +32 -0
  42. package/esm/src/platform/compat/http/pinned-fetch.d.ts.map +1 -1
  43. package/esm/src/platform/compat/http/pinned-fetch.js +52 -1
  44. package/esm/src/security/sandbox/worker-egress-guard.d.ts.map +1 -1
  45. package/esm/src/security/sandbox/worker-egress-guard.js +14 -2
  46. package/esm/src/utils/version-constant.d.ts +1 -1
  47. package/esm/src/utils/version-constant.js +1 -1
  48. package/package.json +8 -8
  49. package/esm/cli/app/handlers/remote-navigation.d.ts +0 -20
  50. package/esm/cli/app/handlers/remote-navigation.d.ts.map +0 -1
  51. package/esm/cli/app/handlers/remote-navigation.js +0 -51
  52. package/esm/cli/app/handlers/view-handlers.d.ts +0 -36
  53. package/esm/cli/app/handlers/view-handlers.d.ts.map +0 -1
  54. package/esm/cli/app/handlers/view-handlers.js +0 -130
@@ -1,21 +1,38 @@
1
1
  /**************************
2
2
  * CLI App Actions
3
3
  *
4
- * Handlers for opening projects in browser, Studio, and IDE.
5
- * Uses cross-runtime platform abstractions for filesystem and command execution.
4
+ * Opening a project in the browser, Studio, or an IDE. Everything that leaves
5
+ * the process, spawning commands, opening URLs, touching disk, goes through
6
+ * the LauncherHost seam, so IDE detection order, the Windows/POSIX split, and
7
+ * the settings-file bootstrap are all reachable from a test.
8
+ *
9
+ * Two adapters justify the seam: the platform host in production, a fake in
10
+ * tests.
6
11
  **************************/
7
12
  import type { ProjectInfo } from "./state.js";
8
- import { type EnvironmentConfig } from "../../src/config/index.js";
9
13
  export type IDE = "cursor" | "code" | "zed" | "idea" | "webstorm";
10
14
  export interface ActionResult {
11
15
  success: boolean;
12
16
  message?: string;
13
17
  }
14
- export declare function openInBrowser(project: ProjectInfo, port: number): Promise<ActionResult>;
15
- export declare function openInStudio(project: ProjectInfo): Promise<ActionResult>;
16
- export declare function detectIDEs(): Promise<IDE[]>;
17
- export declare function getPreferredIDE(): Promise<IDE | null>;
18
- export declare function openInIDE(project: ProjectInfo, ide?: IDE): Promise<ActionResult>;
19
- export declare function openFileInIDE(filePath: string, ide?: IDE): Promise<ActionResult>;
20
- export declare function openMCPSettings(env?: EnvironmentConfig): Promise<ActionResult>;
18
+ /** Everything the launcher needs from the outside world. */
19
+ export interface LauncherHost {
20
+ openUrl(url: string): Promise<void>;
21
+ /** Whether `command` is on PATH. */
22
+ commandExists(command: string): Promise<boolean>;
23
+ /** Run `command`; resolves to whether it succeeded. */
24
+ run(command: string, args: string[]): Promise<boolean>;
25
+ /** Create `path` with `contents` if it does not already exist. */
26
+ ensureFile(path: string, contents: string): Promise<void>;
27
+ homeDir(): string;
28
+ }
29
+ export interface Launcher {
30
+ openInBrowser(project: ProjectInfo, port: number): Promise<ActionResult>;
31
+ openInStudio(project: ProjectInfo): Promise<ActionResult>;
32
+ openInIDE(project: ProjectInfo, ide?: IDE): Promise<ActionResult>;
33
+ openMCPSettings(): Promise<ActionResult>;
34
+ }
35
+ export declare function createLauncher(host: LauncherHost): Launcher;
36
+ /** The production host: real processes, real browser, real disk. */
37
+ export declare function createPlatformHost(): LauncherHost;
21
38
  //# sourceMappingURL=actions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/cli/app/actions.ts"],"names":[],"mappings":"AAAA;;;;;4BAK4B;AAO5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAC9C,OAAO,EAAE,KAAK,iBAAiB,EAAwB,MAAM,2BAA2B,CAAC;AAEzF,MAAM,MAAM,GAAG,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAwCD,wBAAsB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAS7F;AAED,wBAAsB,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAS9E;AAED,wBAAsB,UAAU,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC,CAUjD;AAED,wBAAsB,eAAe,IAAI,OAAO,CAAC,GAAG,GAAG,IAAI,CAAC,CAG3D;AAoBD,wBAAsB,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,CAMtF;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,CAEhF;AAED,wBAAsB,eAAe,CACnC,GAAG,GAAE,iBAA0C,GAC9C,OAAO,CAAC,YAAY,CAAC,CAiBvB"}
1
+ {"version":3,"file":"actions.d.ts","sourceRoot":"","sources":["../../../src/cli/app/actions.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;4BAU4B;AAO5B,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAG9C,MAAM,MAAM,GAAG,GAAG,QAAQ,GAAG,MAAM,GAAG,KAAK,GAAG,MAAM,GAAG,UAAU,CAAC;AAElE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,4DAA4D;AAC5D,MAAM,WAAW,YAAY;IAC3B,OAAO,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpC,oCAAoC;IACpC,aAAa,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACjD,uDAAuD;IACvD,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;IACvD,kEAAkE;IAClE,UAAU,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC1D,OAAO,IAAI,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,QAAQ;IACvB,aAAa,CAAC,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IACzE,YAAY,CAAC,OAAO,EAAE,WAAW,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAC1D,SAAS,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,GAAG,GAAG,OAAO,CAAC,YAAY,CAAC,CAAC;IAClE,eAAe,IAAI,OAAO,CAAC,YAAY,CAAC,CAAC;CAC1C;AAqBD,wBAAgB,cAAc,CAAC,IAAI,EAAE,YAAY,GAAG,QAAQ,CAwF3D;AAED,oEAAoE;AACpE,wBAAgB,kBAAkB,IAAI,YAAY,CAqCjD"}
@@ -1,14 +1,19 @@
1
1
  /**************************
2
2
  * CLI App Actions
3
3
  *
4
- * Handlers for opening projects in browser, Studio, and IDE.
5
- * Uses cross-runtime platform abstractions for filesystem and command execution.
4
+ * Opening a project in the browser, Studio, or an IDE. Everything that leaves
5
+ * the process, spawning commands, opening URLs, touching disk, goes through
6
+ * the LauncherHost seam, so IDE detection order, the Windows/POSIX split, and
7
+ * the settings-file bootstrap are all reachable from a test.
8
+ *
9
+ * Two adapters justify the seam: the platform host in production, a fake in
10
+ * tests.
6
11
  **************************/
7
12
  import { openBrowser } from "../auth/browser.js";
8
13
  import { createFileSystem } from "../../src/platform/index.js";
9
14
  import { getOsType, runCommand } from "../../src/platform/index.js";
10
15
  import { formatError } from "../utils/string.js";
11
- import { join } from "../../src/platform/compat/path/index.js";
16
+ import { dirname, join } from "../../src/platform/compat/path/index.js";
12
17
  import { getEnvironmentConfig } from "../../src/config/index.js";
13
18
  const IDE_COMMANDS = {
14
19
  cursor: "cursor",
@@ -24,97 +29,118 @@ const IDE_NAMES = {
24
29
  idea: "IntelliJ IDEA",
25
30
  webstorm: "WebStorm",
26
31
  };
32
+ /** First match wins. */
27
33
  const IDE_DETECTION_ORDER = ["cursor", "code", "zed", "idea", "webstorm"];
28
- async function commandExists(cmd) {
29
- const whichCmd = getOsType() === "windows" ? "where" : "which";
30
- try {
31
- const result = await runCommand(whichCmd, { args: [cmd] });
32
- return result.success;
33
- }
34
- catch {
35
- return false;
36
- }
37
- }
38
- async function runCommandLocal(cmd, args) {
39
- try {
40
- const result = await runCommand(cmd, { args });
41
- return result.success;
42
- }
43
- catch {
44
- return false;
45
- }
46
- }
47
- export async function openInBrowser(project, port) {
48
- const url = `http://${project.slug}.veryfront.me:${port}`;
49
- try {
50
- await openBrowser(url);
51
- return { success: true, message: `Opened ${url}` };
52
- }
53
- catch (error) {
54
- return { success: false, message: `Failed to open browser: ${formatError(error)}` };
34
+ export function createLauncher(host) {
35
+ async function open(url, describe) {
36
+ try {
37
+ await host.openUrl(url);
38
+ return { success: true, message: describe(url) };
39
+ }
40
+ catch (error) {
41
+ return { success: false, message: `Failed to open: ${formatError(error)}` };
42
+ }
55
43
  }
56
- }
57
- export async function openInStudio(project) {
58
- const url = `https://veryfront.com/projects/${project.slug}`;
59
- try {
60
- await openBrowser(url);
61
- return { success: true, message: `Opened Studio for ${project.slug}` };
44
+ async function preferredIDE() {
45
+ for (const ide of IDE_DETECTION_ORDER) {
46
+ if (await host.commandExists(IDE_COMMANDS[ide]))
47
+ return ide;
48
+ }
49
+ return null;
62
50
  }
63
- catch (error) {
64
- return { success: false, message: `Failed to open Studio: ${formatError(error)}` };
51
+ async function openPath(path, ide) {
52
+ const target = ide ?? (await preferredIDE());
53
+ if (!target)
54
+ return { ok: false, name: "" };
55
+ const ok = await host.run(IDE_COMMANDS[target], [path]);
56
+ return { ok, name: IDE_NAMES[target] };
65
57
  }
66
- }
67
- export async function detectIDEs() {
68
- const available = [];
69
- for (const ide of IDE_DETECTION_ORDER) {
70
- if (await commandExists(IDE_COMMANDS[ide])) {
71
- available.push(ide);
58
+ async function openFileInIDE(path) {
59
+ const { ok, name } = await openPath(path);
60
+ if (!name) {
61
+ return {
62
+ success: false,
63
+ message: "No supported IDE found. Install VS Code, Cursor, or Zed.",
64
+ };
72
65
  }
66
+ return ok
67
+ ? { success: true, message: `Opened in ${name}` }
68
+ : { success: false, message: `Failed to open ${name}` };
73
69
  }
74
- return available;
75
- }
76
- export async function getPreferredIDE() {
77
- const ides = await detectIDEs();
78
- return ides[0] ?? null;
79
- }
80
- async function openPathInIDE(path, ide) {
81
- const targetIDE = ide ?? (await getPreferredIDE());
82
- if (!targetIDE) {
83
- return {
84
- success: false,
85
- message: "No supported IDE found. Install VS Code, Cursor, or Zed.",
86
- };
87
- }
88
- const cmd = IDE_COMMANDS[targetIDE];
89
- const name = IDE_NAMES[targetIDE];
90
- const success = await runCommandLocal(cmd, [path]);
91
- return success
92
- ? { success: true, message: `Opened in ${name}` }
93
- : { success: false, message: `Failed to open ${name}` };
70
+ return {
71
+ openInBrowser(project, port) {
72
+ return open(`http://${project.slug}.veryfront.me:${port}`, (url) => `Opened ${url}`);
73
+ },
74
+ openInStudio(project) {
75
+ return open(`https://veryfront.com/projects/${project.slug}`, () => `Opened Studio for ${project.slug}`);
76
+ },
77
+ async openInIDE(project, ide) {
78
+ const { ok, name } = await openPath(project.path, ide);
79
+ if (!name) {
80
+ return {
81
+ success: false,
82
+ message: "No supported IDE found. Install VS Code, Cursor, or Zed.",
83
+ };
84
+ }
85
+ return ok
86
+ ? { success: true, message: `Opened ${project.slug} in ${name}` }
87
+ : { success: false, message: `Failed to open ${name}` };
88
+ },
89
+ async openMCPSettings() {
90
+ const home = host.homeDir().trim();
91
+ if (!home) {
92
+ // Joining "" would write .claude/settings.json into the current
93
+ // directory and still report success.
94
+ return { success: false, message: "Could not determine your home directory." };
95
+ }
96
+ const settingsPath = join(home, ".claude", "settings.json");
97
+ // Every launcher method reports failure through ActionResult. Letting a
98
+ // write error escape instead would reject inside the shell's key
99
+ // handling, where it surfaces as a dead keypress rather than a log line.
100
+ try {
101
+ await host.ensureFile(settingsPath, `${JSON.stringify({ mcpServers: {} }, null, 2)}\n`);
102
+ }
103
+ catch (error) {
104
+ return { success: false, message: `Failed to write settings: ${formatError(error)}` };
105
+ }
106
+ return openFileInIDE(settingsPath);
107
+ },
108
+ };
94
109
  }
95
- export async function openInIDE(project, ide) {
96
- const result = await openPathInIDE(project.path, ide);
97
- if (!result.success)
98
- return result;
99
- const ideName = result.message?.split(" in ")[1];
100
- return { success: true, message: `Opened ${project.slug} in ${ideName}` };
101
- }
102
- export function openFileInIDE(filePath, ide) {
103
- return openPathInIDE(filePath, ide);
104
- }
105
- export async function openMCPSettings(env = getEnvironmentConfig()) {
106
- const home = env.homeDir ?? "";
107
- const claudeDir = join(home, ".claude");
108
- const settingsPath = join(claudeDir, "settings.json");
109
- const fs = createFileSystem();
110
- try {
111
- await fs.mkdir(claudeDir, { recursive: true });
112
- }
113
- catch {
114
- // Already exists
115
- }
116
- if (!(await fs.exists(settingsPath))) {
117
- await fs.writeTextFile(settingsPath, JSON.stringify({ mcpServers: {} }, null, 2));
118
- }
119
- return openFileInIDE(settingsPath);
110
+ /** The production host: real processes, real browser, real disk. */
111
+ export function createPlatformHost() {
112
+ return {
113
+ openUrl: (url) => openBrowser(url),
114
+ async commandExists(command) {
115
+ const probe = getOsType() === "windows" ? "where" : "which";
116
+ try {
117
+ const result = await runCommand(probe, { args: [command] });
118
+ return result.success;
119
+ }
120
+ catch {
121
+ return false;
122
+ }
123
+ },
124
+ async run(command, args) {
125
+ try {
126
+ const result = await runCommand(command, { args });
127
+ return result.success;
128
+ }
129
+ catch {
130
+ return false;
131
+ }
132
+ },
133
+ async ensureFile(path, contents) {
134
+ const fs = createFileSystem();
135
+ try {
136
+ await fs.mkdir(dirname(path), { recursive: true });
137
+ }
138
+ catch {
139
+ // Already exists
140
+ }
141
+ if (!(await fs.exists(path)))
142
+ await fs.writeTextFile(path, contents);
143
+ },
144
+ homeDir: () => getEnvironmentConfig().homeDir ?? "",
145
+ };
120
146
  }
@@ -1 +1 @@
1
- {"version":3,"file":"list-select.d.ts","sourceRoot":"","sources":["../../../../src/cli/app/components/list-select.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO;IACnC,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,CAAC,CAAC;CACV;AAED,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAE3E;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAQvE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,YAAY,SAAK,GAChB,eAAe,CAAC,CAAC,CAAC,CAcpB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,GAAG,EAAE,MAAM,GACV,eAAe,CAAC,CAAC,CAAC,CAIpB;AAQD;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAC1B,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAmFR"}
1
+ {"version":3,"file":"list-select.d.ts","sourceRoot":"","sources":["../../../../src/cli/app/components/list-select.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAKH,MAAM,WAAW,QAAQ,CAAC,CAAC,GAAG,OAAO;IACnC,wBAAwB;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,oBAAoB;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,2BAA2B;IAC3B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gCAAgC;IAChC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,sBAAsB;IACtB,IAAI,CAAC,EAAE,CAAC,CAAC;CACV;AAED,MAAM,WAAW,iBAAiB;IAChC,iCAAiC;IACjC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,8CAA8C;IAC9C,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,kCAAkC;IAClC,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,+DAA+D;IAC/D,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,0BAA0B;IAC1B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,4EAA4E;IAC5E,aAAa,CAAC,EAAE,OAAO,CAAC;CACzB;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC1C,4BAA4B;IAC5B,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IACrB,+BAA+B;IAC/B,aAAa,EAAE,MAAM,CAAC;IACtB,mCAAmC;IACnC,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,CAAC,EAAE,KAAK,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,GAAG,eAAe,CAAC,CAAC,CAAC,CAE3E;AAED;;GAEG;AACH,wBAAgB,MAAM,CAAC,CAAC,EAAE,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,CAQvE;AAED;;GAEG;AACH,wBAAgB,QAAQ,CAAC,CAAC,EACxB,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,YAAY,SAAK,GAChB,eAAe,CAAC,CAAC,CAAC,CAcpB;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,GAAG,EAAE,MAAM,GACV,eAAe,CAAC,CAAC,CAAC,CAIpB;AAYD;;GAEG;AACH,wBAAgB,UAAU,CAAC,CAAC,EAC1B,KAAK,EAAE,eAAe,CAAC,CAAC,CAAC,EACzB,OAAO,GAAE,iBAAsB,GAC9B,MAAM,CAmFR"}
@@ -49,12 +49,15 @@ export function selectByNumber(state, num) {
49
49
  return state;
50
50
  return { ...state, selectedIndex: index };
51
51
  }
52
+ /**
53
+ * Only 1-9 are offered. Letters would collide with the action keys the
54
+ * dashboard already binds (a, i, o, p, s, u, x…), so an item past the ninth
55
+ * shows no shortcut and is reached with the arrow keys.
56
+ */
52
57
  function getShortcut(displayNum) {
53
- if (displayNum <= 0 || displayNum > 35)
58
+ if (displayNum <= 0 || displayNum > 9)
54
59
  return undefined;
55
- if (displayNum <= 9)
56
- return String(displayNum);
57
- return String.fromCharCode(96 + displayNum - 9); // 10='a', 11='b', etc.
60
+ return String(displayNum);
58
61
  }
59
62
  /**
60
63
  * Render the list as a string
@@ -4,10 +4,8 @@
4
4
  * Interactive app-like CLI experience with dashboard and project navigation.
5
5
  */
6
6
  export { createApp } from "./shell.js";
7
- export { showStartup } from "./startup.js";
7
+ export { startStartupProgress, type StartupProgress } from "./startup.js";
8
8
  export type { App, AppConfig } from "./types.js";
9
9
  export type { AppState, LogMeta, ProjectInfo, StateUpdater } from "./state.js";
10
- export * from "./state.js";
11
- export * from "./actions.js";
12
- export * from "./components/list-select.js";
10
+ export { type Effect, type KeyEnv, type KeyResult, reduceKey } from "./key-reducer.js";
13
11
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/app/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,WAAW,EAAE,MAAM,cAAc,CAAC;AAG3C,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/E,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,6BAA6B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/cli/app/index.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACvC,OAAO,EAAE,oBAAoB,EAAE,KAAK,eAAe,EAAE,MAAM,cAAc,CAAC;AAG1E,YAAY,EAAE,GAAG,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AACjD,YAAY,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAG/E,OAAO,EAAE,KAAK,MAAM,EAAE,KAAK,MAAM,EAAE,KAAK,SAAS,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC"}
@@ -5,8 +5,6 @@
5
5
  */
6
6
  // Core app
7
7
  export { createApp } from "./shell.js";
8
- export { showStartup } from "./startup.js";
9
- // State management (for external consumers)
10
- export * from "./state.js";
11
- export * from "./actions.js";
12
- export * from "./components/list-select.js";
8
+ export { startStartupProgress } from "./startup.js";
9
+ // Key transition (the app's decision surface)
10
+ export { reduceKey } from "./key-reducer.js";
@@ -0,0 +1,74 @@
1
+ /**
2
+ * Key Transition
3
+ *
4
+ * The single owner of "what does this key press do?". A pure function of
5
+ * (state, key) that settles into the next state plus the effects the shell
6
+ * should perform. Nothing here touches the terminal, the network, or the
7
+ * clock, so every key path is reachable from a test through this one
8
+ * interface, the same one the shell uses.
9
+ *
10
+ * Effects are data, never callbacks. The shell performs them; this module
11
+ * decides them.
12
+ */
13
+ import type { InitTemplate } from "../commands/init/types.js";
14
+ import { type AppState, type ProjectInfo } from "./state.js";
15
+ export declare const KEY_UP = "\u001B[A";
16
+ export declare const KEY_DOWN = "\u001B[B";
17
+ export declare const KEY_ESCAPE = "\u001B";
18
+ export declare const KEY_ENTER = "\r";
19
+ export declare const KEY_NEWLINE = "\n";
20
+ export declare const KEY_CTRL_C = "\u0003";
21
+ export declare const KEY_TAB = "\t";
22
+ export type AuthProvider = "google" | "github" | "microsoft";
23
+ export declare const AUTH_PROVIDERS: AuthProvider[];
24
+ /** Work the shell performs on the reducer's behalf. */
25
+ export type Effect = {
26
+ kind: "exit";
27
+ } | {
28
+ kind: "open-browser";
29
+ project: ProjectInfo;
30
+ } | {
31
+ kind: "open-studio";
32
+ project: ProjectInfo;
33
+ } | {
34
+ kind: "open-ide";
35
+ project: ProjectInfo;
36
+ } | {
37
+ kind: "pull";
38
+ project: ProjectInfo;
39
+ } | {
40
+ kind: "push";
41
+ project: ProjectInfo;
42
+ } | {
43
+ kind: "login";
44
+ provider: AuthProvider;
45
+ } | {
46
+ kind: "logout";
47
+ } | {
48
+ kind: "open-mcp-settings";
49
+ } | {
50
+ kind: "create-project";
51
+ template: InitTemplate;
52
+ name: string;
53
+ };
54
+ export interface KeyResult {
55
+ state: AppState;
56
+ effects: Effect[];
57
+ }
58
+ /**
59
+ * The impure facts the transition needs. Injected rather than reached for, so
60
+ * the reducer stays a pure function and tests can pin the suggestion.
61
+ */
62
+ export interface KeyEnv {
63
+ /** Pre-filled value for the project-name prompt. */
64
+ suggestProjectName: () => string;
65
+ }
66
+ /**
67
+ * Reduce one key press against the current state.
68
+ *
69
+ * The returned state is always the state to adopt, there is no second write
70
+ * path. Callers assign it and run the effects; they never mutate state
71
+ * themselves.
72
+ */
73
+ export declare function reduceKey(state: AppState, key: string, env: KeyEnv): KeyResult;
74
+ //# sourceMappingURL=key-reducer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"key-reducer.d.ts","sourceRoot":"","sources":["../../../src/cli/app/key-reducer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAEL,KAAK,QAAQ,EAKb,KAAK,WAAW,EAQjB,MAAM,YAAY,CAAC;AASpB,eAAO,MAAM,MAAM,aAAW,CAAC;AAC/B,eAAO,MAAM,QAAQ,aAAW,CAAC;AACjC,eAAO,MAAM,UAAU,WAAS,CAAC;AACjC,eAAO,MAAM,SAAS,OAAO,CAAC;AAC9B,eAAO,MAAM,WAAW,OAAO,CAAC;AAChC,eAAO,MAAM,UAAU,WAAS,CAAC;AACjC,eAAO,MAAM,OAAO,OAAO,CAAC;AAK5B,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,GAAG,WAAW,CAAC;AAE7D,eAAO,MAAM,cAAc,EAAE,YAAY,EAAsC,CAAC;AAEhF,uDAAuD;AACvD,MAAM,MAAM,MAAM,GACd;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,GAChB;IAAE,IAAI,EAAE,cAAc,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAC9C;IAAE,IAAI,EAAE,aAAa,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAC7C;IAAE,IAAI,EAAE,UAAU,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GAC1C;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,OAAO,EAAE,WAAW,CAAA;CAAE,GACtC;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAA;CAAE,GACzC;IAAE,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClB;IAAE,IAAI,EAAE,mBAAmB,CAAA;CAAE,GAC7B;IAAE,IAAI,EAAE,gBAAgB,CAAC;IAAC,QAAQ,EAAE,YAAY,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAErE,MAAM,WAAW,SAAS;IACxB,KAAK,EAAE,QAAQ,CAAC;IAChB,OAAO,EAAE,MAAM,EAAE,CAAC;CACnB;AAED;;;GAGG;AACH,MAAM,WAAW,MAAM;IACrB,oDAAoD;IACpD,kBAAkB,EAAE,MAAM,MAAM,CAAC;CAClC;AAID;;;;;;GAMG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,QAAQ,EAAE,GAAG,EAAE,MAAM,EAAE,GAAG,EAAE,MAAM,GAAG,SAAS,CAwB9E"}