sauron-cli 1.4.5 → 1.4.7
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/README.md +13 -2
- package/dist/index.js +62 -29
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> O "Lobo Frontal" dos assistentes de código de IA. Resolva a amnésia de contexto de uma vez por todas.
|
|
4
4
|
|
|
5
|
-
O **Sauron CLI** é uma infraestrutura passiva de orquestração de contexto para IAs. Ele injeta um "Cérebro" estruturado nos seus repositórios, forçando as Inteligências Artificiais (como Cursor, Windsurf e
|
|
5
|
+
O **Sauron CLI** é uma infraestrutura passiva de orquestração de contexto para IAs. Ele injeta um "Cérebro" estruturado nos seus repositórios, forçando as Inteligências Artificiais (como Cursor, Windsurf, Aider, Claude Code, Codex e Opencode) a respeitarem um conceito de **"Write Obligation" (Obrigação de Escrita)**. Em vez de apenas ler código, a IA passará a documentar decisões de negócio e arquitetura continuamente, garantindo que o seu projeto não quebre após meses sem ser tocado.
|
|
6
6
|
|
|
7
7
|
## O Problema
|
|
8
8
|
|
|
@@ -12,6 +12,16 @@ Assistentes de código são incrivelmente poderosos, mas sofrem de amnésia vol
|
|
|
12
12
|
|
|
13
13
|
O Sauron resolve isso ejetando pastas estruturadas (`.sauron` e `.agents`) no seu repositório local. A partir desse momento, as IAs são condicionadas a documentar regras passivamente de acordo com os templates gerados, preservando o **Single Source of Truth** do seu produto.
|
|
14
14
|
|
|
15
|
+
## Destaques da versão 1.4.7
|
|
16
|
+
|
|
17
|
+
A versão `1.4.7` aprimora a instalação passiva do Sauron para garantir que reinstalações e atualizações não dupliquem blocos de governança nos arquivos dos agentes.
|
|
18
|
+
|
|
19
|
+
- **Injeção de memória idempotente**: o `init` agora usa o template canônico `templates/.agents/rules/memory.md` como fonte limpa para os adaptadores, evitando aninhamento repetido de `SAURON START/END`.
|
|
20
|
+
- **Mais agentes detectados automaticamente**: o scanner agora reconhece Codex, Opencode e Claude Code, além de Cursor, Windsurf, Aider e Antigravity.
|
|
21
|
+
- **Marcadores de stack aninhados**: assinaturas como `prisma/schema.prisma` agora podem ser detectadas mesmo quando o arquivo marcador está dentro de subpastas conhecidas.
|
|
22
|
+
- **Auditoria mais fiel ao projeto**: o `doctor` prioriza os targets definidos no manifesto local antes do registro global da máquina.
|
|
23
|
+
- **Versão sincronizada**: `sauron --version` passa a ler a versão diretamente do `package.json`.
|
|
24
|
+
|
|
15
25
|
|
|
16
26
|
## Instalação
|
|
17
27
|
|
|
@@ -26,7 +36,7 @@ npx sauron-cli init
|
|
|
26
36
|
### `sauron init`
|
|
27
37
|
|
|
28
38
|
Inicializa o Sauron Memory System no projeto atual de forma inteligente.
|
|
29
|
-
O comando executa uma **varredura heurística não-bloqueante** ($O(1)$) na raiz do repositório para detectar de forma autônoma a linguagem primária, frameworks, bancos de dados, gerenciadores de pacotes e ferramentas de IA ativas (Cursor, Windsurf, Aider, Antigravity).
|
|
39
|
+
O comando executa uma **varredura heurística não-bloqueante** ($O(1)$) na raiz do repositório para detectar de forma autônoma a linguagem primária, frameworks, bancos de dados, gerenciadores de pacotes e ferramentas de IA ativas (Cursor, Windsurf, Aider, Antigravity, Claude Code, Codex, Opencode).
|
|
30
40
|
|
|
31
41
|
Durante o onboarding interativo, as perguntas são pré-populadas de forma inteligente com as informações detectadas. Ao concluir, o Sauron realiza a **injeção automática de receitas de Wiki** (Wiki Recipes) personalizadas para a sua stack tecnológica em `.sauron/wiki/standards/` (ex. TypeScript, Next.js, React, Tailwind CSS, PostgreSQL) de forma idempotente e não-destrutiva, gerando o manifesto dinâmico `AGENTS.md` e as regras de governança locais das IAs.
|
|
32
42
|
|
|
@@ -35,6 +45,7 @@ O comando é totalmente **Idempotente**. Se executado em um repositório já ini
|
|
|
35
45
|
- **Fast-Track (Bypass)**: O CLI detecta a configuração passada e oferece a opção de pular o onboarding, atualizando os agentes internos silenciosamente.
|
|
36
46
|
- **Reidratação Interativa**: Caso você queira alterar a configuração, os formulários do terminal são reidratados com o seu último estado configurado, poupando a necessidade de redigitar contextos complexos.
|
|
37
47
|
- **Wiki Protection**: Um filtro cirúrgico é ativado no motor de cópia da CLI, tornando a sua Base de Conhecimento em `.sauron/wiki/` 100% blindada contra *overwrites* acidentais durante a atualização.
|
|
48
|
+
- **Canonical Memory Payload**: Os arquivos específicos de agentes são regenerados a partir de uma fonte limpa de template, preservando a fluidez da governança passiva sem acumular wrappers de instalações anteriores.
|
|
38
49
|
|
|
39
50
|
### Como Atualizar um Projeto Existente
|
|
40
51
|
Para trazer as regras e inteligências mais recentes para o seu repositório sem perder o seu contexto salvo, basta rodar o `init` forçando a versão `@latest` do NPM:
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
// src/index.ts
|
|
4
4
|
import { Command } from "commander";
|
|
5
|
+
import { createRequire } from "module";
|
|
5
6
|
|
|
6
7
|
// src/features/init/init.command.ts
|
|
7
8
|
import fs13 from "fs-extra";
|
|
@@ -630,6 +631,28 @@ var WikiBootstrapper = class {
|
|
|
630
631
|
};
|
|
631
632
|
|
|
632
633
|
// src/features/init/init.service.ts
|
|
634
|
+
var ADAPTER_OWNED_TEMPLATE_FILES = /* @__PURE__ */ new Set([".agents/rules/memory.md"]);
|
|
635
|
+
function stripLeadingFrontmatter(content) {
|
|
636
|
+
let normalized = content.trimStart();
|
|
637
|
+
let previous = "";
|
|
638
|
+
while (normalized !== previous) {
|
|
639
|
+
previous = normalized;
|
|
640
|
+
normalized = normalized.replace(/^---\r?\n[\s\S]*?\r?\n---\r?\n*/, "").trimStart();
|
|
641
|
+
}
|
|
642
|
+
return normalized;
|
|
643
|
+
}
|
|
644
|
+
function normalizeMemoryRules(content) {
|
|
645
|
+
const withoutMarkers = content.replace(/^\s*# SAURON START\s*$/gm, "").replace(/^\s*# SAURON END\s*$/gm, "");
|
|
646
|
+
const normalized = stripLeadingFrontmatter(withoutMarkers).trim();
|
|
647
|
+
return normalized ? `${normalized}
|
|
648
|
+
` : "";
|
|
649
|
+
}
|
|
650
|
+
async function loadCanonicalMemoryRules(templatesDir, fallbackContent) {
|
|
651
|
+
const templateMemoryPath = path11.join(templatesDir, ".agents", "rules", "memory.md");
|
|
652
|
+
const rawContent = await fs11.pathExists(templateMemoryPath) ? await fs11.readFile(templateMemoryPath, "utf8") : fallbackContent;
|
|
653
|
+
return normalizeMemoryRules(rawContent) || `${fallbackContent.trim()}
|
|
654
|
+
`;
|
|
655
|
+
}
|
|
633
656
|
var InitService = class {
|
|
634
657
|
registryService = new RegistryService();
|
|
635
658
|
async execute(options, driver) {
|
|
@@ -651,8 +674,11 @@ var InitService = class {
|
|
|
651
674
|
await fs11.ensureDir(targetPath);
|
|
652
675
|
await processDirectory(sourcePath, targetPath);
|
|
653
676
|
} else {
|
|
654
|
-
const content = await fs11.readFile(sourcePath, "utf8");
|
|
655
677
|
const relPath = path11.relative(cwd, targetPath).replace(/\\/g, "/");
|
|
678
|
+
if (ADAPTER_OWNED_TEMPLATE_FILES.has(relPath)) {
|
|
679
|
+
continue;
|
|
680
|
+
}
|
|
681
|
+
const content = await fs11.readFile(sourcePath, "utf8");
|
|
656
682
|
let shouldWrite = true;
|
|
657
683
|
if (await fs11.pathExists(targetPath)) {
|
|
658
684
|
const localContent = await fs11.readFile(targetPath, "utf8");
|
|
@@ -670,7 +696,7 @@ var InitService = class {
|
|
|
670
696
|
await fs11.writeFile(targetPath, content, "utf8");
|
|
671
697
|
modifiedFiles.push(relPath);
|
|
672
698
|
}
|
|
673
|
-
const isMutable = relPath.startsWith(".sauron/wiki/")
|
|
699
|
+
const isMutable = relPath.startsWith(".sauron/wiki/");
|
|
674
700
|
if (!isMutable) {
|
|
675
701
|
manifest.files[relPath] = generateHash(content);
|
|
676
702
|
}
|
|
@@ -710,13 +736,7 @@ var InitService = class {
|
|
|
710
736
|
};
|
|
711
737
|
await saveManifest(cwd, manifest);
|
|
712
738
|
modifiedFiles.push(".sauron/.manifest.json");
|
|
713
|
-
const
|
|
714
|
-
let memoryRulesContent = "";
|
|
715
|
-
if (await fs11.pathExists(memoryFilePath)) {
|
|
716
|
-
memoryRulesContent = await fs11.readFile(memoryFilePath, "utf8");
|
|
717
|
-
} else {
|
|
718
|
-
memoryRulesContent = agentsMdContent;
|
|
719
|
-
}
|
|
739
|
+
const memoryRulesContent = await loadCanonicalMemoryRules(templatesDir, agentsMdContent);
|
|
720
740
|
const projectName = path11.basename(cwd) || "Unnamed Project";
|
|
721
741
|
const memoryPayload = {
|
|
722
742
|
projectName,
|
|
@@ -1063,35 +1083,35 @@ var ProjectScanner = class {
|
|
|
1063
1083
|
if (rootFiles.includes("tsconfig.json")) {
|
|
1064
1084
|
context.primaryLanguage = "TypeScript";
|
|
1065
1085
|
}
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
context.detectedIAs.push("Aider");
|
|
1074
|
-
}
|
|
1075
|
-
if (rootDirs.includes(".agents")) {
|
|
1076
|
-
context.detectedIAs.push("Antigravity");
|
|
1077
|
-
}
|
|
1086
|
+
this.addDetectedIA(context, rootDirs.includes(".cursor") || rootFiles.includes(".cursorrules"), "Cursor");
|
|
1087
|
+
this.addDetectedIA(context, rootDirs.includes(".windsurf") || rootFiles.includes(".windsurfrules"), "Windsurf");
|
|
1088
|
+
this.addDetectedIA(context, rootFiles.includes(".aider.instructions.md") || rootFiles.includes(".aider.conf.yml"), "Aider");
|
|
1089
|
+
this.addDetectedIA(context, rootDirs.includes(".agents"), "Antigravity");
|
|
1090
|
+
this.addDetectedIA(context, rootDirs.includes(".codex"), "Codex");
|
|
1091
|
+
this.addDetectedIA(context, rootDirs.includes(".opencode") || rootFiles.includes("opencode.json"), "Opencode");
|
|
1092
|
+
this.addDetectedIA(context, rootDirs.includes(".claude") || rootFiles.includes("CLAUDE.md"), "Claude");
|
|
1078
1093
|
if (context.detectedIAs.length === 0) {
|
|
1079
|
-
context.detectedIAs = ["Cursor", "Windsurf", "Aider", "Antigravity"];
|
|
1094
|
+
context.detectedIAs = ["Cursor", "Windsurf", "Aider", "Antigravity", "Codex", "Opencode", "Claude"];
|
|
1080
1095
|
}
|
|
1081
1096
|
if (rootFiles.includes("package.json")) {
|
|
1082
1097
|
const pkgPath = path12.join(this.cwd, "package.json");
|
|
1083
|
-
const
|
|
1098
|
+
const pkg2 = await fs12.readJson(pkgPath);
|
|
1084
1099
|
const allDeps = {
|
|
1085
|
-
...
|
|
1086
|
-
...
|
|
1100
|
+
...pkg2.dependencies || {},
|
|
1101
|
+
...pkg2.devDependencies || {}
|
|
1087
1102
|
};
|
|
1088
1103
|
for (const sig of TECHNOLOGY_SIGNATURES) {
|
|
1089
1104
|
let matched = false;
|
|
1090
1105
|
if (sig.deps && sig.deps.some((dep) => allDeps[dep])) {
|
|
1091
1106
|
matched = true;
|
|
1092
1107
|
}
|
|
1093
|
-
if (sig.files
|
|
1094
|
-
|
|
1108
|
+
if (sig.files) {
|
|
1109
|
+
for (const marker of sig.files) {
|
|
1110
|
+
if (await this.matchesFileMarker(marker, rootFiles, rootDirs)) {
|
|
1111
|
+
matched = true;
|
|
1112
|
+
break;
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1095
1115
|
}
|
|
1096
1116
|
if (matched) {
|
|
1097
1117
|
this.categorizeAndAdd(sig, context);
|
|
@@ -1132,6 +1152,17 @@ var ProjectScanner = class {
|
|
|
1132
1152
|
context.wikiTemplatesToInject.push(sig.wikiTemplate);
|
|
1133
1153
|
}
|
|
1134
1154
|
}
|
|
1155
|
+
addDetectedIA(context, condition, name) {
|
|
1156
|
+
if (condition && !context.detectedIAs.includes(name)) {
|
|
1157
|
+
context.detectedIAs.push(name);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
async matchesFileMarker(marker, rootFiles, rootDirs) {
|
|
1161
|
+
if (!marker.includes("/") && !marker.includes("\\")) {
|
|
1162
|
+
return rootFiles.includes(marker) || rootDirs.includes(marker);
|
|
1163
|
+
}
|
|
1164
|
+
return fs12.pathExists(path12.join(this.cwd, marker));
|
|
1165
|
+
}
|
|
1135
1166
|
};
|
|
1136
1167
|
|
|
1137
1168
|
// src/features/init/init.command.ts
|
|
@@ -1420,7 +1451,7 @@ var DoctorService = class {
|
|
|
1420
1451
|
});
|
|
1421
1452
|
}
|
|
1422
1453
|
}
|
|
1423
|
-
const targets = registered?.aiTargets || ["Cursor", "Windsurf", "Aider", "Antigravity"];
|
|
1454
|
+
const targets = manifest?.config?.aiTargets || registered?.aiTargets || ["Cursor", "Windsurf", "Aider", "Antigravity", "Codex", "Opencode", "Claude"];
|
|
1424
1455
|
for (const target of targets) {
|
|
1425
1456
|
try {
|
|
1426
1457
|
const adapters = AdapterRegistry.resolve([target]);
|
|
@@ -1611,8 +1642,10 @@ async function runUninstallCommand(options) {
|
|
|
1611
1642
|
}
|
|
1612
1643
|
|
|
1613
1644
|
// src/index.ts
|
|
1645
|
+
var require2 = createRequire(import.meta.url);
|
|
1646
|
+
var pkg = require2("../package.json");
|
|
1614
1647
|
var program = new Command();
|
|
1615
|
-
program.name("sauron").description("Sauron CLI - Framework para resolu\xE7\xE3o de Amn\xE9sia de Contexto em IAs").version("
|
|
1648
|
+
program.name("sauron").description("Sauron CLI - Framework para resolu\xE7\xE3o de Amn\xE9sia de Contexto em IAs").version(pkg.version || "0.0.0");
|
|
1616
1649
|
program.command("init").description("Inicializa o Sauron Memory System no projeto atual").option("-y, --yes", "Pula os prompts interativos e usa os valores padr\xE3o (N\xE3o-interativo)").option("--json", "Sa\xEDda do resultado em formato JSON estruturado").option("--conflict <resolution>", "Estrat\xE9gia de resolu\xE7\xE3o de conflitos autom\xE1tica (ours | theirs)").action((options) => runInitCommand(options));
|
|
1617
1650
|
program.command("doctor").description("Executa uma auditoria de integridade e conformidade estrutural das regras e wiki").option("--json", "Sa\xEDda do relat\xF3rio em formato JSON estruturado").action((options) => runDoctorCommand(options));
|
|
1618
1651
|
program.command("uninstall").description("Remove as vincula\xE7\xF5es do Sauron CLI e regras de assistentes locais").option("--purge", "Remove fisicamente a pasta .sauron/ incluindo a wiki de documenta\xE7\xE3o").option("-y, --yes", "Pula os prompts de confirma\xE7\xE3o de desinstala\xE7\xE3o").option("--json", "Sa\xEDda do resultado em formato JSON estruturado").action((options) => runUninstallCommand(options));
|