skilluse 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.
Files changed (68) hide show
  1. package/README.md +100 -0
  2. package/dist/app.d.ts +6 -0
  3. package/dist/app.js +6 -0
  4. package/dist/cli.d.ts +3 -0
  5. package/dist/cli.js +167 -0
  6. package/dist/commands/demo.d.ts +14 -0
  7. package/dist/commands/demo.js +46 -0
  8. package/dist/commands/index.d.ts +8 -0
  9. package/dist/commands/index.js +77 -0
  10. package/dist/commands/list.d.ts +14 -0
  11. package/dist/commands/list.js +54 -0
  12. package/dist/commands/login.d.ts +14 -0
  13. package/dist/commands/login.js +153 -0
  14. package/dist/commands/logout.d.ts +8 -0
  15. package/dist/commands/logout.js +47 -0
  16. package/dist/commands/repo/add.d.ts +22 -0
  17. package/dist/commands/repo/add.js +139 -0
  18. package/dist/commands/repo/edit.d.ts +19 -0
  19. package/dist/commands/repo/edit.js +117 -0
  20. package/dist/commands/repo/index.d.ts +8 -0
  21. package/dist/commands/repo/index.js +47 -0
  22. package/dist/commands/repo/list.d.ts +8 -0
  23. package/dist/commands/repo/list.js +47 -0
  24. package/dist/commands/repo/remove.d.ts +16 -0
  25. package/dist/commands/repo/remove.js +83 -0
  26. package/dist/commands/repo/sync.d.ts +10 -0
  27. package/dist/commands/repo/sync.js +78 -0
  28. package/dist/commands/repo/use.d.ts +10 -0
  29. package/dist/commands/repo/use.js +56 -0
  30. package/dist/commands/repos.d.ts +11 -0
  31. package/dist/commands/repos.js +50 -0
  32. package/dist/commands/search.d.ts +16 -0
  33. package/dist/commands/search.js +199 -0
  34. package/dist/commands/skills.d.ts +11 -0
  35. package/dist/commands/skills.js +43 -0
  36. package/dist/commands/whoami.d.ts +8 -0
  37. package/dist/commands/whoami.js +69 -0
  38. package/dist/components/CLIError.d.ts +27 -0
  39. package/dist/components/CLIError.js +24 -0
  40. package/dist/components/ProgressBar.d.ts +7 -0
  41. package/dist/components/ProgressBar.js +9 -0
  42. package/dist/components/Select.d.ts +11 -0
  43. package/dist/components/Select.js +6 -0
  44. package/dist/components/Spinner.d.ts +6 -0
  45. package/dist/components/Spinner.js +7 -0
  46. package/dist/components/StatusMessage.d.ts +9 -0
  47. package/dist/components/StatusMessage.js +13 -0
  48. package/dist/components/Table.d.ts +9 -0
  49. package/dist/components/Table.js +27 -0
  50. package/dist/components/TextInput.d.ts +9 -0
  51. package/dist/components/TextInput.js +6 -0
  52. package/dist/components/index.d.ts +8 -0
  53. package/dist/components/index.js +8 -0
  54. package/dist/index.d.ts +3 -0
  55. package/dist/index.js +43815 -0
  56. package/dist/services/credentials.d.ts +69 -0
  57. package/dist/services/credentials.js +216 -0
  58. package/dist/services/index.d.ts +6 -0
  59. package/dist/services/index.js +10 -0
  60. package/dist/services/oauth.d.ts +106 -0
  61. package/dist/services/oauth.js +208 -0
  62. package/dist/services/paths.d.ts +19 -0
  63. package/dist/services/paths.js +21 -0
  64. package/dist/services/store.d.ts +64 -0
  65. package/dist/services/store.js +107 -0
  66. package/dist/services/update.d.ts +20 -0
  67. package/dist/services/update.js +93 -0
  68. package/package.json +70 -0
@@ -0,0 +1,153 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { Box, Text, useApp } from "ink";
4
+ import { z } from "zod";
5
+ import { Spinner, StatusMessage } from "../components/index.js";
6
+ import { requestDeviceCode, pollUntilComplete, openBrowser, getCredentials, setCredentials, getUserInstallations, setInstallations, setDefaultInstallation, } from "../services/index.js";
7
+ // GitHub App Client ID for Skilluse CLI
8
+ // These are public values, safe to embed in client code
9
+ // Override with SKILLUSE_GITHUB_CLIENT_ID env var for development
10
+ const GITHUB_CLIENT_ID = process.env.SKILLUSE_GITHUB_CLIENT_ID || "Iv23liOOBSjdH2IRT6W2";
11
+ export const options = z.object({
12
+ force: z
13
+ .boolean()
14
+ .default(false)
15
+ .describe("Force re-authentication even if already logged in"),
16
+ });
17
+ export default function Login({ options: opts }) {
18
+ const { exit } = useApp();
19
+ const [state, setState] = useState({ phase: "checking" });
20
+ useEffect(() => {
21
+ async function runLogin() {
22
+ // Check if already logged in
23
+ if (!opts.force) {
24
+ const existing = await getCredentials();
25
+ if (existing) {
26
+ setState({ phase: "already_logged_in", username: existing.user });
27
+ exit();
28
+ return;
29
+ }
30
+ }
31
+ // Request device code
32
+ setState({ phase: "requesting_code" });
33
+ let deviceCode;
34
+ try {
35
+ deviceCode = await requestDeviceCode(GITHUB_CLIENT_ID, "repo,user");
36
+ }
37
+ catch (err) {
38
+ setState({
39
+ phase: "error",
40
+ message: err instanceof Error ? err.message : "Failed to start authentication",
41
+ });
42
+ exit();
43
+ return;
44
+ }
45
+ // Show user code and open browser
46
+ setState({
47
+ phase: "waiting_for_auth",
48
+ userCode: deviceCode.user_code,
49
+ verificationUri: deviceCode.verification_uri,
50
+ });
51
+ // Try to open browser automatically
52
+ try {
53
+ await openBrowser(deviceCode.verification_uri);
54
+ }
55
+ catch {
56
+ // Browser opening failed, user will need to open manually
57
+ }
58
+ // Poll for access token
59
+ const result = await pollUntilComplete(GITHUB_CLIENT_ID, deviceCode.device_code, deviceCode.expires_in, deviceCode.interval);
60
+ if (!result.success) {
61
+ const messages = {
62
+ expired: "Authentication timed out. Please try again.",
63
+ denied: "Authentication was denied.",
64
+ error: result.message || "Authentication failed.",
65
+ };
66
+ setState({ phase: "error", message: messages[result.reason] });
67
+ exit();
68
+ return;
69
+ }
70
+ // Fetch user info from GitHub
71
+ let username;
72
+ const accessToken = result.token.access_token;
73
+ try {
74
+ const userResponse = await fetch("https://api.github.com/user", {
75
+ headers: {
76
+ Authorization: `Bearer ${accessToken}`,
77
+ Accept: "application/vnd.github+json",
78
+ },
79
+ });
80
+ if (!userResponse.ok) {
81
+ throw new Error("Failed to fetch user info");
82
+ }
83
+ const userData = (await userResponse.json());
84
+ username = userData.login;
85
+ }
86
+ catch {
87
+ setState({ phase: "error", message: "Failed to fetch user info from GitHub" });
88
+ exit();
89
+ return;
90
+ }
91
+ // Store credentials
92
+ await setCredentials(accessToken, username);
93
+ // Fetch GitHub App installations
94
+ setState({ phase: "fetching_installations" });
95
+ let installations;
96
+ try {
97
+ installations = await getUserInstallations(accessToken);
98
+ }
99
+ catch (err) {
100
+ // If we can't fetch installations, show prompt to install
101
+ setState({
102
+ phase: "no_installation",
103
+ installUrl: "https://github.com/apps/skilluse/installations/new",
104
+ });
105
+ exit();
106
+ return;
107
+ }
108
+ if (installations.length === 0) {
109
+ // No installations, prompt user to install
110
+ setState({
111
+ phase: "no_installation",
112
+ installUrl: "https://github.com/apps/skilluse/installations/new",
113
+ });
114
+ exit();
115
+ return;
116
+ }
117
+ // Convert to StoredInstallation and save
118
+ const storedInstallations = installations.map((inst) => ({
119
+ id: inst.id,
120
+ account: inst.account.login,
121
+ accountType: inst.account.type,
122
+ repositorySelection: inst.repository_selection,
123
+ }));
124
+ await setInstallations(storedInstallations);
125
+ // If single installation, set as default
126
+ if (storedInstallations.length === 1) {
127
+ await setDefaultInstallation(storedInstallations[0].id);
128
+ }
129
+ setState({ phase: "success", username, installations: storedInstallations });
130
+ exit();
131
+ }
132
+ runLogin();
133
+ }, [opts.force, exit]);
134
+ switch (state.phase) {
135
+ case "checking":
136
+ return _jsx(Spinner, { text: "Checking authentication status..." });
137
+ case "already_logged_in":
138
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusMessage, { type: "success", children: ["Already logged in as ", state.username] }), _jsx(Text, { dimColor: true, children: "Use --force to re-authenticate" })] }));
139
+ case "requesting_code":
140
+ return _jsx(Spinner, { text: "Starting authentication..." });
141
+ case "waiting_for_auth":
142
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { children: "Open " }), _jsx(Text, { color: "cyan", bold: true, children: state.verificationUri }), _jsx(Text, { children: " and enter code:" })] }), _jsx(Box, { marginBottom: 1, children: _jsx(Text, { color: "yellow", bold: true, children: state.userCode }) }), _jsx(Spinner, { text: "Waiting for authentication..." })] }));
143
+ case "fetching_installations":
144
+ return _jsx(Spinner, { text: "Fetching GitHub App installations..." });
145
+ case "no_installation":
146
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusMessage, { type: "warning", children: "You need to install Skilluse CLI on your GitHub account" }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Open: " }), _jsx(Text, { color: "cyan", bold: true, children: state.installUrl })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Then run 'skilluse login' again." }) })] }));
147
+ case "success":
148
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusMessage, { type: "success", children: ["Logged in as ", state.username] }), state.installations.length > 0 && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [state.installations.length, " installation", state.installations.length > 1 ? "s" : "", " found:"] }), state.installations.map((inst) => (_jsxs(Text, { dimColor: true, children: ["\u2022 ", inst.account, " (", inst.accountType.toLowerCase(), ")"] }, inst.id)))] }))] }));
149
+ case "error":
150
+ return _jsx(StatusMessage, { type: "error", children: state.message });
151
+ }
152
+ }
153
+ //# sourceMappingURL=login.js.map
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ export declare const options: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
3
+ interface Props {
4
+ options: z.infer<typeof options>;
5
+ }
6
+ export default function Logout(_props: Props): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=logout.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { useApp } from "ink";
4
+ import { z } from "zod";
5
+ import { Spinner, StatusMessage } from "../components/index.js";
6
+ import { getCredentials, clearAllCredentials, clearInstallations, } from "../services/index.js";
7
+ export const options = z.object({});
8
+ export default function Logout(_props) {
9
+ const { exit } = useApp();
10
+ const [state, setState] = useState({ phase: "checking" });
11
+ useEffect(() => {
12
+ async function runLogout() {
13
+ // Check if logged in
14
+ const existing = await getCredentials();
15
+ if (!existing) {
16
+ setState({ phase: "not_logged_in" });
17
+ exit();
18
+ return;
19
+ }
20
+ // Clear all credentials (user token, token cache) and installations
21
+ try {
22
+ await clearAllCredentials();
23
+ clearInstallations();
24
+ setState({ phase: "success" });
25
+ }
26
+ catch (err) {
27
+ setState({
28
+ phase: "error",
29
+ message: err instanceof Error ? err.message : "Failed to clear credentials",
30
+ });
31
+ }
32
+ exit();
33
+ }
34
+ runLogout();
35
+ }, [exit]);
36
+ switch (state.phase) {
37
+ case "checking":
38
+ return _jsx(Spinner, { text: "Logging out..." });
39
+ case "not_logged_in":
40
+ return _jsx(StatusMessage, { type: "warning", children: "Not logged in" });
41
+ case "success":
42
+ return _jsx(StatusMessage, { type: "success", children: "Logged out successfully" });
43
+ case "error":
44
+ return _jsx(StatusMessage, { type: "error", children: state.message });
45
+ }
46
+ }
47
+ //# sourceMappingURL=logout.js.map
@@ -0,0 +1,22 @@
1
+ import { z } from "zod";
2
+ export declare const args: z.ZodTuple<[z.ZodString], null>;
3
+ export declare const options: z.ZodObject<{
4
+ path: z.ZodOptional<z.ZodString>;
5
+ branch: z.ZodDefault<z.ZodString>;
6
+ default: z.ZodDefault<z.ZodBoolean>;
7
+ }, "strip", z.ZodTypeAny, {
8
+ default: boolean;
9
+ branch: string;
10
+ path?: string | undefined;
11
+ }, {
12
+ path?: string | undefined;
13
+ default?: boolean | undefined;
14
+ branch?: string | undefined;
15
+ }>;
16
+ interface Props {
17
+ args: z.infer<typeof args>;
18
+ options: z.infer<typeof options>;
19
+ }
20
+ export default function RepoAdd({ args: [repoArg], options: opts }: Props): import("react/jsx-runtime").JSX.Element;
21
+ export {};
22
+ //# sourceMappingURL=add.d.ts.map
@@ -0,0 +1,139 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { Box, Text, useApp, useInput } from "ink";
4
+ import { z } from "zod";
5
+ import { Spinner, StatusMessage } from "../../components/index.js";
6
+ import { getCredentials, addRepo, setDefaultRepo, getConfig, } from "../../services/index.js";
7
+ export const args = z.tuple([z.string().describe("Repository in owner/repo format")]);
8
+ export const options = z.object({
9
+ path: z
10
+ .string()
11
+ .optional()
12
+ .describe("Skill path within the repo (e.g., skills/)"),
13
+ branch: z
14
+ .string()
15
+ .default("main")
16
+ .describe("Branch to use"),
17
+ default: z
18
+ .boolean()
19
+ .default(false)
20
+ .describe("Set as default repo"),
21
+ });
22
+ export default function RepoAdd({ args: [repoArg], options: opts }) {
23
+ const { exit } = useApp();
24
+ const [state, setState] = useState({ phase: "checking" });
25
+ useEffect(() => {
26
+ async function checkAndAdd() {
27
+ // Check if logged in
28
+ const credentials = await getCredentials();
29
+ if (!credentials) {
30
+ setState({ phase: "not_logged_in" });
31
+ exit();
32
+ return;
33
+ }
34
+ // Validate repo format
35
+ if (!repoArg.match(/^[a-zA-Z0-9_.-]+\/[a-zA-Z0-9_.-]+$/)) {
36
+ setState({ phase: "invalid_repo" });
37
+ exit();
38
+ return;
39
+ }
40
+ // Check if already exists
41
+ const config = getConfig();
42
+ if (config.repos.find((r) => r.repo === repoArg)) {
43
+ setState({ phase: "already_exists", repo: repoArg });
44
+ exit();
45
+ return;
46
+ }
47
+ // If path is provided via --path, save directly
48
+ if (opts.path !== undefined) {
49
+ setState({ phase: "saving", repo: repoArg });
50
+ const paths = opts.path ? [opts.path] : [];
51
+ addRepo({
52
+ repo: repoArg,
53
+ branch: opts.branch,
54
+ paths,
55
+ });
56
+ // Set as default if requested or if first repo
57
+ const isDefault = opts.default || config.repos.length === 0;
58
+ if (isDefault) {
59
+ setDefaultRepo(repoArg);
60
+ }
61
+ setState({
62
+ phase: "success",
63
+ repo: repoArg,
64
+ path: opts.path || "(all paths)",
65
+ isDefault,
66
+ });
67
+ exit();
68
+ return;
69
+ }
70
+ // Interactive mode: prompt for path
71
+ setState({ phase: "input_path", repo: repoArg, currentPath: "" });
72
+ }
73
+ checkAndAdd();
74
+ }, [repoArg, opts.path, opts.branch, opts.default, exit]);
75
+ // Handle input for path
76
+ useInput((input, key) => {
77
+ if (state.phase !== "input_path")
78
+ return;
79
+ if (key.return) {
80
+ // Save with current path
81
+ const config = getConfig();
82
+ const paths = state.currentPath ? [state.currentPath] : [];
83
+ addRepo({
84
+ repo: state.repo,
85
+ branch: opts.branch,
86
+ paths,
87
+ });
88
+ const isDefault = opts.default || config.repos.length === 0;
89
+ if (isDefault) {
90
+ setDefaultRepo(state.repo);
91
+ }
92
+ setState({
93
+ phase: "success",
94
+ repo: state.repo,
95
+ path: state.currentPath || "(all paths)",
96
+ isDefault,
97
+ });
98
+ exit();
99
+ return;
100
+ }
101
+ if (key.backspace || key.delete) {
102
+ setState({
103
+ ...state,
104
+ currentPath: state.currentPath.slice(0, -1),
105
+ });
106
+ return;
107
+ }
108
+ if (key.escape) {
109
+ exit();
110
+ return;
111
+ }
112
+ // Add character
113
+ if (input && !key.ctrl && !key.meta) {
114
+ setState({
115
+ ...state,
116
+ currentPath: state.currentPath + input,
117
+ });
118
+ }
119
+ }, { isActive: state.phase === "input_path" });
120
+ switch (state.phase) {
121
+ case "checking":
122
+ return _jsx(Spinner, { text: "Checking..." });
123
+ case "not_logged_in":
124
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusMessage, { type: "error", children: "Not authenticated" }), _jsx(Text, { dimColor: true, children: "Run 'skilluse login' to authenticate with GitHub" })] }));
125
+ case "invalid_repo":
126
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusMessage, { type: "error", children: "Invalid repository format" }), _jsx(Text, { dimColor: true, children: "Use the format: owner/repo" })] }));
127
+ case "already_exists":
128
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusMessage, { type: "warning", children: ["Repository ", state.repo, " is already configured"] }), _jsxs(Text, { dimColor: true, children: ["Use 'skilluse repo edit ", state.repo, "' to modify it."] })] }));
129
+ case "input_path":
130
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Adding repository: ", _jsx(Text, { color: "cyan", children: state.repo })] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "Skill path (leave empty for all): " }), _jsx(Text, { color: "green", children: state.currentPath }), _jsx(Text, { color: "gray", children: "\u2588" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Press Enter to confirm, Esc to cancel" }) })] }));
131
+ case "saving":
132
+ return _jsx(Spinner, { text: `Adding ${state.repo}...` });
133
+ case "success":
134
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusMessage, { type: "success", children: ["Added ", state.repo] }), _jsxs(Text, { dimColor: true, children: ["Path: ", state.path] }), state.isDefault && _jsx(Text, { dimColor: true, children: "Set as default repository" })] }));
135
+ case "error":
136
+ return _jsx(StatusMessage, { type: "error", children: state.message });
137
+ }
138
+ }
139
+ //# sourceMappingURL=add.js.map
@@ -0,0 +1,19 @@
1
+ import { z } from "zod";
2
+ export declare const args: z.ZodTuple<[z.ZodString], null>;
3
+ export declare const options: z.ZodObject<{
4
+ path: z.ZodOptional<z.ZodString>;
5
+ branch: z.ZodOptional<z.ZodString>;
6
+ }, "strip", z.ZodTypeAny, {
7
+ path?: string | undefined;
8
+ branch?: string | undefined;
9
+ }, {
10
+ path?: string | undefined;
11
+ branch?: string | undefined;
12
+ }>;
13
+ interface Props {
14
+ args: z.infer<typeof args>;
15
+ options: z.infer<typeof options>;
16
+ }
17
+ export default function RepoEdit({ args: [repoArg], options: opts }: Props): import("react/jsx-runtime").JSX.Element;
18
+ export {};
19
+ //# sourceMappingURL=edit.d.ts.map
@@ -0,0 +1,117 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { Box, Text, useApp, useInput } from "ink";
4
+ import { z } from "zod";
5
+ import { Spinner, StatusMessage } from "../../components/index.js";
6
+ import { getCredentials, addRepo, getConfig, } from "../../services/index.js";
7
+ export const args = z.tuple([z.string().describe("Repository in owner/repo format")]);
8
+ export const options = z.object({
9
+ path: z
10
+ .string()
11
+ .optional()
12
+ .describe("New skill path within the repo"),
13
+ branch: z
14
+ .string()
15
+ .optional()
16
+ .describe("New branch to use"),
17
+ });
18
+ export default function RepoEdit({ args: [repoArg], options: opts }) {
19
+ const { exit } = useApp();
20
+ const [state, setState] = useState({ phase: "checking" });
21
+ useEffect(() => {
22
+ async function checkAndEdit() {
23
+ // Check if logged in
24
+ const credentials = await getCredentials();
25
+ if (!credentials) {
26
+ setState({ phase: "not_logged_in" });
27
+ exit();
28
+ return;
29
+ }
30
+ // Check if repo exists
31
+ const config = getConfig();
32
+ const existingConfig = config.repos.find((r) => r.repo === repoArg);
33
+ if (!existingConfig) {
34
+ setState({ phase: "not_found", repo: repoArg });
35
+ exit();
36
+ return;
37
+ }
38
+ // If path or branch provided via flags, update directly
39
+ if (opts.path !== undefined || opts.branch !== undefined) {
40
+ const updatedConfig = {
41
+ repo: repoArg,
42
+ branch: opts.branch || existingConfig.branch,
43
+ paths: opts.path !== undefined ? (opts.path ? [opts.path] : []) : existingConfig.paths,
44
+ };
45
+ addRepo(updatedConfig);
46
+ setState({
47
+ phase: "success",
48
+ repo: repoArg,
49
+ path: updatedConfig.paths.length > 0 ? updatedConfig.paths.join(", ") : "(all paths)",
50
+ });
51
+ exit();
52
+ return;
53
+ }
54
+ // Interactive mode: prompt for new path
55
+ setState({
56
+ phase: "input_path",
57
+ repo: repoArg,
58
+ existingConfig,
59
+ currentPath: existingConfig.paths.length > 0 ? existingConfig.paths[0] : "",
60
+ });
61
+ }
62
+ checkAndEdit();
63
+ }, [repoArg, opts.path, opts.branch, exit]);
64
+ // Handle input for path
65
+ useInput((input, key) => {
66
+ if (state.phase !== "input_path")
67
+ return;
68
+ if (key.return) {
69
+ const updatedConfig = {
70
+ repo: state.repo,
71
+ branch: state.existingConfig.branch,
72
+ paths: state.currentPath ? [state.currentPath] : [],
73
+ };
74
+ addRepo(updatedConfig);
75
+ setState({
76
+ phase: "success",
77
+ repo: state.repo,
78
+ path: state.currentPath || "(all paths)",
79
+ });
80
+ exit();
81
+ return;
82
+ }
83
+ if (key.backspace || key.delete) {
84
+ setState({
85
+ ...state,
86
+ currentPath: state.currentPath.slice(0, -1),
87
+ });
88
+ return;
89
+ }
90
+ if (key.escape) {
91
+ exit();
92
+ return;
93
+ }
94
+ // Add character
95
+ if (input && !key.ctrl && !key.meta) {
96
+ setState({
97
+ ...state,
98
+ currentPath: state.currentPath + input,
99
+ });
100
+ }
101
+ }, { isActive: state.phase === "input_path" });
102
+ switch (state.phase) {
103
+ case "checking":
104
+ return _jsx(Spinner, { text: "Checking..." });
105
+ case "not_logged_in":
106
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusMessage, { type: "error", children: "Not authenticated" }), _jsx(Text, { dimColor: true, children: "Run 'skilluse login' to authenticate with GitHub" })] }));
107
+ case "not_found":
108
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusMessage, { type: "error", children: ["Repository ", state.repo, " not found in config"] }), _jsxs(Text, { dimColor: true, children: ["Run 'skilluse repo add ", state.repo, "' to add it first."] })] }));
109
+ case "input_path":
110
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Editing repository: ", _jsx(Text, { color: "cyan", children: state.repo })] }), _jsxs(Text, { dimColor: true, children: ["Current: ", state.existingConfig.paths.join(", ") || "(all paths)"] }), _jsxs(Box, { marginTop: 1, children: [_jsx(Text, { children: "New path (leave empty for all): " }), _jsx(Text, { color: "green", children: state.currentPath }), _jsx(Text, { color: "gray", children: "\u2588" })] }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Press Enter to confirm, Esc to cancel" }) })] }));
111
+ case "success":
112
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusMessage, { type: "success", children: ["Updated ", state.repo] }), _jsxs(Text, { dimColor: true, children: ["Path: ", state.path] })] }));
113
+ case "error":
114
+ return _jsx(StatusMessage, { type: "error", children: state.message });
115
+ }
116
+ }
117
+ //# sourceMappingURL=edit.js.map
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ export declare const options: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
3
+ interface Props {
4
+ options: z.infer<typeof options>;
5
+ }
6
+ export default function Repo(_props: Props): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { Box, Text, useApp } from "ink";
4
+ import { z } from "zod";
5
+ import { Spinner, StatusMessage } from "../../components/index.js";
6
+ import { getCredentials, getConfig, } from "../../services/index.js";
7
+ export const options = z.object({});
8
+ export default function Repo(_props) {
9
+ const { exit } = useApp();
10
+ const [state, setState] = useState({ phase: "checking" });
11
+ useEffect(() => {
12
+ async function checkRepo() {
13
+ // Check if logged in
14
+ const credentials = await getCredentials();
15
+ if (!credentials) {
16
+ setState({ phase: "not_logged_in" });
17
+ exit();
18
+ return;
19
+ }
20
+ const config = getConfig();
21
+ setState({
22
+ phase: "success",
23
+ defaultRepo: config.defaultRepo,
24
+ repos: config.repos,
25
+ });
26
+ exit();
27
+ }
28
+ checkRepo();
29
+ }, [exit]);
30
+ switch (state.phase) {
31
+ case "checking":
32
+ return _jsx(Spinner, { text: "Loading..." });
33
+ case "not_logged_in":
34
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusMessage, { type: "error", children: "Not authenticated" }), _jsx(Text, { dimColor: true, children: "Run 'skilluse login' to authenticate with GitHub" })] }));
35
+ case "success":
36
+ if (!state.defaultRepo) {
37
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Current Repository" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "(no default repo set)" }) }), state.repos.length === 0 ? (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { dimColor: true, children: "No repositories configured." }), _jsx(Text, { dimColor: true, children: "Run 'skilluse repo add owner/repo' to add one." })] })) : (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Text, { dimColor: true, children: [state.repos.length, " repo", state.repos.length > 1 ? "s" : "", " configured."] }), _jsx(Text, { dimColor: true, children: "Run 'skilluse repo use owner/repo' to set a default." })] }))] }));
38
+ }
39
+ const defaultRepoConfig = state.repos.find((r) => r.repo === state.defaultRepo);
40
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Current Repository" }), _jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", children: state.defaultRepo }), _jsx(Text, { dimColor: true, children: " (default)" })] }), defaultRepoConfig && (_jsxs(_Fragment, { children: [_jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Branch: " }), _jsx(Text, { children: defaultRepoConfig.branch })] }), _jsxs(Box, { children: [_jsx(Text, { dimColor: true, children: "Paths: " }), _jsx(Text, { children: defaultRepoConfig.paths.length > 0
41
+ ? defaultRepoConfig.paths.join(", ")
42
+ : "(all)" })] })] }))] }), state.repos.length > 1 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: ["Run 'skilluse repo list' to see all ", state.repos.length, " configured repos."] }) }))] }));
43
+ case "error":
44
+ return _jsx(StatusMessage, { type: "error", children: state.message });
45
+ }
46
+ }
47
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1,8 @@
1
+ import { z } from "zod";
2
+ export declare const options: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
3
+ interface Props {
4
+ options: z.infer<typeof options>;
5
+ }
6
+ export default function RepoList(_props: Props): import("react/jsx-runtime").JSX.Element;
7
+ export {};
8
+ //# sourceMappingURL=list.d.ts.map
@@ -0,0 +1,47 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect } from "react";
3
+ import { Box, Text, useApp } from "ink";
4
+ import { z } from "zod";
5
+ import { Spinner, StatusMessage } from "../../components/index.js";
6
+ import { getCredentials, getConfig, } from "../../services/index.js";
7
+ export const options = z.object({});
8
+ export default function RepoList(_props) {
9
+ const { exit } = useApp();
10
+ const [state, setState] = useState({ phase: "checking" });
11
+ useEffect(() => {
12
+ async function loadRepos() {
13
+ // Check if logged in
14
+ const credentials = await getCredentials();
15
+ if (!credentials) {
16
+ setState({ phase: "not_logged_in" });
17
+ exit();
18
+ return;
19
+ }
20
+ const config = getConfig();
21
+ setState({
22
+ phase: "success",
23
+ defaultRepo: config.defaultRepo,
24
+ repos: config.repos,
25
+ });
26
+ exit();
27
+ }
28
+ loadRepos();
29
+ }, [exit]);
30
+ switch (state.phase) {
31
+ case "checking":
32
+ return _jsx(Spinner, { text: "Loading..." });
33
+ case "not_logged_in":
34
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusMessage, { type: "error", children: "Not authenticated" }), _jsx(Text, { dimColor: true, children: "Run 'skilluse login' to authenticate with GitHub" })] }));
35
+ case "success":
36
+ if (state.repos.length === 0) {
37
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Configured Repositories" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "(no repositories configured)" }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Run 'skilluse repo add owner/repo' to add one." }) })] }));
38
+ }
39
+ return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Configured Repositories" }), _jsx(Text, { children: " " }), state.repos.map((repo) => {
40
+ const isDefault = repo.repo === state.defaultRepo;
41
+ return (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: isDefault ? "green" : undefined, children: isDefault ? "● " : "○ " }), _jsx(Text, { color: isDefault ? "cyan" : undefined, bold: isDefault, children: repo.repo }), isDefault && _jsx(Text, { dimColor: true, children: " (default)" })] }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["Branch: ", repo.branch, " | Paths:", " ", repo.paths.length > 0 ? repo.paths.join(", ") : "(all)"] }) })] }, repo.repo));
42
+ })] }));
43
+ case "error":
44
+ return _jsx(StatusMessage, { type: "error", children: state.message });
45
+ }
46
+ }
47
+ //# sourceMappingURL=list.js.map
@@ -0,0 +1,16 @@
1
+ import { z } from "zod";
2
+ export declare const args: z.ZodTuple<[z.ZodString], null>;
3
+ export declare const options: z.ZodObject<{
4
+ force: z.ZodDefault<z.ZodBoolean>;
5
+ }, "strip", z.ZodTypeAny, {
6
+ force: boolean;
7
+ }, {
8
+ force?: boolean | undefined;
9
+ }>;
10
+ interface Props {
11
+ args: z.infer<typeof args>;
12
+ options: z.infer<typeof options>;
13
+ }
14
+ export default function RepoRemove({ args: [repoArg], options: opts }: Props): import("react/jsx-runtime").JSX.Element;
15
+ export {};
16
+ //# sourceMappingURL=remove.d.ts.map