truecourse 0.6.0-next.4 → 0.6.0-next.6

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 (3) hide show
  1. package/cli.mjs +45 -15
  2. package/package.json +1 -1
  3. package/server.mjs +25 -8
package/cli.mjs CHANGED
@@ -128242,7 +128242,7 @@ function readToolVersion() {
128242
128242
  if (cachedVersion)
128243
128243
  return cachedVersion;
128244
128244
  if (true) {
128245
- cachedVersion = "0.6.0-next.4";
128245
+ cachedVersion = "0.6.0-next.6";
128246
128246
  return cachedVersion;
128247
128247
  }
128248
128248
  try {
@@ -147772,15 +147772,24 @@ async function repair(artifacts, slices, opts = {}) {
147772
147772
  const modelArgs = buildModelArgs2(opts.model, opts.fallbackModel);
147773
147773
  const log2 = [];
147774
147774
  const allIssues = [];
147775
+ let done = 0;
147776
+ let total = 0;
147775
147777
  const missing = detectMissingArtifacts(artifacts);
147776
147778
  allIssues.push(...missing);
147777
- for (const issue of missing) {
147778
- const slice3 = findSliceForMissing(issue.artifactKey, slices);
147779
+ const missingTasks = missing.map((issue) => ({
147780
+ issue,
147781
+ slice: findSliceForMissing(issue.artifactKey, slices)
147782
+ }));
147783
+ total += missingTasks.filter((t2) => t2.slice).length;
147784
+ for (const { issue, slice: slice3 } of missingTasks) {
147779
147785
  if (!slice3) {
147780
147786
  log2.push(`repair: missing ${issue.artifactKey} \u2014 no candidate slice found, skipping.`);
147781
147787
  continue;
147782
147788
  }
147783
- log2.push(`repair: missing ${issue.artifactKey} \u2014 re-prompting "${slice3.headingPath.join(" \u2192 ")}".`);
147789
+ done += 1;
147790
+ const message = `missing ${issue.artifactKey} \u2014 re-prompting "${slice3.headingPath.join(" \u2192 ")}"`;
147791
+ log2.push(`repair: ${message}.`);
147792
+ opts.onProgress?.({ done, total, message });
147784
147793
  const fragments = await runFixOne({ previousArtifact: null, missingKey: issue.artifactKey, slice: slice3, issues: [issue.detail] }, bin, timeoutMs, modelArgs);
147785
147794
  if (!fragments) {
147786
147795
  log2.push(`repair: re-prompt failed for ${issue.artifactKey}.`);
@@ -147816,16 +147825,23 @@ async function repair(artifacts, slices, opts = {}) {
147816
147825
  grouped.set(issue.artifactKey, arr);
147817
147826
  }
147818
147827
  allIssues.push(...incomplete);
147819
- for (const [k, issues] of grouped) {
147828
+ const incompleteTasks = [...grouped].map(([k, issues]) => {
147820
147829
  const artifact = artifacts.find((a) => key(a.kind, a.identity) === k);
147830
+ const slice3 = artifact ? sliceForArtifact(artifact, slices) : void 0;
147831
+ return { k, issues, artifact, slice: slice3 };
147832
+ });
147833
+ total += incompleteTasks.filter((t2) => t2.artifact && t2.slice).length;
147834
+ for (const { k, issues, artifact, slice: slice3 } of incompleteTasks) {
147821
147835
  if (!artifact)
147822
147836
  continue;
147823
- const slice3 = sliceForArtifact(artifact, slices);
147824
147837
  if (!slice3) {
147825
147838
  log2.push(`repair: incomplete ${k} \u2014 could not locate source slice, skipping.`);
147826
147839
  continue;
147827
147840
  }
147828
- log2.push(`repair: incomplete ${k} (${issues.length} issue${issues.length === 1 ? "" : "s"}) \u2014 re-prompting.`);
147841
+ done += 1;
147842
+ const message = `incomplete ${k} (${issues.length} issue${issues.length === 1 ? "" : "s"}) \u2014 re-prompting`;
147843
+ log2.push(`repair: ${message}.`);
147844
+ opts.onProgress?.({ done, total, message });
147829
147845
  const fragments = await runFixOne({ previousArtifact: artifact, slice: slice3, issues: issues.map((i) => i.detail) }, bin, timeoutMs, modelArgs);
147830
147846
  if (!fragments || fragments.length === 0) {
147831
147847
  log2.push(`repair: re-prompt failed for ${k}.`);
@@ -148193,7 +148209,8 @@ async function generateContracts(opts) {
148193
148209
  if (slices.length > 0 && !dryRun && !opts.disableRepair) {
148194
148210
  const repaired = await repair(merged.artifacts, slices, {
148195
148211
  model: models.repair,
148196
- fallbackModel: models.fallback
148212
+ fallbackModel: models.fallback,
148213
+ onProgress: opts.onRepairProgress
148197
148214
  });
148198
148215
  merged.artifacts = repaired.artifacts;
148199
148216
  merged.diagnostics.push(...repaired.log.map((message) => ({
@@ -149106,13 +149123,15 @@ async function runContractsGenerate(options = {}) {
149106
149123
  const repairModel = resolveModel("contract.repair", void 0, repoRoot5);
149107
149124
  const fallbackModel = resolveFallbackModel(repoRoot5) ?? void 0;
149108
149125
  let totalSlices = 0;
149109
- let sliceNum = 0;
149126
+ let doneSlices = 0;
149110
149127
  const runner = spawnRunner2({
149111
149128
  concurrency,
149112
149129
  model: extractModel,
149113
149130
  fallbackModel,
149114
- onSliceStart: (s) => {
149115
- O2.step(`extracting ${++sliceNum}/${totalSlices} ${s.specPath} :: ${s.headingPath.join(" \u2192 ")}`);
149131
+ onSliceDone: (s, ok) => {
149132
+ O2.step(
149133
+ `extracted ${++doneSlices}/${totalSlices} ${s.specPath} :: ${s.headingPath.join(" \u2192 ")}${ok ? "" : " (failed)"}`
149134
+ );
149116
149135
  }
149117
149136
  });
149118
149137
  let result;
@@ -149126,7 +149145,12 @@ async function runContractsGenerate(options = {}) {
149126
149145
  totalSlices = t2;
149127
149146
  },
149128
149147
  onSliceCacheHit: (s) => {
149129
- O2.message(` cache hit ${++sliceNum}/${totalSlices} ${s.specPath} :: ${s.headingPath.join(" \u2192 ")}`, { symbol: "\xB7" });
149148
+ O2.message(` cache hit ${++doneSlices}/${totalSlices} ${s.specPath} :: ${s.headingPath.join(" \u2192 ")}`, { symbol: "\xB7" });
149149
+ },
149150
+ // The repair pass runs sequential `claude` re-prompts after extraction —
149151
+ // stream each one so the terminal isn't silent through the LLM calls.
149152
+ onRepairProgress: (e) => {
149153
+ O2.step(`repairing ${e.done}/${e.total} ${e.message}`);
149130
149154
  }
149131
149155
  });
149132
149156
  } catch (e) {
@@ -149157,15 +149181,21 @@ async function runContractsGenerate(options = {}) {
149157
149181
  process.exit(1);
149158
149182
  }
149159
149183
  for (const d3 of result.mergeDiagnostics) {
149184
+ if (d3.artifactKey === "repair" && d3.message.includes("re-prompting")) continue;
149160
149185
  O2.warn(d3.message);
149161
149186
  }
149187
+ const LIST_CAP = 20;
149188
+ const printFileList = (files, marker) => {
149189
+ for (const f2 of files.slice(0, LIST_CAP)) console.log(` ${marker} ${path54.relative(repoRoot5, f2)}`);
149190
+ if (files.length > LIST_CAP) console.log(` \u2026 and ${files.length - LIST_CAP} more`);
149191
+ };
149162
149192
  if (options.diff) {
149163
149193
  if (result.write.proposed.length === 0) {
149164
149194
  gt("No changes \u2014 every contract is already up to date.");
149165
149195
  return;
149166
149196
  }
149167
149197
  O2.info(`Would write ${result.write.proposed.length} file${result.write.proposed.length === 1 ? "" : "s"}:`);
149168
- for (const f2 of result.write.proposed) console.log(` + ${path54.relative(repoRoot5, f2)}`);
149198
+ printFileList(result.write.proposed, "+");
149169
149199
  gt("Run `truecourse contracts generate` to apply.");
149170
149200
  return;
149171
149201
  }
@@ -149175,7 +149205,7 @@ async function runContractsGenerate(options = {}) {
149175
149205
  return;
149176
149206
  }
149177
149207
  O2.success(`Wrote ${result.write.written.length} contract file${result.write.written.length === 1 ? "" : "s"}.`);
149178
- for (const f2 of result.write.written) console.log(` \u2022 ${path54.relative(repoRoot5, f2)}`);
149208
+ printFileList(result.write.written, "\u2022");
149179
149209
  syncShippedTcSyntax();
149180
149210
  gt(`Run \`truecourse verify\` to check code against the new contracts.`);
149181
149211
  }
@@ -152788,7 +152818,7 @@ async function runHooksRun() {
152788
152818
 
152789
152819
  // tools/cli/src/index.ts
152790
152820
  var program2 = new Command();
152791
- program2.name("truecourse").version("0.6.0-next.4").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
152821
+ program2.name("truecourse").version("0.6.0-next.6").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
152792
152822
  var dashboardCmd = program2.command("dashboard").description("Start the TrueCourse dashboard and open it in your browser").option("--reconfigure", "Re-prompt for console vs background service mode").option("--service", "Run as a background service (skips mode prompt)").option("--console", "Run in this terminal (skips mode prompt)").action(async (options) => {
152793
152823
  if (options.service && options.console) {
152794
152824
  console.error("error: --service and --console are mutually exclusive");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "truecourse",
3
- "version": "0.6.0-next.4",
3
+ "version": "0.6.0-next.6",
4
4
  "description": "Visualize your codebase architecture as an interactive graph",
5
5
  "type": "module",
6
6
  "bin": {
package/server.mjs CHANGED
@@ -156920,7 +156920,7 @@ function readToolVersion() {
156920
156920
  if (cachedVersion)
156921
156921
  return cachedVersion;
156922
156922
  if (true) {
156923
- cachedVersion = "0.6.0-next.4";
156923
+ cachedVersion = "0.6.0-next.6";
156924
156924
  return cachedVersion;
156925
156925
  }
156926
156926
  try {
@@ -164554,15 +164554,24 @@ async function repair(artifacts, slices, opts = {}) {
164554
164554
  const modelArgs = buildModelArgs2(opts.model, opts.fallbackModel);
164555
164555
  const log2 = [];
164556
164556
  const allIssues = [];
164557
+ let done = 0;
164558
+ let total = 0;
164557
164559
  const missing = detectMissingArtifacts(artifacts);
164558
164560
  allIssues.push(...missing);
164559
- for (const issue of missing) {
164560
- const slice3 = findSliceForMissing(issue.artifactKey, slices);
164561
+ const missingTasks = missing.map((issue) => ({
164562
+ issue,
164563
+ slice: findSliceForMissing(issue.artifactKey, slices)
164564
+ }));
164565
+ total += missingTasks.filter((t) => t.slice).length;
164566
+ for (const { issue, slice: slice3 } of missingTasks) {
164561
164567
  if (!slice3) {
164562
164568
  log2.push(`repair: missing ${issue.artifactKey} \u2014 no candidate slice found, skipping.`);
164563
164569
  continue;
164564
164570
  }
164565
- log2.push(`repair: missing ${issue.artifactKey} \u2014 re-prompting "${slice3.headingPath.join(" \u2192 ")}".`);
164571
+ done += 1;
164572
+ const message = `missing ${issue.artifactKey} \u2014 re-prompting "${slice3.headingPath.join(" \u2192 ")}"`;
164573
+ log2.push(`repair: ${message}.`);
164574
+ opts.onProgress?.({ done, total, message });
164566
164575
  const fragments = await runFixOne({ previousArtifact: null, missingKey: issue.artifactKey, slice: slice3, issues: [issue.detail] }, bin, timeoutMs, modelArgs);
164567
164576
  if (!fragments) {
164568
164577
  log2.push(`repair: re-prompt failed for ${issue.artifactKey}.`);
@@ -164598,16 +164607,23 @@ async function repair(artifacts, slices, opts = {}) {
164598
164607
  grouped.set(issue.artifactKey, arr);
164599
164608
  }
164600
164609
  allIssues.push(...incomplete);
164601
- for (const [k, issues] of grouped) {
164610
+ const incompleteTasks = [...grouped].map(([k, issues]) => {
164602
164611
  const artifact = artifacts.find((a) => key(a.kind, a.identity) === k);
164612
+ const slice3 = artifact ? sliceForArtifact(artifact, slices) : void 0;
164613
+ return { k, issues, artifact, slice: slice3 };
164614
+ });
164615
+ total += incompleteTasks.filter((t) => t.artifact && t.slice).length;
164616
+ for (const { k, issues, artifact, slice: slice3 } of incompleteTasks) {
164603
164617
  if (!artifact)
164604
164618
  continue;
164605
- const slice3 = sliceForArtifact(artifact, slices);
164606
164619
  if (!slice3) {
164607
164620
  log2.push(`repair: incomplete ${k} \u2014 could not locate source slice, skipping.`);
164608
164621
  continue;
164609
164622
  }
164610
- log2.push(`repair: incomplete ${k} (${issues.length} issue${issues.length === 1 ? "" : "s"}) \u2014 re-prompting.`);
164623
+ done += 1;
164624
+ const message = `incomplete ${k} (${issues.length} issue${issues.length === 1 ? "" : "s"}) \u2014 re-prompting`;
164625
+ log2.push(`repair: ${message}.`);
164626
+ opts.onProgress?.({ done, total, message });
164611
164627
  const fragments = await runFixOne({ previousArtifact: artifact, slice: slice3, issues: issues.map((i) => i.detail) }, bin, timeoutMs, modelArgs);
164612
164628
  if (!fragments || fragments.length === 0) {
164613
164629
  log2.push(`repair: re-prompt failed for ${k}.`);
@@ -175613,7 +175629,8 @@ async function generateContracts(opts) {
175613
175629
  if (slices.length > 0 && !dryRun && !opts.disableRepair) {
175614
175630
  const repaired = await repair(merged.artifacts, slices, {
175615
175631
  model: models.repair,
175616
- fallbackModel: models.fallback
175632
+ fallbackModel: models.fallback,
175633
+ onProgress: opts.onRepairProgress
175617
175634
  });
175618
175635
  merged.artifacts = repaired.artifacts;
175619
175636
  merged.diagnostics.push(...repaired.log.map((message) => ({