truecourse 0.5.2 → 0.5.4

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 (2) hide show
  1. package/cli.mjs +60 -37
  2. package/package.json +1 -1
package/cli.mjs CHANGED
@@ -4080,7 +4080,7 @@ __export(helpers_exports, {
4080
4080
  writeConfig: () => writeConfig
4081
4081
  });
4082
4082
  import { exec as exec2 } from "node:child_process";
4083
- import { cpSync, existsSync } from "node:fs";
4083
+ import { cpSync, existsSync, mkdirSync, readdirSync } from "node:fs";
4084
4084
  import fs3 from "node:fs";
4085
4085
  import os2 from "node:os";
4086
4086
  import path3 from "node:path";
@@ -4319,12 +4319,6 @@ function openInBrowser(url) {
4319
4319
  const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
4320
4320
  exec2(`${cmd} ${url}`);
4321
4321
  }
4322
- function skillDestPath(repoPath) {
4323
- return resolve(repoPath, ".claude", "skills", "truecourse");
4324
- }
4325
- function hasInstalledSkills(repoPath) {
4326
- return existsSync(skillDestPath(repoPath));
4327
- }
4328
4322
  function isInteractive() {
4329
4323
  return !!process.stdin.isTTY;
4330
4324
  }
@@ -4336,35 +4330,64 @@ Running non-interactively with no answer. ${flagGuidance}`
4336
4330
  );
4337
4331
  process.exit(1);
4338
4332
  }
4339
- function copySkillsInto(repoPath) {
4333
+ function resolveSkillsSrcDir() {
4340
4334
  const __dirname4 = dirname(fileURLToPath(import.meta.url));
4341
- const srcPath = resolve(__dirname4, "..", "..", "skills", "truecourse");
4342
- const distPath = resolve(__dirname4, "skills", "truecourse");
4343
- const skillsSrc = existsSync(srcPath) ? srcPath : distPath;
4344
- if (!existsSync(skillsSrc)) {
4335
+ const candidate = resolve(__dirname4, "skills", "truecourse");
4336
+ return existsSync(candidate) ? candidate : null;
4337
+ }
4338
+ function skillsParentDir(repoPath) {
4339
+ return resolve(repoPath, ".claude", "skills");
4340
+ }
4341
+ function listSkillDirs(root) {
4342
+ if (!existsSync(root)) return [];
4343
+ return readdirSync(root, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
4344
+ }
4345
+ function computeMissingSkills(repoPath) {
4346
+ const src = resolveSkillsSrcDir();
4347
+ if (!src) return [];
4348
+ const shipped = listSkillDirs(src);
4349
+ const parent = skillsParentDir(repoPath);
4350
+ return shipped.filter((name) => !existsSync(resolve(parent, name)));
4351
+ }
4352
+ function hasInstalledSkills(repoPath) {
4353
+ return computeMissingSkills(repoPath).length === 0;
4354
+ }
4355
+ function copySkills(repoPath, skillNames) {
4356
+ const src = resolveSkillsSrcDir();
4357
+ if (!src) {
4345
4358
  O2.warn("Skills directory not found in package \u2014 skipping.");
4346
4359
  return;
4347
4360
  }
4348
- const skillsDest = resolve(repoPath, ".claude", "skills");
4349
- cpSync(skillsSrc, skillsDest, { recursive: true });
4350
- O2.success("Installed Claude Code skills:");
4351
- O2.message(" - truecourse-analyze (run analysis)");
4352
- O2.message(" - truecourse-list (list violations)");
4353
- O2.message(" - truecourse-fix (apply fixes)");
4361
+ const parent = skillsParentDir(repoPath);
4362
+ mkdirSync(parent, { recursive: true });
4363
+ for (const name of skillNames) {
4364
+ const skillSrc = resolve(src, name);
4365
+ const skillDest = resolve(parent, name);
4366
+ if (existsSync(skillDest)) continue;
4367
+ cpSync(skillSrc, skillDest, { recursive: true });
4368
+ }
4369
+ O2.success(
4370
+ `Installed ${skillNames.length} Claude Code skill${skillNames.length === 1 ? "" : "s"}:`
4371
+ );
4372
+ for (const name of skillNames) O2.message(` - ${name}`);
4354
4373
  }
4355
4374
  async function promptInstallSkills(repoPath, { install } = {}) {
4356
- if (hasInstalledSkills(repoPath)) return;
4375
+ const missing = computeMissingSkills(repoPath);
4376
+ if (missing.length === 0) return;
4357
4377
  if (install === true) {
4358
- copySkillsInto(repoPath);
4378
+ copySkills(repoPath, missing);
4359
4379
  return;
4360
4380
  }
4361
4381
  if (install === false) return;
4362
4382
  if (!isInteractive()) return;
4363
- const answer = await ot2({
4364
- message: "Would you like to install Claude Code skills?"
4365
- });
4383
+ const src = resolveSkillsSrcDir();
4384
+ const shipped = src ? listSkillDirs(src) : [];
4385
+ const parent = skillsParentDir(repoPath);
4386
+ const alreadyInstalled = shipped.some((name) => existsSync(resolve(parent, name)));
4387
+ const message = alreadyInstalled ? `New Claude Code skill${missing.length === 1 ? "" : "s"} available: ${missing.join(", ")}. Install?` : "Would you like to install Claude Code skills?";
4388
+ const answer = await ot2({ message });
4366
4389
  if (q(answer) || !answer) return;
4367
- copySkillsInto(repoPath);
4390
+ copySkills(repoPath, missing);
4368
4391
  }
4369
4392
  var DEFAULT_PORT, DEFAULT_CONFIG;
4370
4393
  var init_helpers = __esm({
@@ -10563,7 +10586,7 @@ var init_language_config = __esm({
10563
10586
  });
10564
10587
 
10565
10588
  // packages/analyzer/dist/file-discovery.js
10566
- import { existsSync as existsSync2, readFileSync, readdirSync, statSync } from "fs";
10589
+ import { existsSync as existsSync2, readFileSync, readdirSync as readdirSync2, statSync } from "fs";
10567
10590
  import { join, relative, resolve as resolve2 } from "path";
10568
10591
  function findAllGitignores(startDir) {
10569
10592
  const gitignores = [];
@@ -10606,7 +10629,7 @@ function discoverFiles(dir) {
10606
10629
  const { ig, rootDir } = loadIgnorePatterns(dir);
10607
10630
  function traverse(currentPath) {
10608
10631
  try {
10609
- const entries = readdirSync(currentPath).sort();
10632
+ const entries = readdirSync2(currentPath).sort();
10610
10633
  for (const entry of entries) {
10611
10634
  const fullPath = join(currentPath, entry);
10612
10635
  const relativePath = relative(rootDir, fullPath);
@@ -10894,7 +10917,7 @@ var init_service_patterns = __esm({
10894
10917
  // packages/analyzer/dist/ts-compiler.js
10895
10918
  import * as ts from "typescript";
10896
10919
  import { dirname as dirname2, join as join2 } from "path";
10897
- import { existsSync as existsSync3, readdirSync as readdirSync2, statSync as statSync2 } from "fs";
10920
+ import { existsSync as existsSync3, readdirSync as readdirSync3, statSync as statSync2 } from "fs";
10898
10921
  function buildScopedCompilerOptions(rootPath) {
10899
10922
  const result = [];
10900
10923
  const candidates = [join2(rootPath, "tsconfig.json")];
@@ -10903,7 +10926,7 @@ function buildScopedCompilerOptions(rootPath) {
10903
10926
  if (!existsSync3(dirPath) || !statSync2(dirPath).isDirectory())
10904
10927
  continue;
10905
10928
  try {
10906
- for (const entry of readdirSync2(dirPath).sort()) {
10929
+ for (const entry of readdirSync3(dirPath).sort()) {
10907
10930
  candidates.push(join2(dirPath, entry, "tsconfig.json"));
10908
10931
  }
10909
10932
  } catch {
@@ -13533,7 +13556,7 @@ var init_registry2 = __esm({
13533
13556
 
13534
13557
  // packages/analyzer/dist/dependency-graph.js
13535
13558
  import { resolve as resolve4, dirname as dirname4, join as join3 } from "path";
13536
- import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync3, realpathSync, statSync as statSync3 } from "fs";
13559
+ import { existsSync as existsSync4, readFileSync as readFileSync2, readdirSync as readdirSync4, realpathSync, statSync as statSync3 } from "fs";
13537
13560
  function resolveRelativeFallback(importSource, containingFile, analyzedFiles, extensions, indexFiles) {
13538
13561
  const fromDir = dirname4(containingFile);
13539
13562
  const basePath = resolve4(fromDir, importSource);
@@ -13561,7 +13584,7 @@ function buildWorkspacePackageMap(rootPath) {
13561
13584
  if (!existsSync4(dirPath) || !statSync3(dirPath).isDirectory())
13562
13585
  continue;
13563
13586
  try {
13564
- for (const entry of readdirSync3(dirPath).sort()) {
13587
+ for (const entry of readdirSync4(dirPath).sort()) {
13565
13588
  const pkgDir = join3(dirPath, entry);
13566
13589
  const pkgJsonPath = join3(pkgDir, "package.json");
13567
13590
  if (!existsSync4(pkgJsonPath))
@@ -16249,7 +16272,7 @@ var init_registry3 = __esm({
16249
16272
  });
16250
16273
 
16251
16274
  // packages/analyzer/dist/service-detector.js
16252
- import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync4, statSync as statSync4 } from "fs";
16275
+ import { existsSync as existsSync7, readFileSync as readFileSync5, readdirSync as readdirSync5, statSync as statSync4 } from "fs";
16253
16276
  import { join as join6, basename, dirname as dirname5 } from "path";
16254
16277
  function detectServices(rootPath, allFiles) {
16255
16278
  const monorepoServices = detectMonorepoServices(rootPath, allFiles);
@@ -16294,7 +16317,7 @@ function detectMonorepoServices(rootPath, allFiles) {
16294
16317
  if (!existsSync7(dirPath))
16295
16318
  continue;
16296
16319
  try {
16297
- const entries = readdirSync4(dirPath).sort();
16320
+ const entries = readdirSync5(dirPath).sort();
16298
16321
  for (const entry of entries) {
16299
16322
  const servicePath = join6(dirPath, entry);
16300
16323
  const stats = statSync4(servicePath);
@@ -17050,7 +17073,7 @@ var init_registry4 = __esm({
17050
17073
  });
17051
17074
 
17052
17075
  // packages/analyzer/dist/database-detector.js
17053
- import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync5 } from "fs";
17076
+ import { existsSync as existsSync8, readFileSync as readFileSync6, readdirSync as readdirSync6 } from "fs";
17054
17077
  import { join as join7, resolve as resolve5 } from "path";
17055
17078
  function detectDatabases(rootPath, analyses, services) {
17056
17079
  const detections = [];
@@ -17219,7 +17242,7 @@ function parseDockerCompose(rootPath) {
17219
17242
  function findFiles(dir, fileName, ignoreDirs) {
17220
17243
  const results = [];
17221
17244
  try {
17222
- const entries = readdirSync5(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
17245
+ const entries = readdirSync6(dir, { withFileTypes: true }).sort((a, b) => a.name.localeCompare(b.name));
17223
17246
  for (const entry of entries) {
17224
17247
  if (ignoreDirs.includes(entry.name))
17225
17248
  continue;
@@ -123658,7 +123681,7 @@ var init_schemas2 = __esm({
123658
123681
 
123659
123682
  // apps/server/dist/services/llm/cli-provider.js
123660
123683
  import { spawn as spawn3 } from "node:child_process";
123661
- import { mkdirSync, writeFileSync } from "node:fs";
123684
+ import { mkdirSync as mkdirSync2, writeFileSync } from "node:fs";
123662
123685
  import { join as join8 } from "node:path";
123663
123686
  import { tmpdir } from "node:os";
123664
123687
  import { randomUUID as randomUUID3 } from "node:crypto";
@@ -123721,7 +123744,7 @@ var init_cli_provider = __esm({
123721
123744
  constructor() {
123722
123745
  if (process.env.TRUECOURSE_CLI_DEBUG) {
123723
123746
  this.debugDir = join8(tmpdir(), "truecourse-cli-debug");
123724
- mkdirSync(this.debugDir, { recursive: true });
123747
+ mkdirSync2(this.debugDir, { recursive: true });
123725
123748
  log.info(`[CLI] Debug output: ${this.debugDir}`);
123726
123749
  }
123727
123750
  }
@@ -130710,7 +130733,7 @@ async function runHooksRun() {
130710
130733
 
130711
130734
  // tools/cli/src/index.ts
130712
130735
  var program2 = new Command();
130713
- program2.name("truecourse").version("0.5.2").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
130736
+ program2.name("truecourse").version("0.5.4").description("TrueCourse CLI \u2014 analyze your repository and open the dashboard");
130714
130737
  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) => {
130715
130738
  if (options.service && options.console) {
130716
130739
  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.5.2",
3
+ "version": "0.5.4",
4
4
  "description": "Visualize your codebase architecture as an interactive graph",
5
5
  "type": "module",
6
6
  "bin": {