truecourse 0.5.4 → 0.5.5
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 +22 -0
- package/cli.mjs +357 -54
- package/package.json +1 -1
- package/server.mjs +376 -62
package/server.mjs
CHANGED
|
@@ -23239,6 +23239,14 @@ var init_env = __esm({
|
|
|
23239
23239
|
});
|
|
23240
23240
|
|
|
23241
23241
|
// apps/server/src/config/index.ts
|
|
23242
|
+
function parsePositiveInt(envVar, raw, defaultValue) {
|
|
23243
|
+
if (raw === void 0 || raw === "") return defaultValue;
|
|
23244
|
+
const parsed = Number(raw);
|
|
23245
|
+
if (!Number.isInteger(parsed) || parsed < 1) {
|
|
23246
|
+
throw new Error(`${envVar} must be a positive integer, got "${raw}"`);
|
|
23247
|
+
}
|
|
23248
|
+
return parsed;
|
|
23249
|
+
}
|
|
23242
23250
|
var config;
|
|
23243
23251
|
var init_config = __esm({
|
|
23244
23252
|
"apps/server/src/config/index.ts"() {
|
|
@@ -23251,7 +23259,12 @@ var init_config = __esm({
|
|
|
23251
23259
|
claudeCodeBinary: process.env.CLAUDE_CODE_BINARY || "claude",
|
|
23252
23260
|
claudeCodeModel: process.env.CLAUDE_CODE_MODEL || "",
|
|
23253
23261
|
claudeCodeTimeoutMs: parseInt(process.env.CLAUDE_CODE_TIMEOUT_MS || "120000", 10),
|
|
23254
|
-
claudeCodeMaxRetries: parseInt(process.env.CLAUDE_CODE_MAX_RETRIES || "2", 10)
|
|
23262
|
+
claudeCodeMaxRetries: parseInt(process.env.CLAUDE_CODE_MAX_RETRIES || "2", 10),
|
|
23263
|
+
claudeCodeMaxConcurrency: parsePositiveInt(
|
|
23264
|
+
"CLAUDE_CODE_MAX_CONCURRENCY",
|
|
23265
|
+
process.env.CLAUDE_CODE_MAX_CONCURRENCY,
|
|
23266
|
+
10
|
|
23267
|
+
)
|
|
23255
23268
|
};
|
|
23256
23269
|
}
|
|
23257
23270
|
});
|
|
@@ -124048,6 +124061,172 @@ var init_dist2 = __esm({
|
|
|
124048
124061
|
}
|
|
124049
124062
|
});
|
|
124050
124063
|
|
|
124064
|
+
// node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js
|
|
124065
|
+
var Node, Queue;
|
|
124066
|
+
var init_yocto_queue = __esm({
|
|
124067
|
+
"node_modules/.pnpm/yocto-queue@1.2.2/node_modules/yocto-queue/index.js"() {
|
|
124068
|
+
Node = class {
|
|
124069
|
+
value;
|
|
124070
|
+
next;
|
|
124071
|
+
constructor(value) {
|
|
124072
|
+
this.value = value;
|
|
124073
|
+
}
|
|
124074
|
+
};
|
|
124075
|
+
Queue = class {
|
|
124076
|
+
#head;
|
|
124077
|
+
#tail;
|
|
124078
|
+
#size;
|
|
124079
|
+
constructor() {
|
|
124080
|
+
this.clear();
|
|
124081
|
+
}
|
|
124082
|
+
enqueue(value) {
|
|
124083
|
+
const node2 = new Node(value);
|
|
124084
|
+
if (this.#head) {
|
|
124085
|
+
this.#tail.next = node2;
|
|
124086
|
+
this.#tail = node2;
|
|
124087
|
+
} else {
|
|
124088
|
+
this.#head = node2;
|
|
124089
|
+
this.#tail = node2;
|
|
124090
|
+
}
|
|
124091
|
+
this.#size++;
|
|
124092
|
+
}
|
|
124093
|
+
dequeue() {
|
|
124094
|
+
const current = this.#head;
|
|
124095
|
+
if (!current) {
|
|
124096
|
+
return;
|
|
124097
|
+
}
|
|
124098
|
+
this.#head = this.#head.next;
|
|
124099
|
+
this.#size--;
|
|
124100
|
+
if (!this.#head) {
|
|
124101
|
+
this.#tail = void 0;
|
|
124102
|
+
}
|
|
124103
|
+
return current.value;
|
|
124104
|
+
}
|
|
124105
|
+
peek() {
|
|
124106
|
+
if (!this.#head) {
|
|
124107
|
+
return;
|
|
124108
|
+
}
|
|
124109
|
+
return this.#head.value;
|
|
124110
|
+
}
|
|
124111
|
+
clear() {
|
|
124112
|
+
this.#head = void 0;
|
|
124113
|
+
this.#tail = void 0;
|
|
124114
|
+
this.#size = 0;
|
|
124115
|
+
}
|
|
124116
|
+
get size() {
|
|
124117
|
+
return this.#size;
|
|
124118
|
+
}
|
|
124119
|
+
*[Symbol.iterator]() {
|
|
124120
|
+
let current = this.#head;
|
|
124121
|
+
while (current) {
|
|
124122
|
+
yield current.value;
|
|
124123
|
+
current = current.next;
|
|
124124
|
+
}
|
|
124125
|
+
}
|
|
124126
|
+
*drain() {
|
|
124127
|
+
while (this.#head) {
|
|
124128
|
+
yield this.dequeue();
|
|
124129
|
+
}
|
|
124130
|
+
}
|
|
124131
|
+
};
|
|
124132
|
+
}
|
|
124133
|
+
});
|
|
124134
|
+
|
|
124135
|
+
// node_modules/.pnpm/p-limit@7.3.0/node_modules/p-limit/index.js
|
|
124136
|
+
function pLimit(concurrency) {
|
|
124137
|
+
let rejectOnClear = false;
|
|
124138
|
+
if (typeof concurrency === "object") {
|
|
124139
|
+
({ concurrency, rejectOnClear = false } = concurrency);
|
|
124140
|
+
}
|
|
124141
|
+
validateConcurrency(concurrency);
|
|
124142
|
+
if (typeof rejectOnClear !== "boolean") {
|
|
124143
|
+
throw new TypeError("Expected `rejectOnClear` to be a boolean");
|
|
124144
|
+
}
|
|
124145
|
+
const queue = new Queue();
|
|
124146
|
+
let activeCount = 0;
|
|
124147
|
+
const resumeNext = () => {
|
|
124148
|
+
if (activeCount < concurrency && queue.size > 0) {
|
|
124149
|
+
activeCount++;
|
|
124150
|
+
queue.dequeue().run();
|
|
124151
|
+
}
|
|
124152
|
+
};
|
|
124153
|
+
const next = () => {
|
|
124154
|
+
activeCount--;
|
|
124155
|
+
resumeNext();
|
|
124156
|
+
};
|
|
124157
|
+
const run = async (function_, resolve7, arguments_) => {
|
|
124158
|
+
const result = (async () => function_(...arguments_))();
|
|
124159
|
+
resolve7(result);
|
|
124160
|
+
try {
|
|
124161
|
+
await result;
|
|
124162
|
+
} catch {
|
|
124163
|
+
}
|
|
124164
|
+
next();
|
|
124165
|
+
};
|
|
124166
|
+
const enqueue = (function_, resolve7, reject, arguments_) => {
|
|
124167
|
+
const queueItem = { reject };
|
|
124168
|
+
new Promise((internalResolve) => {
|
|
124169
|
+
queueItem.run = internalResolve;
|
|
124170
|
+
queue.enqueue(queueItem);
|
|
124171
|
+
}).then(run.bind(void 0, function_, resolve7, arguments_));
|
|
124172
|
+
if (activeCount < concurrency) {
|
|
124173
|
+
resumeNext();
|
|
124174
|
+
}
|
|
124175
|
+
};
|
|
124176
|
+
const generator = (function_, ...arguments_) => new Promise((resolve7, reject) => {
|
|
124177
|
+
enqueue(function_, resolve7, reject, arguments_);
|
|
124178
|
+
});
|
|
124179
|
+
Object.defineProperties(generator, {
|
|
124180
|
+
activeCount: {
|
|
124181
|
+
get: () => activeCount
|
|
124182
|
+
},
|
|
124183
|
+
pendingCount: {
|
|
124184
|
+
get: () => queue.size
|
|
124185
|
+
},
|
|
124186
|
+
clearQueue: {
|
|
124187
|
+
value() {
|
|
124188
|
+
if (!rejectOnClear) {
|
|
124189
|
+
queue.clear();
|
|
124190
|
+
return;
|
|
124191
|
+
}
|
|
124192
|
+
const abortError = AbortSignal.abort().reason;
|
|
124193
|
+
while (queue.size > 0) {
|
|
124194
|
+
queue.dequeue().reject(abortError);
|
|
124195
|
+
}
|
|
124196
|
+
}
|
|
124197
|
+
},
|
|
124198
|
+
concurrency: {
|
|
124199
|
+
get: () => concurrency,
|
|
124200
|
+
set(newConcurrency) {
|
|
124201
|
+
validateConcurrency(newConcurrency);
|
|
124202
|
+
concurrency = newConcurrency;
|
|
124203
|
+
queueMicrotask(() => {
|
|
124204
|
+
while (activeCount < concurrency && queue.size > 0) {
|
|
124205
|
+
resumeNext();
|
|
124206
|
+
}
|
|
124207
|
+
});
|
|
124208
|
+
}
|
|
124209
|
+
},
|
|
124210
|
+
map: {
|
|
124211
|
+
async value(iterable, function_) {
|
|
124212
|
+
const promises = Array.from(iterable, (value, index) => this(function_, value, index));
|
|
124213
|
+
return Promise.all(promises);
|
|
124214
|
+
}
|
|
124215
|
+
}
|
|
124216
|
+
});
|
|
124217
|
+
return generator;
|
|
124218
|
+
}
|
|
124219
|
+
function validateConcurrency(concurrency) {
|
|
124220
|
+
if (!((Number.isInteger(concurrency) || concurrency === Number.POSITIVE_INFINITY) && concurrency > 0)) {
|
|
124221
|
+
throw new TypeError("Expected `concurrency` to be a number from 1 and up");
|
|
124222
|
+
}
|
|
124223
|
+
}
|
|
124224
|
+
var init_p_limit = __esm({
|
|
124225
|
+
"node_modules/.pnpm/p-limit@7.3.0/node_modules/p-limit/index.js"() {
|
|
124226
|
+
init_yocto_queue();
|
|
124227
|
+
}
|
|
124228
|
+
});
|
|
124229
|
+
|
|
124051
124230
|
// apps/server/src/services/analysis-registry.ts
|
|
124052
124231
|
function registerAnalysis(repoId, analysisId) {
|
|
124053
124232
|
const existing = activeAnalyses2.get(repoId);
|
|
@@ -126381,6 +126560,7 @@ var init_cli_provider = __esm({
|
|
|
126381
126560
|
"apps/server/src/services/llm/cli-provider.ts"() {
|
|
126382
126561
|
"use strict";
|
|
126383
126562
|
init_logger();
|
|
126563
|
+
init_p_limit();
|
|
126384
126564
|
init_analysis_registry();
|
|
126385
126565
|
init_esm4();
|
|
126386
126566
|
init_config();
|
|
@@ -126388,6 +126568,7 @@ var init_cli_provider = __esm({
|
|
|
126388
126568
|
init_schemas();
|
|
126389
126569
|
BaseCLIProvider = class {
|
|
126390
126570
|
maxRetries = config.claudeCodeMaxRetries ?? 2;
|
|
126571
|
+
limit = pLimit(config.claudeCodeMaxConcurrency);
|
|
126391
126572
|
debugDir = null;
|
|
126392
126573
|
callCounter = 0;
|
|
126393
126574
|
_analysisId = null;
|
|
@@ -126572,35 +126753,42 @@ var init_cli_provider = __esm({
|
|
|
126572
126753
|
}
|
|
126573
126754
|
/** Spawn CLI with retry on parse/validation failure. */
|
|
126574
126755
|
async spawnAndParse(prompt, schema, opts) {
|
|
126575
|
-
|
|
126576
|
-
|
|
126577
|
-
|
|
126578
|
-
|
|
126579
|
-
|
|
126580
|
-
|
|
126581
|
-
|
|
126582
|
-
|
|
126583
|
-
|
|
126584
|
-
|
|
126585
|
-
|
|
126586
|
-
|
|
126587
|
-
|
|
126756
|
+
return this.limit(async () => {
|
|
126757
|
+
if (this._abortSignal?.aborted) {
|
|
126758
|
+
throw this._abortSignal.reason ?? new DOMException("Analysis cancelled", "AbortError");
|
|
126759
|
+
}
|
|
126760
|
+
opts?.onStart?.();
|
|
126761
|
+
const jsonSchemaStr = this.toJsonSchema(schema);
|
|
126762
|
+
const label = opts?.label ?? "call";
|
|
126763
|
+
let lastError = null;
|
|
126764
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
126765
|
+
try {
|
|
126766
|
+
const raw = await this.spawnCLI(prompt, jsonSchemaStr, opts);
|
|
126767
|
+
this.dumpDebug(label, prompt, raw, jsonSchemaStr);
|
|
126768
|
+
return this.parseAndValidate(raw, schema);
|
|
126769
|
+
} catch (err) {
|
|
126770
|
+
lastError = err;
|
|
126771
|
+
if (this._abortSignal?.aborted) throw lastError;
|
|
126772
|
+
if (attempt < this.maxRetries) {
|
|
126773
|
+
log.warn(`[CLI] Attempt ${attempt + 1} failed, retrying... (${lastError.message})`);
|
|
126774
|
+
}
|
|
126588
126775
|
}
|
|
126589
126776
|
}
|
|
126590
|
-
|
|
126591
|
-
|
|
126777
|
+
throw lastError;
|
|
126778
|
+
});
|
|
126592
126779
|
}
|
|
126593
126780
|
// ---------------------------------------------------------------------------
|
|
126594
126781
|
// LLMProvider implementation
|
|
126595
126782
|
// ---------------------------------------------------------------------------
|
|
126596
|
-
async generateServiceViolations(context) {
|
|
126783
|
+
async generateServiceViolations(context, opts) {
|
|
126597
126784
|
const { vars, idMap } = buildServiceTemplateVars(context);
|
|
126598
126785
|
const prompt = getPrompt("violations-service", vars);
|
|
126599
126786
|
log.info("[CLI] Service violations call starting...");
|
|
126600
126787
|
const t0 = Date.now();
|
|
126601
126788
|
const { data: object, usage: cliUsage } = await this.spawnAndParse(prompt, ServiceViolationOutputSchema, {
|
|
126602
126789
|
extraArgs: ["--tools", ""],
|
|
126603
|
-
label: "service"
|
|
126790
|
+
label: "service",
|
|
126791
|
+
onStart: opts?.onStart
|
|
126604
126792
|
});
|
|
126605
126793
|
const dur = Date.now() - t0;
|
|
126606
126794
|
log.info(`[CLI] Service violations call done in ${dur}ms \u2014 ${object.violations.length} violations`);
|
|
@@ -126623,14 +126811,15 @@ var init_cli_provider = __esm({
|
|
|
126623
126811
|
}))
|
|
126624
126812
|
};
|
|
126625
126813
|
}
|
|
126626
|
-
async generateDatabaseViolations(context) {
|
|
126814
|
+
async generateDatabaseViolations(context, opts) {
|
|
126627
126815
|
const { vars, idMap } = buildDatabaseTemplateVars(context);
|
|
126628
126816
|
const prompt = getPrompt("violations-database", vars);
|
|
126629
126817
|
log.info("[CLI] Database violations call starting...");
|
|
126630
126818
|
const t0 = Date.now();
|
|
126631
126819
|
const { data: object, usage: cliUsage } = await this.spawnAndParse(prompt, DatabaseViolationOutputSchema, {
|
|
126632
126820
|
extraArgs: ["--tools", ""],
|
|
126633
|
-
label: "database"
|
|
126821
|
+
label: "database",
|
|
126822
|
+
onStart: opts?.onStart
|
|
126634
126823
|
});
|
|
126635
126824
|
const dur = Date.now() - t0;
|
|
126636
126825
|
log.info(`[CLI] Database violations call done in ${dur}ms \u2014 ${object.violations.length} violations`);
|
|
@@ -126650,7 +126839,7 @@ var init_cli_provider = __esm({
|
|
|
126650
126839
|
}))
|
|
126651
126840
|
};
|
|
126652
126841
|
}
|
|
126653
|
-
async generateModuleViolations(context) {
|
|
126842
|
+
async generateModuleViolations(context, opts) {
|
|
126654
126843
|
const { vars, idMap } = buildModuleTemplateVars(context);
|
|
126655
126844
|
const prompt = getPrompt("violations-module", vars);
|
|
126656
126845
|
const moduleIdToServiceId = new Map(
|
|
@@ -126662,7 +126851,8 @@ var init_cli_provider = __esm({
|
|
|
126662
126851
|
const { data: object, usage: cliUsage } = await this.spawnAndParse(prompt, ModuleViolationOutputSchema, {
|
|
126663
126852
|
extraArgs: ["--tools", ""],
|
|
126664
126853
|
label: "module",
|
|
126665
|
-
timeoutMs: moduleTimeoutMs
|
|
126854
|
+
timeoutMs: moduleTimeoutMs,
|
|
126855
|
+
onStart: opts?.onStart
|
|
126666
126856
|
});
|
|
126667
126857
|
const dur = Date.now() - t0;
|
|
126668
126858
|
log.info(`[CLI] Module violations call done in ${dur}ms \u2014 ${object.violations.length} violations`);
|
|
@@ -126689,15 +126879,23 @@ var init_cli_provider = __esm({
|
|
|
126689
126879
|
}
|
|
126690
126880
|
async generateAllViolations(contexts) {
|
|
126691
126881
|
const onStepComplete = contexts.onStepComplete;
|
|
126882
|
+
const onCallStart = contexts.onCallStart;
|
|
126883
|
+
const onCallDone = contexts.onCallDone;
|
|
126692
126884
|
const promises = [];
|
|
126693
126885
|
if (contexts.service) {
|
|
126694
|
-
promises.push(["service", this.generateServiceViolations(contexts.service
|
|
126886
|
+
promises.push(["service", this.generateServiceViolations(contexts.service, {
|
|
126887
|
+
onStart: () => onCallStart?.("service")
|
|
126888
|
+
})]);
|
|
126695
126889
|
}
|
|
126696
126890
|
if (contexts.database) {
|
|
126697
|
-
promises.push(["database", this.generateDatabaseViolations(contexts.database
|
|
126891
|
+
promises.push(["database", this.generateDatabaseViolations(contexts.database, {
|
|
126892
|
+
onStart: () => onCallStart?.("database")
|
|
126893
|
+
})]);
|
|
126698
126894
|
}
|
|
126699
126895
|
if (contexts.module) {
|
|
126700
|
-
promises.push(["module", this.generateModuleViolations(contexts.module
|
|
126896
|
+
promises.push(["module", this.generateModuleViolations(contexts.module, {
|
|
126897
|
+
onStart: () => onCallStart?.("module")
|
|
126898
|
+
})]);
|
|
126701
126899
|
}
|
|
126702
126900
|
const stepLabels = {
|
|
126703
126901
|
service: "Service architecture checks done",
|
|
@@ -126705,10 +126903,17 @@ var init_cli_provider = __esm({
|
|
|
126705
126903
|
module: "Module & function checks done"
|
|
126706
126904
|
};
|
|
126707
126905
|
const settled = await Promise.allSettled(promises.map(
|
|
126708
|
-
([key, p]) => p.then(
|
|
126709
|
-
|
|
126710
|
-
|
|
126711
|
-
|
|
126906
|
+
([key, p]) => p.then(
|
|
126907
|
+
(v) => {
|
|
126908
|
+
onStepComplete?.(stepLabels[key] || `${key} done`);
|
|
126909
|
+
onCallDone?.(key, true);
|
|
126910
|
+
return v;
|
|
126911
|
+
},
|
|
126912
|
+
(err) => {
|
|
126913
|
+
onCallDone?.(key, false);
|
|
126914
|
+
throw err;
|
|
126915
|
+
}
|
|
126916
|
+
)
|
|
126712
126917
|
));
|
|
126713
126918
|
const result = {};
|
|
126714
126919
|
for (let i = 0; i < promises.length; i++) {
|
|
@@ -126723,6 +126928,7 @@ var init_cli_provider = __esm({
|
|
|
126723
126928
|
return result;
|
|
126724
126929
|
}
|
|
126725
126930
|
async generateAllViolationsWithLifecycle(contexts, onStepComplete) {
|
|
126931
|
+
const onCallStart = contexts.onCallStart;
|
|
126726
126932
|
const allResolved = [];
|
|
126727
126933
|
const allNew = [];
|
|
126728
126934
|
let serviceDescriptions = [];
|
|
@@ -126739,7 +126945,8 @@ var init_cli_provider = __esm({
|
|
|
126739
126945
|
const t0 = Date.now();
|
|
126740
126946
|
const { data: object, usage: cliUsage } = await this.spawnAndParse(prompt, LifecycleServiceOutputSchema, {
|
|
126741
126947
|
extraArgs: ["--tools", ""],
|
|
126742
|
-
label: "service-lifecycle"
|
|
126948
|
+
label: "service-lifecycle",
|
|
126949
|
+
onStart: () => onCallStart?.("service")
|
|
126743
126950
|
});
|
|
126744
126951
|
const dur = Date.now() - t0;
|
|
126745
126952
|
log.info(`[CLI] Lifecycle service call done in ${dur}ms \u2014 resolved: ${object.resolvedViolationIds.length}, new: ${object.newViolations.length}`);
|
|
@@ -126747,7 +126954,9 @@ var init_cli_provider = __esm({
|
|
|
126747
126954
|
return object;
|
|
126748
126955
|
})()]);
|
|
126749
126956
|
} else {
|
|
126750
|
-
promises.push(["service-normal", this.generateServiceViolations(ctx
|
|
126957
|
+
promises.push(["service-normal", this.generateServiceViolations(ctx, {
|
|
126958
|
+
onStart: () => onCallStart?.("service")
|
|
126959
|
+
})]);
|
|
126751
126960
|
}
|
|
126752
126961
|
}
|
|
126753
126962
|
if (contexts.database) {
|
|
@@ -126761,7 +126970,8 @@ var init_cli_provider = __esm({
|
|
|
126761
126970
|
const t0 = Date.now();
|
|
126762
126971
|
const { data: object, usage: cliUsage } = await this.spawnAndParse(prompt, DiffViolationOutputSchema, {
|
|
126763
126972
|
extraArgs: ["--tools", ""],
|
|
126764
|
-
label: "database-lifecycle"
|
|
126973
|
+
label: "database-lifecycle",
|
|
126974
|
+
onStart: () => onCallStart?.("database")
|
|
126765
126975
|
});
|
|
126766
126976
|
const dur = Date.now() - t0;
|
|
126767
126977
|
log.info(`[CLI] Lifecycle database call done in ${dur}ms \u2014 resolved: ${object.resolvedViolationIds.length}, new: ${object.newViolations.length}`);
|
|
@@ -126769,7 +126979,9 @@ var init_cli_provider = __esm({
|
|
|
126769
126979
|
return object;
|
|
126770
126980
|
})()]);
|
|
126771
126981
|
} else {
|
|
126772
|
-
promises.push(["database-normal", this.generateDatabaseViolations(ctx
|
|
126982
|
+
promises.push(["database-normal", this.generateDatabaseViolations(ctx, {
|
|
126983
|
+
onStart: () => onCallStart?.("database")
|
|
126984
|
+
})]);
|
|
126773
126985
|
}
|
|
126774
126986
|
}
|
|
126775
126987
|
if (contexts.module) {
|
|
@@ -126786,7 +126998,8 @@ var init_cli_provider = __esm({
|
|
|
126786
126998
|
const t0 = Date.now();
|
|
126787
126999
|
const { data: object, usage: cliUsage } = await this.spawnAndParse(prompt, DiffViolationOutputSchema, {
|
|
126788
127000
|
extraArgs: ["--tools", ""],
|
|
126789
|
-
label: "module-lifecycle"
|
|
127001
|
+
label: "module-lifecycle",
|
|
127002
|
+
onStart: () => onCallStart?.("module")
|
|
126790
127003
|
});
|
|
126791
127004
|
const dur = Date.now() - t0;
|
|
126792
127005
|
log.info(`[CLI] Lifecycle module call done in ${dur}ms \u2014 resolved: ${object.resolvedViolationIds.length}, new: ${object.newViolations.length}`);
|
|
@@ -126807,7 +127020,9 @@ var init_cli_provider = __esm({
|
|
|
126807
127020
|
};
|
|
126808
127021
|
})()]);
|
|
126809
127022
|
} else {
|
|
126810
|
-
promises.push(["module-normal", this.generateModuleViolations(ctx
|
|
127023
|
+
promises.push(["module-normal", this.generateModuleViolations(ctx, {
|
|
127024
|
+
onStart: () => onCallStart?.("module")
|
|
127025
|
+
})]);
|
|
126811
127026
|
}
|
|
126812
127027
|
}
|
|
126813
127028
|
const stepLabels = {
|
|
@@ -126818,11 +127033,20 @@ var init_cli_provider = __esm({
|
|
|
126818
127033
|
module: "Module checks done",
|
|
126819
127034
|
"module-normal": "Module checks done"
|
|
126820
127035
|
};
|
|
127036
|
+
const baseKey = (key) => key.replace("-normal", "");
|
|
127037
|
+
const onCallDone = contexts.onCallDone;
|
|
126821
127038
|
const settled = await Promise.allSettled(promises.map(
|
|
126822
|
-
([key, p]) => p.then(
|
|
126823
|
-
|
|
126824
|
-
|
|
126825
|
-
|
|
127039
|
+
([key, p]) => p.then(
|
|
127040
|
+
(v) => {
|
|
127041
|
+
onStepComplete?.(stepLabels[key] || `${key} done`);
|
|
127042
|
+
onCallDone?.(baseKey(key), true);
|
|
127043
|
+
return v;
|
|
127044
|
+
},
|
|
127045
|
+
(err) => {
|
|
127046
|
+
onCallDone?.(baseKey(key), false);
|
|
127047
|
+
throw err;
|
|
127048
|
+
}
|
|
127049
|
+
)
|
|
126826
127050
|
));
|
|
126827
127051
|
for (let i = 0; i < promises.length; i++) {
|
|
126828
127052
|
const [key] = promises[i];
|
|
@@ -126914,7 +127138,7 @@ var init_cli_provider = __esm({
|
|
|
126914
127138
|
}
|
|
126915
127139
|
return { resolvedViolationIds: allResolved, newViolations: allNew, serviceDescriptions };
|
|
126916
127140
|
}
|
|
126917
|
-
async generateCodeViolations(context) {
|
|
127141
|
+
async generateCodeViolations(context, opts) {
|
|
126918
127142
|
const hasExisting = context.existingViolations && context.existingViolations.length > 0;
|
|
126919
127143
|
let promptName;
|
|
126920
127144
|
if (context.tier === "metadata") {
|
|
@@ -126935,7 +127159,8 @@ var init_cli_provider = __esm({
|
|
|
126935
127159
|
const { data: object2, usage: cliUsage2 } = await this.spawnAndParse(prompt, CodeViolationLifecycleOutputSchema, {
|
|
126936
127160
|
extraArgs: codeExtraArgs,
|
|
126937
127161
|
label: "code-lifecycle",
|
|
126938
|
-
timeoutMs: codeTimeoutMs
|
|
127162
|
+
timeoutMs: codeTimeoutMs,
|
|
127163
|
+
onStart: opts?.onStart
|
|
126939
127164
|
});
|
|
126940
127165
|
const dur2 = Date.now() - t0;
|
|
126941
127166
|
log.info(`[CLI] Code violations call done in ${dur2}ms \u2014 new: ${object2.newViolations.length}, resolved: ${object2.resolvedViolationIds.length}, unchanged: ${object2.unchangedViolationIds.length}`);
|
|
@@ -126958,7 +127183,8 @@ var init_cli_provider = __esm({
|
|
|
126958
127183
|
const { data: object, usage: cliUsage } = await this.spawnAndParse(prompt, CodeViolationOutputSchema, {
|
|
126959
127184
|
extraArgs: codeExtraArgs,
|
|
126960
127185
|
label: "code",
|
|
126961
|
-
timeoutMs: codeTimeoutMs
|
|
127186
|
+
timeoutMs: codeTimeoutMs,
|
|
127187
|
+
onStart: opts?.onStart
|
|
126962
127188
|
});
|
|
126963
127189
|
const dur = Date.now() - t0;
|
|
126964
127190
|
log.info(`[CLI] Code violations call done in ${dur}ms \u2014 ${object.violations.length} violations`);
|
|
@@ -146488,7 +146714,7 @@ function routeContext(rules, fileAnalyses, fileContents) {
|
|
|
146488
146714
|
|
|
146489
146715
|
// apps/server/src/services/violation.service.ts
|
|
146490
146716
|
init_provider();
|
|
146491
|
-
async function generateViolations(input, onProgress, externalProvider) {
|
|
146717
|
+
async function generateViolations(input, onProgress, externalProvider, onCallStart, onCallDone) {
|
|
146492
146718
|
const provider = externalProvider ?? createLLMProvider();
|
|
146493
146719
|
const archRules = (input.llmRules || []).filter((r) => r.category === "service");
|
|
146494
146720
|
const dbRules = (input.llmRules || []).filter((r) => r.category === "database");
|
|
@@ -146532,7 +146758,9 @@ async function generateViolations(input, onProgress, externalProvider) {
|
|
|
146532
146758
|
service: serviceContext,
|
|
146533
146759
|
database: dbContext,
|
|
146534
146760
|
module: moduleContext,
|
|
146535
|
-
onStepComplete: onProgress
|
|
146761
|
+
onStepComplete: onProgress,
|
|
146762
|
+
onCallStart,
|
|
146763
|
+
onCallDone
|
|
146536
146764
|
});
|
|
146537
146765
|
const allViolations = [];
|
|
146538
146766
|
let serviceDescriptions = [];
|
|
@@ -146569,7 +146797,7 @@ async function generateViolations(input, onProgress, externalProvider) {
|
|
|
146569
146797
|
}
|
|
146570
146798
|
return { violations: allViolations, serviceDescriptions };
|
|
146571
146799
|
}
|
|
146572
|
-
async function generateViolationsWithLifecycle(input, onProgress, externalProvider) {
|
|
146800
|
+
async function generateViolationsWithLifecycle(input, onProgress, externalProvider, onCallStart, onCallDone) {
|
|
146573
146801
|
const provider = externalProvider ?? createLLMProvider();
|
|
146574
146802
|
const archRules = (input.llmRules || []).filter((r) => r.category === "service");
|
|
146575
146803
|
const dbRules = (input.llmRules || []).filter((r) => r.category === "database");
|
|
@@ -146610,7 +146838,9 @@ async function generateViolationsWithLifecycle(input, onProgress, externalProvid
|
|
|
146610
146838
|
const result = await provider.generateAllViolationsWithLifecycle({
|
|
146611
146839
|
service: serviceContext,
|
|
146612
146840
|
database: dbContext,
|
|
146613
|
-
module: moduleContext
|
|
146841
|
+
module: moduleContext,
|
|
146842
|
+
onCallStart,
|
|
146843
|
+
onCallDone
|
|
146614
146844
|
}, (step) => {
|
|
146615
146845
|
onProgress?.(step);
|
|
146616
146846
|
});
|
|
@@ -146867,6 +147097,44 @@ init_logger();
|
|
|
146867
147097
|
function throwIfAborted(signal) {
|
|
146868
147098
|
if (signal?.aborted) throw new DOMException("Analysis cancelled", "AbortError");
|
|
146869
147099
|
}
|
|
147100
|
+
function formatElapsed(ms) {
|
|
147101
|
+
const totalSec = Math.floor(ms / 1e3);
|
|
147102
|
+
const min = Math.floor(totalSec / 60);
|
|
147103
|
+
const sec = totalSec % 60;
|
|
147104
|
+
return min === 0 ? `${sec}s` : `${min}m ${sec}s`;
|
|
147105
|
+
}
|
|
147106
|
+
function renderLlmDetail(s) {
|
|
147107
|
+
const parts = [];
|
|
147108
|
+
if (s.detCount > 0) parts.push(`${s.detCount} det`);
|
|
147109
|
+
parts.push(`LLM ${s.done}/${s.total}`);
|
|
147110
|
+
if (s.running > 0) parts.push(`${s.running} running`);
|
|
147111
|
+
if (s.elapsedMs >= 1e3) parts.push(formatElapsed(s.elapsedMs));
|
|
147112
|
+
return parts.join(" \xB7 ");
|
|
147113
|
+
}
|
|
147114
|
+
function createLlmTracker(tracker, domain, detCount, total) {
|
|
147115
|
+
let done = 0;
|
|
147116
|
+
let running = 0;
|
|
147117
|
+
const t0 = Date.now();
|
|
147118
|
+
const render = () => tracker?.detail(domain, renderLlmDetail({
|
|
147119
|
+
detCount,
|
|
147120
|
+
total,
|
|
147121
|
+
done,
|
|
147122
|
+
running,
|
|
147123
|
+
elapsedMs: Date.now() - t0
|
|
147124
|
+
}));
|
|
147125
|
+
return {
|
|
147126
|
+
initialDetail: renderLlmDetail({ detCount, total, done: 0, running: 0, elapsedMs: 0 }),
|
|
147127
|
+
onCallStart: () => {
|
|
147128
|
+
running++;
|
|
147129
|
+
render();
|
|
147130
|
+
},
|
|
147131
|
+
onCallDone: (started) => {
|
|
147132
|
+
if (started) running--;
|
|
147133
|
+
done++;
|
|
147134
|
+
render();
|
|
147135
|
+
}
|
|
147136
|
+
};
|
|
147137
|
+
}
|
|
146870
147138
|
function getDetComparisonKey(v) {
|
|
146871
147139
|
return `${v.ruleKey}::${v.serviceName}::${v.moduleName || ""}::${v.methodName || ""}::${v.title}`;
|
|
146872
147140
|
}
|
|
@@ -147210,11 +147478,6 @@ async function runViolationPipeline(input) {
|
|
|
147210
147478
|
const allNewLlmItems = [];
|
|
147211
147479
|
const allResolvedLlmIds = [];
|
|
147212
147480
|
const hasArchLlm = enableLlmRules !== false && !llmSkipped;
|
|
147213
|
-
if (hasArchLlm) tracker?.start("architecture", "Running LLM analysis...");
|
|
147214
|
-
for (const [domain] of domainCodeBatches) {
|
|
147215
|
-
const detCount = violationsByDomain.get(domain) ?? 0;
|
|
147216
|
-
tracker?.start(domain, detCount > 0 ? `${detCount} det, running LLM...` : "Running LLM...");
|
|
147217
|
-
}
|
|
147218
147481
|
const previousDetForComparison = previousDetViolations.map((v) => ({
|
|
147219
147482
|
ruleKey: v.ruleKey,
|
|
147220
147483
|
serviceName: v.targetServiceName || "",
|
|
@@ -147439,14 +147702,43 @@ async function runViolationPipeline(input) {
|
|
|
147439
147702
|
existingDatabaseViolations: void 0,
|
|
147440
147703
|
existingModuleViolations: hasLlmOnlyExistingViolations ? existingModuleViolations : void 0
|
|
147441
147704
|
};
|
|
147705
|
+
const llmTrackers = /* @__PURE__ */ new Map();
|
|
147706
|
+
for (const [domain, batches] of domainCodeBatches) {
|
|
147707
|
+
const detCount = violationsByDomain.get(domain) ?? 0;
|
|
147708
|
+
const ll = createLlmTracker(tracker, domain, detCount, batches.length);
|
|
147709
|
+
llmTrackers.set(domain, ll);
|
|
147710
|
+
tracker?.start(domain, ll.initialDetail);
|
|
147711
|
+
}
|
|
147712
|
+
if (dbSchemaContext && !llmSkipped && !domainCodeBatches.has("database")) {
|
|
147713
|
+
const detCount = violationsByDomain.get("database") ?? 0;
|
|
147714
|
+
const ll = createLlmTracker(tracker, "database", detCount, 1);
|
|
147715
|
+
llmTrackers.set("database", ll);
|
|
147716
|
+
tracker?.start("database", ll.initialDetail);
|
|
147717
|
+
}
|
|
147718
|
+
if (hasArchLlm) {
|
|
147719
|
+
const detCount = violationsByDomain.get("architecture") ?? 0;
|
|
147720
|
+
const archTotal2 = 1 + (violationModules && violationModules.length > 0 ? 1 : 0);
|
|
147721
|
+
const ll = createLlmTracker(tracker, "architecture", detCount, archTotal2);
|
|
147722
|
+
llmTrackers.set("architecture", ll);
|
|
147723
|
+
tracker?.start("architecture", ll.initialDetail);
|
|
147724
|
+
}
|
|
147442
147725
|
const domainLlmPromises = [];
|
|
147443
147726
|
for (const [domain, batches] of domainCodeBatches) {
|
|
147444
147727
|
domainLlmPromises.push((async () => {
|
|
147445
147728
|
const detCount = violationsByDomain.get(domain) ?? 0;
|
|
147446
147729
|
log.info(`[LLM] ${domain}: starting (${batches.length} code batches)`);
|
|
147447
147730
|
const t0 = Date.now();
|
|
147731
|
+
const ll = llmTrackers.get(domain);
|
|
147448
147732
|
const codeResults = await Promise.allSettled(
|
|
147449
|
-
batches.map((b) =>
|
|
147733
|
+
batches.map((b) => {
|
|
147734
|
+
let started = false;
|
|
147735
|
+
return provider.generateCodeViolations(b, {
|
|
147736
|
+
onStart: () => {
|
|
147737
|
+
started = true;
|
|
147738
|
+
ll.onCallStart();
|
|
147739
|
+
}
|
|
147740
|
+
}).finally(() => ll.onCallDone(started));
|
|
147741
|
+
})
|
|
147450
147742
|
);
|
|
147451
147743
|
const rawViolations = [];
|
|
147452
147744
|
const resolvedIds = [];
|
|
@@ -147471,15 +147763,19 @@ async function runViolationPipeline(input) {
|
|
|
147471
147763
|
}
|
|
147472
147764
|
let dbSchemaViolations = [];
|
|
147473
147765
|
if (dbSchemaContext && !llmSkipped) {
|
|
147474
|
-
|
|
147475
|
-
const detCount = violationsByDomain.get("database") ?? 0;
|
|
147476
|
-
tracker?.start("database", detCount > 0 ? `${detCount} det, running LLM...` : "Running LLM...");
|
|
147477
|
-
}
|
|
147766
|
+
const schemaLl = domainCodeBatches.has("database") ? void 0 : llmTrackers.get("database");
|
|
147478
147767
|
domainLlmPromises.push((async () => {
|
|
147479
147768
|
log.info(`[LLM] database-schema: starting`);
|
|
147480
147769
|
const t0 = Date.now();
|
|
147770
|
+
let started = false;
|
|
147481
147771
|
try {
|
|
147482
|
-
const dbResult = await provider.generateDatabaseViolations(dbSchemaContext
|
|
147772
|
+
const dbResult = await provider.generateDatabaseViolations(dbSchemaContext, {
|
|
147773
|
+
onStart: () => {
|
|
147774
|
+
started = true;
|
|
147775
|
+
schemaLl?.onCallStart();
|
|
147776
|
+
}
|
|
147777
|
+
});
|
|
147778
|
+
schemaLl?.onCallDone(started);
|
|
147483
147779
|
const dur = Date.now() - t0;
|
|
147484
147780
|
log.info(`[LLM] database-schema: done in ${dur}ms \u2014 ${dbResult.violations.length} violations`);
|
|
147485
147781
|
for (const v of dbResult.violations) {
|
|
@@ -147519,6 +147815,7 @@ async function runViolationPipeline(input) {
|
|
|
147519
147815
|
}
|
|
147520
147816
|
return { domain: "database-schema", violations: [], resolvedIds: [], unchangedIds: [] };
|
|
147521
147817
|
} catch (err) {
|
|
147818
|
+
schemaLl?.onCallDone(started);
|
|
147522
147819
|
const dur = Date.now() - t0;
|
|
147523
147820
|
log.warn(`[LLM] database-schema: failed in ${dur}ms \u2014 ${err instanceof Error ? err.message : String(err)}`);
|
|
147524
147821
|
if (!domainCodeBatches.has("database")) tracker?.error("database", `Schema LLM failed`);
|
|
@@ -147530,11 +147827,22 @@ async function runViolationPipeline(input) {
|
|
|
147530
147827
|
onProgress?.({ step: "analyzing", percent: 86, detail: "Analyzing architecture & modules..." });
|
|
147531
147828
|
const llmRulePromise = (async () => {
|
|
147532
147829
|
if (enableLlmRules === false || llmSkipped) return;
|
|
147830
|
+
const archLl = llmTrackers.get("architecture");
|
|
147831
|
+
const archStarted = /* @__PURE__ */ new Set();
|
|
147832
|
+
const archOnCallStart = (key) => {
|
|
147833
|
+
archStarted.add(key);
|
|
147834
|
+
archLl?.onCallStart();
|
|
147835
|
+
};
|
|
147836
|
+
const archOnCallDone = (key) => {
|
|
147837
|
+
archLl?.onCallDone(archStarted.has(key));
|
|
147838
|
+
};
|
|
147533
147839
|
if (hasLlmOnlyExistingViolations) {
|
|
147534
147840
|
const archResult = await generateViolationsWithLifecycle(
|
|
147535
147841
|
violationInput,
|
|
147536
|
-
|
|
147537
|
-
provider
|
|
147842
|
+
void 0,
|
|
147843
|
+
provider,
|
|
147844
|
+
archOnCallStart,
|
|
147845
|
+
archOnCallDone
|
|
147538
147846
|
);
|
|
147539
147847
|
serviceDescriptions = archResult.serviceDescriptions;
|
|
147540
147848
|
allResolvedLlmIds.push(...archResult.resolvedViolationIds);
|
|
@@ -147563,8 +147871,10 @@ async function runViolationPipeline(input) {
|
|
|
147563
147871
|
} else {
|
|
147564
147872
|
const archResult = await generateViolations(
|
|
147565
147873
|
violationInput,
|
|
147566
|
-
|
|
147567
|
-
provider
|
|
147874
|
+
void 0,
|
|
147875
|
+
provider,
|
|
147876
|
+
archOnCallStart,
|
|
147877
|
+
archOnCallDone
|
|
147568
147878
|
);
|
|
147569
147879
|
serviceDescriptions = archResult.serviceDescriptions;
|
|
147570
147880
|
for (const v of archResult.violations) {
|
|
@@ -148282,8 +148592,13 @@ function makeDenormalizer(graph) {
|
|
|
148282
148592
|
}
|
|
148283
148593
|
|
|
148284
148594
|
// apps/server/src/commands/analyze-in-process.ts
|
|
148595
|
+
init_config();
|
|
148596
|
+
init_logger();
|
|
148285
148597
|
async function analyzeInProcess(project, options = {}) {
|
|
148286
148598
|
const startedAt = Date.now();
|
|
148599
|
+
log.info(
|
|
148600
|
+
`[LLM] Provider: claude-code, model: ${config.claudeCodeModel || "default"}, maxConcurrency: ${config.claudeCodeMaxConcurrency}`
|
|
148601
|
+
);
|
|
148287
148602
|
const core = await analyzeCore(project, { ...options, mode: "full" });
|
|
148288
148603
|
return persistFullAnalysis(project, core, startedAt);
|
|
148289
148604
|
}
|
|
@@ -154034,7 +154349,6 @@ async function main() {
|
|
|
154034
154349
|
if (wipeLegacyPostgresData()) {
|
|
154035
154350
|
log.info("[Storage] Legacy Postgres data wiped. Re-analyze to repopulate.");
|
|
154036
154351
|
}
|
|
154037
|
-
log.info(`[LLM] Provider: claude-code, model: ${config.claudeCodeModel || "default"}`);
|
|
154038
154352
|
const app = (0, import_express10.default)();
|
|
154039
154353
|
const httpServer = createServer(app);
|
|
154040
154354
|
setupSocket(httpServer);
|