@stigg/terminal 0.0.1-alpha

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 (199) hide show
  1. package/LICENSE +15 -0
  2. package/README.md +47 -0
  3. package/dist/api/client.d.ts +6 -0
  4. package/dist/api/client.js +48 -0
  5. package/dist/api/format-key.d.ts +7 -0
  6. package/dist/api/format-key.js +12 -0
  7. package/dist/api/graphql-client.d.ts +5 -0
  8. package/dist/api/graphql-client.js +44 -0
  9. package/dist/api/operations.d.ts +65 -0
  10. package/dist/api/operations.js +77 -0
  11. package/dist/api/types.d.ts +18 -0
  12. package/dist/api/types.js +1 -0
  13. package/dist/auth/callback-server.d.ts +14 -0
  14. package/dist/auth/callback-server.js +145 -0
  15. package/dist/auth/config.d.ts +2 -0
  16. package/dist/auth/config.js +24 -0
  17. package/dist/auth/oauth.d.ts +17 -0
  18. package/dist/auth/oauth.js +94 -0
  19. package/dist/auth/storage.d.ts +6 -0
  20. package/dist/auth/storage.js +34 -0
  21. package/dist/bin.d.ts +2 -0
  22. package/dist/bin.js +3 -0
  23. package/dist/cli.d.ts +1 -0
  24. package/dist/cli.js +79 -0
  25. package/dist/commands/dash.d.ts +1 -0
  26. package/dist/commands/dash.js +24 -0
  27. package/dist/commands/debug.d.ts +1 -0
  28. package/dist/commands/debug.js +53 -0
  29. package/dist/commands/env.d.ts +1 -0
  30. package/dist/commands/env.js +15 -0
  31. package/dist/commands/init.d.ts +13 -0
  32. package/dist/commands/init.js +59 -0
  33. package/dist/commands/mcp.d.ts +7 -0
  34. package/dist/commands/mcp.js +37 -0
  35. package/dist/commands/skills.d.ts +6 -0
  36. package/dist/commands/skills.js +48 -0
  37. package/dist/headless/host-agent.d.ts +19 -0
  38. package/dist/headless/host-agent.js +64 -0
  39. package/dist/headless/init-phase1.d.ts +9 -0
  40. package/dist/headless/init-phase1.js +173 -0
  41. package/dist/headless/init-phase2.d.ts +36 -0
  42. package/dist/headless/init-phase2.js +150 -0
  43. package/dist/headless/next-step-prompt.d.ts +7 -0
  44. package/dist/headless/next-step-prompt.js +25 -0
  45. package/dist/headless/options.d.ts +30 -0
  46. package/dist/headless/options.js +77 -0
  47. package/dist/headless/reporter.d.ts +16 -0
  48. package/dist/headless/reporter.js +41 -0
  49. package/dist/headless/setup.d.ts +29 -0
  50. package/dist/headless/setup.js +80 -0
  51. package/dist/launch/agent.d.ts +55 -0
  52. package/dist/launch/agent.js +134 -0
  53. package/dist/mcp/clients/base.d.ts +49 -0
  54. package/dist/mcp/clients/base.js +66 -0
  55. package/dist/mcp/clients/claude-code.d.ts +22 -0
  56. package/dist/mcp/clients/claude-code.js +120 -0
  57. package/dist/mcp/clients/claude-desktop.d.ts +9 -0
  58. package/dist/mcp/clients/claude-desktop.js +35 -0
  59. package/dist/mcp/clients/codex.d.ts +13 -0
  60. package/dist/mcp/clients/codex.js +113 -0
  61. package/dist/mcp/clients/cursor.d.ts +9 -0
  62. package/dist/mcp/clients/cursor.js +26 -0
  63. package/dist/mcp/clients/index.d.ts +7 -0
  64. package/dist/mcp/clients/index.js +27 -0
  65. package/dist/mcp/clients/mcp-remote.d.ts +11 -0
  66. package/dist/mcp/clients/mcp-remote.js +13 -0
  67. package/dist/mcp/clients/vscode.d.ts +17 -0
  68. package/dist/mcp/clients/vscode.js +84 -0
  69. package/dist/mcp/clients.d.ts +4 -0
  70. package/dist/mcp/clients.js +50 -0
  71. package/dist/mcp/config-merge.d.ts +9 -0
  72. package/dist/mcp/config-merge.js +51 -0
  73. package/dist/mcp/writer.d.ts +6 -0
  74. package/dist/mcp/writer.js +65 -0
  75. package/dist/setup/storage.d.ts +21 -0
  76. package/dist/setup/storage.js +34 -0
  77. package/dist/skills/install.d.ts +19 -0
  78. package/dist/skills/install.js +64 -0
  79. package/dist/types.d.ts +35 -0
  80. package/dist/types.js +1 -0
  81. package/dist/ui/components/Card.d.ts +11 -0
  82. package/dist/ui/components/Card.js +19 -0
  83. package/dist/ui/components/ContextRow.d.ts +11 -0
  84. package/dist/ui/components/ContextRow.js +8 -0
  85. package/dist/ui/components/Footer.d.ts +10 -0
  86. package/dist/ui/components/Footer.js +9 -0
  87. package/dist/ui/components/Header.d.ts +11 -0
  88. package/dist/ui/components/Header.js +6 -0
  89. package/dist/ui/components/JsonPreview.d.ts +13 -0
  90. package/dist/ui/components/JsonPreview.js +25 -0
  91. package/dist/ui/components/SectionTitle.d.ts +6 -0
  92. package/dist/ui/components/SectionTitle.js +5 -0
  93. package/dist/ui/hooks/useAsyncEffect.d.ts +3 -0
  94. package/dist/ui/hooks/useAsyncEffect.js +20 -0
  95. package/dist/ui/hooks/useResize.d.ts +4 -0
  96. package/dist/ui/hooks/useResize.js +20 -0
  97. package/dist/ui/hud.d.ts +15 -0
  98. package/dist/ui/hud.js +30 -0
  99. package/dist/ui/ink-theme.d.ts +2 -0
  100. package/dist/ui/ink-theme.js +34 -0
  101. package/dist/ui/intro/LogoView.d.ts +12 -0
  102. package/dist/ui/intro/LogoView.js +226 -0
  103. package/dist/ui/intro/MatrixIntro.d.ts +6 -0
  104. package/dist/ui/intro/MatrixIntro.js +80 -0
  105. package/dist/ui/intro/logo.d.ts +6 -0
  106. package/dist/ui/intro/logo.js +21 -0
  107. package/dist/ui/messages.d.ts +5 -0
  108. package/dist/ui/messages.js +5 -0
  109. package/dist/ui/screens/DashScreen.d.ts +6 -0
  110. package/dist/ui/screens/DashScreen.js +27 -0
  111. package/dist/ui/screens/DebugScreen.d.ts +6 -0
  112. package/dist/ui/screens/DebugScreen.js +39 -0
  113. package/dist/ui/screens/InitScreen.d.ts +6 -0
  114. package/dist/ui/screens/InitScreen.js +138 -0
  115. package/dist/ui/screens/MenuScreen.d.ts +7 -0
  116. package/dist/ui/screens/MenuScreen.js +38 -0
  117. package/dist/ui/state.d.ts +72 -0
  118. package/dist/ui/state.js +107 -0
  119. package/dist/ui/steps/AccountStep.d.ts +8 -0
  120. package/dist/ui/steps/AccountStep.js +42 -0
  121. package/dist/ui/steps/ApiKeyStep.d.ts +8 -0
  122. package/dist/ui/steps/ApiKeyStep.js +91 -0
  123. package/dist/ui/steps/ClientsStep.d.ts +10 -0
  124. package/dist/ui/steps/ClientsStep.js +69 -0
  125. package/dist/ui/steps/CredentialKindStep.d.ts +7 -0
  126. package/dist/ui/steps/CredentialKindStep.js +18 -0
  127. package/dist/ui/steps/EnvironmentStep.d.ts +8 -0
  128. package/dist/ui/steps/EnvironmentStep.js +37 -0
  129. package/dist/ui/steps/LoginStep.d.ts +7 -0
  130. package/dist/ui/steps/LoginStep.js +56 -0
  131. package/dist/ui/steps/SkillsStep.d.ts +7 -0
  132. package/dist/ui/steps/SkillsStep.js +7 -0
  133. package/dist/ui/steps/SummaryStep.d.ts +8 -0
  134. package/dist/ui/steps/SummaryStep.js +41 -0
  135. package/dist/ui/steps/WritingStep.d.ts +10 -0
  136. package/dist/ui/steps/WritingStep.js +96 -0
  137. package/dist/ui/theme.d.ts +53 -0
  138. package/dist/ui/theme.js +66 -0
  139. package/dist/ui/tui/App.d.ts +10 -0
  140. package/dist/ui/tui/App.js +51 -0
  141. package/dist/ui/tui/components/ContextStrip.d.ts +11 -0
  142. package/dist/ui/tui/components/ContextStrip.js +8 -0
  143. package/dist/ui/tui/components/TitleBar.d.ts +6 -0
  144. package/dist/ui/tui/components/TitleBar.js +18 -0
  145. package/dist/ui/tui/components/WizardChecklist.d.ts +7 -0
  146. package/dist/ui/tui/components/WizardChecklist.js +69 -0
  147. package/dist/ui/tui/hooks/keyboard-hints-utils.d.ts +26 -0
  148. package/dist/ui/tui/hooks/keyboard-hints-utils.js +69 -0
  149. package/dist/ui/tui/hooks/useKeyBindings.d.ts +14 -0
  150. package/dist/ui/tui/hooks/useKeyBindings.js +44 -0
  151. package/dist/ui/tui/hooks/useKeyboardHints.d.ts +13 -0
  152. package/dist/ui/tui/hooks/useKeyboardHints.js +38 -0
  153. package/dist/ui/tui/hooks/useStdoutDimensions.d.ts +8 -0
  154. package/dist/ui/tui/hooks/useStdoutDimensions.js +28 -0
  155. package/dist/ui/tui/primitives/BlinkingLabel.d.ts +19 -0
  156. package/dist/ui/tui/primitives/BlinkingLabel.js +25 -0
  157. package/dist/ui/tui/primitives/ConfirmPrompt.d.ts +16 -0
  158. package/dist/ui/tui/primitives/ConfirmPrompt.js +36 -0
  159. package/dist/ui/tui/primitives/KeyboardHintsBar.d.ts +2 -0
  160. package/dist/ui/tui/primitives/KeyboardHintsBar.js +8 -0
  161. package/dist/ui/tui/primitives/PickerMenu.d.ts +38 -0
  162. package/dist/ui/tui/primitives/PickerMenu.js +162 -0
  163. package/dist/ui/tui/primitives/PromptLabel.d.ts +6 -0
  164. package/dist/ui/tui/primitives/PromptLabel.js +6 -0
  165. package/dist/ui/tui/primitives/ScreenContainer.d.ts +39 -0
  166. package/dist/ui/tui/primitives/ScreenContainer.js +39 -0
  167. package/dist/ui/tui/primitives/Spinner.d.ts +7 -0
  168. package/dist/ui/tui/primitives/Spinner.js +18 -0
  169. package/dist/ui/tui/screens/DashScreen.d.ts +6 -0
  170. package/dist/ui/tui/screens/DashScreen.js +37 -0
  171. package/dist/ui/tui/screens/DebugScreen.d.ts +6 -0
  172. package/dist/ui/tui/screens/DebugScreen.js +48 -0
  173. package/dist/ui/tui/screens/EnvScreen.d.ts +6 -0
  174. package/dist/ui/tui/screens/EnvScreen.js +192 -0
  175. package/dist/ui/tui/screens/InitScreen.d.ts +9 -0
  176. package/dist/ui/tui/screens/InitScreen.js +102 -0
  177. package/dist/ui/tui/screens/MenuScreen.d.ts +7 -0
  178. package/dist/ui/tui/screens/MenuScreen.js +84 -0
  179. package/dist/ui/tui/start-tui.d.ts +11 -0
  180. package/dist/ui/tui/start-tui.js +72 -0
  181. package/dist/ui/tui/steps/AccountStep.d.ts +8 -0
  182. package/dist/ui/tui/steps/AccountStep.js +42 -0
  183. package/dist/ui/tui/steps/ApiKeyStep.d.ts +8 -0
  184. package/dist/ui/tui/steps/ApiKeyStep.js +53 -0
  185. package/dist/ui/tui/steps/ClientsStep.d.ts +10 -0
  186. package/dist/ui/tui/steps/ClientsStep.js +52 -0
  187. package/dist/ui/tui/steps/CredentialKindStep.d.ts +7 -0
  188. package/dist/ui/tui/steps/CredentialKindStep.js +18 -0
  189. package/dist/ui/tui/steps/EnvironmentStep.d.ts +8 -0
  190. package/dist/ui/tui/steps/EnvironmentStep.js +38 -0
  191. package/dist/ui/tui/steps/LoginStep.d.ts +8 -0
  192. package/dist/ui/tui/steps/LoginStep.js +79 -0
  193. package/dist/ui/tui/steps/SkillsStep.d.ts +7 -0
  194. package/dist/ui/tui/steps/SkillsStep.js +7 -0
  195. package/dist/ui/tui/steps/SummaryStep.d.ts +10 -0
  196. package/dist/ui/tui/steps/SummaryStep.js +133 -0
  197. package/dist/ui/tui/steps/WritingStep.d.ts +10 -0
  198. package/dist/ui/tui/steps/WritingStep.js +101 -0
  199. package/package.json +62 -0
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import type { Action } from "../state.js";
3
+ interface Props {
4
+ dispatch: React.Dispatch<Action>;
5
+ }
6
+ export declare function LoginStep({ dispatch }: Props): React.ReactElement;
7
+ export {};
@@ -0,0 +1,56 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Spinner } from "@inkjs/ui";
3
+ import { Box, Text, useInput } from "ink";
4
+ import { useState } from "react";
5
+ import { authenticate } from "../../auth/oauth.js";
6
+ import { saveSession } from "../../auth/storage.js";
7
+ import { SectionTitle } from "../components/SectionTitle.js";
8
+ import { useAsyncEffect } from "../hooks/useAsyncEffect.js";
9
+ export function LoginStep({ dispatch }) {
10
+ const [started, setStarted] = useState(false);
11
+ const [message, setMessage] = useState("Opening browser to log in…");
12
+ const [errored, setErrored] = useState(null);
13
+ useInput((_input, key) => {
14
+ if (key.return)
15
+ setStarted(true);
16
+ }, { isActive: !started && !errored });
17
+ useAsyncEffect(async (signal) => {
18
+ if (!started)
19
+ return;
20
+ try {
21
+ const session = await authenticate({
22
+ onAuthUrlReady: (_url, port) => {
23
+ if (!signal.cancelled) {
24
+ setMessage(`Waiting for callback on http://127.0.0.1:${port}`);
25
+ }
26
+ },
27
+ });
28
+ await saveSession(session);
29
+ if (signal.cancelled)
30
+ return;
31
+ dispatch({
32
+ type: "LOGIN_OK",
33
+ session: {
34
+ email: session.email,
35
+ accessToken: session.access_token,
36
+ },
37
+ });
38
+ }
39
+ catch (err) {
40
+ if (signal.cancelled)
41
+ return;
42
+ setErrored(err instanceof Error ? err.message : String(err));
43
+ dispatch({
44
+ type: "ERROR",
45
+ message: err instanceof Error ? err.message : String(err),
46
+ });
47
+ }
48
+ }, [started]);
49
+ if (errored) {
50
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { color: "red", children: "\u2717 Login failed" }), _jsx(Text, { dimColor: true, children: errored })] }));
51
+ }
52
+ if (!started) {
53
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionTitle, { children: "Sign in to Stigg" }), _jsx(Text, { dimColor: true, children: "We'll open your browser to complete the OAuth flow." }), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { children: ["Press ", _jsx(Text, { color: "cyan", children: "enter" }), " to continue."] }) })] }));
54
+ }
55
+ return _jsx(Spinner, { label: message });
56
+ }
@@ -0,0 +1,7 @@
1
+ import React from "react";
2
+ import type { Action } from "../state.js";
3
+ interface Props {
4
+ dispatch: React.Dispatch<Action>;
5
+ }
6
+ export declare function SkillsStep({ dispatch }: Props): React.ReactElement;
7
+ export {};
@@ -0,0 +1,7 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ConfirmInput } from "@inkjs/ui";
3
+ import { Box, Text } from "ink";
4
+ import { SectionTitle } from "../components/SectionTitle.js";
5
+ export function SkillsStep({ dispatch }) {
6
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionTitle, { children: "Install Stigg skills for Claude Code?" }), _jsx(Text, { dimColor: true, children: "Clones stiggio/skills into ~/.claude/plugins/marketplaces/ \u2014 works as a Claude Code marketplace plugin." }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Install? " }), _jsx(ConfirmInput, { onConfirm: () => dispatch({ type: "SKILLS_CHOICE", install: true }), onCancel: () => dispatch({ type: "SKILLS_CHOICE", install: false }) })] })] }));
7
+ }
@@ -0,0 +1,8 @@
1
+ import React from "react";
2
+ import type { WizardState } from "../state.js";
3
+ interface Props {
4
+ state: WizardState;
5
+ onDone: () => void;
6
+ }
7
+ export declare function SummaryStep({ state, onDone }: Props): React.ReactElement;
8
+ export {};
@@ -0,0 +1,41 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text, useInput } from "ink";
3
+ import { ContextRow } from "../components/ContextRow.js";
4
+ import { SectionTitle } from "../components/SectionTitle.js";
5
+ function credentialLabel(state) {
6
+ if (!state.credential)
7
+ return "?";
8
+ if (state.credential.kind === "scoped") {
9
+ const name = state.selectedKey?.name ?? "scoped";
10
+ const last4 = state.selectedKey?.lastFour ?? "????";
11
+ return `API key ${name} (sk_…${last4})`;
12
+ }
13
+ return `User JWT (broad access, env: ${state.environment?.name ?? "?"})`;
14
+ }
15
+ export function SummaryStep({ state, onDone }) {
16
+ useInput(() => onDone());
17
+ const entries = [
18
+ { label: "Account", value: state.session?.email ?? "?" },
19
+ { label: "Credential", value: credentialLabel(state) },
20
+ { label: "Environment", value: state.environment?.name ?? "?" },
21
+ ];
22
+ for (const w of state.writes) {
23
+ entries.push({
24
+ label: entries.find((e) => e.label === "Wrote to") ? "" : "Wrote to",
25
+ value: w.path,
26
+ hint: w.backedUpTo ? `backup: ${w.backedUpTo}` : undefined,
27
+ });
28
+ }
29
+ if (state.installedSkills) {
30
+ entries.push({
31
+ label: "Skills",
32
+ value: state.installedSkills.ok
33
+ ? `cloned to ${state.installedSkills.installedPath ?? "~/.claude/plugins/marketplaces/stiggio-skills"}`
34
+ : `skipped (${state.installedSkills.reason ?? "see logs"})`,
35
+ });
36
+ }
37
+ else if (state.installSkills === false) {
38
+ entries.push({ label: "Skills", value: "not installed (declined)" });
39
+ }
40
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionTitle, { children: "Setup complete" }), _jsx(ContextRow, { entries: entries }), _jsxs(Box, { flexDirection: "column", marginTop: 1, children: [_jsx(Text, { children: "Next:" }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "/stigg help" }), " ", "in Claude Code"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "@stigg" }), " ", "in Cursor (after restart)"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "cyberdeck dash" }), " ", "to open the Stigg dashboard"] }), _jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "cyberdeck debug" }), " ", "to stream audit + usage logs"] })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "press any key to return to menu" }) })] }));
41
+ }
@@ -0,0 +1,10 @@
1
+ import React from "react";
2
+ import type { ClientId, McpClientHandler } from "../../types.js";
3
+ import type { Action, WizardState } from "../state.js";
4
+ interface Props {
5
+ state: WizardState;
6
+ dispatch: React.Dispatch<Action>;
7
+ handlers: Record<ClientId, McpClientHandler>;
8
+ }
9
+ export declare function WritingStep({ state, dispatch, handlers, }: Props): React.ReactElement;
10
+ export {};
@@ -0,0 +1,96 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { ConfirmInput, Spinner } from "@inkjs/ui";
3
+ import { Box, Text } from "ink";
4
+ import { useEffect, useState } from "react";
5
+ import { installStiggSkills } from "../../skills/install.js";
6
+ import { SectionTitle } from "../components/SectionTitle.js";
7
+ import { useAsyncEffect } from "../hooks/useAsyncEffect.js";
8
+ export function WritingStep({ state, dispatch, handlers, }) {
9
+ const [phase, setPhase] = useState({ kind: "init" });
10
+ const [currentClient, setCurrentClient] = useState(null);
11
+ const [skipped, setSkipped] = useState([]);
12
+ useAsyncEffect(async (signal) => {
13
+ if (phase.kind !== "init")
14
+ return;
15
+ const clients = state.selectedClients ?? [];
16
+ const existing = [];
17
+ for (const c of clients) {
18
+ if (await handlers[c].hasExistingEntry())
19
+ existing.push(c);
20
+ }
21
+ if (signal.cancelled)
22
+ return;
23
+ if (existing.length > 0) {
24
+ setPhase({ kind: "needs-confirm", existing });
25
+ }
26
+ else {
27
+ setPhase({ kind: "writing" });
28
+ }
29
+ }, [phase.kind], (err) => dispatch({
30
+ type: "ERROR",
31
+ message: err instanceof Error ? err.message : String(err),
32
+ }));
33
+ useAsyncEffect(async (signal) => {
34
+ if (phase.kind !== "writing")
35
+ return;
36
+ const clients = (state.selectedClients ?? []).filter((c) => !skipped.includes(c));
37
+ for (const c of clients) {
38
+ if (signal.cancelled)
39
+ return;
40
+ setCurrentClient(c);
41
+ try {
42
+ const result = await handlers[c].write(state.credential);
43
+ const decorated = { ...result, client: c };
44
+ dispatch({ type: "WRITE_OK", result: decorated });
45
+ }
46
+ catch (err) {
47
+ dispatch({
48
+ type: "ERROR",
49
+ message: err instanceof Error ? err.message : String(err),
50
+ });
51
+ return;
52
+ }
53
+ }
54
+ setCurrentClient(null);
55
+ if (signal.cancelled)
56
+ return;
57
+ if (state.installSkills) {
58
+ setPhase({ kind: "skills" });
59
+ }
60
+ else {
61
+ setPhase({ kind: "advancing" });
62
+ }
63
+ }, [phase.kind]);
64
+ useAsyncEffect(async (signal) => {
65
+ if (phase.kind !== "skills")
66
+ return;
67
+ const result = await installStiggSkills();
68
+ if (signal.cancelled)
69
+ return;
70
+ dispatch({ type: "SKILLS_INSTALL_OK", result });
71
+ }, [phase.kind], (err) => dispatch({
72
+ type: "ERROR",
73
+ message: err instanceof Error ? err.message : String(err),
74
+ }));
75
+ useEffect(() => {
76
+ if (phase.kind === "advancing") {
77
+ dispatch({ type: "ADVANCE" });
78
+ }
79
+ }, [phase.kind, dispatch]);
80
+ if (phase.kind === "needs-confirm") {
81
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(SectionTitle, { children: "Existing stigg entry found" }), _jsx(Text, { children: "The following clients already have a stigg MCP entry:" }), _jsx(Box, { flexDirection: "column", marginY: 1, children: phase.existing.map((c) => (_jsxs(Text, { children: [" ", _jsx(Text, { color: "cyan", children: "\u2022" }), " ", handlers[c].label] }, c))) }), _jsx(Text, { children: "Overwrite? Existing entries will be backed up." }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Overwrite? " }), _jsx(ConfirmInput, { onConfirm: () => setPhase({ kind: "writing" }), onCancel: () => {
82
+ setSkipped(phase.existing);
83
+ setPhase({ kind: "writing" });
84
+ } })] }), skipped.length > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "yellow", children: ["Will skip: ", skipped.map((c) => handlers[c].label).join(", ")] }) }))] }));
85
+ }
86
+ if (phase.kind === "writing") {
87
+ const label = currentClient
88
+ ? `Writing ${handlers[currentClient].label}…`
89
+ : "Writing config files…";
90
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Spinner, { label: label }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: state.writes.map((w, i) => (_jsxs(Text, { color: "green", children: [" ✓ ", w.path, w.backedUpTo ? (_jsxs(Text, { dimColor: true, children: [" (backup: ", w.backedUpTo, ")"] })) : null] }, i))) })] }));
91
+ }
92
+ if (phase.kind === "skills") {
93
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Spinner, { label: "Cloning stiggio/skills\u2026" }), _jsx(Box, { flexDirection: "column", marginTop: 1, children: state.writes.map((w, i) => (_jsxs(Text, { color: "green", children: [" ✓ ", w.path] }, i))) })] }));
94
+ }
95
+ return _jsx(Spinner, { label: "Preparing\u2026" });
96
+ }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Stigg brand palette — dark background, electric lime accent.
3
+ * Matches the May-2026 website theme.
4
+ */
5
+ export declare const BRAND_LIME: {
6
+ readonly brightest: "#c8ff3c";
7
+ readonly primary: "#9eff00";
8
+ readonly mid: "#7dd83a";
9
+ readonly forest: "#4a9e1f";
10
+ readonly deep: "#235a14";
11
+ };
12
+ export declare const COLORS: {
13
+ readonly primary: "#9eff00";
14
+ readonly accent: "cyan";
15
+ readonly success: "#9eff00";
16
+ readonly warning: "yellow";
17
+ readonly error: "#ff4d4d";
18
+ readonly muted: "gray";
19
+ };
20
+ export declare const GLYPHS: {
21
+ readonly check: "✓";
22
+ readonly cross: "✗";
23
+ readonly bullet: "·";
24
+ readonly arrow: "▸";
25
+ readonly filled: "■";
26
+ readonly empty: "□";
27
+ readonly dot: "●";
28
+ readonly ring: "○";
29
+ };
30
+ /** Shell palette used by the new TUI (TitleBar, PickerMenu, hints bar). */
31
+ export declare const Colors: {
32
+ readonly primary: "#c8ff3c";
33
+ readonly accent: "#9eff00";
34
+ readonly titleColor: "#0a0a0a";
35
+ readonly success: "#9eff00";
36
+ readonly error: "#ff4d4d";
37
+ readonly muted: "gray";
38
+ };
39
+ /** RGB triplet painted into the terminal's alt-screen background via ANSI 48;2;r;g;b. */
40
+ export declare const SHELL_BG_RGB: readonly [10, 10, 10];
41
+ export declare const Icons: {
42
+ readonly triangleSmallRight: "▸";
43
+ readonly squareFilled: "◼";
44
+ readonly squareOpen: "◻";
45
+ readonly check: "✓";
46
+ readonly cross: "✗";
47
+ readonly bullet: "·";
48
+ };
49
+ export declare const MIN_SHELL_WIDTH = 80;
50
+ export declare const MAX_SHELL_WIDTH = 120;
51
+ export declare const MIN_CARD_WIDTH = 78;
52
+ export declare function shouldRenderBorder(): boolean;
53
+ export declare function shouldRenderAnimation(): boolean;
@@ -0,0 +1,66 @@
1
+ /**
2
+ * Stigg brand palette — dark background, electric lime accent.
3
+ * Matches the May-2026 website theme.
4
+ */
5
+ export const BRAND_LIME = {
6
+ brightest: '#c8ff3c',
7
+ primary: '#9eff00',
8
+ mid: '#7dd83a',
9
+ forest: '#4a9e1f',
10
+ deep: '#235a14',
11
+ };
12
+ export const COLORS = {
13
+ primary: BRAND_LIME.primary,
14
+ accent: 'cyan',
15
+ success: BRAND_LIME.primary,
16
+ warning: 'yellow',
17
+ error: '#ff4d4d',
18
+ muted: 'gray',
19
+ };
20
+ export const GLYPHS = {
21
+ check: '✓',
22
+ cross: '✗',
23
+ bullet: '·',
24
+ arrow: '▸',
25
+ filled: '■',
26
+ empty: '□',
27
+ dot: '●',
28
+ ring: '○',
29
+ };
30
+ /** Shell palette used by the new TUI (TitleBar, PickerMenu, hints bar). */
31
+ export const Colors = {
32
+ primary: BRAND_LIME.brightest,
33
+ accent: BRAND_LIME.primary,
34
+ titleColor: '#0a0a0a',
35
+ success: BRAND_LIME.primary,
36
+ error: '#ff4d4d',
37
+ muted: 'gray',
38
+ };
39
+ /** RGB triplet painted into the terminal's alt-screen background via ANSI 48;2;r;g;b. */
40
+ export const SHELL_BG_RGB = [10, 10, 10];
41
+ export const Icons = {
42
+ triangleSmallRight: '▸',
43
+ squareFilled: '◼',
44
+ squareOpen: '◻',
45
+ check: '✓',
46
+ cross: '✗',
47
+ bullet: '·',
48
+ };
49
+ export const MIN_SHELL_WIDTH = 80;
50
+ export const MAX_SHELL_WIDTH = 120;
51
+ export const MIN_CARD_WIDTH = 78;
52
+ export function shouldRenderBorder() {
53
+ const cols = process.stdout.columns ?? 80;
54
+ return cols >= MIN_CARD_WIDTH;
55
+ }
56
+ export function shouldRenderAnimation() {
57
+ if (!process.stdout.isTTY)
58
+ return false;
59
+ if (process.env.CI === 'true' || process.env.CI === '1')
60
+ return false;
61
+ if (process.env.NO_COLOR)
62
+ return false;
63
+ if (process.env.TERM === 'dumb')
64
+ return false;
65
+ return shouldRenderBorder();
66
+ }
@@ -0,0 +1,10 @@
1
+ import React from 'react';
2
+ import type { LaunchRequest } from '../../launch/agent.js';
3
+ export type AppScreen = 'menu' | 'init' | 'dash' | 'env';
4
+ interface AppProps {
5
+ initialScreen?: AppScreen;
6
+ callbackPort?: number;
7
+ onLaunchAgent?: (req: LaunchRequest) => void;
8
+ }
9
+ export declare function App(props: AppProps): React.ReactElement;
10
+ export {};
@@ -0,0 +1,51 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useApp } from 'ink';
3
+ import { useState } from 'react';
4
+ import { Logo } from '../intro/LogoView.js';
5
+ import { KeyMatch } from './hooks/keyboard-hints-utils.js';
6
+ import { DashScreen } from './screens/DashScreen.js';
7
+ import { EnvScreen } from './screens/EnvScreen.js';
8
+ import { InitScreen } from './screens/InitScreen.js';
9
+ import { MenuScreen } from './screens/MenuScreen.js';
10
+ import { useKeyBindings } from './hooks/useKeyBindings.js';
11
+ import { ScreenContainer } from './primitives/ScreenContainer.js';
12
+ function AppInner({ initialScreen = 'menu', callbackPort, onLaunchAgent, }) {
13
+ const [screen, setScreen] = useState(initialScreen);
14
+ const { exit } = useApp();
15
+ useKeyBindings('app', [
16
+ {
17
+ match: KeyMatch.Escape,
18
+ label: 'esc',
19
+ action: screen === 'menu' ? 'quit' : 'menu',
20
+ priority: 30,
21
+ handler: () => {
22
+ if (screen === 'menu') {
23
+ exit();
24
+ }
25
+ else {
26
+ setScreen('menu');
27
+ }
28
+ },
29
+ },
30
+ {
31
+ match: '\x03', // ctrl+c — handled by Ink's exitOnCtrlC, this is a hint-only entry
32
+ label: 'ctrl+c',
33
+ action: 'quit',
34
+ priority: 99,
35
+ handler: () => exit(),
36
+ },
37
+ ]);
38
+ return (_jsxs(ScreenContainer, { chrome: screen === 'menu' ? (cols) => _jsx(Logo, { containerWidth: cols }) : undefined, children: [screen === 'menu' && (_jsx(MenuScreen, { onPick: (c) => {
39
+ if (c === 'exit') {
40
+ exit();
41
+ return;
42
+ }
43
+ setScreen(c);
44
+ } })), screen === 'init' && (_jsx(InitScreen, { onDone: () => setScreen('menu'), onLaunch: (req) => {
45
+ onLaunchAgent?.(req);
46
+ exit();
47
+ }, callbackPort: callbackPort })), screen === 'env' && _jsx(EnvScreen, { onDone: () => setScreen('menu') }), screen === 'dash' && _jsx(DashScreen, { onDone: () => setScreen('menu') })] }));
48
+ }
49
+ export function App(props) {
50
+ return _jsx(AppInner, { ...props });
51
+ }
@@ -0,0 +1,11 @@
1
+ import React from 'react';
2
+ export interface ContextEntry {
3
+ label: string;
4
+ value: string;
5
+ hint?: string;
6
+ }
7
+ interface ContextStripProps {
8
+ entries: ContextEntry[];
9
+ }
10
+ export declare function ContextStrip({ entries }: ContextStripProps): React.ReactElement | null;
11
+ export {};
@@ -0,0 +1,8 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ export function ContextStrip({ entries }) {
4
+ if (entries.length === 0)
5
+ return null;
6
+ const labelWidth = Math.max(...entries.map((e) => (e.label ? e.label.length : 0)));
7
+ return (_jsx(Box, { flexDirection: "column", children: entries.map((e, i) => (_jsxs(Box, { flexDirection: "row", children: [_jsx(Box, { width: labelWidth + 2, children: _jsx(Text, { dimColor: true, children: e.label }) }), _jsx(Text, { children: e.value }), e.hint && (_jsxs(Text, { dimColor: true, children: [' ', e.hint] }))] }, `${e.label}-${i}`))) }));
8
+ }
@@ -0,0 +1,6 @@
1
+ import React from 'react';
2
+ interface TitleBarProps {
3
+ width: number;
4
+ }
5
+ export declare function TitleBar({ width }: TitleBarProps): React.ReactElement;
6
+ export {};
@@ -0,0 +1,18 @@
1
+ import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { Colors } from '../../theme.js';
4
+ const VERSION = '0.0.1-alpha';
5
+ const FULL_TITLE = ` stigg · terminal v${VERSION}`;
6
+ const SHORT_TITLE = ` terminal v${VERSION}`;
7
+ const FEEDBACK = 'github.com/stiggio/terminal ';
8
+ const FEEDBACK_SHORT = 'stigg.io ';
9
+ export function TitleBar({ width }) {
10
+ let title = FULL_TITLE;
11
+ let feedback = FEEDBACK;
12
+ if (title.length + feedback.length > width)
13
+ feedback = FEEDBACK_SHORT;
14
+ if (title.length + feedback.length > width)
15
+ title = SHORT_TITLE;
16
+ const gap = Math.max(0, width - title.length - feedback.length);
17
+ return (_jsx(Box, { width: width, overflow: "hidden", children: _jsxs(Text, { backgroundColor: Colors.accent, color: Colors.titleColor, children: [title, ' '.repeat(gap), feedback] }) }));
18
+ }
@@ -0,0 +1,7 @@
1
+ import React from 'react';
2
+ import type { WizardState } from '../../state.js';
3
+ interface Props {
4
+ state: WizardState;
5
+ }
6
+ export declare function WizardChecklist({ state }: Props): React.ReactElement;
7
+ export {};
@@ -0,0 +1,69 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Box, Text } from 'ink';
3
+ import { Colors, Icons } from '../../theme.js';
4
+ const STEPS = [
5
+ {
6
+ id: 'login',
7
+ meta: { label: 'Sign in', description: 'Authenticate with Stigg via browser (OAuth)' },
8
+ },
9
+ {
10
+ id: 'account',
11
+ meta: { label: 'Account', description: 'Pick which Stigg account to configure' },
12
+ },
13
+ {
14
+ id: 'environment',
15
+ meta: {
16
+ label: 'Environment',
17
+ description: 'Pick a target environment (dev / staging / prod)',
18
+ },
19
+ },
20
+ {
21
+ id: 'apiKey',
22
+ meta: { label: 'API key', description: 'Select an API key for that environment' },
23
+ },
24
+ {
25
+ id: 'clients',
26
+ meta: {
27
+ label: 'Clients',
28
+ description: 'Pick AI clients to configure (Claude Code, Cursor, …)',
29
+ },
30
+ },
31
+ {
32
+ id: 'skills',
33
+ meta: { label: 'Skills', description: "Optionally install Stigg's agent skills" },
34
+ },
35
+ {
36
+ id: 'writing',
37
+ meta: { label: 'Configure', description: 'Write MCP entries to each selected client' },
38
+ },
39
+ {
40
+ id: 'summary',
41
+ meta: { label: 'Done', description: 'Review the setup' },
42
+ },
43
+ ];
44
+ const MARKER_WIDTH = 2;
45
+ const NUMBER_WIDTH = 4;
46
+ const LABEL_WIDTH = 14;
47
+ function classify(state, step) {
48
+ if (state.history.includes(step))
49
+ return 'done';
50
+ if (state.step === step) {
51
+ return state.step === 'summary' ? 'done' : 'current';
52
+ }
53
+ if (step === 'account' && state.accounts != null && state.accounts.length === 1) {
54
+ return 'skipped';
55
+ }
56
+ if (step === 'skills' && state.selectedClients != null && state.selectedClients.length === 0) {
57
+ return 'skipped';
58
+ }
59
+ return 'pending';
60
+ }
61
+ export function WizardChecklist({ state }) {
62
+ return (_jsx(Box, { flexDirection: "column", children: STEPS.map(({ id, meta }, i) => {
63
+ const status = classify(state, id);
64
+ const isDone = status === 'done';
65
+ const labelColor = isDone ? Colors.success : undefined;
66
+ const dim = !isDone;
67
+ return (_jsxs(Box, { children: [_jsx(Box, { width: MARKER_WIDTH, children: _jsx(Text, { color: labelColor, children: isDone ? Icons.check : ' ' }) }), _jsx(Box, { width: NUMBER_WIDTH, children: _jsxs(Text, { color: labelColor, dimColor: dim, children: [i + 1, "."] }) }), _jsx(Box, { width: LABEL_WIDTH, children: _jsx(Text, { color: labelColor, dimColor: dim, children: meta.label }) }), _jsx(Text, { dimColor: true, children: meta.description }), status === 'skipped' && _jsx(Text, { dimColor: true, children: ' (skipped)' })] }, id));
68
+ }) }));
69
+ }
@@ -0,0 +1,26 @@
1
+ import type { Key } from 'ink';
2
+ export declare enum KeyMatch {
3
+ UpArrow = 0,
4
+ DownArrow = 1,
5
+ LeftArrow = 2,
6
+ RightArrow = 3,
7
+ Return = 4,
8
+ Escape = 5,
9
+ Space = 6,
10
+ Tab = 7,
11
+ AnyChar = 8
12
+ }
13
+ export type KeyMatchOrChar = KeyMatch | string;
14
+ export interface KeyHint {
15
+ label: string;
16
+ action: string;
17
+ priority?: number;
18
+ }
19
+ export interface KeyBinding extends KeyHint {
20
+ match: KeyMatchOrChar | KeyMatchOrChar[];
21
+ handler: (input: string, key: Key) => void;
22
+ }
23
+ export declare const DEFAULT_HINT_PRIORITY = 50;
24
+ export declare function defaultPriorityFor(m: KeyMatchOrChar): number;
25
+ export declare function matchesKey(m: KeyMatchOrChar, input: string, key: Key): boolean;
26
+ export declare function dedupeAndSortHints(bag: Map<string, KeyHint[]>): KeyHint[];
@@ -0,0 +1,69 @@
1
+ export var KeyMatch;
2
+ (function (KeyMatch) {
3
+ KeyMatch[KeyMatch["UpArrow"] = 0] = "UpArrow";
4
+ KeyMatch[KeyMatch["DownArrow"] = 1] = "DownArrow";
5
+ KeyMatch[KeyMatch["LeftArrow"] = 2] = "LeftArrow";
6
+ KeyMatch[KeyMatch["RightArrow"] = 3] = "RightArrow";
7
+ KeyMatch[KeyMatch["Return"] = 4] = "Return";
8
+ KeyMatch[KeyMatch["Escape"] = 5] = "Escape";
9
+ KeyMatch[KeyMatch["Space"] = 6] = "Space";
10
+ KeyMatch[KeyMatch["Tab"] = 7] = "Tab";
11
+ KeyMatch[KeyMatch["AnyChar"] = 8] = "AnyChar";
12
+ })(KeyMatch || (KeyMatch = {}));
13
+ export const DEFAULT_HINT_PRIORITY = 50;
14
+ const PRIORITY_BY_MATCH = {
15
+ [KeyMatch.UpArrow]: 0,
16
+ [KeyMatch.DownArrow]: 0,
17
+ [KeyMatch.LeftArrow]: 1,
18
+ [KeyMatch.RightArrow]: 1,
19
+ [KeyMatch.Space]: 10,
20
+ [KeyMatch.Tab]: 15,
21
+ [KeyMatch.Escape]: 20,
22
+ [KeyMatch.Return]: 21,
23
+ };
24
+ export function defaultPriorityFor(m) {
25
+ if (typeof m === 'string')
26
+ return DEFAULT_HINT_PRIORITY;
27
+ return PRIORITY_BY_MATCH[m] ?? DEFAULT_HINT_PRIORITY;
28
+ }
29
+ export function matchesKey(m, input, key) {
30
+ if (typeof m === 'string')
31
+ return input === m;
32
+ switch (m) {
33
+ case KeyMatch.UpArrow:
34
+ return key.upArrow;
35
+ case KeyMatch.DownArrow:
36
+ return key.downArrow;
37
+ case KeyMatch.LeftArrow:
38
+ return key.leftArrow;
39
+ case KeyMatch.RightArrow:
40
+ return key.rightArrow;
41
+ case KeyMatch.Return:
42
+ return key.return;
43
+ case KeyMatch.Escape:
44
+ return key.escape;
45
+ case KeyMatch.Space:
46
+ return input === ' ';
47
+ case KeyMatch.Tab:
48
+ return key.tab;
49
+ case KeyMatch.AnyChar:
50
+ return true;
51
+ }
52
+ }
53
+ export function dedupeAndSortHints(bag) {
54
+ const byKey = new Map();
55
+ for (const hints of bag.values()) {
56
+ for (const h of hints) {
57
+ const k = `${h.label}:${h.action}`;
58
+ // first registration wins for label/action, but keep smallest priority
59
+ const existing = byKey.get(k);
60
+ if (!existing) {
61
+ byKey.set(k, h);
62
+ }
63
+ else if ((h.priority ?? DEFAULT_HINT_PRIORITY) < (existing.priority ?? DEFAULT_HINT_PRIORITY)) {
64
+ byKey.set(k, h);
65
+ }
66
+ }
67
+ }
68
+ return Array.from(byKey.values()).sort((a, b) => (a.priority ?? DEFAULT_HINT_PRIORITY) - (b.priority ?? DEFAULT_HINT_PRIORITY));
69
+ }