@nathapp/nax 0.65.0-canary.1 → 0.65.0-canary.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/dist/nax.js +699 -510
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -4925,6 +4925,24 @@ function formatSessionName(req) {
|
|
|
4925
4925
|
}
|
|
4926
4926
|
var init_session_name = () => {};
|
|
4927
4927
|
|
|
4928
|
+
// src/agents/retry/default-strategy.ts
|
|
4929
|
+
var MAX_RETRIES = 3, defaultRetryStrategy;
|
|
4930
|
+
var init_default_strategy = __esm(() => {
|
|
4931
|
+
defaultRetryStrategy = {
|
|
4932
|
+
shouldRetry(failure, attempt, _ctx) {
|
|
4933
|
+
if (attempt >= MAX_RETRIES)
|
|
4934
|
+
return { retry: false };
|
|
4935
|
+
if (failure instanceof Error)
|
|
4936
|
+
return { retry: false };
|
|
4937
|
+
const af = failure;
|
|
4938
|
+
if (af.outcome !== "fail-rate-limit")
|
|
4939
|
+
return { retry: false };
|
|
4940
|
+
const delayMs = 2 ** (attempt + 1) * 1000;
|
|
4941
|
+
return { retry: true, delayMs };
|
|
4942
|
+
}
|
|
4943
|
+
};
|
|
4944
|
+
});
|
|
4945
|
+
|
|
4928
4946
|
// src/agents/manager.ts
|
|
4929
4947
|
import { EventEmitter } from "events";
|
|
4930
4948
|
|
|
@@ -4941,6 +4959,7 @@ class AgentManager {
|
|
|
4941
4959
|
_runHop;
|
|
4942
4960
|
_dispatchEvents;
|
|
4943
4961
|
_pidRegistry;
|
|
4962
|
+
_retryStrategy;
|
|
4944
4963
|
events;
|
|
4945
4964
|
constructor(config, registry, opts) {
|
|
4946
4965
|
this._config = config;
|
|
@@ -4951,6 +4970,7 @@ class AgentManager {
|
|
|
4951
4970
|
this._sendPrompt = opts?.sendPrompt;
|
|
4952
4971
|
this._runHop = opts?.runHop;
|
|
4953
4972
|
this._dispatchEvents = opts?.dispatchEvents ?? new DispatchEventBus;
|
|
4973
|
+
this._retryStrategy = opts?.retryStrategy ?? defaultRetryStrategy;
|
|
4954
4974
|
this.events = {
|
|
4955
4975
|
on: (event, listener) => {
|
|
4956
4976
|
this._emitter.on(event, listener);
|
|
@@ -5049,7 +5069,6 @@ class AgentManager {
|
|
|
5049
5069
|
const primaryAgent = primaryAgentOverride ?? this.getDefault();
|
|
5050
5070
|
let currentAgent = primaryAgent;
|
|
5051
5071
|
let hopsSoFar = 0;
|
|
5052
|
-
const MAX_RATE_LIMIT_RETRIES = 3;
|
|
5053
5072
|
let rateLimitRetry = 0;
|
|
5054
5073
|
let currentBundle = request.bundle;
|
|
5055
5074
|
let currentFailure;
|
|
@@ -5095,27 +5114,35 @@ class AgentManager {
|
|
|
5095
5114
|
return { result, fallbacks, finalBundle: updatedBundle, finalPrompt, finalAgent: currentAgent };
|
|
5096
5115
|
}
|
|
5097
5116
|
if (!this.shouldSwap(result.adapterFailure, hopsSoFar, !!bundleForSwapCheck)) {
|
|
5098
|
-
if (result.adapterFailure
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5117
|
+
if (result.adapterFailure) {
|
|
5118
|
+
const retryCtx = {
|
|
5119
|
+
site: "run",
|
|
5120
|
+
agentName: currentAgent,
|
|
5121
|
+
stage: request.runOptions.pipelineStage ?? "run",
|
|
5122
|
+
storyId: request.runOptions.storyId
|
|
5123
|
+
};
|
|
5124
|
+
const decision = this._retryStrategy.shouldRetry(result.adapterFailure, rateLimitRetry, retryCtx);
|
|
5125
|
+
if (decision.retry) {
|
|
5126
|
+
if (request.signal?.aborted) {
|
|
5127
|
+
logger?.info("agent-manager", "Rate-limited backoff aborted \u2014 shutdown in progress", {
|
|
5128
|
+
storyId: request.runOptions.storyId
|
|
5129
|
+
});
|
|
5130
|
+
_finalStatus = "cancelled";
|
|
5131
|
+
return { result, fallbacks, finalBundle: updatedBundle, finalPrompt, finalAgent: currentAgent };
|
|
5132
|
+
}
|
|
5133
|
+
rateLimitRetry += 1;
|
|
5134
|
+
logger?.info("agent-manager", "Rate-limited with no swap candidate \u2014 backing off", {
|
|
5135
|
+
storyId: request.runOptions.storyId,
|
|
5136
|
+
attempt: rateLimitRetry,
|
|
5137
|
+
backoffMs: decision.delayMs
|
|
5102
5138
|
});
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
|
|
5106
|
-
|
|
5107
|
-
|
|
5108
|
-
|
|
5109
|
-
storyId: request.runOptions.storyId,
|
|
5110
|
-
attempt: rateLimitRetry,
|
|
5111
|
-
backoffMs
|
|
5112
|
-
});
|
|
5113
|
-
await _agentManagerDeps.sleep(backoffMs, request.signal);
|
|
5114
|
-
if (request.signal?.aborted) {
|
|
5115
|
-
_finalStatus = "cancelled";
|
|
5116
|
-
return { result, fallbacks, finalBundle: updatedBundle, finalPrompt, finalAgent: currentAgent };
|
|
5139
|
+
await _agentManagerDeps.sleep(decision.delayMs, request.signal);
|
|
5140
|
+
if (request.signal?.aborted) {
|
|
5141
|
+
_finalStatus = "cancelled";
|
|
5142
|
+
return { result, fallbacks, finalBundle: updatedBundle, finalPrompt, finalAgent: currentAgent };
|
|
5143
|
+
}
|
|
5144
|
+
continue;
|
|
5117
5145
|
}
|
|
5118
|
-
continue;
|
|
5119
5146
|
}
|
|
5120
5147
|
if (hopsSoFar > 0) {
|
|
5121
5148
|
this._emitter.emit("onSwapExhausted", { storyId: request.runOptions.storyId, hops: hopsSoFar });
|
|
@@ -5435,6 +5462,7 @@ var init_manager = __esm(() => {
|
|
|
5435
5462
|
init_session_name();
|
|
5436
5463
|
init_bun_deps();
|
|
5437
5464
|
init_registry();
|
|
5465
|
+
init_default_strategy();
|
|
5438
5466
|
_agentManagerDeps = {
|
|
5439
5467
|
sleep: (ms, signal) => cancellableDelay(ms, signal)
|
|
5440
5468
|
};
|
|
@@ -5492,6 +5520,30 @@ var init_bridge_builder = __esm(() => {
|
|
|
5492
5520
|
QUESTION_PATTERNS = [/\?\s*$/, /\bwhich\b/i, /\bshould i\b/i, /\bunclear\b/i, /\bplease clarify\b/i];
|
|
5493
5521
|
});
|
|
5494
5522
|
|
|
5523
|
+
// src/agents/retry/presets.ts
|
|
5524
|
+
function resolveRetryPreset(preset) {
|
|
5525
|
+
return {
|
|
5526
|
+
shouldRetry(failure, attempt, _ctx) {
|
|
5527
|
+
if (attempt >= preset.maxAttempts - 1)
|
|
5528
|
+
return { retry: false };
|
|
5529
|
+
if (preset.preset === "transient-network") {
|
|
5530
|
+
if (failure instanceof Error)
|
|
5531
|
+
return { retry: true, delayMs: preset.baseDelayMs };
|
|
5532
|
+
const af = failure;
|
|
5533
|
+
if (af.retriable)
|
|
5534
|
+
return { retry: true, delayMs: preset.baseDelayMs };
|
|
5535
|
+
return { retry: false };
|
|
5536
|
+
}
|
|
5537
|
+
return { retry: false };
|
|
5538
|
+
}
|
|
5539
|
+
};
|
|
5540
|
+
}
|
|
5541
|
+
|
|
5542
|
+
// src/agents/retry/index.ts
|
|
5543
|
+
var init_retry = __esm(() => {
|
|
5544
|
+
init_default_strategy();
|
|
5545
|
+
});
|
|
5546
|
+
|
|
5495
5547
|
// src/config/schema-types.ts
|
|
5496
5548
|
function isBuiltinModelTier(value) {
|
|
5497
5549
|
return value === "fast" || value === "balanced" || value === "powerful";
|
|
@@ -19478,8 +19530,10 @@ var init_schemas_context = __esm(() => {
|
|
|
19478
19530
|
providers: exports_external.object({
|
|
19479
19531
|
historyScope: exports_external.enum(["repo", "package"]).default("package"),
|
|
19480
19532
|
neighborScope: exports_external.enum(["repo", "package"]).default("package"),
|
|
19481
|
-
crossPackageDepth: exports_external.number().int().min(0).default(1)
|
|
19482
|
-
|
|
19533
|
+
crossPackageDepth: exports_external.number().int().min(0).default(1),
|
|
19534
|
+
sourceGlob: exports_external.string().optional(),
|
|
19535
|
+
maxGlobFiles: exports_external.number().int().min(1).default(500)
|
|
19536
|
+
}).default({ historyScope: "package", neighborScope: "package", crossPackageDepth: 1, maxGlobFiles: 500 }),
|
|
19483
19537
|
staleness: exports_external.object({
|
|
19484
19538
|
enabled: exports_external.boolean().default(true),
|
|
19485
19539
|
maxStoryAge: exports_external.number().int().min(1).default(10),
|
|
@@ -19495,7 +19549,12 @@ var init_schemas_context = __esm(() => {
|
|
|
19495
19549
|
deterministic: false,
|
|
19496
19550
|
session: { retentionDays: 7, archiveOnFeatureArchive: true },
|
|
19497
19551
|
staleness: { enabled: true, maxStoryAge: 10, scoreMultiplier: 0.4 },
|
|
19498
|
-
providers: {
|
|
19552
|
+
providers: {
|
|
19553
|
+
historyScope: "package",
|
|
19554
|
+
neighborScope: "package",
|
|
19555
|
+
crossPackageDepth: 1,
|
|
19556
|
+
maxGlobFiles: 500
|
|
19557
|
+
}
|
|
19499
19558
|
}));
|
|
19500
19559
|
ContextConfigSchema = exports_external.object({
|
|
19501
19560
|
testCoverage: TestCoverageConfigSchema,
|
|
@@ -20265,7 +20324,7 @@ var init_schemas3 = __esm(() => {
|
|
|
20265
20324
|
deterministic: false,
|
|
20266
20325
|
session: { retentionDays: 7, archiveOnFeatureArchive: true },
|
|
20267
20326
|
staleness: { enabled: true, maxStoryAge: 10, scoreMultiplier: 0.4 },
|
|
20268
|
-
providers: { historyScope: "package", neighborScope: "package", crossPackageDepth: 1 }
|
|
20327
|
+
providers: { historyScope: "package", neighborScope: "package", crossPackageDepth: 1, maxGlobFiles: 500 }
|
|
20269
20328
|
}
|
|
20270
20329
|
}),
|
|
20271
20330
|
optimizer: OptimizerConfigSchema.optional(),
|
|
@@ -20950,6 +21009,23 @@ function applyBatchModeCompat(conf) {
|
|
|
20950
21009
|
}
|
|
20951
21010
|
return conf;
|
|
20952
21011
|
}
|
|
21012
|
+
function applyRoutingRetryDeprecationWarning(conf, warn = (msg) => {
|
|
21013
|
+
try {
|
|
21014
|
+
getLogger().warn("config", msg);
|
|
21015
|
+
} catch {}
|
|
21016
|
+
}) {
|
|
21017
|
+
const routing = conf.routing;
|
|
21018
|
+
const llm = routing?.llm;
|
|
21019
|
+
if (!llm)
|
|
21020
|
+
return conf;
|
|
21021
|
+
if ("retries" in llm) {
|
|
21022
|
+
warn("routing.llm.retries is deprecated (issue #856). " + "This value is still applied but will be removed in v1.0. " + "Retry policy is now declared on each operation \u2014 remove this key from your config.");
|
|
21023
|
+
}
|
|
21024
|
+
if ("retryDelayMs" in llm) {
|
|
21025
|
+
warn("routing.llm.retryDelayMs is deprecated (issue #856). " + "This value is still applied but will be removed in v1.0. " + "Retry policy is now declared on each operation \u2014 remove this key from your config.");
|
|
21026
|
+
}
|
|
21027
|
+
return conf;
|
|
21028
|
+
}
|
|
20953
21029
|
async function loadConfig(startDir, cliOverrides) {
|
|
20954
21030
|
let rawConfig = structuredClone(DEFAULT_CONFIG);
|
|
20955
21031
|
const projDir = startDir ? basename2(startDir) === PROJECT_NAX_DIR ? startDir : findProjectDir(startDir) : findProjectDir();
|
|
@@ -20962,14 +21038,14 @@ async function loadConfig(startDir, cliOverrides) {
|
|
|
20962
21038
|
} catch {}
|
|
20963
21039
|
if (globalConfRaw) {
|
|
20964
21040
|
const { profile: _gProfile, ...globalConfStripped } = globalConfRaw;
|
|
20965
|
-
const globalConf = applyBatchModeCompat(applyRemovedStrategyCompat(migrateLegacyReviewModelKey(migrateLegacyTestPattern(globalConfStripped, logger), logger)));
|
|
21041
|
+
const globalConf = applyRoutingRetryDeprecationWarning(applyBatchModeCompat(applyRemovedStrategyCompat(migrateLegacyReviewModelKey(migrateLegacyTestPattern(globalConfStripped, logger), logger))));
|
|
20966
21042
|
rawConfig = deepMergeConfig(rawConfig, globalConf);
|
|
20967
21043
|
}
|
|
20968
21044
|
if (projDir) {
|
|
20969
21045
|
const projConf = await loadJsonFile(join3(projDir, "config.json"), "config");
|
|
20970
21046
|
if (projConf) {
|
|
20971
21047
|
const { profile: _pProfile, ...projConfStripped } = projConf;
|
|
20972
|
-
const resolvedProjConf = applyBatchModeCompat(applyRemovedStrategyCompat(migrateLegacyReviewModelKey(migrateLegacyTestPattern(projConfStripped, logger), logger)));
|
|
21048
|
+
const resolvedProjConf = applyRoutingRetryDeprecationWarning(applyBatchModeCompat(applyRemovedStrategyCompat(migrateLegacyReviewModelKey(migrateLegacyTestPattern(projConfStripped, logger), logger))));
|
|
20973
21049
|
rawConfig = deepMergeConfig(rawConfig, resolvedProjConf);
|
|
20974
21050
|
}
|
|
20975
21051
|
}
|
|
@@ -21541,6 +21617,126 @@ var init_packing = __esm(() => {
|
|
|
21541
21617
|
FLOOR_KINDS = ["static", "feature", "test-coverage"];
|
|
21542
21618
|
});
|
|
21543
21619
|
|
|
21620
|
+
// src/project/detector.ts
|
|
21621
|
+
import { join as join5 } from "path";
|
|
21622
|
+
async function _detectLanguageImpl(workdir, pkg) {
|
|
21623
|
+
const deps = _detectorDeps;
|
|
21624
|
+
if (await deps.fileExists(join5(workdir, "go.mod")))
|
|
21625
|
+
return "go";
|
|
21626
|
+
if (await deps.fileExists(join5(workdir, "Cargo.toml")))
|
|
21627
|
+
return "rust";
|
|
21628
|
+
if (await deps.fileExists(join5(workdir, "pyproject.toml")))
|
|
21629
|
+
return "python";
|
|
21630
|
+
if (await deps.fileExists(join5(workdir, "requirements.txt")))
|
|
21631
|
+
return "python";
|
|
21632
|
+
if (pkg != null) {
|
|
21633
|
+
const allDeps = {
|
|
21634
|
+
...pkg.dependencies,
|
|
21635
|
+
...pkg.devDependencies
|
|
21636
|
+
};
|
|
21637
|
+
if ("typescript" in allDeps)
|
|
21638
|
+
return "typescript";
|
|
21639
|
+
return "javascript";
|
|
21640
|
+
}
|
|
21641
|
+
return;
|
|
21642
|
+
}
|
|
21643
|
+
function detectType(pkg) {
|
|
21644
|
+
if (pkg == null)
|
|
21645
|
+
return;
|
|
21646
|
+
if (pkg.workspaces != null)
|
|
21647
|
+
return "monorepo";
|
|
21648
|
+
const allDeps = {
|
|
21649
|
+
...pkg.dependencies,
|
|
21650
|
+
...pkg.devDependencies
|
|
21651
|
+
};
|
|
21652
|
+
for (const dep of WEB_DEPS) {
|
|
21653
|
+
if (dep in allDeps)
|
|
21654
|
+
return "web";
|
|
21655
|
+
}
|
|
21656
|
+
if ("ink" in allDeps)
|
|
21657
|
+
return "tui";
|
|
21658
|
+
for (const dep of API_DEPS) {
|
|
21659
|
+
if (dep in allDeps)
|
|
21660
|
+
return "api";
|
|
21661
|
+
}
|
|
21662
|
+
if (pkg.bin != null)
|
|
21663
|
+
return "cli";
|
|
21664
|
+
return;
|
|
21665
|
+
}
|
|
21666
|
+
async function detectTestFramework(_workdir, language, pkg) {
|
|
21667
|
+
if (language === "go")
|
|
21668
|
+
return "go-test";
|
|
21669
|
+
if (language === "rust")
|
|
21670
|
+
return "cargo-test";
|
|
21671
|
+
if (language === "python")
|
|
21672
|
+
return "pytest";
|
|
21673
|
+
if (pkg != null) {
|
|
21674
|
+
const devDeps = pkg.devDependencies ?? {};
|
|
21675
|
+
if ("vitest" in devDeps)
|
|
21676
|
+
return "vitest";
|
|
21677
|
+
if ("jest" in devDeps)
|
|
21678
|
+
return "jest";
|
|
21679
|
+
}
|
|
21680
|
+
return;
|
|
21681
|
+
}
|
|
21682
|
+
async function detectLanguage(packageDir) {
|
|
21683
|
+
const pkg = await _detectorDeps.readJson(join5(packageDir, "package.json"));
|
|
21684
|
+
return _detectLanguageImpl(packageDir, pkg);
|
|
21685
|
+
}
|
|
21686
|
+
async function detectLintTool(workdir, language) {
|
|
21687
|
+
if (language === "go")
|
|
21688
|
+
return "golangci-lint";
|
|
21689
|
+
if (language === "rust")
|
|
21690
|
+
return "clippy";
|
|
21691
|
+
if (language === "python")
|
|
21692
|
+
return "ruff";
|
|
21693
|
+
const deps = _detectorDeps;
|
|
21694
|
+
if (await deps.fileExists(join5(workdir, "biome.json")))
|
|
21695
|
+
return "biome";
|
|
21696
|
+
if (await deps.fileExists(join5(workdir, ".eslintrc")))
|
|
21697
|
+
return "eslint";
|
|
21698
|
+
if (await deps.fileExists(join5(workdir, ".eslintrc.js")))
|
|
21699
|
+
return "eslint";
|
|
21700
|
+
if (await deps.fileExists(join5(workdir, ".eslintrc.json")))
|
|
21701
|
+
return "eslint";
|
|
21702
|
+
return;
|
|
21703
|
+
}
|
|
21704
|
+
async function detectProjectProfile(workdir, existing) {
|
|
21705
|
+
const pkg = await _detectorDeps.readJson(join5(workdir, "package.json"));
|
|
21706
|
+
const language = existing.language !== undefined ? existing.language : await _detectLanguageImpl(workdir, pkg);
|
|
21707
|
+
const type = existing.type !== undefined ? existing.type : detectType(pkg);
|
|
21708
|
+
const testFramework = existing.testFramework !== undefined ? existing.testFramework : await detectTestFramework(workdir, language, pkg);
|
|
21709
|
+
const lintTool = existing.lintTool !== undefined ? existing.lintTool : await detectLintTool(workdir, language);
|
|
21710
|
+
return { language, type, testFramework, lintTool };
|
|
21711
|
+
}
|
|
21712
|
+
var _detectorDeps, WEB_DEPS, API_DEPS;
|
|
21713
|
+
var init_detector = __esm(() => {
|
|
21714
|
+
_detectorDeps = {
|
|
21715
|
+
async fileExists(path) {
|
|
21716
|
+
const file3 = Bun.file(path);
|
|
21717
|
+
return file3.exists();
|
|
21718
|
+
},
|
|
21719
|
+
async readJson(path) {
|
|
21720
|
+
try {
|
|
21721
|
+
const file3 = Bun.file(path);
|
|
21722
|
+
if (!await file3.exists())
|
|
21723
|
+
return null;
|
|
21724
|
+
const text = await file3.text();
|
|
21725
|
+
return JSON.parse(text);
|
|
21726
|
+
} catch {
|
|
21727
|
+
return null;
|
|
21728
|
+
}
|
|
21729
|
+
}
|
|
21730
|
+
};
|
|
21731
|
+
WEB_DEPS = new Set(["react", "next", "vue", "nuxt"]);
|
|
21732
|
+
API_DEPS = new Set(["express", "fastify", "hono"]);
|
|
21733
|
+
});
|
|
21734
|
+
|
|
21735
|
+
// src/project/index.ts
|
|
21736
|
+
var init_project = __esm(() => {
|
|
21737
|
+
init_detector();
|
|
21738
|
+
});
|
|
21739
|
+
|
|
21544
21740
|
// src/test-runners/detect/workspace.ts
|
|
21545
21741
|
async function expandWorkspaceGlob(workdir, pattern) {
|
|
21546
21742
|
const dirs = [];
|
|
@@ -21663,7 +21859,7 @@ var init_workspace = __esm(() => {
|
|
|
21663
21859
|
|
|
21664
21860
|
// src/utils/path-security.ts
|
|
21665
21861
|
import { realpathSync as realpathSync2 } from "fs";
|
|
21666
|
-
import { dirname as dirname2, isAbsolute as isAbsolute3, join as
|
|
21862
|
+
import { dirname as dirname2, isAbsolute as isAbsolute3, join as join6, normalize as normalize2, resolve as resolve4 } from "path";
|
|
21667
21863
|
function safeRealpathForComparison(p) {
|
|
21668
21864
|
try {
|
|
21669
21865
|
return realpathSync2(p);
|
|
@@ -21672,7 +21868,7 @@ function safeRealpathForComparison(p) {
|
|
|
21672
21868
|
if (parent === p)
|
|
21673
21869
|
return normalize2(p);
|
|
21674
21870
|
const resolvedParent = safeRealpathForComparison(parent);
|
|
21675
|
-
return
|
|
21871
|
+
return join6(resolvedParent, p.split("/").pop() ?? "");
|
|
21676
21872
|
}
|
|
21677
21873
|
}
|
|
21678
21874
|
function isRelativeAndSafe(filePath) {
|
|
@@ -21700,7 +21896,7 @@ function validateModulePath(modulePath, allowedRoots) {
|
|
|
21700
21896
|
} else {
|
|
21701
21897
|
for (let i = 0;i < allowedRoots.length; i++) {
|
|
21702
21898
|
const originalRoot = resolve4(allowedRoots[i]);
|
|
21703
|
-
const absoluteInput = resolve4(
|
|
21899
|
+
const absoluteInput = resolve4(join6(originalRoot, modulePath));
|
|
21704
21900
|
const resolved = safeRealpathForComparison(absoluteInput);
|
|
21705
21901
|
const resolvedRoot = resolvedRoots[i];
|
|
21706
21902
|
if (resolved.startsWith(`${resolvedRoot}/`) || resolved === resolvedRoot) {
|
|
@@ -21717,7 +21913,7 @@ var init_path_security2 = () => {};
|
|
|
21717
21913
|
|
|
21718
21914
|
// src/context/engine/providers/code-neighbor.ts
|
|
21719
21915
|
import { createHash as createHash3 } from "crypto";
|
|
21720
|
-
import { join as
|
|
21916
|
+
import { join as join7, relative, resolve as resolve5 } from "path";
|
|
21721
21917
|
function isExcludedPath(file3, ignoreMatchers) {
|
|
21722
21918
|
for (const prefix of EXCLUDED_DIR_PREFIXES) {
|
|
21723
21919
|
if (file3.startsWith(prefix) || file3.includes(`/${prefix}`))
|
|
@@ -21829,10 +22025,17 @@ function stripExt(s) {
|
|
|
21829
22025
|
function isTestFile(filePath, regex) {
|
|
21830
22026
|
return regex.some((re) => re.test(filePath));
|
|
21831
22027
|
}
|
|
21832
|
-
async function
|
|
22028
|
+
async function resolveSourceGlob(override, packageDir) {
|
|
22029
|
+
if (override)
|
|
22030
|
+
return override;
|
|
22031
|
+
const language = await _codeNeighborDeps.detectLanguage(packageDir);
|
|
22032
|
+
return (language && SOURCE_GLOB_BY_LANGUAGE[language]) ?? FALLBACK_SOURCE_GLOB;
|
|
22033
|
+
}
|
|
22034
|
+
async function collectNeighbors(filePath, workdir, sourceGlob, maxGlobFiles, extraGlobWorkdirs, siblingTestContext, ignoreMatchers, globCtx) {
|
|
21833
22035
|
const neighbors = new Set;
|
|
21834
|
-
|
|
21835
|
-
|
|
22036
|
+
let anyTruncated = false;
|
|
22037
|
+
if (await _codeNeighborDeps.fileExists(join7(workdir, filePath))) {
|
|
22038
|
+
const content = await _codeNeighborDeps.readFile(join7(workdir, filePath));
|
|
21836
22039
|
for (const spec of parseImportSpecifiers(content)) {
|
|
21837
22040
|
const resolved = resolveImport(spec, filePath, workdir);
|
|
21838
22041
|
if (resolved && resolved !== filePath)
|
|
@@ -21842,14 +22045,16 @@ async function collectNeighbors(filePath, workdir, extraGlobWorkdirs, siblingTes
|
|
|
21842
22045
|
const fileBaseName = (filePath.split("/").pop() ?? filePath).replace(/\.[^.]+$/, "");
|
|
21843
22046
|
const fileNoExt = filePath.replace(/\.[^.]+$/, "");
|
|
21844
22047
|
const scanForReverseDeps = async (scanWorkdir) => {
|
|
21845
|
-
const srcFiles = _codeNeighborDeps.glob(
|
|
22048
|
+
const { files: srcFiles, truncated } = _codeNeighborDeps.glob(sourceGlob, scanWorkdir, ignoreMatchers, maxGlobFiles, globCtx);
|
|
22049
|
+
if (truncated)
|
|
22050
|
+
anyTruncated = true;
|
|
21846
22051
|
for (const srcFile of srcFiles) {
|
|
21847
22052
|
if (neighbors.size >= MAX_NEIGHBORS_PER_FILE)
|
|
21848
22053
|
break;
|
|
21849
22054
|
if (srcFile === filePath)
|
|
21850
22055
|
continue;
|
|
21851
22056
|
try {
|
|
21852
|
-
const content = await _codeNeighborDeps.readFile(
|
|
22057
|
+
const content = await _codeNeighborDeps.readFile(join7(scanWorkdir, srcFile));
|
|
21853
22058
|
if (content.includes(fileBaseName)) {
|
|
21854
22059
|
for (const spec of parseImportSpecifiers(content)) {
|
|
21855
22060
|
const resolved = resolveImport(spec, srcFile, scanWorkdir);
|
|
@@ -21874,7 +22079,7 @@ async function collectNeighbors(filePath, workdir, extraGlobWorkdirs, siblingTes
|
|
|
21874
22079
|
const candidates = deriveSiblingTestCandidates(filePath, siblingTestContext.globs);
|
|
21875
22080
|
let chosen = null;
|
|
21876
22081
|
for (const candidate of candidates) {
|
|
21877
|
-
if (await _codeNeighborDeps.fileExists(
|
|
22082
|
+
if (await _codeNeighborDeps.fileExists(join7(workdir, candidate))) {
|
|
21878
22083
|
chosen = candidate;
|
|
21879
22084
|
break;
|
|
21880
22085
|
}
|
|
@@ -21888,7 +22093,7 @@ async function collectNeighbors(filePath, workdir, extraGlobWorkdirs, siblingTes
|
|
|
21888
22093
|
if (chosen !== null && chosen !== filePath)
|
|
21889
22094
|
neighbors.add(chosen);
|
|
21890
22095
|
}
|
|
21891
|
-
return [...neighbors].slice(0, MAX_NEIGHBORS_PER_FILE);
|
|
22096
|
+
return { neighbors: [...neighbors].slice(0, MAX_NEIGHBORS_PER_FILE), truncated: anyTruncated };
|
|
21892
22097
|
}
|
|
21893
22098
|
async function resolveExtraGlobWorkdirs(neighborScope, crossPackageDepth, repoRoot, packageDir) {
|
|
21894
22099
|
if (neighborScope !== "package" || crossPackageDepth <= 0 || packageDir === repoRoot) {
|
|
@@ -21898,7 +22103,7 @@ async function resolveExtraGlobWorkdirs(neighborScope, crossPackageDepth, repoRo
|
|
|
21898
22103
|
const relPkgDirs = await _codeNeighborDeps.discoverWorkspacePackages(repoRoot);
|
|
21899
22104
|
if (relPkgDirs.length === 0)
|
|
21900
22105
|
return [repoRoot];
|
|
21901
|
-
return relPkgDirs.map((rel) =>
|
|
22106
|
+
return relPkgDirs.map((rel) => join7(repoRoot, rel)).filter((abs) => abs !== packageDir);
|
|
21902
22107
|
} catch {
|
|
21903
22108
|
return [repoRoot];
|
|
21904
22109
|
}
|
|
@@ -21909,9 +22114,13 @@ class CodeNeighborProvider {
|
|
|
21909
22114
|
kind = "neighbor";
|
|
21910
22115
|
neighborScope;
|
|
21911
22116
|
crossPackageDepth;
|
|
22117
|
+
sourceGlobOverride;
|
|
22118
|
+
maxGlobFiles;
|
|
21912
22119
|
constructor(options = {}) {
|
|
21913
22120
|
this.neighborScope = options.neighborScope ?? "package";
|
|
21914
22121
|
this.crossPackageDepth = options.crossPackageDepth ?? 1;
|
|
22122
|
+
this.sourceGlobOverride = options.sourceGlob;
|
|
22123
|
+
this.maxGlobFiles = options.maxGlobFiles ?? MAX_GLOB_FILES_DEFAULT;
|
|
21915
22124
|
}
|
|
21916
22125
|
async fetch(request) {
|
|
21917
22126
|
const { touchedFiles } = request;
|
|
@@ -21926,9 +22135,14 @@ class CodeNeighborProvider {
|
|
|
21926
22135
|
regex: request.resolvedTestPatterns.regex
|
|
21927
22136
|
} : undefined;
|
|
21928
22137
|
const ignoreMatchers = request.naxIgnoreIndex?.getMatchers(workdir);
|
|
22138
|
+
const sourceGlob = await resolveSourceGlob(this.sourceGlobOverride, request.packageDir);
|
|
22139
|
+
const globCtx = { storyId: request.storyId, packageDir: request.packageDir };
|
|
21929
22140
|
const sections = [];
|
|
22141
|
+
let anyTruncated = false;
|
|
21930
22142
|
for (const file3 of filesToProcess) {
|
|
21931
|
-
const neighbors = await collectNeighbors(file3, workdir, extraGlobWorkdirs, siblingTestContext, ignoreMatchers);
|
|
22143
|
+
const { neighbors, truncated } = await collectNeighbors(file3, workdir, sourceGlob, this.maxGlobFiles, extraGlobWorkdirs, siblingTestContext, ignoreMatchers, globCtx);
|
|
22144
|
+
if (truncated)
|
|
22145
|
+
anyTruncated = true;
|
|
21932
22146
|
if (neighbors.length > 0) {
|
|
21933
22147
|
sections.push(`### ${file3}
|
|
21934
22148
|
${neighbors.map((n) => `- ${n}`).join(`
|
|
@@ -21947,7 +22161,12 @@ ${sections.join(`
|
|
|
21947
22161
|
|
|
21948
22162
|
`)}`;
|
|
21949
22163
|
const maxChars = MAX_CHUNK_TOKENS * 4;
|
|
21950
|
-
const
|
|
22164
|
+
const body = rawContent.length > maxChars ? rawContent.slice(0, maxChars) : rawContent;
|
|
22165
|
+
const truncationNote = anyTruncated ? `
|
|
22166
|
+
|
|
22167
|
+
> Note: reverse-dep scan capped at ${this.maxGlobFiles} files; some neighbors may be missing.
|
|
22168
|
+
> Increase \`context.v2.providers.maxGlobFiles\` or set \`sourceGlob\` to a narrower pattern (e.g. \`**/*.go\`) to reduce the scan footprint.` : "";
|
|
22169
|
+
const content = body + truncationNote;
|
|
21951
22170
|
const tokens = Math.ceil(content.length / 4);
|
|
21952
22171
|
const chunk = {
|
|
21953
22172
|
id: `code-neighbor:${contentHash8(content)}`,
|
|
@@ -21961,11 +22180,19 @@ ${sections.join(`
|
|
|
21961
22180
|
return { chunks: [chunk], pullTools: [] };
|
|
21962
22181
|
}
|
|
21963
22182
|
}
|
|
21964
|
-
var MAX_FILES = 10, MAX_NEIGHBORS_PER_FILE = 8,
|
|
22183
|
+
var MAX_FILES = 10, MAX_NEIGHBORS_PER_FILE = 8, MAX_GLOB_FILES_DEFAULT = 500, MAX_CHUNK_TOKENS = 500, SOURCE_GLOB_BY_LANGUAGE, FALLBACK_SOURCE_GLOB = "**/*.{ts,tsx,js,jsx,mjs,cjs,py,go,rs,java,rb,php,cs,cpp,c,h}", EXCLUDED_DIR_PREFIXES, _codeNeighborDeps, FROM_PATTERN, REQUIRE_PATTERN, IMPORT_SIDE_EFFECT_PATTERN;
|
|
21965
22184
|
var init_code_neighbor = __esm(() => {
|
|
21966
22185
|
init_logger2();
|
|
22186
|
+
init_project();
|
|
21967
22187
|
init_workspace();
|
|
21968
22188
|
init_path_security2();
|
|
22189
|
+
SOURCE_GLOB_BY_LANGUAGE = {
|
|
22190
|
+
typescript: "**/*.{ts,tsx,js,jsx,mjs,cjs}",
|
|
22191
|
+
javascript: "**/*.{js,jsx,mjs,cjs}",
|
|
22192
|
+
go: "**/*.go",
|
|
22193
|
+
python: "**/*.py",
|
|
22194
|
+
rust: "**/*.rs"
|
|
22195
|
+
};
|
|
21969
22196
|
EXCLUDED_DIR_PREFIXES = [
|
|
21970
22197
|
"node_modules/",
|
|
21971
22198
|
".git/",
|
|
@@ -21980,8 +22207,9 @@ var init_code_neighbor = __esm(() => {
|
|
|
21980
22207
|
fileExists: (path) => Bun.file(path).exists(),
|
|
21981
22208
|
readFile: (path) => Bun.file(path).text(),
|
|
21982
22209
|
discoverWorkspacePackages: (repoRoot) => discoverWorkspacePackages(repoRoot),
|
|
22210
|
+
detectLanguage: (packageDir) => detectLanguage(packageDir),
|
|
21983
22211
|
getLogger,
|
|
21984
|
-
glob: (pattern, cwd, ignoreMatchers = []) => {
|
|
22212
|
+
glob: (pattern, cwd, ignoreMatchers = [], cap = MAX_GLOB_FILES_DEFAULT, ctx) => {
|
|
21985
22213
|
const g = new Bun.Glob(pattern);
|
|
21986
22214
|
const results = [];
|
|
21987
22215
|
let count = 0;
|
|
@@ -21989,7 +22217,7 @@ var init_code_neighbor = __esm(() => {
|
|
|
21989
22217
|
for (const file3 of g.scanSync({ cwd, absolute: false })) {
|
|
21990
22218
|
if (isExcludedPath(file3, ignoreMatchers))
|
|
21991
22219
|
continue;
|
|
21992
|
-
if (count >=
|
|
22220
|
+
if (count >= cap) {
|
|
21993
22221
|
truncated = true;
|
|
21994
22222
|
break;
|
|
21995
22223
|
}
|
|
@@ -21997,13 +22225,16 @@ var init_code_neighbor = __esm(() => {
|
|
|
21997
22225
|
count++;
|
|
21998
22226
|
}
|
|
21999
22227
|
if (truncated) {
|
|
22000
|
-
_codeNeighborDeps.getLogger().
|
|
22228
|
+
_codeNeighborDeps.getLogger().warn("context-v2", "Reverse-dep glob cap reached \u2014 results truncated", {
|
|
22229
|
+
storyId: ctx?.storyId,
|
|
22230
|
+
packageDir: ctx?.packageDir,
|
|
22001
22231
|
pattern,
|
|
22002
22232
|
cwd,
|
|
22003
|
-
cap
|
|
22233
|
+
cap,
|
|
22234
|
+
hint: "Increase context.v2.providers.maxGlobFiles or narrow context.v2.providers.sourceGlob"
|
|
22004
22235
|
});
|
|
22005
22236
|
}
|
|
22006
|
-
return results;
|
|
22237
|
+
return { files: results, truncated };
|
|
22007
22238
|
}
|
|
22008
22239
|
};
|
|
22009
22240
|
FROM_PATTERN = /from\s+['"]([^'"]+)['"]/g;
|
|
@@ -22465,11 +22696,11 @@ class PullToolBudget {
|
|
|
22465
22696
|
return this.sessionCalls;
|
|
22466
22697
|
}
|
|
22467
22698
|
}
|
|
22468
|
-
async function handleQueryNeighbor(input, repoRoot, budget, maxTokensPerCall = DEFAULT_MAX_TOKENS_PER_CALL, resolvedTestPatterns, storyId) {
|
|
22699
|
+
async function handleQueryNeighbor(input, repoRoot, budget, maxTokensPerCall = DEFAULT_MAX_TOKENS_PER_CALL, resolvedTestPatterns, storyId, providerOptions) {
|
|
22469
22700
|
budget.consume();
|
|
22470
|
-
const provider = new CodeNeighborProvider;
|
|
22701
|
+
const provider = new CodeNeighborProvider(providerOptions ?? {});
|
|
22471
22702
|
const request = {
|
|
22472
|
-
storyId: "_pull-tool",
|
|
22703
|
+
storyId: storyId ?? "_pull-tool",
|
|
22473
22704
|
repoRoot,
|
|
22474
22705
|
packageDir: repoRoot,
|
|
22475
22706
|
stage: "pull-tool",
|
|
@@ -23150,7 +23381,7 @@ var init_orchestrator = __esm(() => {
|
|
|
23150
23381
|
});
|
|
23151
23382
|
|
|
23152
23383
|
// src/context/rules/canonical-loader.ts
|
|
23153
|
-
import { basename as basename3, join as
|
|
23384
|
+
import { basename as basename3, join as join8 } from "path";
|
|
23154
23385
|
function parseRuleAllowMarker(line) {
|
|
23155
23386
|
const allowed = new Set;
|
|
23156
23387
|
RULE_ALLOW_MARKER.lastIndex = 0;
|
|
@@ -23285,7 +23516,7 @@ function applyCanonicalRulesBudget(rules, budgetTokens) {
|
|
|
23285
23516
|
}
|
|
23286
23517
|
async function loadCanonicalRules(workdir, options = {}) {
|
|
23287
23518
|
const logger = _canonicalLoaderDeps.getLogger();
|
|
23288
|
-
const rulesDir =
|
|
23519
|
+
const rulesDir = join8(workdir, CANONICAL_RULES_DIR);
|
|
23289
23520
|
const allFilePaths = _canonicalLoaderDeps.globInDir(rulesDir);
|
|
23290
23521
|
const filePaths = allFilePaths.filter((filePath) => {
|
|
23291
23522
|
const normalized = filePath.replaceAll("\\", "/");
|
|
@@ -23386,7 +23617,7 @@ var init_canonical_loader = __esm(() => {
|
|
|
23386
23617
|
for (const rel of files) {
|
|
23387
23618
|
const depth = rel.split("/").length - 1;
|
|
23388
23619
|
if (depth <= 1) {
|
|
23389
|
-
kept.push(
|
|
23620
|
+
kept.push(join8(dir, rel));
|
|
23390
23621
|
} else {
|
|
23391
23622
|
ignored.push(rel);
|
|
23392
23623
|
}
|
|
@@ -23748,7 +23979,7 @@ var init_scratch_writer = __esm(() => {
|
|
|
23748
23979
|
});
|
|
23749
23980
|
|
|
23750
23981
|
// src/utils/path-filters.ts
|
|
23751
|
-
import { join as
|
|
23982
|
+
import { join as join9, relative as relative2 } from "path";
|
|
23752
23983
|
function basename4(path) {
|
|
23753
23984
|
const stripped = path.startsWith("./") ? path.slice(2) : path;
|
|
23754
23985
|
const idx = stripped.lastIndexOf("/");
|
|
@@ -23838,8 +24069,8 @@ async function resolveNaxIgnorePatterns(repoRoot, packageDir) {
|
|
|
23838
24069
|
const normalizedRepoRoot = normalizePath(repoRoot);
|
|
23839
24070
|
const normalizedPackageDir = packageDir ? normalizePath(packageDir) : normalizedRepoRoot;
|
|
23840
24071
|
const packagePrefix = normalizedPackageDir !== normalizedRepoRoot ? normalizePath(relative2(repoRoot, packageDir ?? repoRoot)) : null;
|
|
23841
|
-
const rootFile =
|
|
23842
|
-
const packageFile =
|
|
24072
|
+
const rootFile = join9(repoRoot, NAX_IGNORE_FILENAME);
|
|
24073
|
+
const packageFile = join9(packageDir ?? repoRoot, NAX_IGNORE_FILENAME);
|
|
23843
24074
|
const rootPatterns = await readIgnorePatterns(rootFile);
|
|
23844
24075
|
const packagePatterns = packageDir && packageDir !== repoRoot ? await readIgnorePatterns(packageFile) : [];
|
|
23845
24076
|
return [
|
|
@@ -24014,7 +24245,7 @@ var init_session_scratch = __esm(() => {
|
|
|
24014
24245
|
|
|
24015
24246
|
// src/context/engine/providers/static-rules.ts
|
|
24016
24247
|
import { createHash as createHash8 } from "crypto";
|
|
24017
|
-
import { join as
|
|
24248
|
+
import { join as join10, relative as relative3 } from "path";
|
|
24018
24249
|
function contentHash85(content) {
|
|
24019
24250
|
return createHash8("sha256").update(content).digest("hex").slice(0, 8);
|
|
24020
24251
|
}
|
|
@@ -24222,7 +24453,7 @@ ${rule.content}`,
|
|
|
24222
24453
|
const existingCandidates = [];
|
|
24223
24454
|
for (const fileName of LEGACY_CANDIDATE_FILES) {
|
|
24224
24455
|
try {
|
|
24225
|
-
if (await _staticRulesDeps.fileExists(
|
|
24456
|
+
if (await _staticRulesDeps.fileExists(join10(rootDir, fileName))) {
|
|
24226
24457
|
existingCandidates.push(fileName);
|
|
24227
24458
|
}
|
|
24228
24459
|
} catch {}
|
|
@@ -24238,11 +24469,11 @@ ${rule.content}`,
|
|
|
24238
24469
|
for (const fileName of LEGACY_CANDIDATE_FILES) {
|
|
24239
24470
|
legacySources.push({
|
|
24240
24471
|
sourceId: fileName,
|
|
24241
|
-
filePath:
|
|
24472
|
+
filePath: join10(rootDir, fileName),
|
|
24242
24473
|
heading: fileName
|
|
24243
24474
|
});
|
|
24244
24475
|
}
|
|
24245
|
-
const rulesDir =
|
|
24476
|
+
const rulesDir = join10(rootDir, LEGACY_RULES_DIR);
|
|
24246
24477
|
const nestedRulePaths = _staticRulesDeps.globInDir(rulesDir);
|
|
24247
24478
|
for (const filePath of nestedRulePaths) {
|
|
24248
24479
|
const normalized = normalizePath2(filePath);
|
|
@@ -24301,7 +24532,7 @@ var init_static_rules = __esm(() => {
|
|
|
24301
24532
|
fileExists: async (path) => Bun.file(path).exists(),
|
|
24302
24533
|
globInDir: (dir) => {
|
|
24303
24534
|
try {
|
|
24304
|
-
return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) =>
|
|
24535
|
+
return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) => join10(dir, f));
|
|
24305
24536
|
} catch {
|
|
24306
24537
|
return [];
|
|
24307
24538
|
}
|
|
@@ -25507,7 +25738,7 @@ var init_detect2 = __esm(() => {
|
|
|
25507
25738
|
});
|
|
25508
25739
|
|
|
25509
25740
|
// src/test-runners/resolver.ts
|
|
25510
|
-
import { dirname as dirname4, isAbsolute as isAbsolute4, join as
|
|
25741
|
+
import { dirname as dirname4, isAbsolute as isAbsolute4, join as join11, relative as relative4, resolve as resolve6 } from "path";
|
|
25511
25742
|
function buildResolved(globs, resolution) {
|
|
25512
25743
|
return {
|
|
25513
25744
|
globs,
|
|
@@ -25559,7 +25790,7 @@ async function resolveTestFilePatterns(config2, workdir, packageDir, options) {
|
|
|
25559
25790
|
validateGlobs(rootPatterns, "resolver");
|
|
25560
25791
|
return buildResolved(rootPatterns, "root-config");
|
|
25561
25792
|
}
|
|
25562
|
-
const detectionWorkdir = packageDir ?
|
|
25793
|
+
const detectionWorkdir = packageDir ? join11(workdir, packageDir) : workdir;
|
|
25563
25794
|
const detected = await _resolverDeps2.detectTestFilePatterns(detectionWorkdir);
|
|
25564
25795
|
if (detected.confidence !== "empty" && detected.patterns.length > 0) {
|
|
25565
25796
|
getSafeLogger()?.info("resolver", "Test patterns auto-detected", {
|
|
@@ -25917,7 +26148,9 @@ function createDefaultOrchestrator(story, config2, _storyScratchDirs, additional
|
|
|
25917
26148
|
providers.push(new GitHistoryProvider({ historyScope: providerConfig?.historyScope ?? "package" }));
|
|
25918
26149
|
providers.push(new CodeNeighborProvider({
|
|
25919
26150
|
neighborScope: providerConfig?.neighborScope ?? "package",
|
|
25920
|
-
crossPackageDepth: providerConfig?.crossPackageDepth ?? 1
|
|
26151
|
+
crossPackageDepth: providerConfig?.crossPackageDepth ?? 1,
|
|
26152
|
+
sourceGlob: providerConfig?.sourceGlob,
|
|
26153
|
+
maxGlobFiles: providerConfig?.maxGlobFiles
|
|
25921
26154
|
}));
|
|
25922
26155
|
providers.push(...additionalProviders);
|
|
25923
26156
|
return new ContextOrchestrator(providers);
|
|
@@ -25934,7 +26167,7 @@ var init_orchestrator_factory = __esm(() => {
|
|
|
25934
26167
|
});
|
|
25935
26168
|
|
|
25936
26169
|
// src/context/engine/providers/plugin-loader.ts
|
|
25937
|
-
import { isAbsolute as isAbsolute5, join as
|
|
26170
|
+
import { isAbsolute as isAbsolute5, join as join12, resolve as resolve7 } from "path";
|
|
25938
26171
|
function isInitialisable(p) {
|
|
25939
26172
|
return typeof p.init === "function";
|
|
25940
26173
|
}
|
|
@@ -25962,7 +26195,7 @@ function resolveModuleSpecifier(specifier, workdir) {
|
|
|
25962
26195
|
}
|
|
25963
26196
|
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
25964
26197
|
const resolvedWorkdir = resolve7(workdir);
|
|
25965
|
-
const resolved = resolve7(
|
|
26198
|
+
const resolved = resolve7(join12(workdir, specifier));
|
|
25966
26199
|
if (resolved !== resolvedWorkdir && !resolved.startsWith(`${resolvedWorkdir}/`)) {
|
|
25967
26200
|
throw new Error(`Plugin module path escapes project workdir: "${specifier}" resolves to "${resolved}" (workdir: "${resolvedWorkdir}")`);
|
|
25968
26201
|
}
|
|
@@ -26108,15 +26341,15 @@ var init_available_budget = __esm(() => {
|
|
|
26108
26341
|
|
|
26109
26342
|
// src/context/engine/manifest-store.ts
|
|
26110
26343
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
26111
|
-
import { dirname as dirname5, isAbsolute as isAbsolute6, join as
|
|
26344
|
+
import { dirname as dirname5, isAbsolute as isAbsolute6, join as join13, relative as relative6, resolve as resolve8 } from "path";
|
|
26112
26345
|
function contextStoryDir(projectDir, featureId, storyId) {
|
|
26113
|
-
return
|
|
26346
|
+
return join13(projectDir, ".nax", "features", featureId, "stories", storyId);
|
|
26114
26347
|
}
|
|
26115
26348
|
function contextManifestPath(projectDir, featureId, storyId, stage) {
|
|
26116
|
-
return
|
|
26349
|
+
return join13(contextStoryDir(projectDir, featureId, storyId), `context-manifest-${stage}.json`);
|
|
26117
26350
|
}
|
|
26118
26351
|
function rebuildManifestPath(projectDir, featureId, storyId) {
|
|
26119
|
-
return
|
|
26352
|
+
return join13(contextStoryDir(projectDir, featureId, storyId), "rebuild-manifest.json");
|
|
26120
26353
|
}
|
|
26121
26354
|
function toStoredPath(projectDir, pathValue) {
|
|
26122
26355
|
const relativePath = isAbsolute6(pathValue) ? relative6(projectDir, pathValue) : pathValue;
|
|
@@ -26172,7 +26405,7 @@ async function loadContextManifests(projectDir, storyId, featureId) {
|
|
|
26172
26405
|
const storyDir = contextStoryDir(projectDir, feature, storyId);
|
|
26173
26406
|
const manifestFiles = await _manifestStoreDeps.listManifestFiles(storyDir);
|
|
26174
26407
|
for (const fileName of manifestFiles) {
|
|
26175
|
-
const fullPath =
|
|
26408
|
+
const fullPath = join13(storyDir, fileName);
|
|
26176
26409
|
if (!await _manifestStoreDeps.fileExists(fullPath))
|
|
26177
26410
|
continue;
|
|
26178
26411
|
try {
|
|
@@ -26197,7 +26430,7 @@ var init_manifest_store = __esm(() => {
|
|
|
26197
26430
|
fileExists: (path2) => Bun.file(path2).exists(),
|
|
26198
26431
|
readFile: (path2) => Bun.file(path2).text(),
|
|
26199
26432
|
listFeatureDirs: async (projectDir) => {
|
|
26200
|
-
const baseDir =
|
|
26433
|
+
const baseDir = join13(projectDir, ".nax", "features");
|
|
26201
26434
|
try {
|
|
26202
26435
|
const dirs = [];
|
|
26203
26436
|
for await (const entry of new Bun.Glob("*").scan({ cwd: baseDir, absolute: false })) {
|
|
@@ -26224,7 +26457,7 @@ var init_manifest_store = __esm(() => {
|
|
|
26224
26457
|
|
|
26225
26458
|
// src/context/engine/stage-assembler.ts
|
|
26226
26459
|
import { readdir } from "fs/promises";
|
|
26227
|
-
import { isAbsolute as isAbsolute7, join as
|
|
26460
|
+
import { isAbsolute as isAbsolute7, join as join14, resolve as resolve9 } from "path";
|
|
26228
26461
|
function dedupeScratchDirs(dirs) {
|
|
26229
26462
|
return [...new Set(dirs.filter((dir) => Boolean(dir)))];
|
|
26230
26463
|
}
|
|
@@ -26233,7 +26466,7 @@ function toAbsolutePath2(projectDir, pathValue) {
|
|
|
26233
26466
|
}
|
|
26234
26467
|
async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId, ttlMs) {
|
|
26235
26468
|
const logger = getLogger();
|
|
26236
|
-
const sessionsRoot =
|
|
26469
|
+
const sessionsRoot = join14(projectDir, ".nax", "features", featureName, "sessions");
|
|
26237
26470
|
let entries;
|
|
26238
26471
|
try {
|
|
26239
26472
|
entries = await _stageAssemblerDeps.readdir(sessionsRoot);
|
|
@@ -26243,7 +26476,7 @@ async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId
|
|
|
26243
26476
|
const cutoff = _stageAssemblerDeps.now() - ttlMs;
|
|
26244
26477
|
const found = [];
|
|
26245
26478
|
for (const entry of entries) {
|
|
26246
|
-
const descriptorPath =
|
|
26479
|
+
const descriptorPath = join14(sessionsRoot, entry, "descriptor.json");
|
|
26247
26480
|
try {
|
|
26248
26481
|
const parsed = await _stageAssemblerDeps.readDescriptor(descriptorPath);
|
|
26249
26482
|
if (!parsed || parsed.storyId !== storyId || !parsed.scratchDir)
|
|
@@ -26394,7 +26627,7 @@ function createContextToolRuntime(options) {
|
|
|
26394
26627
|
switch (name) {
|
|
26395
26628
|
case "query_neighbor": {
|
|
26396
26629
|
const patterns = await getResolvedTestPatterns();
|
|
26397
|
-
return handleQueryNeighbor(input, repoRoot, getBudget(tool), tool.maxTokensPerCall, patterns);
|
|
26630
|
+
return handleQueryNeighbor(input, repoRoot, getBudget(tool), tool.maxTokensPerCall, patterns, story.id, config2.context?.v2?.providers);
|
|
26398
26631
|
}
|
|
26399
26632
|
case "query_feature_context":
|
|
26400
26633
|
return handleQueryFeatureContext(input, story, config2, repoRoot, getBudget(tool), tool.maxTokensPerCall);
|
|
@@ -26665,7 +26898,7 @@ function detectFramework(output) {
|
|
|
26665
26898
|
return "unknown";
|
|
26666
26899
|
}
|
|
26667
26900
|
var TEST_FILE_PATTERNS;
|
|
26668
|
-
var
|
|
26901
|
+
var init_detector2 = __esm(() => {
|
|
26669
26902
|
init_conventions();
|
|
26670
26903
|
TEST_FILE_PATTERNS = [
|
|
26671
26904
|
/(?:^|\/)(?:test|tests|__tests__|specs?)(?:\/|$)/,
|
|
@@ -26927,7 +27160,7 @@ function analyzeTestExitCode(output, exitCode) {
|
|
|
26927
27160
|
return result;
|
|
26928
27161
|
}
|
|
26929
27162
|
var init_parser = __esm(() => {
|
|
26930
|
-
|
|
27163
|
+
init_detector2();
|
|
26931
27164
|
});
|
|
26932
27165
|
|
|
26933
27166
|
// src/test-runners/ac-parser.ts
|
|
@@ -26986,14 +27219,14 @@ function parseTestFailures(output) {
|
|
|
26986
27219
|
return failedACs;
|
|
26987
27220
|
}
|
|
26988
27221
|
var init_ac_parser = __esm(() => {
|
|
26989
|
-
|
|
27222
|
+
init_detector2();
|
|
26990
27223
|
});
|
|
26991
27224
|
|
|
26992
27225
|
// src/test-runners/index.ts
|
|
26993
27226
|
var init_test_runners = __esm(() => {
|
|
26994
27227
|
init_conventions();
|
|
26995
27228
|
init_detect2();
|
|
26996
|
-
|
|
27229
|
+
init_detector2();
|
|
26997
27230
|
init_resolver();
|
|
26998
27231
|
init_parser();
|
|
26999
27232
|
init_ac_parser();
|
|
@@ -28120,13 +28353,13 @@ var exports_loader = {};
|
|
|
28120
28353
|
__export(exports_loader, {
|
|
28121
28354
|
loadOverride: () => loadOverride
|
|
28122
28355
|
});
|
|
28123
|
-
import { join as
|
|
28356
|
+
import { join as join15 } from "path";
|
|
28124
28357
|
async function loadOverride(role, workdir, config2) {
|
|
28125
28358
|
const overridePath = config2.prompts?.overrides?.[role];
|
|
28126
28359
|
if (!overridePath) {
|
|
28127
28360
|
return null;
|
|
28128
28361
|
}
|
|
28129
|
-
const absolutePath =
|
|
28362
|
+
const absolutePath = join15(workdir, overridePath);
|
|
28130
28363
|
const file3 = Bun.file(absolutePath);
|
|
28131
28364
|
if (!await file3.exists()) {
|
|
28132
28365
|
return null;
|
|
@@ -30703,6 +30936,17 @@ function resolveTimeoutMs(op, input, buildCtx) {
|
|
|
30703
30936
|
}
|
|
30704
30937
|
return timeoutMs;
|
|
30705
30938
|
}
|
|
30939
|
+
function resolveOpRetry(op, input, buildCtx) {
|
|
30940
|
+
if (!op.retry)
|
|
30941
|
+
return null;
|
|
30942
|
+
if (typeof op.retry === "function") {
|
|
30943
|
+
const preset = op.retry(input, buildCtx);
|
|
30944
|
+
return preset ? resolveRetryPreset(preset) : null;
|
|
30945
|
+
}
|
|
30946
|
+
if ("shouldRetry" in op.retry)
|
|
30947
|
+
return op.retry;
|
|
30948
|
+
return resolveRetryPreset(op.retry);
|
|
30949
|
+
}
|
|
30706
30950
|
function synthesizeStory(storyId) {
|
|
30707
30951
|
return {
|
|
30708
30952
|
id: storyId ?? "",
|
|
@@ -30733,7 +30977,7 @@ async function callOp(ctx, op, input) {
|
|
|
30733
30977
|
if (op.kind === "complete") {
|
|
30734
30978
|
const completeOp = op;
|
|
30735
30979
|
const sessionRole2 = ctx.sessionOverride?.role;
|
|
30736
|
-
const
|
|
30980
|
+
const completeOptions = {
|
|
30737
30981
|
modelDef: resolved.modelDef,
|
|
30738
30982
|
jsonMode: completeOp.jsonMode ?? false,
|
|
30739
30983
|
pipelineStage: op.stage,
|
|
@@ -30742,9 +30986,40 @@ async function callOp(ctx, op, input) {
|
|
|
30742
30986
|
featureName: ctx.featureName,
|
|
30743
30987
|
...sessionRole2 !== undefined ? { sessionRole: sessionRole2 } : {},
|
|
30744
30988
|
...timeoutMs !== undefined ? { timeoutMs } : {}
|
|
30745
|
-
}
|
|
30746
|
-
const
|
|
30747
|
-
|
|
30989
|
+
};
|
|
30990
|
+
const retryStrategy = resolveOpRetry(completeOp, input, buildCtx);
|
|
30991
|
+
let attempt = 0;
|
|
30992
|
+
while (attempt <= MAX_COMPLETE_RETRY_ATTEMPTS) {
|
|
30993
|
+
try {
|
|
30994
|
+
const raw = await ctx.runtime.agentManager.completeAs(dispatchAgent, prompt, completeOptions);
|
|
30995
|
+
const parsedComplete = op.parse(raw.output, input, buildCtx);
|
|
30996
|
+
return await runPostParse(op, parsedComplete, input, buildCtx);
|
|
30997
|
+
} catch (err) {
|
|
30998
|
+
if (!retryStrategy)
|
|
30999
|
+
throw err;
|
|
31000
|
+
const decision = retryStrategy.shouldRetry(err, attempt, {
|
|
31001
|
+
site: "complete",
|
|
31002
|
+
agentName: dispatchAgent,
|
|
31003
|
+
stage: op.stage,
|
|
31004
|
+
storyId: ctx.storyId
|
|
31005
|
+
});
|
|
31006
|
+
if (!decision.retry)
|
|
31007
|
+
throw err;
|
|
31008
|
+
if (ctx.runtime.signal?.aborted)
|
|
31009
|
+
throw err;
|
|
31010
|
+
getSafeLogger()?.warn("call-op", `LLM call failed (attempt ${attempt + 1}), retrying in ${decision.delayMs}ms`, {
|
|
31011
|
+
storyId: ctx.storyId,
|
|
31012
|
+
op: op.name,
|
|
31013
|
+
attempt,
|
|
31014
|
+
delayMs: decision.delayMs
|
|
31015
|
+
});
|
|
31016
|
+
await _callOpDeps.sleep(decision.delayMs, ctx.runtime.signal);
|
|
31017
|
+
if (ctx.runtime.signal?.aborted)
|
|
31018
|
+
throw err;
|
|
31019
|
+
attempt++;
|
|
31020
|
+
}
|
|
31021
|
+
}
|
|
31022
|
+
throw new NaxError(`callOp[${op.name}]: exceeded MAX_COMPLETE_RETRY_ATTEMPTS (${MAX_COMPLETE_RETRY_ATTEMPTS})`, "CALL_OP_MAX_RETRIES", { stage: op.stage, storyId: ctx.storyId });
|
|
30748
31023
|
}
|
|
30749
31024
|
const runOp = op;
|
|
30750
31025
|
const story = ctx.story ?? synthesizeStory(ctx.storyId);
|
|
@@ -30819,11 +31094,18 @@ async function runPostParse(op, parsed, input, buildCtx) {
|
|
|
30819
31094
|
}
|
|
30820
31095
|
return final ?? parsed;
|
|
30821
31096
|
}
|
|
31097
|
+
var _callOpDeps, MAX_COMPLETE_RETRY_ATTEMPTS = 20;
|
|
30822
31098
|
var init_call = __esm(() => {
|
|
31099
|
+
init_retry();
|
|
30823
31100
|
init_config();
|
|
30824
31101
|
init_errors();
|
|
31102
|
+
init_logger2();
|
|
30825
31103
|
init_compose();
|
|
31104
|
+
init_bun_deps();
|
|
30826
31105
|
init_build_hop_callback();
|
|
31106
|
+
_callOpDeps = {
|
|
31107
|
+
sleep: (ms, signal) => cancellableDelay(ms, signal)
|
|
31108
|
+
};
|
|
30827
31109
|
});
|
|
30828
31110
|
|
|
30829
31111
|
// src/prd/validate.ts
|
|
@@ -31507,24 +31789,6 @@ function keywordRoute(story, config2) {
|
|
|
31507
31789
|
const reasoning = reasons.length > 0 ? `${prefix}: ${reasons.join(", ")}` : `${prefix}: ${complexity} task`;
|
|
31508
31790
|
return { complexity, modelTier, testStrategy, reasoning };
|
|
31509
31791
|
}
|
|
31510
|
-
async function classifyWithRetry(ctx, op, input, opts) {
|
|
31511
|
-
let lastErr;
|
|
31512
|
-
for (let i = 0;i <= opts.retries; i++) {
|
|
31513
|
-
try {
|
|
31514
|
-
return await callOp(ctx, op, input);
|
|
31515
|
-
} catch (err) {
|
|
31516
|
-
lastErr = err;
|
|
31517
|
-
if (i < opts.retries) {
|
|
31518
|
-
const logger = getSafeLogger();
|
|
31519
|
-
logger?.warn("routing", `LLM call failed (attempt ${i + 1}/${opts.retries + 1}), retrying in ${opts.retryDelayMs}ms`, {
|
|
31520
|
-
error: lastErr.message
|
|
31521
|
-
});
|
|
31522
|
-
await Bun.sleep(opts.retryDelayMs);
|
|
31523
|
-
}
|
|
31524
|
-
}
|
|
31525
|
-
}
|
|
31526
|
-
throw lastErr ?? new Error("classifyWithRetry: unknown failure");
|
|
31527
|
-
}
|
|
31528
31792
|
async function resolveRouting(story, config2, plugins, dispatchContext) {
|
|
31529
31793
|
const logger = getSafeLogger();
|
|
31530
31794
|
const runtimeContext = dispatchContext;
|
|
@@ -31584,14 +31848,12 @@ async function resolveRouting(story, config2, plugins, dispatchContext) {
|
|
|
31584
31848
|
agentName: resolveDefaultAgent(runtime.configLoader.current()),
|
|
31585
31849
|
storyId: story.id
|
|
31586
31850
|
};
|
|
31587
|
-
const
|
|
31588
|
-
const retryDelayMs = llmConfig.retryDelayMs ?? 1000;
|
|
31589
|
-
const decision = await classifyWithRetry(ctx, classifyRouteOp, {
|
|
31851
|
+
const decision = await callOp(ctx, classifyRouteOp, {
|
|
31590
31852
|
title: story.title,
|
|
31591
31853
|
description: story.description,
|
|
31592
31854
|
acceptanceCriteria: story.acceptanceCriteria,
|
|
31593
31855
|
tags: story.tags
|
|
31594
|
-
}
|
|
31856
|
+
});
|
|
31595
31857
|
if (llmConfig.cacheDecisions) {
|
|
31596
31858
|
if (cachedDecisions.size >= MAX_CACHE_SIZE)
|
|
31597
31859
|
evictOldest();
|
|
@@ -31678,9 +31940,7 @@ async function tryLlmBatchRoute(config2, stories, label = "routing", _deps = _tr
|
|
|
31678
31940
|
packageDir: runtime.workdir,
|
|
31679
31941
|
agentName: resolveDefaultAgent(runtime.configLoader.current())
|
|
31680
31942
|
};
|
|
31681
|
-
const
|
|
31682
|
-
const retryDelayMs = llmConfig.retryDelayMs ?? 1000;
|
|
31683
|
-
const decisions = await classifyWithRetry(ctx, classifyRouteBatchOp, needsRouting, { retries, retryDelayMs });
|
|
31943
|
+
const decisions = await callOp(ctx, classifyRouteBatchOp, needsRouting);
|
|
31684
31944
|
if (llmConfig.cacheDecisions) {
|
|
31685
31945
|
for (const [storyId, decision] of decisions.entries()) {
|
|
31686
31946
|
if (cachedDecisions.size >= MAX_CACHE_SIZE)
|
|
@@ -31771,6 +32031,11 @@ var init_classify_route = __esm(() => {
|
|
|
31771
32031
|
jsonMode: true,
|
|
31772
32032
|
config: routingConfigSelector,
|
|
31773
32033
|
model: (_input, ctx) => ctx.config.routing.llm?.model ?? "balanced",
|
|
32034
|
+
retry: (_input, ctx) => ({
|
|
32035
|
+
preset: "transient-network",
|
|
32036
|
+
maxAttempts: (ctx.config.routing.llm?.retries ?? 1) + 1,
|
|
32037
|
+
baseDelayMs: ctx.config.routing.llm?.retryDelayMs ?? 1000
|
|
32038
|
+
}),
|
|
31774
32039
|
build(input, _ctx) {
|
|
31775
32040
|
const criteria = input.acceptanceCriteria.map((c, i) => `${i + 1}. ${c}`).join(`
|
|
31776
32041
|
`);
|
|
@@ -31803,6 +32068,11 @@ ${storyBody}`, overridable: false }
|
|
|
31803
32068
|
jsonMode: true,
|
|
31804
32069
|
config: routingConfigSelector,
|
|
31805
32070
|
model: (_input, ctx) => ctx.config.routing.llm?.model ?? "balanced",
|
|
32071
|
+
retry: (_input, ctx) => ({
|
|
32072
|
+
preset: "transient-network",
|
|
32073
|
+
maxAttempts: (ctx.config.routing.llm?.retries ?? 1) + 1,
|
|
32074
|
+
baseDelayMs: ctx.config.routing.llm?.retryDelayMs ?? 1000
|
|
32075
|
+
}),
|
|
31806
32076
|
build(input, _ctx) {
|
|
31807
32077
|
const storyBlocks = input.map((story, idx) => {
|
|
31808
32078
|
const criteria = story.acceptanceCriteria.map((c, i) => ` ${i + 1}. ${c}`).join(`
|
|
@@ -33760,10 +34030,10 @@ var init_plan_helpers = __esm(() => {
|
|
|
33760
34030
|
|
|
33761
34031
|
// src/analyze/scanner.ts
|
|
33762
34032
|
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
33763
|
-
import { join as
|
|
34033
|
+
import { join as join16 } from "path";
|
|
33764
34034
|
async function scanCodebase(workdir) {
|
|
33765
|
-
const srcPath =
|
|
33766
|
-
const packageJsonPath =
|
|
34035
|
+
const srcPath = join16(workdir, "src");
|
|
34036
|
+
const packageJsonPath = join16(workdir, "package.json");
|
|
33767
34037
|
const fileTree = existsSync5(srcPath) ? await generateFileTree(srcPath, 3) : "No src/ directory";
|
|
33768
34038
|
let dependencies = {};
|
|
33769
34039
|
let devDependencies = {};
|
|
@@ -33804,7 +34074,7 @@ async function generateFileTree(dir, maxDepth, currentDepth = 0, prefix = "") {
|
|
|
33804
34074
|
const isDir = dirent.isDirectory();
|
|
33805
34075
|
entries.push(`${prefix}${connector}${dirent.name}${isDir ? "/" : ""}`);
|
|
33806
34076
|
if (isDir) {
|
|
33807
|
-
const subtree = await generateFileTree(
|
|
34077
|
+
const subtree = await generateFileTree(join16(dir, dirent.name), maxDepth, currentDepth + 1, prefix + childPrefix);
|
|
33808
34078
|
if (subtree) {
|
|
33809
34079
|
entries.push(subtree);
|
|
33810
34080
|
}
|
|
@@ -33828,16 +34098,16 @@ function detectTestPatterns(workdir, dependencies, devDependencies) {
|
|
|
33828
34098
|
} else {
|
|
33829
34099
|
patterns.push("Test framework: likely bun:test (no framework dependency)");
|
|
33830
34100
|
}
|
|
33831
|
-
if (existsSync5(
|
|
34101
|
+
if (existsSync5(join16(workdir, "test"))) {
|
|
33832
34102
|
patterns.push("Test directory: test/");
|
|
33833
34103
|
}
|
|
33834
|
-
if (existsSync5(
|
|
34104
|
+
if (existsSync5(join16(workdir, "__tests__"))) {
|
|
33835
34105
|
patterns.push("Test directory: __tests__/");
|
|
33836
34106
|
}
|
|
33837
|
-
if (existsSync5(
|
|
34107
|
+
if (existsSync5(join16(workdir, "tests"))) {
|
|
33838
34108
|
patterns.push("Test directory: tests/");
|
|
33839
34109
|
}
|
|
33840
|
-
const hasTestFiles = existsSync5(
|
|
34110
|
+
const hasTestFiles = existsSync5(join16(workdir, "test")) || existsSync5(join16(workdir, "src"));
|
|
33841
34111
|
if (hasTestFiles) {
|
|
33842
34112
|
patterns.push("Test files: *.test.ts, *.spec.ts");
|
|
33843
34113
|
}
|
|
@@ -33847,9 +34117,9 @@ var init_scanner = () => {};
|
|
|
33847
34117
|
|
|
33848
34118
|
// src/context/injector.ts
|
|
33849
34119
|
import { existsSync as existsSync6 } from "fs";
|
|
33850
|
-
import { join as
|
|
34120
|
+
import { join as join17 } from "path";
|
|
33851
34121
|
async function detectNode(workdir) {
|
|
33852
|
-
const pkgPath =
|
|
34122
|
+
const pkgPath = join17(workdir, "package.json");
|
|
33853
34123
|
if (!existsSync6(pkgPath))
|
|
33854
34124
|
return null;
|
|
33855
34125
|
try {
|
|
@@ -33866,7 +34136,7 @@ async function detectNode(workdir) {
|
|
|
33866
34136
|
}
|
|
33867
34137
|
}
|
|
33868
34138
|
async function detectGo(workdir) {
|
|
33869
|
-
const goMod =
|
|
34139
|
+
const goMod = join17(workdir, "go.mod");
|
|
33870
34140
|
if (!existsSync6(goMod))
|
|
33871
34141
|
return null;
|
|
33872
34142
|
try {
|
|
@@ -33890,7 +34160,7 @@ async function detectGo(workdir) {
|
|
|
33890
34160
|
}
|
|
33891
34161
|
}
|
|
33892
34162
|
async function detectRust(workdir) {
|
|
33893
|
-
const cargoPath =
|
|
34163
|
+
const cargoPath = join17(workdir, "Cargo.toml");
|
|
33894
34164
|
if (!existsSync6(cargoPath))
|
|
33895
34165
|
return null;
|
|
33896
34166
|
try {
|
|
@@ -33906,8 +34176,8 @@ async function detectRust(workdir) {
|
|
|
33906
34176
|
}
|
|
33907
34177
|
}
|
|
33908
34178
|
async function detectPython(workdir) {
|
|
33909
|
-
const pyproject =
|
|
33910
|
-
const requirements =
|
|
34179
|
+
const pyproject = join17(workdir, "pyproject.toml");
|
|
34180
|
+
const requirements = join17(workdir, "requirements.txt");
|
|
33911
34181
|
if (!existsSync6(pyproject) && !existsSync6(requirements))
|
|
33912
34182
|
return null;
|
|
33913
34183
|
try {
|
|
@@ -33926,7 +34196,7 @@ async function detectPython(workdir) {
|
|
|
33926
34196
|
}
|
|
33927
34197
|
}
|
|
33928
34198
|
async function detectPhp(workdir) {
|
|
33929
|
-
const composerPath =
|
|
34199
|
+
const composerPath = join17(workdir, "composer.json");
|
|
33930
34200
|
if (!existsSync6(composerPath))
|
|
33931
34201
|
return null;
|
|
33932
34202
|
try {
|
|
@@ -33939,7 +34209,7 @@ async function detectPhp(workdir) {
|
|
|
33939
34209
|
}
|
|
33940
34210
|
}
|
|
33941
34211
|
async function detectRuby(workdir) {
|
|
33942
|
-
const gemfile =
|
|
34212
|
+
const gemfile = join17(workdir, "Gemfile");
|
|
33943
34213
|
if (!existsSync6(gemfile))
|
|
33944
34214
|
return null;
|
|
33945
34215
|
try {
|
|
@@ -33951,9 +34221,9 @@ async function detectRuby(workdir) {
|
|
|
33951
34221
|
}
|
|
33952
34222
|
}
|
|
33953
34223
|
async function detectJvm(workdir) {
|
|
33954
|
-
const pom =
|
|
33955
|
-
const gradle =
|
|
33956
|
-
const gradleKts =
|
|
34224
|
+
const pom = join17(workdir, "pom.xml");
|
|
34225
|
+
const gradle = join17(workdir, "build.gradle");
|
|
34226
|
+
const gradleKts = join17(workdir, "build.gradle.kts");
|
|
33957
34227
|
if (!existsSync6(pom) && !existsSync6(gradle) && !existsSync6(gradleKts))
|
|
33958
34228
|
return null;
|
|
33959
34229
|
try {
|
|
@@ -33961,7 +34231,7 @@ async function detectJvm(workdir) {
|
|
|
33961
34231
|
const content2 = await Bun.file(pom).text();
|
|
33962
34232
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
33963
34233
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
33964
|
-
const lang2 = existsSync6(
|
|
34234
|
+
const lang2 = existsSync6(join17(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
33965
34235
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
33966
34236
|
}
|
|
33967
34237
|
const gradleFile = existsSync6(gradleKts) ? gradleKts : gradle;
|
|
@@ -34215,7 +34485,7 @@ var init_windsurf = __esm(() => {
|
|
|
34215
34485
|
|
|
34216
34486
|
// src/context/generator.ts
|
|
34217
34487
|
import { existsSync as existsSync7 } from "fs";
|
|
34218
|
-
import { join as
|
|
34488
|
+
import { join as join18, relative as relative8 } from "path";
|
|
34219
34489
|
async function loadContextContent(options, config2) {
|
|
34220
34490
|
if (!_generatorDeps.existsSync(options.contextPath)) {
|
|
34221
34491
|
throw new Error(`Context file not found: ${options.contextPath}`);
|
|
@@ -34233,7 +34503,7 @@ async function generateFor(agent, options, config2) {
|
|
|
34233
34503
|
try {
|
|
34234
34504
|
const context = await loadContextContent(options, config2);
|
|
34235
34505
|
const content = generator.generate(context);
|
|
34236
|
-
const outputPath =
|
|
34506
|
+
const outputPath = join18(options.outputDir, generator.outputFile);
|
|
34237
34507
|
validateFilePath(outputPath, options.outputDir);
|
|
34238
34508
|
if (!options.dryRun) {
|
|
34239
34509
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -34251,7 +34521,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
34251
34521
|
for (const [agentKey, generator] of entries) {
|
|
34252
34522
|
try {
|
|
34253
34523
|
const content = generator.generate(context);
|
|
34254
|
-
const outputPath =
|
|
34524
|
+
const outputPath = join18(options.outputDir, generator.outputFile);
|
|
34255
34525
|
validateFilePath(outputPath, options.outputDir);
|
|
34256
34526
|
if (!options.dryRun) {
|
|
34257
34527
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -34271,7 +34541,7 @@ async function discoverPackages(repoRoot) {
|
|
|
34271
34541
|
const glob = new Bun.Glob(pattern);
|
|
34272
34542
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
34273
34543
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
34274
|
-
const pkgAbsolute =
|
|
34544
|
+
const pkgAbsolute = join18(repoRoot, pkgRelative);
|
|
34275
34545
|
if (!seen.has(pkgAbsolute)) {
|
|
34276
34546
|
seen.add(pkgAbsolute);
|
|
34277
34547
|
packages.push(pkgAbsolute);
|
|
@@ -34303,14 +34573,14 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
34303
34573
|
}
|
|
34304
34574
|
}
|
|
34305
34575
|
}
|
|
34306
|
-
const turboPath =
|
|
34576
|
+
const turboPath = join18(repoRoot, "turbo.json");
|
|
34307
34577
|
try {
|
|
34308
34578
|
const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
|
|
34309
34579
|
if (Array.isArray(turbo.packages)) {
|
|
34310
34580
|
await resolveGlobs(turbo.packages);
|
|
34311
34581
|
}
|
|
34312
34582
|
} catch {}
|
|
34313
|
-
const pkgPath =
|
|
34583
|
+
const pkgPath = join18(repoRoot, "package.json");
|
|
34314
34584
|
try {
|
|
34315
34585
|
const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
|
|
34316
34586
|
const ws = pkg.workspaces;
|
|
@@ -34318,7 +34588,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
34318
34588
|
if (patterns.length > 0)
|
|
34319
34589
|
await resolveGlobs(patterns);
|
|
34320
34590
|
} catch {}
|
|
34321
|
-
const pnpmPath =
|
|
34591
|
+
const pnpmPath = join18(repoRoot, "pnpm-workspace.yaml");
|
|
34322
34592
|
try {
|
|
34323
34593
|
const raw = await _generatorDeps.readTextFile(pnpmPath);
|
|
34324
34594
|
const lines = raw.split(`
|
|
@@ -34344,7 +34614,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
34344
34614
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
34345
34615
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
34346
34616
|
const relativePkgPath = relative8(resolvedRepoRoot, packageDir);
|
|
34347
|
-
const contextPath =
|
|
34617
|
+
const contextPath = join18(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
34348
34618
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
34349
34619
|
return [
|
|
34350
34620
|
{
|
|
@@ -34602,8 +34872,8 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
|
|
|
34602
34872
|
const agentName = resolverConfig.agent ?? RESOLVER_FALLBACK_AGENT;
|
|
34603
34873
|
const manager = agentManager ?? _debateSessionDeps.agentManager;
|
|
34604
34874
|
if (manager !== undefined && manager.getAgent(agentName) !== undefined) {
|
|
34605
|
-
const configModels = config2
|
|
34606
|
-
const configDefaultAgent = resolveDefaultAgent(config2
|
|
34875
|
+
const configModels = config2.models ?? DEFAULT_CONFIG.models;
|
|
34876
|
+
const configDefaultAgent = resolveDefaultAgent(config2);
|
|
34607
34877
|
const synthesisSessionName = workdir !== undefined ? formatSessionName({ workdir, featureName, storyId, role: "synthesis" }) : undefined;
|
|
34608
34878
|
const resolverDebater = { agent: agentName, model: resolverConfig.model };
|
|
34609
34879
|
const resolverSelection = { agent: agentName, model: resolverConfig.model ?? "fast" };
|
|
@@ -34646,8 +34916,8 @@ async function resolveOutcome(proposalOutputs, critiqueOutputs, stageConfig, con
|
|
|
34646
34916
|
if (!manager) {
|
|
34647
34917
|
return { outcome: "passed", resolverCostUsd: 0 };
|
|
34648
34918
|
}
|
|
34649
|
-
const configModels = config2
|
|
34650
|
-
const configDefaultAgent = resolveDefaultAgent(config2
|
|
34919
|
+
const configModels = config2.models ?? DEFAULT_CONFIG.models;
|
|
34920
|
+
const configDefaultAgent = resolveDefaultAgent(config2);
|
|
34651
34921
|
const judgeSessionName = workdir !== undefined ? formatSessionName({ workdir, featureName, storyId, role: "judge" }) : undefined;
|
|
34652
34922
|
const resolverDebater = { agent: agentName, model: resolverConfig.model };
|
|
34653
34923
|
const resolverSelection = { agent: agentName, model: resolverConfig.model ?? "fast" };
|
|
@@ -34879,7 +35149,7 @@ async function runStateful(ctx, prompt) {
|
|
|
34879
35149
|
output: s.output
|
|
34880
35150
|
}))
|
|
34881
35151
|
} : undefined;
|
|
34882
|
-
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.
|
|
35152
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, ctx.reviewerSession, fullResolverContext, undefined, successfulProposals.map((s) => s.debater), agentManager);
|
|
34883
35153
|
totalCostUsd += outcome.resolverCostUsd;
|
|
34884
35154
|
const proposals = successfulProposals.map((s) => ({
|
|
34885
35155
|
debater: s.debater,
|
|
@@ -35128,7 +35398,7 @@ async function runHybrid(ctx, prompt) {
|
|
|
35128
35398
|
output: s.output
|
|
35129
35399
|
}))
|
|
35130
35400
|
} : undefined;
|
|
35131
|
-
const resolveResult = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.
|
|
35401
|
+
const resolveResult = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, ctx.timeoutSeconds * 1000, ctx.workdir, ctx.featureName, ctx.reviewerSession, fullResolverContext, undefined, successfulProposals.map((s) => s.debater), agentManager);
|
|
35132
35402
|
totalCostUsd += resolveResult.resolverCostUsd;
|
|
35133
35403
|
return {
|
|
35134
35404
|
storyId: ctx.storyId,
|
|
@@ -35160,7 +35430,7 @@ var init_runner_hybrid = __esm(() => {
|
|
|
35160
35430
|
});
|
|
35161
35431
|
|
|
35162
35432
|
// src/debate/runner-plan.ts
|
|
35163
|
-
import { join as
|
|
35433
|
+
import { join as join19 } from "path";
|
|
35164
35434
|
async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
35165
35435
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
35166
35436
|
const config2 = ctx.stageConfig;
|
|
@@ -35188,7 +35458,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
35188
35458
|
const concurrencyLimit = debate?.maxConcurrentDebaters ?? 2;
|
|
35189
35459
|
const proposalBuilder = new DebatePromptBuilder({ taskContext, outputFormat, stage: "plan" }, { debaters: resolved.map((r) => r.debater), sessionMode: ctx.stageConfig.sessionMode ?? "one-shot" });
|
|
35190
35460
|
const settled = await allSettledBounded(resolved.map(({ debater: rd, agentName }, i) => async () => {
|
|
35191
|
-
const tempOutputPath =
|
|
35461
|
+
const tempOutputPath = join19(opts.outputDir, `prd-debate-${i}.json`);
|
|
35192
35462
|
const debaterPrompt = `${proposalBuilder.buildProposalPrompt(i)}
|
|
35193
35463
|
|
|
35194
35464
|
Write the PRD JSON directly to this file path: ${tempOutputPath}
|
|
@@ -35287,7 +35557,6 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
35287
35557
|
stage: ctx.stage,
|
|
35288
35558
|
stageConfig: ctx.stageConfig,
|
|
35289
35559
|
config: ctx.config,
|
|
35290
|
-
completeConfig: ctx.completeConfig,
|
|
35291
35560
|
workdir: opts.workdir,
|
|
35292
35561
|
featureName: opts.feature,
|
|
35293
35562
|
timeoutSeconds: opts.timeoutSeconds ?? 600,
|
|
@@ -35328,7 +35597,7 @@ The spec above is the authoritative source for acceptance criteria.
|
|
|
35328
35597
|
- Preserve the spec's AC wording. You may refine for clarity but must not change semantics.
|
|
35329
35598
|
- Preserve each story's \`routing\` object unchanged \u2014 especially \`routing.complexity\` and \`routing.testStrategy\`. These are required by the schema and must not be dropped or modified during synthesis.` : "";
|
|
35330
35599
|
const planSynthesisSuffix = `IMPORTANT: Your response must be a single valid JSON object in PRD format (with project, feature, branchName, userStories array, etc.). Do NOT wrap it in markdown fences. Output raw JSON only.${specAnchor}`;
|
|
35331
|
-
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.
|
|
35600
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, ctx.stageConfig, ctx.config, ctx.storyId, resolverTimeoutMs, opts.workdir, opts.feature, undefined, undefined, planSynthesisSuffix, successful.map((p) => p.debater), agentManager);
|
|
35332
35601
|
const winningOutput = outcome.output ?? successful[0].output;
|
|
35333
35602
|
const proposals = successful.map((p) => ({ debater: p.debater, output: p.output }));
|
|
35334
35603
|
logger?.info("debate", "debate:result", {
|
|
@@ -35364,7 +35633,6 @@ class DebateRunner {
|
|
|
35364
35633
|
stage;
|
|
35365
35634
|
stageConfig;
|
|
35366
35635
|
config;
|
|
35367
|
-
completeConfig;
|
|
35368
35636
|
workdir;
|
|
35369
35637
|
featureName;
|
|
35370
35638
|
timeoutSeconds;
|
|
@@ -35376,7 +35644,6 @@ class DebateRunner {
|
|
|
35376
35644
|
this.stage = opts.stage;
|
|
35377
35645
|
this.stageConfig = opts.stageConfig;
|
|
35378
35646
|
this.config = opts.config ?? debateConfigSelector.select(DEFAULT_CONFIG);
|
|
35379
|
-
this.completeConfig = opts.config;
|
|
35380
35647
|
this.workdir = opts.workdir ?? opts.ctx.packageDir;
|
|
35381
35648
|
this.featureName = opts.featureName ?? opts.stage;
|
|
35382
35649
|
this.timeoutSeconds = opts.timeoutSeconds ?? opts.stageConfig.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
|
|
@@ -35518,7 +35785,7 @@ class DebateRunner {
|
|
|
35518
35785
|
output: s.output
|
|
35519
35786
|
}))
|
|
35520
35787
|
} : undefined;
|
|
35521
|
-
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, this.stageConfig, this.
|
|
35788
|
+
const outcome = await resolveOutcome(proposalOutputs, critiqueOutputs, this.stageConfig, this.config, this.ctx.storyId ?? "", this.timeoutSeconds * 1000, this.workdir, this.featureName, this.reviewerSession, fullResolverContext, undefined, successful.map((s) => s.debater), agentManager);
|
|
35522
35789
|
totalCostUsd += outcome.resolverCostUsd;
|
|
35523
35790
|
const proposals = successful.map((p) => ({ debater: p.debater, output: p.output }));
|
|
35524
35791
|
logger?.info("debate", "debate:result", { storyId: this.ctx.storyId, stage: this.stage, outcome: outcome.outcome });
|
|
@@ -35539,7 +35806,6 @@ class DebateRunner {
|
|
|
35539
35806
|
stage: this.stage,
|
|
35540
35807
|
stageConfig: this.stageConfig,
|
|
35541
35808
|
config: this.config,
|
|
35542
|
-
completeConfig: this.completeConfig,
|
|
35543
35809
|
workdir: this.workdir,
|
|
35544
35810
|
featureName: this.featureName,
|
|
35545
35811
|
timeoutSeconds: this.timeoutSeconds,
|
|
@@ -35557,7 +35823,6 @@ class DebateRunner {
|
|
|
35557
35823
|
stage: this.stage,
|
|
35558
35824
|
stageConfig: this.stageConfig,
|
|
35559
35825
|
config: this.config,
|
|
35560
|
-
completeConfig: this.completeConfig,
|
|
35561
35826
|
agentManager: this.ctx.runtime.agentManager,
|
|
35562
35827
|
sessionManager: this.sessionManager ?? this.ctx.runtime.sessionManager,
|
|
35563
35828
|
runtime: this.ctx.runtime,
|
|
@@ -36824,7 +37089,7 @@ var init_init = __esm(() => {
|
|
|
36824
37089
|
|
|
36825
37090
|
// src/runtime/cost-aggregator.ts
|
|
36826
37091
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
36827
|
-
import { join as
|
|
37092
|
+
import { join as join20 } from "path";
|
|
36828
37093
|
function createNoOpCostAggregator() {
|
|
36829
37094
|
return {
|
|
36830
37095
|
record() {},
|
|
@@ -36939,7 +37204,7 @@ class CostAggregator {
|
|
|
36939
37204
|
if (events.length === 0 && errors3.length === 0)
|
|
36940
37205
|
return;
|
|
36941
37206
|
mkdirSync2(this._drainDir, { recursive: true });
|
|
36942
|
-
const path4 =
|
|
37207
|
+
const path4 = join20(this._drainDir, `${this._runId}.jsonl`);
|
|
36943
37208
|
const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
|
|
36944
37209
|
await _costAggDeps.write(path4, `${sorted.map((e) => JSON.stringify(e)).join(`
|
|
36945
37210
|
`)}
|
|
@@ -36975,7 +37240,7 @@ var init_cost_aggregator = __esm(() => {
|
|
|
36975
37240
|
// src/runtime/prompt-auditor.ts
|
|
36976
37241
|
import { appendFileSync } from "fs";
|
|
36977
37242
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
36978
|
-
import { join as
|
|
37243
|
+
import { join as join21 } from "path";
|
|
36979
37244
|
function createNoOpPromptAuditor() {
|
|
36980
37245
|
return {
|
|
36981
37246
|
record() {},
|
|
@@ -37041,8 +37306,8 @@ class PromptAuditor {
|
|
|
37041
37306
|
_jsonlPath;
|
|
37042
37307
|
_featureDir;
|
|
37043
37308
|
constructor(runId, flushDir, featureName) {
|
|
37044
|
-
this._featureDir =
|
|
37045
|
-
this._jsonlPath =
|
|
37309
|
+
this._featureDir = join21(flushDir, featureName);
|
|
37310
|
+
this._jsonlPath = join21(this._featureDir, `${runId}.jsonl`);
|
|
37046
37311
|
}
|
|
37047
37312
|
record(entry) {
|
|
37048
37313
|
this._enqueue(entry);
|
|
@@ -37091,7 +37356,7 @@ class PromptAuditor {
|
|
|
37091
37356
|
const auditEntry = entry;
|
|
37092
37357
|
const filename = deriveTxtFilename(auditEntry);
|
|
37093
37358
|
try {
|
|
37094
|
-
await _promptAuditorDeps.write(
|
|
37359
|
+
await _promptAuditorDeps.write(join21(this._featureDir, filename), buildTxtContent(auditEntry));
|
|
37095
37360
|
} catch (err) {
|
|
37096
37361
|
throw tagAuditError(err, "txt");
|
|
37097
37362
|
}
|
|
@@ -37112,11 +37377,11 @@ var init_prompt_auditor = __esm(() => {
|
|
|
37112
37377
|
});
|
|
37113
37378
|
|
|
37114
37379
|
// src/utils/nax-project-root.ts
|
|
37115
|
-
import { dirname as dirname6, join as
|
|
37380
|
+
import { dirname as dirname6, join as join22, resolve as resolve11 } from "path";
|
|
37116
37381
|
async function findNaxProjectRoot(startDir) {
|
|
37117
37382
|
let dir = resolve11(startDir);
|
|
37118
37383
|
for (let depth = 0;depth < MAX_NAX_WALK_DEPTH; depth++) {
|
|
37119
|
-
if (await _naxProjectRootDeps.exists(
|
|
37384
|
+
if (await _naxProjectRootDeps.exists(join22(dir, ".nax", "config.json"))) {
|
|
37120
37385
|
return dir;
|
|
37121
37386
|
}
|
|
37122
37387
|
const parent = dirname6(dir);
|
|
@@ -37137,7 +37402,7 @@ var init_nax_project_root = __esm(() => {
|
|
|
37137
37402
|
|
|
37138
37403
|
// src/review/review-audit.ts
|
|
37139
37404
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
37140
|
-
import { join as
|
|
37405
|
+
import { join as join23 } from "path";
|
|
37141
37406
|
function auditKey(reviewer, storyId) {
|
|
37142
37407
|
return `${reviewer}:${storyId ?? "_feature"}`;
|
|
37143
37408
|
}
|
|
@@ -37167,15 +37432,15 @@ function toPersistedEntry(entry, epochMs) {
|
|
|
37167
37432
|
async function persistReviewAudit(entry) {
|
|
37168
37433
|
let resolvedDir;
|
|
37169
37434
|
if (entry.outputDir) {
|
|
37170
|
-
resolvedDir =
|
|
37435
|
+
resolvedDir = join23(entry.outputDir, "review-audit", entry.featureName ?? "_unknown");
|
|
37171
37436
|
} else {
|
|
37172
37437
|
const projectRoot = entry.projectDir ?? await _reviewAuditDeps.findNaxProjectRoot(entry.workdir);
|
|
37173
|
-
resolvedDir =
|
|
37438
|
+
resolvedDir = join23(projectRoot, ".nax", "review-audit", entry.featureName ?? "_unknown");
|
|
37174
37439
|
}
|
|
37175
37440
|
await _reviewAuditDeps.mkdir(resolvedDir);
|
|
37176
37441
|
const epochMs = _reviewAuditDeps.now();
|
|
37177
37442
|
const filename = `${epochMs}-${entry.sessionName}.json`;
|
|
37178
|
-
await _reviewAuditDeps.writeFile(
|
|
37443
|
+
await _reviewAuditDeps.writeFile(join23(resolvedDir, filename), toPersistedEntry(entry, epochMs));
|
|
37179
37444
|
}
|
|
37180
37445
|
function createNoOpReviewAuditor() {
|
|
37181
37446
|
return {
|
|
@@ -37568,7 +37833,7 @@ var init_pid_registry = __esm(() => {
|
|
|
37568
37833
|
// src/session/manager-deps.ts
|
|
37569
37834
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
37570
37835
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
37571
|
-
import { isAbsolute as isAbsolute8, join as
|
|
37836
|
+
import { isAbsolute as isAbsolute8, join as join24, relative as relative9, sep } from "path";
|
|
37572
37837
|
function resolveProjectDirFromScratchDir(scratchDir) {
|
|
37573
37838
|
const marker = `${sep}.nax${sep}features${sep}`;
|
|
37574
37839
|
const markerIdx = scratchDir.lastIndexOf(marker);
|
|
@@ -37589,7 +37854,7 @@ var init_manager_deps = __esm(() => {
|
|
|
37589
37854
|
now: () => new Date().toISOString(),
|
|
37590
37855
|
nowMs: () => Date.now(),
|
|
37591
37856
|
uuid: () => randomUUID2(),
|
|
37592
|
-
sessionScratchDir: (projectDir, featureName, sessionId) =>
|
|
37857
|
+
sessionScratchDir: (projectDir, featureName, sessionId) => join24(projectDir, ".nax", "features", featureName, "sessions", sessionId),
|
|
37593
37858
|
writeDescriptor: async (scratchDir, descriptor, projectDir) => {
|
|
37594
37859
|
await mkdir5(scratchDir, { recursive: true });
|
|
37595
37860
|
const { handle: _handle, ...persistable } = descriptor;
|
|
@@ -37600,7 +37865,7 @@ var init_manager_deps = __esm(() => {
|
|
|
37600
37865
|
persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
|
|
37601
37866
|
}
|
|
37602
37867
|
}
|
|
37603
|
-
await Bun.write(
|
|
37868
|
+
await Bun.write(join24(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
|
|
37604
37869
|
}
|
|
37605
37870
|
};
|
|
37606
37871
|
});
|
|
@@ -38521,7 +38786,7 @@ __export(exports_runtime, {
|
|
|
38521
38786
|
DispatchEventBus: () => DispatchEventBus,
|
|
38522
38787
|
CostAggregator: () => CostAggregator
|
|
38523
38788
|
});
|
|
38524
|
-
import { basename as basename5, join as
|
|
38789
|
+
import { basename as basename5, join as join25 } from "path";
|
|
38525
38790
|
function createRuntime(config2, workdir, opts) {
|
|
38526
38791
|
const runId = crypto.randomUUID();
|
|
38527
38792
|
const controller = new AbortController;
|
|
@@ -38534,10 +38799,10 @@ function createRuntime(config2, workdir, opts) {
|
|
|
38534
38799
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
38535
38800
|
const globalDir = globalOutputDir();
|
|
38536
38801
|
const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
|
|
38537
|
-
const costDir =
|
|
38802
|
+
const costDir = join25(outputDir, "cost");
|
|
38538
38803
|
const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
|
|
38539
38804
|
const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
|
|
38540
|
-
const auditDir = config2.agent?.promptAudit?.dir ??
|
|
38805
|
+
const auditDir = config2.agent?.promptAudit?.dir ?? join25(outputDir, "prompt-audit");
|
|
38541
38806
|
let promptAuditor;
|
|
38542
38807
|
if (opts?.promptAuditor) {
|
|
38543
38808
|
promptAuditor = opts.promptAuditor;
|
|
@@ -39651,9 +39916,9 @@ __export(exports_plan_decompose, {
|
|
|
39651
39916
|
runReplanLoop: () => runReplanLoop,
|
|
39652
39917
|
planDecomposeCommand: () => planDecomposeCommand
|
|
39653
39918
|
});
|
|
39654
|
-
import { join as
|
|
39919
|
+
import { join as join26 } from "path";
|
|
39655
39920
|
async function planDecomposeCommand(workdir, config2, options) {
|
|
39656
|
-
const prdPath =
|
|
39921
|
+
const prdPath = join26(workdir, ".nax", "features", options.feature, "prd.json");
|
|
39657
39922
|
if (!_planDeps.existsSync(prdPath)) {
|
|
39658
39923
|
throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
|
|
39659
39924
|
stage: "decompose",
|
|
@@ -39827,7 +40092,7 @@ var init_plan_decompose = __esm(() => {
|
|
|
39827
40092
|
|
|
39828
40093
|
// src/cli/plan-runtime.ts
|
|
39829
40094
|
import { existsSync as existsSync13 } from "fs";
|
|
39830
|
-
import { join as
|
|
40095
|
+
import { join as join27 } from "path";
|
|
39831
40096
|
function isRuntimeWithAgentManager(value) {
|
|
39832
40097
|
return typeof value === "object" && value !== null && "agentManager" in value;
|
|
39833
40098
|
}
|
|
@@ -39867,7 +40132,7 @@ var init_plan_runtime = __esm(() => {
|
|
|
39867
40132
|
writeFile: (path5, content) => Bun.write(path5, content).then(() => {}),
|
|
39868
40133
|
scanCodebase: (workdir) => scanCodebase(workdir),
|
|
39869
40134
|
createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
|
|
39870
|
-
readPackageJson: (workdir) => Bun.file(
|
|
40135
|
+
readPackageJson: (workdir) => Bun.file(join27(workdir, "package.json")).json().catch(() => null),
|
|
39871
40136
|
spawnSync: (cmd, opts) => {
|
|
39872
40137
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
39873
40138
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
@@ -40255,7 +40520,7 @@ var init_metrics = __esm(() => {
|
|
|
40255
40520
|
|
|
40256
40521
|
// src/commands/common.ts
|
|
40257
40522
|
import { existsSync as existsSync15, readdirSync as readdirSync3, realpathSync as realpathSync3 } from "fs";
|
|
40258
|
-
import { join as
|
|
40523
|
+
import { join as join29, resolve as resolve12 } from "path";
|
|
40259
40524
|
function resolveProject(options = {}) {
|
|
40260
40525
|
const { dir, feature } = options;
|
|
40261
40526
|
let projectRoot;
|
|
@@ -40263,12 +40528,12 @@ function resolveProject(options = {}) {
|
|
|
40263
40528
|
let configPath;
|
|
40264
40529
|
if (dir) {
|
|
40265
40530
|
projectRoot = realpathSync3(resolve12(dir));
|
|
40266
|
-
naxDir =
|
|
40531
|
+
naxDir = join29(projectRoot, ".nax");
|
|
40267
40532
|
if (!existsSync15(naxDir)) {
|
|
40268
40533
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
40269
40534
|
Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
|
|
40270
40535
|
}
|
|
40271
|
-
configPath =
|
|
40536
|
+
configPath = join29(naxDir, "config.json");
|
|
40272
40537
|
if (!existsSync15(configPath)) {
|
|
40273
40538
|
throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
|
|
40274
40539
|
Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
@@ -40276,22 +40541,22 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
|
40276
40541
|
} else {
|
|
40277
40542
|
const found = findProjectRoot(process.cwd());
|
|
40278
40543
|
if (!found) {
|
|
40279
|
-
const cwdNaxDir =
|
|
40544
|
+
const cwdNaxDir = join29(process.cwd(), ".nax");
|
|
40280
40545
|
if (existsSync15(cwdNaxDir)) {
|
|
40281
|
-
const cwdConfigPath =
|
|
40546
|
+
const cwdConfigPath = join29(cwdNaxDir, "config.json");
|
|
40282
40547
|
throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
|
|
40283
40548
|
Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
|
|
40284
40549
|
}
|
|
40285
40550
|
throw new NaxError("No nax project found. Run this command from within a nax project directory, or use -d flag to specify the project path.", "PROJECT_NOT_FOUND", { cwd: process.cwd() });
|
|
40286
40551
|
}
|
|
40287
40552
|
projectRoot = found;
|
|
40288
|
-
naxDir =
|
|
40289
|
-
configPath =
|
|
40553
|
+
naxDir = join29(projectRoot, ".nax");
|
|
40554
|
+
configPath = join29(naxDir, "config.json");
|
|
40290
40555
|
}
|
|
40291
40556
|
let featureDir;
|
|
40292
40557
|
if (feature) {
|
|
40293
|
-
const featuresDir =
|
|
40294
|
-
featureDir =
|
|
40558
|
+
const featuresDir = join29(naxDir, "features");
|
|
40559
|
+
featureDir = join29(featuresDir, feature);
|
|
40295
40560
|
if (!existsSync15(featureDir)) {
|
|
40296
40561
|
const availableFeatures = existsSync15(featuresDir) ? readdirSync3(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
|
|
40297
40562
|
const availableMsg = availableFeatures.length > 0 ? `
|
|
@@ -40318,12 +40583,12 @@ function findProjectRoot(startDir) {
|
|
|
40318
40583
|
let current = resolve12(startDir);
|
|
40319
40584
|
let depth = 0;
|
|
40320
40585
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
40321
|
-
const naxDir =
|
|
40322
|
-
const configPath =
|
|
40586
|
+
const naxDir = join29(current, ".nax");
|
|
40587
|
+
const configPath = join29(naxDir, "config.json");
|
|
40323
40588
|
if (existsSync15(configPath)) {
|
|
40324
40589
|
return realpathSync3(current);
|
|
40325
40590
|
}
|
|
40326
|
-
const parent =
|
|
40591
|
+
const parent = join29(current, "..");
|
|
40327
40592
|
if (parent === current) {
|
|
40328
40593
|
break;
|
|
40329
40594
|
}
|
|
@@ -41678,13 +41943,13 @@ var init_executor = __esm(() => {
|
|
|
41678
41943
|
|
|
41679
41944
|
// src/verification/runners.ts
|
|
41680
41945
|
import { existsSync as existsSync18 } from "fs";
|
|
41681
|
-
import { join as
|
|
41946
|
+
import { join as join33 } from "path";
|
|
41682
41947
|
async function verifyAssets(workingDirectory, expectedFiles) {
|
|
41683
41948
|
if (!expectedFiles || expectedFiles.length === 0)
|
|
41684
41949
|
return { success: true, missingFiles: [] };
|
|
41685
41950
|
const missingFiles = [];
|
|
41686
41951
|
for (const file3 of expectedFiles) {
|
|
41687
|
-
if (!existsSync18(
|
|
41952
|
+
if (!existsSync18(join33(workingDirectory, file3)))
|
|
41688
41953
|
missingFiles.push(file3);
|
|
41689
41954
|
}
|
|
41690
41955
|
if (missingFiles.length > 0) {
|
|
@@ -41776,7 +42041,7 @@ var init_runners = __esm(() => {
|
|
|
41776
42041
|
});
|
|
41777
42042
|
|
|
41778
42043
|
// src/verification/smart-runner.ts
|
|
41779
|
-
import { join as
|
|
42044
|
+
import { join as join34, relative as relative10 } from "path";
|
|
41780
42045
|
function extractPatternSuffix(pattern) {
|
|
41781
42046
|
const lastStar = pattern.lastIndexOf("*");
|
|
41782
42047
|
if (lastStar === -1)
|
|
@@ -41879,7 +42144,7 @@ async function getChangedNonTestFiles(workdir, baseRef, packagePrefix, testFileR
|
|
|
41879
42144
|
const lines = stdout.trim().split(`
|
|
41880
42145
|
`).filter(Boolean);
|
|
41881
42146
|
const effectiveRepoRoot = repoRoot ?? workdir;
|
|
41882
|
-
const packageDir = packagePrefix ?
|
|
42147
|
+
const packageDir = packagePrefix ? join34(effectiveRepoRoot, packagePrefix) : undefined;
|
|
41883
42148
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(effectiveRepoRoot, packageDir);
|
|
41884
42149
|
let effectivePrefix = packagePrefix;
|
|
41885
42150
|
if (packagePrefix && repoRoot) {
|
|
@@ -41908,7 +42173,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
41908
42173
|
return [];
|
|
41909
42174
|
const lines = stdout.trim().split(`
|
|
41910
42175
|
`).filter(Boolean);
|
|
41911
|
-
const packageDir = packagePrefix ?
|
|
42176
|
+
const packageDir = packagePrefix ? join34(repoRoot, packagePrefix) : undefined;
|
|
41912
42177
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
|
|
41913
42178
|
const gitRoot = await _gitUtilDeps.getGitRoot(workdir);
|
|
41914
42179
|
const extraPrefix = gitRoot && gitRoot !== repoRoot ? relative10(gitRoot, repoRoot) : "";
|
|
@@ -41916,7 +42181,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
41916
42181
|
const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
|
|
41917
42182
|
const scoped = filterNaxInternalPaths(scopedRaw, ignoreMatchers);
|
|
41918
42183
|
const stripped = extraPrefix ? scoped.map((f) => f.slice(`${extraPrefix}/`.length)) : scoped;
|
|
41919
|
-
return stripped.filter((f) => testFileRegex.some((re) => re.test(f))).map((f) =>
|
|
42184
|
+
return stripped.filter((f) => testFileRegex.some((re) => re.test(f))).map((f) => join34(repoRoot, f));
|
|
41920
42185
|
} catch {
|
|
41921
42186
|
return [];
|
|
41922
42187
|
}
|
|
@@ -42081,7 +42346,7 @@ var init_scoped = __esm(() => {
|
|
|
42081
42346
|
});
|
|
42082
42347
|
|
|
42083
42348
|
// src/quality/command-resolver.ts
|
|
42084
|
-
import { join as
|
|
42349
|
+
import { join as join35 } from "path";
|
|
42085
42350
|
async function resolveQualityTestCommands(config2, workdir, storyWorkdir) {
|
|
42086
42351
|
const rawTestCommand = config2.review?.commands?.test ?? config2.quality?.commands?.test;
|
|
42087
42352
|
const rawScopedTemplate = config2.quality?.commands?.testScoped;
|
|
@@ -42108,7 +42373,7 @@ var init_command_resolver = __esm(() => {
|
|
|
42108
42373
|
_commandResolverDeps = {
|
|
42109
42374
|
readPackageName: async (dir) => {
|
|
42110
42375
|
try {
|
|
42111
|
-
const content = await Bun.file(
|
|
42376
|
+
const content = await Bun.file(join35(dir, "package.json")).json();
|
|
42112
42377
|
return typeof content.name === "string" ? content.name : null;
|
|
42113
42378
|
} catch {
|
|
42114
42379
|
return null;
|
|
@@ -42197,7 +42462,7 @@ var init_event_bus = __esm(() => {
|
|
|
42197
42462
|
});
|
|
42198
42463
|
|
|
42199
42464
|
// src/pipeline/stages/autofix-cycle.ts
|
|
42200
|
-
import { join as
|
|
42465
|
+
import { join as join36 } from "path";
|
|
42201
42466
|
function fixCallCtx(ctx) {
|
|
42202
42467
|
const packageView = ctx.packageView ?? ctx.runtime.packages.repo();
|
|
42203
42468
|
return {
|
|
@@ -42299,7 +42564,7 @@ ${lines.join(`
|
|
|
42299
42564
|
}
|
|
42300
42565
|
async function writeShadowReport(ctx, result, initialFindingsCount) {
|
|
42301
42566
|
const logger = getLogger();
|
|
42302
|
-
const shadowDir =
|
|
42567
|
+
const shadowDir = join36(ctx.runtime.outputDir, "cycle-shadow", ctx.story.id);
|
|
42303
42568
|
const timestamp = new Date().toISOString().replace(/[:.]/g, "-");
|
|
42304
42569
|
const report = {
|
|
42305
42570
|
storyId: ctx.story.id,
|
|
@@ -42311,7 +42576,7 @@ async function writeShadowReport(ctx, result, initialFindingsCount) {
|
|
|
42311
42576
|
...result.exhaustedStrategy ? { exhaustedStrategy: result.exhaustedStrategy } : {}
|
|
42312
42577
|
};
|
|
42313
42578
|
try {
|
|
42314
|
-
const file3 =
|
|
42579
|
+
const file3 = join36(shadowDir, `${timestamp}.json`);
|
|
42315
42580
|
await Bun.write(file3, JSON.stringify(report, null, 2));
|
|
42316
42581
|
} catch (err) {
|
|
42317
42582
|
logger.debug("autofix-cycle", "Shadow report write failed (non-fatal)", {
|
|
@@ -44966,16 +45231,16 @@ var init_runner4 = __esm(() => {
|
|
|
44966
45231
|
|
|
44967
45232
|
// src/review/verdict-writer.ts
|
|
44968
45233
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
44969
|
-
import { join as
|
|
45234
|
+
import { join as join37 } from "path";
|
|
44970
45235
|
async function writeReviewVerdict(entry) {
|
|
44971
45236
|
const logger = getSafeLogger();
|
|
44972
45237
|
try {
|
|
44973
|
-
const projectDir = await _verdictWriterDeps.findNaxProjectRoot(entry.featureName ?
|
|
45238
|
+
const projectDir = await _verdictWriterDeps.findNaxProjectRoot(entry.featureName ? join37(entry.featureName) : ".");
|
|
44974
45239
|
const baseDir = projectDir ?? ".";
|
|
44975
|
-
const verdictDir = entry.featureName ?
|
|
45240
|
+
const verdictDir = entry.featureName ? join37(baseDir, ".nax", "review-verdicts", entry.featureName) : join37(baseDir, ".nax", "review-verdicts", "_unknown");
|
|
44976
45241
|
await _verdictWriterDeps.mkdir(verdictDir, { recursive: true });
|
|
44977
45242
|
const fileName = `${entry.storyId}.json`;
|
|
44978
|
-
const filePath =
|
|
45243
|
+
const filePath = join37(verdictDir, fileName);
|
|
44979
45244
|
await _verdictWriterDeps.writeFile(filePath, JSON.stringify(entry, null, 2));
|
|
44980
45245
|
logger?.debug("review", "Review verdict written", {
|
|
44981
45246
|
storyId: entry.storyId,
|
|
@@ -45000,7 +45265,7 @@ var init_verdict_writer = __esm(() => {
|
|
|
45000
45265
|
});
|
|
45001
45266
|
|
|
45002
45267
|
// src/review/orchestrator.ts
|
|
45003
|
-
import { join as
|
|
45268
|
+
import { join as join38 } from "path";
|
|
45004
45269
|
var {spawn: spawn4 } = globalThis.Bun;
|
|
45005
45270
|
async function getChangedFiles2(workdir, baseRef) {
|
|
45006
45271
|
try {
|
|
@@ -45323,7 +45588,7 @@ class ReviewOrchestrator {
|
|
|
45323
45588
|
const baseRef = storyGitRef ?? executionConfig?.storyGitRef;
|
|
45324
45589
|
const changedFiles = await getChangedFiles2(workdir, baseRef);
|
|
45325
45590
|
const repoRoot = projectDir ?? workdir;
|
|
45326
|
-
const packageDir = scopePrefix ?
|
|
45591
|
+
const packageDir = scopePrefix ? join38(repoRoot, scopePrefix) : undefined;
|
|
45327
45592
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
|
|
45328
45593
|
const visibleChangedFiles = filterNaxInternalPaths(changedFiles, ignoreMatchers);
|
|
45329
45594
|
const scopedFiles = scopePrefix ? visibleChangedFiles.filter((f) => f === scopePrefix || f.startsWith(`${scopePrefix}/`)) : visibleChangedFiles;
|
|
@@ -46042,10 +46307,10 @@ var init_effectiveness = __esm(() => {
|
|
|
46042
46307
|
|
|
46043
46308
|
// src/execution/progress.ts
|
|
46044
46309
|
import { appendFile as appendFile3, mkdir as mkdir7 } from "fs/promises";
|
|
46045
|
-
import { join as
|
|
46310
|
+
import { join as join39 } from "path";
|
|
46046
46311
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
46047
46312
|
await mkdir7(featureDir, { recursive: true });
|
|
46048
|
-
const progressPath =
|
|
46313
|
+
const progressPath = join39(featureDir, "progress.txt");
|
|
46049
46314
|
const timestamp = new Date().toISOString();
|
|
46050
46315
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
46051
46316
|
`;
|
|
@@ -46177,7 +46442,7 @@ var init_completion = __esm(() => {
|
|
|
46177
46442
|
|
|
46178
46443
|
// src/constitution/loader.ts
|
|
46179
46444
|
import { existsSync as existsSync19 } from "fs";
|
|
46180
|
-
import { join as
|
|
46445
|
+
import { join as join40 } from "path";
|
|
46181
46446
|
function truncateToTokens(text, maxTokens) {
|
|
46182
46447
|
const maxChars = maxTokens * 3;
|
|
46183
46448
|
if (text.length <= maxChars) {
|
|
@@ -46199,7 +46464,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
46199
46464
|
}
|
|
46200
46465
|
let combinedContent = "";
|
|
46201
46466
|
if (!config2.skipGlobal) {
|
|
46202
|
-
const globalPath =
|
|
46467
|
+
const globalPath = join40(globalConfigDir(), config2.path);
|
|
46203
46468
|
if (existsSync19(globalPath)) {
|
|
46204
46469
|
const validatedPath = validateFilePath(globalPath, globalConfigDir());
|
|
46205
46470
|
const globalFile = Bun.file(validatedPath);
|
|
@@ -46209,7 +46474,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
46209
46474
|
}
|
|
46210
46475
|
}
|
|
46211
46476
|
}
|
|
46212
|
-
const projectPath =
|
|
46477
|
+
const projectPath = join40(projectDir, config2.path);
|
|
46213
46478
|
if (existsSync19(projectPath)) {
|
|
46214
46479
|
const validatedPath = validateFilePath(projectPath, projectDir);
|
|
46215
46480
|
const projectFile = Bun.file(validatedPath);
|
|
@@ -46767,14 +47032,14 @@ async function closeAllRunSessions(sessionManager, agentGetFn) {
|
|
|
46767
47032
|
|
|
46768
47033
|
// src/context/greenfield.ts
|
|
46769
47034
|
import { readdir as readdir2 } from "fs/promises";
|
|
46770
|
-
import { join as
|
|
47035
|
+
import { join as join41 } from "path";
|
|
46771
47036
|
async function scanForTestFiles(dir, testPatterns, isRootCall = true) {
|
|
46772
47037
|
const results = [];
|
|
46773
47038
|
const ignoreDirs = new Set(["node_modules", "dist", "build", ".next", ".git"]);
|
|
46774
47039
|
try {
|
|
46775
47040
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
46776
47041
|
for (const entry of entries) {
|
|
46777
|
-
const fullPath =
|
|
47042
|
+
const fullPath = join41(dir, entry.name);
|
|
46778
47043
|
if (entry.isDirectory()) {
|
|
46779
47044
|
if (ignoreDirs.has(entry.name))
|
|
46780
47045
|
continue;
|
|
@@ -46900,8 +47165,9 @@ var UNMAPPED_FAILURE_OUTPUT_MAX_LINES = 200, UNMAPPED_FAILURE_OUTPUT_MAX_CHARS =
|
|
|
46900
47165
|
// src/tdd/rectification-gate.ts
|
|
46901
47166
|
async function runFullSuiteGate(story, config2, workdir, agentManager, implementerTier, lite, logger, featureName, projectDir, sessionManager, sessionId, runtime) {
|
|
46902
47167
|
const rectificationEnabled = config2.execution.rectification?.enabled ?? false;
|
|
46903
|
-
if (!rectificationEnabled)
|
|
46904
|
-
return { passed: false, cost: 0, fullSuiteGatePassed: false };
|
|
47168
|
+
if (!rectificationEnabled) {
|
|
47169
|
+
return { passed: false, cost: 0, fullSuiteGatePassed: false, status: "disabled" };
|
|
47170
|
+
}
|
|
46905
47171
|
const rectificationConfig = config2.execution.rectification;
|
|
46906
47172
|
const fullSuiteTimeout = rectificationConfig.fullSuiteTimeoutSeconds;
|
|
46907
47173
|
const { testCommand: resolvedTestCmd } = await _rectificationGateDeps.resolveTestCommands(config2, workdir, story.workdir);
|
|
@@ -46923,7 +47189,7 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
|
|
|
46923
47189
|
outputLength: fullSuiteResult.output.length,
|
|
46924
47190
|
outputTail: fullSuiteResult.output.slice(-200)
|
|
46925
47191
|
});
|
|
46926
|
-
return { passed: true, cost: 0, fullSuiteGatePassed: false };
|
|
47192
|
+
return { passed: true, cost: 0, fullSuiteGatePassed: false, status: "deferred-unattributable" };
|
|
46927
47193
|
}
|
|
46928
47194
|
return await runRectificationLoop(story, config2, workdir, agentManager, implementerTier, lite, logger, testSummary, rectificationConfig, effectiveTestCmd, fullSuiteTimeout, fullSuiteResult.output, featureName, projectDir, sessionManager, sessionId, runtime);
|
|
46929
47195
|
}
|
|
@@ -46933,7 +47199,7 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
|
|
|
46933
47199
|
exitCode: fullSuiteResult.exitCode,
|
|
46934
47200
|
passedTests: testSummary.passed
|
|
46935
47201
|
});
|
|
46936
|
-
return { passed: true, cost: 0, fullSuiteGatePassed: true };
|
|
47202
|
+
return { passed: true, cost: 0, fullSuiteGatePassed: true, status: "passed-with-nonzero-exit" };
|
|
46937
47203
|
}
|
|
46938
47204
|
logger.warn("tdd", "Full suite gate inconclusive \u2014 no test results parsed from output (possible crash/OOM)", {
|
|
46939
47205
|
storyId: story.id,
|
|
@@ -46941,17 +47207,17 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
|
|
|
46941
47207
|
outputLength: fullSuiteResult.output.length,
|
|
46942
47208
|
outputTail: fullSuiteResult.output.slice(-200)
|
|
46943
47209
|
});
|
|
46944
|
-
return { passed: false, cost: 0, fullSuiteGatePassed: false };
|
|
47210
|
+
return { passed: false, cost: 0, fullSuiteGatePassed: false, status: "inconclusive" };
|
|
46945
47211
|
}
|
|
46946
47212
|
if (fullSuitePassed) {
|
|
46947
47213
|
logger.info("tdd", "Full suite gate passed", { storyId: story.id });
|
|
46948
|
-
return { passed: true, cost: 0, fullSuiteGatePassed: true };
|
|
47214
|
+
return { passed: true, cost: 0, fullSuiteGatePassed: true, status: "passed" };
|
|
46949
47215
|
}
|
|
46950
47216
|
logger.warn("tdd", "Full suite gate execution failed (no output)", {
|
|
46951
47217
|
storyId: story.id,
|
|
46952
47218
|
exitCode: fullSuiteResult.exitCode
|
|
46953
47219
|
});
|
|
46954
|
-
return { passed: false, cost: 0, fullSuiteGatePassed: false };
|
|
47220
|
+
return { passed: false, cost: 0, fullSuiteGatePassed: false, status: "execution-failed" };
|
|
46955
47221
|
}
|
|
46956
47222
|
async function runRectificationLoop(story, config2, workdir, agentManager, implementerTier, lite, logger, testSummary, rectificationConfig, testCmd, fullSuiteTimeout, testOutput, featureName, projectDir, sessionManager, sessionId, runtime) {
|
|
46957
47223
|
logger.warn("tdd", "Full suite gate detected regressions", {
|
|
@@ -47125,13 +47391,19 @@ async function runRectificationLoop(story, config2, workdir, agentManager, imple
|
|
|
47125
47391
|
});
|
|
47126
47392
|
const fixed = outcome.outcome === "fixed";
|
|
47127
47393
|
if (fixed) {
|
|
47128
|
-
return { passed: true, cost: gateCostAccum, fullSuiteGatePassed: true };
|
|
47394
|
+
return { passed: true, cost: gateCostAccum, fullSuiteGatePassed: true, status: "passed" };
|
|
47129
47395
|
}
|
|
47130
47396
|
logger.warn("tdd", "[WARN] Full suite gate failed after rectification exhausted", {
|
|
47131
47397
|
storyId: story.id,
|
|
47132
47398
|
attempts: outcome.attempts
|
|
47133
47399
|
});
|
|
47134
|
-
return {
|
|
47400
|
+
return {
|
|
47401
|
+
passed: false,
|
|
47402
|
+
cost: gateCostAccum,
|
|
47403
|
+
fullSuiteGatePassed: false,
|
|
47404
|
+
status: "rectification-exhausted",
|
|
47405
|
+
attempts: outcome.attempts
|
|
47406
|
+
};
|
|
47135
47407
|
}
|
|
47136
47408
|
var _rectificationGateDeps;
|
|
47137
47409
|
var init_rectification_gate = __esm(() => {
|
|
@@ -47422,6 +47694,24 @@ var init_verdict = __esm(() => {
|
|
|
47422
47694
|
});
|
|
47423
47695
|
|
|
47424
47696
|
// src/tdd/orchestrator.ts
|
|
47697
|
+
async function rollbackTddFailureIfNeeded(shouldRollback, workdir, initialRef, storyId, failureCategory) {
|
|
47698
|
+
if (!shouldRollback) {
|
|
47699
|
+
return;
|
|
47700
|
+
}
|
|
47701
|
+
const logger = getLogger();
|
|
47702
|
+
try {
|
|
47703
|
+
await rollbackToRef(workdir, initialRef);
|
|
47704
|
+
logger.info("tdd", "Rolled back git changes due to TDD failure", {
|
|
47705
|
+
storyId,
|
|
47706
|
+
failureCategory
|
|
47707
|
+
});
|
|
47708
|
+
} catch (error48) {
|
|
47709
|
+
logger.error("tdd", "Failed to rollback git changes after TDD failure", {
|
|
47710
|
+
storyId,
|
|
47711
|
+
error: errorMessage(error48)
|
|
47712
|
+
});
|
|
47713
|
+
}
|
|
47714
|
+
}
|
|
47425
47715
|
async function runThreeSessionTdd(options) {
|
|
47426
47716
|
const {
|
|
47427
47717
|
agent,
|
|
@@ -47582,7 +47872,33 @@ async function runThreeSessionTdd(options) {
|
|
|
47582
47872
|
};
|
|
47583
47873
|
}
|
|
47584
47874
|
const implementerBinding = getTddSessionBinding?.("implementer");
|
|
47585
|
-
const
|
|
47875
|
+
const fullSuiteGate = await runFullSuiteGate(story, config2, workdir, agentManager, implementerTier, lite, logger, featureName, projectDir, implementerBinding?.sessionManager, implementerBinding?.sessionId, runtime);
|
|
47876
|
+
const { cost: fullSuiteGateCost, fullSuiteGatePassed } = fullSuiteGate;
|
|
47877
|
+
if (fullSuiteGate.status === "rectification-exhausted") {
|
|
47878
|
+
const failureCategory = "full-suite-gate-exhausted";
|
|
47879
|
+
const totalCost2 = sessions.reduce((sum, s) => sum + s.estimatedCostUsd, 0) + fullSuiteGateCost;
|
|
47880
|
+
const totalDurationMs2 = sessions.reduce((sum, s) => sum + s.durationMs, 0);
|
|
47881
|
+
const totalTokenUsage2 = sumTddTokenUsage(sessions);
|
|
47882
|
+
const terminalReviewReason = "Full suite gate failed after rectification exhausted";
|
|
47883
|
+
logger.warn("tdd", "Stopping before verifier because full-suite gate rectification exhausted", {
|
|
47884
|
+
storyId: story.id,
|
|
47885
|
+
attempts: fullSuiteGate.attempts,
|
|
47886
|
+
failureCategory
|
|
47887
|
+
});
|
|
47888
|
+
await rollbackTddFailureIfNeeded(shouldRollbackOnFailure, workdir, initialRef, story.id, failureCategory);
|
|
47889
|
+
return {
|
|
47890
|
+
success: false,
|
|
47891
|
+
sessions,
|
|
47892
|
+
needsHumanReview: true,
|
|
47893
|
+
reviewReason: terminalReviewReason,
|
|
47894
|
+
failureCategory,
|
|
47895
|
+
totalCost: totalCost2,
|
|
47896
|
+
totalDurationMs: totalDurationMs2,
|
|
47897
|
+
...totalTokenUsage2 && { totalTokenUsage: totalTokenUsage2 },
|
|
47898
|
+
lite,
|
|
47899
|
+
fullSuiteGatePassed
|
|
47900
|
+
};
|
|
47901
|
+
}
|
|
47586
47902
|
const session3Ref = await captureGitRef(workdir) ?? "HEAD";
|
|
47587
47903
|
const verifierBundle = await getTddContextBundle?.("verifier") ?? tddContextBundles?.verifier;
|
|
47588
47904
|
const session3 = await runTddSessionOp(verifyTddOp, options, session3Ref, verifierBundle, getTddSessionBinding?.("verifier"));
|
|
@@ -47658,20 +47974,7 @@ async function runThreeSessionTdd(options) {
|
|
|
47658
47974
|
lite,
|
|
47659
47975
|
verdictAvailable: verdict !== null
|
|
47660
47976
|
});
|
|
47661
|
-
|
|
47662
|
-
try {
|
|
47663
|
-
await rollbackToRef(workdir, initialRef);
|
|
47664
|
-
logger.info("tdd", "Rolled back git changes due to TDD failure", {
|
|
47665
|
-
storyId: story.id,
|
|
47666
|
-
failureCategory: finalFailureCategory
|
|
47667
|
-
});
|
|
47668
|
-
} catch (error48) {
|
|
47669
|
-
logger.error("tdd", "Failed to rollback git changes after TDD failure", {
|
|
47670
|
-
storyId: story.id,
|
|
47671
|
-
error: errorMessage(error48)
|
|
47672
|
-
});
|
|
47673
|
-
}
|
|
47674
|
-
}
|
|
47977
|
+
await rollbackTddFailureIfNeeded(shouldRollbackOnFailure && !allSuccessful, workdir, initialRef, story.id, finalFailureCategory);
|
|
47675
47978
|
return {
|
|
47676
47979
|
success: allSuccessful,
|
|
47677
47980
|
sessions,
|
|
@@ -47881,7 +48184,7 @@ function routeTddFailure(failureCategory, isLiteMode, ctx, reviewReason) {
|
|
|
47881
48184
|
}
|
|
47882
48185
|
return { action: "escalate" };
|
|
47883
48186
|
}
|
|
47884
|
-
if (failureCategory === "session-failure" || failureCategory === "tests-failing" || failureCategory === "verifier-rejected") {
|
|
48187
|
+
if (failureCategory === "session-failure" || failureCategory === "tests-failing" || failureCategory === "full-suite-gate-exhausted" || failureCategory === "verifier-rejected") {
|
|
47885
48188
|
return { action: "escalate" };
|
|
47886
48189
|
}
|
|
47887
48190
|
if (failureCategory === "greenfield-no-tests") {
|
|
@@ -49804,7 +50107,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
49804
50107
|
}
|
|
49805
50108
|
|
|
49806
50109
|
// src/cli/prompts-tdd.ts
|
|
49807
|
-
import { join as
|
|
50110
|
+
import { join as join42 } from "path";
|
|
49808
50111
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
49809
50112
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
49810
50113
|
TddPromptBuilder.for("test-writer", { isolation: "strict" }).withLoader(ctx.workdir, ctx.config).story(story).context(ctx.contextMarkdown).constitution(ctx.constitution?.content).testCommand(ctx.config.quality?.commands?.test).build(),
|
|
@@ -49823,7 +50126,7 @@ ${frontmatter}---
|
|
|
49823
50126
|
|
|
49824
50127
|
${session.prompt}`;
|
|
49825
50128
|
if (outputDir) {
|
|
49826
|
-
const promptFile =
|
|
50129
|
+
const promptFile = join42(outputDir, `${story.id}.${session.role}.md`);
|
|
49827
50130
|
await Bun.write(promptFile, fullOutput);
|
|
49828
50131
|
logger.info("cli", "Written TDD prompt file", {
|
|
49829
50132
|
storyId: story.id,
|
|
@@ -49839,7 +50142,7 @@ ${"=".repeat(80)}`);
|
|
|
49839
50142
|
}
|
|
49840
50143
|
}
|
|
49841
50144
|
if (outputDir && ctx.contextMarkdown) {
|
|
49842
|
-
const contextFile =
|
|
50145
|
+
const contextFile = join42(outputDir, `${story.id}.context.md`);
|
|
49843
50146
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
49844
50147
|
const contextOutput = `---
|
|
49845
50148
|
${frontmatter}---
|
|
@@ -49854,16 +50157,16 @@ var init_prompts_tdd = __esm(() => {
|
|
|
49854
50157
|
|
|
49855
50158
|
// src/cli/prompts-main.ts
|
|
49856
50159
|
import { existsSync as existsSync20, mkdirSync as mkdirSync3 } from "fs";
|
|
49857
|
-
import { join as
|
|
50160
|
+
import { join as join43 } from "path";
|
|
49858
50161
|
async function promptsCommand(options) {
|
|
49859
50162
|
const logger = getLogger();
|
|
49860
50163
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
49861
|
-
const naxDir =
|
|
50164
|
+
const naxDir = join43(workdir, ".nax");
|
|
49862
50165
|
if (!existsSync20(naxDir)) {
|
|
49863
50166
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
49864
50167
|
}
|
|
49865
|
-
const featureDir =
|
|
49866
|
-
const prdPath =
|
|
50168
|
+
const featureDir = join43(naxDir, "features", feature);
|
|
50169
|
+
const prdPath = join43(featureDir, "prd.json");
|
|
49867
50170
|
if (!existsSync20(prdPath)) {
|
|
49868
50171
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
49869
50172
|
}
|
|
@@ -49930,10 +50233,10 @@ ${frontmatter}---
|
|
|
49930
50233
|
|
|
49931
50234
|
${ctx.prompt}`;
|
|
49932
50235
|
if (outputDir) {
|
|
49933
|
-
const promptFile =
|
|
50236
|
+
const promptFile = join43(outputDir, `${story.id}.prompt.md`);
|
|
49934
50237
|
await Bun.write(promptFile, fullOutput);
|
|
49935
50238
|
if (ctx.contextMarkdown) {
|
|
49936
|
-
const contextFile =
|
|
50239
|
+
const contextFile = join43(outputDir, `${story.id}.context.md`);
|
|
49937
50240
|
const contextOutput = `---
|
|
49938
50241
|
${frontmatter}---
|
|
49939
50242
|
|
|
@@ -49969,12 +50272,12 @@ var init_prompts_main = __esm(() => {
|
|
|
49969
50272
|
|
|
49970
50273
|
// src/cli/prompts-init.ts
|
|
49971
50274
|
import { existsSync as existsSync21, mkdirSync as mkdirSync4 } from "fs";
|
|
49972
|
-
import { join as
|
|
50275
|
+
import { join as join44 } from "path";
|
|
49973
50276
|
async function promptsInitCommand(options) {
|
|
49974
50277
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
49975
|
-
const templatesDir =
|
|
50278
|
+
const templatesDir = join44(workdir, ".nax", "templates");
|
|
49976
50279
|
mkdirSync4(templatesDir, { recursive: true });
|
|
49977
|
-
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(
|
|
50280
|
+
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join44(templatesDir, f)));
|
|
49978
50281
|
if (existingFiles.length > 0 && !force) {
|
|
49979
50282
|
console.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
49980
50283
|
Pass --force to overwrite existing templates.`);
|
|
@@ -49982,7 +50285,7 @@ async function promptsInitCommand(options) {
|
|
|
49982
50285
|
}
|
|
49983
50286
|
const written = [];
|
|
49984
50287
|
for (const template of TEMPLATE_ROLES) {
|
|
49985
|
-
const filePath =
|
|
50288
|
+
const filePath = join44(templatesDir, template.file);
|
|
49986
50289
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
49987
50290
|
const content = TEMPLATE_HEADER + roleBody;
|
|
49988
50291
|
await Bun.write(filePath, content);
|
|
@@ -49998,7 +50301,7 @@ async function promptsInitCommand(options) {
|
|
|
49998
50301
|
return written;
|
|
49999
50302
|
}
|
|
50000
50303
|
async function autoWirePromptsConfig(workdir) {
|
|
50001
|
-
const configPath =
|
|
50304
|
+
const configPath = join44(workdir, "nax.config.json");
|
|
50002
50305
|
if (!existsSync21(configPath)) {
|
|
50003
50306
|
const exampleConfig = JSON.stringify({
|
|
50004
50307
|
prompts: {
|
|
@@ -50162,7 +50465,7 @@ __export(exports_init_context, {
|
|
|
50162
50465
|
});
|
|
50163
50466
|
import { existsSync as existsSync22 } from "fs";
|
|
50164
50467
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
50165
|
-
import { basename as basename9, join as
|
|
50468
|
+
import { basename as basename9, join as join45 } from "path";
|
|
50166
50469
|
async function findFiles(dir, maxFiles = 200) {
|
|
50167
50470
|
try {
|
|
50168
50471
|
const proc = Bun.spawnSync([
|
|
@@ -50190,7 +50493,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
50190
50493
|
return [];
|
|
50191
50494
|
}
|
|
50192
50495
|
async function readPackageManifest(projectRoot) {
|
|
50193
|
-
const packageJsonPath =
|
|
50496
|
+
const packageJsonPath = join45(projectRoot, "package.json");
|
|
50194
50497
|
if (!existsSync22(packageJsonPath)) {
|
|
50195
50498
|
return null;
|
|
50196
50499
|
}
|
|
@@ -50208,7 +50511,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
50208
50511
|
}
|
|
50209
50512
|
}
|
|
50210
50513
|
async function readReadmeSnippet(projectRoot) {
|
|
50211
|
-
const readmePath =
|
|
50514
|
+
const readmePath = join45(projectRoot, "README.md");
|
|
50212
50515
|
if (!existsSync22(readmePath)) {
|
|
50213
50516
|
return null;
|
|
50214
50517
|
}
|
|
@@ -50226,7 +50529,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
50226
50529
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
50227
50530
|
const found = [];
|
|
50228
50531
|
for (const candidate of candidates) {
|
|
50229
|
-
const path14 =
|
|
50532
|
+
const path14 = join45(projectRoot, candidate);
|
|
50230
50533
|
if (existsSync22(path14)) {
|
|
50231
50534
|
found.push(candidate);
|
|
50232
50535
|
}
|
|
@@ -50237,7 +50540,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
50237
50540
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
50238
50541
|
const found = [];
|
|
50239
50542
|
for (const candidate of candidates) {
|
|
50240
|
-
const path14 =
|
|
50543
|
+
const path14 = join45(projectRoot, candidate);
|
|
50241
50544
|
if (existsSync22(path14)) {
|
|
50242
50545
|
found.push(candidate);
|
|
50243
50546
|
}
|
|
@@ -50398,8 +50701,8 @@ function generatePackageContextTemplate(packagePath) {
|
|
|
50398
50701
|
}
|
|
50399
50702
|
async function initPackage(repoRoot, packagePath, force = false) {
|
|
50400
50703
|
const logger = getLogger();
|
|
50401
|
-
const naxDir =
|
|
50402
|
-
const contextPath =
|
|
50704
|
+
const naxDir = join45(repoRoot, ".nax", "mono", packagePath);
|
|
50705
|
+
const contextPath = join45(naxDir, "context.md");
|
|
50403
50706
|
if (existsSync22(contextPath) && !force) {
|
|
50404
50707
|
logger.info("init", "Package context.md already exists (use --force to overwrite)", { path: contextPath });
|
|
50405
50708
|
return;
|
|
@@ -50413,8 +50716,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
50413
50716
|
}
|
|
50414
50717
|
async function initContext(projectRoot, options = {}) {
|
|
50415
50718
|
const logger = getLogger();
|
|
50416
|
-
const naxDir =
|
|
50417
|
-
const contextPath =
|
|
50719
|
+
const naxDir = join45(projectRoot, ".nax");
|
|
50720
|
+
const contextPath = join45(naxDir, "context.md");
|
|
50418
50721
|
if (existsSync22(contextPath) && !options.force) {
|
|
50419
50722
|
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", { path: contextPath });
|
|
50420
50723
|
return;
|
|
@@ -50444,9 +50747,9 @@ var init_init_context = __esm(() => {
|
|
|
50444
50747
|
|
|
50445
50748
|
// src/cli/init-detect.ts
|
|
50446
50749
|
import { existsSync as existsSync23, readFileSync } from "fs";
|
|
50447
|
-
import { join as
|
|
50750
|
+
import { join as join46 } from "path";
|
|
50448
50751
|
function readPackageJson(projectRoot) {
|
|
50449
|
-
const pkgPath =
|
|
50752
|
+
const pkgPath = join46(projectRoot, "package.json");
|
|
50450
50753
|
if (!existsSync23(pkgPath))
|
|
50451
50754
|
return;
|
|
50452
50755
|
try {
|
|
@@ -50489,41 +50792,41 @@ function detectStack(projectRoot) {
|
|
|
50489
50792
|
};
|
|
50490
50793
|
}
|
|
50491
50794
|
function detectRuntime(projectRoot) {
|
|
50492
|
-
if (existsSync23(
|
|
50795
|
+
if (existsSync23(join46(projectRoot, "bun.lockb")) || existsSync23(join46(projectRoot, "bunfig.toml"))) {
|
|
50493
50796
|
return "bun";
|
|
50494
50797
|
}
|
|
50495
|
-
if (existsSync23(
|
|
50798
|
+
if (existsSync23(join46(projectRoot, "package-lock.json")) || existsSync23(join46(projectRoot, "yarn.lock")) || existsSync23(join46(projectRoot, "pnpm-lock.yaml"))) {
|
|
50496
50799
|
return "node";
|
|
50497
50800
|
}
|
|
50498
50801
|
return "unknown";
|
|
50499
50802
|
}
|
|
50500
|
-
function
|
|
50501
|
-
if (existsSync23(
|
|
50803
|
+
function detectLanguage2(projectRoot) {
|
|
50804
|
+
if (existsSync23(join46(projectRoot, "tsconfig.json")))
|
|
50502
50805
|
return "typescript";
|
|
50503
|
-
if (existsSync23(
|
|
50806
|
+
if (existsSync23(join46(projectRoot, "pyproject.toml")) || existsSync23(join46(projectRoot, "setup.py"))) {
|
|
50504
50807
|
return "python";
|
|
50505
50808
|
}
|
|
50506
|
-
if (existsSync23(
|
|
50809
|
+
if (existsSync23(join46(projectRoot, "Cargo.toml")))
|
|
50507
50810
|
return "rust";
|
|
50508
|
-
if (existsSync23(
|
|
50811
|
+
if (existsSync23(join46(projectRoot, "go.mod")))
|
|
50509
50812
|
return "go";
|
|
50510
50813
|
return "unknown";
|
|
50511
50814
|
}
|
|
50512
50815
|
function detectLinter(projectRoot) {
|
|
50513
|
-
if (existsSync23(
|
|
50816
|
+
if (existsSync23(join46(projectRoot, "biome.json")) || existsSync23(join46(projectRoot, "biome.jsonc"))) {
|
|
50514
50817
|
return "biome";
|
|
50515
50818
|
}
|
|
50516
|
-
if (existsSync23(
|
|
50819
|
+
if (existsSync23(join46(projectRoot, ".eslintrc.json")) || existsSync23(join46(projectRoot, ".eslintrc.js")) || existsSync23(join46(projectRoot, "eslint.config.js"))) {
|
|
50517
50820
|
return "eslint";
|
|
50518
50821
|
}
|
|
50519
50822
|
return "unknown";
|
|
50520
50823
|
}
|
|
50521
50824
|
function detectMonorepo(projectRoot) {
|
|
50522
|
-
if (existsSync23(
|
|
50825
|
+
if (existsSync23(join46(projectRoot, "turbo.json")))
|
|
50523
50826
|
return "turborepo";
|
|
50524
|
-
if (existsSync23(
|
|
50827
|
+
if (existsSync23(join46(projectRoot, "nx.json")))
|
|
50525
50828
|
return "nx";
|
|
50526
|
-
if (existsSync23(
|
|
50829
|
+
if (existsSync23(join46(projectRoot, "pnpm-workspace.yaml")))
|
|
50527
50830
|
return "pnpm-workspaces";
|
|
50528
50831
|
const pkg = readPackageJson(projectRoot);
|
|
50529
50832
|
if (pkg?.workspaces)
|
|
@@ -50533,7 +50836,7 @@ function detectMonorepo(projectRoot) {
|
|
|
50533
50836
|
function detectProjectStack(projectRoot) {
|
|
50534
50837
|
return {
|
|
50535
50838
|
runtime: detectRuntime(projectRoot),
|
|
50536
|
-
language:
|
|
50839
|
+
language: detectLanguage2(projectRoot),
|
|
50537
50840
|
linter: detectLinter(projectRoot),
|
|
50538
50841
|
monorepo: detectMonorepo(projectRoot)
|
|
50539
50842
|
};
|
|
@@ -50666,7 +50969,7 @@ __export(exports_init, {
|
|
|
50666
50969
|
});
|
|
50667
50970
|
import { existsSync as existsSync24 } from "fs";
|
|
50668
50971
|
import { mkdir as mkdir9 } from "fs/promises";
|
|
50669
|
-
import { join as
|
|
50972
|
+
import { join as join47 } from "path";
|
|
50670
50973
|
function validateProjectName(name) {
|
|
50671
50974
|
if (!name)
|
|
50672
50975
|
return { valid: false, error: "name must be non-empty" };
|
|
@@ -50702,7 +51005,7 @@ async function checkInitCollision(name, currentWorkdir, currentRemote) {
|
|
|
50702
51005
|
}
|
|
50703
51006
|
async function updateGitignore(projectRoot) {
|
|
50704
51007
|
const logger = getLogger();
|
|
50705
|
-
const gitignorePath =
|
|
51008
|
+
const gitignorePath = join47(projectRoot, ".gitignore");
|
|
50706
51009
|
let existing = "";
|
|
50707
51010
|
if (existsSync24(gitignorePath)) {
|
|
50708
51011
|
existing = await Bun.file(gitignorePath).text();
|
|
@@ -50788,7 +51091,7 @@ async function initGlobal() {
|
|
|
50788
51091
|
await mkdir9(globalDir, { recursive: true });
|
|
50789
51092
|
logger.info("init", "Created global config directory", { path: globalDir });
|
|
50790
51093
|
}
|
|
50791
|
-
const configPath =
|
|
51094
|
+
const configPath = join47(globalDir, "config.json");
|
|
50792
51095
|
if (!existsSync24(configPath)) {
|
|
50793
51096
|
await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
|
|
50794
51097
|
`);
|
|
@@ -50796,14 +51099,14 @@ async function initGlobal() {
|
|
|
50796
51099
|
} else {
|
|
50797
51100
|
logger.info("init", "Global config already exists", { path: configPath });
|
|
50798
51101
|
}
|
|
50799
|
-
const constitutionPath =
|
|
51102
|
+
const constitutionPath = join47(globalDir, "constitution.md");
|
|
50800
51103
|
if (!existsSync24(constitutionPath)) {
|
|
50801
51104
|
await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
|
|
50802
51105
|
logger.info("init", "Created global constitution", { path: constitutionPath });
|
|
50803
51106
|
} else {
|
|
50804
51107
|
logger.info("init", "Global constitution already exists", { path: constitutionPath });
|
|
50805
51108
|
}
|
|
50806
|
-
const hooksDir =
|
|
51109
|
+
const hooksDir = join47(globalDir, "hooks");
|
|
50807
51110
|
if (!existsSync24(hooksDir)) {
|
|
50808
51111
|
await mkdir9(hooksDir, { recursive: true });
|
|
50809
51112
|
logger.info("init", "Created global hooks directory", { path: hooksDir });
|
|
@@ -50836,7 +51139,7 @@ async function initProject(projectRoot, options) {
|
|
|
50836
51139
|
if (detectedName && !options?.force) {
|
|
50837
51140
|
const collision = await checkInitCollision(detectedName, projectRoot, currentRemote);
|
|
50838
51141
|
if (collision.collision && collision.existing) {
|
|
50839
|
-
const configPath2 =
|
|
51142
|
+
const configPath2 = join47(projectDir, "config.json");
|
|
50840
51143
|
throw new NaxError([
|
|
50841
51144
|
`Project name collision: "${detectedName}"`,
|
|
50842
51145
|
` This project: ${projectRoot}`,
|
|
@@ -50864,7 +51167,7 @@ async function initProject(projectRoot, options) {
|
|
|
50864
51167
|
linter: stack.linter,
|
|
50865
51168
|
monorepo: stack.monorepo
|
|
50866
51169
|
});
|
|
50867
|
-
const configPath =
|
|
51170
|
+
const configPath = join47(projectDir, "config.json");
|
|
50868
51171
|
if (!existsSync24(configPath)) {
|
|
50869
51172
|
await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
|
|
50870
51173
|
`);
|
|
@@ -50873,14 +51176,14 @@ async function initProject(projectRoot, options) {
|
|
|
50873
51176
|
logger.info("init", "Project config already exists", { path: configPath });
|
|
50874
51177
|
}
|
|
50875
51178
|
await initContext(projectRoot, { ai: options?.ai, force: options?.force });
|
|
50876
|
-
const constitutionPath =
|
|
51179
|
+
const constitutionPath = join47(projectDir, "constitution.md");
|
|
50877
51180
|
if (!existsSync24(constitutionPath) || options?.force) {
|
|
50878
51181
|
await Bun.write(constitutionPath, buildConstitution(stack));
|
|
50879
51182
|
logger.info("init", "Created project constitution", { path: constitutionPath });
|
|
50880
51183
|
} else {
|
|
50881
51184
|
logger.info("init", "Project constitution already exists", { path: constitutionPath });
|
|
50882
51185
|
}
|
|
50883
|
-
const hooksDir =
|
|
51186
|
+
const hooksDir = join47(projectDir, "hooks");
|
|
50884
51187
|
if (!existsSync24(hooksDir)) {
|
|
50885
51188
|
await mkdir9(hooksDir, { recursive: true });
|
|
50886
51189
|
logger.info("init", "Created project hooks directory", { path: hooksDir });
|
|
@@ -52342,19 +52645,19 @@ var init_command_argv = __esm(() => {
|
|
|
52342
52645
|
});
|
|
52343
52646
|
|
|
52344
52647
|
// src/hooks/runner.ts
|
|
52345
|
-
import { join as
|
|
52648
|
+
import { join as join66 } from "path";
|
|
52346
52649
|
async function loadHooksConfig(projectDir, globalDir) {
|
|
52347
52650
|
let globalHooks = { hooks: {} };
|
|
52348
52651
|
let projectHooks = { hooks: {} };
|
|
52349
52652
|
let skipGlobal = false;
|
|
52350
|
-
const projectPath =
|
|
52653
|
+
const projectPath = join66(projectDir, "hooks.json");
|
|
52351
52654
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
52352
52655
|
if (projectData) {
|
|
52353
52656
|
projectHooks = projectData;
|
|
52354
52657
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
52355
52658
|
}
|
|
52356
52659
|
if (!skipGlobal && globalDir) {
|
|
52357
|
-
const globalPath =
|
|
52660
|
+
const globalPath = join66(globalDir, "hooks.json");
|
|
52358
52661
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
52359
52662
|
if (globalData) {
|
|
52360
52663
|
globalHooks = globalData;
|
|
@@ -52509,7 +52812,7 @@ var package_default;
|
|
|
52509
52812
|
var init_package = __esm(() => {
|
|
52510
52813
|
package_default = {
|
|
52511
52814
|
name: "@nathapp/nax",
|
|
52512
|
-
version: "0.65.0-canary.
|
|
52815
|
+
version: "0.65.0-canary.2",
|
|
52513
52816
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
52514
52817
|
type: "module",
|
|
52515
52818
|
bin: {
|
|
@@ -52594,8 +52897,8 @@ var init_version = __esm(() => {
|
|
|
52594
52897
|
NAX_VERSION = package_default.version;
|
|
52595
52898
|
NAX_COMMIT = (() => {
|
|
52596
52899
|
try {
|
|
52597
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
52598
|
-
return "
|
|
52900
|
+
if (/^[0-9a-f]{6,10}$/.test("32aecf38"))
|
|
52901
|
+
return "32aecf38";
|
|
52599
52902
|
} catch {}
|
|
52600
52903
|
try {
|
|
52601
52904
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -53470,15 +53773,15 @@ var init_acceptance_loop = __esm(() => {
|
|
|
53470
53773
|
|
|
53471
53774
|
// src/session/scratch-purge.ts
|
|
53472
53775
|
import { mkdir as mkdir13, rename, rm } from "fs/promises";
|
|
53473
|
-
import { dirname as dirname11, join as
|
|
53776
|
+
import { dirname as dirname11, join as join67 } from "path";
|
|
53474
53777
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
53475
|
-
const sessionsDir =
|
|
53778
|
+
const sessionsDir = join67(projectDir, ".nax", "features", featureName, "sessions");
|
|
53476
53779
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
53477
53780
|
const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
|
|
53478
53781
|
let purged = 0;
|
|
53479
53782
|
for (const sessionId of sessionIds) {
|
|
53480
|
-
const sessionDir =
|
|
53481
|
-
const descriptorPath =
|
|
53783
|
+
const sessionDir = join67(sessionsDir, sessionId);
|
|
53784
|
+
const descriptorPath = join67(sessionDir, "descriptor.json");
|
|
53482
53785
|
if (!await _scratchPurgeDeps.fileExists(descriptorPath))
|
|
53483
53786
|
continue;
|
|
53484
53787
|
let lastActivityAt;
|
|
@@ -53494,7 +53797,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
|
|
|
53494
53797
|
if (new Date(lastActivityAt).getTime() >= cutoffMs)
|
|
53495
53798
|
continue;
|
|
53496
53799
|
if (archiveInsteadOfDelete) {
|
|
53497
|
-
const archiveDest =
|
|
53800
|
+
const archiveDest = join67(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
|
|
53498
53801
|
await _scratchPurgeDeps.move(sessionDir, archiveDest);
|
|
53499
53802
|
} else {
|
|
53500
53803
|
await _scratchPurgeDeps.remove(sessionDir);
|
|
@@ -54205,12 +54508,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
|
54205
54508
|
// src/pipeline/subscribers/events-writer.ts
|
|
54206
54509
|
import { appendFile as appendFile5, mkdir as mkdir14 } from "fs/promises";
|
|
54207
54510
|
import { homedir as homedir5 } from "os";
|
|
54208
|
-
import { basename as basename14, join as
|
|
54511
|
+
import { basename as basename14, join as join68 } from "path";
|
|
54209
54512
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
54210
54513
|
const logger = getSafeLogger();
|
|
54211
54514
|
const project = basename14(workdir);
|
|
54212
|
-
const eventsDir =
|
|
54213
|
-
const eventsFile =
|
|
54515
|
+
const eventsDir = join68(homedir5(), ".nax", "events", project);
|
|
54516
|
+
const eventsFile = join68(eventsDir, "events.jsonl");
|
|
54214
54517
|
let dirReady = false;
|
|
54215
54518
|
const write = (line) => {
|
|
54216
54519
|
return (async () => {
|
|
@@ -54391,12 +54694,12 @@ var init_interaction2 = __esm(() => {
|
|
|
54391
54694
|
// src/pipeline/subscribers/registry.ts
|
|
54392
54695
|
import { mkdir as mkdir15, writeFile as writeFile2 } from "fs/promises";
|
|
54393
54696
|
import { homedir as homedir6 } from "os";
|
|
54394
|
-
import { basename as basename15, join as
|
|
54697
|
+
import { basename as basename15, join as join69 } from "path";
|
|
54395
54698
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
54396
54699
|
const logger = getSafeLogger();
|
|
54397
54700
|
const project = basename15(workdir);
|
|
54398
|
-
const runDir =
|
|
54399
|
-
const metaFile =
|
|
54701
|
+
const runDir = join69(homedir6(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
54702
|
+
const metaFile = join69(runDir, "meta.json");
|
|
54400
54703
|
const unsub = bus.on("run:started", (_ev) => {
|
|
54401
54704
|
return (async () => {
|
|
54402
54705
|
try {
|
|
@@ -54406,8 +54709,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
|
54406
54709
|
project,
|
|
54407
54710
|
feature,
|
|
54408
54711
|
workdir,
|
|
54409
|
-
statusPath:
|
|
54410
|
-
eventsDir:
|
|
54712
|
+
statusPath: join69(outputDir, "features", feature, "status.json"),
|
|
54713
|
+
eventsDir: join69(outputDir, "features", feature, "runs"),
|
|
54411
54714
|
registeredAt: new Date().toISOString()
|
|
54412
54715
|
};
|
|
54413
54716
|
await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -54655,7 +54958,7 @@ var init_types8 = __esm(() => {
|
|
|
54655
54958
|
|
|
54656
54959
|
// src/worktree/dependencies.ts
|
|
54657
54960
|
import { existsSync as existsSync32 } from "fs";
|
|
54658
|
-
import { join as
|
|
54961
|
+
import { join as join70 } from "path";
|
|
54659
54962
|
async function prepareWorktreeDependencies(options) {
|
|
54660
54963
|
const mode = options.config.execution.worktreeDependencies.mode;
|
|
54661
54964
|
const resolvedCwd = resolveDependencyCwd(options);
|
|
@@ -54669,7 +54972,7 @@ async function prepareWorktreeDependencies(options) {
|
|
|
54669
54972
|
}
|
|
54670
54973
|
}
|
|
54671
54974
|
function resolveDependencyCwd(options) {
|
|
54672
|
-
return options.storyWorkdir ?
|
|
54975
|
+
return options.storyWorkdir ? join70(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
|
|
54673
54976
|
}
|
|
54674
54977
|
function resolveInheritedDependencies(options, resolvedCwd) {
|
|
54675
54978
|
if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
|
|
@@ -54679,7 +54982,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
|
|
|
54679
54982
|
}
|
|
54680
54983
|
function hasDependencyManifests(worktreeRoot, resolvedCwd) {
|
|
54681
54984
|
const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
|
|
54682
|
-
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(
|
|
54985
|
+
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join70(directory, filename))));
|
|
54683
54986
|
}
|
|
54684
54987
|
async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
|
|
54685
54988
|
const setupCommand = config2.execution.worktreeDependencies.setupCommand;
|
|
@@ -54743,13 +55046,13 @@ __export(exports_manager, {
|
|
|
54743
55046
|
});
|
|
54744
55047
|
import { existsSync as existsSync33, symlinkSync } from "fs";
|
|
54745
55048
|
import { mkdir as mkdir16 } from "fs/promises";
|
|
54746
|
-
import { join as
|
|
55049
|
+
import { join as join71 } from "path";
|
|
54747
55050
|
|
|
54748
55051
|
class WorktreeManager {
|
|
54749
55052
|
async ensureGitExcludes(projectRoot) {
|
|
54750
55053
|
const logger = getSafeLogger();
|
|
54751
|
-
const infoDir =
|
|
54752
|
-
const excludePath =
|
|
55054
|
+
const infoDir = join71(projectRoot, ".git", "info");
|
|
55055
|
+
const excludePath = join71(infoDir, "exclude");
|
|
54753
55056
|
try {
|
|
54754
55057
|
await mkdir16(infoDir, { recursive: true });
|
|
54755
55058
|
let existing = "";
|
|
@@ -54776,7 +55079,7 @@ ${missing.join(`
|
|
|
54776
55079
|
}
|
|
54777
55080
|
async create(projectRoot, storyId) {
|
|
54778
55081
|
validateStoryId(storyId);
|
|
54779
|
-
const worktreePath =
|
|
55082
|
+
const worktreePath = join71(projectRoot, ".nax-wt", storyId);
|
|
54780
55083
|
const branchName = `nax/${storyId}`;
|
|
54781
55084
|
try {
|
|
54782
55085
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -54817,9 +55120,9 @@ ${missing.join(`
|
|
|
54817
55120
|
}
|
|
54818
55121
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
54819
55122
|
}
|
|
54820
|
-
const envSource =
|
|
55123
|
+
const envSource = join71(projectRoot, ".env");
|
|
54821
55124
|
if (existsSync33(envSource)) {
|
|
54822
|
-
const envTarget =
|
|
55125
|
+
const envTarget = join71(worktreePath, ".env");
|
|
54823
55126
|
try {
|
|
54824
55127
|
symlinkSync(envSource, envTarget, "file");
|
|
54825
55128
|
} catch (error48) {
|
|
@@ -54830,7 +55133,7 @@ ${missing.join(`
|
|
|
54830
55133
|
}
|
|
54831
55134
|
async remove(projectRoot, storyId) {
|
|
54832
55135
|
validateStoryId(storyId);
|
|
54833
|
-
const worktreePath =
|
|
55136
|
+
const worktreePath = join71(projectRoot, ".nax-wt", storyId);
|
|
54834
55137
|
const branchName = `nax/${storyId}`;
|
|
54835
55138
|
try {
|
|
54836
55139
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -55321,6 +55624,7 @@ function resolveMaxAttemptsOutcome(failureCategory) {
|
|
|
55321
55624
|
return "pause";
|
|
55322
55625
|
case "session-failure":
|
|
55323
55626
|
case "tests-failing":
|
|
55627
|
+
case "full-suite-gate-exhausted":
|
|
55324
55628
|
case "dependency-prep":
|
|
55325
55629
|
return "fail";
|
|
55326
55630
|
default:
|
|
@@ -55542,10 +55846,10 @@ var init_merge_conflict_rectify = __esm(() => {
|
|
|
55542
55846
|
});
|
|
55543
55847
|
|
|
55544
55848
|
// src/execution/pipeline-result-handler.ts
|
|
55545
|
-
import { join as
|
|
55849
|
+
import { join as join72 } from "path";
|
|
55546
55850
|
async function removeWorktreeDirectory(projectRoot, storyId) {
|
|
55547
55851
|
const logger = getSafeLogger();
|
|
55548
|
-
const worktreePath =
|
|
55852
|
+
const worktreePath = join72(projectRoot, ".nax-wt", storyId);
|
|
55549
55853
|
try {
|
|
55550
55854
|
const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
55551
55855
|
cwd: projectRoot,
|
|
@@ -55756,7 +56060,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
55756
56060
|
|
|
55757
56061
|
// src/execution/iteration-runner.ts
|
|
55758
56062
|
import { existsSync as existsSync34 } from "fs";
|
|
55759
|
-
import { join as
|
|
56063
|
+
import { join as join73 } from "path";
|
|
55760
56064
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
55761
56065
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
55762
56066
|
if (ctx.dryRun) {
|
|
@@ -55781,7 +56085,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
55781
56085
|
const storyStartTime = Date.now();
|
|
55782
56086
|
let effectiveWorkdir = ctx.workdir;
|
|
55783
56087
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
55784
|
-
const worktreePath =
|
|
56088
|
+
const worktreePath = join73(ctx.workdir, ".nax-wt", story.id);
|
|
55785
56089
|
const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
|
|
55786
56090
|
if (!worktreeExists) {
|
|
55787
56091
|
await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
|
|
@@ -55801,7 +56105,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
55801
56105
|
}
|
|
55802
56106
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
55803
56107
|
const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
|
|
55804
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
56108
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join73(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
|
|
55805
56109
|
let dependencyContext;
|
|
55806
56110
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
55807
56111
|
try {
|
|
@@ -55828,7 +56132,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
55828
56132
|
};
|
|
55829
56133
|
}
|
|
55830
56134
|
}
|
|
55831
|
-
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ?
|
|
56135
|
+
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join73(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join73(ctx.workdir, story.workdir) : ctx.workdir;
|
|
55832
56136
|
const pipelineContext = {
|
|
55833
56137
|
config: effectiveConfig,
|
|
55834
56138
|
rootConfig: ctx.config,
|
|
@@ -56025,7 +56329,7 @@ __export(exports_parallel_worker, {
|
|
|
56025
56329
|
executeParallelBatch: () => executeParallelBatch,
|
|
56026
56330
|
_parallelWorkerDeps: () => _parallelWorkerDeps
|
|
56027
56331
|
});
|
|
56028
|
-
import { join as
|
|
56332
|
+
import { join as join74 } from "path";
|
|
56029
56333
|
async function executeStoryInWorktree(story, worktreePath, dependencyContext, context, routing, eventEmitter) {
|
|
56030
56334
|
const logger = getSafeLogger();
|
|
56031
56335
|
try {
|
|
@@ -56045,7 +56349,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
|
|
|
56045
56349
|
story,
|
|
56046
56350
|
stories: [story],
|
|
56047
56351
|
projectDir: context.projectDir,
|
|
56048
|
-
workdir: dependencyContext.cwd ?? (story.workdir ?
|
|
56352
|
+
workdir: dependencyContext.cwd ?? (story.workdir ? join74(worktreePath, story.workdir) : worktreePath),
|
|
56049
56353
|
worktreeDependencyContext: dependencyContext,
|
|
56050
56354
|
routing,
|
|
56051
56355
|
storyGitRef: storyGitRef ?? undefined
|
|
@@ -56813,122 +57117,6 @@ var init_unified_executor = __esm(() => {
|
|
|
56813
57117
|
};
|
|
56814
57118
|
});
|
|
56815
57119
|
|
|
56816
|
-
// src/project/detector.ts
|
|
56817
|
-
import { join as join74 } from "path";
|
|
56818
|
-
async function detectLanguage2(workdir, pkg) {
|
|
56819
|
-
const deps = _detectorDeps;
|
|
56820
|
-
if (await deps.fileExists(join74(workdir, "go.mod")))
|
|
56821
|
-
return "go";
|
|
56822
|
-
if (await deps.fileExists(join74(workdir, "Cargo.toml")))
|
|
56823
|
-
return "rust";
|
|
56824
|
-
if (await deps.fileExists(join74(workdir, "pyproject.toml")))
|
|
56825
|
-
return "python";
|
|
56826
|
-
if (await deps.fileExists(join74(workdir, "requirements.txt")))
|
|
56827
|
-
return "python";
|
|
56828
|
-
if (pkg != null) {
|
|
56829
|
-
const allDeps2 = {
|
|
56830
|
-
...pkg.dependencies,
|
|
56831
|
-
...pkg.devDependencies
|
|
56832
|
-
};
|
|
56833
|
-
if ("typescript" in allDeps2)
|
|
56834
|
-
return "typescript";
|
|
56835
|
-
return "javascript";
|
|
56836
|
-
}
|
|
56837
|
-
return;
|
|
56838
|
-
}
|
|
56839
|
-
function detectType(pkg) {
|
|
56840
|
-
if (pkg == null)
|
|
56841
|
-
return;
|
|
56842
|
-
if (pkg.workspaces != null)
|
|
56843
|
-
return "monorepo";
|
|
56844
|
-
const allDeps2 = {
|
|
56845
|
-
...pkg.dependencies,
|
|
56846
|
-
...pkg.devDependencies
|
|
56847
|
-
};
|
|
56848
|
-
for (const dep of WEB_DEPS) {
|
|
56849
|
-
if (dep in allDeps2)
|
|
56850
|
-
return "web";
|
|
56851
|
-
}
|
|
56852
|
-
if ("ink" in allDeps2)
|
|
56853
|
-
return "tui";
|
|
56854
|
-
for (const dep of API_DEPS) {
|
|
56855
|
-
if (dep in allDeps2)
|
|
56856
|
-
return "api";
|
|
56857
|
-
}
|
|
56858
|
-
if (pkg.bin != null)
|
|
56859
|
-
return "cli";
|
|
56860
|
-
return;
|
|
56861
|
-
}
|
|
56862
|
-
async function detectTestFramework(_workdir, language, pkg) {
|
|
56863
|
-
if (language === "go")
|
|
56864
|
-
return "go-test";
|
|
56865
|
-
if (language === "rust")
|
|
56866
|
-
return "cargo-test";
|
|
56867
|
-
if (language === "python")
|
|
56868
|
-
return "pytest";
|
|
56869
|
-
if (pkg != null) {
|
|
56870
|
-
const devDeps = pkg.devDependencies ?? {};
|
|
56871
|
-
if ("vitest" in devDeps)
|
|
56872
|
-
return "vitest";
|
|
56873
|
-
if ("jest" in devDeps)
|
|
56874
|
-
return "jest";
|
|
56875
|
-
}
|
|
56876
|
-
return;
|
|
56877
|
-
}
|
|
56878
|
-
async function detectLintTool(workdir, language) {
|
|
56879
|
-
if (language === "go")
|
|
56880
|
-
return "golangci-lint";
|
|
56881
|
-
if (language === "rust")
|
|
56882
|
-
return "clippy";
|
|
56883
|
-
if (language === "python")
|
|
56884
|
-
return "ruff";
|
|
56885
|
-
const deps = _detectorDeps;
|
|
56886
|
-
if (await deps.fileExists(join74(workdir, "biome.json")))
|
|
56887
|
-
return "biome";
|
|
56888
|
-
if (await deps.fileExists(join74(workdir, ".eslintrc")))
|
|
56889
|
-
return "eslint";
|
|
56890
|
-
if (await deps.fileExists(join74(workdir, ".eslintrc.js")))
|
|
56891
|
-
return "eslint";
|
|
56892
|
-
if (await deps.fileExists(join74(workdir, ".eslintrc.json")))
|
|
56893
|
-
return "eslint";
|
|
56894
|
-
return;
|
|
56895
|
-
}
|
|
56896
|
-
async function detectProjectProfile(workdir, existing) {
|
|
56897
|
-
const pkg = await _detectorDeps.readJson(join74(workdir, "package.json"));
|
|
56898
|
-
const language = existing.language !== undefined ? existing.language : await detectLanguage2(workdir, pkg);
|
|
56899
|
-
const type = existing.type !== undefined ? existing.type : detectType(pkg);
|
|
56900
|
-
const testFramework = existing.testFramework !== undefined ? existing.testFramework : await detectTestFramework(workdir, language, pkg);
|
|
56901
|
-
const lintTool = existing.lintTool !== undefined ? existing.lintTool : await detectLintTool(workdir, language);
|
|
56902
|
-
return { language, type, testFramework, lintTool };
|
|
56903
|
-
}
|
|
56904
|
-
var _detectorDeps, WEB_DEPS, API_DEPS;
|
|
56905
|
-
var init_detector2 = __esm(() => {
|
|
56906
|
-
_detectorDeps = {
|
|
56907
|
-
async fileExists(path22) {
|
|
56908
|
-
const file3 = Bun.file(path22);
|
|
56909
|
-
return file3.exists();
|
|
56910
|
-
},
|
|
56911
|
-
async readJson(path22) {
|
|
56912
|
-
try {
|
|
56913
|
-
const file3 = Bun.file(path22);
|
|
56914
|
-
if (!await file3.exists())
|
|
56915
|
-
return null;
|
|
56916
|
-
const text = await file3.text();
|
|
56917
|
-
return JSON.parse(text);
|
|
56918
|
-
} catch {
|
|
56919
|
-
return null;
|
|
56920
|
-
}
|
|
56921
|
-
}
|
|
56922
|
-
};
|
|
56923
|
-
WEB_DEPS = new Set(["react", "next", "vue", "nuxt"]);
|
|
56924
|
-
API_DEPS = new Set(["express", "fastify", "hono"]);
|
|
56925
|
-
});
|
|
56926
|
-
|
|
56927
|
-
// src/project/index.ts
|
|
56928
|
-
var init_project = __esm(() => {
|
|
56929
|
-
init_detector2();
|
|
56930
|
-
});
|
|
56931
|
-
|
|
56932
57120
|
// src/execution/status-file.ts
|
|
56933
57121
|
import { rename as rename2, unlink as unlink3 } from "fs/promises";
|
|
56934
57122
|
import { resolve as resolve16 } from "path";
|
|
@@ -89328,9 +89516,9 @@ init_plan_runtime();
|
|
|
89328
89516
|
init_plan_runtime();
|
|
89329
89517
|
init_plan_decompose();
|
|
89330
89518
|
import { existsSync as existsSync14 } from "fs";
|
|
89331
|
-
import { join as
|
|
89519
|
+
import { join as join28 } from "path";
|
|
89332
89520
|
async function planCommand(workdir, config2, options) {
|
|
89333
|
-
const naxDir =
|
|
89521
|
+
const naxDir = join28(workdir, ".nax");
|
|
89334
89522
|
if (!existsSync14(naxDir)) {
|
|
89335
89523
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
89336
89524
|
}
|
|
@@ -89346,13 +89534,13 @@ async function planCommand(workdir, config2, options) {
|
|
|
89346
89534
|
const codebaseContext = buildCodebaseContext(scan);
|
|
89347
89535
|
const relativePackages = discoveredPackages.map((p) => p.startsWith("/") ? p.replace(`${workdir}/`, "") : p);
|
|
89348
89536
|
const packageDetails = relativePackages.length > 0 ? await Promise.all(relativePackages.map(async (rel) => {
|
|
89349
|
-
const pkgJson = await _planDeps.readPackageJsonAt(
|
|
89537
|
+
const pkgJson = await _planDeps.readPackageJsonAt(join28(workdir, rel, "package.json"));
|
|
89350
89538
|
return buildPackageSummary(rel, pkgJson);
|
|
89351
89539
|
})) : [];
|
|
89352
89540
|
const projectName = detectProjectName(workdir, pkg);
|
|
89353
89541
|
const branchName = options.branch ?? `feat/${options.feature}`;
|
|
89354
|
-
const outputDir =
|
|
89355
|
-
const outputPath =
|
|
89542
|
+
const outputDir = join28(naxDir, "features", options.feature);
|
|
89543
|
+
const outputPath = join28(outputDir, "prd.json");
|
|
89356
89544
|
await _planDeps.mkdirp(outputDir);
|
|
89357
89545
|
const defaultAgentName = resolveDefaultAgent(config2);
|
|
89358
89546
|
const timeoutSeconds = config2?.plan?.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS2;
|
|
@@ -89716,7 +89904,7 @@ init_interaction();
|
|
|
89716
89904
|
init_prd();
|
|
89717
89905
|
init_runtime();
|
|
89718
89906
|
import { existsSync as existsSync16, readdirSync as readdirSync4 } from "fs";
|
|
89719
|
-
import { basename as basename7, join as
|
|
89907
|
+
import { basename as basename7, join as join31, resolve as resolve14 } from "path";
|
|
89720
89908
|
var _statusFeaturesDeps = {
|
|
89721
89909
|
projectOutputDir,
|
|
89722
89910
|
loadConfig
|
|
@@ -89730,7 +89918,7 @@ function isPidAlive(pid) {
|
|
|
89730
89918
|
}
|
|
89731
89919
|
}
|
|
89732
89920
|
async function loadStatusFile(featureDir) {
|
|
89733
|
-
const statusPath =
|
|
89921
|
+
const statusPath = join31(featureDir, "status.json");
|
|
89734
89922
|
if (!existsSync16(statusPath)) {
|
|
89735
89923
|
return null;
|
|
89736
89924
|
}
|
|
@@ -89745,7 +89933,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
89745
89933
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
89746
89934
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
89747
89935
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
89748
|
-
const statusPath =
|
|
89936
|
+
const statusPath = join31(outputDir, "status.json");
|
|
89749
89937
|
if (!existsSync16(statusPath)) {
|
|
89750
89938
|
return null;
|
|
89751
89939
|
}
|
|
@@ -89757,7 +89945,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
89757
89945
|
}
|
|
89758
89946
|
}
|
|
89759
89947
|
async function getFeatureSummary(featureName, featureDir) {
|
|
89760
|
-
const prdPath =
|
|
89948
|
+
const prdPath = join31(featureDir, "prd.json");
|
|
89761
89949
|
if (!existsSync16(prdPath)) {
|
|
89762
89950
|
return {
|
|
89763
89951
|
name: featureName,
|
|
@@ -89800,7 +89988,7 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
89800
89988
|
};
|
|
89801
89989
|
}
|
|
89802
89990
|
}
|
|
89803
|
-
const runsDir =
|
|
89991
|
+
const runsDir = join31(featureDir, "runs");
|
|
89804
89992
|
if (existsSync16(runsDir)) {
|
|
89805
89993
|
const runs = readdirSync4(runsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl") && e.name !== "latest.jsonl").map((e) => e.name).sort().reverse();
|
|
89806
89994
|
if (runs.length > 0) {
|
|
@@ -89814,7 +90002,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
89814
90002
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
89815
90003
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
89816
90004
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
89817
|
-
const featuresDir =
|
|
90005
|
+
const featuresDir = join31(outputDir, "features");
|
|
89818
90006
|
if (!existsSync16(featuresDir)) {
|
|
89819
90007
|
console.log(source_default.dim("No features found."));
|
|
89820
90008
|
return;
|
|
@@ -89855,7 +90043,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
89855
90043
|
console.log();
|
|
89856
90044
|
}
|
|
89857
90045
|
}
|
|
89858
|
-
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name,
|
|
90046
|
+
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join31(featuresDir, name))));
|
|
89859
90047
|
console.log(source_default.bold(`\uD83D\uDCCA Features
|
|
89860
90048
|
`));
|
|
89861
90049
|
const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
|
|
@@ -89881,7 +90069,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
89881
90069
|
console.log();
|
|
89882
90070
|
}
|
|
89883
90071
|
async function displayFeatureDetails(featureName, featureDir) {
|
|
89884
|
-
const prdPath =
|
|
90072
|
+
const prdPath = join31(featureDir, "prd.json");
|
|
89885
90073
|
if (!existsSync16(prdPath)) {
|
|
89886
90074
|
console.log(source_default.bold(`
|
|
89887
90075
|
\uD83D\uDCCA ${featureName}
|
|
@@ -90027,7 +90215,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
90027
90215
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
90028
90216
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
90029
90217
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
90030
|
-
featureDir =
|
|
90218
|
+
featureDir = join31(outputDir, "features", options.feature);
|
|
90031
90219
|
} else {
|
|
90032
90220
|
const resolved = resolveProject({ feature: options.feature });
|
|
90033
90221
|
if (!resolved.featureDir) {
|
|
@@ -90047,7 +90235,7 @@ init_errors();
|
|
|
90047
90235
|
init_logger2();
|
|
90048
90236
|
init_runtime();
|
|
90049
90237
|
import { existsSync as existsSync17, readdirSync as readdirSync5 } from "fs";
|
|
90050
|
-
import { basename as basename8, join as
|
|
90238
|
+
import { basename as basename8, join as join32 } from "path";
|
|
90051
90239
|
async function resolveOutputDir2(workdir, override) {
|
|
90052
90240
|
if (override)
|
|
90053
90241
|
return override;
|
|
@@ -90071,7 +90259,7 @@ async function runsListCommand(options) {
|
|
|
90071
90259
|
const logger = getLogger();
|
|
90072
90260
|
const { feature, workdir } = options;
|
|
90073
90261
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
90074
|
-
const runsDir =
|
|
90262
|
+
const runsDir = join32(outputDir, "features", feature, "runs");
|
|
90075
90263
|
if (!existsSync17(runsDir)) {
|
|
90076
90264
|
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
90077
90265
|
return;
|
|
@@ -90083,7 +90271,7 @@ async function runsListCommand(options) {
|
|
|
90083
90271
|
}
|
|
90084
90272
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
90085
90273
|
for (const file3 of files.sort().reverse()) {
|
|
90086
|
-
const logPath =
|
|
90274
|
+
const logPath = join32(runsDir, file3);
|
|
90087
90275
|
const entries = await parseRunLog(logPath);
|
|
90088
90276
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
90089
90277
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
@@ -90110,7 +90298,7 @@ async function runsShowCommand(options) {
|
|
|
90110
90298
|
const logger = getLogger();
|
|
90111
90299
|
const { runId, feature, workdir } = options;
|
|
90112
90300
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
90113
|
-
const logPath =
|
|
90301
|
+
const logPath = join32(outputDir, "features", feature, "runs", `${runId}.jsonl`);
|
|
90114
90302
|
if (!existsSync17(logPath)) {
|
|
90115
90303
|
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
90116
90304
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
@@ -90223,7 +90411,7 @@ init_logger2();
|
|
|
90223
90411
|
init_prd();
|
|
90224
90412
|
init_runtime();
|
|
90225
90413
|
import { existsSync as existsSync25, readdirSync as readdirSync6 } from "fs";
|
|
90226
|
-
import { basename as basename12, join as
|
|
90414
|
+
import { basename as basename12, join as join52 } from "path";
|
|
90227
90415
|
|
|
90228
90416
|
// src/cli/diagnose-analysis.ts
|
|
90229
90417
|
function detectFailurePattern(story, _prd, status) {
|
|
@@ -90426,7 +90614,7 @@ function isProcessAlive2(pid) {
|
|
|
90426
90614
|
}
|
|
90427
90615
|
}
|
|
90428
90616
|
async function loadStatusFile2(outputDir) {
|
|
90429
|
-
const statusPath =
|
|
90617
|
+
const statusPath = join52(outputDir, "status.json");
|
|
90430
90618
|
if (!existsSync25(statusPath))
|
|
90431
90619
|
return null;
|
|
90432
90620
|
try {
|
|
@@ -90454,7 +90642,7 @@ async function countCommitsSince(workdir, since) {
|
|
|
90454
90642
|
}
|
|
90455
90643
|
}
|
|
90456
90644
|
async function checkLock(workdir) {
|
|
90457
|
-
const lockFile = Bun.file(
|
|
90645
|
+
const lockFile = Bun.file(join52(workdir, "nax.lock"));
|
|
90458
90646
|
if (!await lockFile.exists())
|
|
90459
90647
|
return { lockPresent: false };
|
|
90460
90648
|
try {
|
|
@@ -90472,8 +90660,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
90472
90660
|
const logger = getLogger();
|
|
90473
90661
|
const workdir = options.workdir ?? process.cwd();
|
|
90474
90662
|
const naxSubdir = findProjectDir(workdir);
|
|
90475
|
-
let projectDir = naxSubdir ?
|
|
90476
|
-
if (!projectDir && existsSync25(
|
|
90663
|
+
let projectDir = naxSubdir ? join52(naxSubdir, "..") : null;
|
|
90664
|
+
if (!projectDir && existsSync25(join52(workdir, ".nax"))) {
|
|
90477
90665
|
projectDir = workdir;
|
|
90478
90666
|
}
|
|
90479
90667
|
if (!projectDir)
|
|
@@ -90487,7 +90675,7 @@ async function diagnoseCommand(options = {}) {
|
|
|
90487
90675
|
if (status2) {
|
|
90488
90676
|
feature = status2.run.feature;
|
|
90489
90677
|
} else {
|
|
90490
|
-
const featuresDir =
|
|
90678
|
+
const featuresDir = join52(outputDir, "features");
|
|
90491
90679
|
if (!existsSync25(featuresDir))
|
|
90492
90680
|
throw new Error("No features found in project");
|
|
90493
90681
|
const features = readdirSync6(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
@@ -90497,8 +90685,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
90497
90685
|
logger.info("diagnose", "No feature specified, using first found", { feature });
|
|
90498
90686
|
}
|
|
90499
90687
|
}
|
|
90500
|
-
const featureDir =
|
|
90501
|
-
const prdPath =
|
|
90688
|
+
const featureDir = join52(outputDir, "features", feature);
|
|
90689
|
+
const prdPath = join52(featureDir, "prd.json");
|
|
90502
90690
|
if (!existsSync25(prdPath))
|
|
90503
90691
|
throw new Error(`Feature not found: ${feature}`);
|
|
90504
90692
|
const prd = await loadPRD(prdPath);
|
|
@@ -90543,7 +90731,7 @@ init_source();
|
|
|
90543
90731
|
init_loader();
|
|
90544
90732
|
init_generator2();
|
|
90545
90733
|
import { existsSync as existsSync26 } from "fs";
|
|
90546
|
-
import { join as
|
|
90734
|
+
import { join as join53 } from "path";
|
|
90547
90735
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
90548
90736
|
async function generateCommand(options) {
|
|
90549
90737
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -90586,7 +90774,7 @@ async function generateCommand(options) {
|
|
|
90586
90774
|
return;
|
|
90587
90775
|
}
|
|
90588
90776
|
if (options.package) {
|
|
90589
|
-
const packageDir =
|
|
90777
|
+
const packageDir = join53(workdir, options.package);
|
|
90590
90778
|
if (dryRun) {
|
|
90591
90779
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
90592
90780
|
}
|
|
@@ -90606,8 +90794,8 @@ async function generateCommand(options) {
|
|
|
90606
90794
|
process.exit(1);
|
|
90607
90795
|
return;
|
|
90608
90796
|
}
|
|
90609
|
-
const contextPath = options.context ?
|
|
90610
|
-
const outputDir = options.output ?
|
|
90797
|
+
const contextPath = options.context ? join53(workdir, options.context) : join53(workdir, ".nax/context.md");
|
|
90798
|
+
const outputDir = options.output ? join53(workdir, options.output) : workdir;
|
|
90611
90799
|
const autoInject = !options.noAutoInject;
|
|
90612
90800
|
if (!existsSync26(contextPath)) {
|
|
90613
90801
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -90713,7 +90901,7 @@ async function generateCommand(options) {
|
|
|
90713
90901
|
// src/cli/config-display.ts
|
|
90714
90902
|
init_loader();
|
|
90715
90903
|
import { existsSync as existsSync28 } from "fs";
|
|
90716
|
-
import { join as
|
|
90904
|
+
import { join as join55 } from "path";
|
|
90717
90905
|
|
|
90718
90906
|
// src/cli/config-descriptions.ts
|
|
90719
90907
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -90954,7 +91142,7 @@ function deepEqual(a, b) {
|
|
|
90954
91142
|
init_defaults();
|
|
90955
91143
|
init_loader();
|
|
90956
91144
|
import { existsSync as existsSync27 } from "fs";
|
|
90957
|
-
import { join as
|
|
91145
|
+
import { join as join54 } from "path";
|
|
90958
91146
|
async function loadConfigFile(path19) {
|
|
90959
91147
|
if (!existsSync27(path19))
|
|
90960
91148
|
return null;
|
|
@@ -90976,7 +91164,7 @@ async function loadProjectConfig() {
|
|
|
90976
91164
|
const projectDir = findProjectDir();
|
|
90977
91165
|
if (!projectDir)
|
|
90978
91166
|
return null;
|
|
90979
|
-
const projectPath =
|
|
91167
|
+
const projectPath = join54(projectDir, "config.json");
|
|
90980
91168
|
return await loadConfigFile(projectPath);
|
|
90981
91169
|
}
|
|
90982
91170
|
|
|
@@ -91036,7 +91224,7 @@ async function configCommand(config2, options = {}) {
|
|
|
91036
91224
|
function determineConfigSources() {
|
|
91037
91225
|
const globalPath = globalConfigPath();
|
|
91038
91226
|
const projectDir = findProjectDir();
|
|
91039
|
-
const projectPath = projectDir ?
|
|
91227
|
+
const projectPath = projectDir ? join55(projectDir, "config.json") : null;
|
|
91040
91228
|
return {
|
|
91041
91229
|
global: fileExists(globalPath) ? globalPath : null,
|
|
91042
91230
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -91185,15 +91373,15 @@ init_paths();
|
|
|
91185
91373
|
init_profile();
|
|
91186
91374
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
91187
91375
|
import { readdirSync as readdirSync7 } from "fs";
|
|
91188
|
-
import { join as
|
|
91376
|
+
import { join as join56 } from "path";
|
|
91189
91377
|
var _profileCLIDeps = {
|
|
91190
91378
|
env: process.env
|
|
91191
91379
|
};
|
|
91192
91380
|
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
91193
91381
|
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
91194
91382
|
async function profileListCommand(startDir) {
|
|
91195
|
-
const globalProfilesDir =
|
|
91196
|
-
const projectProfilesDir =
|
|
91383
|
+
const globalProfilesDir = join56(globalConfigDir(), "profiles");
|
|
91384
|
+
const projectProfilesDir = join56(projectConfigDir(startDir), "profiles");
|
|
91197
91385
|
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
91198
91386
|
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
91199
91387
|
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
@@ -91252,7 +91440,7 @@ function maskProfileValues(obj) {
|
|
|
91252
91440
|
return result;
|
|
91253
91441
|
}
|
|
91254
91442
|
async function profileUseCommand(profileName, startDir) {
|
|
91255
|
-
const configPath =
|
|
91443
|
+
const configPath = join56(projectConfigDir(startDir), "config.json");
|
|
91256
91444
|
const configFile = Bun.file(configPath);
|
|
91257
91445
|
let existing = {};
|
|
91258
91446
|
if (await configFile.exists()) {
|
|
@@ -91271,8 +91459,8 @@ async function profileCurrentCommand(startDir) {
|
|
|
91271
91459
|
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
91272
91460
|
}
|
|
91273
91461
|
async function profileCreateCommand(profileName, startDir) {
|
|
91274
|
-
const profilesDir =
|
|
91275
|
-
const profilePath =
|
|
91462
|
+
const profilesDir = join56(projectConfigDir(startDir), "profiles");
|
|
91463
|
+
const profilePath = join56(profilesDir, `${profileName}.json`);
|
|
91276
91464
|
const profileFile = Bun.file(profilePath);
|
|
91277
91465
|
if (await profileFile.exists()) {
|
|
91278
91466
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
@@ -91394,7 +91582,7 @@ async function contextInspectCommand(options) {
|
|
|
91394
91582
|
init_canonical_loader();
|
|
91395
91583
|
init_errors();
|
|
91396
91584
|
import { mkdir as mkdir12 } from "fs/promises";
|
|
91397
|
-
import { basename as basename13, join as
|
|
91585
|
+
import { basename as basename13, join as join57 } from "path";
|
|
91398
91586
|
var _rulesCLIDeps = {
|
|
91399
91587
|
readFile: async (path19) => Bun.file(path19).text(),
|
|
91400
91588
|
writeFile: async (path19, content) => {
|
|
@@ -91403,7 +91591,7 @@ var _rulesCLIDeps = {
|
|
|
91403
91591
|
fileExists: async (path19) => Bun.file(path19).exists(),
|
|
91404
91592
|
globInDir: (dir) => {
|
|
91405
91593
|
try {
|
|
91406
|
-
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) =>
|
|
91594
|
+
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join57(dir, f));
|
|
91407
91595
|
} catch {
|
|
91408
91596
|
return [];
|
|
91409
91597
|
}
|
|
@@ -91452,7 +91640,7 @@ ${r.content}`).join(`
|
|
|
91452
91640
|
`);
|
|
91453
91641
|
const shimContent = `${header + body}
|
|
91454
91642
|
`;
|
|
91455
|
-
const shimPath =
|
|
91643
|
+
const shimPath = join57(workdir, shimFileName);
|
|
91456
91644
|
if (options.dryRun) {
|
|
91457
91645
|
console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
|
|
91458
91646
|
return;
|
|
@@ -91481,14 +91669,14 @@ function neutralizeContent(content) {
|
|
|
91481
91669
|
}
|
|
91482
91670
|
async function collectMigrationSources(workdir) {
|
|
91483
91671
|
const sources = [];
|
|
91484
|
-
const claudeMdPath =
|
|
91672
|
+
const claudeMdPath = join57(workdir, "CLAUDE.md");
|
|
91485
91673
|
if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
|
|
91486
91674
|
const content = await _rulesCLIDeps.readFile(claudeMdPath);
|
|
91487
91675
|
if (content.trim()) {
|
|
91488
91676
|
sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
|
|
91489
91677
|
}
|
|
91490
91678
|
}
|
|
91491
|
-
const rulesDir =
|
|
91679
|
+
const rulesDir = join57(workdir, ".claude", "rules");
|
|
91492
91680
|
const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
|
|
91493
91681
|
for (const filePath of ruleFiles) {
|
|
91494
91682
|
try {
|
|
@@ -91508,7 +91696,7 @@ async function rulesMigrateCommand(options) {
|
|
|
91508
91696
|
console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
|
|
91509
91697
|
return;
|
|
91510
91698
|
}
|
|
91511
|
-
const targetDir =
|
|
91699
|
+
const targetDir = join57(workdir, CANONICAL_RULES_DIR);
|
|
91512
91700
|
if (!options.dryRun) {
|
|
91513
91701
|
try {
|
|
91514
91702
|
await _rulesCLIDeps.mkdir(targetDir);
|
|
@@ -91519,7 +91707,7 @@ async function rulesMigrateCommand(options) {
|
|
|
91519
91707
|
let written = 0;
|
|
91520
91708
|
let skipped = 0;
|
|
91521
91709
|
for (const { sourcePath, targetFileName, content } of sources) {
|
|
91522
|
-
const targetPath =
|
|
91710
|
+
const targetPath = join57(targetDir, targetFileName);
|
|
91523
91711
|
if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
|
|
91524
91712
|
console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
|
|
91525
91713
|
skipped++;
|
|
@@ -91558,7 +91746,7 @@ function collectCanonicalRuleRoots(workdir) {
|
|
|
91558
91746
|
const packageRel = normalized.slice(0, idx);
|
|
91559
91747
|
if (!packageRel)
|
|
91560
91748
|
continue;
|
|
91561
|
-
roots.add(
|
|
91749
|
+
roots.add(join57(workdir, packageRel));
|
|
91562
91750
|
}
|
|
91563
91751
|
return [...roots].sort();
|
|
91564
91752
|
}
|
|
@@ -91580,7 +91768,7 @@ init_logger2();
|
|
|
91580
91768
|
init_detect2();
|
|
91581
91769
|
init_workspace();
|
|
91582
91770
|
init_common();
|
|
91583
|
-
import { join as
|
|
91771
|
+
import { join as join58 } from "path";
|
|
91584
91772
|
function resolveEffective(detected, configPatterns) {
|
|
91585
91773
|
if (configPatterns !== undefined)
|
|
91586
91774
|
return "config";
|
|
@@ -91665,7 +91853,7 @@ async function detectCommand(options) {
|
|
|
91665
91853
|
const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
91666
91854
|
const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
|
|
91667
91855
|
const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
91668
|
-
const pkgConfigPath =
|
|
91856
|
+
const pkgConfigPath = join58(workdir, ".nax", "mono", dir, "config.json");
|
|
91669
91857
|
const pkgRaw = await loadRawConfig(pkgConfigPath);
|
|
91670
91858
|
const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
|
|
91671
91859
|
const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
|
|
@@ -91719,13 +91907,13 @@ async function detectCommand(options) {
|
|
|
91719
91907
|
if (rootDetected.confidence === "empty") {
|
|
91720
91908
|
console.log(source_default.yellow(" root: skipped (empty detection)"));
|
|
91721
91909
|
} else {
|
|
91722
|
-
const rootConfigPath =
|
|
91910
|
+
const rootConfigPath = join58(workdir, ".nax", "config.json");
|
|
91723
91911
|
try {
|
|
91724
91912
|
const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
|
|
91725
91913
|
if (status === "skipped") {
|
|
91726
91914
|
console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
|
|
91727
91915
|
} else {
|
|
91728
|
-
console.log(source_default.green(` root: ${status} \u2192 ${
|
|
91916
|
+
console.log(source_default.green(` root: ${status} \u2192 ${join58(".nax", "config.json")}`));
|
|
91729
91917
|
}
|
|
91730
91918
|
} catch (err) {
|
|
91731
91919
|
console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
|
|
@@ -91738,13 +91926,13 @@ async function detectCommand(options) {
|
|
|
91738
91926
|
console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
|
|
91739
91927
|
continue;
|
|
91740
91928
|
}
|
|
91741
|
-
const pkgConfigPath =
|
|
91929
|
+
const pkgConfigPath = join58(workdir, ".nax", "mono", dir, "config.json");
|
|
91742
91930
|
try {
|
|
91743
91931
|
const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
|
|
91744
91932
|
if (status === "skipped") {
|
|
91745
91933
|
console.log(source_default.dim(` ${dir}: skipped (already set)`));
|
|
91746
91934
|
} else {
|
|
91747
|
-
console.log(source_default.green(` ${dir}: ${status} \u2192 ${
|
|
91935
|
+
console.log(source_default.green(` ${dir}: ${status} \u2192 ${join58(".nax", "mono", dir, "config.json")}`));
|
|
91748
91936
|
}
|
|
91749
91937
|
} catch (err) {
|
|
91750
91938
|
console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
|
|
@@ -91767,24 +91955,24 @@ async function diagnose(options) {
|
|
|
91767
91955
|
// src/commands/logs.ts
|
|
91768
91956
|
init_common();
|
|
91769
91957
|
import { existsSync as existsSync30 } from "fs";
|
|
91770
|
-
import { join as
|
|
91958
|
+
import { join as join62 } from "path";
|
|
91771
91959
|
|
|
91772
91960
|
// src/commands/logs-formatter.ts
|
|
91773
91961
|
init_source();
|
|
91774
91962
|
init_formatter();
|
|
91775
91963
|
import { readdirSync as readdirSync9 } from "fs";
|
|
91776
|
-
import { join as
|
|
91964
|
+
import { join as join61 } from "path";
|
|
91777
91965
|
|
|
91778
91966
|
// src/commands/logs-reader.ts
|
|
91779
91967
|
import { existsSync as existsSync29, readdirSync as readdirSync8 } from "fs";
|
|
91780
91968
|
import { readdir as readdir4 } from "fs/promises";
|
|
91781
|
-
import { join as
|
|
91969
|
+
import { join as join60 } from "path";
|
|
91782
91970
|
|
|
91783
91971
|
// src/utils/paths.ts
|
|
91784
91972
|
import { homedir as homedir4 } from "os";
|
|
91785
|
-
import { join as
|
|
91973
|
+
import { join as join59 } from "path";
|
|
91786
91974
|
function getRunsDir() {
|
|
91787
|
-
return process.env.NAX_RUNS_DIR ??
|
|
91975
|
+
return process.env.NAX_RUNS_DIR ?? join59(homedir4(), ".nax", "runs");
|
|
91788
91976
|
}
|
|
91789
91977
|
|
|
91790
91978
|
// src/commands/logs-reader.ts
|
|
@@ -91801,7 +91989,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
91801
91989
|
}
|
|
91802
91990
|
let matched = null;
|
|
91803
91991
|
for (const entry of entries) {
|
|
91804
|
-
const metaPath =
|
|
91992
|
+
const metaPath = join60(runsDir, entry, "meta.json");
|
|
91805
91993
|
try {
|
|
91806
91994
|
const meta3 = await Bun.file(metaPath).json();
|
|
91807
91995
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -91823,14 +92011,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
91823
92011
|
return null;
|
|
91824
92012
|
}
|
|
91825
92013
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
91826
|
-
return
|
|
92014
|
+
return join60(matched.eventsDir, specificFile ?? files[0]);
|
|
91827
92015
|
}
|
|
91828
92016
|
async function selectRunFile(runsDir) {
|
|
91829
92017
|
const files = readdirSync8(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
91830
92018
|
if (files.length === 0) {
|
|
91831
92019
|
return null;
|
|
91832
92020
|
}
|
|
91833
|
-
return
|
|
92021
|
+
return join60(runsDir, files[0]);
|
|
91834
92022
|
}
|
|
91835
92023
|
async function extractRunSummary(filePath) {
|
|
91836
92024
|
const file3 = Bun.file(filePath);
|
|
@@ -91915,7 +92103,7 @@ Runs:
|
|
|
91915
92103
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
91916
92104
|
console.log(source_default.gray(" \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"));
|
|
91917
92105
|
for (const file3 of files) {
|
|
91918
|
-
const filePath =
|
|
92106
|
+
const filePath = join61(runsDir, file3);
|
|
91919
92107
|
const summary = await extractRunSummary(filePath);
|
|
91920
92108
|
const timestamp = file3.replace(".jsonl", "");
|
|
91921
92109
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -92029,7 +92217,7 @@ async function logsCommand(options) {
|
|
|
92029
92217
|
return;
|
|
92030
92218
|
}
|
|
92031
92219
|
const resolved = resolveProject({ dir: options.dir });
|
|
92032
|
-
const naxDir =
|
|
92220
|
+
const naxDir = join62(resolved.projectDir, ".nax");
|
|
92033
92221
|
const configPath = resolved.configPath;
|
|
92034
92222
|
const configFile = Bun.file(configPath);
|
|
92035
92223
|
const config2 = await configFile.json();
|
|
@@ -92037,8 +92225,8 @@ async function logsCommand(options) {
|
|
|
92037
92225
|
if (!featureName) {
|
|
92038
92226
|
throw new Error("No feature specified in config.json");
|
|
92039
92227
|
}
|
|
92040
|
-
const featureDir =
|
|
92041
|
-
const runsDir =
|
|
92228
|
+
const featureDir = join62(naxDir, "features", featureName);
|
|
92229
|
+
const runsDir = join62(featureDir, "runs");
|
|
92042
92230
|
if (!existsSync30(runsDir)) {
|
|
92043
92231
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
92044
92232
|
}
|
|
@@ -92064,7 +92252,7 @@ init_prd();
|
|
|
92064
92252
|
init_precheck();
|
|
92065
92253
|
init_common();
|
|
92066
92254
|
import { existsSync as existsSync31 } from "fs";
|
|
92067
|
-
import { join as
|
|
92255
|
+
import { join as join63 } from "path";
|
|
92068
92256
|
async function precheckCommand(options) {
|
|
92069
92257
|
const resolved = resolveProject({
|
|
92070
92258
|
dir: options.dir,
|
|
@@ -92086,9 +92274,9 @@ async function precheckCommand(options) {
|
|
|
92086
92274
|
process.exit(1);
|
|
92087
92275
|
}
|
|
92088
92276
|
}
|
|
92089
|
-
const naxDir =
|
|
92090
|
-
const featureDir =
|
|
92091
|
-
const prdPath =
|
|
92277
|
+
const naxDir = join63(resolved.projectDir, ".nax");
|
|
92278
|
+
const featureDir = join63(naxDir, "features", featureName);
|
|
92279
|
+
const prdPath = join63(featureDir, "prd.json");
|
|
92092
92280
|
if (!existsSync31(featureDir)) {
|
|
92093
92281
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
92094
92282
|
process.exit(1);
|
|
@@ -92110,7 +92298,7 @@ async function precheckCommand(options) {
|
|
|
92110
92298
|
// src/commands/runs.ts
|
|
92111
92299
|
init_source();
|
|
92112
92300
|
import { readdir as readdir5 } from "fs/promises";
|
|
92113
|
-
import { join as
|
|
92301
|
+
import { join as join64 } from "path";
|
|
92114
92302
|
var DEFAULT_LIMIT = 20;
|
|
92115
92303
|
var _runsCmdDeps = {
|
|
92116
92304
|
getRunsDir
|
|
@@ -92165,7 +92353,7 @@ async function runsCommand(options = {}) {
|
|
|
92165
92353
|
}
|
|
92166
92354
|
const rows = [];
|
|
92167
92355
|
for (const entry of entries) {
|
|
92168
|
-
const metaPath =
|
|
92356
|
+
const metaPath = join64(runsDir, entry, "meta.json");
|
|
92169
92357
|
let meta3;
|
|
92170
92358
|
try {
|
|
92171
92359
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -92242,7 +92430,7 @@ async function runsCommand(options = {}) {
|
|
|
92242
92430
|
|
|
92243
92431
|
// src/commands/unlock.ts
|
|
92244
92432
|
init_source();
|
|
92245
|
-
import { join as
|
|
92433
|
+
import { join as join65 } from "path";
|
|
92246
92434
|
function isProcessAlive3(pid) {
|
|
92247
92435
|
try {
|
|
92248
92436
|
process.kill(pid, 0);
|
|
@@ -92257,7 +92445,7 @@ function formatLockAge(ageMs) {
|
|
|
92257
92445
|
}
|
|
92258
92446
|
async function unlockCommand(options) {
|
|
92259
92447
|
const workdir = options.dir ?? process.cwd();
|
|
92260
|
-
const lockPath =
|
|
92448
|
+
const lockPath = join65(workdir, "nax.lock");
|
|
92261
92449
|
const lockFile = Bun.file(lockPath);
|
|
92262
92450
|
const exists = await lockFile.exists();
|
|
92263
92451
|
if (!exists) {
|
|
@@ -100472,6 +100660,7 @@ program2.command("migrate").description("Migrate generated content from .nax/ to
|
|
|
100472
100660
|
return;
|
|
100473
100661
|
}
|
|
100474
100662
|
const { migrateCommand: migrateCommand2 } = await Promise.resolve().then(() => (init_migrate(), exports_migrate));
|
|
100663
|
+
initLogger({ level: "info", useChalk: true });
|
|
100475
100664
|
try {
|
|
100476
100665
|
await migrateCommand2({
|
|
100477
100666
|
workdir,
|