codetac 0.1.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 deltaXmodules
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/package.json ADDED
@@ -0,0 +1,60 @@
1
+ {
2
+ "name": "codetac",
3
+ "version": "0.1.0",
4
+ "description": "codeTAC — perceber o que o código da sua app Node faz, ação a ação: do clique no browser às funções do servidor, base de dados e serviços externos.",
5
+ "keywords": [
6
+ "debug",
7
+ "trace",
8
+ "understand",
9
+ "nodejs",
10
+ "nextjs",
11
+ "vite",
12
+ "express",
13
+ "ai-generated",
14
+ "vibe-coding",
15
+ "dossier"
16
+ ],
17
+ "license": "MIT",
18
+ "author": "deltaXmodules",
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/deltaxmodules/codetacvibe.git"
22
+ },
23
+ "bugs": {
24
+ "url": "https://github.com/deltaxmodules/codetacvibe/issues"
25
+ },
26
+ "homepage": "https://github.com/deltaxmodules/codetacvibe#readme",
27
+ "type": "module",
28
+ "bin": {
29
+ "codetac": "src/cli.mjs"
30
+ },
31
+ "files": [
32
+ "src/"
33
+ ],
34
+ "engines": {
35
+ "node": ">=24.0.0"
36
+ },
37
+ "scripts": {
38
+ "test": "node --test test/*.test.mjs",
39
+ "test:stacks": "node scripts/validate-stacks.mjs",
40
+ "check": "node --check src/register.mjs && node --check src/transform.mjs && node --check src/runtime.mjs && node --check src/boundaries.mjs && node --check src/store.mjs && node --check src/panel.mjs && node --check src/page.mjs && node --check src/origins.mjs && node --check src/action-view.mjs && node --check src/browser/bar.js && node --check src/digest.mjs && node --check src/sentences.mjs && node --check src/ai.mjs && node --check src/cli.mjs && node --check src/detect.mjs && node --check src/recording.mjs && node --check src/diagnose.mjs",
41
+ "test:performance": "node scripts/benchmark-functions.mjs",
42
+ "panel": "node src/panel.mjs",
43
+ "test:browser": "node scripts/accept-browser.mjs docs/aceitacao-browser-exemplo.json",
44
+ "codetac": "node src/cli.mjs"
45
+ },
46
+ "dependencies": {
47
+ "@anthropic-ai/sdk": "^0.128.0",
48
+ "@jridgewell/trace-mapping": "^0.3.31",
49
+ "acorn": "^8.18.0",
50
+ "acorn-walk": "^8.3.5",
51
+ "magic-string": "^1.4.1"
52
+ },
53
+ "devDependencies": {
54
+ "express": "^5.2.1",
55
+ "next": "^16.3.6",
56
+ "react": "^19.3.0",
57
+ "react-dom": "^19.3.0",
58
+ "vite": "^8.3.0"
59
+ }
60
+ }
package/readme.md ADDED
@@ -0,0 +1,172 @@
1
+ # codeTAC
2
+
3
+ **Perceber o que o código da sua app faz, ação a ação.**
4
+
5
+ *See what your app's code does, one click at a time: a local tool for Node.js apps, including those generated by AI tools (Lovable, Bolt, v0, Cursor, Replit, Claude Code…). Explanations are in Portuguese by default (`CODETAC_LANG=en` for English).*
6
+
7
+ Clica num botão da sua app e o codeTAC mostra um **dossiê** dessa ação:
8
+ - **no browser:** o que foi clicado, o componente e onde está no código;
9
+ - **no servidor:** as funções do seu projeto que correram, pela ordem, com o ficheiro e a linha;
10
+ - **as fronteiras:** base de dados, serviços externos, email, pagamentos, ficheiros, IA;
11
+ - **o que mudou no ecrã;**
12
+ - **os efeitos permanentes:** linhas gravadas, emails enviados, cookies…;
13
+ - uma frase simples em cada passo a dizer para que serve.
14
+
15
+ Tudo corre no seu computador. O código da app não é alterado.
16
+
17
+ ---
18
+
19
+ ## 1. Antes de começar
20
+
21
+ Precisa de três coisas:
22
+
23
+ 1. **Node.js 24 ou mais recente.** Para verificar, abra o Terminal e escreva `node -v`. Tem de aparecer `v24` ou um número maior. Se não aparecer, instale a versão LTS em [nodejs.org](https://nodejs.org).
24
+ 2. **O projeto da sua app no seu computador.** Se a app foi feita no Lovable, Bolt, v0 ou Replit, ligue-a ao GitHub e descarregue-a:
25
+ - no GitHub, **Code → Download ZIP**, e descompacte o ficheiro;
26
+ - ou `git clone <endereço>`, se usa o Git.
27
+ 3. **Um browser:** Chrome, Edge, Firefox ou Safari.
28
+
29
+ **Como abrir o Terminal:**
30
+ - **macOS:** Cmd+Espaço, escreva «Terminal», Enter;
31
+ - **Windows:** menu Iniciar, escreva «PowerShell», Enter. No Windows, o codeTAC ainda não foi ensaiado.
32
+
33
+ ## 2. Instalar
34
+
35
+ No Terminal:
36
+
37
+ ```sh
38
+ npm install -g codetac
39
+ ```
40
+
41
+ Confirme com `codetac --version`, que mostra o número da versão.
42
+
43
+ Se aparecer um erro de permissões (`EACCES`), use `npx codetac` em vez de `codetac` nos passos abaixo. Funciona sem instalar.
44
+
45
+ ## 3. O primeiro dossiê
46
+
47
+ 1. No Terminal, vá para a pasta do projeto: escreva `cd ` (com um espaço), arraste a pasta do projeto para a janela do Terminal e carregue em Enter.
48
+ 2. Escreva:
49
+
50
+ ```sh
51
+ codetac
52
+ ```
53
+
54
+ 3. O codeTAC diz o que vai arrancar, por exemplo `Arranque: Vite · npm run dev`.
55
+ - Se faltarem as dependências da app, pergunta se as instala. Carregue em **Enter**, que quer dizer sim.
56
+ - Se o projeto tiver várias partes, pergunta quais arranca. **Enter** arranca todas.
57
+ 4. Quando aparecer **«✓ App pronta em http://localhost:…»**, o browser abre com a sua app.
58
+ 5. **Use a app:** clique num botão, numa ligação, envie um formulário.
59
+ - No canto inferior direito da página aparece uma pequena barra com «Gravada: …».
60
+ - No Terminal aparece «✓ Primeiro dossiê …» com um link.
61
+ 6. Carregue na barra, ou abra o link, para ver o dossiê.
62
+ 7. Para terminar, volte ao Terminal e carregue em **Ctrl+C**.
63
+
64
+ Numa app que já conhece, o primeiro dossiê chega normalmente em menos de um minuto.
65
+
66
+ ## 4. Ler o dossiê
67
+
68
+ - **A frase do topo** resume a ação, por exemplo: «Clique em botão «Add» → POST /api/items (estado 201) → altera o ecrã».
69
+ - **browser → servidor:** cada pedido que a ação fez ao servidor. Por baixo estão as funções do seu projeto que correram.
70
+ - **Etiquetas coloridas** («base de dados», «HTTP», «email»…): os pontos em que a app fala com o exterior.
71
+ - **▸** abre um grupo, por exemplo repetições ou preparação da base de dados. **Ver tudo** mostra a sequência completa.
72
+ - **Clique numa função** para ver o código dela e fazer uma pergunta sobre esse passo.
73
+ - **pedir detalhe:** a partir da ação seguinte, essa função grava também os valores que recebeu e devolveu, e as linhas que correram.
74
+ - **Efeitos permanentes:** o que ficou depois da ação (linhas gravadas, emails, cookies). «Nenhum efeito permanente observado» quer dizer que a ação só consultou.
75
+ - **Modo mínimo** (faixa amarela): o codeTAC não conseguiu seguir as funções do projeto e mostra só os pedidos, as fronteiras e o browser. A faixa diz porquê.
76
+
77
+ ## 5. Explicações com IA (opcional)
78
+
79
+ Sem configuração:
80
+ - se o [Ollama](https://ollama.com) estiver a correr neste computador, o codeTAC usa-o, e nada sai da máquina;
81
+ - se não estiver, as frases são geradas só a partir do que foi gravado.
82
+
83
+ Para usar a Anthropic ou a OpenAI, crie o ficheiro `~/.codetac/ia.json`:
84
+
85
+ ```json
86
+ { "provider": "anthropic", "key": "a-sua-chave" }
87
+ ```
88
+
89
+ Para a OpenAI: `{ "provider": "openai", "model": "gpt-5.4-mini", "key": "…" }`.
90
+
91
+ Com um modelo na nuvem, são enviados excertos do código das funções, depois de retirados os segredos. Os valores gravados nunca são enviados. O painel indica o que está a ser usado.
92
+
93
+ - **«IA»** ao lado de uma frase: a frase veio do modelo e bate certo com o que foi gravado.
94
+ - **«IA rejeitada»:** a frase do modelo dizia algo que não foi observado. Foi trocada pela frase feita a partir dos factos. Passe o rato por cima para ver o motivo.
95
+
96
+ ## 6. Quando algo não funciona
97
+
98
+ Com a app a correr, noutra janela do Terminal e na mesma pasta:
99
+
100
+ ```sh
101
+ codetac diagnostico
102
+ ```
103
+
104
+ Cada linha começa por **✓** (funciona), **!** (atenção) ou **✗** (problema), com o que fazer.
105
+
106
+ | Mensagem | O que fazer |
107
+ | --- | --- |
108
+ | «Não encontrei um package.json…» | Está na pasta errada. Vá para a pasta que tem o ficheiro `package.json`, ou indique o arranque: `codetac -- node server.js` |
109
+ | «A porta 3000 já está ocupada por outro programa…» | Feche o outro programa, por exemplo outra app aberta noutra janela do Terminal. Numa app com uma só parte, o codeTAC muda de porta sozinho |
110
+ | «A app falhou também em modo mínimo» | O problema é da própria app. Muitas vezes faltam as chaves no ficheiro `.env` (Supabase, Firebase…). Veja o `.env.example` ou as instruções do projeto |
111
+ | «A página inicial respondeu com erro 500» | A app arrancou, mas falha. O dossiê desse carregamento mostra onde |
112
+ | A barra não aparece na página | Recarregue a página. Veja se o endereço é o que o codeTAC indicou |
113
+
114
+ **Comunicar um problema:**
115
+
116
+ ```sh
117
+ codetac relatorio
118
+ ```
119
+
120
+ Guarda o diagnóstico num ficheiro, sem código nem dados da app. Envie-o com uma descrição do que fez e do que esperava em [github.com/deltaxmodules/codetacvibe/issues](https://github.com/deltaxmodules/codetacvibe/issues).
121
+
122
+ ## 7. Privacidade
123
+
124
+ - Tudo fica no seu computador, em `~/.codetac` (pode ser outra pasta, com `CODETAC_HOME`).
125
+ - Antes de gravar, são retirados os segredos: passwords, tokens, chaves, emails e telefones.
126
+ - Por omissão não se gravam valores, só nomes, ficheiros e linhas. Os valores só são gravados nas funções onde pedir detalhe.
127
+ - O painel só responde neste computador (`127.0.0.1`).
128
+
129
+ ## 8. Comandos
130
+
131
+ | Comando | O que faz |
132
+ | --- | --- |
133
+ | `codetac [pasta]` | Arranca a app com o codeTAC |
134
+ | `codetac diagnostico [pasta]` | Explica o que funciona e o que não funciona |
135
+ | `codetac relatorio [pasta]` | Guarda o diagnóstico para enviar com uma incidência |
136
+ | `codetac ajuda` | Todas as opções |
137
+
138
+ **Opções:**
139
+
140
+ | Opção | Para quê |
141
+ | --- | --- |
142
+ | `--script <nome>` | usar outro script do `package.json` |
143
+ | `--porta <n>` | a porta da app, se não for descoberta sozinha |
144
+ | `--painel <n>` | a porta do painel (por omissão 4000) |
145
+ | `--minimo` | não seguir as funções do projeto |
146
+ | `--sim` | responder «sim» às perguntas |
147
+ | `--nao-abrir` | não abrir o browser |
148
+ | `-- <comando>` | o comando de arranque, por exemplo `codetac -- node server.js` |
149
+
150
+ ## 9. Limites
151
+
152
+ - Só apps **Node.js** (JavaScript e TypeScript) a correr **no seu computador**, em desenvolvimento.
153
+ - Não serve para apps em produção nem só na nuvem.
154
+ - Partes da app que não correm no Node não são observadas por dentro: Bun, Deno, Cloudflare Workers, o middleware do Next.js. O comando avisa quando as reconhece.
155
+ - No browser, mostra-se o elemento, o componente e o caminho até cada pedido, mas não cada função.
156
+ - Windows: ainda não ensaiado.
157
+
158
+ ## Licença
159
+
160
+ [MIT](LICENSE).
161
+
162
+ ## Desenvolvimento
163
+
164
+ ```sh
165
+ git clone https://github.com/deltaxmodules/codetacvibe.git
166
+ cd codetacvibe
167
+ npm ci
168
+ npm test
169
+ node src/cli.mjs ajuda
170
+ ```
171
+
172
+ Num checkout Git, as gravações ficam em `.codetac/`, dentro do repositório.
@@ -0,0 +1,104 @@
1
+ // Action dossier as shown to a person: the store's dossier with the browser
2
+ // positions resolved to project files (through the application's source maps).
3
+ import { sep } from 'node:path';
4
+ import { createResolver } from './origins.mjs';
5
+
6
+ export function createActionView(store, resolver = createResolver()) {
7
+ const browserFiles = new Map();
8
+ // Browser files that may be shown: project files found by resolving the
9
+ // positions of a recorded action of that run.
10
+ const LIBRARIES_OF_REACT = new Set(['react', 'react-dom', 'scheduler', 'react-server-dom-webpack', 'react-server-dom-turbopack']);
11
+
12
+ function shortFile(root, file) {
13
+ return root && file && file.startsWith(root + sep) ? file.slice(root.length + 1) : file;
14
+ }
15
+ function allowBrowserFile(run, file) {
16
+ if (!browserFiles.has(run)) browserFiles.set(run, new Set());
17
+ browserFiles.get(run).add(file);
18
+ }
19
+ // React itself, also when a framework ships its own copy (next/dist/compiled/react).
20
+ function reactInternal(frame) {
21
+ return LIBRARIES_OF_REACT.has(frame.library) || /\/compiled\/(react|react-dom|react-server-dom-[\w-]+|scheduler)\//.test(frame.file ?? '')
22
+ || /^(exports\.)?(jsxDEV|jsxs?|createElement|fakeJSXCallSite)$/.test(frame.fn ?? '');
23
+ }
24
+ // Where the element was written: the first frame outside React itself.
25
+ function creation(frames, root, run) {
26
+ const frame = frames.find(item => item.resolved && !reactInternal(item));
27
+ if (!frame) return null;
28
+ if (frame.project) {
29
+ allowBrowserFile(run, frame.file);
30
+ return { project: true, fn: frame.fn ?? null, file: frame.file, short: shortFile(root, frame.file), line: frame.line };
31
+ }
32
+ return { project: false, library: frame.library ?? null };
33
+ }
34
+ // The project functions on the way to a request, outermost first.
35
+ function chain(frames, root, run) {
36
+ const result = [];
37
+ for (const frame of [...frames].reverse()) {
38
+ if (!frame.project) continue;
39
+ const previous = result.at(-1);
40
+ if (previous && previous.file === frame.file && previous.line === frame.line) continue;
41
+ allowBrowserFile(run, frame.file);
42
+ result.push({ fn: frame.fn ?? '(anónima)', file: frame.file, short: shortFile(root, frame.file), line: frame.line, resolved: frame.resolved });
43
+ }
44
+ const libraries = [...new Set(frames.filter(frame => frame.library && !reactInternal(frame)).map(frame => frame.library))];
45
+ return { chain: result, libraries, unresolved: frames.length > 0 && frames.every(frame => !frame.resolved) };
46
+ }
47
+
48
+ async function resolvedAction(id) {
49
+ store.ingest();
50
+ const dossier = store.actionDossier(id);
51
+ if (!dossier) return null;
52
+ const context = { origin: dossier.origin, root: dossier.root, scripts: dossier.scripts };
53
+ const resolve = async frames => {
54
+ if (!dossier.root) return [];
55
+ return Promise.all((frames ?? []).map(async frame => {
56
+ // Script URLs carry the port of that run, so the key is unique to it.
57
+ const key = JSON.stringify([dossier.root, frame.url ?? frame.file, frame.line, frame.column]);
58
+ const cached = store.cachedOrigin?.(key);
59
+ if (cached) return { ...cached, fn: frame.fn || cached.fn };
60
+ if (!dossier.origin && !frame.file) return { ...frame, resolved: false };
61
+ const result = await resolver.resolveFrame(frame, context);
62
+ if (result.resolved) store.cacheOrigin?.(key, result);
63
+ return result;
64
+ }));
65
+ };
66
+ const run = dossier.run;
67
+ for (const item of dossier.timeline) {
68
+ if (item.type === 'trigger' && item.trigger.component) {
69
+ const component = item.trigger.component;
70
+ component.origin = creation(await resolve(component.frames), dossier.root, run);
71
+ delete component.frames;
72
+ // When the element comes from a library component (a router link, a
73
+ // UI kit button), the first owner written in the project is named too.
74
+ for (const owner of component.owners ?? []) {
75
+ owner.origin = creation(await resolve(owner.frames), dossier.root, run);
76
+ delete owner.frames;
77
+ }
78
+ if (!component.origin?.project) {
79
+ const index = (component.owners ?? []).findIndex(owner => owner.origin?.project);
80
+ // owners[index] was written in the project, inside owners[index + 1].
81
+ if (index >= 0) component.project = { name: component.owners[index + 1]?.name ?? null, via: component.owners[index].name,
82
+ origin: component.owners[index].origin };
83
+ }
84
+ }
85
+ if (item.type === 'request') {
86
+ Object.assign(item.browser, chain(await resolve(item.browser.frames), dossier.root, run));
87
+ delete item.browser.frames;
88
+ }
89
+ if (item.type === 'screen') {
90
+ for (const key of ['stateChanged', 'mounted', 'unmounted']) {
91
+ for (const component of item.screen[key] ?? []) {
92
+ component.origin = creation(await resolve(component.frames), dossier.root, run);
93
+ delete component.frames;
94
+ }
95
+ }
96
+ }
97
+ }
98
+ delete dossier.scripts;
99
+ return dossier;
100
+ }
101
+
102
+
103
+ return { resolvedAction, browserFile: (run, file) => Boolean(browserFiles.get(run)?.has(file)) };
104
+ }