create-githat-app 0.4.1 → 0.5.1

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 (2) hide show
  1. package/dist/cli.js +611 -19
  2. package/package.json +26 -8
package/dist/cli.js CHANGED
@@ -1,9 +1,9 @@
1
1
  import { createRequire } from 'module'; const require = createRequire(import.meta.url);
2
2
 
3
3
  // src/cli.ts
4
- import { Command } from "commander";
5
- import * as p8 from "@clack/prompts";
6
- import chalk3 from "chalk";
4
+ import { Command as Command7 } from "commander";
5
+ import * as p9 from "@clack/prompts";
6
+ import chalk9 from "chalk";
7
7
 
8
8
  // src/utils/ascii.ts
9
9
  import figlet from "figlet";
@@ -11,7 +11,7 @@ import gradient from "gradient-string";
11
11
  import chalk from "chalk";
12
12
 
13
13
  // src/constants.ts
14
- var VERSION = "1.3.0";
14
+ var VERSION = "0.5.1";
15
15
  var DEFAULT_API_URL = "https://api.githat.io";
16
16
  var DASHBOARD_URL = "https://githat.io/dashboard/apps";
17
17
  var BRAND_COLORS = ["#7c3aed", "#6366f1", "#8b5cf6"];
@@ -21,7 +21,7 @@ var DEPS = {
21
21
  next: "^16.0.0",
22
22
  react: "^19.0.0",
23
23
  "react-dom": "^19.0.0",
24
- "@githat/nextjs": "^0.4.0"
24
+ "@githat/nextjs": "^0.5.0"
25
25
  },
26
26
  devDependencies: {
27
27
  typescript: "^5.9.0",
@@ -35,7 +35,7 @@ var DEPS = {
35
35
  react: "^19.0.0",
36
36
  "react-dom": "^19.0.0",
37
37
  "react-router-dom": "^7.0.0",
38
- "@githat/nextjs": "^0.4.0"
38
+ "@githat/nextjs": "^0.5.0"
39
39
  },
40
40
  devDependencies: {
41
41
  vite: "^7.0.0",
@@ -123,7 +123,7 @@ function sectionHeader(title) {
123
123
  console.log(dim(` \u2500\u2500\u2500 ${title} ${"\u2500".repeat(lineLen)}`));
124
124
  console.log("");
125
125
  }
126
- function displaySuccess(projectName, packageManager, framework) {
126
+ function displaySuccess(projectName, packageManager, framework, hasPublishableKey = true) {
127
127
  const devCmd = packageManager === "npm" ? "npm run dev" : `${packageManager} dev`;
128
128
  const port = framework === "react-vite" ? "5173" : "3000";
129
129
  console.log("");
@@ -140,6 +140,11 @@ function displaySuccess(projectName, packageManager, framework) {
140
140
  `${violet("/sign-up")} Create account`,
141
141
  `${violet("/dashboard")} Protected dashboard`,
142
142
  "",
143
+ ...hasPublishableKey ? [] : [
144
+ chalk.yellow("No key configured \u2014 auth works on localhost."),
145
+ `For production: ${violet("githat.io/dashboard/apps")}`,
146
+ ""
147
+ ],
143
148
  dim("Docs \u2192 https://githat.io/docs/sdk")
144
149
  ]);
145
150
  console.log("");
@@ -305,8 +310,8 @@ async function promptGitHat(existingKey) {
305
310
  }
306
311
  publishableKey = pastedKey || "";
307
312
  } else if (connectChoice === "skip") {
308
- p3.log.info("Your app will work on localhost without a key!");
309
- p3.log.info("For production, get your key at https://githat.io/dashboard/apps");
313
+ p3.log.info("Auth works on localhost without a key (CORS bypass for development).");
314
+ p3.log.info("Sign up at githat.io \u2014 a publishable key is auto-created for you.");
310
315
  }
311
316
  }
312
317
  const authFeatures = await p3.multiselect({
@@ -543,18 +548,18 @@ function sortKeys(obj) {
543
548
 
544
549
  // src/utils/spinner.ts
545
550
  import ora from "ora";
546
- function createSpinner(text3) {
547
- return ora({ text: text3, color: "magenta" });
551
+ function createSpinner(text4) {
552
+ return ora({ text: text4, color: "magenta" });
548
553
  }
549
- async function withSpinner(text3, fn, successText) {
550
- const spinner = createSpinner(text3);
554
+ async function withSpinner(text4, fn, successText) {
555
+ const spinner = createSpinner(text4);
551
556
  spinner.start();
552
557
  try {
553
558
  const result = await fn();
554
- spinner.succeed(successText || text3);
559
+ spinner.succeed(successText || text4);
555
560
  return result;
556
561
  } catch (err) {
557
- spinner.fail(text3);
562
+ spinner.fail(text4);
558
563
  throw err;
559
564
  }
560
565
  }
@@ -629,12 +634,598 @@ async function scaffold(context, options) {
629
634
  );
630
635
  }
631
636
  p7.outro("Setup complete!");
632
- displaySuccess(context.projectName, context.packageManager, context.framework);
637
+ displaySuccess(context.projectName, context.packageManager, context.framework, !!context.publishableKey);
638
+ const starPrompt = await p7.confirm({
639
+ message: "Star GitHat on GitHub? (helps us grow!)",
640
+ initialValue: false
641
+ });
642
+ if (!p7.isCancel(starPrompt) && starPrompt) {
643
+ try {
644
+ const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
645
+ execSync3(`${cmd} "https://github.com/GitHat-IO/githat"`, { stdio: "ignore" });
646
+ } catch {
647
+ p7.log.info("Visit https://github.com/GitHat-IO/githat to star us!");
648
+ }
649
+ }
650
+ }
651
+
652
+ // src/commands/skills/index.ts
653
+ import { Command as Command6 } from "commander";
654
+ import chalk8 from "chalk";
655
+
656
+ // src/commands/skills/search.ts
657
+ import { Command } from "commander";
658
+ import chalk3 from "chalk";
659
+
660
+ // src/commands/skills/api.ts
661
+ async function fetchApi(endpoint, options = {}) {
662
+ const url = `${DEFAULT_API_URL}${endpoint}`;
663
+ const response = await fetch(url, {
664
+ ...options,
665
+ headers: {
666
+ "Content-Type": "application/json",
667
+ ...options.headers
668
+ }
669
+ });
670
+ if (!response.ok) {
671
+ const error = await response.json().catch(() => ({ error: response.statusText }));
672
+ throw new Error(error.error || `API error: ${response.status}`);
673
+ }
674
+ return response.json();
675
+ }
676
+ async function searchSkills(query, type) {
677
+ const params = new URLSearchParams();
678
+ if (type) params.set("type", type);
679
+ const url = `/skills?${params.toString()}`;
680
+ const result = await fetchApi(url);
681
+ const q = query.toLowerCase();
682
+ return {
683
+ skills: result.skills.filter(
684
+ (s) => s.name.toLowerCase().includes(q) || s.description.toLowerCase().includes(q) || s.keywords.some((k) => k.toLowerCase().includes(q))
685
+ )
686
+ };
687
+ }
688
+ async function listSkills(options) {
689
+ const params = new URLSearchParams();
690
+ if (options.type) params.set("type", options.type);
691
+ if (options.limit) params.set("limit", options.limit.toString());
692
+ if (options.cursor) params.set("cursor", options.cursor);
693
+ return fetchApi(`/skills?${params.toString()}`);
694
+ }
695
+ async function getSkill(slug) {
696
+ return fetchApi(`/skills/${slug}`);
697
+ }
698
+ async function getDownloadUrl(slug, version) {
699
+ const params = version ? `?version=${version}` : "";
700
+ return fetchApi(`/skills/${slug}/download${params}`);
701
+ }
702
+
703
+ // src/commands/skills/search.ts
704
+ function formatSkill(skill) {
705
+ const typeColors = {
706
+ template: chalk3.blue,
707
+ integration: chalk3.green,
708
+ ui: chalk3.magenta,
709
+ ai: chalk3.yellow,
710
+ workflow: chalk3.cyan
711
+ };
712
+ const typeColor = typeColors[skill.type] || chalk3.white;
713
+ return [
714
+ `${chalk3.bold(skill.name)} ${chalk3.dim(`@${skill.latestVersion}`)}`,
715
+ ` ${skill.description}`,
716
+ ` ${typeColor(skill.type)} \xB7 \u2B07 ${skill.downloads} \xB7 \u2B50 ${skill.stars} \xB7 by ${skill.authorName}`,
717
+ ` ${chalk3.dim(`githat skills install ${skill.slug}`)}`
718
+ ].join("\n");
719
+ }
720
+ var searchCommand = new Command("search").description("Search skills by keyword").argument("<query>", "Search query").option("-t, --type <type>", "Filter by type (template, integration, ui, ai, workflow)").action(async (query, options) => {
721
+ try {
722
+ console.log(chalk3.dim(`
723
+ Searching for "${query}"...
724
+ `));
725
+ const result = await searchSkills(query, options.type);
726
+ if (result.skills.length === 0) {
727
+ console.log(chalk3.yellow("No skills found matching your query."));
728
+ console.log(chalk3.dim("\nTry a different search term or browse all skills:"));
729
+ console.log(chalk3.dim(" githat skills list"));
730
+ return;
731
+ }
732
+ console.log(chalk3.cyan(`Found ${result.skills.length} skill(s):
733
+ `));
734
+ for (const skill of result.skills) {
735
+ console.log(formatSkill(skill));
736
+ console.log("");
737
+ }
738
+ } catch (err) {
739
+ console.error(chalk3.red(`Error: ${err.message}`));
740
+ process.exit(1);
741
+ }
742
+ });
743
+
744
+ // src/commands/skills/list.ts
745
+ import { Command as Command2 } from "commander";
746
+ import chalk4 from "chalk";
747
+ function formatSkillCompact(skill) {
748
+ const typeColors = {
749
+ template: chalk4.blue,
750
+ integration: chalk4.green,
751
+ ui: chalk4.magenta,
752
+ ai: chalk4.yellow,
753
+ workflow: chalk4.cyan
754
+ };
755
+ const typeColor = typeColors[skill.type] || chalk4.white;
756
+ const name = chalk4.bold(skill.name.padEnd(25));
757
+ const type = typeColor(skill.type.padEnd(12));
758
+ const stats = `\u2B07 ${String(skill.downloads).padStart(5)} \u2B50 ${String(skill.stars).padStart(4)}`;
759
+ const desc = skill.description.length > 40 ? skill.description.substring(0, 37) + "..." : skill.description;
760
+ return `${name} ${type} ${stats} ${chalk4.dim(desc)}`;
761
+ }
762
+ var listCommand = new Command2("list").description("List available skills").option("-t, --type <type>", "Filter by type (template, integration, ui, ai, workflow)").option("-l, --limit <n>", "Number of results (default: 25)", "25").action(async (options) => {
763
+ try {
764
+ const limit = parseInt(options.limit, 10);
765
+ console.log(chalk4.dim("\nFetching skills...\n"));
766
+ const result = await listSkills({ type: options.type, limit });
767
+ if (result.skills.length === 0) {
768
+ console.log(chalk4.yellow("No skills found."));
769
+ if (options.type) {
770
+ console.log(chalk4.dim(`
771
+ Try without the type filter:`));
772
+ console.log(chalk4.dim(" githat skills list"));
773
+ }
774
+ return;
775
+ }
776
+ const header = `${"NAME".padEnd(25)} ${"TYPE".padEnd(12)} ${"DOWNLOADS".padStart(10)} DESCRIPTION`;
777
+ console.log(chalk4.dim(header));
778
+ console.log(chalk4.dim("\u2500".repeat(80)));
779
+ for (const skill of result.skills) {
780
+ console.log(formatSkillCompact(skill));
781
+ }
782
+ console.log(chalk4.dim("\u2500".repeat(80)));
783
+ console.log(chalk4.dim(`Showing ${result.skills.length} skill(s)`));
784
+ if (result.nextCursor) {
785
+ console.log(chalk4.dim("\nMore results available. Use --limit to see more."));
786
+ }
787
+ console.log(chalk4.dim("\nTo install: githat skills install <name>"));
788
+ } catch (err) {
789
+ console.error(chalk4.red(`Error: ${err.message}`));
790
+ process.exit(1);
791
+ }
792
+ });
793
+
794
+ // src/commands/skills/install.ts
795
+ import { Command as Command3 } from "commander";
796
+ import chalk5 from "chalk";
797
+ import * as fs4 from "fs";
798
+ import * as path4 from "path";
799
+ import { pipeline } from "stream/promises";
800
+ import { createWriteStream, mkdirSync } from "fs";
801
+ import { Extract } from "unzipper";
802
+ async function downloadAndExtract(url, destDir) {
803
+ const response = await fetch(url);
804
+ if (!response.ok) {
805
+ throw new Error(`Download failed: ${response.statusText}`);
806
+ }
807
+ const tempZip = path4.join(destDir, ".skill-download.zip");
808
+ const fileStream = createWriteStream(tempZip);
809
+ await pipeline(response.body, fileStream);
810
+ await new Promise((resolve4, reject) => {
811
+ fs4.createReadStream(tempZip).pipe(Extract({ path: destDir })).on("close", resolve4).on("error", reject);
812
+ });
813
+ fs4.unlinkSync(tempZip);
814
+ }
815
+ function updateGithatLock(projectDir, skill) {
816
+ const lockPath = path4.join(projectDir, "githat.lock");
817
+ let lock = {};
818
+ if (fs4.existsSync(lockPath)) {
819
+ try {
820
+ lock = JSON.parse(fs4.readFileSync(lockPath, "utf-8"));
821
+ } catch {
822
+ }
823
+ }
824
+ lock[skill.slug] = {
825
+ version: skill.version,
826
+ installedAt: (/* @__PURE__ */ new Date()).toISOString()
827
+ };
828
+ fs4.writeFileSync(lockPath, JSON.stringify(lock, null, 2));
829
+ }
830
+ function updateEnvExample(projectDir, manifest) {
831
+ if (!manifest.requires?.env?.length) return;
832
+ const envPath = path4.join(projectDir, ".env.local");
833
+ const envExamplePath = path4.join(projectDir, ".env.example");
834
+ let envContent = "";
835
+ if (fs4.existsSync(envPath)) {
836
+ envContent = fs4.readFileSync(envPath, "utf-8");
837
+ } else if (fs4.existsSync(envExamplePath)) {
838
+ envContent = fs4.readFileSync(envExamplePath, "utf-8");
839
+ }
840
+ const existingVars = new Set(
841
+ envContent.split("\n").filter((line) => line.includes("=")).map((line) => line.split("=")[0].trim())
842
+ );
843
+ const newVars = [];
844
+ for (const envVar of manifest.requires.env) {
845
+ if (!existingVars.has(envVar)) {
846
+ newVars.push(`${envVar}=`);
847
+ }
848
+ }
849
+ if (newVars.length > 0) {
850
+ const addition = `
851
+ # Added by skill install
852
+ ${newVars.join("\n")}
853
+ `;
854
+ if (fs4.existsSync(envPath)) {
855
+ fs4.appendFileSync(envPath, addition);
856
+ } else {
857
+ fs4.writeFileSync(envPath, `# Environment variables
858
+ ${newVars.join("\n")}
859
+ `);
860
+ }
861
+ }
862
+ }
863
+ var installCommand = new Command3("install").description("Install a skill to your project").argument("<slug>", "Skill slug (e.g., stripe-billing)").option("-v, --version <version>", "Specific version to install").option("-d, --dir <dir>", "Project directory (default: current directory)").action(async (slug, options) => {
864
+ try {
865
+ const projectDir = options.dir ? path4.resolve(options.dir) : process.cwd();
866
+ const packageJsonPath = path4.join(projectDir, "package.json");
867
+ if (!fs4.existsSync(packageJsonPath)) {
868
+ console.error(chalk5.red("Error: No package.json found. Are you in a project directory?"));
869
+ process.exit(1);
870
+ }
871
+ console.log(chalk5.dim(`
872
+ Fetching skill info for "${slug}"...
873
+ `));
874
+ const skill = await getSkill(slug);
875
+ console.log(chalk5.cyan(`\u{1F4E6} ${skill.name}`));
876
+ console.log(chalk5.dim(` ${skill.description}`));
877
+ console.log(chalk5.dim(` Type: ${skill.type} \xB7 Author: ${skill.authorName}
878
+ `));
879
+ const download = await getDownloadUrl(slug, options.version);
880
+ const version = download.version.version;
881
+ console.log(chalk5.dim(`Downloading ${skill.name}@${version}...`));
882
+ const skillDir = path4.join(projectDir, "githat", "skills", slug);
883
+ mkdirSync(skillDir, { recursive: true });
884
+ await downloadAndExtract(download.downloadUrl, skillDir);
885
+ console.log(chalk5.green(`\u2713 Downloaded to ${path4.relative(projectDir, skillDir)}`));
886
+ const manifestPath = path4.join(skillDir, "githat-skill.json");
887
+ if (fs4.existsSync(manifestPath)) {
888
+ const manifest = JSON.parse(fs4.readFileSync(manifestPath, "utf-8"));
889
+ updateEnvExample(projectDir, manifest);
890
+ if (manifest.requires?.env?.length) {
891
+ console.log(chalk5.yellow(`
892
+ \u26A0 Required environment variables:`));
893
+ for (const envVar of manifest.requires.env) {
894
+ console.log(chalk5.dim(` ${envVar}`));
895
+ }
896
+ console.log(chalk5.dim(`
897
+ Add these to your .env.local file`));
898
+ }
899
+ if (manifest.install?.dependencies) {
900
+ const deps = Object.entries(manifest.install.dependencies).map(([name, ver]) => `${name}@${ver}`).join(" ");
901
+ console.log(chalk5.yellow(`
902
+ \u26A0 Install npm dependencies:`));
903
+ console.log(chalk5.dim(` npm install ${deps}`));
904
+ }
905
+ }
906
+ updateGithatLock(projectDir, { slug, version });
907
+ console.log(chalk5.green(`
908
+ \u2705 Successfully installed ${skill.name}@${version}
909
+ `));
910
+ console.log(chalk5.dim("Next steps:"));
911
+ console.log(chalk5.dim(` 1. Check githat/skills/${slug}/README.md for usage`));
912
+ console.log(chalk5.dim(" 2. Add required environment variables to .env.local"));
913
+ console.log(chalk5.dim(" 3. Import and use the skill in your code"));
914
+ } catch (err) {
915
+ console.error(chalk5.red(`Error: ${err.message}`));
916
+ process.exit(1);
917
+ }
918
+ });
919
+
920
+ // src/commands/skills/installed.ts
921
+ import { Command as Command4 } from "commander";
922
+ import chalk6 from "chalk";
923
+ import * as fs5 from "fs";
924
+ import * as path5 from "path";
925
+ var installedCommand = new Command4("installed").alias("ls").description("List installed skills in current project").option("-d, --dir <dir>", "Project directory (default: current directory)").action(async (options) => {
926
+ try {
927
+ const projectDir = options.dir ? path5.resolve(options.dir) : process.cwd();
928
+ const lockPath = path5.join(projectDir, "githat.lock");
929
+ if (!fs5.existsSync(lockPath)) {
930
+ console.log(chalk6.yellow("\nNo skills installed in this project."));
931
+ console.log(chalk6.dim("\nTo install a skill:"));
932
+ console.log(chalk6.dim(" githat skills install <slug>"));
933
+ return;
934
+ }
935
+ let lock;
936
+ try {
937
+ lock = JSON.parse(fs5.readFileSync(lockPath, "utf-8"));
938
+ } catch {
939
+ console.error(chalk6.red("Error: Invalid githat.lock file"));
940
+ process.exit(1);
941
+ }
942
+ const entries = Object.entries(lock);
943
+ if (entries.length === 0) {
944
+ console.log(chalk6.yellow("\nNo skills installed in this project."));
945
+ return;
946
+ }
947
+ console.log(chalk6.cyan(`
948
+ \u{1F4E6} Installed skills (${entries.length}):
949
+ `));
950
+ console.log(chalk6.dim(`${"SKILL".padEnd(30)} ${"VERSION".padEnd(12)} INSTALLED`));
951
+ console.log(chalk6.dim("\u2500".repeat(60)));
952
+ for (const [slug, entry] of entries) {
953
+ const date = new Date(entry.installedAt).toLocaleDateString();
954
+ console.log(`${chalk6.bold(slug.padEnd(30))} ${entry.version.padEnd(12)} ${chalk6.dim(date)}`);
955
+ }
956
+ console.log(chalk6.dim("\u2500".repeat(60)));
957
+ console.log(chalk6.dim("\nTo update a skill:"));
958
+ console.log(chalk6.dim(" githat skills install <slug> --version <new-version>"));
959
+ } catch (err) {
960
+ console.error(chalk6.red(`Error: ${err.message}`));
961
+ process.exit(1);
962
+ }
963
+ });
964
+
965
+ // src/commands/skills/init.ts
966
+ import { Command as Command5 } from "commander";
967
+ import chalk7 from "chalk";
968
+ import * as fs6 from "fs";
969
+ import * as path6 from "path";
970
+ import * as p8 from "@clack/prompts";
971
+ var SKILL_TYPES = ["template", "integration", "ui", "ai", "workflow"];
972
+ function generateReadme(manifest) {
973
+ return `# ${manifest.name}
974
+
975
+ ${manifest.description}
976
+
977
+ ## Installation
978
+
979
+ \`\`\`bash
980
+ githat skills install ${manifest.name}
981
+ \`\`\`
982
+
983
+ ## Requirements
984
+
985
+ ${manifest.requires.env.length > 0 ? `
986
+ ### Environment Variables
987
+
988
+ ${manifest.requires.env.map((e) => `- \`${e}\``).join("\n")}
989
+ ` : ""}
990
+ ${manifest.requires.tier ? `
991
+ ### Minimum Tier
992
+
993
+ This skill requires **${manifest.requires.tier}** tier or higher.
994
+ ` : ""}
995
+
996
+ ## Usage
997
+
998
+ \`\`\`typescript
999
+ // Import from the installed skill
1000
+ import { /* exports */ } from './githat/skills/${manifest.name}';
1001
+
1002
+ // Use the skill
1003
+ // ...
1004
+ \`\`\`
1005
+
1006
+ ## License
1007
+
1008
+ ${manifest.license}
1009
+ `;
1010
+ }
1011
+ function generateIndexFile(manifest) {
1012
+ const typeExports = {
1013
+ template: `// Template skill - provides project scaffolding
1014
+ export const templateName = '${manifest.name}';
1015
+ export const templateVersion = '${manifest.version}';
1016
+
1017
+ // Add your template exports here
1018
+ `,
1019
+ integration: `// Integration skill - connects to external services
1020
+ // Replace with your actual integration code
1021
+
1022
+ export interface ${toPascalCase(manifest.name)}Config {
1023
+ apiKey: string;
1024
+ // Add configuration options
1025
+ }
1026
+
1027
+ export function create${toPascalCase(manifest.name)}(config: ${toPascalCase(manifest.name)}Config) {
1028
+ // Initialize your integration
1029
+ return {
1030
+ // Return your integration client/functions
1031
+ };
1032
+ }
1033
+ `,
1034
+ ui: `// UI skill - provides React components
1035
+ import React from 'react';
1036
+
1037
+ export interface ${toPascalCase(manifest.name)}Props {
1038
+ // Add component props
633
1039
  }
634
1040
 
1041
+ export function ${toPascalCase(manifest.name)}({ ...props }: ${toPascalCase(manifest.name)}Props) {
1042
+ return (
1043
+ <div>
1044
+ {/* Your component */}
1045
+ </div>
1046
+ );
1047
+ }
1048
+ `,
1049
+ ai: `// AI skill - MCP server / Claude tools
1050
+ export interface Tool {
1051
+ name: string;
1052
+ description: string;
1053
+ inputSchema: Record<string, unknown>;
1054
+ }
1055
+
1056
+ export const tools: Tool[] = [
1057
+ {
1058
+ name: '${manifest.name.replace(/-/g, "_")}',
1059
+ description: '${manifest.description}',
1060
+ inputSchema: {
1061
+ type: 'object',
1062
+ properties: {
1063
+ // Add input properties
1064
+ },
1065
+ },
1066
+ },
1067
+ ];
1068
+
1069
+ export async function handleTool(name: string, input: Record<string, unknown>) {
1070
+ // Handle tool invocations
1071
+ }
1072
+ `,
1073
+ workflow: `// Workflow skill - automation recipes
1074
+ export interface WorkflowTrigger {
1075
+ event: string;
1076
+ condition?: string;
1077
+ }
1078
+
1079
+ export interface WorkflowStep {
1080
+ id: string;
1081
+ run?: string;
1082
+ action?: string;
1083
+ }
1084
+
1085
+ export const triggers: WorkflowTrigger[] = [
1086
+ { event: 'deploy.success' },
1087
+ ];
1088
+
1089
+ export const steps: WorkflowStep[] = [
1090
+ { id: 'notify', run: 'echo "Workflow executed"' },
1091
+ ];
1092
+ `
1093
+ };
1094
+ return typeExports[manifest.type];
1095
+ }
1096
+ function toPascalCase(str) {
1097
+ return str.split("-").map((word) => word.charAt(0).toUpperCase() + word.slice(1)).join("");
1098
+ }
1099
+ var initCommand = new Command5("init").description("Initialize a new skill package").argument("<name>", "Skill name (slug format: lowercase-with-hyphens)").option("-t, --type <type>", "Skill type (template, integration, ui, ai, workflow)").option("-d, --dir <dir>", "Parent directory (default: current directory)").action(async (name, options) => {
1100
+ try {
1101
+ if (!/^[a-z][a-z0-9-]{1,62}[a-z0-9]$/.test(name)) {
1102
+ console.error(chalk7.red("Error: Name must be lowercase alphanumeric with hyphens (2-64 chars, start with letter)"));
1103
+ process.exit(1);
1104
+ }
1105
+ const parentDir = options.dir ? path6.resolve(options.dir) : process.cwd();
1106
+ const skillDir = path6.join(parentDir, name);
1107
+ if (fs6.existsSync(skillDir)) {
1108
+ console.error(chalk7.red(`Error: Directory "${name}" already exists`));
1109
+ process.exit(1);
1110
+ }
1111
+ console.log(chalk7.cyan(`
1112
+ \u{1F4E6} Initializing skill: ${name}
1113
+ `));
1114
+ let type;
1115
+ if (options.type && SKILL_TYPES.includes(options.type)) {
1116
+ type = options.type;
1117
+ } else {
1118
+ const result = await p8.select({
1119
+ message: "What type of skill are you creating?",
1120
+ options: [
1121
+ { value: "integration", label: "Integration", hint: "Connect to external services (Stripe, SendGrid, etc.)" },
1122
+ { value: "template", label: "Template", hint: "Full project scaffolding" },
1123
+ { value: "ui", label: "UI Pack", hint: "Reusable React components" },
1124
+ { value: "ai", label: "AI Skill", hint: "MCP server / Claude tools" },
1125
+ { value: "workflow", label: "Workflow", hint: "Automation recipes" }
1126
+ ]
1127
+ });
1128
+ if (p8.isCancel(result)) {
1129
+ p8.cancel("Operation cancelled");
1130
+ process.exit(0);
1131
+ }
1132
+ type = result;
1133
+ }
1134
+ const description = await p8.text({
1135
+ message: "Short description:",
1136
+ placeholder: `A ${type} skill for...`,
1137
+ validate: (v) => v.length < 10 ? "Description must be at least 10 characters" : void 0
1138
+ });
1139
+ if (p8.isCancel(description)) {
1140
+ p8.cancel("Operation cancelled");
1141
+ process.exit(0);
1142
+ }
1143
+ const manifest = {
1144
+ name,
1145
+ version: "1.0.0",
1146
+ description,
1147
+ type,
1148
+ author: {
1149
+ name: "Your Name",
1150
+ email: "your@email.com"
1151
+ },
1152
+ license: "MIT",
1153
+ requires: {
1154
+ env: []
1155
+ },
1156
+ files: {
1157
+ lib: "src/index.ts"
1158
+ },
1159
+ install: {
1160
+ dependencies: {},
1161
+ envExample: {}
1162
+ },
1163
+ keywords: [type]
1164
+ };
1165
+ fs6.mkdirSync(skillDir, { recursive: true });
1166
+ fs6.mkdirSync(path6.join(skillDir, "src"), { recursive: true });
1167
+ fs6.writeFileSync(
1168
+ path6.join(skillDir, "githat-skill.json"),
1169
+ JSON.stringify(manifest, null, 2)
1170
+ );
1171
+ fs6.writeFileSync(
1172
+ path6.join(skillDir, "README.md"),
1173
+ generateReadme(manifest)
1174
+ );
1175
+ fs6.writeFileSync(
1176
+ path6.join(skillDir, "src", "index.ts"),
1177
+ generateIndexFile(manifest)
1178
+ );
1179
+ fs6.writeFileSync(
1180
+ path6.join(skillDir, ".gitignore"),
1181
+ `node_modules/
1182
+ dist/
1183
+ .env
1184
+ .env.local
1185
+ *.log
1186
+ `
1187
+ );
1188
+ console.log(chalk7.green(`
1189
+ \u2705 Created skill at ${skillDir}
1190
+ `));
1191
+ console.log(chalk7.dim("Files created:"));
1192
+ console.log(chalk7.dim(` githat-skill.json - Skill manifest`));
1193
+ console.log(chalk7.dim(` README.md - Documentation`));
1194
+ console.log(chalk7.dim(` src/index.ts - Main entry point`));
1195
+ console.log(chalk7.dim(` .gitignore - Git ignore rules`));
1196
+ console.log(chalk7.dim("\nNext steps:"));
1197
+ console.log(chalk7.dim(` 1. cd ${name}`));
1198
+ console.log(chalk7.dim(` 2. Edit githat-skill.json with your details`));
1199
+ console.log(chalk7.dim(` 3. Implement your skill in src/index.ts`));
1200
+ console.log(chalk7.dim(` 4. Publish: githat skills publish .`));
1201
+ } catch (err) {
1202
+ console.error(chalk7.red(`Error: ${err.message}`));
1203
+ process.exit(1);
1204
+ }
1205
+ });
1206
+
1207
+ // src/commands/skills/index.ts
1208
+ var skillsCommand = new Command6("skills").description("Manage GitHat skills marketplace").addCommand(searchCommand).addCommand(listCommand).addCommand(installCommand).addCommand(installedCommand).addCommand(initCommand);
1209
+ skillsCommand.action(() => {
1210
+ console.log(chalk8.cyan("\n\u{1F4E6} GitHat Skills Marketplace\n"));
1211
+ console.log("Commands:");
1212
+ console.log(" search <query> Search skills by keyword");
1213
+ console.log(" list List skills (filterable by type)");
1214
+ console.log(" install <slug> Install a skill to your project");
1215
+ console.log(" installed List installed skills");
1216
+ console.log(" init <name> Initialize a new skill package");
1217
+ console.log("\nExamples:");
1218
+ console.log(" githat skills search stripe");
1219
+ console.log(" githat skills list --type=integration");
1220
+ console.log(" githat skills install stripe-billing");
1221
+ console.log(" githat skills init my-skill --type=integration");
1222
+ console.log("");
1223
+ });
1224
+
635
1225
  // src/cli.ts
636
- var program = new Command();
637
- program.name("create-githat-app").description("Scaffold enterprise-grade apps with GitHat identity").version(VERSION).argument("[project-name]", "Name of the project directory").option("--key <key>", "GitHat publishable key (pk_live_...)").option("--ts", "Use TypeScript (default)").option("--js", "Use JavaScript").action(async (projectName, opts) => {
1226
+ var program = new Command7();
1227
+ program.name("githat").description("GitHat CLI - Scaffold apps and manage skills").version(VERSION);
1228
+ program.command("create [project-name]", { isDefault: true }).description("Scaffold a new GitHat app").option("--key <key>", "GitHat publishable key (pk_live_...)").option("--ts", "Use TypeScript (default)").option("--js", "Use JavaScript").action(async (projectName, opts) => {
638
1229
  try {
639
1230
  displayBanner();
640
1231
  const typescript = opts.js ? false : opts.ts ? true : void 0;
@@ -649,8 +1240,9 @@ program.name("create-githat-app").description("Scaffold enterprise-grade apps wi
649
1240
  initGit: answers.initGit
650
1241
  });
651
1242
  } catch (err) {
652
- p8.cancel(chalk3.red(err.message || "Something went wrong."));
1243
+ p9.cancel(chalk9.red(err.message || "Something went wrong."));
653
1244
  process.exit(1);
654
1245
  }
655
1246
  });
1247
+ program.addCommand(skillsCommand);
656
1248
  program.parse();
package/package.json CHANGED
@@ -1,35 +1,53 @@
1
1
  {
2
2
  "name": "create-githat-app",
3
- "version": "0.4.1",
4
- "description": "Scaffold enterprise-grade apps with GitHat identityfor humans, AI agents, and MCP servers",
3
+ "version": "0.5.1",
4
+ "description": "GitHat CLIscaffold apps and manage the skills marketplace",
5
5
  "type": "module",
6
6
  "bin": {
7
- "create-githat-app": "./bin/index.js"
7
+ "create-githat-app": "./bin/index.js",
8
+ "githat": "./bin/index.js"
8
9
  },
9
- "files": ["bin", "dist", "templates"],
10
+ "files": [
11
+ "bin",
12
+ "dist",
13
+ "templates"
14
+ ],
10
15
  "scripts": {
11
16
  "build": "tsup",
12
17
  "dev": "tsup --watch",
13
18
  "test": "node bin/index.js --help"
14
19
  },
15
20
  "dependencies": {
16
- "@clack/prompts": "^0.9.1",
21
+ "@clack/prompts": "^1.0.1",
17
22
  "chalk": "^5.4.1",
18
- "commander": "^13.1.0",
23
+ "commander": "^14.0.3",
19
24
  "figlet": "^1.8.0",
20
25
  "fs-extra": "^11.2.0",
21
26
  "gradient-string": "^3.0.0",
22
27
  "handlebars": "^4.7.8",
23
- "ora": "^8.1.1"
28
+ "ora": "^9.3.0",
29
+ "unzipper": "^0.12.3"
24
30
  },
25
31
  "devDependencies": {
26
32
  "@types/figlet": "^1.7.0",
27
33
  "@types/fs-extra": "^11.0.4",
28
34
  "@types/gradient-string": "^1.1.6",
35
+ "@types/unzipper": "^0.10.10",
29
36
  "tsup": "^8.4.0",
30
37
  "typescript": "^5.9.0"
31
38
  },
32
- "keywords": ["githat", "nextjs", "react", "vite", "create-app", "cli", "auth", "identity", "mcp", "ai-agents"],
39
+ "keywords": [
40
+ "githat",
41
+ "nextjs",
42
+ "react",
43
+ "vite",
44
+ "create-app",
45
+ "cli",
46
+ "auth",
47
+ "identity",
48
+ "mcp",
49
+ "ai-agents"
50
+ ],
33
51
  "license": "SEE LICENSE IN LICENSE",
34
52
  "publishConfig": {
35
53
  "access": "public",