Skip to content

Commit b924343

Browse files
elberrdclaude
andcommitted
fix: mata o painel Skill conflicts + avisos de update com marca imp
Skills: o Pi >=0.84 varre .agents/skills/ nativamente (mesmo gate de trust, interativo e headless), então a cópia em .pi/skills/ (perna `skills add -a pi`) duplicava cada skill oficial por realpath e abria toda sessão com ~40 colisões. installProjectSkills faz UMA invocação (-a claude-code cursor); backfillPiSkills vira prunePiSkillCopies (lock-driven, nunca toca .pi/skills/fia/), rodando no step FIA e no --update-runtime para migrar projetos antigos; o --verify inverte o check (cópia = warning de duplicata). Branding: imp lança o Pi com PI_SKIP_VERSION_CHECK=1 e imprime aviso próprio ("Run imp update") após a sessão — probe collectUpdateNotices em background, nada em saída piped. Os 3 pacotes Pi entram com pin exato (o check do Pi pula pacote pinado → banner "pi update --extensions" some) e imp update agora re-resolve e re-pina os 3 (antes nem os atualizava, o banner voltava sempre). isNewer entende prerelease (impactus vive em 2.0.0-alpha.N). Novos: CLAUDE.md (mapa do sistema) e lessons.md (log vivo de problemas recorrentes, semeado com as duas lições de hoje). Versão 2.0.0-alpha.4. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
1 parent 0d933d2 commit b924343

15 files changed

Lines changed: 450 additions & 261 deletions

CLAUDE.md

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
1+
# CLAUDE.md — impactus (the IAI installer)
2+
3+
## What this is
4+
5+
`impactus` is the IAI installer CLI, published on npm. Students run
6+
`npx impactus` (or the `imp` brand launcher) to stamp a project with the agent
7+
harness plus the FIA — the IAI Agent Factory: Pi as the interactive agent,
8+
deterministic FDAs (fully-automated dev agents), and subscription-based auth
9+
(Claude only through the official `claude` CLI, Codex through Pi — never API
10+
keys or extra usage).
11+
12+
- `bin/create-iai.js` — the installer entry (`npx impactus`, `imp init`).
13+
- `bin/imp.js` — the `imp` launcher: a thin brand wrapper over the real `pi`
14+
binary, **not a Pi fork**. `init`/`update`/`tui`/`help` are imp commands;
15+
everything else passes straight through to `pi` (stdout must stay
16+
byte-identical for piped runs).
17+
- `src/steps/` — the install pipeline, one step per file (preflight, template,
18+
clerk, convex, stack, fia, verify, update-runtime, finish…).
19+
- `src/lib/` — shared helpers (proc, ui, skills, pi-auth, args…).
20+
- `fia-templates/` — the `imp/` runtime stamped into projects (FDA scripts,
21+
sequencers, TUI, gates).
22+
- `pi-templates/` — the project `.pi/` (prompts, the `fia` skill + cookbooks,
23+
extensions).
24+
- `harness/`, `live1/`, `live2/` — the harness and the two live templates
25+
(separate repos checked out here; the harness is the single source of truth
26+
for skills/commands — templates never carry their own copies).
27+
- `test/``node:test` suite. `docs/` + `DOCS.md` — the deep documentation.
28+
29+
## Commands
30+
31+
```bash
32+
npm test # full suite (node test/run.mjs)
33+
npm run lint # eslint
34+
npm run sync:skills # regenerates .cursor/skills mirrors (never edit those by hand)
35+
```
36+
37+
## Hard rules
38+
39+
- Node floor is **>= 22.12** everywhere (repo, templates, generated projects).
40+
- Everything that ships (CLI output, templates, prompts, docs) is **English**.
41+
Commit messages are Portuguese, matching the history.
42+
- Nothing about optional extras (skills, Impeccable, addons) may ever abort an
43+
install — degrade with a warning and print the manual command.
44+
- Skills topology: the canonical copy lives in `.agents/skills/<name>/`;
45+
Claude Code symlinks it, Cursor and Pi read it directly. Never reintroduce
46+
copies in `.pi/skills/` (Pi dedupes by realpath — a copy means a "Skill
47+
conflicts" panel at every launch).
48+
- A new student-facing command must be registered everywhere it is listed
49+
(prompt file, `fia` SKILL.md routing, `finish.js` panels, README, DOCS).
50+
- Run `npm test` and `npm run lint` before committing. Commit/push only when
51+
the user asks.
52+
53+
## Lessons
54+
55+
`lessons.md` at the repo root is the living log of recurring problems and
56+
their fixes. **Check it before debugging a symptom that feels familiar**, and
57+
append a new entry (symptom → cause → fix → guard) whenever a notable bug is
58+
identified and corrected.

DOCS.md

Lines changed: 19 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -358,28 +358,27 @@ On disk, per engine:
358358
| ------ | ---- | ----------------- |
359359
| Cursor | `.agents/skills/<name>/SKILL.md` | the canonical store, read directly ("universal") |
360360
| Claude Code | `.claude/skills/<name>` | symlink to the store |
361-
| Pi (FIA agents) | `.pi/skills/<name>` | a copy — Pi does not follow the symlink |
361+
| Pi (FIA agents) | `.agents/skills/<name>` | native `.agents/` discovery — no copy of its own |
362362

363363
`skillsAddArgs(spec, agents)` builds the argv and `installProjectSkills` runs it
364-
**twice**: `-a claude-code cursor`, then `-a pi`. Measured against
365-
skills@1.5.22 (Aug 2026), that is the only combination that covers the three
366-
engines: `-a claude-code` alone never creates `.agents/skills/` (Cursor gets
367-
nothing); `-a claude-code cursor pi` installs the first two and silently drops
368-
pi; the comma form `-a a,b,c` is rejected and installs nothing. The `-a` stays
369-
last in the argv so the variadic cannot swallow the `-y`. The second run is
370-
best-effort and only happens when the project has a `.pi/` — a `--no-fia`
371-
install has none, and a failure there warns without failing the step (as ever,
372-
nothing about skills aborts an install; the manual command for **both** groups
373-
is printed).
374-
375-
Ordering matters here: every step that installs skills (storage, integrations,
376-
stack docs) runs BEFORE the FIA step that stamps `.pi/`, so on a fresh install
377-
that second run would find no `.pi/` and skip. That is what
378-
`backfillPiSkills(dir)` closes — called by `src/steps/fia.js` right after the
379-
stamp, it reads `skills-lock.json`, groups the entries by `source` and replays
380-
one `skills add … -a pi` per source. Best-effort as well: a failed source warns
381-
with its manual command, and an install re-run in an existing project (where
382-
`.pi/` is already there) simply does the copy twice, harmlessly.
364+
**once**, with `-a claude-code cursor`. Measured against skills@1.5.22
365+
(Aug 2026): `-a claude-code` alone never creates `.agents/skills/` (Cursor gets
366+
nothing), and the comma form `-a a,b` is rejected and installs nothing. The
367+
`-a` stays last in the argv so the variadic cannot swallow the `-y`. A failure
368+
warns without failing the step (as ever, nothing about skills aborts an
369+
install; the manual command is printed).
370+
371+
Pi needs no leg of its own: it scans the project's `.agents/skills/` natively
372+
(behind the same project-trust gate as `.pi/skills/`, in interactive and
373+
headless runs alike), so one canonical copy serves the three engines. Older
374+
CLI versions DID run a second `skills add … -a pi` per source — and since Pi
375+
dedupes discovered skills by realpath, that real copy made every skill load
376+
twice and opened each Pi session with a "Skill conflicts" panel listing all of
377+
them. `prunePiSkillCopies(dir)` cleans those leftovers: lock-driven (so
378+
harness-owned skills like `.pi/skills/fia/` are never touched) and
379+
conservative (a copy is only removed when its `.agents/skills/` canonical
380+
exists). It runs in the FIA step and in `--update-runtime`, which is how
381+
projects stamped by the old flow heal on their next update.
383382

384383
The install is recorded in `skills-lock.json` (v1: `{version, skills: {<name>:
385384
{source, sourceType, skillPath, computedHash}}}`), which the project commits —

README.md

Lines changed: 9 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ Academy launcher):
1818
npm i -g impactus
1919
imp init # installs the harness/FIA in the current folder (same as npx impactus)
2020
imp # starts Pi here — IMPACTUS banner, installs Pi if it's missing
21-
imp update # updates impactus + Pi
21+
imp update # updates impactus + Pi + the Pi extension packages
2222
```
2323

2424
`imp` is a thin brand wrapper, **not a Pi fork**: anything that isn't
@@ -826,16 +826,14 @@ each engine reads them from:
826826
| ------ | ---- | ----------------- |
827827
| Cursor | `.agents/skills/<name>/SKILL.md` | the canonical copy — Cursor reads this folder directly |
828828
| Claude Code | `.claude/skills/<name>` | symlink to the canonical copy |
829-
| Pi (FIA agents) | `.pi/skills/<name>` | a **copy** — Pi does not follow the symlink |
830-
831-
The installer therefore runs `skills add` **twice**: once for
832-
`-a claude-code cursor`, then once for `-a pi`. Passing the three at once makes
833-
the CLI silently skip Pi, and the comma form (`-a a,b,c`) installs nothing at
834-
all. The Pi run needs a `.pi/` folder, which only exists once the FIA is
835-
stamped — so on a fresh install the FIA step replays the sources from
836-
`skills-lock.json` into `.pi/skills/` right after stamping. Without the FIA
837-
(`--no-fia`) there is no Pi copy, and nothing about skills ever fails an
838-
install.
829+
| Pi (FIA agents) | `.agents/skills/<name>` | Pi discovers the `.agents/` store natively — no copy |
830+
831+
The installer runs `skills add` once, with `-a claude-code cursor` (the comma
832+
form `-a a,b` installs nothing at all). One canonical copy serves the three
833+
engines — a separate `.pi/skills/` copy would make Pi load every skill twice
834+
and open each session with a "Skill conflicts" panel, which is exactly what
835+
older installer versions did; updated projects clean those leftovers
836+
automatically. Nothing about skills ever fails an install.
839837

840838
In the project, day to day:
841839

bin/imp.js

Lines changed: 38 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
#!/usr/bin/env node
22
// imp — the IMPACTUS Academy launcher. A thin brand wrapper, NOT a Pi fork:
33
// imp init … → runs the impactus installer (bin/create-iai.js) in-place
4-
// imp update → updates impactus + Pi
4+
// imp update → updates impactus + Pi + the Pi extension packages
55
// imp [args] → hands everything else to the real `pi` binary (stdio
66
// inherited), installing Pi first if it is missing.
77
// Keeping Pi as the actual agent means `pi update`, Codex login and the
8-
// project-level .pi/ config all keep working unchanged.
8+
// project-level .pi/ config all keep working unchanged. The only Pi behavior
9+
// imp touches is the startup update notice: it is pi-branded ("Run pi
10+
// update"), so imp suppresses it (PI_SKIP_VERSION_CHECK) and prints its own
11+
// imp-branded one after the session ends — see collectUpdateNotices.
912

1013
// Node version gate — the `engines` field doesn't block `npx` execution, so
1114
// enforce it here with a clear message (before importing anything modern).
@@ -56,7 +59,7 @@ Usage:
5659
imp Start Pi in the current folder (installs Pi if missing)
5760
imp init [options] Install the harness/FIA here (same as npx impactus;
5861
all impactus flags work — see \`imp init --help\`)
59-
imp update Update impactus and Pi to the latest versions
62+
imp update Update impactus, Pi and the Pi extension packages
6063
imp tui Terminal dashboard — tasks, specs and runs (same as npm run tui)
6164
imp help Show this help
6265
imp --version Print the impactus version
@@ -95,7 +98,7 @@ if (cmd === 'init') {
9598
if (cmd === 'update') {
9699
banner();
97100
const { runInherit } = await import('../src/lib/proc.js');
98-
const { hasPi, ensurePiReady } = await import('../src/lib/pi-auth.js');
101+
const { hasPi, ensurePiReady, installPiPackages } = await import('../src/lib/pi-auth.js');
99102

100103
console.log(`Updating impactus (npm install -g ${pkg.name}@latest)…`);
101104
const up = await runInherit('npm', ['install', '-g', `${pkg.name}@latest`]);
@@ -115,6 +118,17 @@ if (cmd === 'update') {
115118
console.error(err?.message || String(err));
116119
process.exit(1);
117120
}
121+
122+
// The Pi extension packages are exact-pinned (that pin is what keeps Pi's
123+
// pi-branded "Package Updates Available" panel away) — move the pins here.
124+
// Best-effort: an offline refresh must not fail the whole update.
125+
try {
126+
console.log('Updating the Pi extension packages (subagents + MCP + web access)…');
127+
await installPiPackages(process.cwd());
128+
} catch (err) {
129+
console.error(`Could not refresh the Pi extension packages: ${err?.message || String(err)}`);
130+
console.error('Run `imp update` again when back online.');
131+
}
118132
console.log('Done.');
119133
process.exit(up.ok ? 0 : 1);
120134
}
@@ -139,9 +153,9 @@ if (cmd === 'tui') {
139153
// must arrive exactly as `pi` would produce it.
140154
if (process.stdout.isTTY) banner();
141155
const { runInherit } = await import('../src/lib/proc.js');
142-
const { hasPi, ensurePiReady } = await import('../src/lib/pi-auth.js');
156+
const { hasPi, ensurePiReady, collectUpdateNotices } = await import('../src/lib/pi-auth.js');
143157

144-
// Only install when missing — no network version check on every launch
158+
// Only install when missing — the launch itself never blocks on the network
145159
// (`imp update` and the installer already keep Pi fresh).
146160
if (!(await hasPi())) {
147161
try {
@@ -153,6 +167,23 @@ if (!(await hasPi())) {
153167
console.log(pc.dim('Tip: inside Pi, type /login openai-codex to connect your ChatGPT subscription.'));
154168
}
155169

170+
// The update probe runs in the background WHILE the session is open and is
171+
// only printed after Pi exits — zero launch latency, and nothing for piped
172+
// runs (`imp -p … > file` must carry exactly Pi's output).
173+
const notices = process.stdout.isTTY ? collectUpdateNotices(pkg.version).catch(() => []) : null;
174+
156175
const args = cmd === undefined ? [] : [cmd, ...rest];
157-
const r = await runInherit('pi', args);
176+
const r = await runInherit('pi', args, { env: { PI_SKIP_VERSION_CHECK: '1' } });
177+
178+
if (notices) {
179+
// Race, don't wait: if the probe is still mid-flight after a short
180+
// one-shot session, skip it rather than hold the student's terminal.
181+
const lines = await Promise.race([notices, new Promise((res) => setTimeout(res, 400, []))]);
182+
if (lines.length > 0) {
183+
console.log('');
184+
console.log(pc.bold(pc.yellow('Updates available')));
185+
for (const line of lines) console.log(pc.yellow(` - ${line}`));
186+
console.log(pc.yellow('Run `imp update` to bring everything current.'));
187+
}
188+
}
158189
process.exit(r.exitCode);

lessons.md

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Lessons — problemas identificados e como foram corrigidos
2+
3+
Log vivo dos problemas que aparecem com frequência neste sistema. Formato
4+
simples: um bloco por lição, o mais novo no topo. Antes de investigar um bug
5+
"novo", confira se ele já está aqui; depois de corrigir um bug notável,
6+
registre a lição.
7+
8+
---
9+
10+
## 2026-08-13 — Painel "Skill conflicts" no primeiro `imp` do projeto
11+
12+
- **Sintoma:** logo após o install, `imp` abre com ~40 colisões de skills
13+
(`"convex" collision: … skipped`), uma por skill oficial instalada.
14+
- **Causa:** o instalador copiava cada skill para `.pi/skills/` (segunda
15+
invocação `skills add … -a pi`). O Pi ≥0.84 já varre `.agents/skills/`
16+
nativamente e dedupa por realpath — a cópia real fazia cada skill ser
17+
descoberta duas vezes, e cada duplicata vira uma linha do painel.
18+
- **Correção:** perna `-a pi` removida (`src/lib/skills.js` — uma invocação
19+
só, `-a claude-code cursor`); `backfillPiSkills` substituído por
20+
`prunePiSkillCopies` (lock-driven, roda no step FIA e no
21+
`--update-runtime`, migra projetos antigos); `--verify` agora acusa cópia
22+
duplicada em vez de exigi-la.
23+
- **Guarda:** `test/skills.test.js` (prune) e `test/verify.test.js`
24+
(warning de duplicata). Colisão residual = skill global do usuário em
25+
`~/.pi/agent/skills/` ou `~/.agents/skills/` — informacional, projeto ganha.
26+
27+
## 2026-08-13 — Avisos de update com a marca errada ("Run pi update")
28+
29+
- **Sintoma:** o Pi mostra "Update Available — Run **pi** update" e "Package
30+
Updates Available — Run **pi** update --extensions"; o aluno só conhece
31+
`imp`. Pior: `imp update` não atualizava os pacotes de extensão, então o
32+
segundo banner voltava sempre.
33+
- **Causa:** os textos usam `APP_NAME` lido do package.json do próprio pacote
34+
Pi (`piConfig.name`) — rebrandear exigiria fork. E `imp update` rodava só
35+
`npm i -g impactus` + `pi update`, nunca `pi update --extensions`.
36+
- **Correção (sem fork, wrapper continua fino):**
37+
1. `imp` lança o Pi com `PI_SKIP_VERSION_CHECK=1` e imprime aviso próprio
38+
("Run `imp update`") DEPOIS da sessão — probe em `collectUpdateNotices`
39+
(`src/lib/pi-auth.js`), roda em background, nunca em saída piped.
40+
2. Os 3 pacotes Pi (pi-subagents, pi-mcp-adapter, pi-web-access) são
41+
instalados com **pin exato** (`npm:<pkg>@x.y.z`) — o check de update do
42+
Pi pula pacote pinado, então o banner de pacotes não existe mais.
43+
3. `imp update` agora re-resolve e re-pina os 3 (o `pi install` substitui a
44+
entry quando o nome bate).
45+
- **Guarda:** `test/pi-auth.test.js` (isNewer com prerelease) e
46+
`test/imp-smoke.test.js` (stdout piped continua idêntico ao do `pi`).
47+
Nunca editar o package.json global do Pi — `pi update` sobrescreve.

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "impactus",
3-
"version": "2.0.0-alpha.3",
3+
"version": "2.0.0-alpha.4",
44
"description": "impactus — the IAI installer: agent harness + FIA (the IAI Agent Factory) with Pi, deterministic FDAs and Claude/Codex subscription auth.",
55
"type": "module",
66
"bin": {

0 commit comments

Comments
 (0)