@futdevpro/fdp-agent-memory 0.1.0 → 1.1.12

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 (100) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +7 -7
  3. package/build/package.json +6 -5
  4. package/build/src/_cli/_collections/fam-arg.util.js +48 -0
  5. package/build/src/_cli/_collections/fam-cli.const.js +40 -0
  6. package/build/src/_cli/_collections/fam-output.util.js +86 -0
  7. package/build/src/_cli/_collections/fam-project-discovery.util.js +98 -0
  8. package/build/src/_cli/_commands/capture.command.js +73 -0
  9. package/build/src/_cli/_commands/config.command.js +93 -0
  10. package/build/src/_cli/_commands/doctor.command.js +124 -0
  11. package/build/src/_cli/_commands/errors.command.js +66 -0
  12. package/build/src/_cli/_commands/export.command.js +65 -0
  13. package/build/src/_cli/_commands/find-duplicates.command.js +97 -0
  14. package/build/src/_cli/_commands/import.command.js +136 -0
  15. package/build/src/_cli/_commands/init.command.js +147 -0
  16. package/build/src/_cli/_commands/read.command.js +109 -0
  17. package/build/src/_cli/_commands/scan-projects.command.js +138 -0
  18. package/build/src/_cli/_commands/scan.command.js +98 -0
  19. package/build/src/_cli/_commands/seed.command.js +40 -0
  20. package/build/src/_cli/_commands/serve.command.js +350 -0
  21. package/build/src/_cli/_commands/start.command.js +134 -0
  22. package/build/src/_cli/_commands/stats.command.js +54 -0
  23. package/build/src/_cli/_commands/write.command.js +103 -0
  24. package/build/src/_cli/_models/interfaces/fam-cli-global-options.interface.js +2 -0
  25. package/build/src/_cli/_models/interfaces/fam-cli-output.interface.js +9 -0
  26. package/build/src/_cli/_models/interfaces/fam-client-result.interface.js +2 -0
  27. package/build/src/_cli/_services/fam-client.service.js +140 -0
  28. package/build/src/_cli/register-commands.js +86 -0
  29. package/build/src/_collections/config-catalog.const.js +67 -1
  30. package/build/src/_collections/fam-console.util.js +367 -0
  31. package/build/src/_collections/fam-entry-bootstrap.util.js +158 -4
  32. package/build/src/_collections/fam-error-factory.util.js +0 -9
  33. package/build/src/_collections/fam-mcp-bridge.util.js +49 -0
  34. package/build/src/_collections/fam-reference-code.util.js +105 -0
  35. package/build/src/_collections/fam-version.const.js +10 -0
  36. package/build/src/_models/data-models/fam-entry-base-properties.const.js +1 -0
  37. package/build/src/_models/data-models/fam-entry.data-model.js +6 -0
  38. package/build/src/_models/data-models/fam-ingest-run.data-model.js +3 -1
  39. package/build/src/_models/data-models/fam-reference.data-model.js +7 -0
  40. package/build/src/_modules/capture/_collections/fam-capture.const.js +11 -0
  41. package/build/src/_modules/capture/_services/fam-auto-capture.control-service.js +87 -0
  42. package/build/src/_modules/capture/index.js +8 -0
  43. package/build/src/_modules/embedding/_collections/fam-embedding-prefix.util.js +77 -0
  44. package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +202 -0
  45. package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +33 -9
  46. package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +21 -2
  47. package/build/src/_modules/embedding/_services/fam-entry.data-service.js +135 -0
  48. package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +42 -32
  49. package/build/src/_modules/embedding/index.js +4 -1
  50. package/build/src/_modules/export/_collections/fam-export.const.js +22 -0
  51. package/build/src/_modules/export/_services/fam-export.control-service.js +64 -0
  52. package/build/src/_modules/export/index.js +8 -0
  53. package/build/src/_modules/ingest/_collections/fam-famignore.util.js +83 -0
  54. package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +59 -48
  55. package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +134 -0
  56. package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +57 -0
  57. package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +60 -0
  58. package/build/src/_modules/ingest/_collections/fam-scan-weight.util.js +53 -0
  59. package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +37 -14
  60. package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +34 -0
  61. package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +200 -14
  62. package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +4 -1
  63. package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +7 -4
  64. package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +346 -17
  65. package/build/src/_modules/ingest/_services/fam-scan.control-service.js +25 -2
  66. package/build/src/_modules/ingest/index.js +3 -1
  67. package/build/src/_modules/mcp/_collections/fam-active-rules.util.js +56 -0
  68. package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +47 -6
  69. package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +4 -4
  70. package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +224 -18
  71. package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +4 -4
  72. package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +4 -4
  73. package/build/src/_modules/mcp/_services/fam-read-tool.service.js +53 -1
  74. package/build/src/_modules/mcp/_services/fam-write-tool.service.js +104 -8
  75. package/build/src/_modules/mcp/index.js +4 -4
  76. package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +66 -3
  77. package/build/src/_modules/migration/_collections/fam-prompt-aggregate.util.js +143 -0
  78. package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +19 -0
  79. package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +6 -0
  80. package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +5 -0
  81. package/build/src/_modules/migration/_services/fam-agent-memory-reader.service.js +125 -0
  82. package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +101 -18
  83. package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +53 -0
  84. package/build/src/_modules/migration/index.js +3 -1
  85. package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +78 -4
  86. package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +293 -50
  87. package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +6 -3
  88. package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +18 -0
  89. package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +79 -20
  90. package/build/src/_routes/server/api/api.controller.js +34 -2
  91. package/build/src/_routes/server/client-app/client-app.control-service.js +1 -1
  92. package/build/src/_routes/server/server-status/server-status.controller.js +2 -1
  93. package/build/src/app.server.js +13 -1
  94. package/build/src/environments/environment.js +1 -1
  95. package/build/src/index.js +1 -1
  96. package/client-dist/{chunk-GHKRM4SM.js → chunk-I77GXVAQ.js} +1 -1
  97. package/client-dist/{chunk-LMTL7GA3.js → chunk-YXHWCJ5O.js} +1 -1
  98. package/client-dist/index.html +1 -1
  99. package/client-dist/{main-2KWB3QYK.js → main-PJPEDVJT.js} +1 -1
  100. package/package.json +6 -5
@@ -9,77 +9,88 @@ const fam_table_type_enum_1 = require("../../../_enums/fam-table.type-enum");
9
9
  * felderített fájlt a kiterjesztése / fájlnév-mintája alapján `documents` / `codebase` / `skip`
10
10
  * cél-tárra route-ol. Statikus util (nincs DB-/service-függés).
11
11
  *
12
- * Routing-térkép (dsgn-004 §2):
13
- * - `documents` `.md` `.mdx` `.txt` `.rst`
14
- * - `codebase` ← `.html` `.ts` `.tsx` `.js` `.jsx` `.py` `.css` `.scss` `.json` + config-fájlok
15
- * (`*.config.*`, `tsconfig*.json`, `package.json`, `.eslintrc*`, `.prettierrc*`,
16
- * `*.yml`/`*.yaml`/`*.toml`/`*.ini`, `Dockerfile`)
17
- * - `skip` minden egyéb (bináris, kép, lock-fájl, stb.) verdikt nélkül, `filesSkipped`-ben.
18
- *
19
- * **A `.html` markup KIZÁRÓLAG `codebase`** (NEM `documents`) a `documents` route a tartalmi
20
- * dokumentációt célozza (`.md`/`.mdx`/`.txt`/`.rst`), nem a markup-ot (dsgn-004 §2). A
21
- * `chunkStrategyFor` a route + kiterjesztés alapján adja a chunker-stratégiát (SP-4.2 dispatch).
12
+ * **FAM-REV-064 — „semmi ne vesszen el" inverzió:** a korábbi szűk ALLOWLIST (csak ismert kiterjesztés
13
+ * → tár, MINDEN MÁS → skip) szöveges, info-hordozó fájlokat ejtett el (pl. `.mdc` Cursor-rules, `.sh`,
14
+ * `.ps1`, `.xml`, git-hookok, `LICENSE`). Az új logika DENYLIST-alapú: a **dokumentum-kiterjesztések** a
15
+ * `documents` táron; a **known-binary** (kép/font/archív/média/bináris/office) + a **generált zaj** (lock-
16
+ * fájlok, `.min.*`, source-map) `skip`; **MINDEN MÁS szöveges fájl** (ismert VAGY ismeretlen kiterjesztés,
17
+ * extension-nélküli `LICENSE`/`Dockerfile`/git-hook) → `codebase`. A bináris-tartalmú álcázott fájlokat a
18
+ * scanner null-byte tartalom-ellenőrzése szűri (`binary-content` skip), így nem kerül szemét a generic
19
+ * chunkerbe. Így minden valódi szöveg bekerül; a tényleges bináris + a generált zaj kimarad.
22
20
  */
23
21
  class FAM_FileRouting_Util {
24
- /** Dokumentum-kiterjesztések → `documents` (dsgn-004 §2). */
25
- static DOCUMENT_EXTENSIONS = ['.md', '.mdx', '.txt', '.rst'];
26
- /** Kód-/markup-kiterjesztések `codebase` (a `.html` markup IDE tartozik, dsgn-004 §2). */
27
- static CODE_EXTENSIONS = [
28
- '.html', '.ts', '.tsx', '.mts', '.js', '.jsx', '.py', '.css', '.scss', '.json',
29
- '.yml', '.yaml', '.toml', '.ini',
30
- ];
31
- /** Markdown-kiterjesztések (a header-aware chunker célja, dsgn-004 §3.2). */
32
- static MARKDOWN_EXTENSIONS = ['.md', '.mdx'];
33
- /** TS-szemantikus chunker kiterjesztései (dsgn-004 §3.1). */
34
- static TS_EXTENSIONS = ['.ts', '.tsx', '.mts', '.js', '.jsx'];
35
- /** Extension-nélküli config-fájlnevek → `codebase` (dsgn-004 §2). */
36
- static CONFIG_FILENAMES = ['dockerfile'];
22
+ /** Dokumentum-kiterjesztések → `documents` (a `.mdc` Cursor-rules + `.markdown` IDE, FAM-REV-064). */
23
+ static DOCUMENT_EXTENSIONS = ['.md', '.mdx', '.markdown', '.mdc', '.txt', '.text', '.rst', '.adoc'];
24
+ /** Markdown-kiterjesztések (a header-aware chunker célja; a `.mdc`/`.markdown` is, FAM-REV-064). */
25
+ static MARKDOWN_EXTENSIONS = ['.md', '.mdx', '.markdown', '.mdc'];
26
+ /** TS-szemantikus chunker kiterjesztései (dsgn-004 §3.1; `.cjs`/`.cts`/`.mjs` is JS/TS-variáns). */
27
+ static TS_EXTENSIONS = ['.ts', '.tsx', '.mts', '.cts', '.js', '.jsx', '.cjs', '.mjs'];
28
+ /**
29
+ * Known-binary kiterjesztések `skip` (FAM-REV-064 denylist). Kép/font/archív/média/futtatható/
30
+ * lefordított/office/adatbázis + source-map (`.map`). Az office/PDF szöveg-kinyerés BACKLOG (dsgn BACKLOG),
31
+ * ezért egyelőre skip (nem néma — `filesSkipped` + `skipReason`). NEM tartalmazza a `.svg`-t? — DE: a
32
+ * `.svg` döntően generált path-grafika (alacsony szemantikai szöveg), ezért IDE soroljuk.
33
+ */
34
+ static BINARY_EXTENSIONS = new Set([
35
+ // képek
36
+ '.png', '.jpg', '.jpeg', '.gif', '.bmp', '.ico', '.webp', '.tiff', '.tif', '.svg', '.psd', '.ai',
37
+ // fontok
38
+ '.woff', '.woff2', '.ttf', '.eot', '.otf',
39
+ // archívok
40
+ '.zip', '.tgz', '.gz', '.tar', '.rar', '.7z', '.bz2', '.xz', '.jar', '.war', '.nupkg',
41
+ // média
42
+ '.mp3', '.mp4', '.wav', '.avi', '.mov', '.mkv', '.flac', '.ogg', '.webm', '.m4a', '.aac',
43
+ // futtatható / lefordított
44
+ '.exe', '.dll', '.so', '.dylib', '.bin', '.o', '.a', '.lib', '.node', '.wasm', '.class', '.pyc', '.pyo',
45
+ // office / PDF (szöveg-kinyerés BACKLOG)
46
+ '.pdf', '.doc', '.docx', '.xls', '.xlsx', '.ppt', '.pptx', '.odt', '.ods',
47
+ // adatbázis / egyéb bináris
48
+ '.db', '.sqlite', '.sqlite3', '.mdb', '.dat', '.map',
49
+ ]);
37
50
  /**
38
- * Egy fájl route-verdiktje (dsgn-004 §2). A kiterjesztés-térkép, majd a config-fájlnév-minták
39
- * (extension-nélküli `Dockerfile`, `*.config.*`, `tsconfig*.json`, `.eslintrc*`, `.prettierrc*`)
40
- * → `codebase`; egyébként `skip`. A vizsgálat case-insensitive a kiterjesztésre/fájlnévre.
51
+ * Egy fájl route-verdiktje (FAM-REV-064 denylist-inverzió). Sorrend: (1) generált-zaj (lock/`.min.`/
52
+ * source-map) → skip; (2) dokumentum-kiterjesztés `documents`; (3) known-binary skip; (4) MINDEN MÁS
53
+ * (bármilyen szöveg, ismeretlen kiterjesztés, extension-nélküli) → `codebase`. Case-insensitive.
41
54
  */
42
55
  static route(relativePath) {
43
56
  const fileName = path.basename(relativePath).toLowerCase();
44
57
  const extension = path.extname(fileName);
58
+ if (FAM_FileRouting_Util.isGeneratedNoise(fileName)) {
59
+ return 'skip';
60
+ }
45
61
  if (FAM_FileRouting_Util.DOCUMENT_EXTENSIONS.includes(extension)) {
46
62
  return fam_table_type_enum_1.FAM_Table.documents;
47
63
  }
48
- if (FAM_FileRouting_Util.CODE_EXTENSIONS.includes(extension)) {
49
- return fam_table_type_enum_1.FAM_Table.codebase;
50
- }
51
- if (FAM_FileRouting_Util.isConfigFile(fileName)) {
52
- return fam_table_type_enum_1.FAM_Table.codebase;
64
+ if (FAM_FileRouting_Util.BINARY_EXTENSIONS.has(extension)) {
65
+ return 'skip';
53
66
  }
54
- return 'skip';
67
+ // Minden egyéb (ismert kód, ismeretlen-de-szöveges, extension-nélküli LICENSE/Dockerfile/git-hook) →
68
+ // codebase. A bináris-tartalmú álcázott fájlt a scanner null-byte tartalom-ellenőrzése szűri.
69
+ return fam_table_type_enum_1.FAM_Table.codebase;
55
70
  }
56
71
  /**
57
- * Config-fájl-e a fájlnév (dsgn-004 §2): extension-nélküli ismert config (`Dockerfile`) VAGY
58
- * a `*.config.*` / `tsconfig*.json` / `package.json` / `.eslintrc*` / `.prettierrc*` minta.
72
+ * Generált / zaj fájl-e (FAM-REV-064): a lock-fájlok (pnpm-lock.yaml / package-lock.json / yarn.lock /
73
+ * `*.lock`), a minifikált bundle-k (`.min.js`/`.min.css`) ezek hatalmasak + generáltak, NEM info-
74
+ * hordozó forrás, ezért `skip` (NEM vesztünk valódi információt; a forrás `.ts`/`.scss` megvan).
59
75
  */
60
- static isConfigFile(fileName) {
61
- if (FAM_FileRouting_Util.CONFIG_FILENAMES.includes(fileName)) {
62
- return true;
63
- }
64
- if (fileName === 'package.json' || fileName === 'package-lock.json') {
65
- return true;
66
- }
67
- if (/^tsconfig.*\.json$/.test(fileName)) {
76
+ static isGeneratedNoise(fileName) {
77
+ if (fileName === 'pnpm-lock.yaml' || fileName === 'package-lock.json' || fileName === 'yarn.lock'
78
+ || fileName === 'bun.lockb' || fileName === 'composer.lock') {
68
79
  return true;
69
80
  }
70
- if (/\.config\./.test(fileName)) {
81
+ if (fileName.endsWith('.lock')) {
71
82
  return true;
72
83
  }
73
- if (/^\.eslintrc/.test(fileName) || /^\.prettierrc/.test(fileName)) {
84
+ if (/\.min\.(js|css|mjs)$/.test(fileName)) {
74
85
  return true;
75
86
  }
76
87
  return false;
77
88
  }
78
89
  /**
79
- * A chunker-stratégia kulcsa egy fájlhoz (SP-4.2 dispatch, dsgn-004 §3). `ts` a TS-szemantikus
80
- * (.ts/.tsx/.mts/.js/.jsx), `md` a markdown-header-aware (.md/.mdx), `generic` minden más
81
- * `documents`/`codebase` fájlra (config/JSON/HTML-markup/.txt/.rst). A `route` előfeltétel:
82
- * `skip` fájlra NEM hívandó.
90
+ * A chunker-stratégia kulcsa egy fájlhoz (SP-4.2 dispatch, dsgn-004 §3). `ts` a TS/JS-szemantikus,
91
+ * `md` a markdown-header-aware (`.md`/`.mdx`/`.markdown`/`.mdc`), `generic` minden más `documents`/
92
+ * `codebase` fájlra (config/JSON/HTML/`.txt`/`.sh`/`.ps1`/`.xml`/ismeretlen-szöveg). A `route`
93
+ * előfeltétel: `skip` fájlra NEM hívandó.
83
94
  */
84
95
  static chunkStrategyFor(relativePath) {
85
96
  const extension = path.extname(path.basename(relativePath).toLowerCase());
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_ProjectIdentity_Util = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const fs = tslib_1.__importStar(require("fs"));
6
+ const path = tslib_1.__importStar(require("path"));
7
+ /**
8
+ * `FAM_ProjectIdentity_Util` — egy scan-gyökér **projekt-identitásának** felismerése (git + `package.json`).
9
+ * A scan ebből (a) egy kereshető `knowledge` „project-identity" entry-t készít (név/verzió/leírás/függőségek/
10
+ * git-repo), és (b) reference-aliasokat (a package-név + rövid alakok → a projekt scope-ja). A parse-rész PURE
11
+ * (önállóan tesztelhető), a `detect` az IO-wrapper (a `.git`/`package.json` olvasása — opt-in: hiányzó fájl no-op).
12
+ */
13
+ class FAM_ProjectIdentity_Util {
14
+ /** A generált identity-entry `kind`-ja. */
15
+ static KIND = 'project-identity';
16
+ /** A `package.json`-ból kinyert mezők (PURE — érvénytelen JSON → üres). */
17
+ static parsePackageJson(content) {
18
+ try {
19
+ const parsed = JSON.parse(content);
20
+ const deps = [
21
+ ...Object.keys(parsed.dependencies ?? {}),
22
+ ...Object.keys(parsed.peerDependencies ?? {}),
23
+ ];
24
+ return {
25
+ packageName: typeof parsed.name === 'string' ? parsed.name : undefined,
26
+ version: typeof parsed.version === 'string' ? parsed.version : undefined,
27
+ description: typeof parsed.description === 'string' ? parsed.description : undefined,
28
+ dependencies: Array.from(new Set(deps)).sort(),
29
+ };
30
+ }
31
+ catch {
32
+ return { dependencies: [] };
33
+ }
34
+ }
35
+ /** A git `origin` remote URL-je a `.git/config` tartalmából (PURE; nincs origin → undefined). */
36
+ static parseGitRemote(gitConfigContent) {
37
+ const lines = gitConfigContent.split(/\r?\n/);
38
+ let inOrigin = false;
39
+ for (const rawLine of lines) {
40
+ const line = rawLine.trim();
41
+ if (line.startsWith('[remote ')) {
42
+ inOrigin = line.includes('"origin"');
43
+ continue;
44
+ }
45
+ if (inOrigin && line.startsWith('url')) {
46
+ const eq = line.indexOf('=');
47
+ if (eq >= 0) {
48
+ return line.slice(eq + 1).trim();
49
+ }
50
+ }
51
+ }
52
+ return undefined;
53
+ }
54
+ /** Egy git-remote-URL → `org/repo` rövid alak (`.git` levágva; SSH/HTTPS egyaránt; PURE). Hiba → undefined. */
55
+ static shortRepo(remoteUrl) {
56
+ if (!remoteUrl) {
57
+ return undefined;
58
+ }
59
+ // git@host:org/repo.git VAGY https://host/org/repo.git
60
+ const afterHost = remoteUrl.includes(':') && !remoteUrl.startsWith('http')
61
+ ? remoteUrl.slice(remoteUrl.lastIndexOf(':') + 1)
62
+ : remoteUrl.replace(/^https?:\/\/[^/]+\//, '');
63
+ const cleaned = afterHost.replace(/\.git$/, '').replace(/^\/+/, '');
64
+ const segments = cleaned.split('/').filter(Boolean);
65
+ return segments.length >= 2 ? segments.slice(-2).join('/') : (segments[0] || undefined);
66
+ }
67
+ /** A projekt-identitás felismerése egy scan-gyökérből (IO: `package.json` + `.git/config`). Hiányzó → részleges. */
68
+ static detect(rootAbsolutePath) {
69
+ const identity = { isGit: false, dependencies: [] };
70
+ try {
71
+ const pkgPath = path.join(rootAbsolutePath, 'package.json');
72
+ if (fs.existsSync(pkgPath)) {
73
+ const parsed = FAM_ProjectIdentity_Util.parsePackageJson(fs.readFileSync(pkgPath, 'utf-8'));
74
+ identity.packageName = parsed.packageName;
75
+ identity.version = parsed.version;
76
+ identity.description = parsed.description;
77
+ identity.dependencies = parsed.dependencies;
78
+ }
79
+ const gitDir = path.join(rootAbsolutePath, '.git');
80
+ if (fs.existsSync(gitDir)) {
81
+ identity.isGit = true;
82
+ const configPath = path.join(gitDir, 'config');
83
+ if (fs.existsSync(configPath)) {
84
+ identity.gitRemote = FAM_ProjectIdentity_Util.parseGitRemote(fs.readFileSync(configPath, 'utf-8'));
85
+ identity.gitRepo = FAM_ProjectIdentity_Util.shortRepo(identity.gitRemote);
86
+ }
87
+ }
88
+ }
89
+ catch {
90
+ // best-effort: az identitás-olvasás hibája NEM buktatja a scant (a részleges identity visszaadva).
91
+ }
92
+ return identity;
93
+ }
94
+ /** Van-e bármi érdemi identitás (package.json VAGY git) — különben nincs értelme entry-t generálni. */
95
+ static hasIdentity(identity) {
96
+ return Boolean(identity.packageName || identity.isGit);
97
+ }
98
+ /** A kereshető `knowledge` „project-identity" entry szövege (PURE). */
99
+ static buildContent(identity, scopeLabel, root) {
100
+ const lines = [];
101
+ lines.push(`Projekt-identitás — ${scopeLabel}`);
102
+ lines.push(`Gyökér: ${root}`);
103
+ if (identity.packageName) {
104
+ lines.push(`Csomag: ${identity.packageName}${identity.version ? ` v${identity.version}` : ''}`);
105
+ }
106
+ lines.push(`Git-projekt: ${identity.isGit ? 'igen' : 'nem'}${identity.gitRepo ? ` (repo: ${identity.gitRepo})` : ''}`);
107
+ if (identity.description) {
108
+ lines.push(`Leírás: ${identity.description}`);
109
+ }
110
+ if (identity.dependencies.length) {
111
+ lines.push(`Függőségek (${identity.dependencies.length}): ${identity.dependencies.join(', ')}`);
112
+ }
113
+ return lines.join('\n');
114
+ }
115
+ /** A reference-aliasok (a package-név + rövid alakok + repo + dir-név → a projekt scope-ja). Dedup, üresek nélkül (PURE). */
116
+ static buildAliases(identity, dirName, scopeCanonical) {
117
+ const aliases = [dirName, scopeCanonical];
118
+ if (identity.packageName) {
119
+ aliases.push(identity.packageName);
120
+ const bare = identity.packageName.replace(/^@[^/]+\//, ''); // @futdevpro/fsm-dynamo → fsm-dynamo
121
+ aliases.push(bare);
122
+ const firstSegment = bare.split('-')[0]; // fsm-dynamo → fsm
123
+ if (firstSegment && firstSegment !== bare) {
124
+ aliases.push(firstSegment);
125
+ }
126
+ }
127
+ if (identity.gitRepo) {
128
+ aliases.push(identity.gitRepo);
129
+ aliases.push(identity.gitRepo.split('/').pop() ?? '');
130
+ }
131
+ return Array.from(new Set(aliases.map((alias) => alias.trim()).filter(Boolean)));
132
+ }
133
+ }
134
+ exports.FAM_ProjectIdentity_Util = FAM_ProjectIdentity_Util;
@@ -0,0 +1,57 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_ScanProgress_Util = void 0;
4
+ /**
5
+ * `FAM_ScanProgress_Util` (descriptive user feedback) — a scan **pre-becslés** + **futás-közbeni progress** PURE
6
+ * (DB-/provider-mentes) magja. A nagy, workspace-szintű scanhez: a hívó ELŐRE lássa a becsült időt, és FUTÁS
7
+ * közben kövesse a haladást (X/N fájl, %, chunk, verdikt-bontás, eltelt + ETA). Statikus util — a control-service
8
+ * a delta-számokból + az óra-időből táplálja; a formázás/ETA-matek itt izoláltan tesztelhető.
9
+ */
10
+ class FAM_ScanProgress_Util {
11
+ /**
12
+ * Pre-scan idő-becslés az embeddelendő chunk-számból + a per-chunk-ms-ből. `estimatedSeconds = chunks×ms/1000`.
13
+ * Durva (a tényleges sebesség gép-/modell-függő); a futó scan ETA-ja pontosítja.
14
+ */
15
+ static estimate(set) {
16
+ const estimatedSeconds = Math.round((set.chunksToEmbed * set.msPerChunk) / 1000);
17
+ return {
18
+ chunksToEmbed: set.chunksToEmbed,
19
+ msPerChunk: set.msPerChunk,
20
+ estimatedSeconds: estimatedSeconds,
21
+ note: `~${FAM_ScanProgress_Util.formatDuration(estimatedSeconds)} embeddelés (${set.chunksToEmbed} chunk × `
22
+ + `${set.msPerChunk}ms — DURVA becslés, gép-/modell-függő; a futó scan ETA-ja pontosítja)`,
23
+ };
24
+ }
25
+ /** Másodperc → ember-olvasható (`45s` / `3m 20s` / `1h 5m`). Negatív/0 → `0s`. */
26
+ static formatDuration(totalSeconds) {
27
+ const s = Math.max(0, Math.round(totalSeconds));
28
+ if (s < 60) {
29
+ return `${s}s`;
30
+ }
31
+ const minutes = Math.floor(s / 60);
32
+ const remSec = s % 60;
33
+ if (minutes < 60) {
34
+ return remSec ? `${minutes}m ${remSec}s` : `${minutes}m`;
35
+ }
36
+ const hours = Math.floor(minutes / 60);
37
+ const remMin = minutes % 60;
38
+ return remMin ? `${hours}h ${remMin}m` : `${hours}h`;
39
+ }
40
+ /**
41
+ * Egy futás-közbeni progress-sor: `processed/total` fájl + %, chunk-szám, verdikt-bontás, eltelt idő + ETA.
42
+ * Az ETA a MÉRT sebességből (`processed / elapsed`) → a hátralévő fájlok ideje (csak ha van haladás + maradék).
43
+ * `[famIngest]` prefix → az activity-monitor allowlistjén keresztül megjelenik a konzol-ablakban.
44
+ */
45
+ static progressLine(set) {
46
+ const pct = set.total ? Math.round((set.processed / set.total) * 100) : 100;
47
+ const rate = set.processed / Math.max(set.elapsedSeconds, 0.001);
48
+ const remaining = set.total - set.processed;
49
+ const eta = rate > 0 && remaining > 0
50
+ ? `, ETA ~${FAM_ScanProgress_Util.formatDuration(remaining / rate)}`
51
+ : '';
52
+ return `[famIngest] feldolgozás ${set.processed}/${set.total} fájl (${pct}%), ${set.chunkCount} chunk, `
53
+ + `${set.verdicts.new} új / ${set.verdicts.modified} mód / ${set.verdicts.equal} vált., `
54
+ + `eltelt ${FAM_ScanProgress_Util.formatDuration(set.elapsedSeconds)}${eta}`;
55
+ }
56
+ }
57
+ exports.FAM_ScanProgress_Util = FAM_ScanProgress_Util;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_ScanSummary_Util = void 0;
4
+ /**
5
+ * `FAM_ScanSummary_Util` (file-rendszer összefoglaló) — a scan által GENERÁLT, kereshető „mi van a projektben"
6
+ * összefoglaló PURE építője. A scan után egy `knowledge`-entry készül a mappastruktúrából (mappák + fájlok
7
+ * felsorolása), hogy az agent lekérdezhesse a projekt/mappa tartalmát (NEM kell végigscannelnie). Statikus util.
8
+ *
9
+ * **Méret-korlát:** a per-mappa fájllista a `MAX_FILES_PER_DIR`-nál levágódik (`+N további`), a teljes szöveg a
10
+ * `MAX_CHARS`-nál — így az összefoglaló egyetlen, embeddelhető chunk marad nagy fáknál is (minden MAPPA így is felsorolt).
11
+ */
12
+ class FAM_ScanSummary_Util {
13
+ /** A generált összefoglaló-entry `kind`-ja. */
14
+ static KIND = 'fs-summary';
15
+ /** A generált összefoglaló-entry `source.type`-ja (a reconciliation `scan`-t szűr → ezt nem érinti). */
16
+ static SOURCE_TYPE = 'scan-summary';
17
+ /** Per-mappa felsorolt fájlok plafonja (felette `+N további`). */
18
+ static MAX_FILES_PER_DIR = 80;
19
+ /** A teljes összefoglaló-szöveg plafonja (egyetlen embeddelhető chunk). */
20
+ static MAX_CHARS = 40000;
21
+ /**
22
+ * A mappastruktúra-összefoglaló szövege: fejléc (scope / root / fájl-számok) + a mappánként csoportosított,
23
+ * rendezett fájllista. A relatív utak `/`-normalizáltak; a gyökér-szintű fájlok a `(gyökér)` alatt.
24
+ */
25
+ static build(set) {
26
+ const byDir = new Map();
27
+ for (const rawPath of set.relativePaths) {
28
+ const normalized = rawPath.replace(/\\/g, '/');
29
+ const slashIndex = normalized.lastIndexOf('/');
30
+ const dir = slashIndex >= 0 ? normalized.slice(0, slashIndex) : '';
31
+ const name = slashIndex >= 0 ? normalized.slice(slashIndex + 1) : normalized;
32
+ if (!byDir.has(dir)) {
33
+ byDir.set(dir, []);
34
+ }
35
+ byDir.get(dir)?.push(name);
36
+ }
37
+ const lines = [];
38
+ lines.push(`File-rendszer összefoglaló — ${set.scopeLabel}`);
39
+ lines.push(`Gyökér: ${set.root}`);
40
+ lines.push(`${set.relativePaths.length} fájl ingestelve (${set.tables.join(', ') || '—'}), ${set.skippedCount} kihagyva.`);
41
+ lines.push('');
42
+ lines.push(`Mappák (${byDir.size}) + fájlok:`);
43
+ for (const dir of Array.from(byDir.keys()).sort()) {
44
+ const files = (byDir.get(dir) ?? []).sort();
45
+ const label = dir === '' ? '(gyökér)' : `${dir}/`;
46
+ const shown = files.length > FAM_ScanSummary_Util.MAX_FILES_PER_DIR
47
+ ? files.slice(0, FAM_ScanSummary_Util.MAX_FILES_PER_DIR)
48
+ : files;
49
+ const more = files.length > FAM_ScanSummary_Util.MAX_FILES_PER_DIR
50
+ ? ` (+${files.length - FAM_ScanSummary_Util.MAX_FILES_PER_DIR} további)`
51
+ : '';
52
+ lines.push(` ${label} — ${files.length} fájl: ${shown.join(', ')}${more}`);
53
+ }
54
+ const content = lines.join('\n');
55
+ return content.length > FAM_ScanSummary_Util.MAX_CHARS
56
+ ? `${content.slice(0, FAM_ScanSummary_Util.MAX_CHARS)}\n… (összefoglaló levágva a méret-plafonnál)`
57
+ : content;
58
+ }
59
+ }
60
+ exports.FAM_ScanSummary_Util = FAM_ScanSummary_Util;
@@ -0,0 +1,53 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.FAM_ScanWeight_Util = void 0;
4
+ const fam_glob_match_util_1 = require("./fam-glob-match.util");
5
+ /**
6
+ * `FAM_ScanWeight_Util` (FAM-REV-055) — a scan-idejű **súly-kiemelés** pure magja. A hívó (agent) scankor
7
+ * megadhat `weights` szabályokat (glob → súly), hogy a FONTOSABB fájlok chunkjai NAGYOBB retrieval-súlyt
8
+ * kapjanak (a `finalScore = score × weight` → előrébb a rangsorban). **First-match-wins:** a `weights` sorrend
9
+ * SZÁMÍT (az első illeszkedő szabály nyer). Nincs match → `undefined` (→ a tár model-default súlya marad).
10
+ */
11
+ class FAM_ScanWeight_Util {
12
+ /**
13
+ * Az adott (projekt-relatív) fájl súlya a `weights` szabályokból: az ELSŐ illeszkedő minta súlya, vagy
14
+ * `undefined` (nincs szabály / nincs match). Az illesztés a `FAM_GlobMatch_Util` (a scan ignore/include-pal
15
+ * azonos szemantika). A negatív / nem-véges súlyt kihagyja (defenzív — a 0/negatív weight retrieval-kizárást
16
+ * jelentene; ha szándékos, a `write`-on át explicit állítható).
17
+ */
18
+ static resolve(relativePath, weights) {
19
+ if (!weights || !weights.length) {
20
+ return undefined;
21
+ }
22
+ for (const rule of weights) {
23
+ if (typeof rule.weight !== 'number' || !Number.isFinite(rule.weight) || rule.weight <= 0) {
24
+ continue;
25
+ }
26
+ if (fam_glob_match_util_1.FAM_GlobMatch_Util.matches(relativePath, rule.pattern)) {
27
+ return rule.weight;
28
+ }
29
+ }
30
+ return undefined;
31
+ }
32
+ /** Teszt-/spec-fájl-e (`*.spec.*` / `*.test.*` a TS/JS-kiterjesztéseken). FAM-REV-058. */
33
+ static isTestFile(relativePath) {
34
+ return /\.(spec|test)\.(ts|tsx|mts|cts|js|jsx|mjs|cjs)$/i.test(relativePath);
35
+ }
36
+ /**
37
+ * A fájl EFFEKTÍV súlya a DEFAULT teszt-súllyal (FAM-REV-058): az explicit scan-`weights` match NYER; különben
38
+ * egy teszt-/spec-fájl a `testFileWeight`-et kapja (a próza-nehéz specek különben az implementáció fölé
39
+ * rangsorolnak); különben `undefined` (→ a tár model-default súlya). A `testFileWeight===1` (nincs csökkentés)
40
+ * vagy hiánya → nincs default-súly.
41
+ */
42
+ static resolveWithDefault(relativePath, weights, testFileWeight) {
43
+ const explicit = FAM_ScanWeight_Util.resolve(relativePath, weights);
44
+ if (explicit !== undefined) {
45
+ return explicit;
46
+ }
47
+ if (typeof testFileWeight === 'number' && testFileWeight !== 1 && FAM_ScanWeight_Util.isTestFile(relativePath)) {
48
+ return testFileWeight;
49
+ }
50
+ return undefined;
51
+ }
52
+ }
53
+ exports.FAM_ScanWeight_Util = FAM_ScanWeight_Util;
@@ -3,9 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.FAM_SecretExclude_Util = void 0;
4
4
  /**
5
5
  * `FAM_SecretExclude_Util` (SP-4.1, dsgn-004 §6.3) — a scan-réteg **fájlnév-/útvonal-alapú**
6
- * secret-kizárása. A felsorolt minták (`.env*`, `*.key`, `*.pem`, `*.p12`, `id_rsa*`, `*secret*`,
7
- * `*credential*`, `*.crt`, `secrets/` mappa) **SOHA** nem ingestálódnak; a kizárás a file-típus-
8
- * routing **ELŐTT** fut, és a kizárt fájlok a `filesSkipped`-be számolódnak (audit, NEM néma).
6
+ * secret-kizárása. A felsorolt minták (`.env*`, `*.key`, `*.pem`, `*.p12`, `id_rsa*`, `*.crt`,
7
+ * `secrets/` mappa + a `secret`/`credential` névrész NEM-forrásfájlokon) **SOHA** nem ingestálódnak;
8
+ * a kizárás a file-típus-routing **ELŐTT** fut, és a kizárt fájlok a `filesSkipped`-be számolódnak
9
+ * (audit, NEM néma).
10
+ *
11
+ * **FALSE-POSITIVE FIX (2026-06-20 audit):** a korábbi `*secret*` / `*credential*` substring-minta
12
+ * **bármely** forrásfájlt is kizárt, aminek a neve tartalmazta ezeket (pl. maga a
13
+ * `fam-secret-exclude.util.ts`, vagy egy `secret-manager.service.ts`) → kód-RAG-lefedettség-rés. Ezért
14
+ * a `secret`/`credential` névrész-minta MOST CSAK **nem-forrás/doc-kiterjesztésekre** alkalmazódik
15
+ * (egy `.ts`/`.md` fájl, aminek a neve „secret", az KÓD a titokról, NEM titok-fájl). A valódi titok-
16
+ * fájlok (`.env`/`.key`/`.pem`/…) kiterjesztés-/névalapon továbbra is mindig kizártak.
9
17
  *
10
18
  * **HATÁR (dsgn-004 §6 megjegyzés + `MP-4-detailed` kockázat) — KIFEJEZETTEN BACKLOG:** ez CSAK
11
19
  * fájlnév-/útvonal-alapú kizárás. A **tartalmi** PII/secret-detektálás (a fájlon BELÜLI titkok
@@ -15,12 +23,10 @@ exports.FAM_SecretExclude_Util = void 0;
15
23
  */
16
24
  class FAM_SecretExclude_Util {
17
25
  /**
18
- * A beépített secret-fájlnév-minták (dsgn-004 §6.3). Ezek a `scan.ignorePatterns` config
19
- * biztonsági default-jával átfedők (dsgn-007 §4.5 `SCAN_IGNORE_PATTERNS_DEFAULT`), de a scan
20
- * ettől FÜGGETLENÜL is alkalmazza őket (a config-override NEM kapcsolhatja ki a secret-védelmet).
21
- * A vizsgálat a fájlnévre (regex) + az útvonal-szegmensekre (`secrets/`) megy.
26
+ * HARD secret-fájlnév-minták (dsgn-004 §6.3) **MINDIG** kizárnak, kiterjesztéstől függetlenül:
27
+ * ezek a fájlok maguk a titkok (kulcs/cert/env). A config-override NEM kapcsolhatja ki őket.
22
28
  */
23
- static SECRET_FILENAME_PATTERNS = [
29
+ static HARD_SECRET_PATTERNS = [
24
30
  /^\.env(\..*)?$/i, // .env, .env.local, .env.production, ...
25
31
  /\.key$/i, // *.key
26
32
  /\.pem$/i, // *.pem
@@ -29,14 +35,24 @@ class FAM_SecretExclude_Util {
29
35
  /\.pfx$/i, // *.pfx (cert)
30
36
  /^id_rsa.*$/i, // id_rsa, id_rsa.pub, ...
31
37
  /^id_ed25519.*$/i, // id_ed25519, id_ed25519.pub
32
- /secret/i, // *secret* (bárhol a fájlnévben)
33
- /credential/i, // *credential* (bárhol a fájlnévben)
34
38
  ];
39
+ /**
40
+ * SOFT névrész-minták (`secret`/`credential` bárhol a fájlnévben) — CSAK NEM-forrás/doc-fájlokra
41
+ * (pl. `secrets.json`, `my-credential.txt`). Egy `.ts`/`.md` fájl, aminek a neve „secret", az KÓD/
42
+ * DOC a titokról (legit forrás), NEM titok → ingestálódik (false-positive fix, lásd osztály-jsdoc).
43
+ */
44
+ static SOFT_SECRET_NAME_PATTERNS = [
45
+ /secret/i, // *secret* (csak nem-forrás kiterjesztésen)
46
+ /credential/i, // *credential* (csak nem-forrás kiterjesztésen)
47
+ ];
48
+ /** Ismert FORRÁS-/DOC-kiterjesztések — ezeken a SOFT névrész-minta NEM zár ki (legit kód/doc). */
49
+ static SOURCE_DOC_EXTENSION = /\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|md|markdown)$/i;
35
50
  /** A secret-mappák (az útvonal BÁRMELY szegmense — pl. `secrets/`, `.ssh/`). */
36
51
  static SECRET_DIR_SEGMENTS = ['secrets', '.ssh', '.gnupg'];
37
52
  /**
38
- * Secret-e a (projekt-relatív, `/`-normalizált) útvonal — fájlnév-minta VAGY secret-mappa-
39
- * szegmens alapján (dsgn-004 §6.3). `true` → SOHA nem ingestálódik (`filesSkipped`, audit).
53
+ * Secret-e a (projekt-relatív, `/`-normalizált) útvonal — secret-mappa-szegmens VAGY HARD-minta
54
+ * VAGY (nem-forrásfájlon) SOFT névrész-minta alapján (dsgn-004 §6.3). `true` → SOHA nem ingestálódik
55
+ * (`filesSkipped`, audit).
40
56
  */
41
57
  static isSecret(relativePath) {
42
58
  const normalized = relativePath.replace(/\\/g, '/');
@@ -47,8 +63,15 @@ class FAM_SecretExclude_Util {
47
63
  if (dirSegments.some((segment) => FAM_SecretExclude_Util.SECRET_DIR_SEGMENTS.includes(segment))) {
48
64
  return true;
49
65
  }
50
- // (2) Secret-fájlnév-minta.
51
- return FAM_SecretExclude_Util.SECRET_FILENAME_PATTERNS.some((pattern) => pattern.test(fileName));
66
+ // (2) HARD secret-fájlnév-minta (mindig).
67
+ if (FAM_SecretExclude_Util.HARD_SECRET_PATTERNS.some((pattern) => pattern.test(fileName))) {
68
+ return true;
69
+ }
70
+ // (3) SOFT névrész-minta — CSAK ha NEM ismert forrás-/doc-kiterjesztés (a "secret"-nevű .ts legit kód).
71
+ if (FAM_SecretExclude_Util.SOURCE_DOC_EXTENSION.test(fileName)) {
72
+ return false;
73
+ }
74
+ return FAM_SecretExclude_Util.SOFT_SECRET_NAME_PATTERNS.some((pattern) => pattern.test(fileName));
52
75
  }
53
76
  }
54
77
  exports.FAM_SecretExclude_Util = FAM_SecretExclude_Util;
@@ -32,6 +32,19 @@ class FAM_SlidingChunker_Util {
32
32
  if (paragraphStart >= 0) {
33
33
  charCursor = paragraphStart;
34
34
  }
35
+ // FAM-REV-065: egy paragrafus ÖNMAGÁBAN a maxSize felett (pl. sortörés-nélküli nagy JSON/blob) →
36
+ // flush + HARD-SPLIT (char-vágás maxSize-onként). KÜLÖNBEN egyetlen óriás-chunk lenne, ami az
37
+ // embedding-modell token-limitjét túllépi → `error` státusz → a tartalom NEM kereshető (info-veszteség).
38
+ if (paragraph.length > sizing.maxSize) {
39
+ if (currentText.trim().length) {
40
+ FAM_SlidingChunker_Util.emit(chunks, currentText.trim(), currentCharStart, sizing.minSize);
41
+ currentText = '';
42
+ }
43
+ FAM_SlidingChunker_Util.hardSplit(chunks, paragraph, charCursor, sizing);
44
+ charCursor = charCursor + paragraph.length;
45
+ currentCharStart = charCursor;
46
+ continue;
47
+ }
35
48
  if (currentText.length && currentText.length + paragraph.length + 2 > sizing.maxSize) {
36
49
  FAM_SlidingChunker_Util.emit(chunks, currentText.trim(), currentCharStart, sizing.minSize);
37
50
  if (currentText.length > sizing.overlap) {
@@ -60,6 +73,27 @@ class FAM_SlidingChunker_Util {
60
73
  }
61
74
  return chunks;
62
75
  }
76
+ /**
77
+ * Egy maxSize feletti, természetes törés-pont NÉLKÜLI szöveg HARD-SPLIT-je (FAM-REV-065): char-vágás
78
+ * `maxSize`-onként, `overlap`-pal lépve. A darabok a `minSize`-szűrés ALÓL MENTESEK (a tail-darab is kell
79
+ * — semmi ne vesszen el), és a `position` a forrás-offszetre mutat. Így a nagy JSON/blob is embeddelhető
80
+ * (modell-token-limit alatti darabok), nem ragad `error`-ba.
81
+ */
82
+ static hardSplit(chunks, text, charStart, sizing) {
83
+ const step = Math.max(1, sizing.maxSize - sizing.overlap);
84
+ for (let i = 0; i < text.length; i += step) {
85
+ const piece = text.slice(i, i + sizing.maxSize);
86
+ chunks.push({
87
+ content: piece,
88
+ position: { charStart: charStart + i, charEnd: charStart + i + piece.length },
89
+ chunkType: 'generic',
90
+ headingPath: [],
91
+ });
92
+ if (i + sizing.maxSize >= text.length) {
93
+ break;
94
+ }
95
+ }
96
+ }
63
97
  /** Egy chunk hozzáadása `chunkType='generic'`-kel (zaj-szűrés a `minSize`-ra). */
64
98
  static emit(chunks, content, charStart, minSize) {
65
99
  if (content.length < minSize) {