sp-rag 0.3.0 → 0.4.0
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 +107 -76
- package/dist/cli.js +52 -15
- package/dist/lib/mcp-config.js +175 -53
- package/dist/lib/skill.js +77 -37
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,71 +1,122 @@
|
|
|
1
|
-
# `sp-rag`
|
|
2
|
-
|
|
3
|
-
CLI cho setup nhanh SP-RAG theo hướng dev-friendly:
|
|
4
|
-
|
|
5
|
-
- lưu cấu hình mặc định để dev không phải nhớ lại URL, client, alias
|
|
6
|
-
-
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- chạy evaluation/regression suite từ file JSON
|
|
12
|
-
|
|
13
|
-
## Yêu cầu
|
|
14
|
-
|
|
15
|
-
- Node.js `>= 20`
|
|
16
|
-
|
|
1
|
+
# `sp-rag`
|
|
2
|
+
|
|
3
|
+
CLI cho setup nhanh SP-RAG theo hướng dev-friendly:
|
|
4
|
+
|
|
5
|
+
- lưu cấu hình mặc định để dev không phải nhớ lại URL, client, alias
|
|
6
|
+
- cài MCP config đúng format cho từng client
|
|
7
|
+
- cài skill cho các IDE/agent có thư mục skill riêng ổn định
|
|
8
|
+
- kiểm tra nhanh health và observability của stack
|
|
9
|
+
- gọi sync codegraph/GitNexus theo branch hoặc `commit_sha`
|
|
10
|
+
- đọc docs đã render
|
|
11
|
+
- chạy evaluation/regression suite từ file JSON
|
|
12
|
+
|
|
13
|
+
## Yêu cầu
|
|
14
|
+
|
|
15
|
+
- Node.js `>= 20`
|
|
16
|
+
|
|
17
|
+
## Trạng thái package
|
|
18
|
+
|
|
19
|
+
- package npm public: `sp-rag`
|
|
20
|
+
- version đang publish: `0.4.0`
|
|
21
|
+
- binary public: `sp-rag`
|
|
22
|
+
|
|
17
23
|
## Cài từ source trong monorepo
|
|
18
24
|
|
|
19
25
|
```bash
|
|
20
26
|
cd apps/sp-rag-cli
|
|
21
27
|
npm install
|
|
28
|
+
npm test -- --run
|
|
22
29
|
npm run build
|
|
23
30
|
node dist/index.js doctor
|
|
24
31
|
```
|
|
25
32
|
|
|
26
33
|
## Cài nhanh qua `npx`
|
|
27
34
|
|
|
28
|
-
Nếu package đã được publish, luồng ngắn nhất là:
|
|
29
|
-
|
|
30
35
|
```bash
|
|
31
|
-
npx sp-rag@latest install --client codex --mcp-token <token>
|
|
32
|
-
npx sp-rag@latest token add --token <token> --client codex
|
|
36
|
+
npx sp-rag@latest install --client codex --mcp-token <token truy cập MCP> --doctor
|
|
37
|
+
npx sp-rag@latest token add --token <token truy cập MCP> --client codex
|
|
38
|
+
npx sp-rag@latest mcp add vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <token truy cập MCP>
|
|
39
|
+
npx sp-rag@latest mcp add opencode --scope project --cwd D:/Webs/seo-booster --mcp-token <token truy cập MCP>
|
|
40
|
+
npx sp-rag@latest skill install --skill-client antigravity
|
|
41
|
+
npx sp-rag@latest skill install --skill-client opencode
|
|
33
42
|
```
|
|
34
43
|
|
|
35
44
|
Tương đương bằng `npm`:
|
|
36
45
|
|
|
37
46
|
```bash
|
|
38
|
-
npm exec --yes sp-rag@latest install -- --client codex --mcp-token <token>
|
|
39
|
-
npm exec --yes sp-rag@latest token add -- --token <token> --client codex
|
|
47
|
+
npm exec --yes sp-rag@latest install -- --client codex --mcp-token <token truy cập MCP> --doctor
|
|
48
|
+
npm exec --yes sp-rag@latest token add -- --token <token truy cập MCP> --client codex
|
|
40
49
|
```
|
|
41
50
|
|
|
51
|
+
## MCP client được hỗ trợ
|
|
52
|
+
|
|
53
|
+
- `codex`
|
|
54
|
+
- `cursor`
|
|
55
|
+
- `claude-code`
|
|
56
|
+
- `antigravity`
|
|
57
|
+
- `vscode`
|
|
58
|
+
- `opencode`
|
|
59
|
+
|
|
60
|
+
## Skill client được hỗ trợ
|
|
61
|
+
|
|
62
|
+
- `codex`
|
|
63
|
+
- `claude-code`
|
|
64
|
+
- `antigravity`
|
|
65
|
+
- `opencode`
|
|
66
|
+
|
|
42
67
|
Ghi chú:
|
|
43
68
|
|
|
44
|
-
- `
|
|
45
|
-
-
|
|
69
|
+
- `cursor` và `vscode` chỉ cài MCP config, không auto-cài skill mặc định
|
|
70
|
+
- generated skill luôn được render bằng tiếng Anh
|
|
46
71
|
- nếu không muốn lưu token literal vào file config client, dùng `sp-rag mcp add --auth-env-var SP_RAG_MCP_TOKEN`
|
|
47
72
|
|
|
48
|
-
##
|
|
73
|
+
## Path mặc định quan trọng
|
|
74
|
+
|
|
75
|
+
### MCP
|
|
76
|
+
|
|
77
|
+
- `codex`: `~/.codex/config.toml`
|
|
78
|
+
- `cursor` project: `.cursor/mcp.json`
|
|
79
|
+
- `cursor` global: `~/.cursor/mcp.json`
|
|
80
|
+
- `claude-code`: `.mcp.json`
|
|
81
|
+
- `antigravity`: `~/.gemini/antigravity/mcp_config.json`
|
|
82
|
+
- `vscode` project: `.vscode/mcp.json`
|
|
83
|
+
- `vscode` global: file `mcp.json` trong user profile của VS Code
|
|
84
|
+
- `opencode` project: `opencode.json`
|
|
85
|
+
- `opencode` global: `~/.config/opencode/opencode.json`
|
|
86
|
+
|
|
87
|
+
### Skill
|
|
88
|
+
|
|
89
|
+
- `codex`: `~/.codex/skills/sp-rag/SKILL.md`
|
|
90
|
+
- `claude-code`: `~/.claude/skills/sp-rag/SKILL.md`
|
|
91
|
+
- `antigravity`: `~/.gemini/antigravity/skills/sp-rag/SKILL.md`
|
|
92
|
+
- `opencode`: `~/.config/opencode/skills/sp-rag/SKILL.md`
|
|
93
|
+
|
|
94
|
+
## Luồng khuyến dùng cho dev mới
|
|
49
95
|
|
|
50
96
|
```bash
|
|
51
|
-
sp-rag install --client codex --mcp-token <token> --doctor
|
|
52
|
-
sp-rag token add --token <token> --client codex
|
|
97
|
+
sp-rag install --client codex --mcp-token <token truy cập MCP> --doctor
|
|
98
|
+
sp-rag token add --token <token truy cập MCP> --client codex
|
|
53
99
|
sp-rag config show
|
|
54
100
|
sp-rag codegraph status
|
|
55
101
|
sp-rag codegraph watch --interval-ms 2000
|
|
56
102
|
sp-rag codegraph runs --limit 5
|
|
57
103
|
sp-rag codegraph metrics
|
|
58
104
|
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
59
|
-
sp-rag mcp add
|
|
105
|
+
sp-rag mcp add antigravity --mcp-token <token truy cập MCP>
|
|
106
|
+
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <token truy cập MCP>
|
|
107
|
+
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster --mcp-token <token truy cập MCP>
|
|
60
108
|
sp-rag skill install
|
|
109
|
+
sp-rag skill install --skill-client claude-code
|
|
110
|
+
sp-rag skill install --skill-client antigravity
|
|
111
|
+
sp-rag skill install --skill-client opencode
|
|
61
112
|
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
62
|
-
```
|
|
63
|
-
|
|
64
|
-
## Lệnh chính
|
|
65
|
-
|
|
113
|
+
```
|
|
114
|
+
|
|
115
|
+
## Lệnh chính
|
|
116
|
+
|
|
66
117
|
```bash
|
|
67
|
-
sp-rag install --client codex --mcp-token <token> --doctor
|
|
68
|
-
sp-rag token add --token <token> --client codex
|
|
118
|
+
sp-rag install --client codex --mcp-token <token truy cập MCP> --doctor
|
|
119
|
+
sp-rag token add --token <token truy cập MCP> --client codex
|
|
69
120
|
sp-rag config show
|
|
70
121
|
sp-rag doctor
|
|
71
122
|
sp-rag codegraph status
|
|
@@ -73,16 +124,22 @@ sp-rag codegraph watch --interval-ms 2000
|
|
|
73
124
|
sp-rag codegraph runs --limit 10
|
|
74
125
|
sp-rag codegraph metrics
|
|
75
126
|
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
76
|
-
sp-rag codegraph sync --branch master --commit-sha <sha> --webhook-token <token> --gitlab-job-token <ci-job-token>
|
|
127
|
+
sp-rag codegraph sync --branch master --commit-sha <sha> --webhook-token <token webhook codegraph> --gitlab-job-token <ci-job-token>
|
|
77
128
|
sp-rag docs get public --format md
|
|
78
|
-
sp-rag mcp add codex --mcp-token <token>
|
|
129
|
+
sp-rag mcp add codex --mcp-token <token truy cập MCP>
|
|
130
|
+
sp-rag mcp add antigravity --mcp-token <token truy cập MCP>
|
|
131
|
+
sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <token truy cập MCP>
|
|
132
|
+
sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster --mcp-token <token truy cập MCP>
|
|
79
133
|
sp-rag skill install
|
|
134
|
+
sp-rag skill install --skill-client claude-code
|
|
135
|
+
sp-rag skill install --skill-client antigravity
|
|
136
|
+
sp-rag skill install --skill-client opencode
|
|
80
137
|
sp-rag eval run --file ./examples/eval-suite.sample.json
|
|
81
|
-
sp-rag update setup --client codex
|
|
82
|
-
```
|
|
83
|
-
|
|
84
|
-
## Cấu hình mặc định
|
|
85
|
-
|
|
138
|
+
sp-rag update setup --client codex
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
## Cấu hình mặc định
|
|
142
|
+
|
|
86
143
|
CLI lưu cấu hình tại:
|
|
87
144
|
|
|
88
145
|
- `~/.sp-rag/config.json`
|
|
@@ -91,48 +148,22 @@ CLI lưu cấu hình tại:
|
|
|
91
148
|
CLI có thể lưu thêm:
|
|
92
149
|
|
|
93
150
|
- `mcpToken` cho flow cài nhanh
|
|
94
|
-
-
|
|
95
|
-
|
|
96
|
-
Giá trị mặc định:
|
|
97
|
-
|
|
98
|
-
- base URL: `https://sp-rag.secomapp.com`
|
|
99
|
-
- MCP URL: `https://sp-rag.secomapp.com/mcp`
|
|
100
|
-
- alias MCP: `sp-rag`
|
|
101
|
-
|
|
102
|
-
Override nhanh bằng env:
|
|
103
|
-
|
|
104
|
-
```bash
|
|
105
|
-
SP_RAG_BASE_URL=https://sp-rag.secomapp.com
|
|
106
|
-
SP_RAG_MCP_URL=https://sp-rag.secomapp.com/mcp
|
|
107
|
-
SP_RAG_HOME_DIR=D:/Temp/sp-rag-home
|
|
108
|
-
```
|
|
109
|
-
|
|
110
|
-
## Evaluation mẫu
|
|
111
|
-
|
|
112
|
-
- file mẫu: [`examples/eval-suite.sample.json`](./examples/eval-suite.sample.json)
|
|
113
|
-
|
|
114
|
-
## Recovery khi sync bị treo
|
|
115
|
-
|
|
116
|
-
Nếu worker crash giữa chừng, `codegraph-ts` sẽ tự đổi run `running` cũ sang `failed` ở lần truy cập đầu tiên sau khi service lên lại. Khi ops muốn dọn tay hoặc gắn lý do vận hành rõ ràng hơn, dùng:
|
|
151
|
+
- `authEnvVar` nếu muốn client đọc token từ biến môi trường thay vì lưu literal
|
|
152
|
+
- `defaultClient`, `defaultScope`, `skillClient`
|
|
117
153
|
|
|
118
|
-
|
|
119
|
-
sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
|
|
120
|
-
```
|
|
154
|
+
Giá trị mặc định:
|
|
121
155
|
|
|
122
|
-
|
|
156
|
+
- base URL: `https://sp-rag.secomapp.com`
|
|
157
|
+
- MCP URL: `https://sp-rag.secomapp.com/mcp`
|
|
158
|
+
- alias MCP: `sp-rag`
|
|
123
159
|
|
|
124
|
-
##
|
|
125
|
-
|
|
126
|
-
Khi một sync đang chạy và ops muốn biết phase hiện tại theo thời gian thực, dùng:
|
|
160
|
+
## Evaluation mẫu
|
|
127
161
|
|
|
128
|
-
|
|
129
|
-
sp-rag codegraph watch --interval-ms 2000
|
|
130
|
-
```
|
|
162
|
+
- file mẫu: [`examples/eval-suite.sample.json`](./examples/eval-suite.sample.json)
|
|
131
163
|
|
|
132
|
-
CLI sẽ in liên tục `lastStatus`, `activity.currentPhase`, `progressPercentHint`, `elapsed` và `message`.
|
|
133
|
-
|
|
134
164
|
## Tài liệu thêm
|
|
135
165
|
|
|
136
166
|
- [Hướng dẫn dev sử dụng SP-RAG](../../docs/runbooks/dev-usage-guide.md)
|
|
137
167
|
- [Runbook CLI `sp-rag`](../../docs/runbooks/sp-rag-cli.md)
|
|
168
|
+
- [Runbook phát hành CLI `sp-rag`](../../docs/runbooks/sp-rag-cli-release.md)
|
|
138
169
|
- [Runbook MCP Public](../../docs/runbooks/mcp-public-clients.md)
|
package/dist/cli.js
CHANGED
|
@@ -3,7 +3,7 @@ import { loadCliConfig, saveCliConfig, } from './lib/config-store.js';
|
|
|
3
3
|
import { runEvaluationSuite } from './lib/eval.js';
|
|
4
4
|
import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, } from './lib/mcp-config.js';
|
|
5
5
|
import { fetchJson, fetchText, runDoctor } from './lib/http.js';
|
|
6
|
-
import {
|
|
6
|
+
import { installSkill, } from './lib/skill.js';
|
|
7
7
|
function parseArgv(argv) {
|
|
8
8
|
const positionals = [];
|
|
9
9
|
const options = {};
|
|
@@ -42,10 +42,38 @@ function supportedClient(value) {
|
|
|
42
42
|
if (!value) {
|
|
43
43
|
return undefined;
|
|
44
44
|
}
|
|
45
|
-
if (value === 'codex' ||
|
|
45
|
+
if (value === 'codex' ||
|
|
46
|
+
value === 'cursor' ||
|
|
47
|
+
value === 'claude-code' ||
|
|
48
|
+
value === 'antigravity' ||
|
|
49
|
+
value === 'vscode' ||
|
|
50
|
+
value === 'opencode') {
|
|
46
51
|
return value;
|
|
47
52
|
}
|
|
48
|
-
throw new Error('Client phải là codex, cursor
|
|
53
|
+
throw new Error('Client phải là codex, cursor, claude-code, antigravity, vscode hoặc opencode.');
|
|
54
|
+
}
|
|
55
|
+
function supportedSkillClient(value) {
|
|
56
|
+
if (!value) {
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
if (value === 'codex' || value === 'claude-code' || value === 'antigravity' || value === 'opencode') {
|
|
60
|
+
return value;
|
|
61
|
+
}
|
|
62
|
+
throw new Error('Skill client phải là codex, claude-code, antigravity hoặc opencode.');
|
|
63
|
+
}
|
|
64
|
+
function defaultSkillClientForMcpClient(client) {
|
|
65
|
+
switch (client) {
|
|
66
|
+
case 'codex':
|
|
67
|
+
return 'codex';
|
|
68
|
+
case 'claude-code':
|
|
69
|
+
return 'claude-code';
|
|
70
|
+
case 'antigravity':
|
|
71
|
+
return 'antigravity';
|
|
72
|
+
case 'opencode':
|
|
73
|
+
return 'opencode';
|
|
74
|
+
default:
|
|
75
|
+
return undefined;
|
|
76
|
+
}
|
|
49
77
|
}
|
|
50
78
|
function supportedScope(value) {
|
|
51
79
|
if (!value) {
|
|
@@ -90,6 +118,9 @@ async function loadRuntimeDefaults(parsed) {
|
|
|
90
118
|
optionString(parsed, 'token') ??
|
|
91
119
|
process.env['SP_RAG_MCP_TOKEN']?.trim() ??
|
|
92
120
|
config?.mcpToken,
|
|
121
|
+
skillClient: supportedSkillClient(optionString(parsed, 'skill-client')) ??
|
|
122
|
+
config?.skillClient ??
|
|
123
|
+
defaultSkillClientForMcpClient(defaultClient),
|
|
93
124
|
skillTargetDir: optionString(parsed, 'target-dir') ?? config?.skillTargetDir,
|
|
94
125
|
};
|
|
95
126
|
}
|
|
@@ -97,9 +128,9 @@ function helpText() {
|
|
|
97
128
|
return `sp-rag - CLI cho setup, MCP, codegraph, eval và skill của SP-RAG
|
|
98
129
|
|
|
99
130
|
Lệnh chính:
|
|
100
|
-
sp-rag install [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code] [--scope global|project] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
101
|
-
sp-rag init [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code] [--scope global|project] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
102
|
-
sp-rag token add --token TOKEN [--client codex|cursor|claude-code] [--scope global|project] [--cwd PATH]
|
|
131
|
+
sp-rag install [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|claude-code|antigravity|opencode] [--scope global|project] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
132
|
+
sp-rag init [--base-url URL] [--mcp-url URL] [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|claude-code|antigravity|opencode] [--scope global|project] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
133
|
+
sp-rag token add --token TOKEN [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
|
|
103
134
|
sp-rag config show
|
|
104
135
|
sp-rag doctor [--base-url URL]
|
|
105
136
|
sp-rag codegraph status [--base-url URL]
|
|
@@ -109,10 +140,10 @@ Lệnh chính:
|
|
|
109
140
|
sp-rag codegraph recover [--base-url URL] [--reason TEXT]
|
|
110
141
|
sp-rag codegraph sync [--base-url URL] [--branch BRANCH] [--commit-sha SHA] [--force] [--webhook-token TOKEN] [--gitlab-job-token TOKEN]
|
|
111
142
|
sp-rag docs get <public|function|dev> [--base-url URL] [--format md|json|html]
|
|
112
|
-
sp-rag mcp add <codex|cursor|claude-code> [--url URL] [--scope global|project] [--auth-env-var ENV_VAR] [--mcp-token TOKEN]
|
|
113
|
-
sp-rag skill install [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
|
|
143
|
+
sp-rag mcp add <codex|cursor|claude-code|antigravity|vscode|opencode> [--url URL] [--scope global|project] [--auth-env-var ENV_VAR] [--mcp-token TOKEN]
|
|
144
|
+
sp-rag skill install [--skill-client codex|claude-code|antigravity|opencode] [--cwd PATH] [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
|
|
114
145
|
sp-rag eval run --file eval-suite.json [--base-url URL]
|
|
115
|
-
sp-rag update setup [--client codex|cursor|claude-code] [--scope global|project] [--url URL] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
146
|
+
sp-rag update setup [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|claude-code|antigravity|opencode] [--scope global|project] [--url URL] [--auth-env-var ENV_VAR] [--target-dir PATH]
|
|
116
147
|
`;
|
|
117
148
|
}
|
|
118
149
|
function buildCliConfig(defaults) {
|
|
@@ -124,6 +155,7 @@ function buildCliConfig(defaults) {
|
|
|
124
155
|
defaultScope: defaults.defaultScope,
|
|
125
156
|
authEnvVar: defaults.authEnvVar,
|
|
126
157
|
mcpToken: defaults.mcpToken,
|
|
158
|
+
skillClient: defaults.skillClient,
|
|
127
159
|
skillTargetDir: defaults.skillTargetDir,
|
|
128
160
|
docsUrl: defaults.docsUrl,
|
|
129
161
|
};
|
|
@@ -256,7 +288,7 @@ async function runDocsGet(parsed) {
|
|
|
256
288
|
async function runMcpAdd(parsed, defaults, explicitClient) {
|
|
257
289
|
const client = supportedClient(explicitClient ?? parsed.positionals[2] ?? defaults.defaultClient);
|
|
258
290
|
if (!client) {
|
|
259
|
-
throw new Error('Thiếu client. Dùng codex, cursor
|
|
291
|
+
throw new Error('Thiếu client. Dùng codex, cursor, claude-code, antigravity, vscode hoặc opencode.');
|
|
260
292
|
}
|
|
261
293
|
const result = await installMcpConfig({
|
|
262
294
|
client,
|
|
@@ -270,13 +302,18 @@ async function runMcpAdd(parsed, defaults, explicitClient) {
|
|
|
270
302
|
process.stdout.write(`Đã cập nhật cấu hình MCP cho ${result.client} tại ${result.path} (${result.scope}).\n`);
|
|
271
303
|
}
|
|
272
304
|
async function runSkillInstall(parsed, defaults) {
|
|
273
|
-
const
|
|
305
|
+
const client = supportedSkillClient(optionString(parsed, 'skill-client')) ??
|
|
306
|
+
defaults.skillClient ??
|
|
307
|
+
'codex';
|
|
308
|
+
const result = await installSkill({
|
|
309
|
+
client,
|
|
310
|
+
cwd: optionString(parsed, 'cwd'),
|
|
274
311
|
targetDir: optionString(parsed, 'target-dir') ?? defaults.skillTargetDir,
|
|
275
312
|
serverAlias: optionString(parsed, 'server-alias') ?? defaults.serverAlias,
|
|
276
313
|
mcpUrl: optionString(parsed, 'mcp-url') ?? defaults.mcpUrl,
|
|
277
314
|
docsUrl: optionString(parsed, 'docs-url') ?? defaults.docsUrl,
|
|
278
315
|
});
|
|
279
|
-
process.stdout.write(`Đã cài
|
|
316
|
+
process.stdout.write(`Đã cài skill cho ${result.client} tại ${result.path}\n`);
|
|
280
317
|
}
|
|
281
318
|
async function runInit(parsed) {
|
|
282
319
|
const defaults = await loadRuntimeDefaults(parsed);
|
|
@@ -286,7 +323,7 @@ async function runInit(parsed) {
|
|
|
286
323
|
if (!optionFlag(parsed, 'skip-mcp') && defaults.defaultClient) {
|
|
287
324
|
await runMcpAdd(parsed, defaults, defaults.defaultClient);
|
|
288
325
|
}
|
|
289
|
-
if (!optionFlag(parsed, 'skip-skill')) {
|
|
326
|
+
if (!optionFlag(parsed, 'skip-skill') && defaults.skillClient) {
|
|
290
327
|
await runSkillInstall(parsed, defaults);
|
|
291
328
|
}
|
|
292
329
|
if (optionFlag(parsed, 'doctor')) {
|
|
@@ -356,7 +393,7 @@ async function runUpdateSetup(parsed) {
|
|
|
356
393
|
positionals: ['mcp', 'add', client],
|
|
357
394
|
}, defaults, client);
|
|
358
395
|
}
|
|
359
|
-
if (!optionFlag(parsed, 'skip-skill')) {
|
|
396
|
+
if (!optionFlag(parsed, 'skip-skill') && defaults.skillClient) {
|
|
360
397
|
await runSkillInstall(parsed, defaults);
|
|
361
398
|
}
|
|
362
399
|
}
|
|
@@ -444,7 +481,7 @@ export async function runCli(argv) {
|
|
|
444
481
|
return 0;
|
|
445
482
|
}
|
|
446
483
|
if (group === 'version') {
|
|
447
|
-
process.stdout.write('sp-rag 0.
|
|
484
|
+
process.stdout.write('sp-rag 0.4.0\n');
|
|
448
485
|
return 0;
|
|
449
486
|
}
|
|
450
487
|
process.stdout.write(helpText());
|
package/dist/lib/mcp-config.js
CHANGED
|
@@ -7,6 +7,64 @@ function escapeRegex(value) {
|
|
|
7
7
|
function quotedTomlKey(value) {
|
|
8
8
|
return `"${value.replace(/"/g, '\\"')}"`;
|
|
9
9
|
}
|
|
10
|
+
function parseJsonObject(existing) {
|
|
11
|
+
return existing?.trim() ? JSON.parse(existing) : {};
|
|
12
|
+
}
|
|
13
|
+
function withHeaders(target, headers) {
|
|
14
|
+
if (!headers) {
|
|
15
|
+
return target;
|
|
16
|
+
}
|
|
17
|
+
return {
|
|
18
|
+
...target,
|
|
19
|
+
headers,
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
function bearerHeader(authToken, authEnvVar, envStyle = 'shell') {
|
|
23
|
+
const trimmedToken = authToken?.trim();
|
|
24
|
+
if (trimmedToken) {
|
|
25
|
+
return {
|
|
26
|
+
Authorization: `Bearer ${trimmedToken}`,
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
const trimmedEnvVar = authEnvVar?.trim();
|
|
30
|
+
if (!trimmedEnvVar) {
|
|
31
|
+
return undefined;
|
|
32
|
+
}
|
|
33
|
+
switch (envStyle) {
|
|
34
|
+
case 'vscode':
|
|
35
|
+
return {
|
|
36
|
+
Authorization: `Bearer \${env:${trimmedEnvVar}}`,
|
|
37
|
+
};
|
|
38
|
+
case 'opencode':
|
|
39
|
+
return {
|
|
40
|
+
Authorization: `Bearer {env:${trimmedEnvVar}}`,
|
|
41
|
+
};
|
|
42
|
+
default:
|
|
43
|
+
return {
|
|
44
|
+
Authorization: `Bearer \${${trimmedEnvVar}}`,
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
function upsertObjectEntry(base, sectionKey, entryKey, entryValue) {
|
|
49
|
+
const section = base[sectionKey] && typeof base[sectionKey] === 'object'
|
|
50
|
+
? { ...base[sectionKey] }
|
|
51
|
+
: {};
|
|
52
|
+
section[entryKey] = entryValue;
|
|
53
|
+
return {
|
|
54
|
+
...base,
|
|
55
|
+
[sectionKey]: section,
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
function vscodeGlobalConfigPath(home) {
|
|
59
|
+
if (process.platform === 'win32') {
|
|
60
|
+
const appData = process.env['APPDATA']?.trim() || path.join(home, 'AppData', 'Roaming');
|
|
61
|
+
return path.join(appData, 'Code', 'User', 'mcp.json');
|
|
62
|
+
}
|
|
63
|
+
if (process.platform === 'darwin') {
|
|
64
|
+
return path.join(home, 'Library', 'Application Support', 'Code', 'User', 'mcp.json');
|
|
65
|
+
}
|
|
66
|
+
return path.join(home, '.config', 'Code', 'User', 'mcp.json');
|
|
67
|
+
}
|
|
10
68
|
export function defaultMcpServerAlias() {
|
|
11
69
|
return 'sp-rag';
|
|
12
70
|
}
|
|
@@ -27,54 +85,60 @@ export function upsertCodexConfig(existing, options) {
|
|
|
27
85
|
`[mcp_servers.${quotedTomlKey(alias)}]`,
|
|
28
86
|
`url = "${options.url}"`,
|
|
29
87
|
];
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
if (authToken) {
|
|
33
|
-
lines.push('', `[mcp_servers.${quotedTomlKey(alias)}.headers]`);
|
|
34
|
-
lines.push(`Authorization = "Bearer ${authToken.replace(/"/g, '\\"')}"`);
|
|
35
|
-
}
|
|
36
|
-
else if (authEnvVar) {
|
|
88
|
+
const headers = bearerHeader(options.authToken, options.authEnvVar, 'shell');
|
|
89
|
+
if (headers?.Authorization) {
|
|
37
90
|
lines.push('', `[mcp_servers.${quotedTomlKey(alias)}.headers]`);
|
|
38
|
-
lines.push(`Authorization = "
|
|
91
|
+
lines.push(`Authorization = "${headers.Authorization.replace(/"/g, '\\"')}"`);
|
|
39
92
|
}
|
|
40
93
|
return `${cleaned ? `${cleaned}\n\n` : ''}${lines.join('\n')}\n`;
|
|
41
94
|
}
|
|
42
95
|
export function upsertJsonMcpConfig(existing, options) {
|
|
43
|
-
const base = existing
|
|
44
|
-
|
|
45
|
-
: {}
|
|
46
|
-
const mcpServers = base['mcpServers'] && typeof base['mcpServers'] === 'object'
|
|
47
|
-
? { ...base['mcpServers'] }
|
|
48
|
-
: {};
|
|
49
|
-
const serverConfig = {
|
|
96
|
+
const base = parseJsonObject(existing);
|
|
97
|
+
const serverConfig = withHeaders({
|
|
98
|
+
...(options.includeTypeHttp ? { type: 'http' } : {}),
|
|
50
99
|
url: options.url,
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
const
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
100
|
+
}, bearerHeader(options.authToken, options.authEnvVar, 'shell'));
|
|
101
|
+
return `${JSON.stringify(upsertObjectEntry(base, 'mcpServers', options.serverAlias, serverConfig), null, 2)}\n`;
|
|
102
|
+
}
|
|
103
|
+
export function upsertAntigravityConfig(existing, options) {
|
|
104
|
+
const base = parseJsonObject(existing);
|
|
105
|
+
const serverConfig = withHeaders({
|
|
106
|
+
serverUrl: options.url,
|
|
107
|
+
}, bearerHeader(options.authToken, options.authEnvVar, 'shell'));
|
|
108
|
+
return `${JSON.stringify(upsertObjectEntry(base, 'mcpServers', options.serverAlias, serverConfig), null, 2)}\n`;
|
|
109
|
+
}
|
|
110
|
+
export function upsertVsCodeConfig(existing, options) {
|
|
111
|
+
const base = parseJsonObject(existing);
|
|
112
|
+
const serverConfig = withHeaders({
|
|
113
|
+
type: 'http',
|
|
114
|
+
url: options.url,
|
|
115
|
+
}, bearerHeader(options.authToken, options.authEnvVar, 'vscode'));
|
|
116
|
+
return `${JSON.stringify(upsertObjectEntry(base, 'servers', options.serverAlias, serverConfig), null, 2)}\n`;
|
|
117
|
+
}
|
|
118
|
+
export function upsertOpenCodeConfig(existing, options) {
|
|
119
|
+
const base = parseJsonObject(existing);
|
|
120
|
+
const headers = bearerHeader(options.authToken, options.authEnvVar, 'opencode');
|
|
121
|
+
const nextBase = typeof base['$schema'] === 'string'
|
|
122
|
+
? base
|
|
123
|
+
: {
|
|
124
|
+
...base,
|
|
125
|
+
$schema: 'https://opencode.ai/config.json',
|
|
65
126
|
};
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
},
|
|
127
|
+
const serverConfig = withHeaders({
|
|
128
|
+
type: 'remote',
|
|
129
|
+
url: options.url,
|
|
130
|
+
enabled: true,
|
|
131
|
+
...(headers ? { oauth: false } : {}),
|
|
132
|
+
}, headers);
|
|
133
|
+
return `${JSON.stringify(upsertObjectEntry(nextBase, 'mcp', options.serverAlias, serverConfig), null, 2)}\n`;
|
|
72
134
|
}
|
|
73
135
|
export function resolveMcpConfigPath(options) {
|
|
74
|
-
const
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
136
|
+
const inferredScope = options.client === 'codex' || options.client === 'antigravity'
|
|
137
|
+
? 'global'
|
|
138
|
+
: 'project';
|
|
139
|
+
const scope = options.scope ?? inferredScope;
|
|
140
|
+
if ((options.client === 'codex' || options.client === 'antigravity') && scope !== 'global') {
|
|
141
|
+
throw new Error(`${options.client} hiện chỉ hỗ trợ scope global trong CLI này.`);
|
|
78
142
|
}
|
|
79
143
|
if (options.client === 'claude-code' && scope !== 'project') {
|
|
80
144
|
throw new Error('Claude Code hiện chỉ hỗ trợ scope project trong CLI này.');
|
|
@@ -102,6 +166,26 @@ export function resolveMcpConfigPath(options) {
|
|
|
102
166
|
scope,
|
|
103
167
|
path: path.join(cwd, '.mcp.json'),
|
|
104
168
|
};
|
|
169
|
+
case 'antigravity':
|
|
170
|
+
return {
|
|
171
|
+
client: options.client,
|
|
172
|
+
scope,
|
|
173
|
+
path: path.join(home, '.gemini', 'antigravity', 'mcp_config.json'),
|
|
174
|
+
};
|
|
175
|
+
case 'vscode':
|
|
176
|
+
return {
|
|
177
|
+
client: options.client,
|
|
178
|
+
scope,
|
|
179
|
+
path: scope === 'global' ? vscodeGlobalConfigPath(home) : path.join(cwd, '.vscode', 'mcp.json'),
|
|
180
|
+
};
|
|
181
|
+
case 'opencode':
|
|
182
|
+
return {
|
|
183
|
+
client: options.client,
|
|
184
|
+
scope,
|
|
185
|
+
path: scope === 'global'
|
|
186
|
+
? path.join(home, '.config', 'opencode', 'opencode.json')
|
|
187
|
+
: path.join(cwd, 'opencode.json'),
|
|
188
|
+
};
|
|
105
189
|
}
|
|
106
190
|
}
|
|
107
191
|
export async function installMcpConfig(options) {
|
|
@@ -109,20 +193,58 @@ export async function installMcpConfig(options) {
|
|
|
109
193
|
const existing = await readFile(resolved.path, 'utf8').catch(() => '');
|
|
110
194
|
const alias = options.serverAlias?.trim() || defaultMcpServerAlias();
|
|
111
195
|
const url = options.url.trim();
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
196
|
+
let content;
|
|
197
|
+
switch (resolved.client) {
|
|
198
|
+
case 'codex':
|
|
199
|
+
content = upsertCodexConfig(existing, {
|
|
200
|
+
serverAlias: alias,
|
|
201
|
+
url,
|
|
202
|
+
authEnvVar: options.authEnvVar,
|
|
203
|
+
authToken: options.authToken,
|
|
204
|
+
});
|
|
205
|
+
break;
|
|
206
|
+
case 'cursor':
|
|
207
|
+
content = upsertJsonMcpConfig(existing, {
|
|
208
|
+
serverAlias: alias,
|
|
209
|
+
url,
|
|
210
|
+
authEnvVar: options.authEnvVar,
|
|
211
|
+
authToken: options.authToken,
|
|
212
|
+
});
|
|
213
|
+
break;
|
|
214
|
+
case 'claude-code':
|
|
215
|
+
content = upsertJsonMcpConfig(existing, {
|
|
216
|
+
serverAlias: alias,
|
|
217
|
+
url,
|
|
218
|
+
authEnvVar: options.authEnvVar,
|
|
219
|
+
authToken: options.authToken,
|
|
220
|
+
includeTypeHttp: true,
|
|
221
|
+
});
|
|
222
|
+
break;
|
|
223
|
+
case 'antigravity':
|
|
224
|
+
content = upsertAntigravityConfig(existing, {
|
|
225
|
+
serverAlias: alias,
|
|
226
|
+
url,
|
|
227
|
+
authEnvVar: options.authEnvVar,
|
|
228
|
+
authToken: options.authToken,
|
|
229
|
+
});
|
|
230
|
+
break;
|
|
231
|
+
case 'vscode':
|
|
232
|
+
content = upsertVsCodeConfig(existing, {
|
|
233
|
+
serverAlias: alias,
|
|
234
|
+
url,
|
|
235
|
+
authEnvVar: options.authEnvVar,
|
|
236
|
+
authToken: options.authToken,
|
|
237
|
+
});
|
|
238
|
+
break;
|
|
239
|
+
case 'opencode':
|
|
240
|
+
content = upsertOpenCodeConfig(existing, {
|
|
241
|
+
serverAlias: alias,
|
|
242
|
+
url,
|
|
243
|
+
authEnvVar: options.authEnvVar,
|
|
244
|
+
authToken: options.authToken,
|
|
245
|
+
});
|
|
246
|
+
break;
|
|
247
|
+
}
|
|
126
248
|
await mkdir(path.dirname(resolved.path), { recursive: true });
|
|
127
249
|
await writeFile(resolved.path, content, 'utf8');
|
|
128
250
|
return resolved;
|
package/dist/lib/skill.js
CHANGED
|
@@ -1,51 +1,91 @@
|
|
|
1
1
|
import os from 'node:os';
|
|
2
2
|
import path from 'node:path';
|
|
3
3
|
import { mkdir, writeFile } from 'node:fs/promises';
|
|
4
|
-
export function defaultSkillDir() {
|
|
5
|
-
|
|
4
|
+
export function defaultSkillDir(client = 'codex', _cwd) {
|
|
5
|
+
switch (client) {
|
|
6
|
+
case 'codex':
|
|
7
|
+
return path.join(os.homedir(), '.codex', 'skills', 'sp-rag');
|
|
8
|
+
case 'claude-code':
|
|
9
|
+
return path.join(os.homedir(), '.claude', 'skills', 'sp-rag');
|
|
10
|
+
case 'antigravity':
|
|
11
|
+
return path.join(os.homedir(), '.gemini', 'antigravity', 'skills', 'sp-rag');
|
|
12
|
+
case 'opencode':
|
|
13
|
+
return path.join(os.homedir(), '.config', 'opencode', 'skills', 'sp-rag');
|
|
14
|
+
}
|
|
6
15
|
}
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
-
|
|
22
|
-
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
##
|
|
33
|
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
-
|
|
37
|
-
|
|
16
|
+
function clientLabel(client) {
|
|
17
|
+
switch (client) {
|
|
18
|
+
case 'codex':
|
|
19
|
+
return 'Codex';
|
|
20
|
+
case 'claude-code':
|
|
21
|
+
return 'Claude Code';
|
|
22
|
+
case 'antigravity':
|
|
23
|
+
return 'Antigravity';
|
|
24
|
+
case 'opencode':
|
|
25
|
+
return 'OpenCode';
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
export function renderSkill(options) {
|
|
29
|
+
return `---
|
|
30
|
+
name: sp-rag
|
|
31
|
+
description: Use SP-RAG whenever the user asks about this codebase, internal business domain, rendered docs, import inventory, or codegraph sync status. Prefer MCP-backed evidence before answering from memory.
|
|
32
|
+
---
|
|
33
|
+
|
|
34
|
+
# SP-RAG
|
|
35
|
+
|
|
36
|
+
Target client: ${clientLabel(options.client)}
|
|
37
|
+
Default MCP server alias: \`${options.serverAlias}\`
|
|
38
|
+
MCP URL: \`${options.mcpUrl}\`
|
|
39
|
+
Docs URL: \`${options.docsUrl}\`
|
|
40
|
+
|
|
41
|
+
## When To Use This Skill
|
|
42
|
+
|
|
43
|
+
- Use this skill whenever the request is about the internal codebase, business workflows, rendered docs, import inventory, or sync state.
|
|
44
|
+
- Use this skill when grounded evidence matters more than memory or intuition.
|
|
45
|
+
- Use this skill when the answer should come from MCP tools or rendered docs before freeform reasoning.
|
|
46
|
+
|
|
47
|
+
## Recommended Workflow
|
|
48
|
+
|
|
49
|
+
1. Call \`healthz\` if the MCP server might be unavailable or the connection is brand new.
|
|
50
|
+
2. Use \`query_context\` for architecture, domain, entities, relations, and business flow questions.
|
|
51
|
+
3. Use \`get_rendered_docs\` for public, function, or dev docs that were already rendered from the latest graph.
|
|
52
|
+
4. Use \`get_sync_status\`, \`get_sync_runs\`, or \`get_sync_metrics\` when you need to verify commit freshness, investigate failures, or inspect operational history.
|
|
53
|
+
5. Only call \`trigger_code_graph_sync\` when the user explicitly asks to refresh the graph or when a stale graph is the confirmed blocker.
|
|
54
|
+
|
|
55
|
+
## Guardrails
|
|
56
|
+
|
|
57
|
+
- Prefer MCP-grounded answers before relying on memory.
|
|
58
|
+
- If the evidence may be stale, say so clearly and mention that the graph or docs may need a refresh.
|
|
59
|
+
- Do not trigger sync or import actions unless the user asked for it or the workflow truly requires it.
|
|
60
|
+
- When rendered docs already answer the question, cite or summarize those docs instead of rewriting everything from scratch.
|
|
38
61
|
`;
|
|
39
62
|
}
|
|
40
|
-
export
|
|
41
|
-
|
|
63
|
+
export function renderCodexSkill(options) {
|
|
64
|
+
return renderSkill({
|
|
65
|
+
client: 'codex',
|
|
66
|
+
...options,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
export async function installSkill(options = {}) {
|
|
70
|
+
const client = options.client ?? 'codex';
|
|
71
|
+
const targetDir = path.resolve(options.targetDir ?? defaultSkillDir(client, options.cwd));
|
|
42
72
|
const filePath = path.join(targetDir, 'SKILL.md');
|
|
43
|
-
const content =
|
|
73
|
+
const content = renderSkill({
|
|
74
|
+
client,
|
|
44
75
|
serverAlias: options.serverAlias?.trim() || 'sp-rag',
|
|
45
76
|
mcpUrl: options.mcpUrl?.trim() || 'https://sp-rag.secomapp.com/mcp',
|
|
46
77
|
docsUrl: options.docsUrl?.trim() || 'https://sp-rag.secomapp.com/codegraph/docs/public?format=md',
|
|
47
78
|
});
|
|
48
79
|
await mkdir(targetDir, { recursive: true });
|
|
49
80
|
await writeFile(filePath, content, 'utf8');
|
|
50
|
-
return {
|
|
81
|
+
return {
|
|
82
|
+
client,
|
|
83
|
+
path: filePath,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
export async function installCodexSkill(options = {}) {
|
|
87
|
+
return installSkill({
|
|
88
|
+
...options,
|
|
89
|
+
client: 'codex',
|
|
90
|
+
});
|
|
51
91
|
}
|