paris-immersion 0.1.12 → 0.1.14
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/commands/install.js +6 -0
- package/dist/commands/serve.d.ts +0 -14
- package/dist/commands/serve.js +41 -3
- package/dist/index.js +1 -1
- package/dist/templates/hook.js +1 -1
- package/dist/templates/tostudy-command.d.ts +10 -0
- package/dist/templates/tostudy-command.js +37 -0
- package/package.json +1 -1
package/dist/commands/install.js
CHANGED
|
@@ -10,6 +10,7 @@ import { renderClaudeMd } from '../templates/claude-md.js';
|
|
|
10
10
|
import { renderSettingsJson } from '../templates/settings.js';
|
|
11
11
|
import { renderParisCoachSkill } from '../templates/skill.js';
|
|
12
12
|
import { renderHookScript } from '../templates/hook.js';
|
|
13
|
+
import { renderTostudyCommand } from '../templates/tostudy-command.js';
|
|
13
14
|
export async function installCommand(opts) {
|
|
14
15
|
const creds = await readCredentials();
|
|
15
16
|
if (!creds) {
|
|
@@ -89,6 +90,11 @@ async function writeWorkspaceFiles(targetDir, boot, apiUrl, email) {
|
|
|
89
90
|
const skillDir = path.join(claudeDir, 'skills', 'paris-coach');
|
|
90
91
|
await fs.mkdir(skillDir, { recursive: true });
|
|
91
92
|
await fs.writeFile(path.join(skillDir, 'SKILL.md'), renderParisCoachSkill());
|
|
93
|
+
// .claude/commands/tostudy.md — NOSSO slash-command /tostudy (rota confiável do
|
|
94
|
+
// curso). Garante que /tostudy sempre exista, independente do @tostudy-ai/cli.
|
|
95
|
+
const commandsDir = path.join(claudeDir, 'commands');
|
|
96
|
+
await fs.mkdir(commandsDir, { recursive: true });
|
|
97
|
+
await fs.writeFile(path.join(commandsDir, 'tostudy.md'), renderTostudyCommand());
|
|
92
98
|
// CLAUDE.md — workspace-level rules.
|
|
93
99
|
await fs.writeFile(path.join(targetDir, 'CLAUDE.md'), renderClaudeMd({
|
|
94
100
|
immersionName: boot.immersion.name,
|
package/dist/commands/serve.d.ts
CHANGED
|
@@ -37,21 +37,7 @@ export interface ServeHandle {
|
|
|
37
37
|
port: number;
|
|
38
38
|
close: () => void;
|
|
39
39
|
}
|
|
40
|
-
/**
|
|
41
|
-
* Descobre o slash-command do tutor pra injetar no kickoff. O `@tostudy-ai/cli`
|
|
42
|
-
* instala o comando com nome ESPECÍFICO do curso (`.claude/commands/tostudy-<slug>.md`),
|
|
43
|
-
* NÃO `/tostudy` — então cravar `/tostudy` dava "Unknown command: /tostudy" e o
|
|
44
|
-
* curso não iniciava. Aqui varremos `.claude/commands/tostudy*.md` e usamos o
|
|
45
|
-
* comando real; com vários cursos, casamos com o `slug` ativo do
|
|
46
|
-
* `.tostudy/workspace.json`. Override explícito via PARIS_COACH_KICKOFF.
|
|
47
|
-
* Fallback `/tostudy` (legado / antes do `tostudy` instalar o comando).
|
|
48
|
-
*/
|
|
49
40
|
export declare function resolveKickoff(cwd: string): string;
|
|
50
|
-
/**
|
|
51
|
-
* Spawn the coached pty (`claude '/tostudy-<curso>'` inside the provisioned
|
|
52
|
-
* workspace). Shared by the local ws server and the relay agent. Throws if
|
|
53
|
-
* node-pty can't load/spawn — callers report it on their channel.
|
|
54
|
-
*/
|
|
55
41
|
export declare function spawnCoachPty(cwd: string): IPty;
|
|
56
42
|
/**
|
|
57
43
|
* Start a localhost WebSocket server that brokers a pty (running `claude`) to a
|
package/dist/commands/serve.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import { WebSocketServer } from 'ws';
|
|
2
2
|
import { createRequire } from 'node:module';
|
|
3
|
-
import { promises as fs, chmodSync, existsSync, statSync, readdirSync, readFileSync } from 'node:fs';
|
|
3
|
+
import { promises as fs, chmodSync, existsSync, statSync, readdirSync, readFileSync, mkdirSync, writeFileSync } from 'node:fs';
|
|
4
4
|
import { homedir } from 'node:os';
|
|
5
5
|
import path from 'node:path';
|
|
6
6
|
import { readCredentials } from '../lib/credentials.js';
|
|
7
7
|
import { ensureWorkspace } from './install.js';
|
|
8
8
|
import { startRelayAgent } from '../lib/relay-agent.js';
|
|
9
|
+
import { renderTostudyCommand } from '../templates/tostudy-command.js';
|
|
9
10
|
import { c } from '../lib/colors.js';
|
|
10
11
|
/**
|
|
11
12
|
* Restore the executable bit on node-pty's `spawn-helper`. Some install paths
|
|
@@ -79,6 +80,21 @@ const DEFAULT_ALLOWED_ORIGINS = [
|
|
|
79
80
|
* `.tostudy/workspace.json`. Override explícito via PARIS_COACH_KICKOFF.
|
|
80
81
|
* Fallback `/tostudy` (legado / antes do `tostudy` instalar o comando).
|
|
81
82
|
*/
|
|
83
|
+
// Fallback ROBUSTO quando o slash-command `/tostudy-<slug>` não existe (o
|
|
84
|
+
// `@tostudy-ai/cli` não rodou `init` no workspace, ou rodou DEPOIS do Claude
|
|
85
|
+
// subir — slash-commands carregam no startup, então um comando criado depois dá
|
|
86
|
+
// "Unknown command: /tostudy"). Em vez de cravar `/tostudy` (que erra), passamos
|
|
87
|
+
// um prompt de tutor em TEXTO: o Claude conduz o curso direto pela tostudy CLI
|
|
88
|
+
// (select/next/progress), sem depender de nenhum comando instalado. Sem apóstrofo
|
|
89
|
+
// no texto (o kickoff vai dentro de aspas simples no shell).
|
|
90
|
+
const COURSE_TUTOR_PROMPT = 'Você é o tutor do curso de onboarding da imersão Paris Group, rodando no terminal do aluno. ' +
|
|
91
|
+
'Comece AGORA falando primeiro, em português — o aluno ainda não digitou nada. ' +
|
|
92
|
+
'Antes de ensinar, prepare o curso da ToStudy em silêncio: rode no bash "tostudy courses --json" para ver o curso matriculado; ' +
|
|
93
|
+
'rode "tostudy select <id-ou-número-do-curso>" para ativá-lo; e "tostudy progress --json" para ver onde o aluno parou. ' +
|
|
94
|
+
'Depois conduza lição por lição, no ritmo do aluno, usando: "tostudy next" (avançar), "tostudy lesson" e "tostudy theory" (conteúdo da lição), ' +
|
|
95
|
+
'"tostudy hint" (dica) e "tostudy progress" (status). ' +
|
|
96
|
+
'Se o tostudy não estiver disponível ou sem curso ativo, use as ferramentas MCP get_course e complete_lesson do paris como fallback. ' +
|
|
97
|
+
'Dê as boas-vindas e já comece a primeira lição.';
|
|
82
98
|
export function resolveKickoff(cwd) {
|
|
83
99
|
const override = process.env.PARIS_COACH_KICKOFF;
|
|
84
100
|
if (override)
|
|
@@ -102,8 +118,12 @@ export function resolveKickoff(cwd) {
|
|
|
102
118
|
}
|
|
103
119
|
}
|
|
104
120
|
if (names.size === 0)
|
|
105
|
-
return
|
|
121
|
+
return COURSE_TUTOR_PROMPT;
|
|
106
122
|
const all = [...names];
|
|
123
|
+
// Nosso /tostudy (escrito pelo install/serve, sempre presente) é a rota
|
|
124
|
+
// CONFIÁVEL — preferimos ele: o aluno e o tutor sempre rodam via /tostudy.
|
|
125
|
+
if (all.some((n) => n.toLowerCase() === 'tostudy'))
|
|
126
|
+
return '/tostudy';
|
|
107
127
|
// Curso ATIVO deste workspace = fonte da verdade pra escolher entre vários cursos.
|
|
108
128
|
let slug = '';
|
|
109
129
|
try {
|
|
@@ -126,17 +146,35 @@ export function resolveKickoff(cwd) {
|
|
|
126
146
|
const courseSpecific = all.filter((n) => /^tostudy-.+/i.test(n)).sort();
|
|
127
147
|
if (courseSpecific.length)
|
|
128
148
|
return '/' + courseSpecific[0];
|
|
129
|
-
return
|
|
149
|
+
return COURSE_TUTOR_PROMPT;
|
|
130
150
|
}
|
|
131
151
|
/**
|
|
132
152
|
* Spawn the coached pty (`claude '/tostudy-<curso>'` inside the provisioned
|
|
133
153
|
* workspace). Shared by the local ws server and the relay agent. Throws if
|
|
134
154
|
* node-pty can't load/spawn — callers report it on their channel.
|
|
135
155
|
*/
|
|
156
|
+
/**
|
|
157
|
+
* Garante que o slash-command `/tostudy` exista no workspace ANTES do Claude
|
|
158
|
+
* subir (slash-commands carregam no startup — criados depois não aparecem sem
|
|
159
|
+
* reload). O `paris install` já escreve, mas reescrevemos aqui pra cobrir
|
|
160
|
+
* workspaces antigos e manter o conteúdo fresco a cada serve. Best-effort.
|
|
161
|
+
*/
|
|
162
|
+
function ensureTostudyCommand(cwd) {
|
|
163
|
+
try {
|
|
164
|
+
const dir = path.join(cwd, '.claude', 'commands');
|
|
165
|
+
mkdirSync(dir, { recursive: true });
|
|
166
|
+
writeFileSync(path.join(dir, 'tostudy.md'), renderTostudyCommand());
|
|
167
|
+
}
|
|
168
|
+
catch {
|
|
169
|
+
/* best-effort — se falhar, o resolveKickoff cai no fallback de texto */
|
|
170
|
+
}
|
|
171
|
+
}
|
|
136
172
|
export function spawnCoachPty(cwd) {
|
|
137
173
|
const req = createRequire(import.meta.url);
|
|
138
174
|
ensureSpawnHelperExecutable(req);
|
|
139
175
|
const ptyMod = req('node-pty');
|
|
176
|
+
// Escreve /tostudy ANTES de resolver o kickoff + subir o Claude.
|
|
177
|
+
ensureTostudyCommand(cwd);
|
|
140
178
|
const kickoff = resolveKickoff(cwd);
|
|
141
179
|
const shell = process.env.SHELL || (process.platform === 'win32' ? 'powershell.exe' : 'bash');
|
|
142
180
|
const shellArgs = process.platform === 'win32'
|
package/dist/index.js
CHANGED
|
@@ -13,7 +13,7 @@ import { daemonCommand, restartCommand } from './commands/daemon.js';
|
|
|
13
13
|
const program = new Command()
|
|
14
14
|
.name('paris')
|
|
15
15
|
.description('Paris Immersion — CLI do aluno (login, workspace, tarefas)')
|
|
16
|
-
.version('0.1.
|
|
16
|
+
.version('0.1.14');
|
|
17
17
|
program
|
|
18
18
|
.command('login')
|
|
19
19
|
.description('Autenticar com sua conta Paris (device-code OR --token direto)')
|
package/dist/templates/hook.js
CHANGED
|
@@ -139,7 +139,7 @@ async function main() {
|
|
|
139
139
|
if (inCourse) {
|
|
140
140
|
lines.push('## FASE DO CURSO DE ONBOARDING')
|
|
141
141
|
lines.push('O aluno está na etapa do curso **' + course.title + '** (progresso: **' + course.lessonsCompleted + '/' + course.totalLessons + '**).')
|
|
142
|
-
lines.push('Rode
|
|
142
|
+
lines.push('Rode **/tostudy** pra conduzir o curso da ToStudy (o comando está instalado no workspace e o aluno já chega logado). Ele ativa o curso e ensina lição por lição. NÃO trabalhe em missões da imersão enquanto o curso não terminar. (Fallback: ferramentas MCP **get_course**/**complete_lesson** do paris.)')
|
|
143
143
|
if (PHASE === 'session-start') {
|
|
144
144
|
lines.push('')
|
|
145
145
|
lines.push('**Comece falando primeiro**: rode /tostudy e dê as boas-vindas. O aluno ainda não digitou nada.')
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* O slash-command `/tostudy` que O NOSSO LADO escreve no workspace
|
|
3
|
+
* (`<ws>/.claude/commands/tostudy.md`). É a rota CONFIÁVEL pra iniciar o curso:
|
|
4
|
+
* o `@tostudy-ai/cli` só instala um comando específico do curso (`/tostudy-<slug>`)
|
|
5
|
+
* SE rodar `init` — e mesmo então, se o Claude já tinha subido, ele não recarrega
|
|
6
|
+
* slash-commands. Escrevendo `/tostudy` nós mesmos (no `paris install` E no
|
|
7
|
+
* `paris serve`, antes do Claude subir), garantimos que `/tostudy` SEMPRE exista
|
|
8
|
+
* e SEMPRE funcione, conduzindo o curso real via a tostudy CLI.
|
|
9
|
+
*/
|
|
10
|
+
export declare function renderTostudyCommand(): string;
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* O slash-command `/tostudy` que O NOSSO LADO escreve no workspace
|
|
3
|
+
* (`<ws>/.claude/commands/tostudy.md`). É a rota CONFIÁVEL pra iniciar o curso:
|
|
4
|
+
* o `@tostudy-ai/cli` só instala um comando específico do curso (`/tostudy-<slug>`)
|
|
5
|
+
* SE rodar `init` — e mesmo então, se o Claude já tinha subido, ele não recarrega
|
|
6
|
+
* slash-commands. Escrevendo `/tostudy` nós mesmos (no `paris install` E no
|
|
7
|
+
* `paris serve`, antes do Claude subir), garantimos que `/tostudy` SEMPRE exista
|
|
8
|
+
* e SEMPRE funcione, conduzindo o curso real via a tostudy CLI.
|
|
9
|
+
*/
|
|
10
|
+
export function renderTostudyCommand() {
|
|
11
|
+
return `---
|
|
12
|
+
description: Inicia ou retoma o curso de onboarding da imersão (tutor ToStudy no terminal)
|
|
13
|
+
---
|
|
14
|
+
|
|
15
|
+
Você é o **tutor do curso de onboarding** da imersão Paris Group, rodando no terminal do aluno. Conduza o curso da ToStudy em **português**, no ritmo do aluno. O aluno já chega logado na conta dele da ToStudy (auto-login no install).
|
|
16
|
+
|
|
17
|
+
## 1. Prepare o curso (em silêncio, antes de falar)
|
|
18
|
+
|
|
19
|
+
1. Rode \`tostudy courses --json\` para ver o curso em que o aluno está matriculado.
|
|
20
|
+
2. Rode \`tostudy select <id-ou-número-do-curso>\` para ativá-lo (escolha o curso da imersão; normalmente só há um).
|
|
21
|
+
3. Rode \`tostudy progress --json\` para ver onde ele parou.
|
|
22
|
+
|
|
23
|
+
## 2. Ensine lição por lição
|
|
24
|
+
|
|
25
|
+
- \`tostudy next\` — avança para a próxima lição (marca a atual como concluída).
|
|
26
|
+
- \`tostudy lesson\` / \`tostudy theory\` — conteúdo da lição atual.
|
|
27
|
+
- \`tostudy hint\` — dica progressiva quando o aluno travar.
|
|
28
|
+
- \`tostudy validate\` — valida exercícios.
|
|
29
|
+
- \`tostudy progress\` — status atual (lição X de Y, %).
|
|
30
|
+
|
|
31
|
+
Explique cada lição com suas palavras, traga exemplos do contexto de negócio do aluno e só avance quando ele entender. **Comece dando as boas-vindas** e já ensinando a primeira lição — o aluno ainda não digitou nada. NÃO trabalhe em missões da imersão enquanto o curso não terminar.
|
|
32
|
+
|
|
33
|
+
## 3. Fallback (se a ToStudy não estiver disponível)
|
|
34
|
+
|
|
35
|
+
Use as ferramentas MCP do paris: \`get_course\` (currículo + progresso) e \`complete_lesson\` (marcar lição concluída).
|
|
36
|
+
`;
|
|
37
|
+
}
|
package/package.json
CHANGED