ai-execution-protocol 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.
Files changed (39) hide show
  1. package/AGENTS.md +90 -0
  2. package/LICENSE +21 -0
  3. package/README.md +159 -0
  4. package/dist/minimal/.aiignore +8 -0
  5. package/dist/minimal/AGENTS.md +61 -0
  6. package/dist/minimal/README.md +60 -0
  7. package/dist/minimal/protocol/README.yaml +35 -0
  8. package/dist/minimal/protocol/context-compiler.yaml +110 -0
  9. package/dist/minimal/protocol/context-rules.yaml +71 -0
  10. package/dist/minimal/protocol/execution-rules.yaml +56 -0
  11. package/dist/minimal/protocol/fast-path.yaml +33 -0
  12. package/dist/minimal/protocol/formatting-rules.yaml +75 -0
  13. package/dist/minimal/protocol/mapping-checklists.yaml +42 -0
  14. package/dist/minimal/protocol/modes.yaml +42 -0
  15. package/dist/minimal/protocol/prompt-economy.yaml +96 -0
  16. package/dist/minimal/protocol/risk-levels.yaml +87 -0
  17. package/dist/minimal/protocol/router.yaml +100 -0
  18. package/dist/minimal/protocol/spec-driven.yaml +53 -0
  19. package/dist/minimal/protocol/validation-checklist.yaml +59 -0
  20. package/install.ps1 +16 -0
  21. package/package.json +46 -0
  22. package/protocol/README.yaml +35 -0
  23. package/protocol/context-compiler.yaml +110 -0
  24. package/protocol/context-rules.yaml +71 -0
  25. package/protocol/execution-rules.yaml +56 -0
  26. package/protocol/fast-path.yaml +33 -0
  27. package/protocol/formatting-rules.yaml +75 -0
  28. package/protocol/mapping-checklists.yaml +42 -0
  29. package/protocol/modes.yaml +42 -0
  30. package/protocol/prompt-economy.yaml +96 -0
  31. package/protocol/risk-levels.yaml +87 -0
  32. package/protocol/router.yaml +100 -0
  33. package/protocol/spec-driven.yaml +53 -0
  34. package/protocol/validation-checklist.yaml +59 -0
  35. package/scripts/README.md +222 -0
  36. package/scripts/build_dist.py +191 -0
  37. package/scripts/install_protocol.py +28 -0
  38. package/scripts/npm_install_protocol.js +278 -0
  39. package/scripts/verify_install.py +92 -0
@@ -0,0 +1,278 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const root = path.resolve(__dirname, "..");
7
+ const markerStart = "<!-- AI_PROTOCOL_BEGIN -->";
8
+ const markerEnd = "<!-- AI_PROTOCOL_END -->";
9
+ const protocolFiles = [
10
+ "README.yaml",
11
+ "fast-path.yaml",
12
+ "router.yaml",
13
+ "modes.yaml",
14
+ "execution-rules.yaml",
15
+ "risk-levels.yaml",
16
+ "mapping-checklists.yaml",
17
+ "validation-checklist.yaml",
18
+ "context-rules.yaml",
19
+ "context-compiler.yaml",
20
+ "formatting-rules.yaml",
21
+ "prompt-economy.yaml",
22
+ "spec-driven.yaml",
23
+ ];
24
+ const aiignoreLines = [
25
+ "results/",
26
+ "benchmarks/generated/",
27
+ "model-runs/generated/",
28
+ "dist/",
29
+ "scripts/__pycache__/",
30
+ "*.pyc",
31
+ ];
32
+ const requiredText = {
33
+ "AGENTS.md": [
34
+ "AI_PROTOCOL_BEGIN",
35
+ "protocol/fast-path.yaml",
36
+ "protocol/router.yaml",
37
+ "Classifique o risco antes de agir",
38
+ "Nenhum arquivo deve passar de 400 linhas.",
39
+ ],
40
+ ".aiignore": ["results/", "dist/", "*.pyc"],
41
+ "protocol/router.yaml": ["feature_or_spec", "spec-driven.yaml", "prompt_improvement"],
42
+ "protocol/spec-driven.yaml": ["protocol_is_base_spec_is_tool", "do_not_create_spec_for_low_value_tasks"],
43
+ };
44
+
45
+ function readText(file) {
46
+ return fs.readFileSync(file, "utf8");
47
+ }
48
+
49
+ function writeText(file, text) {
50
+ fs.mkdirSync(path.dirname(file), { recursive: true });
51
+ fs.writeFileSync(file, text, "utf8");
52
+ }
53
+
54
+ function sourceProtocolDir() {
55
+ const direct = path.join(root, "protocol");
56
+ if (fs.existsSync(path.join(direct, "fast-path.yaml"))) return direct;
57
+ return path.join(root, "dist", "minimal", "protocol");
58
+ }
59
+
60
+ function minimalAgents() {
61
+ const file = path.join(root, "dist", "minimal", "AGENTS.md");
62
+ if (fs.existsSync(file)) return readText(file);
63
+ return `# AGENTS.md
64
+
65
+ ## Regra principal
66
+
67
+ ${markerStart}
68
+
69
+ Use \`protocol/fast-path.yaml\` como entrada operacional minima.
70
+
71
+ Siga \`.aiignore\` antes de abrir relatorios gerados.
72
+
73
+ Este protocolo e obrigatorio para tarefas tecnicas neste projeto. Antes de
74
+ editar, classifique risco, escolha rota e valide a entrega.
75
+
76
+ ## Ordem de leitura
77
+
78
+ 1. \`protocol/fast-path.yaml\`
79
+ 2. \`protocol/router.yaml\`
80
+ 3. Arquivo YAML especifico em \`protocol/\` conforme a rota.
81
+
82
+ ## Regras de execucao
83
+
84
+ - Classifique o risco antes de agir.
85
+ - Use o menor contexto suficiente.
86
+ - Leia apenas os arquivos indicados por \`protocol/router.yaml\`.
87
+ - Quando houver contexto grande, historico longo ou risco de confusao, use
88
+ \`protocol/context-compiler.yaml\` antes de abrir muitos arquivos.
89
+ - Use \`protocol/spec-driven.yaml\` para feature, refatoracao grande ou tarefa
90
+ critica, sem criar spec pesada para tarefa simples.
91
+ - Sempre entregue \`Prompt original\` e \`Prompt melhorado da IA\`; em baixo risco,
92
+ prefira micro formato legivel com \`PO\`, \`PM\` e \`OK\`.
93
+ - Nao edite arquivo que nao foi identificado como candidato.
94
+ - Preserve documentos, regras de IDE e configuracoes de framework existentes,
95
+ salvo pedido explicito do usuario.
96
+ - Se o risco subir, atualize a classificacao antes de continuar.
97
+ - Para nivel critico, peca confirmacao antes de acao sensivel.
98
+ - Quando houver risco de quebrar fluxo existente, entregue uma lista do que
99
+ testar.
100
+ - Entregue com evidencia em poucas linhas: mudanca, validacao, limite e risco
101
+ residual.
102
+ - Explique o resultado em linguagem clara para uma pessoa leiga entender.
103
+
104
+ ## Convivencia com arquivos existentes
105
+
106
+ Quando houver conflito, siga esta prioridade:
107
+
108
+ 1. pedido atual do usuario;
109
+ 2. este bloco obrigatorio em \`AGENTS.md\`;
110
+ 3. regras especificas da IDE ou do projeto;
111
+ 4. docs e historico somente quando a rota pedir.
112
+
113
+ Nao sobrescreva \`README.md\`, \`docs/\`, \`.cursorrules\`, \`CLAUDE.md\`,
114
+ \`.github/copilot-instructions.md\` ou configs de framework sem pedido claro.
115
+
116
+ ## Regras de organizacao
117
+
118
+ - Nenhum arquivo deve passar de 400 linhas.
119
+ - Organize primeiro para legibilidade por IA.
120
+ - Use YAML para regras operacionais.
121
+ - Evite duplicar a mesma regra em muitos lugares.
122
+
123
+ ${markerEnd}
124
+ `;
125
+ }
126
+
127
+ function protocolBlock() {
128
+ const text = minimalAgents();
129
+ const start = text.indexOf(markerStart);
130
+ const end = text.indexOf(markerEnd) + markerEnd.length;
131
+ return text.slice(start, end);
132
+ }
133
+
134
+ function copyRecursive(source, target) {
135
+ const stat = fs.statSync(source);
136
+ if (stat.isDirectory()) {
137
+ fs.mkdirSync(target, { recursive: true });
138
+ for (const item of fs.readdirSync(source)) {
139
+ copyRecursive(path.join(source, item), path.join(target, item));
140
+ }
141
+ return;
142
+ }
143
+ fs.mkdirSync(path.dirname(target), { recursive: true });
144
+ fs.copyFileSync(source, target);
145
+ }
146
+
147
+ function backup(existingPath, backupRoot) {
148
+ if (!fs.existsSync(existingPath)) return;
149
+ fs.mkdirSync(backupRoot, { recursive: true });
150
+ copyRecursive(existingPath, path.join(backupRoot, path.basename(existingPath)));
151
+ }
152
+
153
+ function timestamp() {
154
+ return new Date().toISOString().replace(/\D/g, "").slice(0, 14);
155
+ }
156
+
157
+ function printDryRun(targetRoot, force) {
158
+ console.log(`DRY-RUN target -> ${path.resolve(targetRoot)}`);
159
+ console.log(`${fs.existsSync(path.join(targetRoot, "AGENTS.md")) ? "update" : "create"}:AGENTS.md`);
160
+ console.log(`${fs.existsSync(path.join(targetRoot, ".aiignore")) ? "update" : "create"}:.aiignore`);
161
+ const protocolDir = path.join(targetRoot, "protocol");
162
+ if (fs.existsSync(protocolDir) && force) {
163
+ console.log("backup:protocol/");
164
+ console.log("replace:protocol/");
165
+ } else if (fs.existsSync(protocolDir)) {
166
+ console.log("keep:protocol/");
167
+ } else {
168
+ console.log("create:protocol/");
169
+ }
170
+ }
171
+
172
+ function installAgents(target) {
173
+ const block = protocolBlock();
174
+ if (!fs.existsSync(target)) {
175
+ writeText(target, minimalAgents());
176
+ return;
177
+ }
178
+ const text = readText(target);
179
+ if (text.includes(markerStart) && text.includes(markerEnd)) {
180
+ const before = text.slice(0, text.indexOf(markerStart)).trimEnd();
181
+ const after = text.slice(text.indexOf(markerEnd) + markerEnd.length);
182
+ writeText(target, `${before}\n\n${block}${after}`.trim() + "\n");
183
+ return;
184
+ }
185
+ writeText(target, `${block}\n\n## Instrucoes existentes do projeto\n\n${text}`.trim() + "\n");
186
+ }
187
+
188
+ function installAiignore(target) {
189
+ const existing = fs.existsSync(target) ? readText(target).split(/\r?\n/).filter(Boolean) : [];
190
+ for (const line of aiignoreLines) {
191
+ if (!existing.includes(line)) existing.push(line);
192
+ }
193
+ writeText(target, existing.join("\n").trim() + "\n");
194
+ }
195
+
196
+ function copyProtocol(target, force, backupRoot) {
197
+ if (fs.existsSync(target) && !force) return;
198
+ if (fs.existsSync(target)) {
199
+ backup(target, backupRoot);
200
+ fs.rmSync(target, { recursive: true, force: true });
201
+ }
202
+ fs.mkdirSync(target, { recursive: true });
203
+ const source = sourceProtocolDir();
204
+ for (const file of protocolFiles) {
205
+ fs.copyFileSync(path.join(source, file), path.join(target, file));
206
+ }
207
+ }
208
+
209
+ function install(target, force, dryRun) {
210
+ const targetRoot = path.resolve(target);
211
+ if (dryRun) {
212
+ printDryRun(targetRoot, force);
213
+ return 0;
214
+ }
215
+ fs.mkdirSync(targetRoot, { recursive: true });
216
+ const backupRoot = path.join(targetRoot, ".ai-protocol-backup", timestamp());
217
+ backup(path.join(targetRoot, "AGENTS.md"), backupRoot);
218
+ backup(path.join(targetRoot, ".aiignore"), backupRoot);
219
+ installAgents(path.join(targetRoot, "AGENTS.md"));
220
+ installAiignore(path.join(targetRoot, ".aiignore"));
221
+ copyProtocol(path.join(targetRoot, "protocol"), force, backupRoot);
222
+ console.log(`installed protocol -> ${targetRoot}`);
223
+ return verify(targetRoot);
224
+ }
225
+
226
+ function verify(target) {
227
+ const targetRoot = path.resolve(target);
228
+ const errors = [];
229
+ const requiredFiles = ["AGENTS.md", ".aiignore", ...protocolFiles.map((file) => `protocol/${file}`)];
230
+ for (const item of requiredFiles) {
231
+ const file = path.join(targetRoot, item);
232
+ if (!fs.existsSync(file)) {
233
+ errors.push(`missing:${item}`);
234
+ continue;
235
+ }
236
+ if (fs.statSync(file).isFile() && readText(file).split(/\r?\n/).length > 400) {
237
+ errors.push(`line_limit:${item}`);
238
+ }
239
+ }
240
+ for (const [item, snippets] of Object.entries(requiredText)) {
241
+ const file = path.join(targetRoot, item);
242
+ if (!fs.existsSync(file) || !fs.statSync(file).isFile()) continue;
243
+ const text = readText(file);
244
+ for (const snippet of snippets) {
245
+ if (!text.includes(snippet)) errors.push(`missing_text:${item}:${snippet}`);
246
+ }
247
+ }
248
+ if (errors.length) {
249
+ console.log("FAIL");
250
+ for (const error of errors) console.log(error);
251
+ return 1;
252
+ }
253
+ console.log("PASS");
254
+ return 0;
255
+ }
256
+
257
+ function parseArgs(argv) {
258
+ const command = argv[0] || "install";
259
+ const flags = new Set(argv.filter((item) => item.startsWith("-")));
260
+ const target = argv.slice(1).find((item) => !item.startsWith("-")) || ".";
261
+ return {
262
+ command,
263
+ target,
264
+ force: !flags.has("--no-force"),
265
+ dryRun: flags.has("--dry-run"),
266
+ };
267
+ }
268
+
269
+ const parsed = parseArgs(process.argv.slice(2));
270
+ if (parsed.command === "install" || parsed.command === "init") {
271
+ process.exit(install(parsed.target, parsed.force, parsed.dryRun));
272
+ }
273
+ if (parsed.command === "verify" || parsed.command === "--verify-only") {
274
+ process.exit(verify(parsed.target));
275
+ }
276
+
277
+ console.error("usage: ai-protocol install|init|verify [target] [--dry-run] [--no-force]");
278
+ process.exit(1);
@@ -0,0 +1,92 @@
1
+ #!/usr/bin/env python3
2
+ """Verify that a target project has the mandatory AI protocol installed."""
3
+
4
+ from __future__ import annotations
5
+
6
+ import argparse
7
+ from pathlib import Path
8
+
9
+
10
+ REQUIRED_FILES = [
11
+ "AGENTS.md",
12
+ ".aiignore",
13
+ "protocol/README.yaml",
14
+ "protocol/fast-path.yaml",
15
+ "protocol/router.yaml",
16
+ "protocol/modes.yaml",
17
+ "protocol/execution-rules.yaml",
18
+ "protocol/risk-levels.yaml",
19
+ "protocol/mapping-checklists.yaml",
20
+ "protocol/validation-checklist.yaml",
21
+ "protocol/context-rules.yaml",
22
+ "protocol/context-compiler.yaml",
23
+ "protocol/formatting-rules.yaml",
24
+ "protocol/prompt-economy.yaml",
25
+ "protocol/spec-driven.yaml",
26
+ ]
27
+
28
+ REQUIRED_TEXT = {
29
+ "AGENTS.md": [
30
+ "AI_PROTOCOL_BEGIN",
31
+ "protocol/fast-path.yaml",
32
+ "protocol/router.yaml",
33
+ "Classifique o risco antes de agir",
34
+ "Prompt original",
35
+ "Prompt melhorado da IA",
36
+ "lista do que",
37
+ "Nenhum arquivo deve passar de 400 linhas.",
38
+ ],
39
+ ".aiignore": [
40
+ "results/",
41
+ "dist/",
42
+ "*.pyc",
43
+ ],
44
+ "protocol/router.yaml": [
45
+ "feature_or_spec",
46
+ "spec-driven.yaml",
47
+ "prompt_improvement",
48
+ ],
49
+ "protocol/spec-driven.yaml": [
50
+ "protocol_is_base_spec_is_tool",
51
+ "do_not_create_spec_for_low_value_tasks",
52
+ ],
53
+ }
54
+
55
+
56
+ def main() -> int:
57
+ parser = argparse.ArgumentParser()
58
+ parser.add_argument("--target", required=True, help="Project root to verify")
59
+ args = parser.parse_args()
60
+
61
+ root = Path(args.target).resolve()
62
+ errors: list[str] = []
63
+
64
+ for item in REQUIRED_FILES:
65
+ path = root / item
66
+ if not path.exists():
67
+ errors.append(f"missing:{item}")
68
+ continue
69
+ if path.is_file() and len(path.read_text(encoding="utf-8").splitlines()) > 400:
70
+ errors.append(f"line_limit:{item}")
71
+
72
+ for item, snippets in REQUIRED_TEXT.items():
73
+ path = root / item
74
+ if not path.exists() or not path.is_file():
75
+ continue
76
+ text = path.read_text(encoding="utf-8")
77
+ for snippet in snippets:
78
+ if snippet not in text:
79
+ errors.append(f"missing_text:{item}:{snippet}")
80
+
81
+ if errors:
82
+ print("FAIL")
83
+ for error in errors:
84
+ print(error)
85
+ return 1
86
+
87
+ print("PASS")
88
+ return 0
89
+
90
+
91
+ if __name__ == "__main__":
92
+ raise SystemExit(main())