truecourse 0.1.13 → 0.1.14
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 +149 -75
- package/package.json +4 -4
- package/public/assets/{index-CVwZeppe.js → index-DMFqQ7fW.js} +63 -63
- package/public/index.html +2 -2
- package/server.mjs +258 -94
- package/skills/truecourse/truecourse-analyze/SKILL.md +39 -0
- package/skills/truecourse/truecourse-fix/SKILL.md +34 -0
- package/skills/truecourse/truecourse-list/SKILL.md +26 -0
package/cli.mjs
CHANGED
|
@@ -12610,6 +12610,7 @@ var init_logs = __esm({
|
|
|
12610
12610
|
// tools/cli/src/commands/start.ts
|
|
12611
12611
|
var start_exports = {};
|
|
12612
12612
|
__export(start_exports, {
|
|
12613
|
+
getServerProcess: () => getServerProcess,
|
|
12613
12614
|
runStart: () => runStart
|
|
12614
12615
|
});
|
|
12615
12616
|
import { spawn as spawn2 } from "node:child_process";
|
|
@@ -12662,11 +12663,14 @@ async function startServiceMode(openBrowser) {
|
|
|
12662
12663
|
v2.info("Check logs with: truecourse service logs");
|
|
12663
12664
|
}
|
|
12664
12665
|
}
|
|
12666
|
+
function getServerProcess() {
|
|
12667
|
+
return _serverProcess;
|
|
12668
|
+
}
|
|
12665
12669
|
function startConsoleMode(openBrowser) {
|
|
12666
12670
|
const serverPath = getServerPath();
|
|
12667
12671
|
const url2 = getServerUrl();
|
|
12668
12672
|
v2.step("Starting server (embedded PostgreSQL starts automatically)...");
|
|
12669
|
-
const serverProcess = spawn2(
|
|
12673
|
+
const serverProcess = _serverProcess = spawn2(
|
|
12670
12674
|
process.execPath,
|
|
12671
12675
|
[serverPath],
|
|
12672
12676
|
{
|
|
@@ -12709,7 +12713,7 @@ async function runStart({ openBrowser = true } = {}) {
|
|
|
12709
12713
|
startConsoleMode(openBrowser);
|
|
12710
12714
|
}
|
|
12711
12715
|
}
|
|
12712
|
-
var __dirname;
|
|
12716
|
+
var __dirname, _serverProcess;
|
|
12713
12717
|
var init_start = __esm({
|
|
12714
12718
|
"tools/cli/src/commands/start.ts"() {
|
|
12715
12719
|
"use strict";
|
|
@@ -12718,6 +12722,7 @@ var init_start = __esm({
|
|
|
12718
12722
|
init_platform();
|
|
12719
12723
|
init_logs();
|
|
12720
12724
|
__dirname = path4.dirname(fileURLToPath(import.meta.url));
|
|
12725
|
+
_serverProcess = null;
|
|
12721
12726
|
}
|
|
12722
12727
|
});
|
|
12723
12728
|
|
|
@@ -12730,6 +12735,7 @@ __export(helpers_exports, {
|
|
|
12730
12735
|
getConfigPath: () => getConfigPath,
|
|
12731
12736
|
getServerUrl: () => getServerUrl,
|
|
12732
12737
|
openInBrowser: () => openInBrowser,
|
|
12738
|
+
promptInstallSkills: () => promptInstallSkills,
|
|
12733
12739
|
readConfig: () => readConfig,
|
|
12734
12740
|
renderDiffResults: () => renderDiffResults,
|
|
12735
12741
|
renderDiffResultsSummary: () => renderDiffResultsSummary,
|
|
@@ -12740,8 +12746,11 @@ __export(helpers_exports, {
|
|
|
12740
12746
|
writeConfig: () => writeConfig
|
|
12741
12747
|
});
|
|
12742
12748
|
import { exec } from "node:child_process";
|
|
12749
|
+
import { cpSync, existsSync } from "node:fs";
|
|
12743
12750
|
import fs5 from "node:fs";
|
|
12744
12751
|
import path5 from "node:path";
|
|
12752
|
+
import { dirname, resolve } from "node:path";
|
|
12753
|
+
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
12745
12754
|
import os4 from "node:os";
|
|
12746
12755
|
function getConfigPath() {
|
|
12747
12756
|
return path5.join(os4.homedir(), ".truecourse", "config.json");
|
|
@@ -12775,16 +12784,28 @@ async function ensureServer() {
|
|
|
12775
12784
|
if (!res.ok) throw new Error(`Server returned ${res.status}`);
|
|
12776
12785
|
return false;
|
|
12777
12786
|
} catch {
|
|
12778
|
-
const
|
|
12787
|
+
const envPath = path5.join(os4.homedir(), ".truecourse", ".env");
|
|
12788
|
+
if (!fs5.existsSync(envPath)) {
|
|
12789
|
+
const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_setup(), setup_exports));
|
|
12790
|
+
await runSetup2();
|
|
12791
|
+
}
|
|
12792
|
+
const { runStart: runStart2, getServerProcess: getServerProcess2 } = await Promise.resolve().then(() => (init_start(), start_exports));
|
|
12779
12793
|
await runStart2({ openBrowser: false });
|
|
12780
|
-
|
|
12781
|
-
const
|
|
12782
|
-
if (!
|
|
12783
|
-
}
|
|
12784
|
-
|
|
12785
|
-
|
|
12794
|
+
const killServer = () => {
|
|
12795
|
+
const proc = getServerProcess2();
|
|
12796
|
+
if (proc && !proc.killed) proc.kill("SIGTERM");
|
|
12797
|
+
};
|
|
12798
|
+
process.on("exit", killServer);
|
|
12799
|
+
for (let i = 0; i < 120; i++) {
|
|
12800
|
+
try {
|
|
12801
|
+
const res = await fetch(`${url2}/api/health`);
|
|
12802
|
+
if (res.ok) return true;
|
|
12803
|
+
} catch {
|
|
12804
|
+
}
|
|
12805
|
+
await new Promise((r2) => setTimeout(r2, 500));
|
|
12786
12806
|
}
|
|
12787
|
-
|
|
12807
|
+
v2.error("Server failed to start. Check logs with: truecourse service logs");
|
|
12808
|
+
process.exit(1);
|
|
12788
12809
|
}
|
|
12789
12810
|
}
|
|
12790
12811
|
async function ensureRepo() {
|
|
@@ -12807,7 +12828,11 @@ async function ensureRepo() {
|
|
|
12807
12828
|
v2.error(message);
|
|
12808
12829
|
process.exit(1);
|
|
12809
12830
|
}
|
|
12810
|
-
|
|
12831
|
+
const repo = await res.json();
|
|
12832
|
+
if (res.status === 201) {
|
|
12833
|
+
await promptInstallSkills(process.cwd());
|
|
12834
|
+
}
|
|
12835
|
+
return repo;
|
|
12811
12836
|
}
|
|
12812
12837
|
function connectSocket(repoId) {
|
|
12813
12838
|
const url2 = getServerUrl();
|
|
@@ -13005,6 +13030,26 @@ function openInBrowser(url2) {
|
|
|
13005
13030
|
const cmd = process.platform === "darwin" ? "open" : process.platform === "win32" ? "start" : "xdg-open";
|
|
13006
13031
|
exec(`${cmd} ${url2}`);
|
|
13007
13032
|
}
|
|
13033
|
+
async function promptInstallSkills(repoPath) {
|
|
13034
|
+
const installSkills = await me({
|
|
13035
|
+
message: "Would you like to install Claude Code skills?"
|
|
13036
|
+
});
|
|
13037
|
+
if (BD(installSkills) || !installSkills) return;
|
|
13038
|
+
const __dirname3 = dirname(fileURLToPath2(import.meta.url));
|
|
13039
|
+
const srcPath = resolve(__dirname3, "..", "..", "skills", "truecourse");
|
|
13040
|
+
const distPath = resolve(__dirname3, "skills", "truecourse");
|
|
13041
|
+
const skillsSrc = existsSync(srcPath) ? srcPath : distPath;
|
|
13042
|
+
if (!existsSync(skillsSrc)) {
|
|
13043
|
+
v2.warn("Skills directory not found in package \u2014 skipping.");
|
|
13044
|
+
return;
|
|
13045
|
+
}
|
|
13046
|
+
const skillsDest = resolve(repoPath, ".claude", "skills");
|
|
13047
|
+
cpSync(skillsSrc, skillsDest, { recursive: true });
|
|
13048
|
+
v2.success("Installed Claude Code skills:");
|
|
13049
|
+
v2.message(" - truecourse-analyze (run analysis)");
|
|
13050
|
+
v2.message(" - truecourse-list (list violations)");
|
|
13051
|
+
v2.message(" - truecourse-fix (apply fixes)");
|
|
13052
|
+
}
|
|
13008
13053
|
var DEFAULT_PORT, DEFAULT_CONFIG;
|
|
13009
13054
|
var init_helpers = __esm({
|
|
13010
13055
|
"tools/cli/src/commands/helpers.ts"() {
|
|
@@ -13016,39 +13061,15 @@ var init_helpers = __esm({
|
|
|
13016
13061
|
}
|
|
13017
13062
|
});
|
|
13018
13063
|
|
|
13019
|
-
// node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
13020
|
-
var import_index = __toESM(require_commander(), 1);
|
|
13021
|
-
var {
|
|
13022
|
-
program,
|
|
13023
|
-
createCommand,
|
|
13024
|
-
createArgument,
|
|
13025
|
-
createOption,
|
|
13026
|
-
CommanderError,
|
|
13027
|
-
InvalidArgumentError,
|
|
13028
|
-
InvalidOptionArgumentError,
|
|
13029
|
-
// deprecated old name
|
|
13030
|
-
Command,
|
|
13031
|
-
Argument,
|
|
13032
|
-
Option,
|
|
13033
|
-
Help
|
|
13034
|
-
} = import_index.default;
|
|
13035
|
-
|
|
13036
|
-
// tools/cli/src/index.ts
|
|
13037
|
-
init_dist2();
|
|
13038
|
-
import fs7 from "node:fs";
|
|
13039
|
-
import path8 from "node:path";
|
|
13040
|
-
import os6 from "node:os";
|
|
13041
|
-
|
|
13042
13064
|
// tools/cli/src/commands/setup.ts
|
|
13043
|
-
|
|
13065
|
+
var setup_exports = {};
|
|
13066
|
+
__export(setup_exports, {
|
|
13067
|
+
runSetup: () => runSetup
|
|
13068
|
+
});
|
|
13044
13069
|
import { execSync as execSync4 } from "node:child_process";
|
|
13045
13070
|
import fs6 from "node:fs";
|
|
13046
13071
|
import path6 from "node:path";
|
|
13047
13072
|
import os5 from "node:os";
|
|
13048
|
-
var DEFAULT_MODELS = {
|
|
13049
|
-
anthropic: "claude-sonnet-4-20250514",
|
|
13050
|
-
openai: "gpt-5.3-codex"
|
|
13051
|
-
};
|
|
13052
13073
|
function buildEnvContents(config) {
|
|
13053
13074
|
const lines = [
|
|
13054
13075
|
"# TrueCourse Environment Configuration",
|
|
@@ -13194,8 +13215,8 @@ async function runSetup() {
|
|
|
13194
13215
|
const runMode = await de({
|
|
13195
13216
|
message: "How would you like to run TrueCourse?",
|
|
13196
13217
|
options: [
|
|
13197
|
-
{ value: "
|
|
13198
|
-
{ value: "
|
|
13218
|
+
{ value: "service", label: "Background service (Recommended)" },
|
|
13219
|
+
{ value: "console", label: "Console (keep terminal open)" }
|
|
13199
13220
|
]
|
|
13200
13221
|
});
|
|
13201
13222
|
if (BD(runMode)) {
|
|
@@ -13211,16 +13232,46 @@ async function runSetup() {
|
|
|
13211
13232
|
v2.info("Database migrations are applied on server startup.");
|
|
13212
13233
|
fe("Setup complete!");
|
|
13213
13234
|
}
|
|
13235
|
+
var DEFAULT_MODELS;
|
|
13236
|
+
var init_setup = __esm({
|
|
13237
|
+
"tools/cli/src/commands/setup.ts"() {
|
|
13238
|
+
"use strict";
|
|
13239
|
+
init_dist2();
|
|
13240
|
+
DEFAULT_MODELS = {
|
|
13241
|
+
anthropic: "claude-sonnet-4-20250514",
|
|
13242
|
+
openai: "gpt-5.3-codex"
|
|
13243
|
+
};
|
|
13244
|
+
}
|
|
13245
|
+
});
|
|
13246
|
+
|
|
13247
|
+
// node_modules/.pnpm/commander@12.1.0/node_modules/commander/esm.mjs
|
|
13248
|
+
var import_index = __toESM(require_commander(), 1);
|
|
13249
|
+
var {
|
|
13250
|
+
program,
|
|
13251
|
+
createCommand,
|
|
13252
|
+
createArgument,
|
|
13253
|
+
createOption,
|
|
13254
|
+
CommanderError,
|
|
13255
|
+
InvalidArgumentError,
|
|
13256
|
+
InvalidOptionArgumentError,
|
|
13257
|
+
// deprecated old name
|
|
13258
|
+
Command,
|
|
13259
|
+
Argument,
|
|
13260
|
+
Option,
|
|
13261
|
+
Help
|
|
13262
|
+
} = import_index.default;
|
|
13214
13263
|
|
|
13215
13264
|
// tools/cli/src/index.ts
|
|
13265
|
+
init_dist2();
|
|
13266
|
+
init_setup();
|
|
13216
13267
|
init_start();
|
|
13268
|
+
import fs7 from "node:fs";
|
|
13269
|
+
import path8 from "node:path";
|
|
13270
|
+
import os6 from "node:os";
|
|
13217
13271
|
|
|
13218
13272
|
// tools/cli/src/commands/add.ts
|
|
13219
13273
|
init_dist2();
|
|
13220
13274
|
init_helpers();
|
|
13221
|
-
import { cpSync, existsSync } from "node:fs";
|
|
13222
|
-
import { resolve, dirname } from "node:path";
|
|
13223
|
-
import { fileURLToPath as fileURLToPath2 } from "node:url";
|
|
13224
13275
|
async function runAdd() {
|
|
13225
13276
|
const repoPath = process.cwd();
|
|
13226
13277
|
const serverUrl = getServerUrl();
|
|
@@ -13247,27 +13298,7 @@ async function runAdd() {
|
|
|
13247
13298
|
const repo = await res.json();
|
|
13248
13299
|
const repoUrl = `${serverUrl}/repos/${repo.id}`;
|
|
13249
13300
|
v2.success(`Repository "${repo.name}" added`);
|
|
13250
|
-
|
|
13251
|
-
message: "Would you like to install Claude Code skills?"
|
|
13252
|
-
});
|
|
13253
|
-
if (BD(installSkills)) {
|
|
13254
|
-
fe(`Open ${repoUrl}`);
|
|
13255
|
-
return;
|
|
13256
|
-
}
|
|
13257
|
-
if (installSkills) {
|
|
13258
|
-
const cliDir = dirname(fileURLToPath2(import.meta.url));
|
|
13259
|
-
const skillsSrc = resolve(cliDir, "..", "..", "skills", "truecourse");
|
|
13260
|
-
if (!existsSync(skillsSrc)) {
|
|
13261
|
-
v2.warn("Skills directory not found in package \u2014 skipping.");
|
|
13262
|
-
} else {
|
|
13263
|
-
const skillsDest = resolve(repoPath, ".claude", "skills", "truecourse");
|
|
13264
|
-
cpSync(skillsSrc, skillsDest, { recursive: true });
|
|
13265
|
-
v2.success("Installed Claude Code skills:");
|
|
13266
|
-
v2.message(" - truecourse-analyze (run analysis)");
|
|
13267
|
-
v2.message(" - truecourse-list (list violations)");
|
|
13268
|
-
v2.message(" - truecourse-fix (apply fixes)");
|
|
13269
|
-
}
|
|
13270
|
-
}
|
|
13301
|
+
await promptInstallSkills(repoPath);
|
|
13271
13302
|
fe(`Open ${repoUrl}`);
|
|
13272
13303
|
} catch (err) {
|
|
13273
13304
|
const message = err instanceof Error ? err.message : String(err);
|
|
@@ -13286,15 +13317,37 @@ async function runAdd() {
|
|
|
13286
13317
|
init_dist2();
|
|
13287
13318
|
init_helpers();
|
|
13288
13319
|
var TIMEOUT_MS = 15 * 60 * 1e3;
|
|
13289
|
-
async function runAnalyze() {
|
|
13320
|
+
async function runAnalyze({ noAutostart = false } = {}) {
|
|
13290
13321
|
we("Analyzing repository");
|
|
13291
|
-
|
|
13322
|
+
if (noAutostart) {
|
|
13323
|
+
const url2 = getServerUrl();
|
|
13324
|
+
try {
|
|
13325
|
+
const res = await fetch(`${url2}/api/health`);
|
|
13326
|
+
if (!res.ok) throw new Error();
|
|
13327
|
+
} catch {
|
|
13328
|
+
v2.error("TrueCourse server is not running. Start it with: npx truecourse start");
|
|
13329
|
+
process.exit(1);
|
|
13330
|
+
}
|
|
13331
|
+
}
|
|
13332
|
+
const firstRun = noAutostart ? false : await ensureServer();
|
|
13292
13333
|
const repo = await ensureRepo();
|
|
13293
13334
|
v2.step(`Repository: ${repo.name}`);
|
|
13294
13335
|
const serverUrl = getServerUrl();
|
|
13295
13336
|
const socket = connectSocket(repo.id);
|
|
13296
13337
|
const spinner = L2();
|
|
13297
13338
|
spinner.start("Starting analysis...");
|
|
13339
|
+
let canceled = false;
|
|
13340
|
+
const onSigint = () => {
|
|
13341
|
+
if (canceled) return;
|
|
13342
|
+
canceled = true;
|
|
13343
|
+
spinner.stop("Cancelling analysis...");
|
|
13344
|
+
fetch(`${serverUrl}/api/repos/${repo.id}/analyze/cancel`, { method: "POST" }).catch(() => {
|
|
13345
|
+
}).finally(() => {
|
|
13346
|
+
socket.disconnect();
|
|
13347
|
+
process.exit(130);
|
|
13348
|
+
});
|
|
13349
|
+
};
|
|
13350
|
+
process.on("SIGINT", onSigint);
|
|
13298
13351
|
try {
|
|
13299
13352
|
await new Promise((resolve2, reject) => {
|
|
13300
13353
|
const timeout = setTimeout(() => {
|
|
@@ -13321,6 +13374,10 @@ async function runAnalyze() {
|
|
|
13321
13374
|
violationsReady = true;
|
|
13322
13375
|
checkDone();
|
|
13323
13376
|
});
|
|
13377
|
+
socket.on("analysis:canceled", () => {
|
|
13378
|
+
clearTimeout(timeout);
|
|
13379
|
+
reject(new Error("CANCELED"));
|
|
13380
|
+
});
|
|
13324
13381
|
fetch(`${serverUrl}/api/repos/${repo.id}/analyze`, {
|
|
13325
13382
|
method: "POST",
|
|
13326
13383
|
headers: { "Content-Type": "application/json" },
|
|
@@ -13360,17 +13417,34 @@ async function runAnalyze() {
|
|
|
13360
13417
|
fe(`Analysis complete \u2014 open ${repoUrl}`);
|
|
13361
13418
|
}
|
|
13362
13419
|
} catch (err) {
|
|
13363
|
-
spinner.stop("Analysis failed");
|
|
13364
13420
|
const message = err instanceof Error ? err.message : String(err);
|
|
13365
|
-
|
|
13366
|
-
|
|
13421
|
+
if (message === "CANCELED") {
|
|
13422
|
+
spinner.stop("Analysis cancelled");
|
|
13423
|
+
fe("Analysis cancelled");
|
|
13424
|
+
} else {
|
|
13425
|
+
spinner.stop("Analysis failed");
|
|
13426
|
+
v2.error(message);
|
|
13427
|
+
process.exit(1);
|
|
13428
|
+
}
|
|
13367
13429
|
} finally {
|
|
13430
|
+
process.removeListener("SIGINT", onSigint);
|
|
13368
13431
|
socket.disconnect();
|
|
13369
13432
|
}
|
|
13370
13433
|
}
|
|
13371
|
-
async function runAnalyzeDiff() {
|
|
13434
|
+
async function runAnalyzeDiff({ noAutostart = false } = {}) {
|
|
13372
13435
|
we("Running diff check");
|
|
13373
|
-
|
|
13436
|
+
if (noAutostart) {
|
|
13437
|
+
const url2 = getServerUrl();
|
|
13438
|
+
try {
|
|
13439
|
+
const res = await fetch(`${url2}/api/health`);
|
|
13440
|
+
if (!res.ok) throw new Error();
|
|
13441
|
+
} catch {
|
|
13442
|
+
v2.error("TrueCourse server is not running. Start it with: npx truecourse start");
|
|
13443
|
+
process.exit(1);
|
|
13444
|
+
}
|
|
13445
|
+
} else {
|
|
13446
|
+
await ensureServer();
|
|
13447
|
+
}
|
|
13374
13448
|
const repo = await ensureRepo();
|
|
13375
13449
|
v2.step(`Repository: ${repo.name}`);
|
|
13376
13450
|
const serverUrl = getServerUrl();
|
|
@@ -13622,11 +13696,11 @@ program2.command("start").description("Start TrueCourse services").action(async
|
|
|
13622
13696
|
program2.command("add").description("Add the current directory as a repository").action(async () => {
|
|
13623
13697
|
await runAdd();
|
|
13624
13698
|
});
|
|
13625
|
-
program2.command("analyze").description("Analyze the current repository").option("--diff", "Run diff check against latest analysis").action(async (options) => {
|
|
13699
|
+
program2.command("analyze").description("Analyze the current repository").option("--diff", "Run diff check against latest analysis").option("--no-autostart", "Don't auto-start the server (for use from Claude Code skills)").action(async (options) => {
|
|
13626
13700
|
if (options.diff) {
|
|
13627
|
-
await runAnalyzeDiff();
|
|
13701
|
+
await runAnalyzeDiff({ noAutostart: !options.autostart });
|
|
13628
13702
|
} else {
|
|
13629
|
-
await runAnalyze();
|
|
13703
|
+
await runAnalyze({ noAutostart: !options.autostart });
|
|
13630
13704
|
}
|
|
13631
13705
|
});
|
|
13632
13706
|
program2.command("list").description("List violations from the latest analysis").option("--diff", "Show diff check results (new and resolved)").action(async (options) => {
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "truecourse",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.14",
|
|
4
4
|
"description": "Visualize your codebase architecture as an interactive graph",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -15,9 +15,9 @@
|
|
|
15
15
|
"dotenv": "^16.4.0",
|
|
16
16
|
"embedded-postgres": "18.3.0-beta.16",
|
|
17
17
|
"postgres": "^3.4.0",
|
|
18
|
-
"tree-sitter": "^0.
|
|
19
|
-
"tree-sitter-javascript": "^0.
|
|
20
|
-
"tree-sitter-typescript": "^0.
|
|
18
|
+
"tree-sitter": "^0.25.0",
|
|
19
|
+
"tree-sitter-javascript": "^0.25.0",
|
|
20
|
+
"tree-sitter-typescript": "^0.23.2"
|
|
21
21
|
},
|
|
22
22
|
"optionalDependencies": {
|
|
23
23
|
"node-windows": "^1.0.0-beta.8"
|