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.
- package/README.md +100 -0
- package/dist/app.d.ts +6 -0
- package/dist/app.js +6 -0
- package/dist/cli.d.ts +3 -0
- package/dist/cli.js +167 -0
- package/dist/commands/demo.d.ts +14 -0
- package/dist/commands/demo.js +46 -0
- package/dist/commands/index.d.ts +8 -0
- package/dist/commands/index.js +77 -0
- package/dist/commands/list.d.ts +14 -0
- package/dist/commands/list.js +54 -0
- package/dist/commands/login.d.ts +14 -0
- package/dist/commands/login.js +153 -0
- package/dist/commands/logout.d.ts +8 -0
- package/dist/commands/logout.js +47 -0
- package/dist/commands/repo/add.d.ts +22 -0
- package/dist/commands/repo/add.js +139 -0
- package/dist/commands/repo/edit.d.ts +19 -0
- package/dist/commands/repo/edit.js +117 -0
- package/dist/commands/repo/index.d.ts +8 -0
- package/dist/commands/repo/index.js +47 -0
- package/dist/commands/repo/list.d.ts +8 -0
- package/dist/commands/repo/list.js +47 -0
- package/dist/commands/repo/remove.d.ts +16 -0
- package/dist/commands/repo/remove.js +83 -0
- package/dist/commands/repo/sync.d.ts +10 -0
- package/dist/commands/repo/sync.js +78 -0
- package/dist/commands/repo/use.d.ts +10 -0
- package/dist/commands/repo/use.js +56 -0
- package/dist/commands/repos.d.ts +11 -0
- package/dist/commands/repos.js +50 -0
- package/dist/commands/search.d.ts +16 -0
- package/dist/commands/search.js +199 -0
- package/dist/commands/skills.d.ts +11 -0
- package/dist/commands/skills.js +43 -0
- package/dist/commands/whoami.d.ts +8 -0
- package/dist/commands/whoami.js +69 -0
- package/dist/components/CLIError.d.ts +27 -0
- package/dist/components/CLIError.js +24 -0
- package/dist/components/ProgressBar.d.ts +7 -0
- package/dist/components/ProgressBar.js +9 -0
- package/dist/components/Select.d.ts +11 -0
- package/dist/components/Select.js +6 -0
- package/dist/components/Spinner.d.ts +6 -0
- package/dist/components/Spinner.js +7 -0
- package/dist/components/StatusMessage.d.ts +9 -0
- package/dist/components/StatusMessage.js +13 -0
- package/dist/components/Table.d.ts +9 -0
- package/dist/components/Table.js +27 -0
- package/dist/components/TextInput.d.ts +9 -0
- package/dist/components/TextInput.js +6 -0
- package/dist/components/index.d.ts +8 -0
- package/dist/components/index.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.js +43815 -0
- package/dist/services/credentials.d.ts +69 -0
- package/dist/services/credentials.js +216 -0
- package/dist/services/index.d.ts +6 -0
- package/dist/services/index.js +10 -0
- package/dist/services/oauth.d.ts +106 -0
- package/dist/services/oauth.js +208 -0
- package/dist/services/paths.d.ts +19 -0
- package/dist/services/paths.js +21 -0
- package/dist/services/store.d.ts +64 -0
- package/dist/services/store.js +107 -0
- package/dist/services/update.d.ts +20 -0
- package/dist/services/update.js +93 -0
- package/package.json +70 -0
|
@@ -0,0 +1,83 @@
|
|
|
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, CLIError } from "../../components/index.js";
|
|
6
|
+
import { getCredentials, removeRepo, 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
|
+
force: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.default(false)
|
|
12
|
+
.describe("Skip confirmation prompt"),
|
|
13
|
+
});
|
|
14
|
+
export default function RepoRemove({ args: [repoArg], options: opts }) {
|
|
15
|
+
const { exit } = useApp();
|
|
16
|
+
const [state, setState] = useState({ phase: "checking" });
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
async function checkAndRemove() {
|
|
19
|
+
// Check if logged in
|
|
20
|
+
const credentials = await getCredentials();
|
|
21
|
+
if (!credentials) {
|
|
22
|
+
setState({ phase: "not_logged_in" });
|
|
23
|
+
exit();
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
// Check if repo exists in config
|
|
27
|
+
const config = getConfig();
|
|
28
|
+
if (!config.repos.find((r) => r.repo === repoArg)) {
|
|
29
|
+
setState({ phase: "not_found", repo: repoArg });
|
|
30
|
+
exit();
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
// Count skills that will be affected
|
|
34
|
+
const affectedSkills = config.installed.filter((skill) => skill.repo === repoArg).length;
|
|
35
|
+
// If force flag, remove directly
|
|
36
|
+
if (opts.force) {
|
|
37
|
+
removeRepo(repoArg);
|
|
38
|
+
setState({ phase: "success", repo: repoArg });
|
|
39
|
+
exit();
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
// Otherwise, prompt for confirmation
|
|
43
|
+
setState({ phase: "confirm", repo: repoArg, affectedSkills });
|
|
44
|
+
}
|
|
45
|
+
checkAndRemove();
|
|
46
|
+
}, [repoArg, opts.force, exit]);
|
|
47
|
+
// Handle confirmation input
|
|
48
|
+
useInput((input, key) => {
|
|
49
|
+
if (state.phase !== "confirm")
|
|
50
|
+
return;
|
|
51
|
+
if (input.toLowerCase() === "y" || key.return) {
|
|
52
|
+
removeRepo(state.repo);
|
|
53
|
+
setState({ phase: "success", repo: state.repo });
|
|
54
|
+
exit();
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
if (input.toLowerCase() === "n" || key.escape) {
|
|
58
|
+
setState({ phase: "cancelled" });
|
|
59
|
+
exit();
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
}, { isActive: state.phase === "confirm" });
|
|
63
|
+
switch (state.phase) {
|
|
64
|
+
case "checking":
|
|
65
|
+
return _jsx(Spinner, { text: "Checking..." });
|
|
66
|
+
case "not_logged_in":
|
|
67
|
+
return (_jsx(CLIError, { message: "Not authenticated", causes: ["Your session may have expired", "You haven't logged in yet"], suggestion: "Run 'skilluse login' to authenticate with GitHub" }));
|
|
68
|
+
case "not_found":
|
|
69
|
+
return (_jsx(CLIError, { message: "Repository not found in config", context: state.repo, causes: [
|
|
70
|
+
"The repository was never added",
|
|
71
|
+
"It may have been removed already",
|
|
72
|
+
], suggestion: "Run 'skilluse repo list' to see configured repos" }));
|
|
73
|
+
case "confirm":
|
|
74
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Text, { children: ["Remove repository ", _jsx(Text, { color: "cyan", children: state.repo }), "?"] }), state.affectedSkills > 0 && (_jsx(Box, { marginTop: 1, children: _jsxs(Text, { color: "yellow", children: ["This will untrack ", state.affectedSkills, " installed skill", state.affectedSkills === 1 ? "" : "s", " from this repository."] }) })), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Press Y to confirm, N to cancel" }) })] }));
|
|
75
|
+
case "success":
|
|
76
|
+
return (_jsxs(StatusMessage, { type: "success", children: ["Removed ", state.repo] }));
|
|
77
|
+
case "cancelled":
|
|
78
|
+
return _jsx(Text, { dimColor: true, children: "Removal cancelled" });
|
|
79
|
+
case "error":
|
|
80
|
+
return _jsx(StatusMessage, { type: "error", children: state.message });
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
//# sourceMappingURL=remove.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const args: z.ZodTuple<[z.ZodOptional<z.ZodString>], null>;
|
|
3
|
+
export declare const options: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
4
|
+
interface Props {
|
|
5
|
+
args: z.infer<typeof args>;
|
|
6
|
+
options: z.infer<typeof options>;
|
|
7
|
+
}
|
|
8
|
+
export default function RepoSync({ args: [repoArg], options: _opts }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=sync.d.ts.map
|
|
@@ -0,0 +1,78 @@
|
|
|
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 args = z.tuple([
|
|
8
|
+
z.string().optional().describe("Repository in owner/repo format (optional, syncs all if omitted)"),
|
|
9
|
+
]);
|
|
10
|
+
export const options = z.object({});
|
|
11
|
+
export default function RepoSync({ args: [repoArg], options: _opts }) {
|
|
12
|
+
const { exit } = useApp();
|
|
13
|
+
const [state, setState] = useState({ phase: "checking" });
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
async function syncRepos() {
|
|
16
|
+
// Check if logged in
|
|
17
|
+
const credentials = await getCredentials();
|
|
18
|
+
if (!credentials) {
|
|
19
|
+
setState({ phase: "not_logged_in" });
|
|
20
|
+
exit();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const config = getConfig();
|
|
24
|
+
if (config.repos.length === 0) {
|
|
25
|
+
setState({ phase: "no_repos" });
|
|
26
|
+
exit();
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
// If specific repo provided, check it exists
|
|
30
|
+
if (repoArg) {
|
|
31
|
+
if (!config.repos.find((r) => r.repo === repoArg)) {
|
|
32
|
+
setState({ phase: "not_found", repo: repoArg });
|
|
33
|
+
exit();
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
// Get repos to sync
|
|
38
|
+
const reposToSync = repoArg
|
|
39
|
+
? config.repos.filter((r) => r.repo === repoArg)
|
|
40
|
+
: config.repos;
|
|
41
|
+
// Sync each repo
|
|
42
|
+
const synced = [];
|
|
43
|
+
for (let i = 0; i < reposToSync.length; i++) {
|
|
44
|
+
const repo = reposToSync[i];
|
|
45
|
+
setState({
|
|
46
|
+
phase: "syncing",
|
|
47
|
+
repo: repo.repo,
|
|
48
|
+
current: i + 1,
|
|
49
|
+
total: reposToSync.length,
|
|
50
|
+
});
|
|
51
|
+
// TODO: Implement actual sync logic (fetch skill manifest from GitHub)
|
|
52
|
+
// For now, just simulate a sync
|
|
53
|
+
await new Promise((resolve) => setTimeout(resolve, 500));
|
|
54
|
+
synced.push(repo.repo);
|
|
55
|
+
}
|
|
56
|
+
setState({ phase: "success", synced });
|
|
57
|
+
exit();
|
|
58
|
+
}
|
|
59
|
+
syncRepos();
|
|
60
|
+
}, [repoArg, exit]);
|
|
61
|
+
switch (state.phase) {
|
|
62
|
+
case "checking":
|
|
63
|
+
return _jsx(Spinner, { text: "Checking..." });
|
|
64
|
+
case "not_logged_in":
|
|
65
|
+
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" })] }));
|
|
66
|
+
case "no_repos":
|
|
67
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusMessage, { type: "warning", children: "No repositories configured" }), _jsx(Text, { dimColor: true, children: "Run 'skilluse repo add owner/repo' to add one." })] }));
|
|
68
|
+
case "not_found":
|
|
69
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusMessage, { type: "error", children: ["Repository ", state.repo, " not found in config"] }), _jsx(Text, { dimColor: true, children: "Run 'skilluse repo list' to see configured repos." })] }));
|
|
70
|
+
case "syncing":
|
|
71
|
+
return (_jsx(Spinner, { text: `Syncing ${state.repo} (${state.current}/${state.total})...` }));
|
|
72
|
+
case "success":
|
|
73
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(StatusMessage, { type: "success", children: ["Synced ", state.synced.length, " repo", state.synced.length > 1 ? "s" : ""] }), state.synced.map((repo) => (_jsxs(Text, { dimColor: true, children: ["\u2022 ", repo] }, repo)))] }));
|
|
74
|
+
case "error":
|
|
75
|
+
return _jsx(StatusMessage, { type: "error", children: state.message });
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
//# sourceMappingURL=sync.js.map
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
export declare const args: z.ZodTuple<[z.ZodString], null>;
|
|
3
|
+
export declare const options: z.ZodObject<{}, "strip", z.ZodTypeAny, {}, {}>;
|
|
4
|
+
interface Props {
|
|
5
|
+
args: z.infer<typeof args>;
|
|
6
|
+
options: z.infer<typeof options>;
|
|
7
|
+
}
|
|
8
|
+
export default function RepoUse({ args: [repoArg], options: _opts }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=use.d.ts.map
|
|
@@ -0,0 +1,56 @@
|
|
|
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, 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
|
+
export default function RepoUse({ args: [repoArg], options: _opts }) {
|
|
10
|
+
const { exit } = useApp();
|
|
11
|
+
const [state, setState] = useState({ phase: "checking" });
|
|
12
|
+
useEffect(() => {
|
|
13
|
+
async function setDefault() {
|
|
14
|
+
// Check if logged in
|
|
15
|
+
const credentials = await getCredentials();
|
|
16
|
+
if (!credentials) {
|
|
17
|
+
setState({ phase: "not_logged_in" });
|
|
18
|
+
exit();
|
|
19
|
+
return;
|
|
20
|
+
}
|
|
21
|
+
// Check if repo exists in config
|
|
22
|
+
const config = getConfig();
|
|
23
|
+
if (!config.repos.find((r) => r.repo === repoArg)) {
|
|
24
|
+
setState({ phase: "not_found", repo: repoArg });
|
|
25
|
+
exit();
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Check if already default
|
|
29
|
+
if (config.defaultRepo === repoArg) {
|
|
30
|
+
setState({ phase: "already_default", repo: repoArg });
|
|
31
|
+
exit();
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
// Set as default
|
|
35
|
+
setDefaultRepo(repoArg);
|
|
36
|
+
setState({ phase: "success", repo: repoArg });
|
|
37
|
+
exit();
|
|
38
|
+
}
|
|
39
|
+
setDefault();
|
|
40
|
+
}, [repoArg, exit]);
|
|
41
|
+
switch (state.phase) {
|
|
42
|
+
case "checking":
|
|
43
|
+
return _jsx(Spinner, { text: "Setting default..." });
|
|
44
|
+
case "not_logged_in":
|
|
45
|
+
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" })] }));
|
|
46
|
+
case "not_found":
|
|
47
|
+
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."] })] }));
|
|
48
|
+
case "already_default":
|
|
49
|
+
return (_jsxs(StatusMessage, { type: "success", children: [state.repo, " is already the default"] }));
|
|
50
|
+
case "success":
|
|
51
|
+
return (_jsxs(StatusMessage, { type: "success", children: ["Default repo set to ", state.repo] }));
|
|
52
|
+
case "error":
|
|
53
|
+
return _jsx(StatusMessage, { type: "error", children: state.message });
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
//# sourceMappingURL=use.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
/**
|
|
7
|
+
* Alias for `skilluse repo list` - shows configured skill repositories
|
|
8
|
+
*/
|
|
9
|
+
export default function Repos(_props: Props): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=repos.d.ts.map
|
|
@@ -0,0 +1,50 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Alias for `skilluse repo list` - shows configured skill repositories
|
|
10
|
+
*/
|
|
11
|
+
export default function Repos(_props) {
|
|
12
|
+
const { exit } = useApp();
|
|
13
|
+
const [state, setState] = useState({ phase: "checking" });
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
async function loadRepos() {
|
|
16
|
+
// Check if logged in
|
|
17
|
+
const credentials = await getCredentials();
|
|
18
|
+
if (!credentials) {
|
|
19
|
+
setState({ phase: "not_logged_in" });
|
|
20
|
+
exit();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const config = getConfig();
|
|
24
|
+
setState({
|
|
25
|
+
phase: "success",
|
|
26
|
+
defaultRepo: config.defaultRepo,
|
|
27
|
+
repos: config.repos,
|
|
28
|
+
});
|
|
29
|
+
exit();
|
|
30
|
+
}
|
|
31
|
+
loadRepos();
|
|
32
|
+
}, [exit]);
|
|
33
|
+
switch (state.phase) {
|
|
34
|
+
case "checking":
|
|
35
|
+
return _jsx(Spinner, { text: "Loading..." });
|
|
36
|
+
case "not_logged_in":
|
|
37
|
+
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" })] }));
|
|
38
|
+
case "success":
|
|
39
|
+
if (state.repos.length === 0) {
|
|
40
|
+
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." }) })] }));
|
|
41
|
+
}
|
|
42
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Configured Repositories" }), _jsx(Text, { children: " " }), state.repos.map((repo) => {
|
|
43
|
+
const isDefault = repo.repo === state.defaultRepo;
|
|
44
|
+
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));
|
|
45
|
+
})] }));
|
|
46
|
+
case "error":
|
|
47
|
+
return _jsx(StatusMessage, { type: "error", children: state.message });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
//# sourceMappingURL=repos.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
|
+
all: z.ZodDefault<z.ZodBoolean>;
|
|
5
|
+
}, "strip", z.ZodTypeAny, {
|
|
6
|
+
all: boolean;
|
|
7
|
+
}, {
|
|
8
|
+
all?: boolean | undefined;
|
|
9
|
+
}>;
|
|
10
|
+
interface Props {
|
|
11
|
+
args: z.infer<typeof args>;
|
|
12
|
+
options: z.infer<typeof options>;
|
|
13
|
+
}
|
|
14
|
+
export default function Search({ args: [keyword], options: opts }: Props): import("react/jsx-runtime").JSX.Element;
|
|
15
|
+
export {};
|
|
16
|
+
//# sourceMappingURL=search.d.ts.map
|
|
@@ -0,0 +1,199 @@
|
|
|
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 args = z.tuple([z.string().describe("Search keyword")]);
|
|
8
|
+
export const options = z.object({
|
|
9
|
+
all: z
|
|
10
|
+
.boolean()
|
|
11
|
+
.default(false)
|
|
12
|
+
.describe("Search in all configured repos (not just default)"),
|
|
13
|
+
});
|
|
14
|
+
/**
|
|
15
|
+
* Parse YAML frontmatter from SKILL.md content
|
|
16
|
+
*/
|
|
17
|
+
function parseFrontmatter(content) {
|
|
18
|
+
const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
19
|
+
if (!frontmatterMatch) {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
const yaml = frontmatterMatch[1];
|
|
23
|
+
const result = {};
|
|
24
|
+
// Simple YAML parser for frontmatter
|
|
25
|
+
const lines = yaml.split("\n");
|
|
26
|
+
for (const line of lines) {
|
|
27
|
+
const colonIndex = line.indexOf(":");
|
|
28
|
+
if (colonIndex === -1)
|
|
29
|
+
continue;
|
|
30
|
+
const key = line.substring(0, colonIndex).trim();
|
|
31
|
+
let value = line.substring(colonIndex + 1).trim();
|
|
32
|
+
// Handle arrays like [tag1, tag2]
|
|
33
|
+
if (typeof value === "string" && value.startsWith("[") && value.endsWith("]")) {
|
|
34
|
+
value = value
|
|
35
|
+
.slice(1, -1)
|
|
36
|
+
.split(",")
|
|
37
|
+
.map((s) => s.trim());
|
|
38
|
+
}
|
|
39
|
+
if (key) {
|
|
40
|
+
result[key] = value;
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
return result;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Fetch SKILL.md files from a GitHub repo
|
|
47
|
+
*/
|
|
48
|
+
async function fetchSkillsFromRepo(token, repoConfig) {
|
|
49
|
+
const { repo, branch, paths } = repoConfig;
|
|
50
|
+
const skills = [];
|
|
51
|
+
// Search paths - if none specified, search root
|
|
52
|
+
const searchPaths = paths.length > 0 ? paths : [""];
|
|
53
|
+
for (const basePath of searchPaths) {
|
|
54
|
+
try {
|
|
55
|
+
// Get directory contents
|
|
56
|
+
const apiPath = basePath
|
|
57
|
+
? `https://api.github.com/repos/${repo}/contents/${basePath}?ref=${branch}`
|
|
58
|
+
: `https://api.github.com/repos/${repo}/contents?ref=${branch}`;
|
|
59
|
+
const response = await fetch(apiPath, {
|
|
60
|
+
headers: {
|
|
61
|
+
Authorization: `Bearer ${token}`,
|
|
62
|
+
Accept: "application/vnd.github+json",
|
|
63
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
64
|
+
},
|
|
65
|
+
});
|
|
66
|
+
if (!response.ok) {
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
const contents = (await response.json());
|
|
70
|
+
// Find directories that might contain SKILL.md
|
|
71
|
+
const dirs = contents.filter((item) => item.type === "dir");
|
|
72
|
+
for (const dir of dirs) {
|
|
73
|
+
// Check if this directory has a SKILL.md
|
|
74
|
+
const skillMdUrl = `https://api.github.com/repos/${repo}/contents/${dir.path}/SKILL.md?ref=${branch}`;
|
|
75
|
+
const skillResponse = await fetch(skillMdUrl, {
|
|
76
|
+
headers: {
|
|
77
|
+
Authorization: `Bearer ${token}`,
|
|
78
|
+
Accept: "application/vnd.github.raw+json",
|
|
79
|
+
"X-GitHub-Api-Version": "2022-11-28",
|
|
80
|
+
},
|
|
81
|
+
});
|
|
82
|
+
if (skillResponse.ok) {
|
|
83
|
+
const content = await skillResponse.text();
|
|
84
|
+
const frontmatter = parseFrontmatter(content);
|
|
85
|
+
if (frontmatter.name) {
|
|
86
|
+
skills.push({
|
|
87
|
+
name: String(frontmatter.name),
|
|
88
|
+
description: String(frontmatter.description || ""),
|
|
89
|
+
type: frontmatter.type ? String(frontmatter.type) : undefined,
|
|
90
|
+
version: frontmatter.version ? String(frontmatter.version) : undefined,
|
|
91
|
+
tags: Array.isArray(frontmatter.tags)
|
|
92
|
+
? frontmatter.tags.map(String)
|
|
93
|
+
: undefined,
|
|
94
|
+
repo,
|
|
95
|
+
path: dir.path,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
catch {
|
|
102
|
+
// Continue on error
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return skills;
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* Filter skills by search keyword
|
|
109
|
+
*/
|
|
110
|
+
function filterSkills(skills, keyword) {
|
|
111
|
+
const lowerKeyword = keyword.toLowerCase();
|
|
112
|
+
return skills.filter((skill) => {
|
|
113
|
+
// Match name
|
|
114
|
+
if (skill.name.toLowerCase().includes(lowerKeyword)) {
|
|
115
|
+
return true;
|
|
116
|
+
}
|
|
117
|
+
// Match description
|
|
118
|
+
if (skill.description.toLowerCase().includes(lowerKeyword)) {
|
|
119
|
+
return true;
|
|
120
|
+
}
|
|
121
|
+
// Match tags
|
|
122
|
+
if (skill.tags?.some((tag) => tag.toLowerCase().includes(lowerKeyword))) {
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
export default function Search({ args: [keyword], options: opts }) {
|
|
129
|
+
const { exit } = useApp();
|
|
130
|
+
const [state, setState] = useState({ phase: "checking" });
|
|
131
|
+
useEffect(() => {
|
|
132
|
+
async function search() {
|
|
133
|
+
// Check if logged in
|
|
134
|
+
const credentials = await getCredentials();
|
|
135
|
+
if (!credentials) {
|
|
136
|
+
setState({ phase: "not_logged_in" });
|
|
137
|
+
exit();
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const config = getConfig();
|
|
141
|
+
// Determine which repos to search
|
|
142
|
+
let reposToSearch = [];
|
|
143
|
+
if (opts.all) {
|
|
144
|
+
reposToSearch = config.repos;
|
|
145
|
+
}
|
|
146
|
+
else if (config.defaultRepo) {
|
|
147
|
+
const defaultRepoConfig = config.repos.find((r) => r.repo === config.defaultRepo);
|
|
148
|
+
if (defaultRepoConfig) {
|
|
149
|
+
reposToSearch = [defaultRepoConfig];
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
else if (config.repos.length > 0) {
|
|
153
|
+
// Use first repo if no default
|
|
154
|
+
reposToSearch = [config.repos[0]];
|
|
155
|
+
}
|
|
156
|
+
if (reposToSearch.length === 0) {
|
|
157
|
+
setState({ phase: "no_repos" });
|
|
158
|
+
exit();
|
|
159
|
+
return;
|
|
160
|
+
}
|
|
161
|
+
// Fetch skills from all repos
|
|
162
|
+
const allSkills = [];
|
|
163
|
+
for (const repoConfig of reposToSearch) {
|
|
164
|
+
setState({ phase: "searching", repo: repoConfig.repo });
|
|
165
|
+
const skills = await fetchSkillsFromRepo(credentials.token, repoConfig);
|
|
166
|
+
allSkills.push(...skills);
|
|
167
|
+
}
|
|
168
|
+
// Filter by keyword
|
|
169
|
+
const matchingSkills = filterSkills(allSkills, keyword);
|
|
170
|
+
setState({ phase: "success", skills: matchingSkills, keyword });
|
|
171
|
+
exit();
|
|
172
|
+
}
|
|
173
|
+
search().catch((err) => {
|
|
174
|
+
setState({
|
|
175
|
+
phase: "error",
|
|
176
|
+
message: err instanceof Error ? err.message : "Search failed",
|
|
177
|
+
});
|
|
178
|
+
exit();
|
|
179
|
+
});
|
|
180
|
+
}, [keyword, opts.all, exit]);
|
|
181
|
+
switch (state.phase) {
|
|
182
|
+
case "checking":
|
|
183
|
+
return _jsx(Spinner, { text: "Initializing..." });
|
|
184
|
+
case "not_logged_in":
|
|
185
|
+
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" })] }));
|
|
186
|
+
case "no_repos":
|
|
187
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(StatusMessage, { type: "warning", children: "No repositories configured" }), _jsx(Text, { dimColor: true, children: "Run 'skilluse repo add owner/repo' to add a skill repository." })] }));
|
|
188
|
+
case "searching":
|
|
189
|
+
return _jsx(Spinner, { text: `Searching ${state.repo}...` });
|
|
190
|
+
case "success":
|
|
191
|
+
if (state.skills.length === 0) {
|
|
192
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { children: "Search results for \"" }), _jsx(Text, { color: "cyan", children: state.keyword }), _jsx(Text, { children: "\"" })] }), _jsx(StatusMessage, { type: "warning", children: "No skills found" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Try a different search term or check your configured repos with 'skilluse repo list'." }) })] }));
|
|
193
|
+
}
|
|
194
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { marginBottom: 1, children: [_jsx(Text, { children: "Search results for \"" }), _jsx(Text, { color: "cyan", children: state.keyword }), _jsx(Text, { children: "\"" })] }), state.skills.map((skill) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", bold: true, children: skill.name }), skill.version && _jsxs(Text, { dimColor: true, children: [" v", skill.version] })] }), _jsx(Box, { marginLeft: 2, children: _jsx(Text, { children: skill.description }) }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: [skill.repo, skill.type && ` • ${skill.type}`] }) })] }, `${skill.repo}/${skill.path}`))), _jsx(Box, { marginTop: 1, children: _jsxs(Text, { dimColor: true, children: [state.skills.length, " skill", state.skills.length !== 1 ? "s" : "", " found"] }) })] }));
|
|
195
|
+
case "error":
|
|
196
|
+
return _jsx(StatusMessage, { type: "error", children: state.message });
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
//# sourceMappingURL=search.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
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
|
+
/**
|
|
7
|
+
* Alias for `skilluse list` - shows installed skills
|
|
8
|
+
*/
|
|
9
|
+
export default function Skills(_props: Props): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
export {};
|
|
11
|
+
//# sourceMappingURL=skills.d.ts.map
|
|
@@ -0,0 +1,43 @@
|
|
|
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
|
+
/**
|
|
9
|
+
* Alias for `skilluse list` - shows installed skills
|
|
10
|
+
*/
|
|
11
|
+
export default function Skills(_props) {
|
|
12
|
+
const { exit } = useApp();
|
|
13
|
+
const [state, setState] = useState({ phase: "checking" });
|
|
14
|
+
useEffect(() => {
|
|
15
|
+
async function loadSkills() {
|
|
16
|
+
// Check if logged in
|
|
17
|
+
const credentials = await getCredentials();
|
|
18
|
+
if (!credentials) {
|
|
19
|
+
setState({ phase: "not_logged_in" });
|
|
20
|
+
exit();
|
|
21
|
+
return;
|
|
22
|
+
}
|
|
23
|
+
const config = getConfig();
|
|
24
|
+
setState({ phase: "success", skills: config.installed });
|
|
25
|
+
exit();
|
|
26
|
+
}
|
|
27
|
+
loadSkills();
|
|
28
|
+
}, [exit]);
|
|
29
|
+
switch (state.phase) {
|
|
30
|
+
case "checking":
|
|
31
|
+
return _jsx(Spinner, { text: "Loading..." });
|
|
32
|
+
case "not_logged_in":
|
|
33
|
+
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" })] }));
|
|
34
|
+
case "success":
|
|
35
|
+
if (state.skills.length === 0) {
|
|
36
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Installed Skills" }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "(no skills installed)" }) }), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Run 'skilluse install skill-name' to install one." }) })] }));
|
|
37
|
+
}
|
|
38
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsx(Text, { bold: true, children: "Installed Skills" }), _jsx(Text, { children: " " }), state.skills.map((skill) => (_jsxs(Box, { flexDirection: "column", marginBottom: 1, children: [_jsxs(Box, { children: [_jsx(Text, { color: "cyan", bold: true, children: skill.name }), _jsxs(Text, { dimColor: true, children: [" v", skill.version] }), _jsxs(Text, { dimColor: true, children: [" (", skill.scope, ")"] })] }), _jsx(Box, { marginLeft: 2, children: _jsxs(Text, { dimColor: true, children: ["From: ", skill.repo, " | Path: ", skill.installedPath] }) })] }, skill.name)))] }));
|
|
39
|
+
case "error":
|
|
40
|
+
return _jsx(StatusMessage, { type: "error", children: state.message });
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=skills.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 Whoami(_props: Props): import("react/jsx-runtime").JSX.Element;
|
|
7
|
+
export {};
|
|
8
|
+
//# sourceMappingURL=whoami.d.ts.map
|
|
@@ -0,0 +1,69 @@
|
|
|
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, getInstallations, } from "../services/index.js";
|
|
7
|
+
export const options = z.object({});
|
|
8
|
+
export default function Whoami(_props) {
|
|
9
|
+
const { exit } = useApp();
|
|
10
|
+
const [state, setState] = useState({ phase: "checking" });
|
|
11
|
+
useEffect(() => {
|
|
12
|
+
async function checkAuth() {
|
|
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
|
+
// Fetch fresh user info from GitHub
|
|
21
|
+
try {
|
|
22
|
+
const response = await fetch("https://api.github.com/user", {
|
|
23
|
+
headers: {
|
|
24
|
+
Authorization: `Bearer ${credentials.token}`,
|
|
25
|
+
Accept: "application/vnd.github+json",
|
|
26
|
+
},
|
|
27
|
+
});
|
|
28
|
+
if (!response.ok) {
|
|
29
|
+
if (response.status === 401) {
|
|
30
|
+
setState({
|
|
31
|
+
phase: "error",
|
|
32
|
+
message: "Token expired or invalid. Please run 'skilluse login' again.",
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
else {
|
|
36
|
+
setState({
|
|
37
|
+
phase: "error",
|
|
38
|
+
message: `GitHub API error: ${response.status}`,
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
exit();
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
const userData = (await response.json());
|
|
45
|
+
const installations = getInstallations();
|
|
46
|
+
setState({ phase: "logged_in", user: userData, installations });
|
|
47
|
+
}
|
|
48
|
+
catch (err) {
|
|
49
|
+
setState({
|
|
50
|
+
phase: "error",
|
|
51
|
+
message: err instanceof Error ? err.message : "Failed to fetch user info",
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
exit();
|
|
55
|
+
}
|
|
56
|
+
checkAuth();
|
|
57
|
+
}, [exit]);
|
|
58
|
+
switch (state.phase) {
|
|
59
|
+
case "checking":
|
|
60
|
+
return _jsx(Spinner, { text: "Checking authentication..." });
|
|
61
|
+
case "not_logged_in":
|
|
62
|
+
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" })] }));
|
|
63
|
+
case "logged_in":
|
|
64
|
+
return (_jsxs(Box, { flexDirection: "column", children: [_jsxs(Box, { children: [_jsx(Text, { children: "Logged in as " }), _jsx(Text, { color: "green", bold: true, children: state.user.login }), state.user.name && (_jsxs(Text, { dimColor: true, children: [" (", state.user.name, ")"] }))] }), _jsx(Text, { dimColor: true, children: state.user.html_url }), state.installations.length > 0 && (_jsxs(Box, { marginTop: 1, flexDirection: "column", children: [_jsx(Text, { children: "GitHub App Installations:" }), state.installations.map((inst) => (_jsxs(Text, { dimColor: true, children: ["\u2022 ", inst.account, " (", inst.accountType.toLowerCase(), ") -", " ", inst.repositorySelection === "all" ? "all repositories" : "selected repositories"] }, inst.id))), _jsx(Box, { marginTop: 1, children: _jsx(Text, { dimColor: true, children: "Use 'skilluse repos' to see accessible repositories." }) })] })), state.installations.length === 0 && (_jsx(Box, { marginTop: 1, children: _jsx(Text, { color: "yellow", children: "No GitHub App installations found." }) }))] }));
|
|
65
|
+
case "error":
|
|
66
|
+
return _jsx(StatusMessage, { type: "error", children: state.message });
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
//# sourceMappingURL=whoami.js.map
|