paris-immersion 0.1.23 → 0.1.25

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/index.js CHANGED
@@ -14,7 +14,7 @@ import { tostudyLoginCommand } from './commands/tostudy-login.js';
14
14
  const program = new Command()
15
15
  .name('paris')
16
16
  .description('Paris Immersion — CLI do aluno (login, workspace, tarefas)')
17
- .version('0.1.23');
17
+ .version('0.1.25');
18
18
  program
19
19
  .command('login')
20
20
  .description('Autenticar com sua conta Paris (device-code OR --token direto)')
@@ -18,7 +18,7 @@ import { homedir } from 'node:os'
18
18
  import path from 'node:path'
19
19
  import { execFile } from 'node:child_process'
20
20
 
21
- const PHASE = process.argv[2] // session-start | user-prompt-submit | stop
21
+ const PHASE = process.argv[2] // session-start | user-prompt-submit | stop | post-tool-use
22
22
 
23
23
  async function loadCredentials() {
24
24
  try {
@@ -59,6 +59,24 @@ async function apiPost(apiUrl, pathname, token, body) {
59
59
  return res.json().catch(() => null)
60
60
  }
61
61
 
62
+ // Claude Code passa o payload do hook (session_id, tool_name, tool_input, prompt,
63
+ // cwd, ...) como JSON no stdin. Lê com timeout curto pra NUNCA travar a sessão.
64
+ function readStdin() {
65
+ return new Promise((resolve) => {
66
+ try {
67
+ if (process.stdin.isTTY) return resolve(null)
68
+ let data = ''
69
+ let done = false
70
+ const finish = () => { if (done) return; done = true; try { resolve(data ? JSON.parse(data) : null) } catch { resolve(null) } }
71
+ process.stdin.setEncoding('utf8')
72
+ process.stdin.on('data', (c) => { data += c })
73
+ process.stdin.on('end', finish)
74
+ process.stdin.on('error', () => finish())
75
+ setTimeout(finish, 500)
76
+ } catch { resolve(null) }
77
+ })
78
+ }
79
+
62
80
  function runCmd(cmd, args, cwd) {
63
81
  return new Promise((resolve) => {
64
82
  try {
@@ -99,6 +117,24 @@ async function main() {
99
117
 
100
118
  const apiUrl = marker.marker.api_url || creds.api_url
101
119
 
120
+ // Atividade do Claude Code do aluno → endpoint pronto (/api/activity/claude-code)
121
+ // que alimenta socio_messages, o coaching proativo do Sócio e a visão do mentor.
122
+ const hookInput = (PHASE === 'post-tool-use' || PHASE === 'user-prompt-submit') ? await readStdin() : null
123
+
124
+ if (PHASE === 'post-tool-use') {
125
+ // Rápido e silencioso (sem stdout, sem apiGets) — roda após cada Write/Edit/Bash.
126
+ if (hookInput && hookInput.tool_name) {
127
+ await apiPost(apiUrl, '/api/activity/claude-code', creds.token, {
128
+ hook_event_name: 'PostToolUse',
129
+ tool_name: hookInput.tool_name,
130
+ tool_input: hookInput.tool_input || {},
131
+ session_id: hookInput.session_id,
132
+ cwd: hookInput.cwd,
133
+ })
134
+ }
135
+ return
136
+ }
137
+
102
138
  if (PHASE === 'stop') {
103
139
  await apiPost(apiUrl, '/immersion-app/v1/events', creds.token, { type: 'turn_ended' })
104
140
  // Logo após cada interação é o melhor momento pra capturar uma lição que o
@@ -167,12 +203,41 @@ async function main() {
167
203
  }
168
204
  }
169
205
 
206
+ // #3: proativos do Sócio pendentes → injeta no contexto (servidor marca
207
+ // entregue ao ler). É assim que o coaching proativo chega ao terminal do aluno.
208
+ const pending = await apiGet(apiUrl, '/immersion-app/v1/socio/pending', creds.token)
209
+ if (pending && Array.isArray(pending.messages) && pending.messages.length > 0) {
210
+ lines.push('')
211
+ lines.push('## Mensagens do Sócio pra você (proativo)')
212
+ lines.push('Repasse isto ao aluno com naturalidade, no momento certo:')
213
+ for (const m of pending.messages) {
214
+ lines.push('- ' + String(m.content || '').split('\\n').join(' '))
215
+ }
216
+ }
217
+
170
218
  process.stdout.write(lines.join('\\n') + '\\n')
171
219
 
172
220
  if (PHASE === 'session-start') {
173
221
  await apiPost(apiUrl, '/immersion-app/v1/events', creds.token, { type: 'session_started' })
174
222
  }
175
223
 
224
+ // user-prompt-submit: encaminha o prompt do aluno pro Sócio (gera coaching
225
+ // proativo server-side, agora não-bloqueante) + dá ao mentor o "o que ele
226
+ // pediu". Best-effort, depois do contexto já impresso.
227
+ if (PHASE === 'user-prompt-submit') {
228
+ // turn_started fecha o ciclo do turno (com turn_ended no stop) → permite medir
229
+ // cadência/duração de turno e detectar "mandou prompt e não houve resposta".
230
+ await apiPost(apiUrl, '/immersion-app/v1/events', creds.token, { type: 'turn_started' })
231
+ if (hookInput && hookInput.prompt) {
232
+ await apiPost(apiUrl, '/api/activity/claude-code', creds.token, {
233
+ hook_event_name: 'UserPromptSubmit',
234
+ prompt: hookInput.prompt,
235
+ session_id: hookInput.session_id,
236
+ cwd: hookInput.cwd,
237
+ })
238
+ }
239
+ }
240
+
176
241
  // Na fase do curso, sincroniza o progresso da ToStudy também ao abrir/retomar
177
242
  // a sessão (não só no stop), pra gestão refletir mesmo entre interações.
178
243
  if (inCourse) {
@@ -46,6 +46,21 @@ export function renderSettingsJson(opts) {
46
46
  ],
47
47
  },
48
48
  ],
49
+ // PostToolUse: telemetria da atividade REAL do aluno (arquivos editados +
50
+ // comandos) → /api/activity/claude-code, pra o mentor enxergar o trabalho
51
+ // e o Sócio coachar. Matcher de alto sinal (Write|Edit|Bash); Read fica de
52
+ // fora pra não inundar o feed.
53
+ PostToolUse: [
54
+ {
55
+ matcher: 'Write|Edit|Bash',
56
+ hooks: [
57
+ {
58
+ type: 'command',
59
+ command: 'node .paris/hooks/paris-hook.mjs post-tool-use',
60
+ },
61
+ ],
62
+ },
63
+ ],
49
64
  Stop: [
50
65
  {
51
66
  matcher: '*',
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "paris-immersion",
3
- "version": "0.1.23",
4
- "description": "Paris Immersion CLI — login, install workspace, status, task control. Para alunos das imersões da Paris Group.",
3
+ "version": "0.1.25",
4
+ "description": "Paris Immersion CLI \u2014 login, install workspace, status, task control. Para alunos das imers\u00f5es da Paris Group.",
5
5
  "homepage": "https://parisgroup.ai",
6
6
  "repository": {
7
7
  "type": "git",
@@ -13,7 +13,7 @@
13
13
  "paris-group",
14
14
  "paris-immersion",
15
15
  "cli",
16
- "imersão"
16
+ "imers\u00e3o"
17
17
  ],
18
18
  "type": "module",
19
19
  "bin": {