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 +1 -1
- package/dist/templates/hook.js +66 -1
- package/dist/templates/settings.js +15 -0
- package/package.json +3 -3
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.
|
|
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)')
|
package/dist/templates/hook.js
CHANGED
|
@@ -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.
|
|
4
|
-
"description": "Paris Immersion CLI
|
|
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
|
-
"
|
|
16
|
+
"imers\u00e3o"
|
|
17
17
|
],
|
|
18
18
|
"type": "module",
|
|
19
19
|
"bin": {
|