@futdevpro/fdp-agent-memory 0.1.0 → 1.1.14
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/LICENSE +21 -0
- package/README.md +7 -7
- package/build/package.json +6 -5
- package/build/src/_cli/_collections/fam-arg.util.js +48 -0
- package/build/src/_cli/_collections/fam-cli.const.js +40 -0
- package/build/src/_cli/_collections/fam-output.util.js +86 -0
- package/build/src/_cli/_collections/fam-project-discovery.util.js +98 -0
- package/build/src/_cli/_commands/capture.command.js +73 -0
- package/build/src/_cli/_commands/config.command.js +93 -0
- package/build/src/_cli/_commands/doctor.command.js +124 -0
- package/build/src/_cli/_commands/errors.command.js +66 -0
- package/build/src/_cli/_commands/export.command.js +65 -0
- package/build/src/_cli/_commands/find-duplicates.command.js +97 -0
- package/build/src/_cli/_commands/import.command.js +136 -0
- package/build/src/_cli/_commands/init.command.js +147 -0
- package/build/src/_cli/_commands/read.command.js +109 -0
- package/build/src/_cli/_commands/scan-projects.command.js +138 -0
- package/build/src/_cli/_commands/scan.command.js +98 -0
- package/build/src/_cli/_commands/seed.command.js +40 -0
- package/build/src/_cli/_commands/serve.command.js +373 -0
- package/build/src/_cli/_commands/start.command.js +134 -0
- package/build/src/_cli/_commands/stats.command.js +54 -0
- package/build/src/_cli/_commands/write.command.js +103 -0
- package/build/src/_cli/_models/interfaces/fam-cli-global-options.interface.js +2 -0
- package/build/src/_cli/_models/interfaces/fam-cli-output.interface.js +9 -0
- package/build/src/_cli/_models/interfaces/fam-client-result.interface.js +2 -0
- package/build/src/_cli/_services/fam-client.service.js +140 -0
- package/build/src/_cli/register-commands.js +86 -0
- package/build/src/_collections/config-catalog.const.js +67 -1
- package/build/src/_collections/fam-console.util.js +367 -0
- package/build/src/_collections/fam-entry-bootstrap.util.js +158 -4
- package/build/src/_collections/fam-error-factory.util.js +0 -9
- package/build/src/_collections/fam-mcp-bridge.util.js +49 -0
- package/build/src/_collections/fam-reference-code.util.js +105 -0
- package/build/src/_collections/fam-version.const.js +10 -0
- package/build/src/_models/data-models/fam-entry-base-properties.const.js +1 -0
- package/build/src/_models/data-models/fam-entry.data-model.js +6 -0
- package/build/src/_models/data-models/fam-ingest-run.data-model.js +3 -1
- package/build/src/_models/data-models/fam-reference.data-model.js +7 -0
- package/build/src/_modules/capture/_collections/fam-capture.const.js +11 -0
- package/build/src/_modules/capture/_services/fam-auto-capture.control-service.js +87 -0
- package/build/src/_modules/capture/index.js +8 -0
- package/build/src/_modules/embedding/_collections/fam-embedding-prefix.util.js +77 -0
- package/build/src/_modules/embedding/_services/fam-duplicate-scan.control-service.js +202 -0
- package/build/src/_modules/embedding/_services/fam-embedding-pipeline.control-service.js +33 -9
- package/build/src/_modules/embedding/_services/fam-embedding.control-service.js +21 -2
- package/build/src/_modules/embedding/_services/fam-entry.data-service.js +135 -0
- package/build/src/_modules/embedding/_services/fam-vector-search.control-service.js +42 -32
- package/build/src/_modules/embedding/index.js +4 -1
- package/build/src/_modules/export/_collections/fam-export.const.js +22 -0
- package/build/src/_modules/export/_services/fam-export.control-service.js +64 -0
- package/build/src/_modules/export/index.js +8 -0
- package/build/src/_modules/ingest/_collections/fam-famignore.util.js +83 -0
- package/build/src/_modules/ingest/_collections/fam-file-routing.util.js +59 -48
- package/build/src/_modules/ingest/_collections/fam-git-repo.util.js +193 -0
- package/build/src/_modules/ingest/_collections/fam-project-identity.util.js +134 -0
- package/build/src/_modules/ingest/_collections/fam-scan-progress.util.js +57 -0
- package/build/src/_modules/ingest/_collections/fam-scan-summary.util.js +60 -0
- package/build/src/_modules/ingest/_collections/fam-scan-weight.util.js +53 -0
- package/build/src/_modules/ingest/_collections/fam-secret-exclude.util.js +37 -14
- package/build/src/_modules/ingest/_collections/fam-sliding-chunker.util.js +34 -0
- package/build/src/_modules/ingest/_collections/fam-ts-chunker.util.js +200 -14
- package/build/src/_modules/ingest/_services/fam-delta-compare.util.js +4 -1
- package/build/src/_modules/ingest/_services/fam-ingest-run.data-service.js +7 -4
- package/build/src/_modules/ingest/_services/fam-ingest.control-service.js +349 -17
- package/build/src/_modules/ingest/_services/fam-scan.control-service.js +25 -2
- package/build/src/_modules/ingest/index.js +3 -1
- package/build/src/_modules/mcp/_collections/fam-active-rules.util.js +56 -0
- package/build/src/_modules/mcp/_collections/fam-core-tools.const.js +47 -6
- package/build/src/_modules/mcp/_services/fam-capabilities-tool.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-capability-registry.service.js +224 -18
- package/build/src/_modules/mcp/_services/fam-mcp-adapter.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-mcp-server.service.js +4 -4
- package/build/src/_modules/mcp/_services/fam-read-tool.service.js +53 -1
- package/build/src/_modules/mcp/_services/fam-write-tool.service.js +104 -8
- package/build/src/_modules/mcp/index.js +4 -4
- package/build/src/_modules/migration/_collections/fam-claude-mem-normalize.util.js +66 -3
- package/build/src/_modules/migration/_collections/fam-prompt-aggregate.util.js +143 -0
- package/build/src/_modules/migration/_collections/fam-target-mapping.util.js +19 -0
- package/build/src/_modules/migration/_enums/fam-claude-mem-source.type-enum.js +6 -0
- package/build/src/_modules/migration/_models/interfaces/fam-claude-mem.interface.js +5 -0
- package/build/src/_modules/migration/_services/fam-agent-memory-reader.service.js +125 -0
- package/build/src/_modules/migration/_services/fam-claude-mem-import.control-service.js +101 -18
- package/build/src/_modules/migration/_services/fam-import-dedup.data-service.js +53 -0
- package/build/src/_modules/migration/index.js +3 -1
- package/build/src/_modules/retrieval/_services/fam-retrieval-candidate.data-service.js +78 -4
- package/build/src/_modules/retrieval/_services/fam-retrieval.control-service.js +293 -50
- package/build/src/_modules/scope-reference/_collections/fam-scope-normalize.util.js +6 -3
- package/build/src/_modules/scope-reference/_services/fam-reference.data-service.js +18 -0
- package/build/src/_modules/scope-reference/_services/fam-scope-resolver.control-service.js +79 -20
- package/build/src/_routes/server/api/api.controller.js +34 -2
- package/build/src/_routes/server/client-app/client-app.control-service.js +1 -1
- package/build/src/_routes/server/server-status/server-status.controller.js +2 -1
- package/build/src/app.server.js +13 -1
- package/build/src/environments/environment.js +1 -1
- package/build/src/index.js +1 -1
- package/client-dist/{chunk-GHKRM4SM.js → chunk-I77GXVAQ.js} +1 -1
- package/client-dist/{chunk-LMTL7GA3.js → chunk-YXHWCJ5O.js} +1 -1
- package/client-dist/index.html +1 -1
- package/client-dist/{main-2KWB3QYK.js → main-PJPEDVJT.js} +1 -1
- 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
|
-
*
|
|
13
|
-
* -
|
|
14
|
-
*
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* - `
|
|
18
|
-
*
|
|
19
|
-
*
|
|
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` (
|
|
25
|
-
static DOCUMENT_EXTENSIONS = ['.md', '.mdx', '.txt', '.rst'];
|
|
26
|
-
/**
|
|
27
|
-
static
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
static
|
|
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 (
|
|
39
|
-
* (
|
|
40
|
-
* → `codebase
|
|
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.
|
|
49
|
-
return
|
|
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
|
-
|
|
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
|
-
*
|
|
58
|
-
*
|
|
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
|
|
61
|
-
if (
|
|
62
|
-
|
|
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 (
|
|
81
|
+
if (fileName.endsWith('.lock')) {
|
|
71
82
|
return true;
|
|
72
83
|
}
|
|
73
|
-
if (
|
|
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
|
-
*
|
|
81
|
-
* `
|
|
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,193 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FAM_GitRepo_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_GitRepo_Util` (user-FR 2026-06-21) — egy scannelt fájlhoz felderíti a **git-repo provenance-t**:
|
|
9
|
+
* a repo-gyökeret (`.git` felfelé keresve), a **GitHub/remote URL-t** (`.git/config` `remote.origin.url`,
|
|
10
|
+
* normalizálva), a **branch-et** (`.git/HEAD`) és a **repo-gyökérhez relatív utat**. **Függőség-mentes**
|
|
11
|
+
* (NEM `git`-parancs — közvetlenül a `.git/` fájlokból olvas; gyors + offline). A scan **repoRoot-onként cache-eli**
|
|
12
|
+
* (egy mappa-scan ~1 repo → egyszer olvas), így a per-fájl költség elhanyagolható.
|
|
13
|
+
*
|
|
14
|
+
* Miért így (a user „relative path + GitHub repo" döntése): a repo-URL + a **REPO-relatív** út KANONIKUS,
|
|
15
|
+
* gép-független, kattintható citáció (`<repoUrl>/blob/<branch>/<repoRelativePath>`) — szemben a gép-specifikus
|
|
16
|
+
* abszolút úttal vagy a törékeny „projektnévből kiszámolt" relatív úttal.
|
|
17
|
+
*/
|
|
18
|
+
class FAM_GitRepo_Util {
|
|
19
|
+
/** Meddig megyünk felfelé `.git`-et keresve (biztonsági korlát a végtelen ciklus ellen). */
|
|
20
|
+
static MAX_WALK_UP = 40;
|
|
21
|
+
/** Belső repoRoot→info cache (a scan-során a `.git` nem változik; egy repo → egyszer olvas). */
|
|
22
|
+
static _cache = new Map();
|
|
23
|
+
/**
|
|
24
|
+
* Egy scannelt fájl **scan-source git-mezői** (a `FAM_Source`-ba spread-elve): `repoUrl` / `repoName` /
|
|
25
|
+
* `repoRelativePath` (a repo-gyökérhez) / `repoBranch`. Cache-elt (belső statikus Map) + fail-safe (üres
|
|
26
|
+
* objektum, ha a fájl NINCS git-repóban / hiba) — a scan SOHA nem bukik el a git-felderítésen.
|
|
27
|
+
*/
|
|
28
|
+
static sourceFieldsFor(absFilePath) {
|
|
29
|
+
try {
|
|
30
|
+
const info = FAM_GitRepo_Util.resolve(absFilePath, FAM_GitRepo_Util._cache);
|
|
31
|
+
if (!info) {
|
|
32
|
+
return {};
|
|
33
|
+
}
|
|
34
|
+
return {
|
|
35
|
+
repoUrl: info.repoUrl,
|
|
36
|
+
repoName: info.repoName,
|
|
37
|
+
repoRelativePath: FAM_GitRepo_Util.relativePath(absFilePath, info.repoRoot),
|
|
38
|
+
repoBranch: info.repoBranch,
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
catch {
|
|
42
|
+
return {};
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* A teljes repo-info felderítése egy fájl abszolút útjából, **cache-elve a repoRoot-on** (a hívó adja a
|
|
47
|
+
* Map-et; egy scan végig egyet használ). `null`, ha a fájl NINCS git-repóban. A relatív utat a hívó a
|
|
48
|
+
* `relativePath`-szal számolja (a repoRoot ismeretében).
|
|
49
|
+
*/
|
|
50
|
+
static resolve(absFilePath, cache) {
|
|
51
|
+
const repoRoot = FAM_GitRepo_Util.findRepoRoot(absFilePath);
|
|
52
|
+
if (!repoRoot) {
|
|
53
|
+
return null;
|
|
54
|
+
}
|
|
55
|
+
const cached = cache.get(repoRoot);
|
|
56
|
+
if (cached !== undefined) {
|
|
57
|
+
return cached;
|
|
58
|
+
}
|
|
59
|
+
const remote = FAM_GitRepo_Util.readRemoteUrl(repoRoot);
|
|
60
|
+
const info = {
|
|
61
|
+
repoRoot: repoRoot,
|
|
62
|
+
repoUrl: remote?.url,
|
|
63
|
+
repoName: remote?.name,
|
|
64
|
+
repoBranch: FAM_GitRepo_Util.readBranch(repoRoot),
|
|
65
|
+
};
|
|
66
|
+
cache.set(repoRoot, info);
|
|
67
|
+
return info;
|
|
68
|
+
}
|
|
69
|
+
/** A repo-gyökér keresése felfelé (`.git` mappa VAGY fájl — a submodule `.git` fájl). `null`, ha nincs. */
|
|
70
|
+
static findRepoRoot(absStartPath) {
|
|
71
|
+
let dir = absStartPath;
|
|
72
|
+
try {
|
|
73
|
+
// Ha a start egy fájl, a könyvtárából indulunk; ha mappa, magából.
|
|
74
|
+
if (fs.existsSync(absStartPath) && fs.statSync(absStartPath).isFile()) {
|
|
75
|
+
dir = path.dirname(absStartPath);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
catch {
|
|
79
|
+
dir = path.dirname(absStartPath);
|
|
80
|
+
}
|
|
81
|
+
for (let i = 0; i < FAM_GitRepo_Util.MAX_WALK_UP; i++) {
|
|
82
|
+
if (fs.existsSync(path.join(dir, '.git'))) {
|
|
83
|
+
return dir;
|
|
84
|
+
}
|
|
85
|
+
const parent = path.dirname(dir);
|
|
86
|
+
if (parent === dir) {
|
|
87
|
+
return null; // elértük a filesystem-gyökeret
|
|
88
|
+
}
|
|
89
|
+
dir = parent;
|
|
90
|
+
}
|
|
91
|
+
return null;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* A `remote.origin.url` kiolvasása a `.git/config`-ból + normalizálva (`https://github.com/owner/repo`).
|
|
95
|
+
* `undefined`, ha nincs origin / nem olvasható. (A submodule `.git` FÁJL `gitdir:`-re mutat — ezt is követjük.)
|
|
96
|
+
*/
|
|
97
|
+
static readRemoteUrl(repoRoot) {
|
|
98
|
+
try {
|
|
99
|
+
const gitPath = path.join(repoRoot, '.git');
|
|
100
|
+
const configPath = fs.statSync(gitPath).isDirectory()
|
|
101
|
+
? path.join(gitPath, 'config')
|
|
102
|
+
: path.join(FAM_GitRepo_Util.resolveGitdir(repoRoot, gitPath), 'config');
|
|
103
|
+
if (!fs.existsSync(configPath)) {
|
|
104
|
+
return undefined;
|
|
105
|
+
}
|
|
106
|
+
const raw = fs.readFileSync(configPath, 'utf-8');
|
|
107
|
+
const rawUrl = FAM_GitRepo_Util.extractOriginUrl(raw);
|
|
108
|
+
return rawUrl ? FAM_GitRepo_Util.normalizeRepoUrl(rawUrl) : undefined;
|
|
109
|
+
}
|
|
110
|
+
catch {
|
|
111
|
+
return undefined;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
/** A `.git/HEAD`-ből az aktuális branch (`ref: refs/heads/<branch>`); detached SHA / hiba → `undefined`. */
|
|
115
|
+
static readBranch(repoRoot) {
|
|
116
|
+
try {
|
|
117
|
+
const gitPath = path.join(repoRoot, '.git');
|
|
118
|
+
const headPath = fs.statSync(gitPath).isDirectory()
|
|
119
|
+
? path.join(gitPath, 'HEAD')
|
|
120
|
+
: path.join(FAM_GitRepo_Util.resolveGitdir(repoRoot, gitPath), 'HEAD');
|
|
121
|
+
const head = fs.readFileSync(headPath, 'utf-8').trim();
|
|
122
|
+
const match = head.match(/^ref:\s*refs\/heads\/(.+)$/);
|
|
123
|
+
return match ? match[1].trim() : undefined;
|
|
124
|
+
}
|
|
125
|
+
catch {
|
|
126
|
+
return undefined;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
/** A fájl REPO-gyökérhez relatív útja (POSIX-szeparátorral, a kattintható citációhoz). */
|
|
130
|
+
static relativePath(absFilePath, repoRoot) {
|
|
131
|
+
return path.relative(repoRoot, absFilePath).replace(/\\/g, '/');
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* A nyers remote-URL normalizálása kattintható HTTPS-formára + `owner/repo` névre. Lefedi:
|
|
135
|
+
* `git@host:owner/repo.git`, `https://host/owner/repo.git`, `ssh://git@host/owner/repo.git`. A `.git`-suffix
|
|
136
|
+
* + a beágyazott credential levágva. Nem-github host esetén a host marad (gitlab/bitbucket is kattintható).
|
|
137
|
+
*/
|
|
138
|
+
static normalizeRepoUrl(rawUrl) {
|
|
139
|
+
let url = rawUrl.trim();
|
|
140
|
+
// scp-szerű: git@host:owner/repo(.git) → https://host/owner/repo
|
|
141
|
+
const scp = url.match(/^[\w.-]+@([\w.-]+):(.+)$/);
|
|
142
|
+
if (scp) {
|
|
143
|
+
url = `https://${scp[1]}/${scp[2]}`;
|
|
144
|
+
}
|
|
145
|
+
url = url
|
|
146
|
+
.replace(/^ssh:\/\/(?:[\w.-]+@)?/, 'https://')
|
|
147
|
+
.replace(/^git:\/\//, 'https://')
|
|
148
|
+
.replace(/^https?:\/\/[^@/]+@/, 'https://') // beágyazott credential levágása
|
|
149
|
+
.replace(/\.git$/, '')
|
|
150
|
+
.replace(/\/+$/, '');
|
|
151
|
+
const nameMatch = url.match(/^https?:\/\/[\w.-]+\/(.+?\/[^/]+)$/);
|
|
152
|
+
return { url: url, name: nameMatch ? nameMatch[1] : undefined };
|
|
153
|
+
}
|
|
154
|
+
/** A `[remote "origin"]` szekció `url = …` sora a `.git/config`-ból (defenzív, szekció-tudatos). */
|
|
155
|
+
static extractOriginUrl(configText) {
|
|
156
|
+
let inOrigin = false;
|
|
157
|
+
for (const rawLine of configText.split(/\r?\n/)) {
|
|
158
|
+
const line = rawLine.trim();
|
|
159
|
+
const section = line.match(/^\[remote\s+"([^"]+)"\]$/);
|
|
160
|
+
if (section) {
|
|
161
|
+
inOrigin = section[1] === 'origin';
|
|
162
|
+
continue;
|
|
163
|
+
}
|
|
164
|
+
if (line.startsWith('[')) {
|
|
165
|
+
inOrigin = false;
|
|
166
|
+
continue;
|
|
167
|
+
}
|
|
168
|
+
if (inOrigin) {
|
|
169
|
+
const url = line.match(/^url\s*=\s*(.+)$/);
|
|
170
|
+
if (url) {
|
|
171
|
+
return url[1].trim();
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return undefined;
|
|
176
|
+
}
|
|
177
|
+
/** A submodule `.git` FÁJL (`gitdir: <relpath>`) feloldása a tényleges git-könyvtárra. */
|
|
178
|
+
static resolveGitdir(repoRoot, gitFilePath) {
|
|
179
|
+
try {
|
|
180
|
+
const content = fs.readFileSync(gitFilePath, 'utf-8').trim();
|
|
181
|
+
const match = content.match(/^gitdir:\s*(.+)$/);
|
|
182
|
+
if (match) {
|
|
183
|
+
const target = match[1].trim();
|
|
184
|
+
return path.isAbsolute(target) ? target : path.resolve(repoRoot, target);
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
/* fall through */
|
|
189
|
+
}
|
|
190
|
+
return gitFilePath;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
exports.FAM_GitRepo_Util = FAM_GitRepo_Util;
|
|
@@ -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;
|