paris-immersion 0.1.9 → 0.1.10

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.
@@ -1,6 +1,7 @@
1
1
  import { WebSocketServer } from 'ws';
2
2
  import { createRequire } from 'node:module';
3
3
  import { promises as fs, chmodSync, existsSync, statSync, readdirSync, readFileSync } from 'node:fs';
4
+ import { homedir } from 'node:os';
4
5
  import path from 'node:path';
5
6
  import { readCredentials } from '../lib/credentials.js';
6
7
  import { ensureWorkspace } from './install.js';
@@ -82,32 +83,50 @@ export function resolveKickoff(cwd) {
82
83
  const override = process.env.PARIS_COACH_KICKOFF;
83
84
  if (override)
84
85
  return override;
85
- try {
86
- const cmdDir = path.join(cwd, '.claude', 'commands');
87
- const files = readdirSync(cmdDir).filter((f) => /^tostudy.*\.md$/i.test(f));
88
- if (files.length === 0)
89
- return '/tostudy';
90
- let chosen = files.sort()[0];
91
- if (files.length > 1) {
92
- try {
93
- const ws = JSON.parse(readFileSync(path.join(cwd, '.tostudy', 'workspace.json'), 'utf8'));
94
- const slug = ws.slug?.toLowerCase();
95
- if (slug) {
96
- const match = files.find((f) => f.toLowerCase() === `tostudy-${slug}.md`) ??
97
- files.find((f) => f.toLowerCase().includes(slug));
98
- if (match)
99
- chosen = match;
100
- }
101
- }
102
- catch {
103
- /* sem workspace.json legível — usa o primeiro */
86
+ // O Claude Code carrega slash-commands de DOIS lugares: o do projeto
87
+ // (workspace) e o global do usuário (~/.claude/commands). O `@tostudy-ai/cli`
88
+ // instala o comando do tutor em qualquer um deles (na prática, no global) —
89
+ // então varremos os DOIS. Sem isto, numa máquina onde o tostudy escreve só no
90
+ // global, o kickoff não acharia o comando e cairia em `/tostudy` (Unknown).
91
+ const dirs = [path.join(cwd, '.claude', 'commands'), path.join(homedir(), '.claude', 'commands')];
92
+ const names = new Set();
93
+ for (const d of dirs) {
94
+ try {
95
+ for (const f of readdirSync(d)) {
96
+ if (/^tostudy.*\.md$/i.test(f))
97
+ names.add(f.replace(/\.md$/i, ''));
104
98
  }
105
99
  }
106
- return '/' + chosen.replace(/\.md$/i, '');
100
+ catch {
101
+ /* diretório não existe — ok */
102
+ }
107
103
  }
108
- catch {
104
+ if (names.size === 0)
109
105
  return '/tostudy';
106
+ const all = [...names];
107
+ // Curso ATIVO deste workspace = fonte da verdade pra escolher entre vários cursos.
108
+ let slug = '';
109
+ try {
110
+ const ws = JSON.parse(readFileSync(path.join(cwd, '.tostudy', 'workspace.json'), 'utf8'));
111
+ slug = (ws.slug ?? '').toLowerCase();
112
+ }
113
+ catch {
114
+ /* sem workspace.json — segue sem slug */
115
+ }
116
+ if (slug) {
117
+ const exact = all.find((n) => n.toLowerCase() === `tostudy-${slug}`);
118
+ if (exact)
119
+ return '/' + exact;
120
+ const partial = all.find((n) => n.toLowerCase().includes(slug));
121
+ if (partial)
122
+ return '/' + partial;
110
123
  }
124
+ // Sem match por slug: prefere um comando ESPECÍFICO de curso (`tostudy-<algo>`)
125
+ // ao genérico `tostudy`. Se só houver o genérico, usa `/tostudy`.
126
+ const courseSpecific = all.filter((n) => /^tostudy-.+/i.test(n)).sort();
127
+ if (courseSpecific.length)
128
+ return '/' + courseSpecific[0];
129
+ return '/tostudy';
111
130
  }
112
131
  /**
113
132
  * Spawn the coached pty (`claude '/tostudy-<curso>'` inside the provisioned
package/dist/index.js CHANGED
@@ -13,7 +13,7 @@ import { daemonCommand } 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.9');
16
+ .version('0.1.10');
17
17
  program
18
18
  .command('login')
19
19
  .description('Autenticar com sua conta Paris (device-code OR --token direto)')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paris-immersion",
3
- "version": "0.1.9",
3
+ "version": "0.1.10",
4
4
  "description": "Paris Immersion CLI — login, install workspace, status, task control. Para alunos das imersões da Paris Group.",
5
5
  "homepage": "https://parisgroup.ai",
6
6
  "repository": {