@tostudy-ai/cli 0.18.11 → 0.18.16
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/dist/main.js +88 -47
- package/dist/main.js.map +4 -4
- package/package.json +1 -1
package/dist/main.js
CHANGED
|
@@ -1842,12 +1842,30 @@ var init_formatter = __esm({
|
|
|
1842
1842
|
}
|
|
1843
1843
|
});
|
|
1844
1844
|
|
|
1845
|
+
// src/io/has-binary.ts
|
|
1846
|
+
import { execFileSync } from "node:child_process";
|
|
1847
|
+
function locatorCommand() {
|
|
1848
|
+
return process.platform === "win32" ? "where" : "which";
|
|
1849
|
+
}
|
|
1850
|
+
function hasBinary(name) {
|
|
1851
|
+
try {
|
|
1852
|
+
execFileSync(locatorCommand(), [name], { encoding: "utf-8", stdio: "pipe", timeout: 3e3 });
|
|
1853
|
+
return true;
|
|
1854
|
+
} catch {
|
|
1855
|
+
return false;
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
var init_has_binary = __esm({
|
|
1859
|
+
"src/io/has-binary.ts"() {
|
|
1860
|
+
"use strict";
|
|
1861
|
+
}
|
|
1862
|
+
});
|
|
1863
|
+
|
|
1845
1864
|
// src/installer/ide-detector.ts
|
|
1846
1865
|
var ide_detector_exports = {};
|
|
1847
1866
|
__export(ide_detector_exports, {
|
|
1848
1867
|
detectIDEs: () => detectIDEs
|
|
1849
1868
|
});
|
|
1850
|
-
import { execFileSync } from "node:child_process";
|
|
1851
1869
|
import fs3 from "node:fs";
|
|
1852
1870
|
import path4 from "node:path";
|
|
1853
1871
|
import os3 from "node:os";
|
|
@@ -1937,11 +1955,7 @@ function detectIDEs(home = os3.homedir()) {
|
|
|
1937
1955
|
];
|
|
1938
1956
|
const claudeIde = ides.find((ide) => ide.id === "claude-code");
|
|
1939
1957
|
if (claudeIde) {
|
|
1940
|
-
|
|
1941
|
-
execFileSync("which", ["claude"], { encoding: "utf-8" });
|
|
1942
|
-
claudeIde.detected = true;
|
|
1943
|
-
} catch {
|
|
1944
|
-
}
|
|
1958
|
+
claudeIde.detected = hasBinary("claude");
|
|
1945
1959
|
}
|
|
1946
1960
|
for (const [id, bin] of [
|
|
1947
1961
|
["grok", "grok"],
|
|
@@ -1949,11 +1963,7 @@ function detectIDEs(home = os3.homedir()) {
|
|
|
1949
1963
|
]) {
|
|
1950
1964
|
const ide = ides.find((i) => i.id === id);
|
|
1951
1965
|
if (ide && !ide.detected) {
|
|
1952
|
-
|
|
1953
|
-
execFileSync("which", [bin], { encoding: "utf-8" });
|
|
1954
|
-
ide.detected = true;
|
|
1955
|
-
} catch {
|
|
1956
|
-
}
|
|
1966
|
+
ide.detected = hasBinary(bin);
|
|
1957
1967
|
}
|
|
1958
1968
|
}
|
|
1959
1969
|
return ides;
|
|
@@ -1961,6 +1971,7 @@ function detectIDEs(home = os3.homedir()) {
|
|
|
1961
1971
|
var init_ide_detector = __esm({
|
|
1962
1972
|
"src/installer/ide-detector.ts"() {
|
|
1963
1973
|
"use strict";
|
|
1974
|
+
init_has_binary();
|
|
1964
1975
|
}
|
|
1965
1976
|
});
|
|
1966
1977
|
|
|
@@ -2333,14 +2344,6 @@ __export(runtime_registry_exports, {
|
|
|
2333
2344
|
import { execFileSync as execFileSync2 } from "node:child_process";
|
|
2334
2345
|
import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync, writeFileSync as writeFileSync2 } from "node:fs";
|
|
2335
2346
|
import { dirname, join as join2 } from "node:path";
|
|
2336
|
-
function hasBinary(name) {
|
|
2337
|
-
try {
|
|
2338
|
-
execFileSync2("which", [name], { encoding: "utf-8", stdio: "pipe" });
|
|
2339
|
-
return true;
|
|
2340
|
-
} catch {
|
|
2341
|
-
return false;
|
|
2342
|
-
}
|
|
2343
|
-
}
|
|
2344
2347
|
function writeFile2(absDir, fileName, body) {
|
|
2345
2348
|
mkdirSync2(absDir, { recursive: true });
|
|
2346
2349
|
writeFileSync2(join2(absDir, fileName), body);
|
|
@@ -2402,10 +2405,10 @@ ${content}`;
|
|
|
2402
2405
|
function detectedSlashHints(slug, home, cwd) {
|
|
2403
2406
|
return RUNTIMES.filter((r) => r.slashHint && r.detect(home, cwd)).map((r) => r.slashHint(slug));
|
|
2404
2407
|
}
|
|
2405
|
-
function slashHintLines(slug, home, cwd
|
|
2408
|
+
function slashHintLines(slug, home, cwd) {
|
|
2406
2409
|
const hints = slug ? detectedSlashHints(slug, home, cwd) : [];
|
|
2407
2410
|
if (hints.length > 0) return hints.map((h) => `\u2192 ${h}`);
|
|
2408
|
-
return [
|
|
2411
|
+
return ["\u2192 Na sua plataforma, pe\xE7a: vamos estudar (no terminal: tostudy menu)"];
|
|
2409
2412
|
}
|
|
2410
2413
|
function needsRootAgentsConsent(cwd) {
|
|
2411
2414
|
const filePath = join2(cwd, "AGENTS.md");
|
|
@@ -2425,7 +2428,7 @@ function upsertRootAgentsBlock(cwd, ctx) {
|
|
|
2425
2428
|
"",
|
|
2426
2429
|
`- **Course:** ${ctx.courseTitle} (${ctx.progress}% complete)`,
|
|
2427
2430
|
`- Read \`.tostudy/courses/${slug}/AGENTS.md\` for the full tutor instructions.`,
|
|
2428
|
-
|
|
2431
|
+
'- To study, ask in plain language (e.g. "vamos estudar"). In Claude Code or OpenCode you can also run `/tostudy`; in the terminal, `tostudy menu`.',
|
|
2429
2432
|
"",
|
|
2430
2433
|
"<!-- tostudy:end -->"
|
|
2431
2434
|
].join("\n");
|
|
@@ -2467,6 +2470,7 @@ var init_runtime_registry = __esm({
|
|
|
2467
2470
|
"use strict";
|
|
2468
2471
|
init_dist();
|
|
2469
2472
|
init_slug();
|
|
2473
|
+
init_has_binary();
|
|
2470
2474
|
init_instruction_template_v3();
|
|
2471
2475
|
logger2 = createLogger("cli:runtime-registry");
|
|
2472
2476
|
detectOpencode = (home) => existsSync2(join2(home, ".opencode")) || existsSync2(join2(home, ".config", "opencode"));
|
|
@@ -2518,7 +2522,10 @@ var init_runtime_registry = __esm({
|
|
|
2518
2522
|
const file2 = join2(home, ".codex", "prompts", "tostudy.md");
|
|
2519
2523
|
writeFile2(join2(home, ".codex", "prompts"), "tostudy.md", content);
|
|
2520
2524
|
return [file2];
|
|
2521
|
-
}
|
|
2525
|
+
},
|
|
2526
|
+
// GH-2351: Codex never reads ~/.codex/prompts, so there is no /tostudy there;
|
|
2527
|
+
// asking in plain language works (it reads AGENTS.md and finds the course skill).
|
|
2528
|
+
slashHint: () => "No Codex, abra o Codex e pe\xE7a: vamos estudar"
|
|
2522
2529
|
},
|
|
2523
2530
|
{
|
|
2524
2531
|
id: "opencode",
|
|
@@ -3735,27 +3742,57 @@ import { Command as Command3 } from "commander";
|
|
|
3735
3742
|
|
|
3736
3743
|
// src/installer/mcp-setup.ts
|
|
3737
3744
|
import { spawn } from "node:child_process";
|
|
3738
|
-
|
|
3739
|
-
|
|
3740
|
-
|
|
3741
|
-
|
|
3742
|
-
|
|
3743
|
-
|
|
3744
|
-
|
|
3745
|
+
import { hostname } from "node:os";
|
|
3746
|
+
|
|
3747
|
+
// src/version.ts
|
|
3748
|
+
var CLI_VERSION = true ? "0.18.16" : "0.7.1";
|
|
3749
|
+
|
|
3750
|
+
// src/installer/mcp-setup.ts
|
|
3751
|
+
function safeServerError(text2) {
|
|
3752
|
+
if (typeof text2 !== "string" || text2.length === 0 || text2.length > 200) return null;
|
|
3753
|
+
return ["Bearer", "mcp_", "eyJ"].some((m) => text2.includes(m)) ? null : text2;
|
|
3754
|
+
}
|
|
3755
|
+
async function createMcpApiKey(session, fetchImpl = fetch) {
|
|
3756
|
+
let res;
|
|
3757
|
+
try {
|
|
3758
|
+
res = await fetchImpl(`${session.apiUrl}/api/mcp/sessions`, {
|
|
3759
|
+
method: "POST",
|
|
3760
|
+
headers: {
|
|
3761
|
+
Authorization: `Bearer ${session.token}`,
|
|
3762
|
+
"Content-Type": "application/json"
|
|
3763
|
+
},
|
|
3764
|
+
body: JSON.stringify({
|
|
3765
|
+
clientInfo: {
|
|
3766
|
+
source: "tostudy-cli-setup",
|
|
3767
|
+
hostname: hostname(),
|
|
3768
|
+
os: process.platform,
|
|
3769
|
+
version: CLI_VERSION
|
|
3770
|
+
}
|
|
3771
|
+
})
|
|
3772
|
+
});
|
|
3773
|
+
} catch {
|
|
3774
|
+
throw new Error("Falha ao criar a chave MCP: erro de rede");
|
|
3775
|
+
}
|
|
3745
3776
|
if (!res.ok) {
|
|
3746
3777
|
const body = await res.json().catch(() => ({}));
|
|
3747
|
-
throw new Error(
|
|
3778
|
+
throw new Error(
|
|
3779
|
+
`Falha ao criar a chave MCP: ${safeServerError(body.error) ?? `HTTP ${res.status}`}`
|
|
3780
|
+
);
|
|
3781
|
+
}
|
|
3782
|
+
const { apiKey } = await res.json().catch(() => ({}));
|
|
3783
|
+
if (typeof apiKey !== "string" || !apiKey.startsWith("mcp_")) {
|
|
3784
|
+
throw new Error("Falha ao criar a chave MCP: resposta inv\xE1lida do servidor");
|
|
3748
3785
|
}
|
|
3749
|
-
return
|
|
3786
|
+
return apiKey;
|
|
3750
3787
|
}
|
|
3751
|
-
async function runMcpSetup(session,
|
|
3788
|
+
async function runMcpSetup(session, apiKey, spawnImpl = spawn) {
|
|
3752
3789
|
const command = process.platform === "win32" ? "npx.cmd" : "npx";
|
|
3753
3790
|
await new Promise((resolve, reject) => {
|
|
3754
3791
|
const child = spawnImpl(command, ["-y", "@tostudy-ai/mcp-setup", "--url", session.apiUrl], {
|
|
3755
3792
|
stdio: "inherit",
|
|
3756
3793
|
env: {
|
|
3757
3794
|
...process.env,
|
|
3758
|
-
TOSTUDY_API_KEY:
|
|
3795
|
+
TOSTUDY_API_KEY: apiKey
|
|
3759
3796
|
}
|
|
3760
3797
|
});
|
|
3761
3798
|
child.once("error", reject);
|
|
@@ -3790,8 +3827,15 @@ async function runSetupMcpSubcommand() {
|
|
|
3790
3827
|
const session = await requireSession();
|
|
3791
3828
|
console.log(` \u2713 Logado como ${session.userName}
|
|
3792
3829
|
`);
|
|
3793
|
-
const
|
|
3794
|
-
|
|
3830
|
+
const apiKey = await createMcpApiKey(session);
|
|
3831
|
+
try {
|
|
3832
|
+
await runMcpSetup(session, apiKey);
|
|
3833
|
+
} catch (error49) {
|
|
3834
|
+
console.error(
|
|
3835
|
+
" Instala\xE7\xE3o incompleta: a chave MCP criada aparece em Configura\xE7\xF5es \u203A MCP e pode ser revogada."
|
|
3836
|
+
);
|
|
3837
|
+
throw error49;
|
|
3838
|
+
}
|
|
3795
3839
|
console.log("\n Setup MCP completo!");
|
|
3796
3840
|
console.log(" \u2192 Reinicie o IDE para ativar o servidor MCP");
|
|
3797
3841
|
console.log(" \u2192 tostudy doctor (verificar conex\xE3o)\n");
|
|
@@ -3845,9 +3889,6 @@ init_session_store();
|
|
|
3845
3889
|
init_workspace_state();
|
|
3846
3890
|
init_formatter();
|
|
3847
3891
|
|
|
3848
|
-
// src/version.ts
|
|
3849
|
-
var CLI_VERSION = true ? "0.18.11" : "0.7.1";
|
|
3850
|
-
|
|
3851
3892
|
// src/update-checker.ts
|
|
3852
3893
|
init_config_dir();
|
|
3853
3894
|
import fs8 from "node:fs";
|
|
@@ -5206,7 +5247,6 @@ var selectCommand = new Command8("select").description("Activate a course by ID
|
|
|
5206
5247
|
{ json: true }
|
|
5207
5248
|
);
|
|
5208
5249
|
} else {
|
|
5209
|
-
const slashCmd = courseSlug2 ? `/tostudy-${courseSlug2}` : "/tostudy";
|
|
5210
5250
|
const home = os7.homedir();
|
|
5211
5251
|
const cwd = process.cwd();
|
|
5212
5252
|
const namespacedPath = `${cwd}/.tostudy`;
|
|
@@ -5238,7 +5278,7 @@ var selectCommand = new Command8("select").description("Activate a course by ID
|
|
|
5238
5278
|
lines.push(rootAgentsSkippedHint());
|
|
5239
5279
|
}
|
|
5240
5280
|
lines.push("", "\u2192 Abra sua plataforma (claude, codex, cursor...)");
|
|
5241
|
-
lines.push(...slashHintLines(courseSlug2, home, cwd
|
|
5281
|
+
lines.push(...slashHintLines(courseSlug2, home, cwd));
|
|
5242
5282
|
lines.push("\u2192 Em outras plataformas: tostudy init");
|
|
5243
5283
|
output(lines.join("\n"), { json: false });
|
|
5244
5284
|
}
|
|
@@ -5927,7 +5967,7 @@ __export(external_exports, {
|
|
|
5927
5967
|
guid: () => guid2,
|
|
5928
5968
|
hash: () => hash,
|
|
5929
5969
|
hex: () => hex2,
|
|
5930
|
-
hostname: () =>
|
|
5970
|
+
hostname: () => hostname3,
|
|
5931
5971
|
httpUrl: () => httpUrl,
|
|
5932
5972
|
includes: () => _includes,
|
|
5933
5973
|
instanceof: () => _instanceof,
|
|
@@ -7314,7 +7354,7 @@ __export(regexes_exports, {
|
|
|
7314
7354
|
extendedDuration: () => extendedDuration,
|
|
7315
7355
|
guid: () => guid,
|
|
7316
7356
|
hex: () => hex,
|
|
7317
|
-
hostname: () =>
|
|
7357
|
+
hostname: () => hostname2,
|
|
7318
7358
|
html5Email: () => html5Email,
|
|
7319
7359
|
idnEmail: () => idnEmail,
|
|
7320
7360
|
integer: () => integer,
|
|
@@ -7391,7 +7431,7 @@ var cidrv4 = /^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]
|
|
|
7391
7431
|
var cidrv6 = /^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;
|
|
7392
7432
|
var base64 = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$/;
|
|
7393
7433
|
var base64url = /^[A-Za-z0-9_-]*$/;
|
|
7394
|
-
var
|
|
7434
|
+
var hostname2 = /^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
|
|
7395
7435
|
var domain = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
|
|
7396
7436
|
var e164 = /^\+[1-9]\d{6,14}$/;
|
|
7397
7437
|
var dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;
|
|
@@ -17761,7 +17801,7 @@ __export(schemas_exports2, {
|
|
|
17761
17801
|
guid: () => guid2,
|
|
17762
17802
|
hash: () => hash,
|
|
17763
17803
|
hex: () => hex2,
|
|
17764
|
-
hostname: () =>
|
|
17804
|
+
hostname: () => hostname3,
|
|
17765
17805
|
httpUrl: () => httpUrl,
|
|
17766
17806
|
instanceof: () => _instanceof,
|
|
17767
17807
|
int: () => int,
|
|
@@ -18265,7 +18305,7 @@ var ZodCustomStringFormat = /* @__PURE__ */ $constructor("ZodCustomStringFormat"
|
|
|
18265
18305
|
function stringFormat(format, fnOrRegex, _params = {}) {
|
|
18266
18306
|
return _stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params);
|
|
18267
18307
|
}
|
|
18268
|
-
function
|
|
18308
|
+
function hostname3(_params) {
|
|
18269
18309
|
return _stringFormat(ZodCustomStringFormat, "hostname", regexes_exports.hostname, _params);
|
|
18270
18310
|
}
|
|
18271
18311
|
function hex2(_params) {
|
|
@@ -20190,8 +20230,9 @@ var menuCommand = new Command18("menu").description("Show available commands and
|
|
|
20190
20230
|
} else {
|
|
20191
20231
|
lines.push(
|
|
20192
20232
|
" Curso ativo: (nenhum)",
|
|
20193
|
-
" \u2192 tostudy courses
|
|
20233
|
+
" \u2192 tostudy courses para ver seus cursos",
|
|
20194
20234
|
" \u2192 tostudy select <n\xFAmero> para ativar um curso",
|
|
20235
|
+
" \u2192 tostudy suggest se voc\xEA ainda n\xE3o tem nenhum curso",
|
|
20195
20236
|
""
|
|
20196
20237
|
);
|
|
20197
20238
|
}
|