@nac3/forge-cli 1.0.44 → 1.0.47

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 (49) hide show
  1. package/dist/chat/panel.d.ts.map +1 -1
  2. package/dist/chat/panel.js +2 -0
  3. package/dist/chat/panel.js.map +1 -1
  4. package/dist/chat/server.d.ts +9 -1
  5. package/dist/chat/server.d.ts.map +1 -1
  6. package/dist/chat/server.js +65 -17
  7. package/dist/chat/server.js.map +1 -1
  8. package/dist/core/project_identity.d.ts +29 -0
  9. package/dist/core/project_identity.d.ts.map +1 -0
  10. package/dist/core/project_identity.js +118 -0
  11. package/dist/core/project_identity.js.map +1 -0
  12. package/dist/core/repo_state.d.ts +4 -0
  13. package/dist/core/repo_state.d.ts.map +1 -1
  14. package/dist/core/repo_state.js +19 -0
  15. package/dist/core/repo_state.js.map +1 -1
  16. package/dist/relay/board_snapshot.d.ts +1 -1
  17. package/dist/relay/board_snapshot.d.ts.map +1 -1
  18. package/dist/relay/board_snapshot.js +3 -1
  19. package/dist/relay/board_snapshot.js.map +1 -1
  20. package/dist/relay/command_consumer.d.ts +8 -2
  21. package/dist/relay/command_consumer.d.ts.map +1 -1
  22. package/dist/relay/command_consumer.js +4 -2
  23. package/dist/relay/command_consumer.js.map +1 -1
  24. package/dist/semantic_graph/data_model.d.ts +29 -0
  25. package/dist/semantic_graph/data_model.d.ts.map +1 -0
  26. package/dist/semantic_graph/data_model.js +288 -0
  27. package/dist/semantic_graph/data_model.js.map +1 -0
  28. package/dist/semantic_graph/derive.d.ts +3 -0
  29. package/dist/semantic_graph/derive.d.ts.map +1 -1
  30. package/dist/semantic_graph/derive.js +163 -39
  31. package/dist/semantic_graph/derive.js.map +1 -1
  32. package/dist/semantic_graph/lang_imports.d.ts +32 -0
  33. package/dist/semantic_graph/lang_imports.d.ts.map +1 -0
  34. package/dist/semantic_graph/lang_imports.js +111 -0
  35. package/dist/semantic_graph/lang_imports.js.map +1 -0
  36. package/dist/semantic_graph/py_imports.d.ts +30 -0
  37. package/dist/semantic_graph/py_imports.d.ts.map +1 -0
  38. package/dist/semantic_graph/py_imports.js +77 -0
  39. package/dist/semantic_graph/py_imports.js.map +1 -0
  40. package/dist/semantic_graph/render.d.ts.map +1 -1
  41. package/dist/semantic_graph/render.js +2 -1
  42. package/dist/semantic_graph/render.js.map +1 -1
  43. package/dist/semantic_graph/store.js +1 -1
  44. package/dist/semantic_graph/store.js.map +1 -1
  45. package/dist/semantic_graph/types.d.ts +2 -2
  46. package/dist/semantic_graph/types.d.ts.map +1 -1
  47. package/dist/version.d.ts +1 -1
  48. package/dist/version.js +1 -1
  49. package/package.json +1 -1
@@ -0,0 +1,288 @@
1
+ /**
2
+ * data_model.ts -- data-model extraction for the semantic graph
3
+ * (Pablo 2026-06-23).
4
+ *
5
+ * The deriver indexed code modules + libraries + tooling, but NOT the
6
+ * project's DATA MODEL -- the tables/entities the app is actually about.
7
+ * In a large RPA/CRM project the schema IS the backbone (e.g. OSDE: the
8
+ * LLM had hand-noted "BD del demo HITL (6 tablas)" because the graph could
9
+ * not see them). This module discovers entities + their relationships from
10
+ * the common ways teams declare a schema, GENERICALLY (ships to customers;
11
+ * nothing hardcoded), and emits them as `datamodel` nodes + `references`
12
+ * edges so the panel/mobile graph + the prompt show the real domain model.
13
+ *
14
+ * Supported declarations (regex extraction, no per-language parser):
15
+ * - SQL : CREATE TABLE [schema.]name (...), FOREIGN KEY/REFERENCES.
16
+ * - SQLAlchemy : class X(Base) + __tablename__ + Column(...) + ForeignKey.
17
+ * - Django ORM : class X(models.Model) + <f> = models.ForeignKey(...).
18
+ * - Pydantic/dataclass: class X(BaseModel) / @dataclass -> entity + fields.
19
+ * - Prisma : model X { ... } + relation fields.
20
+ * - TypeORM : @Entity() class X + @Column / @ManyToOne(() => Y).
21
+ *
22
+ * Bounded: a file/byte/entity cap keeps a pathological repo cheap; when a
23
+ * cap trips it is reported in stats (no silent truncation).
24
+ *
25
+ * ASCII-only.
26
+ */
27
+ import { promises as fs } from 'node:fs';
28
+ import path from 'node:path';
29
+ const EXCLUDED_DIRS = new Set([
30
+ 'node_modules', 'dist', 'build', 'out', 'coverage',
31
+ '__pycache__', '.pytest_cache', '.mypy_cache', '.ruff_cache',
32
+ 'venv', '.venv', 'env', '.tox', 'site-packages', 'Lib',
33
+ '.git', '.svn', '.hg', '.next', '.expo', '.expo-shared',
34
+ '.gradle', '.idea', '.vscode', 'target', 'bin', 'obj',
35
+ '__snapshots__', '.cache',
36
+ ]);
37
+ const SCHEMA_FILE_RE = /\.(sql|py|prisma|ts|js|rb)$/i;
38
+ const MAX_FILES = 1200;
39
+ const MAX_BYTES_PER_FILE = 600_000;
40
+ const MAX_ENTITIES = 300;
41
+ const MAX_FIELDS_SHOWN = 24;
42
+ function dmId(name) {
43
+ return 'datamodel.' + name.replace(/[^a-z0-9_]/gi, '_').toLowerCase();
44
+ }
45
+ async function walkSchemaFiles(root) {
46
+ const out = [];
47
+ const walk = async (dir) => {
48
+ if (out.length >= MAX_FILES)
49
+ return;
50
+ let entries;
51
+ try {
52
+ entries = await fs.readdir(dir, { withFileTypes: true });
53
+ }
54
+ catch {
55
+ return;
56
+ }
57
+ for (const e of entries) {
58
+ if (out.length >= MAX_FILES)
59
+ return;
60
+ if (e.name.startsWith('.') || EXCLUDED_DIRS.has(e.name))
61
+ continue;
62
+ const full = path.join(dir, e.name);
63
+ if (e.isDirectory())
64
+ await walk(full);
65
+ else if (e.isFile() && SCHEMA_FILE_RE.test(e.name))
66
+ out.push(full);
67
+ }
68
+ };
69
+ await walk(root);
70
+ return out;
71
+ }
72
+ /** Extract CREATE TABLE statements (name + columns + references). */
73
+ export function extractSqlTables(src, rel) {
74
+ const out = [];
75
+ const re = /create\s+table\s+(?:if\s+not\s+exists\s+)?[`"\[]?(?:[\w]+\.)?([\w]+)[`"\]]?\s*\(([\s\S]*?)\)\s*;/gi;
76
+ let m;
77
+ while ((m = re.exec(src)) !== null) {
78
+ const name = m[1];
79
+ const body = m[2] || '';
80
+ const fields = [];
81
+ const refs = new Set();
82
+ for (const rawLine of body.split(',')) {
83
+ const line = rawLine.trim();
84
+ if (!line)
85
+ continue;
86
+ const colM = line.match(/^[`"\[]?([\w]+)[`"\]]?\s+/);
87
+ const upper = line.toUpperCase();
88
+ if (colM && colM[1] && !/^(PRIMARY|FOREIGN|UNIQUE|CONSTRAINT|CHECK|KEY|INDEX)\b/.test(upper)) {
89
+ fields.push(colM[1]);
90
+ }
91
+ const refM = line.match(/references\s+[`"\[]?(?:[\w]+\.)?([\w]+)/i);
92
+ if (refM && refM[1])
93
+ refs.add(refM[1]);
94
+ }
95
+ out.push({ name, rel, flavor: 'sql', fields, refs: [...refs] });
96
+ }
97
+ return out;
98
+ }
99
+ /** Python: SQLAlchemy / Django / Pydantic / dataclass entities. */
100
+ export function extractPythonEntities(src, rel) {
101
+ const out = [];
102
+ /* class X(Base|models.Model|BaseModel|...) or @dataclass\nclass X: */
103
+ const classRe = /(^|\n)[ \t]*(@dataclass[\s\S]{0,40}?)?class\s+([A-Za-z_]\w*)\s*(?:\(([^)]*)\))?\s*:/g;
104
+ let m;
105
+ while ((m = classRe.exec(src)) !== null) {
106
+ const decorator = m[2] || '';
107
+ const name = m[3];
108
+ const bases = (m[4] || '').toLowerCase();
109
+ const isOrm = /\bbase\b|models\.model|declarative|sqlmodel/.test(bases);
110
+ const isSchema = /basemodel|pydantic|sqlmodel/.test(bases);
111
+ const isDataclass = /@dataclass/.test(decorator);
112
+ if (!isOrm && !isSchema && !isDataclass)
113
+ continue;
114
+ /* body = until the next top-level `class ` / EOF */
115
+ const start = classRe.lastIndex;
116
+ const restMatch = src.slice(start).match(/\n(?=class\s|\S)/);
117
+ const bodyEnd = restMatch && restMatch.index != null ? start + restMatch.index : Math.min(src.length, start + 4000);
118
+ const body = src.slice(start, bodyEnd);
119
+ const fields = [];
120
+ const refs = new Set();
121
+ /* SQLAlchemy column / Django field / annotated attr */
122
+ for (const fm of body.matchAll(/(^|\n)[ \t]+([a-z_]\w*)\s*[:=]/gi)) {
123
+ const f = fm[2];
124
+ if (f.startsWith('__') || ['class', 'def', 'return', 'self'].includes(f))
125
+ continue;
126
+ if (!fields.includes(f))
127
+ fields.push(f);
128
+ }
129
+ /* relationships: ForeignKey('other.id'), relationship('Other'),
130
+ * models.ForeignKey(Other), ForeignKey(Other, ...) */
131
+ for (const rm of body.matchAll(/(?:ForeignKey|OneToOneField|ManyToManyField|relationship)\s*\(\s*['"]?([A-Za-z_][\w.]*)/g)) {
132
+ const target = rm[1].split('.')[0]; /* 'other.id' -> 'other' ; 'Other' -> 'Other' */
133
+ if (target && target.toLowerCase() !== name.toLowerCase())
134
+ refs.add(target);
135
+ }
136
+ out.push({ name, rel, flavor: isOrm ? 'sqlalchemy/django' : (isDataclass ? 'dataclass' : 'pydantic'), fields, refs: [...refs] });
137
+ }
138
+ return out;
139
+ }
140
+ /** Prisma schema: model X { field Type ... }. */
141
+ export function extractPrismaModels(src, rel) {
142
+ const out = [];
143
+ const re = /model\s+([A-Za-z_]\w*)\s*\{([\s\S]*?)\}/g;
144
+ let m;
145
+ while ((m = re.exec(src)) !== null) {
146
+ const name = m[1];
147
+ const body = m[2] || '';
148
+ const fields = [];
149
+ const refs = new Set();
150
+ for (const line of body.split('\n')) {
151
+ const fm = line.trim().match(/^([A-Za-z_]\w*)\s+([A-Za-z_]\w*)(\[\])?/);
152
+ if (!fm)
153
+ continue;
154
+ const field = fm[1];
155
+ const type = fm[2];
156
+ if (field.startsWith('@'))
157
+ continue;
158
+ fields.push(field);
159
+ /* a field whose type is another model name (capitalised, not a scalar) */
160
+ if (/^[A-Z]/.test(type) && !['String', 'Int', 'Boolean', 'DateTime', 'Float', 'Json', 'Bytes', 'Decimal', 'BigInt'].includes(type)) {
161
+ if (type.toLowerCase() !== name.toLowerCase())
162
+ refs.add(type);
163
+ }
164
+ }
165
+ out.push({ name, rel, flavor: 'prisma', fields, refs: [...refs] });
166
+ }
167
+ return out;
168
+ }
169
+ /** TypeORM: @Entity() class X + @Column + @ManyToOne(() => Y). */
170
+ export function extractTypeOrmEntities(src, rel) {
171
+ if (!/@Entity\s*\(/.test(src))
172
+ return [];
173
+ const out = [];
174
+ const re = /@Entity\s*\([^)]*\)\s*(?:export\s+)?class\s+([A-Za-z_]\w*)([\s\S]*?)(?=\n@Entity|\nexport\s+class|\nclass\s|$)/g;
175
+ let m;
176
+ while ((m = re.exec(src)) !== null) {
177
+ const name = m[1];
178
+ const body = m[2] || '';
179
+ const fields = [];
180
+ const refs = new Set();
181
+ for (const cm of body.matchAll(/@Column\s*\([^)]*\)\s*([A-Za-z_]\w*)/g))
182
+ fields.push(cm[1]);
183
+ for (const rm of body.matchAll(/@(?:ManyToOne|OneToMany|ManyToMany|OneToOne)\s*\(\s*\(\)\s*=>\s*([A-Za-z_]\w*)/g)) {
184
+ if (rm[1] && rm[1].toLowerCase() !== name.toLowerCase())
185
+ refs.add(rm[1]);
186
+ }
187
+ out.push({ name, rel, flavor: 'typeorm', fields, refs: [...refs] });
188
+ }
189
+ return out;
190
+ }
191
+ function entitiesForFile(src, rel) {
192
+ const ext = rel.slice(rel.lastIndexOf('.') + 1).toLowerCase();
193
+ if (ext === 'sql')
194
+ return extractSqlTables(src, rel);
195
+ if (ext === 'prisma')
196
+ return extractPrismaModels(src, rel);
197
+ /* Code files: the language-specific extractor PLUS embedded SQL --
198
+ * Python/JS frequently ship the schema as CREATE TABLE strings inside
199
+ * the code (e.g. sqlite3 cursor.execute), which the ORM extractor would
200
+ * miss. Both run; extractDataModel dedupes by entity name. */
201
+ const out = [];
202
+ if (ext === 'py')
203
+ out.push(...extractPythonEntities(src, rel));
204
+ if (ext === 'ts' || ext === 'js')
205
+ out.push(...extractTypeOrmEntities(src, rel));
206
+ if (ext === 'py' || ext === 'ts' || ext === 'js') {
207
+ if (/create\s+table/i.test(src))
208
+ out.push(...extractSqlTables(src, rel));
209
+ }
210
+ return out;
211
+ }
212
+ /** Discover the project's data model -> datamodel nodes + reference edges. */
213
+ export async function extractDataModel(projectRoot) {
214
+ const now = new Date().toISOString();
215
+ const files = await walkSchemaFiles(projectRoot);
216
+ const capped = files.length >= MAX_FILES;
217
+ /* name (lowercased) -> entity id, so refs resolve even across flavors. */
218
+ const byName = new Map();
219
+ for (const abs of files) {
220
+ let src;
221
+ try {
222
+ const st = await fs.stat(abs);
223
+ if (st.size > MAX_BYTES_PER_FILE)
224
+ continue;
225
+ src = await fs.readFile(abs, 'utf-8');
226
+ }
227
+ catch {
228
+ continue;
229
+ }
230
+ if (byName.size >= MAX_ENTITIES)
231
+ break;
232
+ const rel = path.relative(projectRoot, abs).replace(/\\/g, '/');
233
+ for (const ent of entitiesForFile(src, rel)) {
234
+ if (byName.size >= MAX_ENTITIES)
235
+ break;
236
+ const key = ent.name.toLowerCase();
237
+ const prev = byName.get(key);
238
+ /* merge duplicate declarations (e.g. SQL + ORM of the same table) */
239
+ if (prev) {
240
+ for (const f of ent.fields)
241
+ if (!prev.fields.includes(f))
242
+ prev.fields.push(f);
243
+ for (const r of ent.refs)
244
+ if (!prev.refs.includes(r))
245
+ prev.refs.push(r);
246
+ }
247
+ else {
248
+ byName.set(key, { ...ent });
249
+ }
250
+ }
251
+ }
252
+ const nodes = [];
253
+ for (const ent of byName.values()) {
254
+ const shown = ent.fields.slice(0, MAX_FIELDS_SHOWN);
255
+ const more = ent.fields.length > shown.length ? ' +' + (ent.fields.length - shown.length) + ' mas' : '';
256
+ const purpose = 'Entidad/tabla "' + ent.name + '" (' + ent.flavor + ', '
257
+ + ent.fields.length + ' campos'
258
+ + (shown.length ? ': ' + shown.join(', ') + more : '') + ') [' + ent.rel + ']';
259
+ nodes.push({
260
+ id: dmId(ent.name), kind: 'datamodel', name: ent.name,
261
+ source: 'data-model',
262
+ purpose: purpose.length > 200 ? purpose.slice(0, 197) + '...' : purpose,
263
+ solves: [], do_not_invent: [], api_surface: shown,
264
+ confidence: 'auto', weight: 0.75, added_at: now, updated_at: now,
265
+ });
266
+ }
267
+ const ids = new Set(nodes.map((n) => n.id));
268
+ const edgeMap = new Map();
269
+ let relations = 0;
270
+ for (const ent of byName.values()) {
271
+ const from = dmId(ent.name);
272
+ for (const ref of ent.refs) {
273
+ const to = dmId(ref);
274
+ if (to === from || !ids.has(to))
275
+ continue; /* no dangling */
276
+ const id = 'references:' + from + '->' + to;
277
+ if (!edgeMap.has(id)) {
278
+ edgeMap.set(id, { id, kind: 'references', from_id: from, to_id: to, weight: 0.7, source: 'data-model', created_at: now });
279
+ relations++;
280
+ }
281
+ }
282
+ }
283
+ return {
284
+ nodes, edges: [...edgeMap.values()],
285
+ stats: { entity_nodes: nodes.length, relation_edges: relations, capped },
286
+ };
287
+ }
288
+ //# sourceMappingURL=data_model.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data_model.js","sourceRoot":"","sources":["../../src/semantic_graph/data_model.ts"],"names":[],"mappings":"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"}
@@ -10,9 +10,12 @@ export interface DerivedGraph {
10
10
  module_nodes: number;
11
11
  lib_nodes: number;
12
12
  tooling_nodes: number;
13
+ datamodel_nodes: number;
13
14
  module_edges: number;
14
15
  lib_edges: number;
16
+ relation_edges: number;
15
17
  skipped_dangling: number;
18
+ capped: boolean;
16
19
  };
17
20
  }
18
21
  /** Tooling we discover GENERICALLY across any repo (this ships to
@@ -1 +1 @@
1
- {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/semantic_graph/derive.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAoGvD;;wDAEwD;AACxD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAchE;AA+CD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAA;KAAE,CAAC;CAC9I;AAuDD;;;;;;;;;sCASsC;AACtC,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CA2F/E;AAoBD;sDACsD;AACtD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,YAAY,CAAC,CAqIxB;AAED;;qDAEqD;AACrD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAYtD"}
1
+ {"version":3,"file":"derive.d.ts","sourceRoot":"","sources":["../../src/semantic_graph/derive.ts"],"names":[],"mappings":"AAoBA,OAAO,KAAK,EAAE,SAAS,EAAE,SAAS,EAAE,MAAM,YAAY,CAAC;AAsJvD;;wDAEwD;AACxD,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAchE;AA+CD,MAAM,WAAW,YAAY;IAC3B,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,KAAK,EAAE;QAAE,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,aAAa,EAAE,MAAM,CAAC;QAAC,eAAe,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,gBAAgB,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,OAAO,CAAA;KAAE,CAAC;CAChN;AAuDD;;;;;;;;;sCASsC;AACtC,wBAAsB,eAAe,CAAC,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,EAAE,CAAC,CA2F/E;AAoBD;sDACsD;AACtD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE;IAC7C,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,YAAY,CAAC,CAiMxB;AAED;;qDAEqD;AACrD,wBAAsB,oBAAoB,CAAC,IAAI,EAAE;IAC/C,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,SAAS,CAAC,EAAE,OAAO,KAAK,CAAC;CAC1B,GAAG,OAAO,CAAC,YAAY,CAAC,OAAO,CAAC,GAAG;IAAE,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAYtD"}
@@ -21,6 +21,9 @@ import path from 'node:path';
21
21
  import { ingestFromPackageJson } from '../chat/semantic_graph_ingest.js';
22
22
  import { upsertNodes, upsertEdges, pruneDanglingEdges, pruneStaleDerivedNodes } from './store.js';
23
23
  import { extractImportSpecifiersAst } from './ast_extract.js';
24
+ import { extractPythonImports } from './py_imports.js';
25
+ import { extractLangImports } from './lang_imports.js';
26
+ import { extractDataModel } from './data_model.js';
24
27
  /* PND-078 -- prefer the TS AST import walk (robust to comments/strings);
25
28
  * fall back to the regex for non-TS/JS files or on a parse failure. */
26
29
  const TS_JS_FILE = /\.(tsx?|jsx?|mjs|cjs)$/i;
@@ -120,6 +123,66 @@ async function collectLooseFiles(root, parent, add) {
120
123
  add(parent ? parent + '/' + e : e, e);
121
124
  }
122
125
  }
126
+ /** Top-level dirs already handled by discoverModuleDirs/discoverLooseFiles
127
+ * (their subtrees are scanned there). Everything ELSE in the tree is found
128
+ * by discoverExtraSourceFiles so a project whose code lives outside these
129
+ * (e.g. OSDE's demo_hitl/) is no longer invisible. */
130
+ const FIXED_ROOTS = new Set(['src', 'packages', 'apps', 'app', 'components']);
131
+ const MAX_EXTRA_FILE_NODES = 600;
132
+ /** Recursively discover source FILES anywhere in the tree EXCEPT under the
133
+ * fixed roots (handled elsewhere) and excluded dirs. One node per file so a
134
+ * flat code dir (demo_hitl/ has 73 .py in a single folder) exposes its
135
+ * files + their import edges instead of collapsing to one dot. Bounded by
136
+ * MAX_EXTRA_FILE_NODES; `capped` tells the caller to surface the cut. */
137
+ async function discoverExtraSourceFiles(projectRoot) {
138
+ const seen = new Set();
139
+ const files = [];
140
+ let capped = false;
141
+ const add = (rel, base) => {
142
+ if (files.length >= MAX_EXTRA_FILE_NODES) {
143
+ capped = true;
144
+ return;
145
+ }
146
+ const noExt = base.replace(/\.[^.]+$/, '');
147
+ const id = 'module.' + sanitiseId(noExt);
148
+ if (seen.has(id))
149
+ return;
150
+ seen.add(id);
151
+ files.push({ id, name: base, rel: rel.replace(/\\/g, '/') });
152
+ };
153
+ const walk = async (relDir) => {
154
+ if (files.length >= MAX_EXTRA_FILE_NODES) {
155
+ capped = true;
156
+ return;
157
+ }
158
+ const abs = relDir ? path.join(projectRoot, relDir) : projectRoot;
159
+ let entries;
160
+ try {
161
+ entries = await fs.readdir(abs, { withFileTypes: true });
162
+ }
163
+ catch {
164
+ return;
165
+ }
166
+ for (const e of entries) {
167
+ if (isExcludedDir(e.name))
168
+ continue;
169
+ const childRel = relDir ? relDir + '/' + e.name : e.name;
170
+ if (e.isDirectory()) {
171
+ /* skip the fixed roots at top level (handled elsewhere) */
172
+ if (!relDir && FIXED_ROOTS.has(e.name))
173
+ continue;
174
+ await walk(childRel);
175
+ }
176
+ else if (e.isFile() && isSourceFile(e.name)) {
177
+ /* root-level loose files are already covered by discoverLooseFiles */
178
+ if (relDir)
179
+ add(childRel, e.name);
180
+ }
181
+ }
182
+ };
183
+ await walk('');
184
+ return { files, capped };
185
+ }
123
186
  /** npm/dir name -> id token (lowercase, alfanumerico + dashes). */
124
187
  function sanitiseId(name) {
125
188
  return name.replace(/^@/, '').replace(/[/]/g, '-').replace(/[^a-z0-9\-_]/gi, '-').toLowerCase();
@@ -175,7 +238,7 @@ async function walkSourceFiles(absDir) {
175
238
  }
176
239
  if (st.isDirectory())
177
240
  out.push(...await walkSourceFiles(full));
178
- else if (/\.(ts|tsx|js|jsx|mjs)$/.test(e) && !e.endsWith('.d.ts'))
241
+ else if (isSourceFile(e))
179
242
  out.push(full);
180
243
  }
181
244
  return out;
@@ -462,11 +525,26 @@ export async function deriveProjectGraph(opts) {
462
525
  }
463
526
  /* Loose source files (incl. .py) as their own nodes -- the dir-based
464
527
  * scan above misses a project whose code is a flat file (e.g.
465
- * src/robot.py). rel -> id so import edges can resolve to them. */
528
+ * src/robot.py). rel -> id so import edges can resolve to them.
529
+ * PLUS extra files anywhere outside the fixed roots (e.g. demo_hitl/),
530
+ * so a project whose code lives in a non-standard dir is no longer a
531
+ * handful of dots. */
466
532
  const looseFiles = await discoverLooseFiles(opts.project_root);
533
+ const extra = await discoverExtraSourceFiles(opts.project_root);
534
+ const looseSeen = new Set(looseFiles.map((f) => f.id));
535
+ for (const f of extra.files) {
536
+ if (!looseSeen.has(f.id)) {
537
+ looseSeen.add(f.id);
538
+ looseFiles.push(f);
539
+ }
540
+ }
467
541
  const looseByRel = new Map();
542
+ const byBasename = new Map(); /* file stem -> node id (Python resolution) */
468
543
  for (const lf of looseFiles) {
469
544
  looseByRel.set(lf.rel, lf.id);
545
+ const stem = (lf.name.replace(/\.[^.]+$/, '') || lf.name).toLowerCase();
546
+ if (!byBasename.has(stem))
547
+ byBasename.set(stem, lf.id);
470
548
  moduleNodes.push({
471
549
  id: lf.id, kind: 'module', name: lf.name,
472
550
  source: 'ast', purpose: 'Archivo ' + lf.name + ' (' + lf.rel + ').',
@@ -478,9 +556,14 @@ export async function deriveProjectGraph(opts) {
478
556
  * any repo so a customer's pipeline + helpers are DISCOVERED and land
479
557
  * in the prompt -- the model stops inventing build/deploy commands. */
480
558
  const toolingNodes = await discoverTooling(opts.project_root);
559
+ /* Data model (Pablo 2026-06-23): tables/entities + their relationships,
560
+ * extracted from SQL / ORMs / Prisma. The schema is the project's domain
561
+ * backbone; the graph was blind to it before. Self-contained nodes+edges
562
+ * (both ends are datamodel nodes). */
563
+ const dataModel = await extractDataModel(opts.project_root);
481
564
  const allNodeIds = new Set([
482
565
  ...mods.map((m) => m.id), ...libIds, ...looseFiles.map((f) => f.id),
483
- ...toolingNodes.map((t) => t.id),
566
+ ...toolingNodes.map((t) => t.id), ...dataModel.nodes.map((n) => n.id),
484
567
  ]);
485
568
  function moduleForFile(absFile) {
486
569
  const rel = path.relative(opts.project_root, absFile).replace(/\\/g, '/');
@@ -498,6 +581,32 @@ export async function deriveProjectGraph(opts) {
498
581
  const pkg = spec.startsWith('@') ? spec.split('/').slice(0, 2).join('/') : (spec.split('/')[0] || spec);
499
582
  return 'lib.' + sanitiseId(pkg);
500
583
  }
584
+ /* Resolve a Python import to a project node id (or null). Handles
585
+ * relative (from .mod / from .. import x) by walking up from the file's
586
+ * dir, and absolute dotted (import a.b / from a.b import c) by trying the
587
+ * dotted path as a file/package, then falling back to a basename match.
588
+ * byBasename holds only real node ids, so a fallback can never dangle. */
589
+ function resolvePyImport(imp, fileAbs) {
590
+ const parts = imp.module.split('.').filter(Boolean);
591
+ if (parts.length === 0)
592
+ return null;
593
+ const tryPath = (baseAbs) => {
594
+ const asFile = moduleForFile(path.join(baseAbs, ...parts) + '.py');
595
+ if (asFile)
596
+ return asFile;
597
+ return moduleForFile(path.join(baseAbs, ...parts, '__init__.py'));
598
+ };
599
+ if (imp.dots > 0) {
600
+ let baseDir = path.dirname(fileAbs);
601
+ for (let i = 1; i < imp.dots; i++)
602
+ baseDir = path.dirname(baseDir);
603
+ return tryPath(baseDir) || byBasename.get(parts[parts.length - 1].toLowerCase()) || null;
604
+ }
605
+ return tryPath(opts.project_root)
606
+ || byBasename.get(parts[parts.length - 1].toLowerCase())
607
+ || byBasename.get(parts[0].toLowerCase())
608
+ || null;
609
+ }
501
610
  /* Aristas. */
502
611
  const edgeMap = new Map();
503
612
  let skipped = 0;
@@ -512,56 +621,68 @@ export async function deriveProjectGraph(opts) {
512
621
  if (!edgeMap.has(id))
513
622
  edgeMap.set(id, { id, kind, from_id: from, to_id: to, weight: 0.6, source: 'ast', created_at: now });
514
623
  }
515
- for (const m of mods) {
516
- const files = await walkSourceFiles(path.join(opts.project_root, m.dir));
517
- for (const f of files) {
518
- const owner = moduleForFile(f) || m.id;
519
- let src;
520
- try {
521
- src = await fs.readFile(f, 'utf-8');
522
- }
523
- catch {
524
- continue;
525
- }
526
- for (const spec of importSpecifiersFor(src, f)) {
527
- if (spec.startsWith('node:') || spec.startsWith('bun:'))
528
- continue;
529
- if (spec.startsWith('.') || spec.startsWith('/')) {
530
- const target = moduleForFile(path.resolve(path.dirname(f), spec));
531
- if (target)
532
- addEdge(owner, target, 'uses');
533
- }
534
- else {
535
- addEdge(owner, libIdFor(spec), 'depends_on');
536
- }
537
- }
624
+ /* Parse a file's imports (language-aware) and add resolved edges from
625
+ * ownerId. JS/TS uses the AST/regex specifier walk; .py uses the Python
626
+ * import grammar. Non-(JS|TS|PY) source files yield no specifiers. */
627
+ /* Resolve a relative import (Ruby require_relative / PHP require path) to a
628
+ * project node, trying the bare ref and the common code extensions. */
629
+ function resolveRelByExt(dirAbs, ref) {
630
+ for (const c of [ref, ref + '.rb', ref + '.php', ref + '.go', ref + '/index.php', ref + '/index.rb']) {
631
+ const t = moduleForFile(path.resolve(dirAbs, c));
632
+ if (t)
633
+ return t;
538
634
  }
635
+ return null;
539
636
  }
540
- /* Loose-file imports (JS/TS specifiers only; .py yields none -- the
541
- * Python-import grammar is not parsed here, so loose .py files appear
542
- * as nodes without edges, which is correct for now). */
543
- for (const lf of looseFiles) {
544
- const abs = path.join(opts.project_root, lf.rel);
637
+ async function addEdgesForFile(absFile, ownerId) {
545
638
  let src;
546
639
  try {
547
- src = await fs.readFile(abs, 'utf-8');
640
+ src = await fs.readFile(absFile, 'utf-8');
548
641
  }
549
642
  catch {
550
- continue;
643
+ return;
644
+ }
645
+ if (/\.py$/i.test(absFile)) {
646
+ for (const imp of extractPythonImports(src)) {
647
+ const target = resolvePyImport(imp, absFile);
648
+ if (target)
649
+ addEdge(ownerId, target, 'uses');
650
+ }
651
+ return;
551
652
  }
552
- for (const spec of importSpecifiersFor(src, abs)) {
653
+ const ext = absFile.slice(absFile.lastIndexOf('.') + 1).toLowerCase();
654
+ if (ext === 'go' || ext === 'rb' || ext === 'php') {
655
+ for (const r of extractLangImports(src, ext)) {
656
+ const target = r.relative
657
+ ? resolveRelByExt(path.dirname(absFile), r.ref)
658
+ : (byBasename.get(r.ref.toLowerCase()) ?? null);
659
+ if (target)
660
+ addEdge(ownerId, target, 'uses');
661
+ }
662
+ return;
663
+ }
664
+ for (const spec of importSpecifiersFor(src, absFile)) {
553
665
  if (spec.startsWith('node:') || spec.startsWith('bun:'))
554
666
  continue;
555
667
  if (spec.startsWith('.') || spec.startsWith('/')) {
556
- const target = moduleForFile(path.resolve(path.dirname(abs), spec));
668
+ const target = moduleForFile(path.resolve(path.dirname(absFile), spec));
557
669
  if (target)
558
- addEdge(lf.id, target, 'uses');
670
+ addEdge(ownerId, target, 'uses');
559
671
  }
560
672
  else {
561
- addEdge(lf.id, libIdFor(spec), 'depends_on');
673
+ addEdge(ownerId, libIdFor(spec), 'depends_on');
562
674
  }
563
675
  }
564
676
  }
677
+ for (const m of mods) {
678
+ const files = await walkSourceFiles(path.join(opts.project_root, m.dir));
679
+ for (const f of files)
680
+ await addEdgesForFile(f, moduleForFile(f) || m.id);
681
+ }
682
+ /* Loose + extra files (incl. .py with real Python-import edges now). */
683
+ for (const lf of looseFiles) {
684
+ await addEdgesForFile(path.join(opts.project_root, lf.rel), lf.id);
685
+ }
565
686
  /* Los nodos de libreria del ingest no traen added_at/updated_at
566
687
  * (su shape es IngestedNode); normalizarlos para el store. */
567
688
  const libNodes = libIngest.map((l) => ({
@@ -569,17 +690,20 @@ export async function deriveProjectGraph(opts) {
569
690
  added_at: now,
570
691
  updated_at: now,
571
692
  }));
572
- const edges = [...edgeMap.values()];
693
+ const edges = [...edgeMap.values(), ...dataModel.edges];
573
694
  return {
574
- nodes: [...moduleNodes, ...libNodes, ...toolingNodes],
695
+ nodes: [...moduleNodes, ...libNodes, ...toolingNodes, ...dataModel.nodes],
575
696
  edges,
576
697
  stats: {
577
698
  module_nodes: moduleNodes.length,
578
699
  lib_nodes: libIngest.length,
579
700
  tooling_nodes: toolingNodes.length,
701
+ datamodel_nodes: dataModel.nodes.length,
580
702
  module_edges: edges.filter((e) => e.to_id.startsWith('module.')).length,
581
703
  lib_edges: edges.filter((e) => e.to_id.startsWith('lib.')).length,
704
+ relation_edges: dataModel.edges.length,
582
705
  skipped_dangling: skipped,
706
+ capped: extra.capped || dataModel.stats.capped,
583
707
  },
584
708
  };
585
709
  }