sauron-cli 1.4.1 → 1.4.3

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/index.js CHANGED
@@ -4,8 +4,8 @@
4
4
  import { Command } from "commander";
5
5
 
6
6
  // src/features/init/init.command.ts
7
- import fs10 from "fs-extra";
8
- import path10 from "path";
7
+ import fs13 from "fs-extra";
8
+ import path13 from "path";
9
9
  import pc2 from "picocolors";
10
10
  import { fileURLToPath } from "url";
11
11
  import * as p2 from "@clack/prompts";
@@ -36,8 +36,8 @@ async function saveManifest(targetDir, manifest) {
36
36
  }
37
37
 
38
38
  // src/features/init/init.service.ts
39
- import fs8 from "fs-extra";
40
- import path8 from "path";
39
+ import fs11 from "fs-extra";
40
+ import path11 from "path";
41
41
 
42
42
  // src/core/merge.service.ts
43
43
  function checkConflict(localContent, newContent, manifestHash) {
@@ -146,31 +146,59 @@ var RegistryService = class {
146
146
  }
147
147
  };
148
148
 
149
+ // src/core/registry/AdapterRegistry.ts
150
+ var AdapterRegistry = class {
151
+ static adapters = /* @__PURE__ */ new Map();
152
+ static register(adapter) {
153
+ if (this.adapters.has(adapter.id)) {
154
+ throw new Error(`Conflito: Adaptador com ID '${adapter.id}' j\xE1 foi registrado no ecossistema.`);
155
+ }
156
+ this.adapters.set(adapter.id, adapter);
157
+ }
158
+ static resolve(ids) {
159
+ const resolvedAdapters = [];
160
+ for (const id of ids) {
161
+ const normalizedId = id.toLowerCase().trim();
162
+ const adapterInstance = this.adapters.get(normalizedId);
163
+ if (!adapterInstance) {
164
+ throw new Error(`Falha Cr\xEDtica: Agente AI '${id}' n\xE3o \xE9 suportado pela vers\xE3o atual.`);
165
+ }
166
+ resolvedAdapters.push(adapterInstance);
167
+ }
168
+ return resolvedAdapters;
169
+ }
170
+ static getAll() {
171
+ return Array.from(this.adapters.values());
172
+ }
173
+ };
174
+
149
175
  // src/core/adapters/cursor.adapter.ts
150
176
  import fs3 from "fs-extra";
151
177
  import path3 from "path";
152
178
  var CursorAdapter = class {
153
- getName() {
154
- return "Cursor";
179
+ id = "cursor";
180
+ displayName = "Cursor IDE Engine";
181
+ async detect(targetDir) {
182
+ return fs3.pathExists(path3.join(targetDir, ".cursor"));
155
183
  }
156
- async inject(cwd, rulesContent) {
157
- const rulesDir = path3.join(cwd, ".cursor", "rules");
184
+ async inject(payload, targetDir) {
185
+ const rulesDir = path3.join(targetDir, ".cursor", "rules");
158
186
  await fs3.ensureDir(rulesDir);
159
187
  const mdcPath = path3.join(rulesDir, "sauron-memory.mdc");
160
188
  const mdcContent = `---
161
189
  description: Diretrizes de Mem\xF3ria e Write Obligation para evitar Amn\xE9sia de Contexto
162
- globs: *
190
+ globs: ${payload.ruleScope || "*"}
163
191
  ---
164
192
 
165
193
  # SAURON START
166
- ${rulesContent}
194
+ ${payload.globalRules}
167
195
  # SAURON END
168
196
  `;
169
197
  await fs3.writeFile(mdcPath, mdcContent, "utf8");
170
198
  return [".cursor/rules/sauron-memory.mdc"];
171
199
  }
172
- async clean(cwd) {
173
- const mdcPath = path3.join(cwd, ".cursor", "rules", "sauron-memory.mdc");
200
+ async clean(targetDir) {
201
+ const mdcPath = path3.join(targetDir, ".cursor", "rules", "sauron-memory.mdc");
174
202
  if (await fs3.pathExists(mdcPath)) {
175
203
  await fs3.remove(mdcPath);
176
204
  return [".cursor/rules/sauron-memory.mdc"];
@@ -183,38 +211,60 @@ ${rulesContent}
183
211
  import fs4 from "fs-extra";
184
212
  import path4 from "path";
185
213
  var WindsurfAdapter = class {
186
- getName() {
187
- return "Windsurf";
188
- }
189
- async inject(cwd, rulesContent) {
190
- const rulesPath = path4.join(cwd, ".windsurfrules");
191
- let localContent = "";
192
- if (await fs4.pathExists(rulesPath)) {
193
- localContent = await fs4.readFile(rulesPath, "utf8");
194
- }
195
- const cleanedContent = this.removeSauronBlock(localContent);
196
- const sauronBlock = `
197
- # SAURON START
198
- ${rulesContent}
199
- # SAURON END
214
+ id = "windsurf";
215
+ displayName = "Windsurf by Codeium";
216
+ async detect(targetDir) {
217
+ return fs4.pathExists(path4.join(targetDir, ".windsurf"));
218
+ }
219
+ async inject(payload, targetDir) {
220
+ const modifications = [];
221
+ const rulesDir = path4.join(targetDir, ".windsurf", "rules");
222
+ await fs4.ensureDir(rulesDir);
223
+ const ruleContent = `---
224
+ description: "Sauron Memory Override - Universal Guidelines and Base Types"
225
+ trigger: always_on
226
+ ---
227
+
228
+ # Overarching Project Context
229
+ ${payload.globalRules}
230
+
231
+ *Note: For dynamic capabilities, rely on native skill sets; for fundamental structures, observe these bounds.*
200
232
  `;
201
- const finalContent = (cleanedContent.trim() + "\n" + sauronBlock).trim() + "\n";
202
- await fs4.writeFile(rulesPath, finalContent, "utf8");
203
- return [".windsurfrules"];
204
- }
205
- async clean(cwd) {
206
- const rulesPath = path4.join(cwd, ".windsurfrules");
207
- if (!await fs4.pathExists(rulesPath)) {
208
- return [];
233
+ const mdPath = path4.join(rulesDir, "sauron-memory.md");
234
+ await fs4.outputFile(mdPath, ruleContent, "utf-8");
235
+ modifications.push(".windsurf/rules/sauron-memory.md");
236
+ const oldPath = path4.join(targetDir, ".windsurfrules");
237
+ if (await fs4.pathExists(oldPath)) {
238
+ const localContent = await fs4.readFile(oldPath, "utf8");
239
+ const cleanedContent = this.removeSauronBlock(localContent).trim();
240
+ if (cleanedContent === "") {
241
+ await fs4.remove(oldPath);
242
+ } else {
243
+ await fs4.writeFile(oldPath, cleanedContent + "\n", "utf8");
244
+ }
245
+ modifications.push(".windsurfrules");
209
246
  }
210
- const localContent = await fs4.readFile(rulesPath, "utf8");
211
- const cleanedContent = this.removeSauronBlock(localContent).trim();
212
- if (cleanedContent === "") {
213
- await fs4.remove(rulesPath);
214
- } else {
215
- await fs4.writeFile(rulesPath, cleanedContent + "\n", "utf8");
247
+ return modifications;
248
+ }
249
+ async clean(targetDir) {
250
+ const modifications = [];
251
+ const mdPath = path4.join(targetDir, ".windsurf", "rules", "sauron-memory.md");
252
+ if (await fs4.pathExists(mdPath)) {
253
+ await fs4.remove(mdPath);
254
+ modifications.push(".windsurf/rules/sauron-memory.md");
255
+ }
256
+ const oldPath = path4.join(targetDir, ".windsurfrules");
257
+ if (await fs4.pathExists(oldPath)) {
258
+ const localContent = await fs4.readFile(oldPath, "utf8");
259
+ const cleanedContent = this.removeSauronBlock(localContent).trim();
260
+ if (cleanedContent === "") {
261
+ await fs4.remove(oldPath);
262
+ } else {
263
+ await fs4.writeFile(oldPath, cleanedContent + "\n", "utf8");
264
+ }
265
+ modifications.push(".windsurfrules");
216
266
  }
217
- return [".windsurfrules"];
267
+ return modifications;
218
268
  }
219
269
  removeSauronBlock(content) {
220
270
  const regex = /# SAURON START[\s\S]*?# SAURON END/g;
@@ -225,26 +275,90 @@ ${rulesContent}
225
275
  // src/core/adapters/aider.adapter.ts
226
276
  import fs5 from "fs-extra";
227
277
  import path5 from "path";
278
+ import * as yaml from "js-yaml";
279
+ function isAiderConfigValid(config) {
280
+ return typeof config === "object" && config !== null;
281
+ }
228
282
  var AiderAdapter = class {
229
- getName() {
230
- return "Aider";
231
- }
232
- async inject(cwd, rulesContent) {
233
- const rulesPath = path5.join(cwd, ".aider.instructions.md");
234
- const content = `# SAURON START
235
- ${rulesContent}
236
- # SAURON END
237
- `;
238
- await fs5.writeFile(rulesPath, content, "utf8");
239
- return [".aider.instructions.md"];
240
- }
241
- async clean(cwd) {
242
- const rulesPath = path5.join(cwd, ".aider.instructions.md");
243
- if (await fs5.pathExists(rulesPath)) {
244
- await fs5.remove(rulesPath);
245
- return [".aider.instructions.md"];
283
+ id = "aider";
284
+ displayName = "Aider Agent CLI";
285
+ async detect(targetDir) {
286
+ return fs5.pathExists(path5.join(targetDir, ".aider.conf.yml"));
287
+ }
288
+ async inject(payload, targetDir) {
289
+ const modifications = [];
290
+ const memoryFileName = ".sauron-aider-instructions.md";
291
+ const instructionsPath = path5.join(targetDir, memoryFileName);
292
+ await fs5.outputFile(instructionsPath, payload.globalRules, "utf-8");
293
+ modifications.push(memoryFileName);
294
+ const configPath = path5.join(targetDir, ".aider.conf.yml");
295
+ let configObj = {};
296
+ if (await fs5.pathExists(configPath)) {
297
+ try {
298
+ const configStr = await fs5.readFile(configPath, "utf8");
299
+ configObj = yaml.load(configStr) || {};
300
+ } catch (err) {
301
+ throw new Error(`O YAML subjacente em ${configPath} est\xE1 corrompido ou malformado.`);
302
+ }
246
303
  }
247
- return [];
304
+ if (isAiderConfigValid(configObj)) {
305
+ if (!configObj.read) {
306
+ configObj.read = [];
307
+ } else if (typeof configObj.read === "string") {
308
+ configObj.read = [configObj.read];
309
+ }
310
+ if (Array.isArray(configObj.read) && !configObj.read.includes(memoryFileName)) {
311
+ configObj.read.push(memoryFileName);
312
+ }
313
+ const newYamlDump = yaml.dump(configObj, { indent: 2, lineWidth: -1 });
314
+ await fs5.outputFile(configPath, newYamlDump, "utf-8");
315
+ modifications.push(".aider.conf.yml");
316
+ } else {
317
+ throw new Error("Falha no cast. A estrutura lida do '.aider.conf.yml' n\xE3o corresponde a um objeto mape\xE1vel.");
318
+ }
319
+ const oldPath = path5.join(targetDir, ".aider.instructions.md");
320
+ if (await fs5.pathExists(oldPath)) {
321
+ await fs5.remove(oldPath);
322
+ modifications.push(".aider.instructions.md");
323
+ }
324
+ return modifications;
325
+ }
326
+ async clean(targetDir) {
327
+ const modifications = [];
328
+ const memoryFileName = ".sauron-aider-instructions.md";
329
+ const instructionsPath = path5.join(targetDir, memoryFileName);
330
+ if (await fs5.pathExists(instructionsPath)) {
331
+ await fs5.remove(instructionsPath);
332
+ modifications.push(memoryFileName);
333
+ }
334
+ const configPath = path5.join(targetDir, ".aider.conf.yml");
335
+ if (await fs5.pathExists(configPath)) {
336
+ try {
337
+ const configStr = await fs5.readFile(configPath, "utf8");
338
+ const configObj = yaml.load(configStr);
339
+ if (isAiderConfigValid(configObj) && Array.isArray(configObj.read)) {
340
+ const newRead = configObj.read.filter((p5) => p5 !== memoryFileName);
341
+ if (newRead.length !== configObj.read.length) {
342
+ configObj.read = newRead;
343
+ if (configObj.read.length === 0) delete configObj.read;
344
+ const newYamlDump = yaml.dump(configObj, { indent: 2, lineWidth: -1 });
345
+ if (Object.keys(configObj).length === 0) {
346
+ await fs5.remove(configPath);
347
+ } else {
348
+ await fs5.outputFile(configPath, newYamlDump, "utf-8");
349
+ }
350
+ modifications.push(".aider.conf.yml");
351
+ }
352
+ }
353
+ } catch (err) {
354
+ }
355
+ }
356
+ const oldPath = path5.join(targetDir, ".aider.instructions.md");
357
+ if (await fs5.pathExists(oldPath)) {
358
+ await fs5.remove(oldPath);
359
+ modifications.push(".aider.instructions.md");
360
+ }
361
+ return modifications;
248
362
  }
249
363
  };
250
364
 
@@ -252,25 +366,27 @@ ${rulesContent}
252
366
  import fs6 from "fs-extra";
253
367
  import path6 from "path";
254
368
  var AntigravityAdapter = class {
255
- getName() {
256
- return "Antigravity";
369
+ id = "antigravity";
370
+ displayName = "Antigravity IDE Engine";
371
+ async detect(targetDir) {
372
+ return fs6.pathExists(path6.join(targetDir, ".agents"));
257
373
  }
258
- async inject(cwd, rulesContent) {
259
- const rulesPath = path6.join(cwd, ".agents", "rules", "memory.md");
374
+ async inject(payload, targetDir) {
375
+ const rulesPath = path6.join(targetDir, ".agents", "rules", "memory.md");
260
376
  await fs6.ensureDir(path6.dirname(rulesPath));
261
377
  const content = `---
262
378
  trigger: always_on
263
379
  ---
264
380
 
265
381
  # SAURON START
266
- ${rulesContent}
382
+ ${payload.globalRules}
267
383
  # SAURON END
268
384
  `;
269
385
  await fs6.writeFile(rulesPath, content, "utf8");
270
386
  return [".agents/rules/memory.md"];
271
387
  }
272
- async clean(cwd) {
273
- const agentsDir = path6.join(cwd, ".agents");
388
+ async clean(targetDir) {
389
+ const agentsDir = path6.join(targetDir, ".agents");
274
390
  if (await fs6.pathExists(agentsDir)) {
275
391
  await fs6.remove(agentsDir);
276
392
  return [".agents/rules/memory.md", ".agents/skills/wiki/SKILL.md"];
@@ -279,75 +395,193 @@ ${rulesContent}
279
395
  }
280
396
  };
281
397
 
282
- // src/core/adapters/adapter.factory.ts
283
- var AdapterFactory = class {
284
- static getAdapter(name) {
285
- switch (name.trim().toLowerCase()) {
286
- case "cursor":
287
- return new CursorAdapter();
288
- case "windsurf":
289
- return new WindsurfAdapter();
290
- case "aider":
291
- return new AiderAdapter();
292
- case "antigravity":
293
- return new AntigravityAdapter();
294
- default:
295
- return null;
398
+ // src/core/adapters/opencode.adapter.ts
399
+ import fs7 from "fs-extra";
400
+ import path7 from "path";
401
+ var OpencodeAdapter = class {
402
+ id = "opencode";
403
+ displayName = "Opencode Multi-Model Interface";
404
+ async detect(targetDir) {
405
+ return fs7.pathExists(path7.join(targetDir, "opencode.json"));
406
+ }
407
+ async inject(payload, targetDir) {
408
+ const modifications = [];
409
+ const instructionDir = path7.join(targetDir, ".opencode", "instructions");
410
+ await fs7.ensureDir(instructionDir);
411
+ const rulesPath = path7.join(instructionDir, "sauron-memory.md");
412
+ await fs7.outputFile(rulesPath, payload.globalRules, "utf-8");
413
+ modifications.push(".opencode/instructions/sauron-memory.md");
414
+ const configPath = path7.join(targetDir, "opencode.json");
415
+ let configObj = {};
416
+ if (await fs7.pathExists(configPath)) {
417
+ try {
418
+ configObj = await fs7.readJson(configPath);
419
+ } catch (e) {
420
+ throw new Error("Falha no parse do arquivo base opencode.json. JSON inv\xE1lido detectado.");
421
+ }
422
+ }
423
+ if (!Array.isArray(configObj.instructions)) {
424
+ configObj.instructions = configObj.instructions ? [configObj.instructions] : [];
296
425
  }
426
+ const relativeRulesPath = ".opencode/instructions/sauron-memory.md";
427
+ if (!configObj.instructions.includes(relativeRulesPath)) {
428
+ configObj.instructions.push(relativeRulesPath);
429
+ await fs7.writeJson(configPath, configObj, { spaces: 2 });
430
+ modifications.push("opencode.json");
431
+ }
432
+ return modifications;
433
+ }
434
+ async clean(targetDir) {
435
+ const modifications = [];
436
+ const rulesPath = path7.join(targetDir, ".opencode", "instructions", "sauron-memory.md");
437
+ if (await fs7.pathExists(rulesPath)) {
438
+ await fs7.remove(rulesPath);
439
+ modifications.push(".opencode/instructions/sauron-memory.md");
440
+ }
441
+ const configPath = path7.join(targetDir, "opencode.json");
442
+ if (await fs7.pathExists(configPath)) {
443
+ try {
444
+ const configObj = await fs7.readJson(configPath);
445
+ if (Array.isArray(configObj.instructions)) {
446
+ const relativeRulesPath = ".opencode/instructions/sauron-memory.md";
447
+ const newInstructions = configObj.instructions.filter((i) => i !== relativeRulesPath);
448
+ if (newInstructions.length !== configObj.instructions.length) {
449
+ configObj.instructions = newInstructions;
450
+ if (configObj.instructions.length === 0) delete configObj.instructions;
451
+ if (Object.keys(configObj).length === 0) {
452
+ await fs7.remove(configPath);
453
+ } else {
454
+ await fs7.writeJson(configPath, configObj, { spaces: 2 });
455
+ }
456
+ modifications.push("opencode.json");
457
+ }
458
+ }
459
+ } catch (e) {
460
+ }
461
+ }
462
+ return modifications;
297
463
  }
298
- static getAllAdapters() {
299
- return [
300
- new CursorAdapter(),
301
- new WindsurfAdapter(),
302
- new AiderAdapter(),
303
- new AntigravityAdapter()
304
- ];
464
+ };
465
+
466
+ // src/core/adapters/codex.adapter.ts
467
+ import fs8 from "fs-extra";
468
+ import path8 from "path";
469
+ var CodexAdapter = class {
470
+ id = "codex";
471
+ displayName = "Codex Local Sandbox";
472
+ async detect(targetDir) {
473
+ return fs8.pathExists(path8.join(targetDir, ".codex"));
474
+ }
475
+ async inject(payload, targetDir) {
476
+ const rulesDir = path8.join(targetDir, ".codex", "rules");
477
+ await fs8.ensureDir(rulesDir);
478
+ const codexPolicy = `prompt: true
479
+ allow:
480
+ - read: .sauron/wiki/**
481
+ - read: AGENTS.md
482
+
483
+ # Sauron Central Governance Policy
484
+ ${payload.globalRules}
485
+ `;
486
+ const outputPath = path8.join(rulesDir, "sauron-memory.rules");
487
+ await fs8.outputFile(outputPath, codexPolicy, "utf-8");
488
+ return [".codex/rules/sauron-memory.rules"];
489
+ }
490
+ async clean(targetDir) {
491
+ const rulesPath = path8.join(targetDir, ".codex", "rules", "sauron-memory.rules");
492
+ if (await fs8.pathExists(rulesPath)) {
493
+ await fs8.remove(rulesPath);
494
+ return [".codex/rules/sauron-memory.rules"];
495
+ }
496
+ return [];
305
497
  }
306
498
  };
307
499
 
500
+ // src/core/adapters/claude.adapter.ts
501
+ import fs9 from "fs-extra";
502
+ import path9 from "path";
503
+ var ClaudeAdapter = class {
504
+ id = "claude";
505
+ displayName = "Claude Code CLI";
506
+ async detect(targetDir) {
507
+ return fs9.pathExists(path9.join(targetDir, ".claude"));
508
+ }
509
+ async inject(payload, targetDir) {
510
+ const rulesDir = path9.join(targetDir, ".claude", "rules");
511
+ await fs9.ensureDir(rulesDir);
512
+ const claudeRuleContent = `# Sauron Governance Directives
513
+
514
+ ## Context Inheritance
515
+ As per architectural guidelines, Claude must persistently obey instructions centralized in the global ${payload.fallbackReference || "AGENTS.md"} file within this project root.
516
+
517
+ ## Local Addendum Context
518
+ ${payload.globalRules}`;
519
+ const outputPath = path9.join(rulesDir, "sauron-base.md");
520
+ await fs9.outputFile(outputPath, claudeRuleContent, "utf-8");
521
+ return [".claude/rules/sauron-base.md"];
522
+ }
523
+ async clean(targetDir) {
524
+ const rulesPath = path9.join(targetDir, ".claude", "rules", "sauron-base.md");
525
+ if (await fs9.pathExists(rulesPath)) {
526
+ await fs9.remove(rulesPath);
527
+ return [".claude/rules/sauron-base.md"];
528
+ }
529
+ return [];
530
+ }
531
+ };
532
+
533
+ // src/core/adapters/index.ts
534
+ AdapterRegistry.register(new CursorAdapter());
535
+ AdapterRegistry.register(new WindsurfAdapter());
536
+ AdapterRegistry.register(new AiderAdapter());
537
+ AdapterRegistry.register(new AntigravityAdapter());
538
+ AdapterRegistry.register(new OpencodeAdapter());
539
+ AdapterRegistry.register(new CodexAdapter());
540
+ AdapterRegistry.register(new ClaudeAdapter());
541
+
308
542
  // src/core/wiki/wiki-bootstrapper.ts
309
- import fs7 from "fs-extra";
310
- import path7 from "path";
543
+ import fs10 from "fs-extra";
544
+ import path10 from "path";
311
545
  var WikiBootstrapper = class {
312
546
  constructor(projectRoot) {
313
547
  this.projectRoot = projectRoot;
314
- this.wikiDir = path7.join(projectRoot, ".sauron", "wiki");
315
- this.standardsDir = path7.join(this.wikiDir, "standards");
548
+ this.wikiDir = path10.join(projectRoot, ".sauron", "wiki");
549
+ this.standardsDir = path10.join(this.wikiDir, "standards");
316
550
  }
317
551
  projectRoot;
318
552
  standardsDir;
319
553
  wikiDir;
320
554
  async bootstrapFromTemplates(templatesDir, wikiTemplatesToInject) {
321
555
  const injectedFiles = [];
322
- const recipesSrcDir = path7.join(templatesDir, "wiki-recipes");
323
- if (!await fs7.pathExists(recipesSrcDir)) {
556
+ const recipesSrcDir = path10.join(templatesDir, "wiki-recipes");
557
+ if (!await fs10.pathExists(recipesSrcDir)) {
324
558
  return [];
325
559
  }
326
- await fs7.ensureDir(this.standardsDir);
327
- const summaryPath = path7.join(this.wikiDir, "summary.json");
560
+ await fs10.ensureDir(this.standardsDir);
561
+ const summaryPath = path10.join(this.wikiDir, "summary.json");
328
562
  let summary = [];
329
- if (await fs7.pathExists(summaryPath)) {
563
+ if (await fs10.pathExists(summaryPath)) {
330
564
  try {
331
- summary = await fs7.readJson(summaryPath);
565
+ summary = await fs10.readJson(summaryPath);
332
566
  } catch (err) {
333
567
  summary = [];
334
568
  }
335
569
  }
336
570
  for (const templateName of wikiTemplatesToInject) {
337
- const srcTemplatePath = path7.join(recipesSrcDir, templateName);
338
- if (!await fs7.pathExists(srcTemplatePath)) {
571
+ const srcTemplatePath = path10.join(recipesSrcDir, templateName);
572
+ if (!await fs10.pathExists(srcTemplatePath)) {
339
573
  continue;
340
574
  }
341
575
  const destFilename = templateName;
342
- const destFullPath = path7.join(this.standardsDir, destFilename);
576
+ const destFullPath = path10.join(this.standardsDir, destFilename);
343
577
  const relWikiPath = `standards/${destFilename}`;
344
- const content = await fs7.readFile(srcTemplatePath, "utf8");
578
+ const content = await fs10.readFile(srcTemplatePath, "utf8");
345
579
  const hash = generateHash(content);
346
580
  const len = Buffer.byteLength(content, "utf8");
347
- const exists = await fs7.pathExists(destFullPath);
581
+ const exists = await fs10.pathExists(destFullPath);
348
582
  if (!exists) {
349
- await fs7.writeFile(destFullPath, content, "utf8");
350
- injectedFiles.push(path7.join(".sauron", "wiki", relWikiPath).replace(/\\/g, "/"));
583
+ await fs10.writeFile(destFullPath, content, "utf8");
584
+ injectedFiles.push(path10.join(".sauron", "wiki", relWikiPath).replace(/\\/g, "/"));
351
585
  }
352
586
  const slug = destFilename.replace(".rules.md", "").replace(".rules.txt", "");
353
587
  const docName = this.inferDocName(slug);
@@ -374,7 +608,7 @@ var WikiBootstrapper = class {
374
608
  summary.push(newItem);
375
609
  }
376
610
  }
377
- await fs7.writeJson(summaryPath, summary, { spaces: 2 });
611
+ await fs10.writeJson(summaryPath, summary, { spaces: 2 });
378
612
  return injectedFiles;
379
613
  }
380
614
  inferDocName(slug) {
@@ -403,25 +637,25 @@ var InitService = class {
403
637
  const manifest = await getManifest(cwd) || { version: "1.0.0", files: {} };
404
638
  const modifiedFiles = [];
405
639
  const processDirectory = async (source, target) => {
406
- if (!await fs8.pathExists(source)) return;
407
- const files = await fs8.readdir(source);
640
+ if (!await fs11.pathExists(source)) return;
641
+ const files = await fs11.readdir(source);
408
642
  for (const file of files) {
409
- const sourcePath = path8.join(source, file);
410
- const targetPath = path8.join(target, file);
411
- const stat = await fs8.stat(sourcePath);
643
+ const sourcePath = path11.join(source, file);
644
+ const targetPath = path11.join(target, file);
645
+ const stat = await fs11.stat(sourcePath);
412
646
  if (stat.isDirectory()) {
413
- const relativeToTarget = path8.relative(cwd, targetPath).replace(/\\/g, "/");
414
- if (relativeToTarget === ".sauron/wiki" && await fs8.pathExists(targetPath)) {
647
+ const relativeToTarget = path11.relative(cwd, targetPath).replace(/\\/g, "/");
648
+ if (relativeToTarget === ".sauron/wiki" && await fs11.pathExists(targetPath)) {
415
649
  continue;
416
650
  }
417
- await fs8.ensureDir(targetPath);
651
+ await fs11.ensureDir(targetPath);
418
652
  await processDirectory(sourcePath, targetPath);
419
653
  } else {
420
- const content = await fs8.readFile(sourcePath, "utf8");
421
- const relPath = path8.relative(cwd, targetPath).replace(/\\/g, "/");
654
+ const content = await fs11.readFile(sourcePath, "utf8");
655
+ const relPath = path11.relative(cwd, targetPath).replace(/\\/g, "/");
422
656
  let shouldWrite = true;
423
- if (await fs8.pathExists(targetPath)) {
424
- const localContent = await fs8.readFile(targetPath, "utf8");
657
+ if (await fs11.pathExists(targetPath)) {
658
+ const localContent = await fs11.readFile(targetPath, "utf8");
425
659
  const hasConflict = checkConflict(localContent, content, manifest.files[relPath]);
426
660
  if (hasConflict) {
427
661
  const decision = await driver.resolveConflict(relPath, localContent, content);
@@ -430,10 +664,10 @@ var InitService = class {
430
664
  }
431
665
  }
432
666
  } else {
433
- await fs8.ensureDir(path8.dirname(targetPath));
667
+ await fs11.ensureDir(path11.dirname(targetPath));
434
668
  }
435
669
  if (shouldWrite) {
436
- await fs8.writeFile(targetPath, content, "utf8");
670
+ await fs11.writeFile(targetPath, content, "utf8");
437
671
  modifiedFiles.push(relPath);
438
672
  }
439
673
  const isMutable = relPath.startsWith(".sauron/wiki/") || relPath === ".agents/rules/memory.md";
@@ -443,9 +677,9 @@ var InitService = class {
443
677
  }
444
678
  }
445
679
  };
446
- await processDirectory(path8.join(templatesDir, ".sauron"), path8.join(cwd, ".sauron"));
447
- await processDirectory(path8.join(templatesDir, ".agents"), path8.join(cwd, ".agents"));
448
- const agentsMdPath = path8.join(cwd, "AGENTS.md");
680
+ await processDirectory(path11.join(templatesDir, ".sauron"), path11.join(cwd, ".sauron"));
681
+ await processDirectory(path11.join(templatesDir, ".agents"), path11.join(cwd, ".agents"));
682
+ const agentsMdPath = path11.join(cwd, "AGENTS.md");
449
683
  const agentsMdContent = generateAgentsMarkdown(
450
684
  options.aiTargets,
451
685
  options.severity,
@@ -453,8 +687,8 @@ var InitService = class {
453
687
  options.projectStack
454
688
  );
455
689
  let shouldWriteAgents = true;
456
- if (await fs8.pathExists(agentsMdPath)) {
457
- const localAgents = await fs8.readFile(agentsMdPath, "utf8");
690
+ if (await fs11.pathExists(agentsMdPath)) {
691
+ const localAgents = await fs11.readFile(agentsMdPath, "utf8");
458
692
  const hasConflict = checkConflict(localAgents, agentsMdContent, manifest.files["AGENTS.md"]);
459
693
  if (hasConflict) {
460
694
  const decision = await driver.resolveConflict("AGENTS.md", localAgents, agentsMdContent);
@@ -464,7 +698,7 @@ var InitService = class {
464
698
  }
465
699
  }
466
700
  if (shouldWriteAgents) {
467
- await fs8.writeFile(agentsMdPath, agentsMdContent, "utf8");
701
+ await fs11.writeFile(agentsMdPath, agentsMdContent, "utf8");
468
702
  modifiedFiles.push("AGENTS.md");
469
703
  }
470
704
  manifest.files["AGENTS.md"] = generateHash(agentsMdContent);
@@ -476,19 +710,28 @@ var InitService = class {
476
710
  };
477
711
  await saveManifest(cwd, manifest);
478
712
  modifiedFiles.push(".sauron/.manifest.json");
479
- const memoryFilePath = path8.join(cwd, ".agents", "rules", "memory.md");
713
+ const memoryFilePath = path11.join(cwd, ".agents", "rules", "memory.md");
480
714
  let memoryRulesContent = "";
481
- if (await fs8.pathExists(memoryFilePath)) {
482
- memoryRulesContent = await fs8.readFile(memoryFilePath, "utf8");
715
+ if (await fs11.pathExists(memoryFilePath)) {
716
+ memoryRulesContent = await fs11.readFile(memoryFilePath, "utf8");
483
717
  } else {
484
718
  memoryRulesContent = agentsMdContent;
485
719
  }
486
- for (const target of options.aiTargets) {
487
- const adapter = AdapterFactory.getAdapter(target);
488
- if (adapter) {
489
- const paths = await adapter.inject(cwd, memoryRulesContent);
720
+ const projectName = path11.basename(cwd) || "Unnamed Project";
721
+ const memoryPayload = {
722
+ projectName,
723
+ globalRules: memoryRulesContent,
724
+ ruleScope: "**/*.{ts,js,tsx,jsx}",
725
+ fallbackReference: "AGENTS.md"
726
+ };
727
+ try {
728
+ const adapters = AdapterRegistry.resolve(options.aiTargets);
729
+ for (const adapter of adapters) {
730
+ const paths = await adapter.inject(memoryPayload, cwd);
490
731
  modifiedFiles.push(...paths);
491
732
  }
733
+ } catch (error) {
734
+ throw new Error(`Erro ao orquestrar adaptadores: ${error.message}`);
492
735
  }
493
736
  const bootstrapper = new WikiBootstrapper(cwd);
494
737
  const injectedWikiFiles = await bootstrapper.bootstrapFromTemplates(
@@ -496,7 +739,6 @@ var InitService = class {
496
739
  options.wikiTemplatesToInject
497
740
  );
498
741
  modifiedFiles.push(...injectedWikiFiles);
499
- const projectName = path8.basename(cwd) || "Unnamed Project";
500
742
  await this.registryService.registerWorkspace(
501
743
  projectName,
502
744
  cwd,
@@ -711,8 +953,8 @@ var PresentationRouter = class {
711
953
  };
712
954
 
713
955
  // src/core/scanner/project-scanner.ts
714
- import fs9 from "fs-extra";
715
- import path9 from "path";
956
+ import fs12 from "fs-extra";
957
+ import path12 from "path";
716
958
 
717
959
  // src/core/scanner/signatures.ts
718
960
  var TECHNOLOGY_SIGNATURES = [
@@ -805,7 +1047,7 @@ var ProjectScanner = class {
805
1047
  wikiTemplatesToInject: []
806
1048
  };
807
1049
  try {
808
- const rootEntries = await fs9.readdir(this.cwd, { withFileTypes: true });
1050
+ const rootEntries = await fs12.readdir(this.cwd, { withFileTypes: true });
809
1051
  const rootFiles = rootEntries.filter((e) => e.isFile()).map((e) => e.name);
810
1052
  const rootDirs = rootEntries.filter((e) => e.isDirectory()).map((e) => e.name);
811
1053
  if (rootFiles.includes("pnpm-lock.yaml") || rootFiles.includes("pnpm-workspace.yaml")) {
@@ -837,8 +1079,8 @@ var ProjectScanner = class {
837
1079
  context.detectedIAs = ["Cursor", "Windsurf", "Aider", "Antigravity"];
838
1080
  }
839
1081
  if (rootFiles.includes("package.json")) {
840
- const pkgPath = path9.join(this.cwd, "package.json");
841
- const pkg = await fs9.readJson(pkgPath);
1082
+ const pkgPath = path12.join(this.cwd, "package.json");
1083
+ const pkg = await fs12.readJson(pkgPath);
842
1084
  const allDeps = {
843
1085
  ...pkg.dependencies || {},
844
1086
  ...pkg.devDependencies || {}
@@ -859,8 +1101,8 @@ var ProjectScanner = class {
859
1101
  const composeFiles = ["compose.yml", "docker-compose.yml"];
860
1102
  for (const composeFile of composeFiles) {
861
1103
  if (rootFiles.includes(composeFile)) {
862
- const composePath = path9.join(this.cwd, composeFile);
863
- const composeContent = await fs9.readFile(composePath, "utf8");
1104
+ const composePath = path12.join(this.cwd, composeFile);
1105
+ const composeContent = await fs12.readFile(composePath, "utf8");
864
1106
  for (const sig of TECHNOLOGY_SIGNATURES) {
865
1107
  if (sig.regex && sig.regex.file === composeFile) {
866
1108
  if (sig.regex.pattern.test(composeContent)) {
@@ -894,7 +1136,7 @@ var ProjectScanner = class {
894
1136
 
895
1137
  // src/features/init/init.command.ts
896
1138
  var __filename = fileURLToPath(import.meta.url);
897
- var __dirname = path10.dirname(__filename);
1139
+ var __dirname = path13.dirname(__filename);
898
1140
  async function runInitCommand(options) {
899
1141
  const cwd = process.cwd();
900
1142
  const session = new SessionContext({
@@ -948,9 +1190,9 @@ async function runInitCommand(options) {
948
1190
  projectContext = manifestData.config.projectContext;
949
1191
  projectStack = manifestData.config.projectStack;
950
1192
  } else {
951
- const agentsMdPath = path10.join(cwd, "AGENTS.md");
952
- if (await fs10.pathExists(agentsMdPath)) {
953
- const content = await fs10.readFile(agentsMdPath, "utf8");
1193
+ const agentsMdPath = path13.join(cwd, "AGENTS.md");
1194
+ if (await fs13.pathExists(agentsMdPath)) {
1195
+ const content = await fs13.readFile(agentsMdPath, "utf8");
954
1196
  const targetsMatch = content.match(/\*\*Targets:\*\* (.*)/);
955
1197
  if (targetsMatch) aiTargets = targetsMatch[1].split(",").map((s) => s.trim());
956
1198
  const severityMatch = content.match(/\*\*Severity:\*\* (.*)/);
@@ -982,6 +1224,9 @@ async function runInitCommand(options) {
982
1224
  options: [
983
1225
  { value: "Cursor", label: "Cursor", hint: "Recomendado" },
984
1226
  { value: "Windsurf", label: "Windsurf" },
1227
+ { value: "Claude", label: "Claude Code" },
1228
+ { value: "Codex", label: "Codex" },
1229
+ { value: "Opencode", label: "Opencode" },
985
1230
  { value: "Aider", label: "Aider" },
986
1231
  { value: "Antigravity", label: "Antigravity", hint: "Agente nativo" }
987
1232
  ],
@@ -1024,9 +1269,9 @@ async function runInitCommand(options) {
1024
1269
  }
1025
1270
  }
1026
1271
  driver.startSpinner("Injetando o C\xE9rebro da IA no reposit\xF3rio...");
1027
- let templatesDir = path10.join(__dirname, "..", "templates");
1028
- if (!fs10.existsSync(templatesDir)) {
1029
- templatesDir = path10.join(__dirname, "..", "..", "..", "templates");
1272
+ let templatesDir = path13.join(__dirname, "..", "templates");
1273
+ if (!fs13.existsSync(templatesDir)) {
1274
+ templatesDir = path13.join(__dirname, "..", "..", "..", "templates");
1030
1275
  }
1031
1276
  const initService = new InitService();
1032
1277
  try {
@@ -1048,7 +1293,7 @@ async function runInitCommand(options) {
1048
1293
  success: true,
1049
1294
  message,
1050
1295
  payload: {
1051
- projectName: path10.basename(cwd),
1296
+ projectName: path13.basename(cwd),
1052
1297
  cwd,
1053
1298
  aiTargets,
1054
1299
  severity,
@@ -1066,16 +1311,16 @@ import pc3 from "picocolors";
1066
1311
  import * as p3 from "@clack/prompts";
1067
1312
 
1068
1313
  // src/features/doctor/doctor.service.ts
1069
- import fs11 from "fs-extra";
1070
- import path11 from "path";
1314
+ import fs14 from "fs-extra";
1315
+ import path14 from "path";
1071
1316
  var DoctorService = class {
1072
1317
  registryService = new RegistryService();
1073
1318
  async execute(cwd) {
1074
1319
  const issues = [];
1075
1320
  const workspaces = await this.registryService.listWorkspaces();
1076
- const normalizedCwd = path11.resolve(cwd).replace(/\\/g, "/");
1321
+ const normalizedCwd = path14.resolve(cwd).replace(/\\/g, "/");
1077
1322
  const registered = workspaces.find(
1078
- (w) => path11.resolve(w.rootPath).replace(/\\/g, "/") === normalizedCwd
1323
+ (w) => path14.resolve(w.rootPath).replace(/\\/g, "/") === normalizedCwd
1079
1324
  );
1080
1325
  if (!registered) {
1081
1326
  issues.push({
@@ -1093,8 +1338,8 @@ var DoctorService = class {
1093
1338
  });
1094
1339
  } else {
1095
1340
  for (const [relPath, expectedHash] of Object.entries(manifest.files)) {
1096
- const fullPath = path11.join(cwd, relPath);
1097
- if (!await fs11.pathExists(fullPath)) {
1341
+ const fullPath = path14.join(cwd, relPath);
1342
+ if (!await fs14.pathExists(fullPath)) {
1098
1343
  issues.push({
1099
1344
  severity: "error",
1100
1345
  message: `Arquivo gerenciado pelo manifesto est\xE1 ausente: ${relPath}`,
@@ -1103,7 +1348,7 @@ var DoctorService = class {
1103
1348
  });
1104
1349
  continue;
1105
1350
  }
1106
- const fileContent = await fs11.readFile(fullPath, "utf8");
1351
+ const fileContent = await fs14.readFile(fullPath, "utf8");
1107
1352
  const computedHash = generateHash(fileContent);
1108
1353
  if (computedHash !== expectedHash) {
1109
1354
  issues.push({
@@ -1115,8 +1360,8 @@ var DoctorService = class {
1115
1360
  }
1116
1361
  }
1117
1362
  }
1118
- const summaryPath = path11.join(cwd, ".sauron", "wiki", "summary.json");
1119
- if (!await fs11.pathExists(summaryPath)) {
1363
+ const summaryPath = path14.join(cwd, ".sauron", "wiki", "summary.json");
1364
+ if (!await fs14.pathExists(summaryPath)) {
1120
1365
  issues.push({
1121
1366
  severity: "error",
1122
1367
  message: "Arquivo de sum\xE1rio da wiki (.sauron/wiki/summary.json) est\xE1 ausente.",
@@ -1124,13 +1369,13 @@ var DoctorService = class {
1124
1369
  });
1125
1370
  } else {
1126
1371
  try {
1127
- const summary = await fs11.readJson(summaryPath);
1372
+ const summary = await fs14.readJson(summaryPath);
1128
1373
  if (Array.isArray(summary)) {
1129
1374
  for (const item of summary) {
1130
1375
  if (item.type === "file") {
1131
- const fileRelPath = path11.join(".sauron", "wiki", item.path).replace(/\\/g, "/");
1132
- const fileFullPath = path11.join(cwd, ".sauron", "wiki", item.path);
1133
- if (!await fs11.pathExists(fileFullPath)) {
1376
+ const fileRelPath = path14.join(".sauron", "wiki", item.path).replace(/\\/g, "/");
1377
+ const fileFullPath = path14.join(cwd, ".sauron", "wiki", item.path);
1378
+ if (!await fs14.pathExists(fileFullPath)) {
1134
1379
  issues.push({
1135
1380
  severity: "error",
1136
1381
  message: `Documento catalogado no sum\xE1rio est\xE1 ausente: ${fileRelPath}`,
@@ -1139,7 +1384,7 @@ var DoctorService = class {
1139
1384
  });
1140
1385
  continue;
1141
1386
  }
1142
- const content = await fs11.readFile(fileFullPath, "utf8");
1387
+ const content = await fs14.readFile(fileFullPath, "utf8");
1143
1388
  const computedHash = generateHash(content);
1144
1389
  const computedLen = Buffer.byteLength(content, "utf8");
1145
1390
  if (item.contentHash && computedHash !== item.contentHash) {
@@ -1177,37 +1422,41 @@ var DoctorService = class {
1177
1422
  }
1178
1423
  const targets = registered?.aiTargets || ["Cursor", "Windsurf", "Aider", "Antigravity"];
1179
1424
  for (const target of targets) {
1180
- const adapter = AdapterFactory.getAdapter(target);
1181
- if (adapter) {
1182
- const rulesFileMap = {
1183
- "Cursor": ".cursor/rules/sauron-memory.mdc",
1184
- "Windsurf": ".windsurfrules",
1185
- "Aider": ".aider.instructions.md",
1186
- "Antigravity": ".agents/rules/memory.md"
1187
- };
1188
- const relPath = rulesFileMap[target];
1189
- if (relPath) {
1190
- const fullPath = path11.join(cwd, relPath);
1191
- if (!await fs11.pathExists(fullPath)) {
1192
- issues.push({
1193
- severity: "error",
1194
- message: `Configura\xE7\xE3o do agente ${target} est\xE1 ausente ou foi deletada: ${relPath}`,
1195
- fix: `Rode sauron init para reinjetar a integra\xE7\xE3o com o ${target}.`
1196
- });
1197
- } else {
1198
- const rulesContent = await fs11.readFile(fullPath, "utf8");
1199
- const hasStart = rulesContent.includes("# SAURON START");
1200
- const hasEnd = rulesContent.includes("# SAURON END");
1201
- if (!hasStart || !hasEnd) {
1425
+ try {
1426
+ const adapters = AdapterRegistry.resolve([target]);
1427
+ if (adapters.length > 0) {
1428
+ const rulesFileMap = {
1429
+ "cursor": ".cursor/rules/sauron-memory.mdc",
1430
+ "windsurf": ".windsurf/rules/sauron-memory.md",
1431
+ "aider": ".sauron-aider-instructions.md",
1432
+ "antigravity": ".agents/rules/memory.md",
1433
+ "opencode": ".opencode/instructions/sauron-memory.md",
1434
+ "codex": ".codex/rules/sauron-memory.rules",
1435
+ "claude": ".claude/rules/sauron-base.md"
1436
+ };
1437
+ const relPath = rulesFileMap[target.toLowerCase().trim()];
1438
+ if (relPath) {
1439
+ const fullPath = path14.join(cwd, relPath);
1440
+ if (!await fs14.pathExists(fullPath)) {
1202
1441
  issues.push({
1203
1442
  severity: "error",
1204
- message: `Bloco de governan\xE7a do Sauron foi violado ou removido em: ${relPath}`,
1205
- file: relPath,
1206
- fix: `Rode sauron init para restaurar as diretrizes de compliance no arquivo do agente.`
1443
+ message: `Configura\xE7\xE3o do agente ${target} est\xE1 ausente ou foi deletada: ${relPath}`,
1444
+ fix: `Rode sauron init para reinjetar a integra\xE7\xE3o com o ${target}.`
1207
1445
  });
1446
+ } else {
1447
+ const rulesContent = await fs14.readFile(fullPath, "utf8");
1448
+ if (rulesContent.trim().length === 0) {
1449
+ issues.push({
1450
+ severity: "error",
1451
+ message: `Arquivo de governan\xE7a do Sauron est\xE1 vazio em: ${relPath}`,
1452
+ file: relPath,
1453
+ fix: `Rode sauron init para restaurar as diretrizes de compliance no arquivo do agente.`
1454
+ });
1455
+ }
1208
1456
  }
1209
1457
  }
1210
1458
  }
1459
+ } catch (e) {
1211
1460
  }
1212
1461
  }
1213
1462
  const hasErrors = issues.some((issue) => issue.severity === "error");
@@ -1277,8 +1526,8 @@ import pc4 from "picocolors";
1277
1526
  import * as p4 from "@clack/prompts";
1278
1527
 
1279
1528
  // src/features/uninstall/uninstall.service.ts
1280
- import fs12 from "fs-extra";
1281
- import path12 from "path";
1529
+ import fs15 from "fs-extra";
1530
+ import path15 from "path";
1282
1531
  var UninstallService = class {
1283
1532
  registryService = new RegistryService();
1284
1533
  async execute(options) {
@@ -1286,25 +1535,25 @@ var UninstallService = class {
1286
1535
  const removedPaths = [];
1287
1536
  await this.registryService.unregisterWorkspace(cwd);
1288
1537
  removedPaths.push("~/.sauron/registry.json (descadastrado)");
1289
- const adapters = AdapterFactory.getAllAdapters();
1538
+ const adapters = AdapterRegistry.getAll();
1290
1539
  for (const adapter of adapters) {
1291
1540
  const paths = await adapter.clean(cwd);
1292
1541
  removedPaths.push(...paths);
1293
1542
  }
1294
- const agentsMdPath = path12.join(cwd, "AGENTS.md");
1295
- if (await fs12.pathExists(agentsMdPath)) {
1296
- await fs12.remove(agentsMdPath);
1543
+ const agentsMdPath = path15.join(cwd, "AGENTS.md");
1544
+ if (await fs15.pathExists(agentsMdPath)) {
1545
+ await fs15.remove(agentsMdPath);
1297
1546
  removedPaths.push("AGENTS.md");
1298
1547
  }
1299
- const sauronDir = path12.join(cwd, ".sauron");
1300
- if (await fs12.pathExists(sauronDir)) {
1548
+ const sauronDir = path15.join(cwd, ".sauron");
1549
+ if (await fs15.pathExists(sauronDir)) {
1301
1550
  if (purge) {
1302
- await fs12.remove(sauronDir);
1551
+ await fs15.remove(sauronDir);
1303
1552
  removedPaths.push(".sauron/ (purgado por completo)");
1304
1553
  } else {
1305
- const manifestPath = path12.join(sauronDir, ".manifest.json");
1306
- if (await fs12.pathExists(manifestPath)) {
1307
- await fs12.remove(manifestPath);
1554
+ const manifestPath = path15.join(sauronDir, ".manifest.json");
1555
+ if (await fs15.pathExists(manifestPath)) {
1556
+ await fs15.remove(manifestPath);
1308
1557
  removedPaths.push(".sauron/.manifest.json");
1309
1558
  }
1310
1559
  removedPaths.push(".sauron/wiki/ (preservado como base est\xE1tica)");