add-ai-tools 1.2.2 → 1.2.4

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/index.mjs +89 -20
  2. package/package.json +1 -1
package/dist/index.mjs CHANGED
@@ -214,8 +214,10 @@ const BITBUCKET_URL_REGEX = /^https?:\/\/(?:[^@]+@)?(?:www\.)?bitbucket\.org\/([
214
214
  * GitHub shorthand 매칭 정규식
215
215
  * 예: owner/repo
216
216
  * vercel-labs/agent-skills
217
+ * khw1031/core/skills
218
+ * owner/repo/deep/nested/path
217
219
  */
218
- const GITHUB_SHORTHAND_REGEX = /^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)$/;
220
+ const GITHUB_SHORTHAND_REGEX = /^([a-zA-Z0-9_.-]+)\/([a-zA-Z0-9_.-]+)(?:\/(.+))?$/;
219
221
  /**
220
222
  * Git URL 매칭 정규식 (SSH 또는 git:// 프로토콜)
221
223
  * 예: git@github.com:owner/repo.git
@@ -308,12 +310,13 @@ function parseSource(input) {
308
310
  }
309
311
  const shorthandMatch = trimmed.match(GITHUB_SHORTHAND_REGEX);
310
312
  if (shorthandMatch) {
311
- const [, owner, repo] = shorthandMatch;
313
+ const [, owner, repo, subpath] = shorthandMatch;
312
314
  return {
313
315
  type: "github",
314
316
  url: `https://github.com/${owner}/${repo}`,
315
317
  owner,
316
318
  repo,
319
+ subpath: subpath || void 0,
317
320
  raw: input
318
321
  };
319
322
  }
@@ -485,9 +488,22 @@ var GitHubFetcher = class {
485
488
  parser;
486
489
  baseApiUrl = "https://api.github.com";
487
490
  baseRawUrl = "https://raw.githubusercontent.com";
491
+ token;
488
492
  concurrencyLimit = 10;
489
493
  constructor() {
490
494
  this.parser = new ResourceParser();
495
+ this.token = process.env.GITHUB_TOKEN || process.env.GH_TOKEN;
496
+ }
497
+ /**
498
+ * 인증 헤더를 포함한 공통 헤더를 반환합니다
499
+ */
500
+ getHeaders() {
501
+ const headers = {
502
+ Accept: "application/vnd.github.v3+json",
503
+ "User-Agent": "ai-toolkit"
504
+ };
505
+ if (this.token) headers["Authorization"] = `Bearer ${this.token}`;
506
+ return headers;
491
507
  }
492
508
  /**
493
509
  * GitHub 소스에서 리소스 목록을 가져옵니다
@@ -513,10 +529,7 @@ var GitHubFetcher = class {
513
529
  async fetchTree(owner, repo, ref) {
514
530
  const sha = await this.getRefSha(owner, repo, ref);
515
531
  const url = `${this.baseApiUrl}/repos/${owner}/${repo}/git/trees/${sha}?recursive=1`;
516
- const response = await fetch(url, { headers: {
517
- Accept: "application/vnd.github.v3+json",
518
- "User-Agent": "ai-toolkit"
519
- } });
532
+ const response = await fetch(url, { headers: this.getHeaders() });
520
533
  if (!response.ok) this.handleHttpError(response.status, `tree/${sha}`);
521
534
  const data = await response.json();
522
535
  if (data.truncated) console.warn("Warning: Repository tree was truncated. Some files may be missing.");
@@ -527,10 +540,7 @@ var GitHubFetcher = class {
527
540
  */
528
541
  async getDefaultBranch(owner, repo) {
529
542
  const url = `${this.baseApiUrl}/repos/${owner}/${repo}`;
530
- const response = await fetch(url, { headers: {
531
- Accept: "application/vnd.github.v3+json",
532
- "User-Agent": "ai-toolkit"
533
- } });
543
+ const response = await fetch(url, { headers: this.getHeaders() });
534
544
  if (!response.ok) return "main";
535
545
  return (await response.json()).default_branch || "main";
536
546
  }
@@ -539,10 +549,7 @@ var GitHubFetcher = class {
539
549
  */
540
550
  async getRefSha(owner, repo, ref) {
541
551
  const branchUrl = `${this.baseApiUrl}/repos/${owner}/${repo}/branches/${ref}`;
542
- const response = await fetch(branchUrl, { headers: {
543
- Accept: "application/vnd.github.v3+json",
544
- "User-Agent": "ai-toolkit"
545
- } });
552
+ const response = await fetch(branchUrl, { headers: this.getHeaders() });
546
553
  if (response.ok) return (await response.json()).commit.sha;
547
554
  if (response.status === 404) return ref;
548
555
  this.handleHttpError(response.status, `branches/${ref}`);
@@ -585,7 +592,7 @@ var GitHubFetcher = class {
585
592
  */
586
593
  async fetchFileContent(owner, repo, path, ref) {
587
594
  const url = `${this.baseRawUrl}/${owner}/${repo}/${ref}/${path}`;
588
- const response = await fetch(url);
595
+ const response = await fetch(url, { headers: this.getHeaders() });
589
596
  if (!response.ok) this.handleHttpError(response.status, path);
590
597
  return response.text();
591
598
  }
@@ -680,7 +687,7 @@ var GitHubFetcher = class {
680
687
  switch (status) {
681
688
  case 404: throw new GitHubNotFoundError(path);
682
689
  case 403: throw new GitHubRateLimitError(path);
683
- case 401: throw new GitHubApiError("Authentication required", status, path);
690
+ case 401: throw new GitHubApiError("Authentication required. Set GITHUB_TOKEN or GH_TOKEN environment variable for private repos.", status, path);
684
691
  case 500:
685
692
  case 502:
686
693
  case 503: throw new GitHubApiError("GitHub server error. Please try again later.", status, path);
@@ -987,12 +994,22 @@ const bitbucketFetcher = new BitbucketFetcher();
987
994
 
988
995
  //#endregion
989
996
  //#region src/prompts/InteractivePrompt.ts
997
+ const FILTER_THRESHOLD$1 = 15;
990
998
  /**
991
999
  * InteractivePrompt - 외부 소스 기반 설치 플로우
992
1000
  *
993
1001
  * 플로우: Agent → Source URL → Type 선택 → Resources 선택 → Scope → Confirm
994
1002
  */
995
- var InteractivePrompt = class {
1003
+ var InteractivePrompt = class InteractivePrompt {
1004
+ /**
1005
+ * 키워드로 리소스 필터링 (name, type, description, metadata.category 대상, 대소문자 무시)
1006
+ */
1007
+ static filterByKeyword(resources, keyword) {
1008
+ const lower = keyword.toLowerCase();
1009
+ return resources.filter((r) => {
1010
+ return r.name.toLowerCase().includes(lower) || r.type.toLowerCase().includes(lower) || r.description.toLowerCase().includes(lower) || r.metadata.category && r.metadata.category.toLowerCase().includes(lower);
1011
+ });
1012
+ }
996
1013
  /**
997
1014
  * Interactive 플로우 실행
998
1015
  */
@@ -1113,14 +1130,35 @@ var InteractivePrompt = class {
1113
1130
  return firstLine;
1114
1131
  }
1115
1132
  /**
1133
+ * 리소스가 많을 때 키워드 필터링 프롬프트 표시
1134
+ */
1135
+ async filterResourcesIfNeeded(resources) {
1136
+ if (resources.length <= FILTER_THRESHOLD$1) return resources;
1137
+ const { keyword } = await inquirer.prompt([{
1138
+ type: "input",
1139
+ name: "keyword",
1140
+ message: `${resources.length} resources found. Enter keyword to filter (press Enter to show all):`
1141
+ }]);
1142
+ const trimmed = keyword.trim();
1143
+ if (!trimmed) return resources;
1144
+ const filtered = InteractivePrompt.filterByKeyword(resources, trimmed);
1145
+ if (filtered.length === 0) {
1146
+ console.log(`No resources matched "${trimmed}". Showing all resources.`);
1147
+ return resources;
1148
+ }
1149
+ console.log(`Filtered to ${filtered.length} resource(s).`);
1150
+ return filtered;
1151
+ }
1152
+ /**
1116
1153
  * 리소스 선택 (가져온 리소스 목록에서)
1117
1154
  */
1118
1155
  async selectResources(availableResources, types) {
1119
- const filteredResources = availableResources.filter((r) => types.includes(r.type));
1156
+ let filteredResources = availableResources.filter((r) => types.includes(r.type));
1120
1157
  if (filteredResources.length === 0) {
1121
1158
  console.log("\nNo resources found for selected types.");
1122
1159
  return [];
1123
1160
  }
1161
+ filteredResources = await this.filterResourcesIfNeeded(filteredResources);
1124
1162
  const { resources } = await inquirer.prompt([{
1125
1163
  type: "checkbox",
1126
1164
  name: "resources",
@@ -1887,12 +1925,13 @@ const commandHandler = new CommandHandler();
1887
1925
 
1888
1926
  //#endregion
1889
1927
  //#region src/prompts/ZipPrompt.ts
1928
+ const FILTER_THRESHOLD = 15;
1890
1929
  /**
1891
1930
  * ZipPrompt - ZIP 내보내기용 선택 플로우
1892
1931
  *
1893
1932
  * 플로우: Types → Resources → Confirm
1894
1933
  */
1895
- var ZipPrompt = class {
1934
+ var ZipPrompt = class ZipPrompt {
1896
1935
  /**
1897
1936
  * ZIP 플로우 실행
1898
1937
  */
@@ -1937,14 +1976,44 @@ var ZipPrompt = class {
1937
1976
  return selected;
1938
1977
  }
1939
1978
  /**
1979
+ * 키워드로 리소스 필터링 (name, type, description, metadata.category 대상, 대소문자 무시)
1980
+ */
1981
+ static filterByKeyword(resources, keyword) {
1982
+ const lower = keyword.toLowerCase();
1983
+ return resources.filter((r) => {
1984
+ return r.name.toLowerCase().includes(lower) || r.type.toLowerCase().includes(lower) || r.description.toLowerCase().includes(lower) || r.metadata.category && r.metadata.category.toLowerCase().includes(lower);
1985
+ });
1986
+ }
1987
+ /**
1988
+ * 리소스가 많을 때 키워드 필터링 프롬프트 표시
1989
+ */
1990
+ async filterResourcesIfNeeded(resources) {
1991
+ if (resources.length <= FILTER_THRESHOLD) return resources;
1992
+ const { keyword } = await inquirer.prompt([{
1993
+ type: "input",
1994
+ name: "keyword",
1995
+ message: `${resources.length} resources found. Enter keyword to filter (press Enter to show all):`
1996
+ }]);
1997
+ const trimmed = keyword.trim();
1998
+ if (!trimmed) return resources;
1999
+ const filtered = ZipPrompt.filterByKeyword(resources, trimmed);
2000
+ if (filtered.length === 0) {
2001
+ console.log(`No resources matched "${trimmed}". Showing all resources.`);
2002
+ return resources;
2003
+ }
2004
+ console.log(`Filtered to ${filtered.length} resource(s).`);
2005
+ return filtered;
2006
+ }
2007
+ /**
1940
2008
  * 리소스 복수 선택
1941
2009
  */
1942
2010
  async selectResources(availableResources, types) {
1943
- const filteredResources = availableResources.filter((r) => types.includes(r.type));
2011
+ let filteredResources = availableResources.filter((r) => types.includes(r.type));
1944
2012
  if (filteredResources.length === 0) {
1945
2013
  console.log("\nNo resources found for selected types.");
1946
2014
  return [];
1947
2015
  }
2016
+ filteredResources = await this.filterResourcesIfNeeded(filteredResources);
1948
2017
  const { selected } = await inquirer.prompt([{
1949
2018
  type: "checkbox",
1950
2019
  name: "selected",
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "add-ai-tools",
3
- "version": "1.2.2",
3
+ "version": "1.2.4",
4
4
  "description": "Universal AI agent resource installer CLI",
5
5
  "type": "module",
6
6
  "bin": {