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.
- package/dist/commands/install.js +33 -0
- package/dist/templates/claude-md.js +2 -1
- package/dist/templates/hook.js +32 -14
- package/package.json +1 -1
package/dist/commands/install.js
CHANGED
|
@@ -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.** \`
|
|
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
|
|
package/dist/templates/hook.js
CHANGED
|
@@ -212,7 +212,7 @@ async function refreshMarkerStatus(markerDir, marker, status) {
|
|
|
212
212
|
}
|
|
213
213
|
|
|
214
214
|
// ---------------------------------------------------------------------------
|
|
215
|
-
// Dossiê do projeto (
|
|
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
|
-
|
|
227
|
-
|
|
228
|
-
|
|
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,
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
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