@novastorm-ai/cli 0.1.1 → 0.1.3
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/dist/bin/nova.js +3 -3
- package/dist/{chunk-KKTDQOQX.js → chunk-7K55GHF5.js} +126 -33
- package/dist/{chunk-QKD6A4EK.js → chunk-DQXTUNZA.js} +1 -1
- package/dist/{chunk-4EPZMOY6.js → chunk-HVDG2MLB.js} +77 -29
- package/dist/{dist-EMATXD3M.js → dist-NNJKY4T4.js} +3 -1
- package/dist/index.d.ts +3 -2
- package/dist/index.js +3 -3
- package/dist/{package-XCCIIMWT.js → package-BSAVJZ7S.js} +1 -1
- package/dist/{setup-L5TRND4P.js → setup-VJMYSGJI.js} +2 -2
- package/package.json +1 -1
package/dist/bin/nova.js
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
3
|
run
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-HVDG2MLB.js";
|
|
5
5
|
import "../chunk-KE7XWO5N.js";
|
|
6
|
-
import "../chunk-
|
|
7
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-DQXTUNZA.js";
|
|
7
|
+
import "../chunk-7K55GHF5.js";
|
|
8
8
|
import "../chunk-3RG5ZIWI.js";
|
|
9
9
|
|
|
10
10
|
// bin/nova.ts
|
|
@@ -331,6 +331,30 @@ var GitManager = class {
|
|
|
331
331
|
}
|
|
332
332
|
}
|
|
333
333
|
};
|
|
334
|
+
var CommitQueue = class {
|
|
335
|
+
constructor(gitManager) {
|
|
336
|
+
this.gitManager = gitManager;
|
|
337
|
+
}
|
|
338
|
+
queue = Promise.resolve("");
|
|
339
|
+
/**
|
|
340
|
+
* Enqueues a commit operation. The commit will execute after all
|
|
341
|
+
* previously enqueued commits have completed.
|
|
342
|
+
*
|
|
343
|
+
* @param message - commit message
|
|
344
|
+
* @param files - relative file paths to stage (passed to gitManager.commit)
|
|
345
|
+
* @returns the commit hash from gitManager.commit
|
|
346
|
+
*/
|
|
347
|
+
enqueue(message, files) {
|
|
348
|
+
this.queue = this.queue.then(
|
|
349
|
+
() => this.gitManager.commit(message, files),
|
|
350
|
+
(err) => {
|
|
351
|
+
console.warn("[Nova] Previous commit failed:", err instanceof Error ? err.message : err);
|
|
352
|
+
return this.gitManager.commit(message, files);
|
|
353
|
+
}
|
|
354
|
+
);
|
|
355
|
+
return this.queue;
|
|
356
|
+
}
|
|
357
|
+
};
|
|
334
358
|
var DEFAULT_AGENT_PROMPTS = {
|
|
335
359
|
developer: `You are a code generation tool. You output ONLY code. No explanations. No questions. No descriptions.
|
|
336
360
|
|
|
@@ -3161,10 +3185,20 @@ var ProjectIndexer = class {
|
|
|
3161
3185
|
const dependencies = /* @__PURE__ */ new Map();
|
|
3162
3186
|
const fileContexts = /* @__PURE__ */ new Map();
|
|
3163
3187
|
const models = [];
|
|
3164
|
-
|
|
3188
|
+
const BATCH_SIZE2 = 50;
|
|
3189
|
+
const fileContents = /* @__PURE__ */ new Map();
|
|
3190
|
+
for (let i = 0; i < scannableFiles.length; i += BATCH_SIZE2) {
|
|
3191
|
+
const batch = scannableFiles.slice(i, i + BATCH_SIZE2);
|
|
3192
|
+
const results = await Promise.all(batch.map(async (absPath) => {
|
|
3193
|
+
const content = await this.readFileSafe(absPath);
|
|
3194
|
+
return [absPath, content];
|
|
3195
|
+
}));
|
|
3196
|
+
for (const [absPath, content] of results) {
|
|
3197
|
+
if (content) fileContents.set(absPath, content);
|
|
3198
|
+
}
|
|
3199
|
+
}
|
|
3200
|
+
for (const [absPath, content] of fileContents) {
|
|
3165
3201
|
const rel = this.toPosix(relative4(projectPath, absPath));
|
|
3166
|
-
const content = await this.readFileSafe(absPath);
|
|
3167
|
-
if (!content) continue;
|
|
3168
3202
|
const imports = this.extractImports(content);
|
|
3169
3203
|
const exports = this.extractExports(content);
|
|
3170
3204
|
const type = this.classifyFile(rel, components, endpoints);
|
|
@@ -3187,18 +3221,20 @@ var ProjectIndexer = class {
|
|
|
3187
3221
|
});
|
|
3188
3222
|
this.extractModels(content, rel, models);
|
|
3189
3223
|
}
|
|
3224
|
+
const TYPE_DEF_REGEX = /export\s+(?:interface|type)\s+\w+[^}]*}/g;
|
|
3225
|
+
const typeDefsCache = /* @__PURE__ */ new Map();
|
|
3226
|
+
for (const [filePath, ctx] of fileContexts) {
|
|
3227
|
+
const matches = ctx.content.match(TYPE_DEF_REGEX);
|
|
3228
|
+
if (matches) typeDefsCache.set(filePath, matches);
|
|
3229
|
+
}
|
|
3190
3230
|
for (const [filePath, ctx] of fileContexts) {
|
|
3191
3231
|
const node = dependencies.get(filePath);
|
|
3192
3232
|
if (!node) continue;
|
|
3193
3233
|
const importedTypes = [];
|
|
3194
3234
|
for (const imp of node.imports) {
|
|
3195
|
-
const
|
|
3196
|
-
if (
|
|
3197
|
-
|
|
3198
|
-
/export\s+(?:interface|type)\s+\w+[^}]*}/g
|
|
3199
|
-
);
|
|
3200
|
-
if (typeMatches) {
|
|
3201
|
-
importedTypes.push(...typeMatches);
|
|
3235
|
+
const cached = typeDefsCache.get(imp);
|
|
3236
|
+
if (cached) {
|
|
3237
|
+
importedTypes.push(...cached);
|
|
3202
3238
|
}
|
|
3203
3239
|
}
|
|
3204
3240
|
ctx.importedTypes = importedTypes.join("\n");
|
|
@@ -4708,21 +4744,32 @@ var CodeValidator = class {
|
|
|
4708
4744
|
constructor(projectPath) {
|
|
4709
4745
|
this.projectPath = projectPath;
|
|
4710
4746
|
}
|
|
4747
|
+
installedDepsCache = null;
|
|
4711
4748
|
/**
|
|
4712
4749
|
* Validate generated files. Returns only errors in the specified files (filters out pre-existing project errors).
|
|
4713
4750
|
*/
|
|
4714
|
-
async validateFiles(files) {
|
|
4751
|
+
async validateFiles(files, options) {
|
|
4715
4752
|
const errors = [];
|
|
4716
4753
|
const generatedPaths = new Set(files.map((f) => f.path));
|
|
4717
|
-
|
|
4718
|
-
|
|
4719
|
-
|
|
4720
|
-
const importErrors = await this.checkImports(file.path, file.content);
|
|
4721
|
-
errors.push(...importErrors);
|
|
4754
|
+
if (!options?.skipTsc) {
|
|
4755
|
+
const tscErrors = await this.runTsc(generatedPaths);
|
|
4756
|
+
errors.push(...tscErrors);
|
|
4722
4757
|
}
|
|
4723
|
-
|
|
4758
|
+
if (!options?.skipImportCheck) {
|
|
4759
|
+
await this.loadInstalledDeps();
|
|
4760
|
+
}
|
|
4761
|
+
const fileErrors = await Promise.all(files.map(async (file) => {
|
|
4762
|
+
const result = [];
|
|
4763
|
+
if (!options?.skipImportCheck) {
|
|
4764
|
+
const importErrors = this.checkImportsSync(file.path, file.content);
|
|
4765
|
+
result.push(...importErrors);
|
|
4766
|
+
}
|
|
4724
4767
|
const relErrors = this.checkRelativeImports(file.path, file.content, generatedPaths);
|
|
4725
|
-
|
|
4768
|
+
result.push(...relErrors);
|
|
4769
|
+
return result;
|
|
4770
|
+
}));
|
|
4771
|
+
for (const fileErrs of fileErrors) {
|
|
4772
|
+
errors.push(...fileErrs);
|
|
4726
4773
|
}
|
|
4727
4774
|
return errors;
|
|
4728
4775
|
}
|
|
@@ -4775,19 +4822,26 @@ var CodeValidator = class {
|
|
|
4775
4822
|
}
|
|
4776
4823
|
return errors;
|
|
4777
4824
|
}
|
|
4778
|
-
|
|
4779
|
-
|
|
4780
|
-
|
|
4825
|
+
/**
|
|
4826
|
+
* Load and cache installed dependencies from package.json.
|
|
4827
|
+
* Called once per validateFiles invocation instead of once per file.
|
|
4828
|
+
*/
|
|
4829
|
+
async loadInstalledDeps() {
|
|
4830
|
+
if (this.installedDepsCache) return;
|
|
4781
4831
|
try {
|
|
4782
4832
|
const pkgRaw = await readFile16(join17(this.projectPath, "package.json"), "utf-8");
|
|
4783
4833
|
const pkg = JSON.parse(pkgRaw);
|
|
4784
|
-
|
|
4834
|
+
this.installedDepsCache = /* @__PURE__ */ new Set([
|
|
4785
4835
|
...Object.keys(pkg.dependencies ?? {}),
|
|
4786
4836
|
...Object.keys(pkg.devDependencies ?? {})
|
|
4787
4837
|
]);
|
|
4788
4838
|
} catch {
|
|
4789
|
-
|
|
4839
|
+
this.installedDepsCache = /* @__PURE__ */ new Set();
|
|
4790
4840
|
}
|
|
4841
|
+
}
|
|
4842
|
+
checkImportsSync(filePath, content) {
|
|
4843
|
+
const errors = [];
|
|
4844
|
+
const installedDeps = this.installedDepsCache ?? /* @__PURE__ */ new Set();
|
|
4791
4845
|
const safePackages = /* @__PURE__ */ new Set([
|
|
4792
4846
|
"react",
|
|
4793
4847
|
"react-dom",
|
|
@@ -5835,14 +5889,16 @@ function extractDiff(response) {
|
|
|
5835
5889
|
return response.trim();
|
|
5836
5890
|
}
|
|
5837
5891
|
var Lane2Executor = class {
|
|
5838
|
-
constructor(projectPath, llmClient, gitManager, pathGuard) {
|
|
5892
|
+
constructor(projectPath, llmClient, gitManager, pathGuard, commitQueue) {
|
|
5839
5893
|
this.projectPath = projectPath;
|
|
5840
5894
|
this.llmClient = llmClient;
|
|
5841
5895
|
this.gitManager = gitManager;
|
|
5842
5896
|
this.pathGuard = pathGuard;
|
|
5843
5897
|
this.diffApplier = new DiffApplier();
|
|
5898
|
+
this.commitQueue = commitQueue ?? new CommitQueue(this.gitManager);
|
|
5844
5899
|
}
|
|
5845
5900
|
diffApplier;
|
|
5901
|
+
commitQueue;
|
|
5846
5902
|
async execute(task, projectMap) {
|
|
5847
5903
|
try {
|
|
5848
5904
|
const targetFile = task.files[0];
|
|
@@ -5875,7 +5931,7 @@ var Lane2Executor = class {
|
|
|
5875
5931
|
const absPath = join19(this.projectPath, targetFile);
|
|
5876
5932
|
await this.pathGuard?.check(absPath);
|
|
5877
5933
|
await this.diffApplier.apply(absPath, diff);
|
|
5878
|
-
const commitHash = await this.
|
|
5934
|
+
const commitHash = await this.commitQueue.enqueue(
|
|
5879
5935
|
`nova: ${task.description}`,
|
|
5880
5936
|
[targetFile]
|
|
5881
5937
|
);
|
|
@@ -6145,7 +6201,7 @@ Available packages: ${deps}`);
|
|
|
6145
6201
|
return parts.join("\n");
|
|
6146
6202
|
}
|
|
6147
6203
|
var Lane3Executor = class {
|
|
6148
|
-
constructor(projectPath, llmClient, gitManager, eventBus, maxFixIterations = 3, modelName, agentPromptLoader, pathGuard) {
|
|
6204
|
+
constructor(projectPath, llmClient, gitManager, eventBus, maxFixIterations = 3, modelName, agentPromptLoader, pathGuard, commitQueue) {
|
|
6149
6205
|
this.projectPath = projectPath;
|
|
6150
6206
|
this.llmClient = llmClient;
|
|
6151
6207
|
this.gitManager = gitManager;
|
|
@@ -6155,8 +6211,10 @@ var Lane3Executor = class {
|
|
|
6155
6211
|
this.agentPromptLoader = agentPromptLoader;
|
|
6156
6212
|
this.pathGuard = pathGuard;
|
|
6157
6213
|
this.diffApplier = new DiffApplier();
|
|
6214
|
+
this.commitQueue = commitQueue ?? new CommitQueue(this.gitManager);
|
|
6158
6215
|
}
|
|
6159
6216
|
diffApplier;
|
|
6217
|
+
commitQueue;
|
|
6160
6218
|
async execute(task, projectMap) {
|
|
6161
6219
|
try {
|
|
6162
6220
|
console.log(`[Nova] Developer: task "${task.description}"`);
|
|
@@ -6260,18 +6318,24 @@ Remember: Output ONLY === FILE === or === DIFF === blocks. No text, no explanati
|
|
|
6260
6318
|
this.eventBus.emit({ type: "secrets_required", data: { envVars: missingVars, taskId: task.id } });
|
|
6261
6319
|
}
|
|
6262
6320
|
const skipValidation = fileBlocks.length === 1 && fileBlocks[0].content.length < 3e3;
|
|
6321
|
+
const tscSkip = this.shouldSkipTsc(fileBlocks);
|
|
6263
6322
|
const validator = new CodeValidator(this.projectPath);
|
|
6264
6323
|
const fixer = new CodeFixer(this.llmClient, this.eventBus);
|
|
6265
6324
|
let currentBlocks = [...fileBlocks];
|
|
6266
6325
|
let errors = [];
|
|
6267
6326
|
if (skipValidation) {
|
|
6268
6327
|
console.log(`[Nova] Tester: skipping validation (small single-file change)`);
|
|
6328
|
+
} else if (tscSkip.skipTsc) {
|
|
6329
|
+
console.log(`[Nova] Tester: skipping tsc (${tscSkip.reason})`);
|
|
6269
6330
|
}
|
|
6270
6331
|
for (let iteration = 1; !skipValidation && iteration <= this.maxFixIterations; iteration++) {
|
|
6271
6332
|
console.log(`[Nova] Tester: validating (iteration ${iteration}/${this.maxFixIterations})...`);
|
|
6272
6333
|
this.eventBus?.emit({ type: "status", data: { message: `Validating code (${iteration}/${this.maxFixIterations})...` } });
|
|
6273
6334
|
try {
|
|
6274
|
-
errors = await validator.validateFiles(currentBlocks
|
|
6335
|
+
errors = await validator.validateFiles(currentBlocks, {
|
|
6336
|
+
skipTsc: tscSkip.skipTsc,
|
|
6337
|
+
skipImportCheck: tscSkip.skipImportCheck
|
|
6338
|
+
});
|
|
6275
6339
|
} catch (validationCrash) {
|
|
6276
6340
|
const msg = validationCrash instanceof Error ? validationCrash.message : String(validationCrash);
|
|
6277
6341
|
console.log(`[Nova] Tester: validation crashed, skipping validation: ${msg}`);
|
|
@@ -6301,17 +6365,17 @@ Remember: Output ONLY === FILE === or === DIFF === blocks. No text, no explanati
|
|
|
6301
6365
|
language: projectMap.stack.language,
|
|
6302
6366
|
packageJson: pkgContent
|
|
6303
6367
|
});
|
|
6304
|
-
|
|
6368
|
+
await Promise.all(fixedBlocks.map(async (block) => {
|
|
6305
6369
|
const absPath = join21(this.projectPath, block.path);
|
|
6306
6370
|
await this.pathGuard?.check(absPath);
|
|
6307
6371
|
await mkdir7(dirname3(absPath), { recursive: true });
|
|
6308
6372
|
await writeFile12(absPath, block.content, "utf-8");
|
|
6309
|
-
}
|
|
6373
|
+
}));
|
|
6310
6374
|
currentBlocks = fixedBlocks;
|
|
6311
6375
|
}
|
|
6312
6376
|
const writtenFiles = currentBlocks.map((b) => b.path);
|
|
6313
6377
|
const safeMsg = `nova: ${task.description.replace(/[\n\r'"\\`$]/g, " ").replace(/\s+/g, " ").trim().slice(0, 72)}`;
|
|
6314
|
-
const commitHash = await this.
|
|
6378
|
+
const commitHash = await this.commitQueue.enqueue(
|
|
6315
6379
|
safeMsg,
|
|
6316
6380
|
writtenFiles
|
|
6317
6381
|
);
|
|
@@ -6329,6 +6393,33 @@ Remember: Output ONLY === FILE === or === DIFF === blocks. No text, no explanati
|
|
|
6329
6393
|
};
|
|
6330
6394
|
}
|
|
6331
6395
|
}
|
|
6396
|
+
/**
|
|
6397
|
+
* Determine whether tsc and/or import checks can be skipped based on file extensions.
|
|
6398
|
+
*/
|
|
6399
|
+
shouldSkipTsc(blocks) {
|
|
6400
|
+
if (blocks.length === 0) {
|
|
6401
|
+
return { skipTsc: false, skipImportCheck: false, reason: "" };
|
|
6402
|
+
}
|
|
6403
|
+
const cssExts = /* @__PURE__ */ new Set([".css", ".scss", ".less", ".sass"]);
|
|
6404
|
+
const nonTsExts = /* @__PURE__ */ new Set([".css", ".scss", ".less", ".sass", ".json", ".md", ".html", ".svg"]);
|
|
6405
|
+
const tsExts = /* @__PURE__ */ new Set([".ts", ".tsx", ".js", ".jsx"]);
|
|
6406
|
+
const getExt = (path2) => {
|
|
6407
|
+
const dot = path2.lastIndexOf(".");
|
|
6408
|
+
return dot !== -1 ? path2.slice(dot) : "";
|
|
6409
|
+
};
|
|
6410
|
+
const exts = blocks.map((b) => getExt(b.path));
|
|
6411
|
+
if (exts.every((ext) => cssExts.has(ext))) {
|
|
6412
|
+
return { skipTsc: true, skipImportCheck: true, reason: "CSS-only changes" };
|
|
6413
|
+
}
|
|
6414
|
+
if (exts.every((ext) => nonTsExts.has(ext))) {
|
|
6415
|
+
return { skipTsc: true, skipImportCheck: true, reason: "no TypeScript/JavaScript files" };
|
|
6416
|
+
}
|
|
6417
|
+
const tsBlocks = blocks.filter((b) => tsExts.has(getExt(b.path)));
|
|
6418
|
+
if (tsBlocks.length === 1 && tsBlocks[0].content.length < 5e3) {
|
|
6419
|
+
return { skipTsc: true, skipImportCheck: false, reason: "single small TS file" };
|
|
6420
|
+
}
|
|
6421
|
+
return { skipTsc: false, skipImportCheck: false, reason: "" };
|
|
6422
|
+
}
|
|
6332
6423
|
/**
|
|
6333
6424
|
* Fuzzy diff apply — find removed lines in the file and replace with added lines.
|
|
6334
6425
|
* Ignores context lines (doesn't require exact line numbers).
|
|
@@ -6477,14 +6568,15 @@ ${retryPrompt}`;
|
|
|
6477
6568
|
}
|
|
6478
6569
|
};
|
|
6479
6570
|
var ExecutorPool = class {
|
|
6480
|
-
constructor(lane1, lane2, eventBus, llm, gitManager, projectPath, fastModel, strongModel, agentPromptLoader, pathGuard, lane4) {
|
|
6571
|
+
constructor(lane1, lane2, eventBus, llm, gitManager, projectPath, fastModel, strongModel, agentPromptLoader, pathGuard, lane4, commitQueue) {
|
|
6481
6572
|
this.lane1 = lane1;
|
|
6482
6573
|
this.lane2 = lane2;
|
|
6483
6574
|
this.eventBus = eventBus;
|
|
6484
6575
|
this.llm = llm;
|
|
6485
6576
|
this.lane4 = lane4;
|
|
6486
|
-
|
|
6487
|
-
this.
|
|
6577
|
+
const sharedQueue = commitQueue ?? (gitManager ? new CommitQueue(gitManager) : void 0);
|
|
6578
|
+
this.lane3Fast = llm && gitManager && projectPath ? new Lane3Executor(projectPath, llm, gitManager, this.eventBus, 3, fastModel, agentPromptLoader, pathGuard, sharedQueue) : null;
|
|
6579
|
+
this.lane3Strong = llm && gitManager && projectPath ? new Lane3Executor(projectPath, llm, gitManager, this.eventBus, 3, strongModel, agentPromptLoader, pathGuard, sharedQueue) : null;
|
|
6488
6580
|
}
|
|
6489
6581
|
lane3Fast;
|
|
6490
6582
|
lane3Strong;
|
|
@@ -7297,6 +7389,7 @@ export {
|
|
|
7297
7389
|
LogLevel,
|
|
7298
7390
|
NovaEventBus,
|
|
7299
7391
|
GitManager,
|
|
7392
|
+
CommitQueue,
|
|
7300
7393
|
DEFAULT_AGENT_PROMPTS,
|
|
7301
7394
|
NovaDir,
|
|
7302
7395
|
GraphStore,
|
|
@@ -6,10 +6,11 @@ import {
|
|
|
6
6
|
import {
|
|
7
7
|
ConfigReader,
|
|
8
8
|
runSetup
|
|
9
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-DQXTUNZA.js";
|
|
10
10
|
import {
|
|
11
11
|
AgentPromptLoader,
|
|
12
12
|
Brain,
|
|
13
|
+
CommitQueue,
|
|
13
14
|
DEFAULT_CONFIG,
|
|
14
15
|
EnvDetector,
|
|
15
16
|
ExecutorPool,
|
|
@@ -25,7 +26,7 @@ import {
|
|
|
25
26
|
ProjectScaffolder,
|
|
26
27
|
ProviderFactory,
|
|
27
28
|
SCAFFOLD_PRESETS
|
|
28
|
-
} from "./chunk-
|
|
29
|
+
} from "./chunk-7K55GHF5.js";
|
|
29
30
|
import {
|
|
30
31
|
__require
|
|
31
32
|
} from "./chunk-3RG5ZIWI.js";
|
|
@@ -489,18 +490,19 @@ var IMAGE_PATTERNS = [
|
|
|
489
490
|
/next\/image.*not configured/i
|
|
490
491
|
];
|
|
491
492
|
var ErrorAutoFixer = class {
|
|
492
|
-
constructor(projectPath, llmClient, gitManager, eventBus, wsServer, projectMap) {
|
|
493
|
+
constructor(projectPath, llmClient, gitManager, eventBus, wsServer, projectMap, commitQueue) {
|
|
493
494
|
this.projectPath = projectPath;
|
|
494
495
|
this.llmClient = llmClient;
|
|
495
496
|
this.gitManager = gitManager;
|
|
496
497
|
this.eventBus = eventBus;
|
|
497
498
|
this.wsServer = wsServer;
|
|
498
499
|
this.projectMap = projectMap;
|
|
500
|
+
this.commitQueue = commitQueue;
|
|
499
501
|
}
|
|
500
502
|
isFixing = false;
|
|
501
503
|
errorBuffer = "";
|
|
502
504
|
debounceTimer = null;
|
|
503
|
-
DEBOUNCE_MS =
|
|
505
|
+
DEBOUNCE_MS = 1e3;
|
|
504
506
|
fixAttempts = 0;
|
|
505
507
|
MAX_FIX_ATTEMPTS = 3;
|
|
506
508
|
lastErrorSignature = "";
|
|
@@ -605,7 +607,16 @@ Error: ${errorOutput.slice(0, 300)}`;
|
|
|
605
607
|
this.projectPath,
|
|
606
608
|
this.llmClient,
|
|
607
609
|
this.gitManager,
|
|
608
|
-
this.eventBus
|
|
610
|
+
this.eventBus,
|
|
611
|
+
1,
|
|
612
|
+
// maxFixIterations — single pass for auto-fix
|
|
613
|
+
void 0,
|
|
614
|
+
// modelName
|
|
615
|
+
void 0,
|
|
616
|
+
// agentPromptLoader
|
|
617
|
+
void 0,
|
|
618
|
+
// pathGuard
|
|
619
|
+
this.commitQueue
|
|
609
620
|
);
|
|
610
621
|
console.log(chalk3.cyan("[Nova] Auto-fixing image errors..."));
|
|
611
622
|
this.wsServer.sendEvent({ type: "status", data: { message: "autofix_start" } });
|
|
@@ -649,7 +660,16 @@ ${truncatedError}`,
|
|
|
649
660
|
this.projectPath,
|
|
650
661
|
this.llmClient,
|
|
651
662
|
this.gitManager,
|
|
652
|
-
this.eventBus
|
|
663
|
+
this.eventBus,
|
|
664
|
+
1,
|
|
665
|
+
// maxFixIterations — single pass for auto-fix
|
|
666
|
+
void 0,
|
|
667
|
+
// modelName
|
|
668
|
+
void 0,
|
|
669
|
+
// agentPromptLoader
|
|
670
|
+
void 0,
|
|
671
|
+
// pathGuard
|
|
672
|
+
this.commitQueue
|
|
653
673
|
);
|
|
654
674
|
console.log(chalk3.cyan("[Nova] Auto-fixing compilation error..."));
|
|
655
675
|
this.wsServer.sendEvent({ type: "status", data: { message: "autofix_start" } });
|
|
@@ -879,6 +899,24 @@ Available: ${SETTABLE_FIELDS.map((f) => f.path).join(", ")}`);
|
|
|
879
899
|
|
|
880
900
|
// src/commands/start.ts
|
|
881
901
|
var PROXY_PORT_OFFSET = 1;
|
|
902
|
+
var MAX_TASK_CONCURRENCY = 3;
|
|
903
|
+
async function runWithConcurrency(tasks, maxConcurrency) {
|
|
904
|
+
const results = new Array(tasks.length);
|
|
905
|
+
const executing = /* @__PURE__ */ new Set();
|
|
906
|
+
for (let i = 0; i < tasks.length; i++) {
|
|
907
|
+
const index = i;
|
|
908
|
+
const p = tasks[index]().then((result) => {
|
|
909
|
+
results[index] = result;
|
|
910
|
+
executing.delete(p);
|
|
911
|
+
});
|
|
912
|
+
executing.add(p);
|
|
913
|
+
if (executing.size >= maxConcurrency) {
|
|
914
|
+
await Promise.race(executing);
|
|
915
|
+
}
|
|
916
|
+
}
|
|
917
|
+
await Promise.all(executing);
|
|
918
|
+
return results;
|
|
919
|
+
}
|
|
882
920
|
function isPortInUse(port) {
|
|
883
921
|
return new Promise((resolve4) => {
|
|
884
922
|
const server = net.createServer();
|
|
@@ -964,7 +1002,7 @@ async function startCommand() {
|
|
|
964
1002
|
projectHash = createHash2("sha256").update(cwd).digest("hex");
|
|
965
1003
|
}
|
|
966
1004
|
const telemetry = new Telemetry();
|
|
967
|
-
const cliPkg = await import("./package-
|
|
1005
|
+
const cliPkg = await import("./package-BSAVJZ7S.js").catch(
|
|
968
1006
|
() => ({ default: { version: "0.0.1" } })
|
|
969
1007
|
);
|
|
970
1008
|
telemetry.send({
|
|
@@ -994,7 +1032,7 @@ ${nudgeMessage}
|
|
|
994
1032
|
});
|
|
995
1033
|
}
|
|
996
1034
|
spinner.start("Detecting project...");
|
|
997
|
-
const { StackDetector } = await import("./dist-
|
|
1035
|
+
const { StackDetector } = await import("./dist-NNJKY4T4.js");
|
|
998
1036
|
const stackDetector = new StackDetector();
|
|
999
1037
|
let stack = await stackDetector.detectStack(cwd);
|
|
1000
1038
|
let detectedDevCommand = await stackDetector.detectDevCommand(stack, cwd);
|
|
@@ -1040,7 +1078,7 @@ ${nudgeMessage}
|
|
|
1040
1078
|
throw err;
|
|
1041
1079
|
}
|
|
1042
1080
|
spinner.succeed("Project indexed.");
|
|
1043
|
-
const { ProjectAnalyzer, RagIndexer, createEmbeddingService } = await import("./dist-
|
|
1081
|
+
const { ProjectAnalyzer, RagIndexer, createEmbeddingService } = await import("./dist-NNJKY4T4.js");
|
|
1044
1082
|
const { ProjectMapApi } = await import("./dist-5FLNK6MH.js");
|
|
1045
1083
|
const projectAnalyzer = new ProjectAnalyzer();
|
|
1046
1084
|
spinner.start("Analyzing project structure...");
|
|
@@ -1048,7 +1086,7 @@ ${nudgeMessage}
|
|
|
1048
1086
|
spinner.succeed(`Project analyzed: ${analysis.fileCount} files, ${analysis.methods.length} methods.`);
|
|
1049
1087
|
let ragIndexer = null;
|
|
1050
1088
|
try {
|
|
1051
|
-
const { VectorStore } = await import("./dist-
|
|
1089
|
+
const { VectorStore } = await import("./dist-NNJKY4T4.js");
|
|
1052
1090
|
let embeddingProvider = "tfidf";
|
|
1053
1091
|
let embeddingApiKey;
|
|
1054
1092
|
let embeddingBaseUrl;
|
|
@@ -1140,7 +1178,7 @@ Tips:`));
|
|
|
1140
1178
|
wsServer.start(httpServer);
|
|
1141
1179
|
}
|
|
1142
1180
|
proxyServer.setProjectMapApi(projectMapApi);
|
|
1143
|
-
const { GraphStore: GS, SearchRouter: SR } = await import("./dist-
|
|
1181
|
+
const { GraphStore: GS, SearchRouter: SR } = await import("./dist-NNJKY4T4.js");
|
|
1144
1182
|
const novaPath = novaDir.getPath(cwd);
|
|
1145
1183
|
const graphStoreForApi = new GS(novaPath);
|
|
1146
1184
|
const searchRouterForApi = new SR(graphStoreForApi);
|
|
@@ -1164,7 +1202,7 @@ Tips:`));
|
|
|
1164
1202
|
}
|
|
1165
1203
|
if (!config.apiKeys.key && config.apiKeys.provider !== "ollama" && config.apiKeys.provider !== "claude-cli") {
|
|
1166
1204
|
console.log(chalk6.yellow("\nNo API key configured. Running setup...\n"));
|
|
1167
|
-
const { runSetup: runSetup2 } = await import("./setup-
|
|
1205
|
+
const { runSetup: runSetup2 } = await import("./setup-VJMYSGJI.js");
|
|
1168
1206
|
await runSetup2(cwd);
|
|
1169
1207
|
const updatedConfig = await configReader.read(cwd);
|
|
1170
1208
|
config.apiKeys = updatedConfig.apiKeys;
|
|
@@ -1195,6 +1233,7 @@ Tips:`));
|
|
|
1195
1233
|
} catch {
|
|
1196
1234
|
}
|
|
1197
1235
|
let executorPool = null;
|
|
1236
|
+
const commitQueue = new CommitQueue(gitManager);
|
|
1198
1237
|
if (llmClient) {
|
|
1199
1238
|
const pathGuard = new PathGuard(cwd);
|
|
1200
1239
|
if (config.project.frontend) pathGuard.allow(resolve2(cwd, config.project.frontend));
|
|
@@ -1206,12 +1245,12 @@ Tips:`));
|
|
|
1206
1245
|
}
|
|
1207
1246
|
const agentPromptLoader = new AgentPromptLoader();
|
|
1208
1247
|
const lane1 = new Lane1Executor(cwd, pathGuard);
|
|
1209
|
-
const lane2 = new Lane2Executor(cwd, llmClient, gitManager, pathGuard);
|
|
1210
|
-
executorPool = new ExecutorPool(lane1, lane2, eventBus, llmClient, gitManager, cwd, config.models.fast, config.models.strong, agentPromptLoader, pathGuard);
|
|
1248
|
+
const lane2 = new Lane2Executor(cwd, llmClient, gitManager, pathGuard, commitQueue);
|
|
1249
|
+
executorPool = new ExecutorPool(lane1, lane2, eventBus, llmClient, gitManager, cwd, config.models.fast, config.models.strong, agentPromptLoader, pathGuard, void 0, commitQueue);
|
|
1211
1250
|
}
|
|
1212
1251
|
let autoFixer = null;
|
|
1213
1252
|
if (llmClient) {
|
|
1214
|
-
autoFixer = new ErrorAutoFixer(cwd, llmClient, gitManager, eventBus, wsServer, projectMap);
|
|
1253
|
+
autoFixer = new ErrorAutoFixer(cwd, llmClient, gitManager, eventBus, wsServer, projectMap, commitQueue);
|
|
1215
1254
|
}
|
|
1216
1255
|
devServer.onOutput((output) => {
|
|
1217
1256
|
autoFixer?.handleOutput(output);
|
|
@@ -1278,9 +1317,7 @@ Tips:`));
|
|
|
1278
1317
|
console.log(chalk6.green(`Auto-executing ${tasks.length} task(s)...`));
|
|
1279
1318
|
wsServer.sendEvent({ type: "status", data: { message: `Auto-executing ${tasks.length} task(s)...` } });
|
|
1280
1319
|
wsServer.sendEvent({ type: "status", data: { message: "Confirmed! Executing tasks..." } });
|
|
1281
|
-
|
|
1282
|
-
eventBus.emit({ type: "task_created", data: task });
|
|
1283
|
-
}
|
|
1320
|
+
executeTasks(tasks);
|
|
1284
1321
|
} catch (err) {
|
|
1285
1322
|
const message = err instanceof Error ? err.message : String(err);
|
|
1286
1323
|
console.error(chalk6.red(`Analysis error: ${message}`));
|
|
@@ -1294,10 +1331,9 @@ Tips:`));
|
|
|
1294
1331
|
}
|
|
1295
1332
|
console.log(chalk6.green(`Confirmed ${pendingTasks.length} task(s). Executing...`));
|
|
1296
1333
|
wsServer.sendEvent({ type: "status", data: { message: "Confirmed! Executing tasks..." } });
|
|
1297
|
-
|
|
1298
|
-
eventBus.emit({ type: "task_created", data: task });
|
|
1299
|
-
}
|
|
1334
|
+
const tasksToRun = [...pendingTasks];
|
|
1300
1335
|
pendingTasks = [];
|
|
1336
|
+
executeTasks(tasksToRun);
|
|
1301
1337
|
});
|
|
1302
1338
|
wsServer.onCancel(() => {
|
|
1303
1339
|
if (pendingTasks.length === 0) {
|
|
@@ -1340,16 +1376,28 @@ ${pendingMessage}
|
|
|
1340
1376
|
wsServer.sendEvent({ type: "status", data: { message: `Analysis error: ${message}` } });
|
|
1341
1377
|
}
|
|
1342
1378
|
});
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
if (executorPool)
|
|
1379
|
+
function executeTasks(tasks) {
|
|
1380
|
+
for (const task of tasks) {
|
|
1381
|
+
eventBus.emit({ type: "task_created", data: task });
|
|
1382
|
+
}
|
|
1383
|
+
if (!executorPool) return;
|
|
1384
|
+
const pool = executorPool;
|
|
1385
|
+
const taskFns = tasks.map((task) => async () => {
|
|
1348
1386
|
try {
|
|
1349
|
-
await
|
|
1387
|
+
return await pool.execute(task, projectMap);
|
|
1350
1388
|
} catch {
|
|
1389
|
+
return { success: false, taskId: task.id, error: "Execution failed" };
|
|
1351
1390
|
}
|
|
1352
|
-
}
|
|
1391
|
+
});
|
|
1392
|
+
runWithConcurrency(taskFns, MAX_TASK_CONCURRENCY).catch((err) => {
|
|
1393
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
1394
|
+
console.error(chalk6.red(`Task batch error: ${message}`));
|
|
1395
|
+
});
|
|
1396
|
+
}
|
|
1397
|
+
eventBus.on("task_created", (event) => {
|
|
1398
|
+
taskMap.set(event.data.id, event.data);
|
|
1399
|
+
logger.logTaskStarted(event.data);
|
|
1400
|
+
wsServer.sendEvent(event);
|
|
1353
1401
|
});
|
|
1354
1402
|
eventBus.on("task_completed", (event) => {
|
|
1355
1403
|
const task = taskMap.get(event.data.taskId);
|
|
@@ -1388,7 +1436,7 @@ ${pendingMessage}
|
|
|
1388
1436
|
}
|
|
1389
1437
|
} catch {
|
|
1390
1438
|
}
|
|
1391
|
-
},
|
|
1439
|
+
}, 1500);
|
|
1392
1440
|
});
|
|
1393
1441
|
eventBus.on("task_failed", (event) => {
|
|
1394
1442
|
const task = taskMap.get(event.data.taskId);
|
|
@@ -9,6 +9,7 @@ import {
|
|
|
9
9
|
CodeChunker,
|
|
10
10
|
CodeFixer,
|
|
11
11
|
CodeValidator,
|
|
12
|
+
CommitQueue,
|
|
12
13
|
ComponentExtractor,
|
|
13
14
|
ConfigError,
|
|
14
15
|
ContextDistiller,
|
|
@@ -72,7 +73,7 @@ import {
|
|
|
72
73
|
parseManifest,
|
|
73
74
|
parseMixedBlocks,
|
|
74
75
|
streamWithEvents
|
|
75
|
-
} from "./chunk-
|
|
76
|
+
} from "./chunk-7K55GHF5.js";
|
|
76
77
|
import "./chunk-3RG5ZIWI.js";
|
|
77
78
|
export {
|
|
78
79
|
AgentPromptLoader,
|
|
@@ -85,6 +86,7 @@ export {
|
|
|
85
86
|
CodeChunker,
|
|
86
87
|
CodeFixer,
|
|
87
88
|
CodeValidator,
|
|
89
|
+
CommitQueue,
|
|
88
90
|
ComponentExtractor,
|
|
89
91
|
ConfigError,
|
|
90
92
|
ContextDistiller,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Command } from 'commander';
|
|
2
|
-
import { IConfigReader, NovaConfig, Observation, TaskItem, LlmClient, IGitManager, EventBus, ProjectMap } from '@novastorm-ai/core';
|
|
2
|
+
import { IConfigReader, NovaConfig, Observation, TaskItem, LlmClient, IGitManager, EventBus, ProjectMap, CommitQueue } from '@novastorm-ai/core';
|
|
3
3
|
import { WebSocketServer } from '@novastorm-ai/proxy';
|
|
4
4
|
|
|
5
5
|
declare class ConfigReader implements IConfigReader {
|
|
@@ -44,6 +44,7 @@ declare class ErrorAutoFixer {
|
|
|
44
44
|
private readonly eventBus;
|
|
45
45
|
private readonly wsServer;
|
|
46
46
|
private readonly projectMap;
|
|
47
|
+
private readonly commitQueue?;
|
|
47
48
|
private isFixing;
|
|
48
49
|
private errorBuffer;
|
|
49
50
|
private debounceTimer;
|
|
@@ -52,7 +53,7 @@ declare class ErrorAutoFixer {
|
|
|
52
53
|
private readonly MAX_FIX_ATTEMPTS;
|
|
53
54
|
private lastErrorSignature;
|
|
54
55
|
private cooldownUntil;
|
|
55
|
-
constructor(projectPath: string, llmClient: LlmClient, gitManager: IGitManager, eventBus: EventBus, wsServer: WebSocketServer, projectMap: ProjectMap);
|
|
56
|
+
constructor(projectPath: string, llmClient: LlmClient, gitManager: IGitManager, eventBus: EventBus, wsServer: WebSocketServer, projectMap: ProjectMap, commitQueue?: CommitQueue | undefined);
|
|
56
57
|
/**
|
|
57
58
|
* Process dev server output. Call this for every stdout/stderr chunk.
|
|
58
59
|
*/
|
package/dist/index.js
CHANGED
|
@@ -4,13 +4,13 @@ import {
|
|
|
4
4
|
createCli,
|
|
5
5
|
promptAndScaffold,
|
|
6
6
|
run
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HVDG2MLB.js";
|
|
8
8
|
import "./chunk-KE7XWO5N.js";
|
|
9
9
|
import {
|
|
10
10
|
ConfigReader,
|
|
11
11
|
runSetup
|
|
12
|
-
} from "./chunk-
|
|
13
|
-
import "./chunk-
|
|
12
|
+
} from "./chunk-DQXTUNZA.js";
|
|
13
|
+
import "./chunk-7K55GHF5.js";
|
|
14
14
|
import "./chunk-3RG5ZIWI.js";
|
|
15
15
|
export {
|
|
16
16
|
ConfigReader,
|