paris-immersion 0.1.23 → 0.1.24
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 +54 -1
- package/dist/templates/settings.js +15 -0
- package/package.json +1 -1
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.24');
|
|
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
|
|
@@ -173,6 +209,23 @@ async function main() {
|
|
|
173
209
|
await apiPost(apiUrl, '/immersion-app/v1/events', creds.token, { type: 'session_started' })
|
|
174
210
|
}
|
|
175
211
|
|
|
212
|
+
// user-prompt-submit: encaminha o prompt do aluno pro Sócio (gera coaching
|
|
213
|
+
// proativo server-side, agora não-bloqueante) + dá ao mentor o "o que ele
|
|
214
|
+
// pediu". Best-effort, depois do contexto já impresso.
|
|
215
|
+
if (PHASE === 'user-prompt-submit') {
|
|
216
|
+
// turn_started fecha o ciclo do turno (com turn_ended no stop) → permite medir
|
|
217
|
+
// cadência/duração de turno e detectar "mandou prompt e não houve resposta".
|
|
218
|
+
await apiPost(apiUrl, '/immersion-app/v1/events', creds.token, { type: 'turn_started' })
|
|
219
|
+
if (hookInput && hookInput.prompt) {
|
|
220
|
+
await apiPost(apiUrl, '/api/activity/claude-code', creds.token, {
|
|
221
|
+
hook_event_name: 'UserPromptSubmit',
|
|
222
|
+
prompt: hookInput.prompt,
|
|
223
|
+
session_id: hookInput.session_id,
|
|
224
|
+
cwd: hookInput.cwd,
|
|
225
|
+
})
|
|
226
|
+
}
|
|
227
|
+
}
|
|
228
|
+
|
|
176
229
|
// Na fase do curso, sincroniza o progresso da ToStudy também ao abrir/retomar
|
|
177
230
|
// a sessão (não só no stop), pra gestão refletir mesmo entre interações.
|
|
178
231
|
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