truecourse 0.6.0-next.7 → 0.6.0-next.9

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 CHANGED
@@ -193,6 +193,8 @@ truecourse verify # Check code against the contracts → d
193
193
 
194
194
  Resolve conflicts and review drifts visually in the [dashboard](#dashboard-web-ui)'s BL Drift section, or drive every step from the CLI.
195
195
 
196
+ > Like `analyze`, the spec → contracts → verify track requires a **git repository** — TrueCourse's baselines are commit-anchored (committable `LATEST.json`, diff vs HEAD, stashing the committed state). On a non-git folder these commands stop with a clear message and the dashboard hides their actions.
197
+
196
198
  ## How it works
197
199
 
198
200
  Three stages run in order, each producing artifacts the next consumes:
@@ -254,7 +256,8 @@ truecourse spec docs uninclude <path>
254
256
 
255
257
  # Contract extraction (canonical spec → .tc artifacts)
256
258
  truecourse contracts generate # Extract / re-extract TC contract files
257
- truecourse contracts list # List generated contracts
259
+ truecourse contracts list # List artifacts (kind · identity · location)
260
+ truecourse contracts list --inferred / --authored # Only reverse-engineered (_inferred/) / only authored
258
261
  truecourse contracts validate # Parse + resolve TC files; report unresolved refs
259
262
 
260
263
  # Verification (code against contracts)
@@ -268,6 +271,7 @@ truecourse drifts list --offset 20 / --severity critical,high # Page through /
268
271
  # Inference (code → inferred contracts) — reverse-engineer undocumented decisions
269
272
  truecourse infer # Write inferred .tc files to contracts/_inferred/
270
273
  truecourse infer --dry-run # Report what would be written, touch nothing
274
+ truecourse contracts list --inferred # Review what infer produced (kind · confidence · code location)
271
275
  ```
272
276
 
273
277
  ---
package/cli.mjs CHANGED
@@ -10077,15 +10077,17 @@ async function getGit(repoPath) {
10077
10077
  const git = simpleGit(repoPath);
10078
10078
  const isRepo = await isGitRepo(repoPath);
10079
10079
  if (!isRepo) {
10080
- throw createAppError("The selected folder is not a git repository. Please select a folder that has been initialized with git.", 400);
10080
+ throw createAppError(NOT_A_GIT_REPO_MESSAGE, 400);
10081
10081
  }
10082
10082
  return git;
10083
10083
  }
10084
+ var NOT_A_GIT_REPO_MESSAGE;
10084
10085
  var init_git = __esm({
10085
10086
  "packages/core/dist/lib/git.js"() {
10086
10087
  "use strict";
10087
10088
  init_esm();
10088
10089
  init_errors();
10090
+ NOT_A_GIT_REPO_MESSAGE = "The selected folder is not a git repository. Please select a folder that has been initialized with git.";
10089
10091
  }
10090
10092
  });
10091
10093
 
@@ -128242,7 +128244,7 @@ function readToolVersion() {
128242
128244
  if (cachedVersion)
128243
128245
  return cachedVersion;
128244
128246
  if (true) {
128245
- cachedVersion = "0.6.0-next.7";
128247
+ cachedVersion = "0.6.0-next.9";
128246
128248
  return cachedVersion;
128247
128249
  }
128248
128250
  try {
@@ -149108,9 +149110,21 @@ function removeManualInclude(repoRoot6, docPath) {
149108
149110
 
149109
149111
  // tools/cli/src/commands/contracts.ts
149110
149112
  init_helpers();
149113
+
149114
+ // tools/cli/src/commands/git-guard.ts
149115
+ init_dist4();
149116
+ init_git();
149117
+ async function requireGitRepo(root) {
149118
+ if (await isGitRepo(root)) return;
149119
+ pt(NOT_A_GIT_REPO_MESSAGE);
149120
+ process.exit(1);
149121
+ }
149122
+
149123
+ // tools/cli/src/commands/contracts.ts
149111
149124
  async function runContractsGenerate(options = {}) {
149112
149125
  const repoRoot6 = options.cwd ?? process.cwd();
149113
149126
  mt(options.diff ? "Contracts (dry run)" : "Contracts");
149127
+ await requireGitRepo(repoRoot6);
149114
149128
  if (!hasCanonicalSpec(repoRoot6)) {
149115
149129
  O2.error(
149116
149130
  "No .truecourse/specs/claims.json found. Run `truecourse spec scan` first to build the canonical claim set."
@@ -149216,23 +149230,51 @@ async function runContractsList(options = {}) {
149216
149230
  O2.info("No contracts found. Run `truecourse contracts generate` first.");
149217
149231
  return;
149218
149232
  }
149219
- const files = [];
149233
+ const { parser: parser4, resolver } = await Promise.resolve().then(() => (init_dist8(), dist_exports3));
149234
+ const fileNodes = [];
149235
+ let parseErrors = 0;
149220
149236
  const visit = (dir) => {
149221
149237
  for (const entry of fs47.readdirSync(dir, { withFileTypes: true })) {
149222
149238
  const full = path54.join(dir, entry.name);
149223
149239
  if (entry.isDirectory()) visit(full);
149224
- else if (entry.isFile() && entry.name.endsWith(".tc")) files.push(full);
149240
+ else if (entry.isFile() && entry.name.endsWith(".tc")) {
149241
+ try {
149242
+ fileNodes.push(parser4.parseFile(full, fs47.readFileSync(full, "utf-8")));
149243
+ } catch {
149244
+ parseErrors += 1;
149245
+ }
149246
+ }
149225
149247
  }
149226
149248
  };
149227
149249
  visit(contractsDir);
149228
- files.sort();
149229
- if (files.length === 0) {
149230
- O2.info("No .tc files in .truecourse/contracts/.");
149250
+ const filter2 = options.inferred && !options.authored ? "inferred" : options.authored && !options.inferred ? "authored" : null;
149251
+ const resolution = resolver.resolve(fileNodes);
149252
+ const artifacts = [...resolution.index.values()].filter((a) => filter2 ? a.provenance === filter2 : true).sort(
149253
+ (a, b) => a.ref.type === b.ref.type ? a.ref.identity.localeCompare(b.ref.identity) : a.ref.type.localeCompare(b.ref.type)
149254
+ );
149255
+ if (artifacts.length === 0) {
149256
+ if (filter2 === "inferred") {
149257
+ O2.info("No inferred contracts. Run `truecourse infer` to reverse-engineer undocumented decisions.");
149258
+ } else if (filter2 === "authored") {
149259
+ O2.info("No authored contracts. Run `truecourse contracts generate` first.");
149260
+ } else {
149261
+ O2.info("No .tc files in .truecourse/contracts/.");
149262
+ }
149231
149263
  return;
149232
149264
  }
149233
- mt(`Contracts (${files.length})`);
149234
- for (const f2 of files) console.log(` ${path54.relative(repoRoot6, f2)}`);
149235
- gt("`truecourse contracts validate` then `truecourse verify`.");
149265
+ const scope = filter2 ? `${filter2} ` : "";
149266
+ mt(`Contracts \u2014 ${artifacts.length} ${scope}artifact${artifacts.length === 1 ? "" : "s"}`);
149267
+ for (const a of artifacts) {
149268
+ const conf = a.confidence ? `[${a.confidence}] ` : "";
149269
+ const loc = a.origin ? a.origin.lines[0] >= 0 ? `${a.origin.source}:${a.origin.lines[0]}` : a.origin.source : `${path54.relative(repoRoot6, a.declarationLoc.filePath)}:${a.declarationLoc.lineStart}`;
149270
+ console.log(` ${conf}${a.ref.type}:${a.ref.identity} ${loc}`);
149271
+ }
149272
+ if (parseErrors > 0) {
149273
+ O2.warn(`${parseErrors} file${parseErrors === 1 ? "" : "s"} could not be parsed \u2014 run \`truecourse contracts validate\`.`);
149274
+ }
149275
+ gt(
149276
+ filter2 ? "`truecourse verify`." : "Filter with `--inferred` / `--authored`. `truecourse contracts validate` then `truecourse verify`."
149277
+ );
149236
149278
  }
149237
149279
  async function runContractsValidate(options = {}) {
149238
149280
  const repoRoot6 = options.cwd ?? process.cwd();
@@ -149361,6 +149403,7 @@ function withTracker(stepDefs) {
149361
149403
  async function runSpecScan(opts = {}) {
149362
149404
  const root = repoRoot(opts);
149363
149405
  mt("Spec scan");
149406
+ await requireGitRepo(root);
149364
149407
  const { renderer, tracker } = withTracker(SCAN_STEPS);
149365
149408
  try {
149366
149409
  const { consolidate: consolidate2 } = await scanInProcess(root, { tracker });
@@ -149408,6 +149451,7 @@ async function runSpecResolve(opts = {}) {
149408
149451
  return;
149409
149452
  }
149410
149453
  mt("Spec resolve \u2014 accepting all defaults");
149454
+ await requireGitRepo(root);
149411
149455
  const { renderer, tracker } = withTracker(RESOLVE_STEPS);
149412
149456
  try {
149413
149457
  const { additions } = await resolveAllDefaultsInProcess(root, { tracker });
@@ -149456,6 +149500,7 @@ async function runVerify(opts = {}) {
149456
149500
  const root = repoRoot(opts);
149457
149501
  const { skipStash } = await resolveStashDecision({ stash: opts.stash }, root);
149458
149502
  mt("Verify");
149503
+ await requireGitRepo(root);
149459
149504
  const { renderer, tracker } = withTracker(VERIFY_STEPS);
149460
149505
  try {
149461
149506
  const { verify: verify2 } = await verifyInProcess(root, { tracker, codeDir: opts.codeDir, skipStash });
@@ -149494,6 +149539,7 @@ async function runVerify(opts = {}) {
149494
149539
  async function runVerifyDiff(opts) {
149495
149540
  const root = repoRoot(opts);
149496
149541
  mt("Verify diff");
149542
+ await requireGitRepo(root);
149497
149543
  const { renderer, tracker } = withTracker(VERIFY_STEPS);
149498
149544
  try {
149499
149545
  const { diff } = await verifyDiffInProcess(root, { tracker, codeDir: opts.codeDir });
@@ -149524,6 +149570,7 @@ async function runVerifyDiff(opts) {
149524
149570
  async function runInfer(opts = {}) {
149525
149571
  const root = repoRoot(opts);
149526
149572
  mt("Infer");
149573
+ await requireGitRepo(root);
149527
149574
  const { renderer, tracker } = withTracker(INFER_STEPS);
149528
149575
  try {
149529
149576
  const { infer: infer2, written, proposed } = await inferInProcess(root, {
@@ -149549,7 +149596,7 @@ async function runInfer(opts = {}) {
149549
149596
  }
149550
149597
  const wrote = opts.dryRun ? proposed.length : written.length;
149551
149598
  gt(
149552
- infer2.decisions.length === 0 ? "No undocumented decisions found." : opts.dryRun ? `${wrote} inferred contract${wrote === 1 ? "" : "s"} would be written to _inferred/ (dry run).` : `${wrote} inferred contract${wrote === 1 ? "" : "s"} written to _inferred/.`
149599
+ infer2.decisions.length === 0 ? "No undocumented decisions found." : opts.dryRun ? `${wrote} inferred contract${wrote === 1 ? "" : "s"} would be written to _inferred/ (dry run).` : `${wrote} inferred contract${wrote === 1 ? "" : "s"} written to _inferred/ \u2014 review with \`truecourse contracts list --inferred\`.`
149553
149600
  );
149554
149601
  } catch (e) {
149555
149602
  renderer.dispose();
@@ -152888,7 +152935,7 @@ async function runHooksRun() {
152888
152935
 
152889
152936
  // tools/cli/src/index.ts
152890
152937
  var program2 = new Command();
152891
- program2.name("truecourse").version("0.6.0-next.7").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
152938
+ program2.name("truecourse").version("0.6.0-next.9").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
152892
152939
  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) => {
152893
152940
  if (options.service && options.console) {
152894
152941
  console.error("error: --service and --console are mutually exclusive");
@@ -152945,8 +152992,8 @@ var contractsCmd = program2.command("contracts").description("Manage spec-driven
152945
152992
  contractsCmd.command("generate").description("Extract .tc artifacts from prose specs (LLM, cached)").option("--diff", "Dry run \u2014 show what would change without writing").action(async (options) => {
152946
152993
  await runContractsGenerate({ diff: !!options.diff });
152947
152994
  });
152948
- contractsCmd.command("list").description("List the .tc artifacts in this repo").action(async () => {
152949
- await runContractsList();
152995
+ contractsCmd.command("list").description("List the .tc artifacts in this repo (kind \xB7 identity \xB7 location)").option("--inferred", "Only inferred artifacts (reverse-engineered, in _inferred/)").option("--authored", "Only authored artifacts (exclude _inferred/)").action(async (options) => {
152996
+ await runContractsList({ inferred: !!options.inferred, authored: !!options.authored });
152950
152997
  });
152951
152998
  contractsCmd.command("validate").description("Parse and resolve all .tc files, report any issues").action(async () => {
152952
152999
  await runContractsValidate();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "truecourse",
3
- "version": "0.6.0-next.7",
3
+ "version": "0.6.0-next.9",
4
4
  "description": "Visualize your codebase architecture as an interactive graph",
5
5
  "type": "module",
6
6
  "bin": {