sp-rag 0.6.10 → 0.6.12
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 +38 -22
- package/dist/cli.js +68 -14
- package/dist/lib/config-store.js +6 -1
- package/dist/lib/mcp-config.js +96 -0
- package/dist/lib/skill.js +40 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -17,7 +17,7 @@ CLI để setup nhanh SP-RAG theo hướng dev-friendly:
|
|
|
17
17
|
## Trạng thái package
|
|
18
18
|
|
|
19
19
|
- package npm public: `sp-rag`
|
|
20
|
-
- version đang publish: `0.6.
|
|
20
|
+
- version đang publish: `0.6.12`
|
|
21
21
|
- binary public: `sp-rag`
|
|
22
22
|
|
|
23
23
|
## Cài từ source trong monorepo
|
|
@@ -36,8 +36,10 @@ node dist/index.js doctor
|
|
|
36
36
|
npx sp-rag@latest install --client codex --mcp-token <grc_pat_...> --doctor
|
|
37
37
|
npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
38
38
|
npx sp-rag@latest install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
|
|
39
|
-
npx sp-rag@latest add --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
40
|
-
npx sp-rag@latest
|
|
39
|
+
npx sp-rag@latest add --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
40
|
+
npx sp-rag@latest update --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
41
|
+
npx sp-rag@latest uninstall --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
42
|
+
npx sp-rag@latest explain --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
41
43
|
npx sp-rag@latest token add --token <grc_pat_...>
|
|
42
44
|
npx sp-rag@latest token verify --token <grc_pat_...>
|
|
43
45
|
npx sp-rag@latest mcp add antigravity
|
|
@@ -50,8 +52,10 @@ Tương đương bằng `npm`:
|
|
|
50
52
|
|
|
51
53
|
```bash
|
|
52
54
|
npm exec --yes sp-rag@latest install -- --client codex --mcp-token <grc_pat_...> --doctor
|
|
53
|
-
npm exec --yes sp-rag@latest add -- --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
54
|
-
npm exec --yes sp-rag@latest
|
|
55
|
+
npm exec --yes sp-rag@latest add -- --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
56
|
+
npm exec --yes sp-rag@latest update -- --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
57
|
+
npm exec --yes sp-rag@latest uninstall -- --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
58
|
+
npm exec --yes sp-rag@latest explain -- --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
55
59
|
npm exec --yes sp-rag@latest token add -- --token <grc_pat_...>
|
|
56
60
|
npm exec --yes sp-rag@latest token verify -- --token <grc_pat_...>
|
|
57
61
|
```
|
|
@@ -61,24 +65,29 @@ npm exec --yes sp-rag@latest token verify -- --token <grc_pat_...>
|
|
|
61
65
|
Flow khuyên dùng sau khi đã có `grc_pat_*`:
|
|
62
66
|
|
|
63
67
|
1. chạy `install` đúng một lần cho client đầu tiên, có kèm `--mcp-token`
|
|
64
|
-
2. từ lần sau, dùng `add --client ...` để cài thêm MCP + skill cho client khác mà không phải nhập lại token
|
|
65
|
-
3. khi
|
|
66
|
-
4. khi
|
|
67
|
-
5. khi muốn
|
|
68
|
+
2. từ lần sau, dùng `add --client ...` để cài thêm MCP + skill cho client khác mà không phải nhập lại token
|
|
69
|
+
3. khi muốn cập nhật lại MCP + skill mà giữ token cũ, dùng `update`
|
|
70
|
+
4. khi muốn gỡ sạch MCP + skill + config CLI do `sp-rag` tạo, dùng `uninstall`
|
|
71
|
+
5. khi chỉ muốn làm một nửa, dùng `mcp add` hoặc `skill install`
|
|
72
|
+
6. khi đổi token, chỉ cần `token add`
|
|
73
|
+
7. khi muốn kiểm tra máy đang được cấu hình ra sao, dùng `explain`
|
|
68
74
|
|
|
69
75
|
Ghi chú:
|
|
70
76
|
|
|
71
|
-
- với `cursor` và `vscode` ở `scope project`, nếu Sếpp đang đứng sẵn trong repo thì có thể bỏ `--cwd`
|
|
72
|
-
- CLI sẽ tự dùng thư mục hiện tại cho cả MCP lẫn skill
|
|
73
|
-
-
|
|
77
|
+
- với `cursor` và `vscode` ở `scope project`, nếu Sếpp đang đứng sẵn trong repo thì có thể bỏ `--cwd`
|
|
78
|
+
- CLI sẽ tự dùng thư mục hiện tại cho cả MCP lẫn skill
|
|
79
|
+
- `update` sẽ tự tính lại target project từ `--cwd` hoặc thư mục hiện tại, không dùng target cũ lệch project nếu user không truyền `--target-dir`
|
|
80
|
+
- rule/agent mới đã được tăng độ ưu tiên MCP-first, giảm khả năng model nhảy thẳng sang grep/read file local
|
|
74
81
|
- skill mới cũng dặn model tổng hợp từ `matched_passages`, `top_entities`, `top_relations`, `citations`; không bê nguyên `answer_brief`
|
|
75
82
|
|
|
76
83
|
Ví dụ:
|
|
77
84
|
|
|
78
85
|
```bash
|
|
79
|
-
sp-rag install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...> --doctor
|
|
80
|
-
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
81
|
-
sp-rag
|
|
86
|
+
sp-rag install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...> --doctor
|
|
87
|
+
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
88
|
+
sp-rag update --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
89
|
+
sp-rag uninstall --client claude-code --scope project --cwd D:/Webs/seo-booster
|
|
90
|
+
sp-rag mcp add antigravity
|
|
82
91
|
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
83
92
|
sp-rag token add --token <grc_pat_moi>
|
|
84
93
|
sp-rag explain --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
@@ -186,7 +195,8 @@ Ghi chú:
|
|
|
186
195
|
### Skill / rule / custom agent
|
|
187
196
|
|
|
188
197
|
- `codex`: `~/.codex/skills/sp-rag/SKILL.md`
|
|
189
|
-
- `claude-code
|
|
198
|
+
- `claude-code` project: `.claude/skills/sp-rag/SKILL.md`
|
|
199
|
+
- `claude-code` global skill-only override: `~/.claude/skills/sp-rag/SKILL.md`
|
|
190
200
|
- `antigravity`: `~/.gemini/antigravity/skills/sp-rag/SKILL.md`
|
|
191
201
|
- `opencode`: `~/.config/opencode/skills/sp-rag/SKILL.md`
|
|
192
202
|
- `cursor` project: `.cursor/rules/sp-rag.mdc`
|
|
@@ -197,8 +207,10 @@ Ghi chú:
|
|
|
197
207
|
|
|
198
208
|
```bash
|
|
199
209
|
sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
|
|
200
|
-
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
201
|
-
sp-rag
|
|
210
|
+
sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
211
|
+
sp-rag update --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
212
|
+
sp-rag uninstall --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
213
|
+
sp-rag explain --client codex
|
|
202
214
|
sp-rag token add --token <grc_pat_...>
|
|
203
215
|
sp-rag token verify --token <grc_pat_...>
|
|
204
216
|
sp-rag config show
|
|
@@ -212,8 +224,10 @@ sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
|
|
|
212
224
|
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
213
225
|
sp-rag skill install --client codex
|
|
214
226
|
sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
215
|
-
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
216
|
-
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
227
|
+
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
228
|
+
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
229
|
+
sp-rag update --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
230
|
+
sp-rag uninstall --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
217
231
|
```
|
|
218
232
|
|
|
219
233
|
## Lệnh chính
|
|
@@ -240,8 +254,10 @@ sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
|
|
|
240
254
|
sp-rag skill install --client codex
|
|
241
255
|
sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
|
|
242
256
|
sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
|
|
243
|
-
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
244
|
-
sp-rag update
|
|
257
|
+
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
258
|
+
sp-rag update --client codex
|
|
259
|
+
sp-rag update setup --client codex
|
|
260
|
+
sp-rag uninstall --client codex
|
|
245
261
|
```
|
|
246
262
|
|
|
247
263
|
## `doctor` kiểm gì
|
package/dist/cli.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { loadCliConfig, saveCliConfig, } from './lib/config-store.js';
|
|
2
|
+
import { clearCliConfig, loadCliConfig, saveCliConfig, } from './lib/config-store.js';
|
|
3
3
|
import { runEvaluationSuite } from './lib/eval.js';
|
|
4
|
-
import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, resolveMcpConfigPath, } from './lib/mcp-config.js';
|
|
4
|
+
import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, removeMcpConfig, resolveMcpConfigPath, } from './lib/mcp-config.js';
|
|
5
5
|
import { diagnoseMcpConfig, formatMcpDoctorResult } from './lib/doctor.js';
|
|
6
6
|
import { fetchJson, fetchText, runDoctor } from './lib/http.js';
|
|
7
|
-
import { installSkill, resolveSkillInstallTarget, } from './lib/skill.js';
|
|
7
|
+
import { installSkill, removeSkill, resolveSkillInstallTarget, } from './lib/skill.js';
|
|
8
8
|
let cliVersionPromise;
|
|
9
9
|
async function resolveCliVersion() {
|
|
10
10
|
if (!cliVersionPromise) {
|
|
@@ -151,7 +151,7 @@ async function loadRuntimeDefaults(parsed) {
|
|
|
151
151
|
(explicitClient ? defaultSkillClientForMcpClient(explicitClient) : undefined) ??
|
|
152
152
|
config?.skillClient ??
|
|
153
153
|
derivedSkillClient,
|
|
154
|
-
skillTargetDir: optionString(parsed, 'target-dir')
|
|
154
|
+
skillTargetDir: optionString(parsed, 'target-dir'),
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
function resolveSelectedClient(parsed, defaults, explicitClient) {
|
|
@@ -205,9 +205,11 @@ function helpText() {
|
|
|
205
205
|
return `sp-rag - CLI cho setup, MCP, codegraph, eval và skill của SP-RAG
|
|
206
206
|
|
|
207
207
|
Lệnh chính:
|
|
208
|
-
sp-rag install --client codex|cursor|claude-code|antigravity|vscode|opencode [--base-url URL] [--mcp-url URL] [--scope global|project] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH] [--doctor]
|
|
209
|
-
sp-rag add --client codex|cursor|claude-code|antigravity|vscode|opencode [--scope global|project] [--cwd PATH] [--target-dir PATH]
|
|
210
|
-
sp-rag
|
|
208
|
+
sp-rag install --client codex|cursor|claude-code|antigravity|vscode|opencode [--base-url URL] [--mcp-url URL] [--scope global|project] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH] [--doctor]
|
|
209
|
+
sp-rag add --client codex|cursor|claude-code|antigravity|vscode|opencode [--scope global|project] [--cwd PATH] [--target-dir PATH]
|
|
210
|
+
sp-rag update [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH] [--doctor]
|
|
211
|
+
sp-rag uninstall [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH]
|
|
212
|
+
sp-rag init [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
211
213
|
sp-rag token add --token TOKEN
|
|
212
214
|
sp-rag token verify [--token TOKEN] [--base-url URL]
|
|
213
215
|
sp-rag config show
|
|
@@ -236,7 +238,6 @@ function buildCliConfig(defaults) {
|
|
|
236
238
|
authEnvVar: defaults.authEnvVar,
|
|
237
239
|
mcpToken: defaults.mcpToken,
|
|
238
240
|
skillClient: defaults.skillClient,
|
|
239
|
-
skillTargetDir: defaults.skillTargetDir,
|
|
240
241
|
docsUrl: defaults.docsUrl,
|
|
241
242
|
};
|
|
242
243
|
}
|
|
@@ -386,8 +387,8 @@ async function runSkillInstall(parsed, defaults, explicitClient) {
|
|
|
386
387
|
const result = await installSkill({
|
|
387
388
|
client,
|
|
388
389
|
cwd: optionString(parsed, 'cwd'),
|
|
389
|
-
scope: supportedScope(optionString(parsed, 'scope'))
|
|
390
|
-
targetDir: optionString(parsed, 'target-dir')
|
|
390
|
+
scope: supportedScope(optionString(parsed, 'scope')),
|
|
391
|
+
targetDir: optionString(parsed, 'target-dir'),
|
|
391
392
|
serverAlias: optionString(parsed, 'server-alias') ?? defaults.serverAlias,
|
|
392
393
|
mcpUrl: optionString(parsed, 'mcp-url') ?? defaults.mcpUrl,
|
|
393
394
|
docsUrl: optionString(parsed, 'docs-url') ?? defaults.docsUrl,
|
|
@@ -538,9 +539,9 @@ async function runExplain(parsed) {
|
|
|
538
539
|
if (skillClient) {
|
|
539
540
|
const skillTarget = resolveSkillInstallTarget({
|
|
540
541
|
client: skillClient,
|
|
541
|
-
scope,
|
|
542
|
+
scope: supportedScope(optionString(parsed, 'scope')),
|
|
542
543
|
cwd: optionString(parsed, 'cwd'),
|
|
543
|
-
targetDir: optionString(parsed, 'target-dir')
|
|
544
|
+
targetDir: optionString(parsed, 'target-dir'),
|
|
544
545
|
serverAlias: defaults.serverAlias,
|
|
545
546
|
mcpUrl: defaults.mcpUrl,
|
|
546
547
|
docsUrl: defaults.docsUrl,
|
|
@@ -598,6 +599,50 @@ async function runUpdateSetup(parsed) {
|
|
|
598
599
|
await runSkillInstall(parsed, defaults, client);
|
|
599
600
|
}
|
|
600
601
|
}
|
|
602
|
+
async function runUpdate(parsed) {
|
|
603
|
+
const defaults = await loadRuntimeDefaults(parsed);
|
|
604
|
+
const client = resolveSelectedClient(parsed, defaults);
|
|
605
|
+
if (!client) {
|
|
606
|
+
throw new Error('Thiếu client. Dùng sp-rag update --client <client>.');
|
|
607
|
+
}
|
|
608
|
+
await runClientSetup(parsed, defaults, client);
|
|
609
|
+
}
|
|
610
|
+
async function runUninstall(parsed) {
|
|
611
|
+
const defaults = await loadRuntimeDefaults(parsed);
|
|
612
|
+
const client = resolveSelectedClient(parsed, defaults);
|
|
613
|
+
if (!client) {
|
|
614
|
+
throw new Error('Thiếu client. Dùng sp-rag uninstall --client <client>.');
|
|
615
|
+
}
|
|
616
|
+
const scope = supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope;
|
|
617
|
+
const mcpResult = await removeMcpConfig({
|
|
618
|
+
client,
|
|
619
|
+
url: optionString(parsed, 'url') ?? defaults.mcpUrl,
|
|
620
|
+
scope,
|
|
621
|
+
cwd: optionString(parsed, 'cwd'),
|
|
622
|
+
serverAlias: optionString(parsed, 'server-alias') ?? defaults.serverAlias,
|
|
623
|
+
});
|
|
624
|
+
process.stdout.write(`${mcpResult.removed ? 'Đã gỡ' : 'Không tìm thấy'} cấu hình MCP cho ${mcpResult.client} tại ${mcpResult.path} (${mcpResult.scope}).\n`);
|
|
625
|
+
const skillClient = resolveSelectedSkillClient(parsed, defaults, client);
|
|
626
|
+
if (skillClient) {
|
|
627
|
+
const skillResult = await removeSkill({
|
|
628
|
+
client: skillClient,
|
|
629
|
+
cwd: optionString(parsed, 'cwd'),
|
|
630
|
+
scope,
|
|
631
|
+
targetDir: optionString(parsed, 'target-dir'),
|
|
632
|
+
serverAlias: optionString(parsed, 'server-alias') ?? defaults.serverAlias,
|
|
633
|
+
mcpUrl: optionString(parsed, 'mcp-url') ?? defaults.mcpUrl,
|
|
634
|
+
docsUrl: optionString(parsed, 'docs-url') ?? defaults.docsUrl,
|
|
635
|
+
});
|
|
636
|
+
for (const removedPath of skillResult.removedPaths) {
|
|
637
|
+
process.stdout.write(`Đã gỡ skill cho ${skillResult.client} tại ${removedPath}\n`);
|
|
638
|
+
}
|
|
639
|
+
if (skillResult.removedPaths.length === 0) {
|
|
640
|
+
process.stdout.write(`Không tìm thấy skill cần gỡ cho ${skillResult.client}.\n`);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
const configPath = await clearCliConfig(defaults.homeDir);
|
|
644
|
+
process.stdout.write(`Đã xóa config CLI tại ${configPath}\n`);
|
|
645
|
+
}
|
|
601
646
|
export async function runCli(argv) {
|
|
602
647
|
const parsed = parseArgv(argv);
|
|
603
648
|
const [group, action] = parsed.positionals;
|
|
@@ -696,8 +741,17 @@ export async function runCli(argv) {
|
|
|
696
741
|
if (group === 'eval' && action === 'run') {
|
|
697
742
|
return runEval(parsed);
|
|
698
743
|
}
|
|
699
|
-
if (group === 'update' && action === 'setup') {
|
|
700
|
-
|
|
744
|
+
if (group === 'update' && (!action || action === 'setup')) {
|
|
745
|
+
if (action === 'setup') {
|
|
746
|
+
await runUpdateSetup(parsed);
|
|
747
|
+
}
|
|
748
|
+
else {
|
|
749
|
+
await runUpdate(parsed);
|
|
750
|
+
}
|
|
751
|
+
return 0;
|
|
752
|
+
}
|
|
753
|
+
if (group === 'uninstall') {
|
|
754
|
+
await runUninstall(parsed);
|
|
701
755
|
return 0;
|
|
702
756
|
}
|
|
703
757
|
if (group === 'version') {
|
package/dist/lib/config-store.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { mkdir, readFile, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
function stripUndefined(value) {
|
|
5
5
|
return Object.fromEntries(Object.entries(value).filter(([, entry]) => entry !== undefined));
|
|
6
6
|
}
|
|
@@ -37,3 +37,8 @@ export async function saveCliConfig(config, homeDir) {
|
|
|
37
37
|
await writeFile(filePath, `${JSON.stringify(normalized, null, 2)}\n`, 'utf8');
|
|
38
38
|
return filePath;
|
|
39
39
|
}
|
|
40
|
+
export async function clearCliConfig(homeDir) {
|
|
41
|
+
const filePath = resolveCliConfigPath(homeDir);
|
|
42
|
+
await rm(filePath, { force: true });
|
|
43
|
+
return filePath;
|
|
44
|
+
}
|
package/dist/lib/mcp-config.js
CHANGED
|
@@ -85,6 +85,30 @@ function upsertObjectEntryPreservingAliasByEndpoint(base, sectionKey, entryKey,
|
|
|
85
85
|
[sectionKey]: section,
|
|
86
86
|
};
|
|
87
87
|
}
|
|
88
|
+
function removeObjectEntryByAliasOrEndpoint(base, sectionKey, entryKey, endpointKey, endpointValue) {
|
|
89
|
+
const section = base[sectionKey] && typeof base[sectionKey] === 'object'
|
|
90
|
+
? { ...base[sectionKey] }
|
|
91
|
+
: {};
|
|
92
|
+
const targetEndpoint = normalizeEndpoint(endpointValue);
|
|
93
|
+
let removed = false;
|
|
94
|
+
for (const [alias, value] of Object.entries(section)) {
|
|
95
|
+
if (!value || typeof value !== 'object') {
|
|
96
|
+
continue;
|
|
97
|
+
}
|
|
98
|
+
const candidateEndpoint = normalizeEndpoint(value[endpointKey]);
|
|
99
|
+
if (alias === entryKey || (targetEndpoint && candidateEndpoint === targetEndpoint)) {
|
|
100
|
+
delete section[alias];
|
|
101
|
+
removed = true;
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
return {
|
|
105
|
+
next: {
|
|
106
|
+
...base,
|
|
107
|
+
[sectionKey]: section,
|
|
108
|
+
},
|
|
109
|
+
removed,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
88
112
|
function vscodeGlobalConfigPath(home) {
|
|
89
113
|
if (process.platform === 'win32') {
|
|
90
114
|
const appData = process.env['APPDATA']?.trim() || path.join(home, 'AppData', 'Roaming');
|
|
@@ -122,6 +146,23 @@ export function upsertCodexConfig(existing, options) {
|
|
|
122
146
|
}
|
|
123
147
|
return `${cleaned ? `${cleaned}\n\n` : ''}${lines.join('\n')}\n`;
|
|
124
148
|
}
|
|
149
|
+
export function removeCodexConfigContent(existing, serverAlias) {
|
|
150
|
+
const alias = serverAlias.trim();
|
|
151
|
+
const aliasPattern = escapeRegex(alias);
|
|
152
|
+
let removed = false;
|
|
153
|
+
const withoutHeaders = existing.replace(new RegExp(`(?:^|\\n)\\[mcp_servers\\.(?:"${aliasPattern}"|${aliasPattern})\\.headers\\][\\s\\S]*?(?=(?:\\n\\[[^\\n]+\\])|$)`, 'g'), () => {
|
|
154
|
+
removed = true;
|
|
155
|
+
return '';
|
|
156
|
+
});
|
|
157
|
+
const cleaned = withoutHeaders.replace(new RegExp(`(?:^|\\n)\\[mcp_servers\\.(?:"${aliasPattern}"|${aliasPattern})\\][\\s\\S]*?(?=(?:\\n\\[[^\\n]+\\])|$)`, 'g'), () => {
|
|
158
|
+
removed = true;
|
|
159
|
+
return '';
|
|
160
|
+
});
|
|
161
|
+
return {
|
|
162
|
+
content: `${cleaned.trimEnd()}${cleaned.trimEnd() ? '\n' : ''}`,
|
|
163
|
+
removed,
|
|
164
|
+
};
|
|
165
|
+
}
|
|
125
166
|
export function upsertJsonMcpConfig(existing, options) {
|
|
126
167
|
const base = parseJsonObject(existing);
|
|
127
168
|
const serverConfig = withHeaders({
|
|
@@ -279,3 +320,58 @@ export async function installMcpConfig(options) {
|
|
|
279
320
|
await writeFile(resolved.path, content, 'utf8');
|
|
280
321
|
return resolved;
|
|
281
322
|
}
|
|
323
|
+
export async function removeMcpConfig(options) {
|
|
324
|
+
const resolved = resolveMcpConfigPath(options);
|
|
325
|
+
const existing = await readFile(resolved.path, 'utf8').catch((error) => {
|
|
326
|
+
if (error.code === 'ENOENT') {
|
|
327
|
+
return '';
|
|
328
|
+
}
|
|
329
|
+
throw error;
|
|
330
|
+
});
|
|
331
|
+
const alias = options.serverAlias?.trim() || defaultMcpServerAlias();
|
|
332
|
+
const url = options.url.trim();
|
|
333
|
+
let content = existing;
|
|
334
|
+
let removed = false;
|
|
335
|
+
if (existing.trim()) {
|
|
336
|
+
switch (resolved.client) {
|
|
337
|
+
case 'codex': {
|
|
338
|
+
const result = removeCodexConfigContent(existing, alias);
|
|
339
|
+
content = result.content;
|
|
340
|
+
removed = result.removed;
|
|
341
|
+
break;
|
|
342
|
+
}
|
|
343
|
+
case 'cursor':
|
|
344
|
+
case 'claude-code': {
|
|
345
|
+
const result = removeObjectEntryByAliasOrEndpoint(parseJsonObject(existing), 'mcpServers', alias, 'url', url);
|
|
346
|
+
content = `${JSON.stringify(result.next, null, 2)}\n`;
|
|
347
|
+
removed = result.removed;
|
|
348
|
+
break;
|
|
349
|
+
}
|
|
350
|
+
case 'antigravity': {
|
|
351
|
+
const result = removeObjectEntryByAliasOrEndpoint(parseJsonObject(existing), 'mcpServers', alias, 'serverUrl', url);
|
|
352
|
+
content = `${JSON.stringify(result.next, null, 2)}\n`;
|
|
353
|
+
removed = result.removed;
|
|
354
|
+
break;
|
|
355
|
+
}
|
|
356
|
+
case 'vscode': {
|
|
357
|
+
const result = removeObjectEntryByAliasOrEndpoint(parseJsonObject(existing), 'servers', alias, 'url', url);
|
|
358
|
+
content = `${JSON.stringify(result.next, null, 2)}\n`;
|
|
359
|
+
removed = result.removed;
|
|
360
|
+
break;
|
|
361
|
+
}
|
|
362
|
+
case 'opencode': {
|
|
363
|
+
const result = removeObjectEntryByAliasOrEndpoint(parseJsonObject(existing), 'mcp', alias, 'url', url);
|
|
364
|
+
content = `${JSON.stringify(result.next, null, 2)}\n`;
|
|
365
|
+
removed = result.removed;
|
|
366
|
+
break;
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
if (removed) {
|
|
371
|
+
await writeFile(resolved.path, content, 'utf8');
|
|
372
|
+
}
|
|
373
|
+
return {
|
|
374
|
+
...resolved,
|
|
375
|
+
removed,
|
|
376
|
+
};
|
|
377
|
+
}
|
package/dist/lib/skill.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
|
-
import { mkdir, writeFile } from 'node:fs/promises';
|
|
3
|
+
import { mkdir, readFile, rm, writeFile } from 'node:fs/promises';
|
|
4
4
|
const seoBoosterRemoteUrl = 'https://gitlab.com/secomapp/development/seo-booster.git';
|
|
5
5
|
const seoBoosterProjectGuard = [
|
|
6
6
|
'- SP-RAG chỉ dành cho repo seo-booster.',
|
|
@@ -29,6 +29,7 @@ function normalizeScope(client, scope) {
|
|
|
29
29
|
return scope;
|
|
30
30
|
}
|
|
31
31
|
switch (client) {
|
|
32
|
+
case 'claude-code':
|
|
32
33
|
case 'cursor':
|
|
33
34
|
return 'project';
|
|
34
35
|
case 'vscode':
|
|
@@ -50,7 +51,9 @@ export function defaultSkillDir(client = 'codex', cwd, scope) {
|
|
|
50
51
|
case 'codex':
|
|
51
52
|
return path.join(os.homedir(), '.codex', 'skills', 'sp-rag');
|
|
52
53
|
case 'claude-code':
|
|
53
|
-
return
|
|
54
|
+
return normalizedScope === 'project'
|
|
55
|
+
? path.join(requireProjectCwd(client, cwd), '.claude', 'skills', 'sp-rag')
|
|
56
|
+
: path.join(os.homedir(), '.claude', 'skills', 'sp-rag');
|
|
54
57
|
case 'antigravity':
|
|
55
58
|
return path.join(os.homedir(), '.gemini', 'antigravity', 'skills', 'sp-rag');
|
|
56
59
|
case 'opencode':
|
|
@@ -257,6 +260,41 @@ export async function installSkill(options = {}) {
|
|
|
257
260
|
paths,
|
|
258
261
|
};
|
|
259
262
|
}
|
|
263
|
+
async function removeIfSpRagArtifact(filePath) {
|
|
264
|
+
const content = await readFile(filePath, 'utf8').catch((error) => {
|
|
265
|
+
if (error.code === 'ENOENT') {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
throw error;
|
|
269
|
+
});
|
|
270
|
+
if (content === null) {
|
|
271
|
+
return false;
|
|
272
|
+
}
|
|
273
|
+
if (!content.includes('SP-RAG') && !content.includes('sp-rag')) {
|
|
274
|
+
return false;
|
|
275
|
+
}
|
|
276
|
+
await rm(filePath, { force: true });
|
|
277
|
+
return true;
|
|
278
|
+
}
|
|
279
|
+
export async function removeSkill(options = {}) {
|
|
280
|
+
const resolved = resolveSkillInstallTarget(options);
|
|
281
|
+
const context = createSkillContext(options, resolved.client);
|
|
282
|
+
const paths = [
|
|
283
|
+
resolved.path,
|
|
284
|
+
...resolveAdditionalSkillArtifacts(options, resolved, context).map((entry) => entry.path),
|
|
285
|
+
];
|
|
286
|
+
const removedPaths = [];
|
|
287
|
+
for (const filePath of paths) {
|
|
288
|
+
if (await removeIfSpRagArtifact(filePath)) {
|
|
289
|
+
removedPaths.push(filePath);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
return {
|
|
293
|
+
client: resolved.client,
|
|
294
|
+
paths,
|
|
295
|
+
removedPaths,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
260
298
|
export async function installCodexSkill(options = {}) {
|
|
261
299
|
return installSkill({
|
|
262
300
|
...options,
|