sp-rag 0.6.1 → 0.6.3

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
@@ -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.1`
20
+ - version đang publish: `0.6.3`
21
21
  - binary public: `sp-rag`
22
22
 
23
23
  ## Cài từ source trong monorepo
@@ -36,22 +36,47 @@ 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 token add --token <grc_pat_...> --client codex
39
+ npx sp-rag@latest add --client claude-code --scope project --cwd D:/Webs/seo-booster
40
+ npx sp-rag@latest explain --client vscode --scope project --cwd D:/Webs/seo-booster
41
+ npx sp-rag@latest token add --token <grc_pat_...>
40
42
  npx sp-rag@latest token verify --token <grc_pat_...>
41
- npx sp-rag@latest mcp add antigravity --mcp-token <grc_pat_...>
42
- npx sp-rag@latest mcp add opencode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
43
- npx sp-rag@latest skill install --skill-client cursor --scope project --cwd D:/Webs/seo-booster
44
- npx sp-rag@latest skill install --skill-client vscode --scope project --cwd D:/Webs/seo-booster
43
+ npx sp-rag@latest mcp add antigravity
44
+ npx sp-rag@latest mcp add opencode --scope project --cwd D:/Webs/seo-booster
45
+ npx sp-rag@latest skill install --client cursor --scope project --cwd D:/Webs/seo-booster
46
+ npx sp-rag@latest skill install --client vscode --scope project --cwd D:/Webs/seo-booster
45
47
  ```
46
48
 
47
49
  Tương đương bằng `npm`:
48
50
 
49
51
  ```bash
50
52
  npm exec --yes sp-rag@latest install -- --client codex --mcp-token <grc_pat_...> --doctor
51
- npm exec --yes sp-rag@latest token add -- --token <grc_pat_...> --client codex
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 explain -- --client vscode --scope project --cwd D:/Webs/seo-booster
55
+ npm exec --yes sp-rag@latest token add -- --token <grc_pat_...>
52
56
  npm exec --yes sp-rag@latest token verify -- --token <grc_pat_...>
53
57
  ```
54
58
 
59
+ ## Flow gọn cho dev
60
+
61
+ Flow khuyên dùng sau khi đã có `grc_pat_*`:
62
+
63
+ 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 chỉ muốn làm một nửa, dùng `mcp add` hoặc `skill install`
66
+ 4. khi đổi token, chỉ cần `token add`
67
+ 5. khi muốn kiểm tra máy đang được cấu hình ra sao, dùng `explain`
68
+
69
+ Ví dụ:
70
+
71
+ ```bash
72
+ sp-rag install --client vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...> --doctor
73
+ sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
74
+ sp-rag mcp add antigravity
75
+ sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
76
+ sp-rag token add --token <grc_pat_moi>
77
+ sp-rag explain --client vscode --scope project --cwd D:/Webs/seo-booster
78
+ ```
79
+
55
80
  ## Lấy token ở đâu
56
81
 
57
82
  `sp-rag` public bây giờ dùng token gắn với tài khoản thật trên server.
@@ -101,6 +126,7 @@ Ví dụ dev dùng token trực tiếp:
101
126
  ```bash
102
127
  npx sp-rag@latest install --client cursor --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
103
128
  npx sp-rag@latest token verify --token <grc_pat_...>
129
+ npx sp-rag@latest explain --client cursor --scope project --cwd D:/Webs/seo-booster
104
130
  ```
105
131
 
106
132
  Hoặc dùng biến môi trường:
@@ -164,7 +190,9 @@ Ghi chú:
164
190
 
165
191
  ```bash
166
192
  sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
167
- sp-rag token add --token <grc_pat_...> --client codex
193
+ sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
194
+ sp-rag explain --client codex
195
+ sp-rag token add --token <grc_pat_...>
168
196
  sp-rag token verify --token <grc_pat_...>
169
197
  sp-rag config show
170
198
  sp-rag codegraph status
@@ -172,12 +200,12 @@ sp-rag codegraph watch --interval-ms 2000
172
200
  sp-rag codegraph runs --limit 5
173
201
  sp-rag codegraph metrics
174
202
  sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
175
- sp-rag mcp add antigravity --mcp-token <grc_pat_...>
176
- sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
177
- sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
178
- sp-rag skill install
179
- sp-rag skill install --skill-client cursor --scope project --cwd D:/Webs/seo-booster
180
- sp-rag skill install --skill-client vscode --scope project --cwd D:/Webs/seo-booster
203
+ sp-rag mcp add antigravity
204
+ sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
205
+ sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
206
+ sp-rag skill install --client codex
207
+ sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
208
+ sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
181
209
  sp-rag eval run --file ./examples/eval-suite.sample.json
182
210
  ```
183
211
 
@@ -185,7 +213,9 @@ sp-rag eval run --file ./examples/eval-suite.sample.json
185
213
 
186
214
  ```bash
187
215
  sp-rag install --client codex --mcp-token <grc_pat_...> --doctor
188
- sp-rag token add --token <grc_pat_...> --client codex
216
+ sp-rag add --client cursor --scope project --cwd D:/Webs/seo-booster
217
+ sp-rag explain --client codex
218
+ sp-rag token add --token <grc_pat_...>
189
219
  sp-rag token verify --token <grc_pat_...>
190
220
  sp-rag config show
191
221
  sp-rag doctor
@@ -196,13 +226,13 @@ sp-rag codegraph metrics
196
226
  sp-rag codegraph recover --reason "Ops dọn stale run sau crash"
197
227
  sp-rag codegraph sync --branch master --commit-sha <sha> --webhook-token <token webhook codegraph> --gitlab-job-token <ci-job-token>
198
228
  sp-rag docs get public --format md
199
- sp-rag mcp add codex --mcp-token <grc_pat_...>
200
- sp-rag mcp add antigravity --mcp-token <grc_pat_...>
201
- sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
202
- sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster --mcp-token <grc_pat_...>
203
- sp-rag skill install
204
- sp-rag skill install --skill-client cursor --scope project --cwd D:/Webs/seo-booster
205
- sp-rag skill install --skill-client vscode --scope project --cwd D:/Webs/seo-booster
229
+ sp-rag mcp add codex
230
+ sp-rag mcp add antigravity
231
+ sp-rag mcp add vscode --scope project --cwd D:/Webs/seo-booster
232
+ sp-rag mcp add opencode --scope project --cwd D:/Webs/seo-booster
233
+ sp-rag skill install --client codex
234
+ sp-rag skill install --client cursor --scope project --cwd D:/Webs/seo-booster
235
+ sp-rag skill install --client vscode --scope project --cwd D:/Webs/seo-booster
206
236
  sp-rag eval run --file ./examples/eval-suite.sample.json
207
237
  sp-rag update setup --client codex
208
238
  ```
package/dist/cli.js CHANGED
@@ -1,14 +1,19 @@
1
1
  import { readFile } from 'node:fs/promises';
2
2
  import { loadCliConfig, saveCliConfig, } from './lib/config-store.js';
3
3
  import { runEvaluationSuite } from './lib/eval.js';
4
- import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, } from './lib/mcp-config.js';
4
+ import { defaultBaseUrl, defaultMcpServerAlias, defaultMcpUrl, installMcpConfig, resolveMcpConfigPath, } from './lib/mcp-config.js';
5
5
  import { fetchJson, fetchText, runDoctor } from './lib/http.js';
6
- import { installSkill, } from './lib/skill.js';
6
+ import { installSkill, resolveSkillInstallTarget, } from './lib/skill.js';
7
+ const cliVersion = '0.6.3';
7
8
  function parseArgv(argv) {
8
9
  const positionals = [];
9
10
  const options = {};
10
11
  for (let index = 0; index < argv.length; index += 1) {
11
12
  const current = argv[index];
13
+ if (current === '-v') {
14
+ options['version'] = true;
15
+ continue;
16
+ }
12
17
  if (!current.startsWith('--')) {
13
18
  positionals.push(current);
14
19
  continue;
@@ -111,8 +116,11 @@ async function loadRuntimeDefaults(parsed) {
111
116
  const docsUrl = optionString(parsed, 'docs-url') ??
112
117
  config?.docsUrl ??
113
118
  `${baseUrl.replace(/\/+$/, '')}/codegraph/docs/public?format=md`;
114
- const defaultClient = supportedClient(optionString(parsed, 'client') ?? config?.defaultClient);
119
+ const explicitClient = supportedClient(optionString(parsed, 'client'));
120
+ const defaultClient = explicitClient ?? supportedClient(config?.defaultClient);
115
121
  const defaultScope = supportedScope(optionString(parsed, 'scope') ?? config?.defaultScope);
122
+ const explicitSkillClient = supportedSkillClient(optionString(parsed, 'skill-client'));
123
+ const derivedSkillClient = defaultSkillClientForMcpClient(defaultClient);
116
124
  return {
117
125
  config,
118
126
  homeDir,
@@ -127,21 +135,58 @@ async function loadRuntimeDefaults(parsed) {
127
135
  optionString(parsed, 'token') ??
128
136
  process.env['SP_RAG_MCP_TOKEN']?.trim() ??
129
137
  config?.mcpToken,
130
- skillClient: supportedSkillClient(optionString(parsed, 'skill-client')) ??
138
+ skillClient: explicitSkillClient ??
139
+ (explicitClient ? defaultSkillClientForMcpClient(explicitClient) : undefined) ??
131
140
  config?.skillClient ??
132
- defaultSkillClientForMcpClient(defaultClient),
141
+ derivedSkillClient,
133
142
  skillTargetDir: optionString(parsed, 'target-dir') ?? config?.skillTargetDir,
134
143
  };
135
144
  }
145
+ function resolveSelectedClient(parsed, defaults, explicitClient) {
146
+ return supportedClient(explicitClient ??
147
+ optionString(parsed, 'client') ??
148
+ parsed.positionals[2] ??
149
+ defaults.defaultClient);
150
+ }
151
+ function resolveSelectedSkillClient(parsed, defaults, client) {
152
+ return (supportedSkillClient(optionString(parsed, 'client')) ??
153
+ supportedSkillClient(optionString(parsed, 'skill-client')) ??
154
+ (client ? defaultSkillClientForMcpClient(client) : undefined) ??
155
+ defaults.skillClient);
156
+ }
157
+ function deriveDefaultsForClient(parsed, defaults, client) {
158
+ const nextClient = client ?? defaults.defaultClient;
159
+ const nextScope = supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope;
160
+ const nextSkillClient = resolveSelectedSkillClient(parsed, defaults, nextClient);
161
+ return {
162
+ ...defaults,
163
+ defaultClient: nextClient,
164
+ defaultScope: nextScope,
165
+ skillClient: nextSkillClient,
166
+ };
167
+ }
168
+ function resolveAuthForMcp(parsed, defaults) {
169
+ const authToken = optionString(parsed, 'mcp-token') ?? defaults.mcpToken;
170
+ const authEnvVar = optionString(parsed, 'auth-env-var') ?? defaults.authEnvVar;
171
+ if (!authToken?.trim() && !authEnvVar?.trim()) {
172
+ throw new Error('Chưa có token MCP trong config. Hãy chạy sp-rag install --client <client> --mcp-token <token> hoặc sp-rag token add --token <token>.');
173
+ }
174
+ return {
175
+ authToken: authToken?.trim(),
176
+ authEnvVar: authEnvVar?.trim(),
177
+ };
178
+ }
136
179
  function helpText() {
137
180
  return `sp-rag - CLI cho setup, MCP, codegraph, eval và skill của SP-RAG
138
181
 
139
182
  Lệnh chính:
140
- sp-rag install [--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] [--mcp-token TOKEN] [--auth-env-var ENV_VAR] [--target-dir PATH]
183
+ 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]
184
+ sp-rag add --client codex|cursor|claude-code|antigravity|vscode|opencode [--scope global|project] [--cwd PATH] [--target-dir PATH]
141
185
  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]
142
- sp-rag token add --token TOKEN [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
186
+ sp-rag token add --token TOKEN
143
187
  sp-rag token verify [--token TOKEN] [--base-url URL]
144
188
  sp-rag config show
189
+ sp-rag explain [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH]
145
190
  sp-rag doctor [--base-url URL]
146
191
  sp-rag codegraph status [--base-url URL]
147
192
  sp-rag codegraph watch [--base-url URL] [--interval-ms N] [--max-polls N]
@@ -151,7 +196,7 @@ Lệnh chính:
151
196
  sp-rag codegraph sync [--base-url URL] [--branch BRANCH] [--commit-sha SHA] [--force] [--webhook-token TOKEN] [--gitlab-job-token TOKEN]
152
197
  sp-rag docs get <public|function|dev> [--base-url URL] [--format md|json|html]
153
198
  sp-rag mcp add <codex|cursor|claude-code|antigravity|vscode|opencode> [--url URL] [--scope global|project] [--auth-env-var ENV_VAR] [--mcp-token TOKEN]
154
- sp-rag skill install [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
199
+ sp-rag skill install [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--cwd PATH] [--target-dir PATH] [--mcp-url URL] [--docs-url URL]
155
200
  sp-rag eval run --file eval-suite.json [--base-url URL]
156
201
  sp-rag update setup [--client codex|cursor|claude-code|antigravity|vscode|opencode] [--skill-client codex|cursor|claude-code|antigravity|vscode|opencode] [--scope global|project] [--url URL] [--auth-env-var ENV_VAR] [--target-dir PATH]
157
202
  `;
@@ -296,7 +341,7 @@ async function runDocsGet(parsed) {
296
341
  process.stdout.write(`${result}${result.endsWith('\n') ? '' : '\n'}`);
297
342
  }
298
343
  async function runMcpAdd(parsed, defaults, explicitClient) {
299
- const client = supportedClient(explicitClient ?? parsed.positionals[2] ?? defaults.defaultClient);
344
+ const client = resolveSelectedClient(parsed, defaults, explicitClient);
300
345
  if (!client) {
301
346
  throw new Error('Thiếu client. Dùng codex, cursor, claude-code, antigravity, vscode hoặc opencode.');
302
347
  }
@@ -304,17 +349,15 @@ async function runMcpAdd(parsed, defaults, explicitClient) {
304
349
  client,
305
350
  url: optionString(parsed, 'url') ?? defaults.mcpUrl,
306
351
  scope: supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope,
307
- authEnvVar: optionString(parsed, 'auth-env-var') ?? defaults.authEnvVar,
308
- authToken: optionString(parsed, 'mcp-token') ?? defaults.mcpToken,
352
+ authEnvVar: resolveAuthForMcp(parsed, defaults).authEnvVar,
353
+ authToken: resolveAuthForMcp(parsed, defaults).authToken,
309
354
  cwd: optionString(parsed, 'cwd'),
310
355
  serverAlias: optionString(parsed, 'server-alias') ?? defaults.serverAlias,
311
356
  });
312
357
  process.stdout.write(`Đã cập nhật cấu hình MCP cho ${result.client} tại ${result.path} (${result.scope}).\n`);
313
358
  }
314
- async function runSkillInstall(parsed, defaults) {
315
- const client = supportedSkillClient(optionString(parsed, 'skill-client')) ??
316
- defaults.skillClient ??
317
- 'codex';
359
+ async function runSkillInstall(parsed, defaults, explicitClient) {
360
+ const client = resolveSelectedSkillClient(parsed, defaults, explicitClient) ?? 'codex';
318
361
  const result = await installSkill({
319
362
  client,
320
363
  cwd: optionString(parsed, 'cwd'),
@@ -326,26 +369,60 @@ async function runSkillInstall(parsed, defaults) {
326
369
  });
327
370
  process.stdout.write(`Đã cài skill cho ${result.client} tại ${result.path}\n`);
328
371
  }
372
+ async function saveResolvedConfig(defaults) {
373
+ return saveCliConfig(buildCliConfig(defaults), defaults.homeDir);
374
+ }
375
+ async function runClientSetup(parsed, defaults, client) {
376
+ const nextDefaults = deriveDefaultsForClient(parsed, defaults, client);
377
+ const configPath = await saveResolvedConfig(nextDefaults);
378
+ process.stdout.write(`Đã lưu config CLI tại ${configPath}\n`);
379
+ if (!optionFlag(parsed, 'skip-mcp')) {
380
+ await runMcpAdd(parsed, nextDefaults, client);
381
+ }
382
+ if (!optionFlag(parsed, 'skip-skill')) {
383
+ await runSkillInstall(parsed, nextDefaults, client);
384
+ }
385
+ if (optionFlag(parsed, 'doctor')) {
386
+ const results = await runDoctor({ baseUrl: nextDefaults.baseUrl });
387
+ for (const result of results) {
388
+ process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
389
+ }
390
+ }
391
+ }
329
392
  async function runInit(parsed) {
330
393
  const defaults = await loadRuntimeDefaults(parsed);
331
- const config = buildCliConfig(defaults);
332
- const configPath = await saveCliConfig(config, defaults.homeDir);
394
+ const client = resolveSelectedClient(parsed, defaults);
395
+ const nextDefaults = deriveDefaultsForClient(parsed, defaults, client);
396
+ const configPath = await saveResolvedConfig(nextDefaults);
333
397
  process.stdout.write(`Đã lưu config CLI tại ${configPath}\n`);
334
- if (!optionFlag(parsed, 'skip-mcp') && defaults.defaultClient) {
335
- await runMcpAdd(parsed, defaults, defaults.defaultClient);
398
+ if (!optionFlag(parsed, 'skip-mcp') && client) {
399
+ await runMcpAdd(parsed, nextDefaults, client);
336
400
  }
337
- if (!optionFlag(parsed, 'skip-skill') && defaults.skillClient) {
338
- await runSkillInstall(parsed, defaults);
401
+ if (!optionFlag(parsed, 'skip-skill') && nextDefaults.skillClient) {
402
+ await runSkillInstall(parsed, nextDefaults, client);
339
403
  }
340
404
  if (optionFlag(parsed, 'doctor')) {
341
- const results = await runDoctor({ baseUrl: defaults.baseUrl });
405
+ const results = await runDoctor({ baseUrl: nextDefaults.baseUrl });
342
406
  for (const result of results) {
343
407
  process.stdout.write(`${result.ok ? 'OK' : 'FAIL'} ${result.name} ${result.status} ${result.url}\n`);
344
408
  }
345
409
  }
346
410
  }
347
411
  async function runInstall(parsed) {
348
- await runInit(parsed);
412
+ const defaults = await loadRuntimeDefaults(parsed);
413
+ const client = resolveSelectedClient(parsed, defaults);
414
+ if (!client) {
415
+ throw new Error('Thiếu client. Dùng sp-rag install --client <client> --mcp-token <token>.');
416
+ }
417
+ await runClientSetup(parsed, defaults, client);
418
+ }
419
+ async function runAdd(parsed) {
420
+ const defaults = await loadRuntimeDefaults(parsed);
421
+ const client = resolveSelectedClient(parsed, defaults);
422
+ if (!client) {
423
+ throw new Error('Thiếu client. Dùng sp-rag add --client <client>.');
424
+ }
425
+ await runClientSetup(parsed, defaults, client);
349
426
  }
350
427
  async function runTokenAdd(parsed) {
351
428
  const defaults = await loadRuntimeDefaults(parsed);
@@ -357,24 +434,9 @@ async function runTokenAdd(parsed) {
357
434
  ...defaults,
358
435
  mcpToken: token.trim(),
359
436
  };
360
- const configPath = await saveCliConfig(buildCliConfig(nextDefaults), defaults.homeDir);
437
+ const configPath = await saveResolvedConfig(nextDefaults);
361
438
  process.stdout.write(`Đã lưu token MCP tại ${configPath}\n`);
362
- if (optionFlag(parsed, 'skip-mcp-update')) {
363
- return;
364
- }
365
- const client = supportedClient(optionString(parsed, 'client') ?? defaults.defaultClient);
366
- if (!client) {
367
- process.stdout.write('Chưa có client mặc định để cập nhật MCP config. Dùng thêm --client nếu muốn ghi ngay.\n');
368
- return;
369
- }
370
- await runMcpAdd({
371
- ...parsed,
372
- positionals: ['mcp', 'add', client],
373
- options: {
374
- ...parsed.options,
375
- 'mcp-token': token.trim(),
376
- },
377
- }, nextDefaults, client);
439
+ return;
378
440
  }
379
441
  async function runTokenVerify(parsed) {
380
442
  const defaults = await loadRuntimeDefaults(parsed);
@@ -395,6 +457,73 @@ async function runConfigShow(parsed) {
395
457
  const config = await loadCliConfig(optionString(parsed, 'home-dir'));
396
458
  process.stdout.write(`${JSON.stringify(config ?? {}, null, 2)}\n`);
397
459
  }
460
+ async function runExplain(parsed) {
461
+ const defaults = await loadRuntimeDefaults(parsed);
462
+ const client = resolveSelectedClient(parsed, defaults);
463
+ const scope = supportedScope(optionString(parsed, 'scope')) ?? defaults.defaultScope;
464
+ const tokenStatus = defaults.mcpToken?.trim()
465
+ ? 'đã lưu'
466
+ : defaults.authEnvVar?.trim()
467
+ ? `dùng biến môi trường ${defaults.authEnvVar}`
468
+ : 'chưa có';
469
+ const lines = [];
470
+ lines.push('## Cấu hình hiện tại');
471
+ lines.push(`- Base URL: ${defaults.baseUrl}`);
472
+ lines.push(`- MCP URL: ${defaults.mcpUrl}`);
473
+ lines.push(`- Client mặc định: ${defaults.defaultClient ?? 'chưa có'}`);
474
+ lines.push(`- Scope mặc định: ${defaults.defaultScope ?? 'chưa có'}`);
475
+ lines.push(`- Token MCP đang ở trạng thái: ${tokenStatus}`);
476
+ if (client) {
477
+ try {
478
+ const mcpTarget = resolveMcpConfigPath({
479
+ client,
480
+ url: defaults.mcpUrl,
481
+ scope,
482
+ cwd: optionString(parsed, 'cwd'),
483
+ });
484
+ lines.push(`- File MCP cho ${client}: ${mcpTarget.path}`);
485
+ }
486
+ catch (error) {
487
+ lines.push(`- File MCP cho ${client}: không xác định được (${error instanceof Error ? error.message : String(error)})`);
488
+ }
489
+ try {
490
+ const skillClient = resolveSelectedSkillClient(parsed, defaults, client) ??
491
+ defaultSkillClientForMcpClient(client);
492
+ if (skillClient) {
493
+ const skillTarget = resolveSkillInstallTarget({
494
+ client: skillClient,
495
+ scope,
496
+ cwd: optionString(parsed, 'cwd'),
497
+ targetDir: optionString(parsed, 'target-dir') ?? defaults.skillTargetDir,
498
+ serverAlias: defaults.serverAlias,
499
+ mcpUrl: defaults.mcpUrl,
500
+ docsUrl: defaults.docsUrl,
501
+ });
502
+ lines.push(`- File skill cho ${client}: ${skillTarget.path}`);
503
+ }
504
+ }
505
+ catch (error) {
506
+ lines.push(`- File skill cho ${client}: không xác định được (${error instanceof Error ? error.message : String(error)})`);
507
+ }
508
+ }
509
+ lines.push('');
510
+ lines.push('## Cách dùng chuẩn');
511
+ if (!defaults.mcpToken?.trim() && !defaults.authEnvVar?.trim()) {
512
+ lines.push('- Máy này chưa có token MCP. Hãy chạy:');
513
+ lines.push(` sp-rag install --client ${client ?? 'vscode'} --mcp-token <token>`);
514
+ }
515
+ else {
516
+ const chosenClient = client ?? defaults.defaultClient ?? 'vscode';
517
+ const chosenSkillClient = resolveSelectedSkillClient(parsed, defaults, client) ?? chosenClient;
518
+ lines.push('- Nếu muốn cài đầy đủ MCP + skill cho client mới, hãy chạy:');
519
+ lines.push(` sp-rag add --client ${chosenClient}`);
520
+ lines.push('- Nếu chỉ muốn cập nhật MCP, hãy chạy:');
521
+ lines.push(` sp-rag mcp add ${chosenClient}`);
522
+ lines.push('- Nếu chỉ muốn cài lại skill, hãy chạy:');
523
+ lines.push(` sp-rag skill install --client ${chosenSkillClient}`);
524
+ }
525
+ process.stdout.write(`${lines.join('\n')}\n`);
526
+ }
398
527
  async function runEval(parsed) {
399
528
  const filePath = optionString(parsed, 'file');
400
529
  if (!filePath) {
@@ -412,21 +541,25 @@ async function runEval(parsed) {
412
541
  }
413
542
  async function runUpdateSetup(parsed) {
414
543
  const defaults = await loadRuntimeDefaults(parsed);
415
- const client = optionString(parsed, 'client') ?? defaults.defaultClient;
544
+ const client = resolveSelectedClient(parsed, defaults);
416
545
  if (client) {
417
546
  await runMcpAdd({
418
547
  ...parsed,
419
548
  positionals: ['mcp', 'add', client],
420
549
  }, defaults, client);
421
550
  }
422
- if (!optionFlag(parsed, 'skip-skill') && defaults.skillClient) {
423
- await runSkillInstall(parsed, defaults);
551
+ if (!optionFlag(parsed, 'skip-skill') && resolveSelectedSkillClient(parsed, defaults, client)) {
552
+ await runSkillInstall(parsed, defaults, client);
424
553
  }
425
554
  }
426
555
  export async function runCli(argv) {
427
556
  const parsed = parseArgv(argv);
428
557
  const [group, action] = parsed.positionals;
429
558
  try {
559
+ if (optionFlag(parsed, 'version')) {
560
+ process.stdout.write(`sp-rag ${cliVersion}\n`);
561
+ return 0;
562
+ }
430
563
  if (!group || group === 'help' || group === '--help') {
431
564
  process.stdout.write(helpText());
432
565
  return 0;
@@ -435,6 +568,10 @@ export async function runCli(argv) {
435
568
  await runInstall(parsed);
436
569
  return 0;
437
570
  }
571
+ if (group === 'add') {
572
+ await runAdd(parsed);
573
+ return 0;
574
+ }
438
575
  if (group === 'init') {
439
576
  await runInit(parsed);
440
577
  return 0;
@@ -451,6 +588,10 @@ export async function runCli(argv) {
451
588
  await runConfigShow(parsed);
452
589
  return 0;
453
590
  }
591
+ if (group === 'explain') {
592
+ await runExplain(parsed);
593
+ return 0;
594
+ }
454
595
  if (group === 'doctor') {
455
596
  const defaults = await loadRuntimeDefaults(parsed);
456
597
  const results = await runDoctor({
@@ -511,7 +652,7 @@ export async function runCli(argv) {
511
652
  return 0;
512
653
  }
513
654
  if (group === 'version') {
514
- process.stdout.write('sp-rag 0.6.1\n');
655
+ process.stdout.write(`sp-rag ${cliVersion}\n`);
515
656
  return 0;
516
657
  }
517
658
  process.stdout.write(helpText());
package/dist/lib/skill.js CHANGED
@@ -156,6 +156,22 @@ function renderSkillArtifact(context) {
156
156
  };
157
157
  }
158
158
  }
159
+ export function resolveSkillInstallTarget(options = {}) {
160
+ const client = options.client ?? 'codex';
161
+ const scope = normalizeScope(client, options.scope);
162
+ const targetDir = path.resolve(options.targetDir ?? defaultSkillDir(client, options.cwd, scope));
163
+ const artifact = renderSkillArtifact({
164
+ client,
165
+ serverAlias: options.serverAlias?.trim() || 'sp-rag',
166
+ mcpUrl: options.mcpUrl?.trim() || 'https://sp-rag.secomapp.com/mcp',
167
+ docsUrl: options.docsUrl?.trim() || 'https://sp-rag.secomapp.com/codegraph/docs/public?format=md',
168
+ });
169
+ return {
170
+ client,
171
+ scope,
172
+ path: path.join(targetDir, artifact.fileName),
173
+ };
174
+ }
159
175
  export function renderSkill(options) {
160
176
  return renderSkillArtifact(options).content;
161
177
  }
@@ -166,21 +182,18 @@ export function renderCodexSkill(options) {
166
182
  });
167
183
  }
168
184
  export async function installSkill(options = {}) {
169
- const client = options.client ?? 'codex';
170
- const scope = normalizeScope(client, options.scope);
171
- const targetDir = path.resolve(options.targetDir ?? defaultSkillDir(client, options.cwd, scope));
185
+ const resolved = resolveSkillInstallTarget(options);
172
186
  const artifact = renderSkillArtifact({
173
- client,
187
+ client: resolved.client,
174
188
  serverAlias: options.serverAlias?.trim() || 'sp-rag',
175
189
  mcpUrl: options.mcpUrl?.trim() || 'https://sp-rag.secomapp.com/mcp',
176
190
  docsUrl: options.docsUrl?.trim() || 'https://sp-rag.secomapp.com/codegraph/docs/public?format=md',
177
191
  });
178
- const filePath = path.join(targetDir, artifact.fileName);
179
- await mkdir(targetDir, { recursive: true });
180
- await writeFile(filePath, artifact.content, 'utf8');
192
+ await mkdir(path.dirname(resolved.path), { recursive: true });
193
+ await writeFile(resolved.path, artifact.content, 'utf8');
181
194
  return {
182
- client,
183
- path: filePath,
195
+ client: resolved.client,
196
+ path: resolved.path,
184
197
  };
185
198
  }
186
199
  export async function installCodexSkill(options = {}) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "sp-rag",
3
- "version": "0.6.1",
3
+ "version": "0.6.3",
4
4
  "description": "CLI cho setup MCP, codegraph GitNexus và skill của SP-RAG",
5
5
  "type": "module",
6
6
  "files": [