fidelizare-integrate 0.4.0 → 0.5.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fidelizare-integrate",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Asistent de integrare Fidelizare pentru softuri de gestiune si case de marcat. Scaneaza codul, gaseste locul potrivit si propune integrarea API-ului, in siguranta.",
5
5
  "type": "module",
6
6
  "bin": {
package/src/cli.js CHANGED
@@ -87,9 +87,15 @@ async function main() {
87
87
 
88
88
  const git = gitState(root);
89
89
 
90
- // 5a. Agentic tool-loop (real coding agent): the model uses tools and
91
- // applies the integration itself, each write gated by confirmation.
92
- if (has('--agent') && (process.env.OPENROUTER_API_KEY || process.env.FIDELIZARE_LLM_KEY)) {
90
+ // 5a. Agentic tool-loop (real coding agent) the DEFAULT when an LLM key is
91
+ // available. The model finds the files and applies the integration itself,
92
+ // each write gated. `--no-agent` forces the basic deterministic proposer.
93
+ const llmKey = process.env.OPENROUTER_API_KEY || process.env.FIDELIZARE_LLM_KEY;
94
+ if (!has('--no-agent') && !llmKey) {
95
+ note('Pentru integrare cu AI (recomandat), seteaza OPENROUTER_API_KEY. Acum folosesc modul determinist de baza.', C.amber);
96
+ rule();
97
+ }
98
+ if (!has('--no-agent') && llmKey) {
93
99
  out(' ' + paint('Agent ', C.ink) + bold(paint(val('--model') || 'Opus 4.8', C.red)) +
94
100
  paint(' · scrierile cer confirmare (y / a / N)', C.dim) + '\n\n');
95
101
  const r = await runAgentLoop({
@@ -1,6 +1,6 @@
1
1
  // Detect the vendor's stack from manifest/extension signals. Read-only.
2
- import { readdirSync, existsSync, statSync } from 'fs';
3
- import { join, extname } from 'path';
2
+ import { readdirSync, existsSync, statSync, readFileSync } from 'fs';
3
+ import { join, extname, basename } from 'path';
4
4
 
5
5
  const MANIFESTS = [
6
6
  { file: 'composer.json', stack: 'php', label: 'PHP' },
@@ -28,10 +28,47 @@ const EXT_STACK = {
28
28
  '.go': { stack: 'go', label: 'Go' },
29
29
  '.rs': { stack: 'rust', label: 'Rust' },
30
30
  };
31
- const IGNORE = new Set(['node_modules', '.git', 'dist', 'build', 'bin', 'obj', 'vendor', '.next', '__pycache__']);
31
+ // Build artifacts, dependencies and tooling dirs never integration targets.
32
+ // Mirrors what ripgrep skips via .gitignore (opencode uses ripgrep).
33
+ const IGNORE = new Set([
34
+ 'node_modules', '.git', '.next', '.nuxt', '.svelte-kit', '.parcel-cache', '.turbo', '.cache',
35
+ 'dist', 'build', 'out', 'bin', 'obj', 'target', 'vendor', 'coverage', '__pycache__',
36
+ '.venv', 'venv', 'env', '.idea', '.vscode', '.gradle', 'static', 'tmp', '.expo',
37
+ ]);
38
+ // Minified / generated bundles by filename.
39
+ const MINIFIED_NAME = /(\.min\.|\.bundle\.|\.chunk\.|[-_.]esm[-_.]|node_modules_|\.d\.ts$|\.map$)/i;
40
+
41
+ function loadGitignoreNames(dir) {
42
+ // Cheap .gitignore support: collect simple directory/file names to skip.
43
+ const names = new Set();
44
+ try {
45
+ for (const line of readFileSync(join(dir, '.gitignore'), 'utf8').split('\n')) {
46
+ const t = line.trim();
47
+ if (!t || t.startsWith('#') || t.startsWith('!')) continue;
48
+ const name = t.replace(/^\/+/, '').replace(/\/+$/, '');
49
+ if (name && !name.includes('/') && !name.includes('*')) names.add(name);
50
+ }
51
+ } catch { /* no .gitignore */ }
52
+ return names;
53
+ }
54
+
55
+ // A real source file looks hand-written: not minified, not huge, sane line length.
56
+ function looksLikeSource(file) {
57
+ if (MINIFIED_NAME.test(basename(file))) return false;
58
+ try {
59
+ if (statSync(file).size > 200_000) return false;
60
+ const head = readFileSync(file, 'utf8').slice(0, 4000);
61
+ if (!head.includes('\n') && head.length > 1200) return false; // single huge line
62
+ const lines = head.split('\n').slice(0, 40);
63
+ const avg = lines.reduce((s, l) => s + l.length, 0) / Math.max(1, lines.length);
64
+ if (avg > 400) return false; // minified
65
+ } catch { return false; }
66
+ return true;
67
+ }
32
68
 
33
69
  export function listSourceFiles(dir, max = 4000) {
34
70
  const out = [];
71
+ const ignored = new Set([...IGNORE, ...loadGitignoreNames(dir)]);
35
72
  const walk = (d) => {
36
73
  if (out.length >= max) return;
37
74
  let entries;
@@ -39,10 +76,10 @@ export function listSourceFiles(dir, max = 4000) {
39
76
  for (const e of entries) {
40
77
  if (out.length >= max) return;
41
78
  if (e.name.startsWith('.') && e.name !== '.') continue;
42
- if (IGNORE.has(e.name)) continue;
79
+ if (ignored.has(e.name)) continue;
43
80
  const p = join(d, e.name);
44
81
  if (e.isDirectory()) walk(p);
45
- else if (EXT_STACK[extname(e.name).toLowerCase()]) out.push(p);
82
+ else if (EXT_STACK[extname(e.name).toLowerCase()] && looksLikeSource(p)) out.push(p);
46
83
  }
47
84
  };
48
85
  walk(dir);