truecourse 0.2.0 → 0.2.2
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 +34 -0
- package/cli.mjs +139 -47
- package/db/migrations/0011_noisy_colleen_wing.sql +2 -0
- package/db/migrations/meta/0011_snapshot.json +2075 -0
- package/db/migrations/meta/_journal.json +7 -0
- package/package.json +3 -1
- package/public/assets/index-B9lyLwLS.css +1 -0
- package/public/assets/{index-HTLMOO-T.js → index-DmLWAe9c.js} +85 -85
- package/public/index.html +2 -2
- package/server.mjs +41205 -31977
- package/public/assets/index-Dkc0tzWP.css +0 -1
package/README.md
CHANGED
|
@@ -113,6 +113,7 @@ Once the server is running, `cd` into any repo and:
|
|
|
113
113
|
npx truecourse add # Register repo without analyzing
|
|
114
114
|
npx truecourse analyze # Analyze current repo, show violations
|
|
115
115
|
npx truecourse analyze --code-review # Analyze with LLM code review (off by default)
|
|
116
|
+
npx truecourse analyze --no-llm # Deterministic checks only, skip all LLM calls
|
|
116
117
|
npx truecourse analyze --diff # Show new/resolved violations from uncommitted changes
|
|
117
118
|
npx truecourse list # Show violations from latest analysis
|
|
118
119
|
npx truecourse list --diff # Show saved diff check results
|
|
@@ -177,6 +178,39 @@ All rules are visible in the **Rules** tab in the web UI. Custom rule generation
|
|
|
177
178
|
| Rust | Planned |
|
|
178
179
|
| PHP | Planned |
|
|
179
180
|
|
|
181
|
+
## Telemetry
|
|
182
|
+
|
|
183
|
+
TrueCourse collects anonymous usage data to help us understand adoption and improve the product. Telemetry is **enabled by default** and can be disabled at any time.
|
|
184
|
+
|
|
185
|
+
### What is collected
|
|
186
|
+
|
|
187
|
+
- Event type (`analyze` or `diff-check`)
|
|
188
|
+
- Tool version
|
|
189
|
+
- Languages detected (e.g., TypeScript, Python)
|
|
190
|
+
- File count range (bucketed: 1-50, 50-200, etc.)
|
|
191
|
+
- Service count
|
|
192
|
+
- Analysis duration range (bucketed)
|
|
193
|
+
- OS and architecture (e.g., `darwin-arm64`)
|
|
194
|
+
- Random anonymous session ID (not tied to user identity)
|
|
195
|
+
|
|
196
|
+
### What is NOT collected
|
|
197
|
+
|
|
198
|
+
- Source code, file paths, repo names, or git URLs
|
|
199
|
+
- Violation details, rule results, or LLM outputs
|
|
200
|
+
- IP addresses, user identity, or machine hostname
|
|
201
|
+
|
|
202
|
+
### Opt out
|
|
203
|
+
|
|
204
|
+
```bash
|
|
205
|
+
npx truecourse telemetry disable # Disable telemetry
|
|
206
|
+
npx truecourse telemetry enable # Re-enable telemetry
|
|
207
|
+
npx truecourse telemetry status # Check current status
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
Telemetry is automatically disabled in CI environments (`CI=true`) and can also be disabled by setting `TRUECOURSE_TELEMETRY=0`.
|
|
211
|
+
|
|
212
|
+
Data is sent to [PostHog](https://posthog.com) for aggregation.
|
|
213
|
+
|
|
180
214
|
## License
|
|
181
215
|
|
|
182
216
|
MIT
|
package/cli.mjs
CHANGED
|
@@ -972,8 +972,8 @@ var require_command = __commonJS({
|
|
|
972
972
|
"node_modules/.pnpm/commander@12.1.0/node_modules/commander/lib/command.js"(exports) {
|
|
973
973
|
var EventEmitter = __require("node:events").EventEmitter;
|
|
974
974
|
var childProcess = __require("node:child_process");
|
|
975
|
-
var
|
|
976
|
-
var
|
|
975
|
+
var path10 = __require("node:path");
|
|
976
|
+
var fs9 = __require("node:fs");
|
|
977
977
|
var process2 = __require("node:process");
|
|
978
978
|
var { Argument: Argument2, humanReadableArgName } = require_argument();
|
|
979
979
|
var { CommanderError: CommanderError2 } = require_error();
|
|
@@ -1905,11 +1905,11 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1905
1905
|
let launchWithNode = false;
|
|
1906
1906
|
const sourceExt = [".js", ".ts", ".tsx", ".mjs", ".cjs"];
|
|
1907
1907
|
function findFile(baseDir, baseName) {
|
|
1908
|
-
const localBin =
|
|
1909
|
-
if (
|
|
1910
|
-
if (sourceExt.includes(
|
|
1908
|
+
const localBin = path10.resolve(baseDir, baseName);
|
|
1909
|
+
if (fs9.existsSync(localBin)) return localBin;
|
|
1910
|
+
if (sourceExt.includes(path10.extname(baseName))) return void 0;
|
|
1911
1911
|
const foundExt = sourceExt.find(
|
|
1912
|
-
(ext) =>
|
|
1912
|
+
(ext) => fs9.existsSync(`${localBin}${ext}`)
|
|
1913
1913
|
);
|
|
1914
1914
|
if (foundExt) return `${localBin}${foundExt}`;
|
|
1915
1915
|
return void 0;
|
|
@@ -1921,21 +1921,21 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1921
1921
|
if (this._scriptPath) {
|
|
1922
1922
|
let resolvedScriptPath;
|
|
1923
1923
|
try {
|
|
1924
|
-
resolvedScriptPath =
|
|
1924
|
+
resolvedScriptPath = fs9.realpathSync(this._scriptPath);
|
|
1925
1925
|
} catch (err) {
|
|
1926
1926
|
resolvedScriptPath = this._scriptPath;
|
|
1927
1927
|
}
|
|
1928
|
-
executableDir =
|
|
1929
|
-
|
|
1928
|
+
executableDir = path10.resolve(
|
|
1929
|
+
path10.dirname(resolvedScriptPath),
|
|
1930
1930
|
executableDir
|
|
1931
1931
|
);
|
|
1932
1932
|
}
|
|
1933
1933
|
if (executableDir) {
|
|
1934
1934
|
let localFile = findFile(executableDir, executableFile);
|
|
1935
1935
|
if (!localFile && !subcommand._executableFile && this._scriptPath) {
|
|
1936
|
-
const legacyName =
|
|
1936
|
+
const legacyName = path10.basename(
|
|
1937
1937
|
this._scriptPath,
|
|
1938
|
-
|
|
1938
|
+
path10.extname(this._scriptPath)
|
|
1939
1939
|
);
|
|
1940
1940
|
if (legacyName !== this._name) {
|
|
1941
1941
|
localFile = findFile(
|
|
@@ -1946,7 +1946,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
1946
1946
|
}
|
|
1947
1947
|
executableFile = localFile || executableFile;
|
|
1948
1948
|
}
|
|
1949
|
-
launchWithNode = sourceExt.includes(
|
|
1949
|
+
launchWithNode = sourceExt.includes(path10.extname(executableFile));
|
|
1950
1950
|
let proc;
|
|
1951
1951
|
if (process2.platform !== "win32") {
|
|
1952
1952
|
if (launchWithNode) {
|
|
@@ -2786,7 +2786,7 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2786
2786
|
* @return {Command}
|
|
2787
2787
|
*/
|
|
2788
2788
|
nameFromFilename(filename) {
|
|
2789
|
-
this._name =
|
|
2789
|
+
this._name = path10.basename(filename, path10.extname(filename));
|
|
2790
2790
|
return this;
|
|
2791
2791
|
}
|
|
2792
2792
|
/**
|
|
@@ -2800,9 +2800,9 @@ Expecting one of '${allowedValues.join("', '")}'`);
|
|
|
2800
2800
|
* @param {string} [path]
|
|
2801
2801
|
* @return {(string|null|Command)}
|
|
2802
2802
|
*/
|
|
2803
|
-
executableDir(
|
|
2804
|
-
if (
|
|
2805
|
-
this._executableDir =
|
|
2803
|
+
executableDir(path11) {
|
|
2804
|
+
if (path11 === void 0) return this._executableDir;
|
|
2805
|
+
this._executableDir = path11;
|
|
2806
2806
|
return this;
|
|
2807
2807
|
}
|
|
2808
2808
|
/**
|
|
@@ -3766,7 +3766,7 @@ ${import_picocolors2.default.gray(m2)} ${s}
|
|
|
3766
3766
|
// node_modules/.pnpm/xmlhttprequest-ssl@2.1.2/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js
|
|
3767
3767
|
var require_XMLHttpRequest = __commonJS({
|
|
3768
3768
|
"node_modules/.pnpm/xmlhttprequest-ssl@2.1.2/node_modules/xmlhttprequest-ssl/lib/XMLHttpRequest.js"(exports, module) {
|
|
3769
|
-
var
|
|
3769
|
+
var fs9 = __require("fs");
|
|
3770
3770
|
var Url = __require("url");
|
|
3771
3771
|
var spawn3 = __require("child_process").spawn;
|
|
3772
3772
|
module.exports = XMLHttpRequest3;
|
|
@@ -3924,7 +3924,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
3924
3924
|
throw new Error("XMLHttpRequest: Only GET method is supported");
|
|
3925
3925
|
}
|
|
3926
3926
|
if (settings.async) {
|
|
3927
|
-
|
|
3927
|
+
fs9.readFile(unescape(url2.pathname), function(error, data2) {
|
|
3928
3928
|
if (error) {
|
|
3929
3929
|
self.handleError(error, error.errno || -1);
|
|
3930
3930
|
} else {
|
|
@@ -3936,7 +3936,7 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
3936
3936
|
});
|
|
3937
3937
|
} else {
|
|
3938
3938
|
try {
|
|
3939
|
-
this.response =
|
|
3939
|
+
this.response = fs9.readFileSync(unescape(url2.pathname));
|
|
3940
3940
|
this.responseText = this.response.toString("utf8");
|
|
3941
3941
|
this.status = 200;
|
|
3942
3942
|
setState(self.DONE);
|
|
@@ -4062,15 +4062,15 @@ var require_XMLHttpRequest = __commonJS({
|
|
|
4062
4062
|
} else {
|
|
4063
4063
|
var contentFile = ".node-xmlhttprequest-content-" + process.pid;
|
|
4064
4064
|
var syncFile = ".node-xmlhttprequest-sync-" + process.pid;
|
|
4065
|
-
|
|
4065
|
+
fs9.writeFileSync(syncFile, "", "utf8");
|
|
4066
4066
|
var execString = "var http = require('http'), https = require('https'), fs = require('fs');var doRequest = http" + (ssl ? "s" : "") + ".request;var options = " + JSON.stringify(options) + ";var responseText = '';var responseData = Buffer.alloc(0);var req = doRequest(options, function(response) {response.on('data', function(chunk) { var data = Buffer.from(chunk); responseText += data.toString('utf8'); responseData = Buffer.concat([responseData, data]);});response.on('end', function() {fs.writeFileSync('" + contentFile + "', JSON.stringify({err: null, data: {statusCode: response.statusCode, headers: response.headers, text: responseText, data: responseData.toString('base64')}}), 'utf8');fs.unlinkSync('" + syncFile + "');});response.on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});}).on('error', function(error) {fs.writeFileSync('" + contentFile + "', 'NODE-XMLHTTPREQUEST-ERROR:' + JSON.stringify(error), 'utf8');fs.unlinkSync('" + syncFile + "');});" + (data ? "req.write('" + JSON.stringify(data).slice(1, -1).replace(/'/g, "\\'") + "');" : "") + "req.end();";
|
|
4067
4067
|
var syncProc = spawn3(process.argv[0], ["-e", execString]);
|
|
4068
4068
|
var statusText;
|
|
4069
|
-
while (
|
|
4069
|
+
while (fs9.existsSync(syncFile)) {
|
|
4070
4070
|
}
|
|
4071
|
-
self.responseText =
|
|
4071
|
+
self.responseText = fs9.readFileSync(contentFile, "utf8");
|
|
4072
4072
|
syncProc.stdin.end();
|
|
4073
|
-
|
|
4073
|
+
fs9.unlinkSync(contentFile);
|
|
4074
4074
|
if (self.responseText.match(/^NODE-XMLHTTPREQUEST-ERROR:/)) {
|
|
4075
4075
|
var errorObj = JSON.parse(self.responseText.replace(/^NODE-XMLHTTPREQUEST-ERROR:/, ""));
|
|
4076
4076
|
self.handleError(errorObj, 503);
|
|
@@ -9762,12 +9762,12 @@ function parse2(str) {
|
|
|
9762
9762
|
uri.queryKey = queryKey(uri, uri["query"]);
|
|
9763
9763
|
return uri;
|
|
9764
9764
|
}
|
|
9765
|
-
function pathNames(obj,
|
|
9766
|
-
const regx = /\/{2,9}/g, names =
|
|
9767
|
-
if (
|
|
9765
|
+
function pathNames(obj, path10) {
|
|
9766
|
+
const regx = /\/{2,9}/g, names = path10.replace(regx, "/").split("/");
|
|
9767
|
+
if (path10.slice(0, 1) == "/" || path10.length === 0) {
|
|
9768
9768
|
names.splice(0, 1);
|
|
9769
9769
|
}
|
|
9770
|
-
if (
|
|
9770
|
+
if (path10.slice(-1) == "/") {
|
|
9771
9771
|
names.splice(names.length - 1, 1);
|
|
9772
9772
|
}
|
|
9773
9773
|
return names;
|
|
@@ -10441,7 +10441,7 @@ var init_esm_debug = __esm({
|
|
|
10441
10441
|
});
|
|
10442
10442
|
|
|
10443
10443
|
// node_modules/.pnpm/socket.io-client@4.8.3/node_modules/socket.io-client/build/esm-debug/url.js
|
|
10444
|
-
function url(uri,
|
|
10444
|
+
function url(uri, path10 = "", loc) {
|
|
10445
10445
|
let obj = uri;
|
|
10446
10446
|
loc = loc || typeof location !== "undefined" && location;
|
|
10447
10447
|
if (null == uri)
|
|
@@ -10475,7 +10475,7 @@ function url(uri, path9 = "", loc) {
|
|
|
10475
10475
|
obj.path = obj.path || "/";
|
|
10476
10476
|
const ipv6 = obj.host.indexOf(":") !== -1;
|
|
10477
10477
|
const host = ipv6 ? "[" + obj.host + "]" : obj.host;
|
|
10478
|
-
obj.id = obj.protocol + "://" + host + ":" + obj.port +
|
|
10478
|
+
obj.id = obj.protocol + "://" + host + ":" + obj.port + path10;
|
|
10479
10479
|
obj.href = obj.protocol + "://" + host + (loc && loc.port === obj.port ? "" : ":" + obj.port);
|
|
10480
10480
|
return obj;
|
|
10481
10481
|
}
|
|
@@ -12146,8 +12146,8 @@ function lookup(uri, opts) {
|
|
|
12146
12146
|
const parsed = url(uri, opts.path || "/socket.io");
|
|
12147
12147
|
const source = parsed.source;
|
|
12148
12148
|
const id = parsed.id;
|
|
12149
|
-
const
|
|
12150
|
-
const sameNamespace = cache[id] &&
|
|
12149
|
+
const path10 = parsed.path;
|
|
12150
|
+
const sameNamespace = cache[id] && path10 in cache[id]["nsps"];
|
|
12151
12151
|
const newConnection = opts.forceNew || opts["force new connection"] || false === opts.multiplex || sameNamespace;
|
|
12152
12152
|
let io;
|
|
12153
12153
|
if (newConnection) {
|
|
@@ -12683,10 +12683,16 @@ function startConsoleMode(openBrowser) {
|
|
|
12683
12683
|
process.execPath,
|
|
12684
12684
|
[serverPath],
|
|
12685
12685
|
{
|
|
12686
|
-
stdio: "
|
|
12686
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
12687
12687
|
env: { ...process.env }
|
|
12688
12688
|
}
|
|
12689
12689
|
);
|
|
12690
|
+
const forwardOutput = (data) => {
|
|
12691
|
+
process.stdout.write("\r\x1B[K");
|
|
12692
|
+
process.stderr.write(data);
|
|
12693
|
+
};
|
|
12694
|
+
serverProcess.stdout?.on("data", forwardOutput);
|
|
12695
|
+
serverProcess.stderr?.on("data", forwardOutput);
|
|
12690
12696
|
serverProcess.on("error", (error) => {
|
|
12691
12697
|
v2.error(`Failed to start server: ${error.message}`);
|
|
12692
12698
|
process.exit(1);
|
|
@@ -13364,9 +13370,9 @@ var {
|
|
|
13364
13370
|
init_dist2();
|
|
13365
13371
|
init_setup();
|
|
13366
13372
|
init_start();
|
|
13367
|
-
import
|
|
13368
|
-
import
|
|
13369
|
-
import
|
|
13373
|
+
import fs8 from "node:fs";
|
|
13374
|
+
import path9 from "node:path";
|
|
13375
|
+
import os7 from "node:os";
|
|
13370
13376
|
|
|
13371
13377
|
// tools/cli/src/commands/add.ts
|
|
13372
13378
|
init_dist2();
|
|
@@ -13415,9 +13421,73 @@ async function runAdd() {
|
|
|
13415
13421
|
// tools/cli/src/commands/analyze.ts
|
|
13416
13422
|
init_dist2();
|
|
13417
13423
|
init_helpers();
|
|
13424
|
+
|
|
13425
|
+
// tools/cli/src/telemetry.ts
|
|
13426
|
+
init_dist2();
|
|
13427
|
+
import fs7 from "node:fs";
|
|
13428
|
+
import path7 from "node:path";
|
|
13429
|
+
import os6 from "node:os";
|
|
13430
|
+
import crypto from "node:crypto";
|
|
13431
|
+
var DEFAULT_CONFIG2 = {
|
|
13432
|
+
enabled: true,
|
|
13433
|
+
anonymousId: "",
|
|
13434
|
+
noticeShown: false
|
|
13435
|
+
};
|
|
13436
|
+
function getTelemetryConfigPath() {
|
|
13437
|
+
return path7.join(os6.homedir(), ".truecourse", "telemetry.json");
|
|
13438
|
+
}
|
|
13439
|
+
function readTelemetryConfig() {
|
|
13440
|
+
const configPath = getTelemetryConfigPath();
|
|
13441
|
+
try {
|
|
13442
|
+
const raw = fs7.readFileSync(configPath, "utf-8");
|
|
13443
|
+
const parsed = JSON.parse(raw);
|
|
13444
|
+
const config = { ...DEFAULT_CONFIG2, ...parsed };
|
|
13445
|
+
if (!config.anonymousId) {
|
|
13446
|
+
config.anonymousId = crypto.randomUUID();
|
|
13447
|
+
writeTelemetryConfig(config);
|
|
13448
|
+
}
|
|
13449
|
+
return config;
|
|
13450
|
+
} catch {
|
|
13451
|
+
const config = {
|
|
13452
|
+
enabled: true,
|
|
13453
|
+
anonymousId: crypto.randomUUID(),
|
|
13454
|
+
noticeShown: false
|
|
13455
|
+
};
|
|
13456
|
+
writeTelemetryConfig(config);
|
|
13457
|
+
return config;
|
|
13458
|
+
}
|
|
13459
|
+
}
|
|
13460
|
+
function writeTelemetryConfig(partial) {
|
|
13461
|
+
const configPath = getTelemetryConfigPath();
|
|
13462
|
+
const dir = path7.dirname(configPath);
|
|
13463
|
+
fs7.mkdirSync(dir, { recursive: true });
|
|
13464
|
+
let current;
|
|
13465
|
+
try {
|
|
13466
|
+
const raw = fs7.readFileSync(configPath, "utf-8");
|
|
13467
|
+
current = { ...DEFAULT_CONFIG2, ...JSON.parse(raw) };
|
|
13468
|
+
} catch {
|
|
13469
|
+
current = { ...DEFAULT_CONFIG2 };
|
|
13470
|
+
}
|
|
13471
|
+
const merged = { ...current, ...partial };
|
|
13472
|
+
fs7.writeFileSync(configPath, JSON.stringify(merged, null, 2) + "\n", "utf-8");
|
|
13473
|
+
}
|
|
13474
|
+
function showFirstRunNotice() {
|
|
13475
|
+
try {
|
|
13476
|
+
const config = readTelemetryConfig();
|
|
13477
|
+
if (!config.enabled || config.noticeShown) return;
|
|
13478
|
+
v2.info(
|
|
13479
|
+
"TrueCourse collects anonymous usage data to improve the product. Run `npx truecourse telemetry disable` to opt out."
|
|
13480
|
+
);
|
|
13481
|
+
writeTelemetryConfig({ noticeShown: true });
|
|
13482
|
+
} catch {
|
|
13483
|
+
}
|
|
13484
|
+
}
|
|
13485
|
+
|
|
13486
|
+
// tools/cli/src/commands/analyze.ts
|
|
13418
13487
|
var TIMEOUT_MS = 15 * 60 * 1e3;
|
|
13419
|
-
async function runAnalyze({ noAutostart = false, codeReview = false } = {}) {
|
|
13488
|
+
async function runAnalyze({ noAutostart = false, codeReview = false, deterministicOnly = false } = {}) {
|
|
13420
13489
|
we("Analyzing repository");
|
|
13490
|
+
showFirstRunNotice();
|
|
13421
13491
|
if (noAutostart) {
|
|
13422
13492
|
const url2 = getServerUrl();
|
|
13423
13493
|
try {
|
|
@@ -13480,7 +13550,7 @@ async function runAnalyze({ noAutostart = false, codeReview = false } = {}) {
|
|
|
13480
13550
|
fetch(`${serverUrl}/api/repos/${repo.id}/analyze`, {
|
|
13481
13551
|
method: "POST",
|
|
13482
13552
|
headers: { "Content-Type": "application/json" },
|
|
13483
|
-
body: JSON.stringify({ codeReview })
|
|
13553
|
+
body: JSON.stringify({ codeReview, deterministicOnly })
|
|
13484
13554
|
}).then((res2) => {
|
|
13485
13555
|
if (!res2.ok) {
|
|
13486
13556
|
clearTimeout(timeout);
|
|
@@ -13508,7 +13578,9 @@ async function runAnalyze({ noAutostart = false, codeReview = false } = {}) {
|
|
|
13508
13578
|
}
|
|
13509
13579
|
const violations = await res.json();
|
|
13510
13580
|
renderViolationsSummary(violations);
|
|
13511
|
-
|
|
13581
|
+
if (!deterministicOnly) {
|
|
13582
|
+
v2.info("Code review running in background \u2014 results will appear in the dashboard");
|
|
13583
|
+
}
|
|
13512
13584
|
const repoUrl = `${serverUrl}/repos/${repo.id}`;
|
|
13513
13585
|
if (firstRun) {
|
|
13514
13586
|
openInBrowser(repoUrl);
|
|
@@ -13533,6 +13605,7 @@ async function runAnalyze({ noAutostart = false, codeReview = false } = {}) {
|
|
|
13533
13605
|
}
|
|
13534
13606
|
async function runAnalyzeDiff({ noAutostart = false } = {}) {
|
|
13535
13607
|
we("Running diff check");
|
|
13608
|
+
showFirstRunNotice();
|
|
13536
13609
|
if (noAutostart) {
|
|
13537
13610
|
const url2 = getServerUrl();
|
|
13538
13611
|
try {
|
|
@@ -13627,11 +13700,11 @@ init_dist2();
|
|
|
13627
13700
|
init_platform();
|
|
13628
13701
|
init_logs();
|
|
13629
13702
|
init_helpers();
|
|
13630
|
-
import
|
|
13703
|
+
import path8 from "node:path";
|
|
13631
13704
|
import { fileURLToPath as fileURLToPath3 } from "node:url";
|
|
13632
|
-
var __dirname2 =
|
|
13705
|
+
var __dirname2 = path8.dirname(fileURLToPath3(import.meta.url));
|
|
13633
13706
|
function getServerPath2() {
|
|
13634
|
-
return
|
|
13707
|
+
return path8.join(__dirname2, "..", "server.mjs");
|
|
13635
13708
|
}
|
|
13636
13709
|
async function healthcheck2() {
|
|
13637
13710
|
const url2 = getServerUrl();
|
|
@@ -13786,7 +13859,7 @@ function registerServiceCommand(program3) {
|
|
|
13786
13859
|
init_helpers();
|
|
13787
13860
|
init_platform();
|
|
13788
13861
|
var program2 = new Command();
|
|
13789
|
-
program2.name("truecourse").version("0.
|
|
13862
|
+
program2.name("truecourse").version("0.2.2").description("TrueCourse CLI - Setup and manage your TrueCourse instance");
|
|
13790
13863
|
program2.command("setup").description("Run the setup wizard to configure TrueCourse").action(async () => {
|
|
13791
13864
|
await runSetup();
|
|
13792
13865
|
});
|
|
@@ -13796,11 +13869,11 @@ program2.command("start").description("Start TrueCourse services").action(async
|
|
|
13796
13869
|
program2.command("add").description("Add the current directory as a repository").action(async () => {
|
|
13797
13870
|
await runAdd();
|
|
13798
13871
|
});
|
|
13799
|
-
program2.command("analyze").description("Analyze the current repository").option("--diff", "Run diff check against latest analysis").option("--code-review", "Include LLM code review (off by default)").option("--no-autostart", "Don't auto-start the server (for use from Claude Code skills)").action(async (options) => {
|
|
13872
|
+
program2.command("analyze").description("Analyze the current repository").option("--diff", "Run diff check against latest analysis").option("--code-review", "Include LLM code review (off by default)").option("--no-llm", "Skip all LLM calls, run only deterministic checks").option("--no-autostart", "Don't auto-start the server (for use from Claude Code skills)").action(async (options) => {
|
|
13800
13873
|
if (options.diff) {
|
|
13801
13874
|
await runAnalyzeDiff({ noAutostart: !options.autostart });
|
|
13802
13875
|
} else {
|
|
13803
|
-
await runAnalyze({ noAutostart: !options.autostart, codeReview: options.codeReview ?? false });
|
|
13876
|
+
await runAnalyze({ noAutostart: !options.autostart, codeReview: options.codeReview ?? false, deterministicOnly: !options.llm });
|
|
13804
13877
|
}
|
|
13805
13878
|
});
|
|
13806
13879
|
program2.command("code-review").description("Run LLM code review on the latest analysis").option("--diff", "Run on the latest diff analysis instead").option("--no-autostart", "Don't auto-start the server").action(async (options) => {
|
|
@@ -13832,10 +13905,29 @@ program2.command("stop").description("Stop the TrueCourse background service").a
|
|
|
13832
13905
|
v2.info("Press Ctrl+C in the terminal where TrueCourse is running.");
|
|
13833
13906
|
}
|
|
13834
13907
|
});
|
|
13908
|
+
var telemetryCmd = program2.command("telemetry").description("Manage anonymous usage telemetry");
|
|
13909
|
+
telemetryCmd.command("enable").description("Enable anonymous usage telemetry").action(() => {
|
|
13910
|
+
writeTelemetryConfig({ enabled: true });
|
|
13911
|
+
v2.success("Telemetry enabled. Thank you for helping improve TrueCourse!");
|
|
13912
|
+
});
|
|
13913
|
+
telemetryCmd.command("disable").description("Disable anonymous usage telemetry").action(() => {
|
|
13914
|
+
writeTelemetryConfig({ enabled: false });
|
|
13915
|
+
v2.success("Telemetry disabled. No data will be collected.");
|
|
13916
|
+
});
|
|
13917
|
+
telemetryCmd.command("status").description("Show current telemetry status").action(() => {
|
|
13918
|
+
const config = readTelemetryConfig();
|
|
13919
|
+
if (process.env.CI === "true") {
|
|
13920
|
+
v2.info("Telemetry is automatically disabled in CI environments.");
|
|
13921
|
+
} else if (config.enabled) {
|
|
13922
|
+
v2.info("Telemetry is enabled.");
|
|
13923
|
+
} else {
|
|
13924
|
+
v2.info("Telemetry is disabled.");
|
|
13925
|
+
}
|
|
13926
|
+
});
|
|
13835
13927
|
program2.action(async () => {
|
|
13836
|
-
const configDir =
|
|
13837
|
-
const envPath =
|
|
13838
|
-
const isFirstRun = !
|
|
13928
|
+
const configDir = path9.join(os7.homedir(), ".truecourse");
|
|
13929
|
+
const envPath = path9.join(configDir, ".env");
|
|
13930
|
+
const isFirstRun = !fs8.existsSync(envPath);
|
|
13839
13931
|
if (isFirstRun) {
|
|
13840
13932
|
await runSetup();
|
|
13841
13933
|
}
|