paris-immersion 0.1.33 → 0.1.35

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.
@@ -27,6 +27,7 @@ export async function installCommand(opts) {
27
27
  const boot = await bootstrapWorkspace(opts.immersion);
28
28
  const targetDir = path.resolve(opts.dir ?? `paris-${boot.immersion.slug}`);
29
29
  await writeWorkspaceFiles(targetDir, boot, creds.api_url, creds.email);
30
+ await ensurePrototipador(targetDir);
30
31
  console.log();
31
32
  console.log(c.green(`✓ Workspace pronto em ${targetDir}`));
32
33
  console.log();
@@ -55,8 +56,40 @@ export async function ensureWorkspace(opts = {}) {
55
56
  // Predictable, findable location regardless of where `paris serve` was run.
56
57
  const targetDir = path.join(homedir(), `paris-${boot.immersion.slug}`);
57
58
  await writeWorkspaceFiles(targetDir, boot, creds.api_url, creds.email);
59
+ await ensurePrototipador(targetDir);
58
60
  return { dir: targetDir, created: true };
59
61
  }
62
+ /**
63
+ * Prototipador (fork literal do buildermethods/design-os, 2026-08-17): é um
64
+ * TEMPLATE que o aluno trabalha dentro — clona pra `<ws>/prototipador`.
65
+ * Preferência: copiar do clone que o instalador deixou em ~/prototipador
66
+ * (offline-friendly); fallback: clone direto. Best-effort — sem git/rede, a
67
+ * missão 4 instrui o clone manual.
68
+ */
69
+ async function ensurePrototipador(targetDir) {
70
+ const dest = path.join(targetDir, 'prototipador');
71
+ try {
72
+ await fs.stat(dest);
73
+ return; // já existe
74
+ }
75
+ catch { /* segue */ }
76
+ const { execFile } = await import('node:child_process');
77
+ const { promisify } = await import('node:util');
78
+ const execFileP = promisify(execFile);
79
+ const homeClone = path.join(homedir(), 'prototipador');
80
+ try {
81
+ await fs.stat(path.join(homeClone, '.git'));
82
+ await execFileP('git', ['clone', homeClone, dest], { timeout: 30_000 });
83
+ // Aponta o origin pro remoto real (o clone local viraria origin=~/prototipador).
84
+ await execFileP('git', ['-C', dest, 'remote', 'set-url', 'origin', 'https://github.com/parisgroup-ai/prototipador.git'], { timeout: 10_000 }).catch(() => { });
85
+ return;
86
+ }
87
+ catch { /* sem clone local — tenta a rede */ }
88
+ try {
89
+ await execFileP('git', ['clone', 'https://github.com/parisgroup-ai/prototipador', dest], { timeout: 60_000 });
90
+ }
91
+ catch { /* best-effort */ }
92
+ }
60
93
  async function bootstrapWorkspace(immersion) {
61
94
  const slugQuery = immersion ? `?immersion=${encodeURIComponent(immersion)}` : '';
62
95
  return apiAuthFetch(`/immersion-app/v1/workspace/bootstrap${slugQuery}`, {
@@ -21,7 +21,8 @@ export function renderClaudeMd(opts) {
21
21
  3. **Transparência total.** Tudo aqui é gravado em audit_logs e o instrutor pode reabrir qualquer tarefa.
22
22
  4. **Nunca invente progresso.** Só marque o que de fato foi feito nessa sessão.
23
23
  5. **Inicie a sessão falando primeiro.** O hook SessionStart te entrega o cronograma + a tarefa atual antes do aluno digitar — abra o dia com um resumo claro (fase, tarefa, primeiro passo sugerido).
24
- 6. **O dossiê do projeto é sagrado.** \`produto/visao.md\` precisa SEMPRE refletir o que é o projeto, a dor, a ideia, como resolve e pra quem (o /visao do Prototipador escreve nesse formato). Toda decisão nova de escopo vai pro arquivo na hora. Ele sincroniza sozinho com a equipe no fim do turno e é pré-requisito pra concluir missões de entregável — sem dossiê, o mark_task_complete recusa.
24
+ 6. **O dossiê do projeto é sagrado.** O \`product/product-overview.md\` do Prototipador (gerado pelo /visao dentro da pasta \`prototipador/\` deste workspace — fork do design-os) precisa SEMPRE refletir o que é o projeto, a dor, a ideia, como resolve e pra quem. Toda decisão nova de escopo vai pro arquivo na hora. Ele sincroniza sozinho com a equipe no fim do turno e é pré-requisito pra concluir missões de entregável — sem dossiê, o mark_task_complete recusa.
25
+ 7. **O Prototipador é a pasta \`prototipador/\` deste workspace.** No dia 2, trabalhe DENTRO dela: /visao → /roadmap → /dados → /estilo → /estrutura → /secao → /dados-exemplo → /tela, e /exportar pra fechar. Se a pasta não existir: \`git clone https://github.com/parisgroup-ai/prototipador\`.
25
26
 
26
27
  ## MCP tools que você tem
27
28
 
@@ -212,7 +212,7 @@ async function refreshMarkerStatus(markerDir, marker, status) {
212
212
  }
213
213
 
214
214
  // ---------------------------------------------------------------------------
215
- // Dossiê do projeto (produto/visao.md + roadmap.md do Prototipador) → Caso do
215
+ // Dossiê do projeto (product/product-overview.md + product-roadmap.md) → Caso do
216
216
  // aluno no servidor. DETERMINÍSTICO: roda em todo Stop/SessionStart e envia
217
217
  // quando o arquivo mudou — o mentor sempre vê o que é o projeto, a dor e o
218
218
  // plano, sem depender do modelo lembrar de anotar.
@@ -223,22 +223,37 @@ async function syncCaseDossier(apiUrl, token, markerDir) {
223
223
  let state = {}
224
224
  try { state = JSON.parse(await fs.readFile(statePath, 'utf-8')) } catch {}
225
225
 
226
- const files = {
227
- visao: path.join(markerDir, 'produto', 'visao.md'),
228
- roadmap: path.join(markerDir, 'produto', 'roadmap.md'),
226
+ // Prototipador virou fork literal do design-os (2026-08-17): a saída canônica
227
+ // é product/product-overview.md + product/product-roadmap.md — na raiz do
228
+ // workspace OU dentro do clone do template (<ws>/prototipador). Os caminhos
229
+ // legados (produto/visao.md) seguem como fallback pra quem começou antes.
230
+ const candidates = {
231
+ visao: [
232
+ path.join(markerDir, 'product', 'product-overview.md'),
233
+ path.join(markerDir, 'prototipador', 'product', 'product-overview.md'),
234
+ path.join(markerDir, 'produto', 'visao.md'),
235
+ ],
236
+ roadmap: [
237
+ path.join(markerDir, 'product', 'product-roadmap.md'),
238
+ path.join(markerDir, 'prototipador', 'product', 'product-roadmap.md'),
239
+ path.join(markerDir, 'produto', 'roadmap.md'),
240
+ ],
229
241
  }
230
242
  const payload = {}
231
243
  const newState = { ...state }
232
- for (const [key, fp] of Object.entries(files)) {
233
- try {
234
- const st = await fs.stat(fp)
235
- const mtime = st.mtimeMs
236
- if (state[key] !== mtime) {
237
- const content = await fs.readFile(fp, 'utf-8')
238
- payload[key] = content.slice(0, 16384)
239
- newState[key] = mtime
240
- }
241
- } catch { /* arquivo ainda não existe — normal antes do /visao */ }
244
+ for (const [key, paths] of Object.entries(candidates)) {
245
+ for (const fp of paths) {
246
+ try {
247
+ const st = await fs.stat(fp)
248
+ const mtime = st.mtimeMs
249
+ if (state[key] !== mtime) {
250
+ const content = await fs.readFile(fp, 'utf-8')
251
+ payload[key] = content.slice(0, 16384)
252
+ newState[key] = mtime
253
+ }
254
+ break // primeiro caminho existente vence
255
+ } catch { /* tenta o próximo candidato */ }
256
+ }
242
257
  }
243
258
  if (Object.keys(payload).length === 0) return
244
259
 
@@ -367,6 +382,9 @@ async function main() {
367
382
  lines.push('### Instrução da tarefa')
368
383
  lines.push(current.current.socio_prompt)
369
384
  }
385
+ lines.push('')
386
+ lines.push('### Timeline do aluno (OBRIGATÓRIO)')
387
+ lines.push('Você constrói a timeline da jornada dele. SEMPRE que fechar algo concreto — deploy no ar, PRD pronto, protótipo exportado, bug resolvido, decisão importante — registre com a tool MCP **log_event** (type: milestone, detail curto e específico, ex.: "PRD do app de agendamento fechado: 5 telas, 3 fluxos"). Se travar em algo, type: blocked; quando destravar, type: unblocked. Sem exagero: marcos de verdade, não cada comando.')
370
388
  } else {
371
389
  lines.push('## Sem tarefa em foco')
372
390
  lines.push('Pergunte ao aluno em que ele quer trabalhar.')
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "paris-immersion",
3
- "version": "0.1.33",
3
+ "version": "0.1.35",
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": {