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 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
- - kiểm tra nhanh health observability của stack
7
- - gọi sync codegraph/GitNexus theo branch hoặc `commit_sha`
8
- - đọc docs đã render
9
- - cài MCP config cho client
10
- - cài skill để agent ưu tiên dùng RAG nội bộ
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 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
- - `install` lệnh onboarding một bước: lưu config, cài MCP config, cài skill, có thể chạy `doctor`
45
- - `token add` dùng khi dev đã cài xong nhưng chưa gắn bearer token vào MCP client
69
+ - `cursor` `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
- ## Luồng khuyên dùng cho dev mới
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 codex --mcp-token <token>
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
- - hoặc `authEnvVar` nếu muốn client đọc token từ biến môi trường thay vì lưu literal
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
- ```bash
119
- sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
120
- ```
154
+ Giá trị mặc định:
121
155
 
122
- CLI này gọi `POST /codegraph/sync-recover-stale` và trả lại danh sách run vừa được chuyển sang `failed`.
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
- ## Theo dõi active
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
- ```bash
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 { installCodexSkill } from './lib/skill.js';
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' || value === 'cursor' || value === 'claude-code') {
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 hoặc claude-code.');
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 hoặc claude-code.');
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 result = await installCodexSkill({
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 Codex skill tại ${result.path}\n`);
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.3.0\n');
484
+ process.stdout.write('sp-rag 0.4.0\n');
448
485
  return 0;
449
486
  }
450
487
  process.stdout.write(helpText());
@@ -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 authToken = options.authToken?.trim();
31
- const authEnvVar = options.authEnvVar?.trim();
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 = "Bearer \${${authEnvVar}}"`);
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?.trim()
44
- ? JSON.parse(existing)
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
- if (options.includeTypeHttp) {
53
- serverConfig['type'] = 'http';
54
- }
55
- const authToken = options.authToken?.trim();
56
- const authEnvVar = options.authEnvVar?.trim();
57
- if (authToken) {
58
- serverConfig['headers'] = {
59
- Authorization: `Bearer ${authToken}`,
60
- };
61
- }
62
- else if (authEnvVar) {
63
- serverConfig['headers'] = {
64
- Authorization: `Bearer \${${authEnvVar}}`,
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
- mcpServers[options.serverAlias] = serverConfig;
68
- return `${JSON.stringify({
69
- ...base,
70
- mcpServers,
71
- }, null, 2)}\n`;
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 scope = options.scope ??
75
- (options.client === 'codex' ? 'global' : 'project');
76
- if (options.client === 'codex' && scope !== 'global') {
77
- throw new Error('Codex hiện chỉ hỗ trợ scope global trong CLI này.');
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
- const content = resolved.client === 'codex'
113
- ? upsertCodexConfig(existing, {
114
- serverAlias: alias,
115
- url,
116
- authEnvVar: options.authEnvVar,
117
- authToken: options.authToken,
118
- })
119
- : upsertJsonMcpConfig(existing, {
120
- serverAlias: alias,
121
- url,
122
- authEnvVar: options.authEnvVar,
123
- authToken: options.authToken,
124
- includeTypeHttp: resolved.client === 'claude-code',
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
- return path.join(os.homedir(), '.codex', 'skills', 'sp-rag');
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
- export function renderCodexSkill(options) {
8
- return `---
9
- name: sp-rag
10
- description: Dùng RAG nội bộ qua MCP để trả lời câu hỏi về codebase, domain, docs và vận hành trước khi dựa vào trí nhớ.
11
- ---
12
-
13
- # SP-RAG
14
-
15
- MCP server alias mặc định: \`${options.serverAlias}\`
16
- MCP URL: \`${options.mcpUrl}\`
17
- Docs URL: \`${options.docsUrl}\`
18
-
19
- ## Khi nào dùng
20
-
21
- - Khi câu hỏi liên quan đến codebase nội bộ, domain nghiệp vụ, docs đã render, trạng thái sync hoặc inventory import.
22
- - Khi cần câu trả lời evidence thay suy đoán theo trí nhớ.
23
-
24
- ## Luồng dùng khuyến nghị
25
-
26
- 1. Gọi \`healthz\` nếu nghi server đang lỗi hoặc vừa kết nối lần đầu.
27
- 2. Dùng \`query_context\` cho câu hỏi về kiến trúc, domain, flow nghiệp vụ, entities, relations.
28
- 3. Dùng \`get_rendered_docs\` cho public docs, function docs hoặc dev docs đã render sẵn.
29
- 4. Dùng \`get_sync_status\`, \`get_sync_runs\` hoặc \`get_sync_metrics\` khi cần kiểm tra code graph đang ở commit nào, sync có lỗi không, hay muốn đọc lịch sử và metrics vận hành.
30
- 5. Chỉ dùng \`trigger_code_graph_sync\` khi user yêu cầu làm mới index hoặc code graph.
31
-
32
- ## Nguyên tắc trả lời
33
-
34
- - Ưu tiên câu trả lời grounded từ MCP/RAG trước.
35
- - Nếu evidence dấu hiệu stale, nói dữ liệu thể chưa được sync mới nhất.
36
- - Không tự kích hoạt sync/index nếu user chưa yêu cầu hoặc chưa thật sự cần.
37
- - Khi docs đã đủ, ưu tiên trích từ docs đã render thay vì tự diễn giải dài dòng.
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 async function installCodexSkill(options = {}) {
41
- const targetDir = path.resolve(options.targetDir ?? defaultSkillDir());
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 = renderCodexSkill({
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 { path: filePath };
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
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sp-rag",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "description": "CLI cho setup MCP, codegraph GitNexus và skill của SP-RAG",
5
5
  "type": "module",
6
6
  "files": [