truecourse 0.7.0-next.5 → 0.7.0-next.7
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/cli.mjs +281 -69
- package/package.json +1 -1
- package/public/assets/{index-CI8n7IHP.js → index-D7hFPhQQ.js} +203 -203
- package/public/index.html +1 -1
- package/roslyn-host/csharp-roslyn-host.dll +0 -0
- package/roslyn-host/csharp-roslyn-host.pdb +0 -0
- package/server.mjs +207 -21
package/cli.mjs
CHANGED
|
@@ -105837,6 +105837,153 @@ var init_guard = __esm({
|
|
|
105837
105837
|
}
|
|
105838
105838
|
});
|
|
105839
105839
|
|
|
105840
|
+
// packages/shared/dist/spec/overlap-resolution.js
|
|
105841
|
+
function coveringRelationInAreas(relations, a, b, areas) {
|
|
105842
|
+
return relations.find((r) => {
|
|
105843
|
+
const samePair = r.older === a && r.newer === b || r.older === b && r.newer === a;
|
|
105844
|
+
return samePair && (r.scope === void 0 || areas.includes(r.scope));
|
|
105845
|
+
});
|
|
105846
|
+
}
|
|
105847
|
+
function dedupeCrossAreaOverlaps(entries) {
|
|
105848
|
+
const byPair = /* @__PURE__ */ new Map();
|
|
105849
|
+
for (const e of entries) {
|
|
105850
|
+
const key4 = unorderedPairKey(e.overlap.docs[0], e.overlap.docs[1]);
|
|
105851
|
+
const list = byPair.get(key4);
|
|
105852
|
+
if (list)
|
|
105853
|
+
list.push(e);
|
|
105854
|
+
else
|
|
105855
|
+
byPair.set(key4, [e]);
|
|
105856
|
+
}
|
|
105857
|
+
const merged = [];
|
|
105858
|
+
for (const members of byPair.values()) {
|
|
105859
|
+
const parent = members.map((_2, i) => i);
|
|
105860
|
+
const find = (i) => {
|
|
105861
|
+
while (parent[i] !== i) {
|
|
105862
|
+
parent[i] = parent[parent[i]];
|
|
105863
|
+
i = parent[i];
|
|
105864
|
+
}
|
|
105865
|
+
return i;
|
|
105866
|
+
};
|
|
105867
|
+
const union = (i, j2) => {
|
|
105868
|
+
const ri = find(i);
|
|
105869
|
+
const rj = find(j2);
|
|
105870
|
+
if (ri !== rj)
|
|
105871
|
+
parent[Math.max(ri, rj)] = Math.min(ri, rj);
|
|
105872
|
+
};
|
|
105873
|
+
const ptrOwner = /* @__PURE__ */ new Map();
|
|
105874
|
+
members.forEach((m, i) => {
|
|
105875
|
+
for (const ptr of sectionPointerKeys(m.overlap)) {
|
|
105876
|
+
const owner = ptrOwner.get(ptr);
|
|
105877
|
+
if (owner === void 0)
|
|
105878
|
+
ptrOwner.set(ptr, i);
|
|
105879
|
+
else
|
|
105880
|
+
union(owner, i);
|
|
105881
|
+
}
|
|
105882
|
+
});
|
|
105883
|
+
const components = /* @__PURE__ */ new Map();
|
|
105884
|
+
members.forEach((_2, i) => {
|
|
105885
|
+
const root2 = find(i);
|
|
105886
|
+
const list = components.get(root2);
|
|
105887
|
+
if (list)
|
|
105888
|
+
list.push(i);
|
|
105889
|
+
else
|
|
105890
|
+
components.set(root2, [i]);
|
|
105891
|
+
});
|
|
105892
|
+
for (const idxs of components.values()) {
|
|
105893
|
+
const group = idxs.map((i) => members[i]);
|
|
105894
|
+
const rep = [...group].sort((x, y) => {
|
|
105895
|
+
const px = preambleCount(x.overlap);
|
|
105896
|
+
const py = preambleCount(y.overlap);
|
|
105897
|
+
if (px !== py)
|
|
105898
|
+
return px - py;
|
|
105899
|
+
if (x.area !== y.area)
|
|
105900
|
+
return x.area < y.area ? -1 : 1;
|
|
105901
|
+
return (x.overlap.note ?? "") < (y.overlap.note ?? "") ? -1 : 1;
|
|
105902
|
+
})[0];
|
|
105903
|
+
const span = /* @__PURE__ */ new Set();
|
|
105904
|
+
for (const g of group) {
|
|
105905
|
+
span.add(g.area);
|
|
105906
|
+
for (const a of g.overlap.areas ?? [])
|
|
105907
|
+
span.add(a);
|
|
105908
|
+
}
|
|
105909
|
+
merged.push({ area: rep.area, areas: [...span].sort(), overlap: rep.overlap });
|
|
105910
|
+
}
|
|
105911
|
+
}
|
|
105912
|
+
merged.sort((x, y) => {
|
|
105913
|
+
if (x.area !== y.area)
|
|
105914
|
+
return x.area < y.area ? -1 : 1;
|
|
105915
|
+
return unorderedPairKey(x.overlap.docs[0], x.overlap.docs[1]) < unorderedPairKey(y.overlap.docs[0], y.overlap.docs[1]) ? -1 : 1;
|
|
105916
|
+
});
|
|
105917
|
+
return merged;
|
|
105918
|
+
}
|
|
105919
|
+
function buildCorpusConflicts(corpus, decisions) {
|
|
105920
|
+
const userRelations = decisions.relations ?? [];
|
|
105921
|
+
const effectiveRels = [...corpus.relations, ...userRelations];
|
|
105922
|
+
const excludes = new Set(decisions.manualExcludes ?? []);
|
|
105923
|
+
const entries = [];
|
|
105924
|
+
for (const area of corpus.areas)
|
|
105925
|
+
for (const ov of area.overlaps)
|
|
105926
|
+
entries.push({ area: area.id, overlap: ov });
|
|
105927
|
+
const mergedOverlaps = dedupeCrossAreaOverlaps(entries);
|
|
105928
|
+
const flagged = [];
|
|
105929
|
+
for (const m of mergedOverlaps) {
|
|
105930
|
+
const [a, b] = m.overlap.docs;
|
|
105931
|
+
const relation = coveringRelationInAreas(effectiveRels, a, b, m.areas);
|
|
105932
|
+
const userRelation = coveringRelationInAreas(userRelations, a, b, m.areas);
|
|
105933
|
+
const excludedRef = excludes.has(a) ? a : excludes.has(b) ? b : void 0;
|
|
105934
|
+
flagged.push({
|
|
105935
|
+
area: m.area,
|
|
105936
|
+
areas: m.areas,
|
|
105937
|
+
a,
|
|
105938
|
+
b,
|
|
105939
|
+
note: m.overlap.note ?? "",
|
|
105940
|
+
resolved: relation !== void 0 || excludedRef !== void 0,
|
|
105941
|
+
synthesized: false,
|
|
105942
|
+
...relation ? { relation } : {},
|
|
105943
|
+
...userRelation ? { userRelation } : {},
|
|
105944
|
+
...excludedRef ? { excludedRef } : {}
|
|
105945
|
+
});
|
|
105946
|
+
}
|
|
105947
|
+
const coversFlagged = (r) => flagged.some((o) => {
|
|
105948
|
+
const samePair = o.a === r.older && o.b === r.newer || o.a === r.newer && o.b === r.older;
|
|
105949
|
+
return samePair && (r.scope === void 0 || o.areas.includes(r.scope));
|
|
105950
|
+
});
|
|
105951
|
+
const synthesized = userRelations.filter((r) => !coversFlagged(r)).map((r) => ({
|
|
105952
|
+
area: r.scope ?? "",
|
|
105953
|
+
areas: r.scope ? [r.scope] : [],
|
|
105954
|
+
a: r.older,
|
|
105955
|
+
b: r.newer,
|
|
105956
|
+
note: r.note ?? "",
|
|
105957
|
+
resolved: true,
|
|
105958
|
+
synthesized: true,
|
|
105959
|
+
relation: r,
|
|
105960
|
+
userRelation: r
|
|
105961
|
+
}));
|
|
105962
|
+
return [...flagged, ...synthesized];
|
|
105963
|
+
}
|
|
105964
|
+
function openConflicts(corpus, decisions) {
|
|
105965
|
+
return buildCorpusConflicts(corpus, decisions).filter((c2) => !c2.resolved);
|
|
105966
|
+
}
|
|
105967
|
+
var PREAMBLE_PTR, NUL, unorderedPairKey, sectionPointerKeys, preambleCount;
|
|
105968
|
+
var init_overlap_resolution = __esm({
|
|
105969
|
+
"packages/shared/dist/spec/overlap-resolution.js"() {
|
|
105970
|
+
"use strict";
|
|
105971
|
+
PREAMBLE_PTR = "\0preamble";
|
|
105972
|
+
NUL = "\0";
|
|
105973
|
+
unorderedPairKey = (a, b) => a < b ? `${a}${NUL}${b}` : `${b}${NUL}${a}`;
|
|
105974
|
+
sectionPointerKeys = (ov) => (ov.sections ?? []).map((s) => `${s.doc}${NUL}${s.heading ?? PREAMBLE_PTR}`);
|
|
105975
|
+
preambleCount = (ov) => (ov.sections ?? []).filter((s) => s.heading === null || s.heading === void 0).length;
|
|
105976
|
+
}
|
|
105977
|
+
});
|
|
105978
|
+
|
|
105979
|
+
// packages/shared/dist/spec/index.js
|
|
105980
|
+
var init_spec = __esm({
|
|
105981
|
+
"packages/shared/dist/spec/index.js"() {
|
|
105982
|
+
"use strict";
|
|
105983
|
+
init_overlap_resolution();
|
|
105984
|
+
}
|
|
105985
|
+
});
|
|
105986
|
+
|
|
105840
105987
|
// packages/shared/dist/fs/tcignore.js
|
|
105841
105988
|
import fs11 from "node:fs";
|
|
105842
105989
|
import path13 from "node:path";
|
|
@@ -105960,6 +106107,7 @@ var init_dist5 = __esm({
|
|
|
105960
106107
|
init_types3();
|
|
105961
106108
|
init_schemas();
|
|
105962
106109
|
init_guard();
|
|
106110
|
+
init_spec();
|
|
105963
106111
|
init_tcignore();
|
|
105964
106112
|
init_skip_dirs();
|
|
105965
106113
|
init_spec_scope();
|
|
@@ -171028,7 +171176,7 @@ function readToolVersion() {
|
|
|
171028
171176
|
if (cachedVersion)
|
|
171029
171177
|
return cachedVersion;
|
|
171030
171178
|
if (true) {
|
|
171031
|
-
cachedVersion = "0.7.0-next.
|
|
171179
|
+
cachedVersion = "0.7.0-next.7";
|
|
171032
171180
|
return cachedVersion;
|
|
171033
171181
|
}
|
|
171034
171182
|
try {
|
|
@@ -197070,7 +197218,16 @@ var OverlapSchema = external_exports.object({
|
|
|
197070
197218
|
/** Short note on what may disagree ("auth0_id vs auth0_sub"). */
|
|
197071
197219
|
note: external_exports.string().default(""),
|
|
197072
197220
|
/** The specific conflicting sections per doc (markdown headings), when known. */
|
|
197073
|
-
sections: external_exports.array(OverlapSectionSchema).default([])
|
|
197221
|
+
sections: external_exports.array(OverlapSectionSchema).default([]),
|
|
197222
|
+
/**
|
|
197223
|
+
* The area ids this dispute spans. Detection runs per area, so one disagreement
|
|
197224
|
+
* on a doc pair sharing several areas is flagged in each; the cross-area merge
|
|
197225
|
+
* collapses those to this single record and lists every area it spanned here, so
|
|
197226
|
+
* a resolution scoped to any of them clears the dispute everywhere. Empty for
|
|
197227
|
+
* older corpora written before the merge — the read layer recomputes the span
|
|
197228
|
+
* from the per-area placement of the (then-duplicated) records.
|
|
197229
|
+
*/
|
|
197230
|
+
areas: external_exports.array(external_exports.string()).default([])
|
|
197074
197231
|
});
|
|
197075
197232
|
var AreaSchema = external_exports.object({
|
|
197076
197233
|
/** Canonical area id, `product/concern`. */
|
|
@@ -197845,6 +198002,7 @@ init_zod();
|
|
|
197845
198002
|
import { createHash as createHash7 } from "node:crypto";
|
|
197846
198003
|
import fs26 from "node:fs";
|
|
197847
198004
|
init_transport();
|
|
198005
|
+
init_dist5();
|
|
197848
198006
|
var DEFAULT_MAX_PAIRS_PER_AREA = 60;
|
|
197849
198007
|
async function flagOverlaps(repoRoot5, areas, docs, opts = {}) {
|
|
197850
198008
|
const result = /* @__PURE__ */ new Map();
|
|
@@ -197916,7 +198074,7 @@ async function flagOverlaps(repoRoot5, areas, docs, opts = {}) {
|
|
|
197916
198074
|
examineOne(repoRoot5, pair.areaId, pair.a, pair.b, runner).then((verdict) => {
|
|
197917
198075
|
if (verdict.overlap) {
|
|
197918
198076
|
const list = result.get(pair.areaId) ?? [];
|
|
197919
|
-
list.push({ docs: [pair.a.path, pair.b.path], note: verdict.note, sections: verdict.sections ?? [] });
|
|
198077
|
+
list.push({ docs: [pair.a.path, pair.b.path], note: verdict.note, sections: verdict.sections ?? [], areas: [] });
|
|
197920
198078
|
result.set(pair.areaId, list);
|
|
197921
198079
|
}
|
|
197922
198080
|
}).catch(() => {
|
|
@@ -197934,19 +198092,13 @@ async function flagOverlaps(repoRoot5, areas, docs, opts = {}) {
|
|
|
197934
198092
|
};
|
|
197935
198093
|
launch();
|
|
197936
198094
|
});
|
|
197937
|
-
const
|
|
197938
|
-
|
|
197939
|
-
|
|
197940
|
-
|
|
197941
|
-
|
|
197942
|
-
|
|
197943
|
-
|
|
197944
|
-
return true;
|
|
197945
|
-
});
|
|
197946
|
-
if (kept.length === 0)
|
|
197947
|
-
result.delete(areaId);
|
|
197948
|
-
else
|
|
197949
|
-
result.set(areaId, kept);
|
|
198095
|
+
const entries = [...result].flatMap(([area, list]) => list.map((overlap) => ({ area, overlap })));
|
|
198096
|
+
const merged = dedupeCrossAreaOverlaps(entries);
|
|
198097
|
+
result.clear();
|
|
198098
|
+
for (const m of merged) {
|
|
198099
|
+
const list = result.get(m.area) ?? [];
|
|
198100
|
+
list.push({ ...m.overlap, areas: m.areas });
|
|
198101
|
+
result.set(m.area, list);
|
|
197950
198102
|
}
|
|
197951
198103
|
for (const [areaId, list] of result) {
|
|
197952
198104
|
list.sort((x, y) => x.docs.join() < y.docs.join() ? -1 : 1);
|
|
@@ -197954,11 +198106,6 @@ async function flagOverlaps(repoRoot5, areas, docs, opts = {}) {
|
|
|
197954
198106
|
}
|
|
197955
198107
|
return result;
|
|
197956
198108
|
}
|
|
197957
|
-
function overlapDedupKey(ov) {
|
|
197958
|
-
const docs = [...ov.docs].sort().join("::");
|
|
197959
|
-
const secs = (ov.sections ?? []).map((s) => `${s.doc}|${s.heading}`).sort().join("::");
|
|
197960
|
-
return `${docs}##${secs}`;
|
|
197961
|
-
}
|
|
197962
198109
|
async function examineOne(repoRoot5, areaId, a, b, runner) {
|
|
197963
198110
|
const cacheKey = computeCacheKey4(areaId, a, b);
|
|
197964
198111
|
const cached = await readCache3(repoRoot5, cacheKey);
|
|
@@ -210356,6 +210503,41 @@ async function storeDecisions(repoKey, next, opts) {
|
|
|
210356
210503
|
}
|
|
210357
210504
|
await saveSpec({ repoKey, commitSha: decisionsRef(opts?.pr) }, "decisions", next);
|
|
210358
210505
|
}
|
|
210506
|
+
async function getDecisions(repoKey, opts) {
|
|
210507
|
+
if (opts?.pr === void 0)
|
|
210508
|
+
return loadDecisions(repoKey);
|
|
210509
|
+
const [base2, overlay] = await Promise.all([
|
|
210510
|
+
loadDecisions(repoKey),
|
|
210511
|
+
loadDecisions(repoKey, { pr: opts.pr })
|
|
210512
|
+
]);
|
|
210513
|
+
return mergeDecisions(base2, overlay);
|
|
210514
|
+
}
|
|
210515
|
+
function mergeDecisions(base2, overlay) {
|
|
210516
|
+
const overlayRelKeys = new Set((overlay.relations ?? []).map(relationKey));
|
|
210517
|
+
const relations = [
|
|
210518
|
+
...(base2.relations ?? []).filter((r) => !overlayRelKeys.has(relationKey(r))),
|
|
210519
|
+
...overlay.relations ?? []
|
|
210520
|
+
];
|
|
210521
|
+
const overlayIncludes = new Set(overlay.manualIncludes ?? []);
|
|
210522
|
+
const overlayExcludes = new Set(overlay.manualExcludes ?? []);
|
|
210523
|
+
const manualIncludes = uniqueStrings([
|
|
210524
|
+
...base2.manualIncludes ?? [],
|
|
210525
|
+
...overlay.manualIncludes ?? []
|
|
210526
|
+
]).filter((p2) => !overlayExcludes.has(p2));
|
|
210527
|
+
const manualExcludes = uniqueStrings([
|
|
210528
|
+
...base2.manualExcludes ?? [],
|
|
210529
|
+
...overlay.manualExcludes ?? []
|
|
210530
|
+
]).filter((p2) => !overlayIncludes.has(p2));
|
|
210531
|
+
const overlayAreaDocs = new Set((overlay.manualAreas ?? []).map((a) => a.doc));
|
|
210532
|
+
const manualAreas = [
|
|
210533
|
+
...(base2.manualAreas ?? []).filter((a) => !overlayAreaDocs.has(a.doc)),
|
|
210534
|
+
...overlay.manualAreas ?? []
|
|
210535
|
+
];
|
|
210536
|
+
return { version: 1, manualIncludes, manualExcludes, relations, manualAreas };
|
|
210537
|
+
}
|
|
210538
|
+
function uniqueStrings(items) {
|
|
210539
|
+
return [...new Set(items)];
|
|
210540
|
+
}
|
|
210359
210541
|
function getCorpus(repoKey) {
|
|
210360
210542
|
return loadLatestSpec(repoKey, "corpus");
|
|
210361
210543
|
}
|
|
@@ -210738,6 +210920,7 @@ async function runContractsValidate(options = {}) {
|
|
|
210738
210920
|
// tools/cli/src/commands/spec.ts
|
|
210739
210921
|
init_dist4();
|
|
210740
210922
|
import path74 from "node:path";
|
|
210923
|
+
init_dist5();
|
|
210741
210924
|
init_progress();
|
|
210742
210925
|
init_registry();
|
|
210743
210926
|
var repoRoot = (opts = {}) => opts.cwd ?? process.cwd();
|
|
@@ -210797,7 +210980,10 @@ async function runSpecScan(opts = {}) {
|
|
|
210797
210980
|
O2.message(` \u2026 (+${s.openOverlaps.length - 10} more)`);
|
|
210798
210981
|
}
|
|
210799
210982
|
}
|
|
210800
|
-
|
|
210983
|
+
const openCount = s.openOverlaps.length;
|
|
210984
|
+
gt(
|
|
210985
|
+
openCount === 0 ? "Corpus written to .truecourse/specs/corpus.json. Run `truecourse guard generate`." : `Corpus written to .truecourse/specs/corpus.json. ${openCount} conflict${openCount === 1 ? "" : "s"} to resolve (\`truecourse spec conflicts list\`), then \`truecourse guard generate\`.`
|
|
210986
|
+
);
|
|
210801
210987
|
}
|
|
210802
210988
|
async function runSpecStatus(opts = {}) {
|
|
210803
210989
|
const root2 = repoRoot(opts);
|
|
@@ -210810,19 +210996,9 @@ async function runSpecStatus(opts = {}) {
|
|
|
210810
210996
|
}
|
|
210811
210997
|
const decisions = readCorpusDecisions(root2);
|
|
210812
210998
|
const userRels = decisions.relations ?? [];
|
|
210813
|
-
const
|
|
210814
|
-
const
|
|
210815
|
-
|
|
210816
|
-
return samePair && (r.scope === void 0 || r.scope === area);
|
|
210817
|
-
});
|
|
210818
|
-
let open = 0;
|
|
210819
|
-
let resolved = 0;
|
|
210820
|
-
for (const area of corpus.areas) {
|
|
210821
|
-
for (const ov of area.overlaps) {
|
|
210822
|
-
if (covered(ov.docs[0], ov.docs[1], area.id)) resolved++;
|
|
210823
|
-
else open++;
|
|
210824
|
-
}
|
|
210825
|
-
}
|
|
210999
|
+
const conflicts2 = buildCorpusConflicts(corpus, decisions);
|
|
211000
|
+
const open = conflicts2.filter((c2) => !c2.resolved).length;
|
|
211001
|
+
const resolved = conflicts2.length - open;
|
|
210826
211002
|
const rows = [
|
|
210827
211003
|
["Docs (kept)", String(corpus.docs.length)],
|
|
210828
211004
|
["Areas", String(corpus.areas.length)],
|
|
@@ -210837,7 +211013,7 @@ async function runSpecStatus(opts = {}) {
|
|
|
210837
211013
|
O2.message(` ${area.id.padEnd(30)} ${area.docRefs.length} doc${area.docRefs.length === 1 ? "" : "s"}${ov}`);
|
|
210838
211014
|
}
|
|
210839
211015
|
gt(
|
|
210840
|
-
open === 0 ? "No open overlaps \u2014 run `truecourse
|
|
211016
|
+
open === 0 ? "No open overlaps \u2014 run `truecourse guard generate`." : "Open overlaps \u2014 see `truecourse spec conflicts list`."
|
|
210841
211017
|
);
|
|
210842
211018
|
}
|
|
210843
211019
|
async function runVerify(opts = {}) {
|
|
@@ -210954,18 +211130,9 @@ async function runInfer(opts = {}) {
|
|
|
210954
211130
|
init_dist4();
|
|
210955
211131
|
import fs69 from "node:fs";
|
|
210956
211132
|
import path75 from "node:path";
|
|
211133
|
+
init_dist5();
|
|
210957
211134
|
var root = (opts) => opts.cwd ?? process.cwd();
|
|
210958
211135
|
var base = (ref) => ref.split("/").pop() ?? ref;
|
|
210959
|
-
function effectiveRelations2(corpus, repoRoot5) {
|
|
210960
|
-
const user = readCorpusDecisions(repoRoot5).relations ?? [];
|
|
210961
|
-
return [...corpus.relations, ...user];
|
|
210962
|
-
}
|
|
210963
|
-
function coveringRelation(rels, a, b, area) {
|
|
210964
|
-
return rels.find((r) => {
|
|
210965
|
-
const samePair = r.older === a && r.newer === b || r.older === b && r.newer === a;
|
|
210966
|
-
return samePair && (r.scope === void 0 || r.scope === area);
|
|
210967
|
-
});
|
|
210968
|
-
}
|
|
210969
211136
|
function loadCorpusOrExit(repoRoot5) {
|
|
210970
211137
|
const corpus = readCorpus(repoRoot5);
|
|
210971
211138
|
if (!corpus) {
|
|
@@ -210977,25 +211144,17 @@ function loadCorpusOrExit(repoRoot5) {
|
|
|
210977
211144
|
async function runSpecConflictsList(opts = {}) {
|
|
210978
211145
|
const repoRoot5 = root(opts);
|
|
210979
211146
|
const corpus = loadCorpusOrExit(repoRoot5);
|
|
210980
|
-
const
|
|
210981
|
-
|
|
210982
|
-
|
|
211147
|
+
const conflicts2 = buildCorpusConflicts(corpus, readCorpusDecisions(repoRoot5));
|
|
211148
|
+
const open = conflicts2.filter((c2) => !c2.resolved);
|
|
211149
|
+
const resolved = conflicts2.length - open.length;
|
|
210983
211150
|
mt("Overlaps (areas where docs may disagree)");
|
|
210984
|
-
for (const
|
|
210985
|
-
|
|
210986
|
-
|
|
210987
|
-
|
|
210988
|
-
resolved++;
|
|
210989
|
-
continue;
|
|
210990
|
-
}
|
|
210991
|
-
open++;
|
|
210992
|
-
O2.warn(`${area.id}`);
|
|
210993
|
-
O2.message(` ${base(a)} \u2194 ${base(b)}${ov.note ? ` \xB7 ${ov.note}` : ""}`);
|
|
210994
|
-
O2.message(` resolve: truecourse spec conflicts resolve ${area.id} --older ${a} --newer ${b} --precedence`);
|
|
210995
|
-
}
|
|
211151
|
+
for (const c2 of open) {
|
|
211152
|
+
O2.warn(`${c2.area}`);
|
|
211153
|
+
O2.message(` ${base(c2.a)} \u2194 ${base(c2.b)}${c2.note ? ` \xB7 ${c2.note}` : ""}`);
|
|
211154
|
+
O2.message(` resolve: truecourse spec conflicts resolve ${c2.area} --older ${c2.a} --newer ${c2.b} --precedence`);
|
|
210996
211155
|
}
|
|
210997
|
-
if (open === 0) O2.step("No open overlaps.");
|
|
210998
|
-
gt(`${open} open \xB7 ${resolved} resolved. Inspect with \`spec conflicts show <area>\`.`);
|
|
211156
|
+
if (open.length === 0) O2.step("No open overlaps.");
|
|
211157
|
+
gt(`${open.length} open \xB7 ${resolved} resolved. Inspect with \`spec conflicts show <area>\`.`);
|
|
210999
211158
|
}
|
|
211000
211159
|
function excerpt(repoRoot5, ref, note, max = 20) {
|
|
211001
211160
|
let text4;
|
|
@@ -211018,14 +211177,18 @@ async function runSpecConflictsShow(area, opts = {}) {
|
|
|
211018
211177
|
pt(`No such area: ${area}. List areas with \`spec status\`.`);
|
|
211019
211178
|
process.exit(1);
|
|
211020
211179
|
}
|
|
211021
|
-
const
|
|
211180
|
+
const conflicts2 = buildCorpusConflicts(corpus, readCorpusDecisions(repoRoot5));
|
|
211022
211181
|
mt(`Overlaps in ${area}`);
|
|
211023
211182
|
if (a.overlaps.length === 0) O2.step("(no overlaps in this area)");
|
|
211024
211183
|
for (const ov of a.overlaps) {
|
|
211025
211184
|
const [da, db] = ov.docs;
|
|
211026
|
-
const
|
|
211185
|
+
const c2 = conflicts2.find(
|
|
211186
|
+
(x) => x.area === area && (x.a === da && x.b === db || x.a === db && x.b === da)
|
|
211187
|
+
);
|
|
211027
211188
|
O2.warn(`${base(da)} \u2194 ${base(db)}${ov.note ? ` \xB7 ${ov.note}` : ""}`);
|
|
211028
|
-
O2.message(
|
|
211189
|
+
if (c2?.relation) O2.message(` resolved \u2192 ${c2.relation.type} (${c2.relation.older} \u21D2 ${c2.relation.newer})`);
|
|
211190
|
+
else if (c2?.excludedRef) O2.message(` resolved \u2192 ${c2.excludedRef} excluded`);
|
|
211191
|
+
else O2.message(" open");
|
|
211029
211192
|
O2.message(` ${da}:`);
|
|
211030
211193
|
O2.message(excerpt(repoRoot5, da, ov.note));
|
|
211031
211194
|
O2.message(` ${db}:`);
|
|
@@ -211044,11 +211207,15 @@ async function runSpecConflictsResolve(area, opts) {
|
|
|
211044
211207
|
for (const ref of [opts.older, opts.newer]) {
|
|
211045
211208
|
if (!known.has(ref)) return fail(`${ref} is not a doc in area ${area}. Docs: ${a.docRefs.join(", ")}`);
|
|
211046
211209
|
}
|
|
211210
|
+
const conflict = buildCorpusConflicts(corpus, readCorpusDecisions(repoRoot5)).find(
|
|
211211
|
+
(c2) => c2.a === opts.older && c2.b === opts.newer || c2.a === opts.newer && c2.b === opts.older
|
|
211212
|
+
);
|
|
211213
|
+
const scope = conflict && conflict.areas.length > 1 ? void 0 : area;
|
|
211047
211214
|
await addRelation(repoRoot5, {
|
|
211048
211215
|
type: opts.type,
|
|
211049
211216
|
older: opts.older,
|
|
211050
211217
|
newer: opts.newer,
|
|
211051
|
-
scope
|
|
211218
|
+
scope,
|
|
211052
211219
|
detectedFrom: "manual",
|
|
211053
211220
|
note: opts.note
|
|
211054
211221
|
});
|
|
@@ -211056,7 +211223,7 @@ async function runSpecConflictsResolve(area, opts) {
|
|
|
211056
211223
|
s.start("Re-scanning to apply the relation");
|
|
211057
211224
|
await curateInProcess(repoRoot5, {});
|
|
211058
211225
|
s.stop("Re-scanned");
|
|
211059
|
-
gt(`Recorded ${opts.type}: ${opts.older} \u21D2 ${opts.newer} (scope ${
|
|
211226
|
+
gt(`Recorded ${opts.type}: ${opts.older} \u21D2 ${opts.newer} (scope ${scope ?? "all areas"}).`);
|
|
211060
211227
|
}
|
|
211061
211228
|
function fail(msg) {
|
|
211062
211229
|
pt(msg);
|
|
@@ -211297,8 +211464,42 @@ import path77 from "node:path";
|
|
|
211297
211464
|
init_progress();
|
|
211298
211465
|
|
|
211299
211466
|
// packages/core/dist/commands/guard-in-process.js
|
|
211467
|
+
init_dist5();
|
|
211300
211468
|
init_git();
|
|
211301
211469
|
init_transport();
|
|
211470
|
+
var OpenConflictsError = class extends Error {
|
|
211471
|
+
conflicts;
|
|
211472
|
+
constructor(conflicts2) {
|
|
211473
|
+
super(formatOpenConflictsMessage(conflicts2));
|
|
211474
|
+
this.conflicts = conflicts2;
|
|
211475
|
+
this.name = "OpenConflictsError";
|
|
211476
|
+
}
|
|
211477
|
+
};
|
|
211478
|
+
function formatOpenConflictsMessage(conflicts2) {
|
|
211479
|
+
const lines = [
|
|
211480
|
+
`${conflicts2.length} open spec conflict${conflicts2.length === 1 ? "" : "s"} must be resolved before guard generate.`,
|
|
211481
|
+
"Extracting both sides of an unresolved overlap births a red finding that is really the dispute.",
|
|
211482
|
+
""
|
|
211483
|
+
];
|
|
211484
|
+
for (const c2 of conflicts2) {
|
|
211485
|
+
lines.push(` ${c2.area}`);
|
|
211486
|
+
lines.push(` ${c2.a} \u2194 ${c2.b}`);
|
|
211487
|
+
if (c2.note)
|
|
211488
|
+
lines.push(` ${c2.note}`);
|
|
211489
|
+
}
|
|
211490
|
+
lines.push("");
|
|
211491
|
+
lines.push("Resolve them with `truecourse spec conflicts list` (or the dashboard Conflicts group), then re-run `truecourse guard generate`.");
|
|
211492
|
+
return lines.join("\n");
|
|
211493
|
+
}
|
|
211494
|
+
async function assertNoOpenConflicts(repoRoot5) {
|
|
211495
|
+
const corpus = await getCorpus(repoRoot5);
|
|
211496
|
+
if (!corpus)
|
|
211497
|
+
return;
|
|
211498
|
+
const decisions = await getDecisions(repoRoot5);
|
|
211499
|
+
const open = openConflicts(corpus, decisions);
|
|
211500
|
+
if (open.length > 0)
|
|
211501
|
+
throw new OpenConflictsError(open);
|
|
211502
|
+
}
|
|
211302
211503
|
var GUARD_GENERATE_STEPS = [
|
|
211303
211504
|
{ key: "index", label: "Indexing sections" },
|
|
211304
211505
|
{ key: "extract", label: "Extracting claims" },
|
|
@@ -211331,6 +211532,7 @@ function resolveTransport2(options) {
|
|
|
211331
211532
|
}
|
|
211332
211533
|
async function guardGenerateInProcess(repoRoot5, options = {}) {
|
|
211333
211534
|
const { tracker } = options;
|
|
211535
|
+
await assertNoOpenConflicts(repoRoot5);
|
|
211334
211536
|
if (options.onLlmEstimate) {
|
|
211335
211537
|
const prices = await getModelPrices();
|
|
211336
211538
|
const estimate = await estimateGuardTokens(repoRoot5, prices);
|
|
@@ -211662,6 +211864,16 @@ async function runGuardGenerate(opts = {}) {
|
|
|
211662
211864
|
pt("Generate cancelled.");
|
|
211663
211865
|
process.exit(0);
|
|
211664
211866
|
}
|
|
211867
|
+
if (e instanceof OpenConflictsError) {
|
|
211868
|
+
O2.error(`${e.conflicts.length} open spec conflict${e.conflicts.length === 1 ? "" : "s"} block guard generate \u2014 resolve them first:`);
|
|
211869
|
+
for (const c2 of e.conflicts) {
|
|
211870
|
+
O2.message(` ${c2.area}`);
|
|
211871
|
+
O2.message(` ${c2.a} \u2194 ${c2.b}${c2.note ? ` \xB7 ${c2.note}` : ""}`);
|
|
211872
|
+
}
|
|
211873
|
+
O2.step("Resolve with `truecourse spec conflicts list` (or the dashboard Conflicts group), then re-run `truecourse guard generate`.");
|
|
211874
|
+
gt("Aborted \u2014 resolve the conflicts first.");
|
|
211875
|
+
process.exit(1);
|
|
211876
|
+
}
|
|
211665
211877
|
O2.error(`Guard generate failed: ${e.message}`);
|
|
211666
211878
|
gt("Aborted.");
|
|
211667
211879
|
process.exit(1);
|
|
@@ -212226,7 +212438,7 @@ async function runHooksRun() {
|
|
|
212226
212438
|
|
|
212227
212439
|
// tools/cli/src/index.ts
|
|
212228
212440
|
var program2 = new Command();
|
|
212229
|
-
program2.name("truecourse").version("0.7.0-next.
|
|
212441
|
+
program2.name("truecourse").version("0.7.0-next.7").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
|
|
212230
212442
|
function warnDiscontinued(command) {
|
|
212231
212443
|
process.stderr.write(
|
|
212232
212444
|
`\u26A0 \`${command}\` is discontinued in favor of \`truecourse guard\` \u2014 see the README. Planned removal: 0.8.
|