@polycode-projects/seonix 0.3.0 → 0.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +93 -23
- package/bin/cli.mjs +27 -4
- package/package.json +1 -1
- package/src/ask-vocab.mjs +262 -2
- package/src/ask.mjs +1025 -8
- package/src/browser.mjs +87 -13
- package/src/chat.mjs +644 -47
- package/src/codegraph.mjs +177 -2
- package/src/nlp-bundle.mjs +120 -0
- package/src/prose.mjs +42 -0
- package/src/temporal.mjs +70 -0
- package/src/timeline.mjs +51 -8
- package/src/viz.mjs +88 -19
package/src/browser.mjs
CHANGED
|
@@ -14,7 +14,7 @@ import { execFile } from "node:child_process";
|
|
|
14
14
|
import { dirname, join } from "node:path";
|
|
15
15
|
import { fileURLToPath } from "node:url";
|
|
16
16
|
import { promisify } from "node:util";
|
|
17
|
-
import { deriveNodeInterval, deriveEdgeInterval } from "./temporal.mjs";
|
|
17
|
+
import { deriveNodeInterval, deriveEdgeInterval, repoOfId, assignRepo, isMultiRepo } from "./temporal.mjs";
|
|
18
18
|
|
|
19
19
|
const here = dirname(fileURLToPath(import.meta.url));
|
|
20
20
|
const execFileP = promisify(execFile);
|
|
@@ -79,8 +79,22 @@ const attr = (ind, key) => {
|
|
|
79
79
|
return a ? String(a.value) : "";
|
|
80
80
|
};
|
|
81
81
|
|
|
82
|
-
/**
|
|
83
|
-
|
|
82
|
+
/**
|
|
83
|
+
* Ordered (oldest → newest) shas for the graph's Commit individuals via git log.
|
|
84
|
+
*
|
|
85
|
+
* A MERGED multi-repo graph (extract.mjs indexRepositories) has no single git repo to
|
|
86
|
+
* shell — `repo` is the common-ancestor out_root, which is usually not itself a git
|
|
87
|
+
* checkout — so `git log` there returns nothing that matches. In that case, if the
|
|
88
|
+
* caller supplies `datesBySha` (commit sha → ISO commit date), fall back to a global
|
|
89
|
+
* chronological order across all repos. Single-repo callers pass no dates and keep the
|
|
90
|
+
* existing git path unchanged. (buildTemporalGraph independently re-derives the
|
|
91
|
+
* date-order when it detects a merge, so a viz caller that passes only cwd is still
|
|
92
|
+
* correct; this fallback makes gitCommitOrder itself repo-set-agnostic and testable.)
|
|
93
|
+
* @param {string} repo
|
|
94
|
+
* @param {Iterable<string>} graphCommitIds
|
|
95
|
+
* @param {{datesBySha?: Map<string,string>|null}} [opts]
|
|
96
|
+
*/
|
|
97
|
+
export async function gitCommitOrder(repo, graphCommitIds, { datesBySha = null } = {}) {
|
|
84
98
|
const shas = new Set([...graphCommitIds].map((id) => String(id).replace(/^commit:/, "")));
|
|
85
99
|
let ordered = [];
|
|
86
100
|
try {
|
|
@@ -89,7 +103,18 @@ export async function gitCommitOrder(repo, graphCommitIds) {
|
|
|
89
103
|
});
|
|
90
104
|
ordered = stdout.trim().split("\n").reverse().filter((s) => shas.has(s));
|
|
91
105
|
} catch {
|
|
92
|
-
/* fall through to graph order */
|
|
106
|
+
/* fall through to graph/date order */
|
|
107
|
+
}
|
|
108
|
+
if (!ordered.length && datesBySha) {
|
|
109
|
+
// Merged-graph fallback: one commit date axis across every repo (tie-break by sha
|
|
110
|
+
// so equal timestamps stay deterministic).
|
|
111
|
+
ordered = [...shas].sort((a, b) => {
|
|
112
|
+
const da = datesBySha.get(a) || "";
|
|
113
|
+
const db = datesBySha.get(b) || "";
|
|
114
|
+
if (da < db) return -1;
|
|
115
|
+
if (da > db) return 1;
|
|
116
|
+
return a < b ? -1 : a > b ? 1 : 0;
|
|
117
|
+
});
|
|
93
118
|
}
|
|
94
119
|
if (!ordered.length) ordered = [...shas];
|
|
95
120
|
return ordered;
|
|
@@ -137,9 +162,40 @@ export function buildTemporalGraph(raw, orderShas, { scope = "product", parentsB
|
|
|
137
162
|
|
|
138
163
|
const commitInds = individuals.filter((i) => i.class === "Commit");
|
|
139
164
|
const commitById = new Map(commitInds.map((c) => [c.id, c]));
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
165
|
+
|
|
166
|
+
// P4 cross-repo: a commit's repo is inferred from the id-prefixes of the modules it
|
|
167
|
+
// touches (touchesSymbol edges) — commits themselves are unprefixed (one per sha).
|
|
168
|
+
// This is order-independent, so we can detect a merge BEFORE choosing the time axis.
|
|
169
|
+
const prefixByCommit = new Map(); // commit id -> Map<repoPrefix, touch count>
|
|
170
|
+
for (const g of groups) {
|
|
171
|
+
if (kindOf(g) !== "touchesSymbol") continue;
|
|
172
|
+
for (const e of g.examples || []) {
|
|
173
|
+
const prefix = repoOfId(e.object);
|
|
174
|
+
if (!prefix || !commitById.has(e.subject)) continue;
|
|
175
|
+
let m = prefixByCommit.get(e.subject);
|
|
176
|
+
if (!m) { m = new Map(); prefixByCommit.set(e.subject, m); }
|
|
177
|
+
m.set(prefix, (m.get(prefix) || 0) + 1);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
const multi = isMultiRepo(commitInds.map((c) => prefixByCommit.get(c.id) || new Map()));
|
|
181
|
+
const repoOfCommitId = (id) => (multi ? assignRepo(prefixByCommit.get(id) || new Map()) : null);
|
|
182
|
+
|
|
183
|
+
// Single-repo: git-log order (or the given order) exactly as before. Merged graph:
|
|
184
|
+
// there is no single repo to shell, so order by the stored commit date across ALL
|
|
185
|
+
// repos — one global time axis, tie-broken by sha for determinism.
|
|
186
|
+
const order = multi
|
|
187
|
+
? [...commitInds]
|
|
188
|
+
.sort((a, b) => {
|
|
189
|
+
const da = attr(a, "date");
|
|
190
|
+
const db = attr(b, "date");
|
|
191
|
+
if (da < db) return -1;
|
|
192
|
+
if (da > db) return 1;
|
|
193
|
+
return a.id < b.id ? -1 : a.id > b.id ? 1 : 0;
|
|
194
|
+
})
|
|
195
|
+
.map((c) => String(c.id).replace(/^commit:/, ""))
|
|
196
|
+
: (orderShas && orderShas.length)
|
|
197
|
+
? orderShas
|
|
198
|
+
: commitInds.map((c) => String(c.id).replace(/^commit:/, ""));
|
|
143
199
|
const ordinalOf = new Map(order.map((sha, i) => [`commit:${sha}`, i]));
|
|
144
200
|
const headIdx = Math.max(0, order.length - 1);
|
|
145
201
|
const commits = order.map((sha, i) => {
|
|
@@ -155,7 +211,7 @@ export function buildTemporalGraph(raw, orderShas, { scope = "product", parentsB
|
|
|
155
211
|
if (pi != null) parentIdx.push(pi);
|
|
156
212
|
else ghostParents += 1;
|
|
157
213
|
}
|
|
158
|
-
|
|
214
|
+
const commit = {
|
|
159
215
|
idx: i,
|
|
160
216
|
sha,
|
|
161
217
|
shortSha: sha.slice(0, 12),
|
|
@@ -166,6 +222,12 @@ export function buildTemporalGraph(raw, orderShas, { scope = "product", parentsB
|
|
|
166
222
|
ghostParents,
|
|
167
223
|
merge: parentIdx.length + ghostParents > 1,
|
|
168
224
|
};
|
|
225
|
+
// P4: tag with the owning repo ONLY on a merged graph (single-repo stays identical).
|
|
226
|
+
if (multi) {
|
|
227
|
+
const r = repoOfCommitId(`commit:${sha}`);
|
|
228
|
+
if (r) commit.repo = r;
|
|
229
|
+
}
|
|
230
|
+
return commit;
|
|
169
231
|
});
|
|
170
232
|
|
|
171
233
|
const symInds = individuals.filter((i) => i.class !== "Commit" && inScope(i.id));
|
|
@@ -232,7 +294,7 @@ export function buildTemporalGraph(raw, orderShas, { scope = "product", parentsB
|
|
|
232
294
|
const touches = [...(touchesByNode.get(ind.id) || [])].sort((a, b) => a - b);
|
|
233
295
|
const [born, died] = deriveNodeInterval(touches, headIdx, fallbackBorn(ind.id));
|
|
234
296
|
bornOf.set(ind.id, born);
|
|
235
|
-
|
|
297
|
+
const node = {
|
|
236
298
|
id: ind.id,
|
|
237
299
|
type: ind.class,
|
|
238
300
|
label: ind.label || ind.id,
|
|
@@ -241,13 +303,19 @@ export function buildTemporalGraph(raw, orderShas, { scope = "product", parentsB
|
|
|
241
303
|
died,
|
|
242
304
|
touches,
|
|
243
305
|
churn: touches.length,
|
|
244
|
-
}
|
|
306
|
+
};
|
|
307
|
+
// P4: a symbol's repo is right in its id prefix on a merged graph — carry it so the
|
|
308
|
+
// browser can badge by repo without re-parsing. Guarded → single-repo unchanged.
|
|
309
|
+
if (multi) { const r = repoOfId(ind.id); if (r) node.repo = r; }
|
|
310
|
+
nodes.push(node);
|
|
245
311
|
}
|
|
246
312
|
for (const c of commits) {
|
|
247
|
-
|
|
313
|
+
const node = {
|
|
248
314
|
id: `commit:${c.sha}`, type: "Commit", label: c.shortSha, site: "",
|
|
249
315
|
born: c.idx, died: headIdx, touches: [], churn: 0, commitIdx: c.idx,
|
|
250
|
-
}
|
|
316
|
+
};
|
|
317
|
+
if (multi && c.repo) node.repo = c.repo;
|
|
318
|
+
nodes.push(node);
|
|
251
319
|
bornOf.set(`commit:${c.sha}`, c.idx);
|
|
252
320
|
}
|
|
253
321
|
|
|
@@ -289,9 +357,13 @@ export function buildTemporalGraph(raw, orderShas, { scope = "product", parentsB
|
|
|
289
357
|
return {
|
|
290
358
|
meta: {
|
|
291
359
|
scope,
|
|
292
|
-
commit_order:
|
|
360
|
+
commit_order: multi
|
|
361
|
+
? "oldest-first by commit date across all repos (merged multi-repo graph)"
|
|
362
|
+
: "oldest-first (ordinal 0 = oldest, N-1 = HEAD)",
|
|
293
363
|
note: "died = HEAD for every node: a HEAD-only index cannot observe deletions.",
|
|
294
364
|
counts: { commits: commits.length, nodes: nodes.length, edges: edges.length, touchesSymbol_edges: tsEdges.length },
|
|
365
|
+
// P4: present ONLY on a merged graph, so single-repo meta is byte-identical.
|
|
366
|
+
...(multi ? { multi_repo: true, repos: [...new Set(commits.map((c) => c.repo).filter(Boolean))].sort() } : {}),
|
|
295
367
|
},
|
|
296
368
|
commits,
|
|
297
369
|
nodes,
|
|
@@ -701,6 +773,7 @@ function drawTimeline(){
|
|
|
701
773
|
const mergeNote=c&&c.merge?' <span style="color:#565f89">· ⑂ merge ('+(c.parentIdx.length+c.ghostParents)+' parent'+((c.parentIdx.length+c.ghostParents)===1?'':'s')+')</span>':'';
|
|
702
774
|
$('tlmeta').innerHTML=(c?('<b>'+esc(c.shortSha)+'</b> · '+(c.date||'').slice(0,10)+' · '+esc(c.author)+' — '+esc(c.subject)):'')
|
|
703
775
|
+mergeNote
|
|
776
|
+
+(c&&c.repo?' <span style="color:#7aa2f7">· ⛃ '+esc(c.repo)+'</span>':'')
|
|
704
777
|
+(S.cursorB!=null?' <span style="color:#e0af68">· B: '+esc(shortAt(S.cursorB))+'</span>':'')
|
|
705
778
|
+' <span style="color:#565f89">('+(S.cursor+1)+' / '+S.tg.commits.length+')</span>';
|
|
706
779
|
}
|
|
@@ -735,6 +808,7 @@ function showBiography(id){
|
|
|
735
808
|
:'';
|
|
736
809
|
$('detail').innerHTML='<h3>'+esc(c.shortSha)+'</h3>'
|
|
737
810
|
+'<span class="badge"><i style="background:'+COLORS.Commit+'"></i>Commit '+(c.idx+1)+' of '+S.tg.commits.length+'</span>'
|
|
811
|
+
+(c.repo?' <span class="badge">repo '+esc(c.repo)+'</span>':'')
|
|
738
812
|
+'<div class="row">'+esc(c.author)+' · '+(c.date||'').slice(0,10)+'</div><div class="row"><em>'+esc(c.subject)+'</em></div>'
|
|
739
813
|
+'<div class="row">'+(url?'<a class="btn" href="'+url+'" target="_blank" rel="noopener">open in GitLab ↗</a>':'')
|
|
740
814
|
+'<button class="btn" id="gocommit">scrub here</button></div>'
|