paris-immersion 0.1.35 → 0.1.36
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 +3 -1
- package/dist/commands/repair.d.ts +3 -0
- package/dist/commands/repair.js +48 -0
- package/dist/commands/serve.js +6 -1
- package/dist/index.js +6 -0
- package/dist/templates/settings.d.ts +8 -0
- package/dist/templates/settings.js +30 -4
- package/package.json +1 -1
- package/templates/mcp/admin.mjs +547 -147
- package/templates/mcp/index.mjs +547 -147
package/dist/commands/install.js
CHANGED
|
@@ -7,7 +7,7 @@ import { findMarker, writeMarker } from '../lib/marker.js';
|
|
|
7
7
|
import { readCredentials } from '../lib/credentials.js';
|
|
8
8
|
import { c } from '../lib/colors.js';
|
|
9
9
|
import { renderClaudeMd } from '../templates/claude-md.js';
|
|
10
|
-
import { renderSettingsJson } from '../templates/settings.js';
|
|
10
|
+
import { renderSettingsJson, renderMcpJson } from '../templates/settings.js';
|
|
11
11
|
import { renderParisCoachSkill } from '../templates/skill.js';
|
|
12
12
|
import { renderHookScript } from '../templates/hook.js';
|
|
13
13
|
import { renderTostudyCommand } from '../templates/tostudy-command.js';
|
|
@@ -119,6 +119,8 @@ async function writeWorkspaceFiles(targetDir, boot, apiUrl, email) {
|
|
|
119
119
|
const settingsPayload = renderSettingsJson({ apiUrl });
|
|
120
120
|
await fs.writeFile(path.join(claudeDir, 'settings.json'), JSON.stringify(settingsPayload, null, 2));
|
|
121
121
|
await fs.writeFile(path.join(claudeDir, 'settings.local.json'), JSON.stringify(settingsPayload, null, 2));
|
|
122
|
+
// .mcp.json — é daqui que o Claude Code carrega o MCP do projeto (não do settings).
|
|
123
|
+
await fs.writeFile(path.join(targetDir, '.mcp.json'), JSON.stringify(renderMcpJson({ apiUrl }), null, 2));
|
|
122
124
|
// .claude/skills/paris-coach/SKILL.md
|
|
123
125
|
const skillDir = path.join(claudeDir, 'skills', 'paris-coach');
|
|
124
126
|
await fs.mkdir(skillDir, { recursive: true });
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
|
+
import { spawnSync } from 'node:child_process';
|
|
3
|
+
import { homedir } from 'node:os';
|
|
4
|
+
import path from 'node:path';
|
|
5
|
+
import { findMarker } from '../lib/marker.js';
|
|
6
|
+
import { c } from '../lib/colors.js';
|
|
7
|
+
import { installCommand } from './install.js';
|
|
8
|
+
/**
|
|
9
|
+
* `paris repair` — uma palavra só, sem `cd` nem `&&`. Existe porque o comando
|
|
10
|
+
* longo colado do WhatsApp quebrava (o app troca espaços por NBSP e o shell lê
|
|
11
|
+
* `cd: too many arguments`). Acha o workspace sozinho (a partir da pasta atual,
|
|
12
|
+
* subindo; senão o `~/paris-*` mais recente), reescreve hooks, MCP (.mcp.json),
|
|
13
|
+
* settings e CLAUDE.md SEM tocar no trabalho do aluno, e reabre o Claude na
|
|
14
|
+
* raiz continuando a última conversa.
|
|
15
|
+
*/
|
|
16
|
+
async function locateWorkspace() {
|
|
17
|
+
const here = await findMarker();
|
|
18
|
+
if (here)
|
|
19
|
+
return here.dir;
|
|
20
|
+
const home = homedir();
|
|
21
|
+
const entries = await fs.readdir(home).catch(() => []);
|
|
22
|
+
let best = null;
|
|
23
|
+
for (const name of entries) {
|
|
24
|
+
if (!name.startsWith('paris-'))
|
|
25
|
+
continue;
|
|
26
|
+
const marker = path.join(home, name, '.paris', 'workspace.json');
|
|
27
|
+
const st = await fs.stat(marker).catch(() => null);
|
|
28
|
+
if (st && (!best || st.mtimeMs > best.mtime))
|
|
29
|
+
best = { dir: path.join(home, name), mtime: st.mtimeMs };
|
|
30
|
+
}
|
|
31
|
+
return best?.dir ?? null;
|
|
32
|
+
}
|
|
33
|
+
export async function repairCommand(opts) {
|
|
34
|
+
const dir = await locateWorkspace();
|
|
35
|
+
if (!dir) {
|
|
36
|
+
console.error(c.red('Não achei a pasta da imersão. Rode `paris install` primeiro.'));
|
|
37
|
+
process.exit(1);
|
|
38
|
+
}
|
|
39
|
+
console.log(c.dim(`Consertando o workspace em ${dir}...`));
|
|
40
|
+
await installCommand({ dir });
|
|
41
|
+
if (opts.open === false)
|
|
42
|
+
return;
|
|
43
|
+
console.log(c.green('✓ Pronto. Abrindo o Claude na pasta da imersão (continuando a última conversa)...'));
|
|
44
|
+
const cont = spawnSync('claude', ['--continue'], { cwd: dir, stdio: 'inherit' });
|
|
45
|
+
if (cont.error || (cont.status !== 0 && cont.status !== null)) {
|
|
46
|
+
spawnSync('claude', [], { cwd: dir, stdio: 'inherit' });
|
|
47
|
+
}
|
|
48
|
+
}
|
package/dist/commands/serve.js
CHANGED
|
@@ -9,7 +9,7 @@ import { startRelayAgent } from '../lib/relay-agent.js';
|
|
|
9
9
|
import { parseMissionAction, startMissionInOrca } from '../lib/orca-launcher.js';
|
|
10
10
|
import { renderTostudyCommand } from '../templates/tostudy-command.js';
|
|
11
11
|
import { renderHookScript } from '../templates/hook.js';
|
|
12
|
-
import { renderSettingsJson } from '../templates/settings.js';
|
|
12
|
+
import { renderSettingsJson, renderMcpJson } from '../templates/settings.js';
|
|
13
13
|
import { fileURLToPath } from 'node:url';
|
|
14
14
|
import { maybeSelfUpdate } from '../lib/self-update.js';
|
|
15
15
|
import { c } from '../lib/colors.js';
|
|
@@ -210,6 +210,7 @@ function ensureWorkspaceVendored(cwd) {
|
|
|
210
210
|
const payload = JSON.stringify(renderSettingsJson({ apiUrl: marker.api_url }), null, 2);
|
|
211
211
|
writeFileSync(path.join(claudeDir, 'settings.json'), payload);
|
|
212
212
|
writeFileSync(path.join(claudeDir, 'settings.local.json'), payload);
|
|
213
|
+
writeFileSync(path.join(cwd, '.mcp.json'), JSON.stringify(renderMcpJson({ apiUrl: marker.api_url }), null, 2));
|
|
213
214
|
}
|
|
214
215
|
}
|
|
215
216
|
catch {
|
|
@@ -478,6 +479,10 @@ export async function serveCommand(opts) {
|
|
|
478
479
|
try {
|
|
479
480
|
const { dir, created } = await ensureWorkspace({});
|
|
480
481
|
cwd = dir;
|
|
482
|
+
// Auto-cura já no boot do daemon (não só quando o terminal embutido abre):
|
|
483
|
+
// quem usa o Claude no terminal próprio/Orca também recebe .mcp.json/hook novos
|
|
484
|
+
// assim que o self-update reinicia o serve.
|
|
485
|
+
ensureWorkspaceVendored(dir);
|
|
481
486
|
console.log(created
|
|
482
487
|
? ` ${c.green('✓')} workspace criado em ${c.bold(dir)}`
|
|
483
488
|
: ` ${c.dim('workspace:')} ${dir}`);
|
package/dist/index.js
CHANGED
|
@@ -3,6 +3,7 @@ import { Command } from 'commander';
|
|
|
3
3
|
import { createRequire } from 'node:module';
|
|
4
4
|
import { loginCommand } from './commands/login.js';
|
|
5
5
|
import { installCommand } from './commands/install.js';
|
|
6
|
+
import { repairCommand } from './commands/repair.js';
|
|
6
7
|
import { doctorCommand } from './commands/doctor.js';
|
|
7
8
|
import { tasksCommand } from './commands/tasks.js';
|
|
8
9
|
import { statusCommand } from './commands/status.js';
|
|
@@ -32,6 +33,11 @@ program
|
|
|
32
33
|
.option('-d, --dir <path>', 'pasta destino (padrão: cria uma nova com o slug da imersão)')
|
|
33
34
|
.option('--immersion <slug>', 'imersão específica (caso o aluno esteja em mais de uma)')
|
|
34
35
|
.action(installCommand);
|
|
36
|
+
program
|
|
37
|
+
.command('repair')
|
|
38
|
+
.description('Conserta a pasta da imersão (hooks, ferramentas do Claude) sem apagar nada e reabre o Claude')
|
|
39
|
+
.option('--no-open', 'só conserta, não abre o Claude')
|
|
40
|
+
.action(repairCommand);
|
|
35
41
|
program
|
|
36
42
|
.command('doctor')
|
|
37
43
|
.description('Diagnostica credentials, marker, hooks, conectividade')
|
|
@@ -10,3 +10,11 @@
|
|
|
10
10
|
export declare function renderSettingsJson(opts: {
|
|
11
11
|
apiUrl: string;
|
|
12
12
|
}): Record<string, unknown>;
|
|
13
|
+
/**
|
|
14
|
+
* `.mcp.json` do workspace — o ÚNICO lugar de onde o Claude Code carrega MCP de
|
|
15
|
+
* projeto. Sem ele o aluno fica sem get_current_task/mark_task_complete/
|
|
16
|
+
* log_event e a missão não conclui sozinha (só pelo botão da War Room).
|
|
17
|
+
*/
|
|
18
|
+
export declare function renderMcpJson(opts: {
|
|
19
|
+
apiUrl: string;
|
|
20
|
+
}): Record<string, unknown>;
|
|
@@ -7,6 +7,10 @@
|
|
|
7
7
|
* already a prerequisite. Paths are relative to the workspace root, which is
|
|
8
8
|
* the cwd Claude Code uses when launching hooks and MCP servers.
|
|
9
9
|
*/
|
|
10
|
+
// Hooks apontam pra RAIZ do projeto ($CLAUDE_PROJECT_DIR, setado pelo Claude Code
|
|
11
|
+
// em todo hook). Com caminho relativo, bastava o Claude dar `cd` numa subpasta
|
|
12
|
+
// (ex.: vault/10-SOPs) pra TODO hook falhar com MODULE_NOT_FOUND — o aluno some
|
|
13
|
+
// do painel e a sessão enche de "Stop hook error" (treino 30/09, 2026-09-30).
|
|
10
14
|
export function renderSettingsJson(opts) {
|
|
11
15
|
return {
|
|
12
16
|
$schema: 'https://json.schemastore.org/claude-code-settings.json',
|
|
@@ -14,6 +18,12 @@ export function renderSettingsJson(opts) {
|
|
|
14
18
|
// pode não conectar o server numa sessão não-interativa (terminal embutido),
|
|
15
19
|
// e o tutor abre sem as tools get_course/complete_lesson.
|
|
16
20
|
enableAllProjectMcpServers: true,
|
|
21
|
+
// O Claude Code NÃO carrega `mcpServers` do settings.json — MCP de projeto
|
|
22
|
+
// vive no `.mcp.json` (renderMcpJson abaixo). Este bloco fica só por
|
|
23
|
+
// compatibilidade; o que liga o server de verdade é o .mcp.json + esta
|
|
24
|
+
// pré-aprovação (sem ela o server fica "Pending approval"). Achado no
|
|
25
|
+
// teste real de 2026-09-29: o Claude do aluno abria SEM mark_task_complete.
|
|
26
|
+
enabledMcpjsonServers: ['paris-immersion'],
|
|
17
27
|
mcpServers: {
|
|
18
28
|
'paris-immersion': {
|
|
19
29
|
command: 'node',
|
|
@@ -30,7 +40,7 @@ export function renderSettingsJson(opts) {
|
|
|
30
40
|
hooks: [
|
|
31
41
|
{
|
|
32
42
|
type: 'command',
|
|
33
|
-
command: 'node
|
|
43
|
+
command: 'node "$CLAUDE_PROJECT_DIR/.paris/hooks/paris-hook.mjs" session-start',
|
|
34
44
|
},
|
|
35
45
|
],
|
|
36
46
|
},
|
|
@@ -41,7 +51,7 @@ export function renderSettingsJson(opts) {
|
|
|
41
51
|
hooks: [
|
|
42
52
|
{
|
|
43
53
|
type: 'command',
|
|
44
|
-
command: 'node
|
|
54
|
+
command: 'node "$CLAUDE_PROJECT_DIR/.paris/hooks/paris-hook.mjs" user-prompt-submit',
|
|
45
55
|
},
|
|
46
56
|
],
|
|
47
57
|
},
|
|
@@ -56,7 +66,7 @@ export function renderSettingsJson(opts) {
|
|
|
56
66
|
hooks: [
|
|
57
67
|
{
|
|
58
68
|
type: 'command',
|
|
59
|
-
command: 'node
|
|
69
|
+
command: 'node "$CLAUDE_PROJECT_DIR/.paris/hooks/paris-hook.mjs" post-tool-use',
|
|
60
70
|
},
|
|
61
71
|
],
|
|
62
72
|
},
|
|
@@ -67,7 +77,7 @@ export function renderSettingsJson(opts) {
|
|
|
67
77
|
hooks: [
|
|
68
78
|
{
|
|
69
79
|
type: 'command',
|
|
70
|
-
command: 'node
|
|
80
|
+
command: 'node "$CLAUDE_PROJECT_DIR/.paris/hooks/paris-hook.mjs" stop',
|
|
71
81
|
},
|
|
72
82
|
],
|
|
73
83
|
},
|
|
@@ -75,3 +85,19 @@ export function renderSettingsJson(opts) {
|
|
|
75
85
|
},
|
|
76
86
|
};
|
|
77
87
|
}
|
|
88
|
+
/**
|
|
89
|
+
* `.mcp.json` do workspace — o ÚNICO lugar de onde o Claude Code carrega MCP de
|
|
90
|
+
* projeto. Sem ele o aluno fica sem get_current_task/mark_task_complete/
|
|
91
|
+
* log_event e a missão não conclui sozinha (só pelo botão da War Room).
|
|
92
|
+
*/
|
|
93
|
+
export function renderMcpJson(opts) {
|
|
94
|
+
return {
|
|
95
|
+
mcpServers: {
|
|
96
|
+
'paris-immersion': {
|
|
97
|
+
command: 'node',
|
|
98
|
+
args: ['.paris/mcp/index.mjs'],
|
|
99
|
+
env: { PARIS_API_URL: opts.apiUrl },
|
|
100
|
+
},
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
package/package.json
CHANGED