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.
- package/dist/index.mjs +89 -20
- 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
|
-
|
|
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
|
-
|
|
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",
|