sp-rag 0.6.11 → 0.6.13
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 +39 -23
- package/dist/cli.js +63 -8
- package/dist/lib/config-store.js +6 -1
- package/dist/lib/mcp-config.js +96 -0
- package/dist/lib/skill.js +86 -32
- 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.13`
|
|
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
|
-
-
|
|
74
|
-
-
|
|
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 route theo intent: feature/domain/docs thì MCP trước, còn edit/debug/current-code thì kiểm tra workspace trước
|
|
81
|
+
- skill mới dùng hướng MCP-grounded + workspace-verified: tổng hợp từ `matched_passages`, `top_entities`, `top_relations`, `citations`, không bê nguyên `answer_brief`, và đối chiếu lại `git status --short`/file hiện tại khi cần
|
|
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) {
|
|
@@ -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
|
|
@@ -597,6 +599,50 @@ async function runUpdateSetup(parsed) {
|
|
|
597
599
|
await runSkillInstall(parsed, defaults, client);
|
|
598
600
|
}
|
|
599
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
|
+
}
|
|
600
646
|
export async function runCli(argv) {
|
|
601
647
|
const parsed = parseArgv(argv);
|
|
602
648
|
const [group, action] = parsed.positionals;
|
|
@@ -695,8 +741,17 @@ export async function runCli(argv) {
|
|
|
695
741
|
if (group === 'eval' && action === 'run') {
|
|
696
742
|
return runEval(parsed);
|
|
697
743
|
}
|
|
698
|
-
if (group === 'update' && action === 'setup') {
|
|
699
|
-
|
|
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);
|
|
700
755
|
return 0;
|
|
701
756
|
}
|
|
702
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.',
|
|
@@ -72,7 +72,7 @@ export function defaultSkillDir(client = 'codex', cwd, scope) {
|
|
|
72
72
|
function renderSkillMarkdown(context) {
|
|
73
73
|
return `---
|
|
74
74
|
name: sp-rag
|
|
75
|
-
description: Use SP-RAG
|
|
75
|
+
description: Use SP-RAG for seo-booster codebase, feature, domain, rendered-docs, import-inventory, or codegraph freshness work that needs grounded evidence.
|
|
76
76
|
---
|
|
77
77
|
|
|
78
78
|
# SP-RAG
|
|
@@ -84,29 +84,43 @@ Docs URL: \`${context.docsUrl}\`
|
|
|
84
84
|
|
|
85
85
|
## When To Use This Skill
|
|
86
86
|
|
|
87
|
-
- Use
|
|
88
|
-
- Use this skill
|
|
89
|
-
-
|
|
87
|
+
- Use SP-RAG whenever the request is about seo-booster codebase, features, business workflows, rendered docs, import inventory, or sync state.
|
|
88
|
+
- Use this skill for seo-booster codebase, feature, business workflow, rendered docs, import inventory, and sync-state questions.
|
|
89
|
+
- The operating model is MCP-grounded, workspace-verified.
|
|
90
|
+
- Do not treat MCP as the only source. MCP gives context; current workspace files and git state verify the final answer.
|
|
90
91
|
|
|
91
|
-
##
|
|
92
|
+
## Intent Routing
|
|
93
|
+
|
|
94
|
+
- For feature, domain, architecture, workflow, docs, or "what does this do" questions: call \`${context.serverAlias}\` MCP first, usually \`query_context\`, then verify with workspace files when implementation details matter.
|
|
95
|
+
- For current-code, edit, bug, failing-test, local-error, or "why is this broken right now" work: inspect the workspace first with \`git status --short\`, \`rg\`, and direct file reads, then use MCP to widen context if needed.
|
|
96
|
+
- For mixed questions: get MCP context, inspect current files, compare both, then answer.
|
|
97
|
+
- For docs questions, use \`get_rendered_docs\` when public, function, or dev docs may already answer the question.
|
|
98
|
+
- For sync freshness and operations, use \`get_sync_status\`, \`get_sync_runs\`, and \`get_sync_metrics\`. Only use \`trigger_code_graph_sync\` when the user explicitly asks or stale evidence is the confirmed blocker.
|
|
99
|
+
|
|
100
|
+
## Workspace Verification
|
|
101
|
+
|
|
102
|
+
1. Run or mentally account for \`git status --short\` before relying on MCP for current behavior.
|
|
103
|
+
2. If MCP cites files, inspect the current workspace version of those files when the answer depends on exact code.
|
|
104
|
+
3. If changed files are related to the question, current workspace files override MCP evidence.
|
|
105
|
+
4. If MCP and workspace disagree, say the graph may be stale and explain which source you are trusting.
|
|
106
|
+
|
|
107
|
+
## Expand Evidence
|
|
92
108
|
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
6. After \`query_context\` returns, synthesize the final answer from \`matched_passages\`, \`top_entities\`, \`top_relations\`, and \`citations\`. Treat \`answer_brief\` as a hint only.
|
|
109
|
+
- After \`query_context\`, synthesize from \`matched_passages\`, \`top_entities\`, \`top_relations\`, \`citations\`, and \`feature_memory\` diagnostics when present.
|
|
110
|
+
- Treat \`answer_brief\` as a hint only.
|
|
111
|
+
- Do not stop at the first one or two MCP citations for broad feature questions.
|
|
112
|
+
- Expand through related symbols, callers, services, controllers, jobs, routes, models, components, and tests using workspace search when needed.
|
|
113
|
+
- Prefer rendered docs when they answer business behavior, but verify current code before making implementation claims.
|
|
99
114
|
|
|
100
115
|
## Guardrails
|
|
101
116
|
|
|
102
117
|
${seoBoosterProjectGuard}
|
|
103
|
-
-
|
|
104
|
-
-
|
|
105
|
-
-
|
|
106
|
-
- If
|
|
118
|
+
- Find root cause before fixing bugs.
|
|
119
|
+
- Use evidence before conclusions; do not claim completion without verification.
|
|
120
|
+
- For behavior changes, write or update tests before implementation when feasible.
|
|
121
|
+
- If evidence may be stale, say so clearly and mention that the graph or docs may need a refresh.
|
|
107
122
|
- Do not trigger sync or import actions unless the user asked for it or the workflow truly requires it.
|
|
108
|
-
- When rendered docs already answer the question, cite or summarize those docs
|
|
109
|
-
- Only fall back to local workspace search or file reads after MCP is unavailable or clearly lacks the needed evidence, and say that you are falling back.
|
|
123
|
+
- When rendered docs already answer the question, cite or summarize those docs, then check current code if the answer depends on implementation details.
|
|
110
124
|
`;
|
|
111
125
|
}
|
|
112
126
|
function renderCursorRule(context) {
|
|
@@ -119,14 +133,16 @@ alwaysApply: true
|
|
|
119
133
|
# SP-RAG
|
|
120
134
|
|
|
121
135
|
${seoBoosterProjectGuard}
|
|
122
|
-
-
|
|
136
|
+
- Route by intent before choosing tools.
|
|
137
|
+
- Use the \`${context.serverAlias}\` MCP server first for feature, domain, architecture, workflow, docs, and "what does this do" questions.
|
|
138
|
+
- Use workspace search first for current-code, edit, bug, and test-failure work.
|
|
139
|
+
- Verify MCP evidence against \`git status --short\` and current workspace files before making implementation claims.
|
|
123
140
|
- Use rendered docs from \`${context.docsUrl}\` when documentation already answers the question.
|
|
124
|
-
- For architecture, domain, entities, relations, and business workflow questions, query MCP first and only then synthesize the answer.
|
|
125
141
|
- Treat \`answer_brief\` only as a hint. Build the final answer from \`matched_passages\`, \`top_entities\`, \`top_relations\`, and \`citations\`.
|
|
142
|
+
- Do not stop at the first one or two MCP citations for broad feature questions. Expand through related files, symbols, services, controllers, jobs, components, and tests.
|
|
126
143
|
- For freshness or operational questions, check sync status, recent runs, and metrics before assuming the graph is current.
|
|
127
144
|
- Only trigger codegraph sync when the user explicitly asks for it or stale evidence is the confirmed blocker.
|
|
128
145
|
- If the evidence may be stale, say so clearly.
|
|
129
|
-
- Only fall back to local workspace search after MCP is unavailable or clearly lacks the needed evidence, and say that you are falling back.
|
|
130
146
|
`;
|
|
131
147
|
}
|
|
132
148
|
function renderVsCodeAgent(context) {
|
|
@@ -140,41 +156,44 @@ You are the SP-RAG custom agent for this workspace.
|
|
|
140
156
|
|
|
141
157
|
Use the \`${context.serverAlias}\` MCP server at \`${context.mcpUrl}\` and the rendered docs URL \`${context.docsUrl}\` as your grounded source of truth.
|
|
142
158
|
|
|
143
|
-
|
|
159
|
+
Route by intent before choosing tools. MCP gives the semantic map; the current workspace verifies exact code.
|
|
144
160
|
|
|
145
161
|
## Recommended Workflow
|
|
146
162
|
|
|
147
|
-
1.
|
|
148
|
-
2.
|
|
163
|
+
1. For feature, domain, architecture, workflow, docs, and "what does this do" questions, use \`query_context\` first.
|
|
164
|
+
2. For current-code, edit, debug, failing-test, and local-error work, inspect the workspace first with \`git status --short\`, \`rg\`, and direct file reads.
|
|
149
165
|
3. Use \`get_rendered_docs\` when public, function, or dev docs may already answer the question.
|
|
150
166
|
4. Use \`get_sync_status\`, \`get_sync_runs\`, or \`get_sync_metrics\` for freshness, incident review, or operational debugging.
|
|
151
|
-
5.
|
|
152
|
-
6.
|
|
167
|
+
5. After MCP returns, verify important citations against current workspace files if the answer depends on exact implementation.
|
|
168
|
+
6. Expand beyond the first one or two MCP citations for broad feature questions. Search related symbols, callers, services, controllers, jobs, routes, models, components, and tests.
|
|
169
|
+
7. Trigger codegraph sync only when the user explicitly requests a refresh or stale evidence is the confirmed blocker.
|
|
153
170
|
|
|
154
171
|
## Guardrails
|
|
155
172
|
|
|
156
173
|
${seoBoosterProjectGuard}
|
|
157
|
-
- Prefer MCP-grounded evidence before answering from memory.
|
|
174
|
+
- Prefer MCP-grounded evidence before answering from memory, but current workspace files override MCP evidence when they differ.
|
|
158
175
|
- Treat \`answer_brief\` as a hint. Prefer the richer evidence in \`matched_passages\`, \`top_entities\`, \`top_relations\`, and \`citations\` when writing the final answer.
|
|
159
|
-
- Do not use local workspace search, grep, or file reads until SP-RAG MCP has been tried first for the current question.
|
|
160
176
|
- When evidence may be stale, say so clearly and mention that a refresh might be needed.
|
|
161
177
|
- Do not trigger sync or import actions unless the workflow truly requires it.
|
|
162
178
|
- When rendered docs already answer the question, summarize those docs instead of rewriting everything from scratch.
|
|
163
|
-
-
|
|
179
|
+
- Find root cause before fixes, write or update tests for behavior changes when feasible, and verify before claiming completion.
|
|
164
180
|
`;
|
|
165
181
|
}
|
|
166
182
|
function renderVsCodeAlwaysOnInstructions(context) {
|
|
167
183
|
return `# SP-RAG workspace instructions
|
|
168
184
|
|
|
169
185
|
${seoBoosterProjectGuard}
|
|
170
|
-
-
|
|
186
|
+
- Route by intent before choosing tools.
|
|
187
|
+
- Use the \`${context.serverAlias}\` MCP server first for feature, domain, architecture, workflow, docs, and "what does this do" questions.
|
|
188
|
+
- Use workspace inspection first for current-code, edit, debug, failing-test, and local-error work.
|
|
189
|
+
- Verify MCP evidence against current workspace state, including \`git status --short\` and current file contents when implementation details matter.
|
|
171
190
|
- Start with \`healthz\` only when connectivity is uncertain or the session is new.
|
|
172
|
-
- Use \`query_context\` first for feature, entity, relation, and business-flow questions.
|
|
173
191
|
- When \`query_context\` returns, synthesize the final answer from \`matched_passages\`, \`top_entities\`, \`top_relations\`, and \`citations\`.
|
|
174
192
|
- Treat \`answer_brief\` as a hint only, not the final answer.
|
|
193
|
+
- Expand beyond the first one or two MCP citations for broad feature questions.
|
|
175
194
|
- Use \`get_rendered_docs\` when rendered docs can answer the question faster than raw code inspection.
|
|
176
195
|
- Only trigger codegraph sync when the user explicitly asks for it or stale graph evidence is the confirmed blocker.
|
|
177
|
-
- If MCP
|
|
196
|
+
- If MCP and current workspace files disagree, say MCP may be stale and trust the current workspace for implementation details.
|
|
178
197
|
`;
|
|
179
198
|
}
|
|
180
199
|
function renderSkillArtifact(context) {
|
|
@@ -260,6 +279,41 @@ export async function installSkill(options = {}) {
|
|
|
260
279
|
paths,
|
|
261
280
|
};
|
|
262
281
|
}
|
|
282
|
+
async function removeIfSpRagArtifact(filePath) {
|
|
283
|
+
const content = await readFile(filePath, 'utf8').catch((error) => {
|
|
284
|
+
if (error.code === 'ENOENT') {
|
|
285
|
+
return null;
|
|
286
|
+
}
|
|
287
|
+
throw error;
|
|
288
|
+
});
|
|
289
|
+
if (content === null) {
|
|
290
|
+
return false;
|
|
291
|
+
}
|
|
292
|
+
if (!content.includes('SP-RAG') && !content.includes('sp-rag')) {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
await rm(filePath, { force: true });
|
|
296
|
+
return true;
|
|
297
|
+
}
|
|
298
|
+
export async function removeSkill(options = {}) {
|
|
299
|
+
const resolved = resolveSkillInstallTarget(options);
|
|
300
|
+
const context = createSkillContext(options, resolved.client);
|
|
301
|
+
const paths = [
|
|
302
|
+
resolved.path,
|
|
303
|
+
...resolveAdditionalSkillArtifacts(options, resolved, context).map((entry) => entry.path),
|
|
304
|
+
];
|
|
305
|
+
const removedPaths = [];
|
|
306
|
+
for (const filePath of paths) {
|
|
307
|
+
if (await removeIfSpRagArtifact(filePath)) {
|
|
308
|
+
removedPaths.push(filePath);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
return {
|
|
312
|
+
client: resolved.client,
|
|
313
|
+
paths,
|
|
314
|
+
removedPaths,
|
|
315
|
+
};
|
|
316
|
+
}
|
|
263
317
|
export async function installCodexSkill(options = {}) {
|
|
264
318
|
return installSkill({
|
|
265
319
|
...options,
|