@nathapp/nax 0.69.0 → 0.69.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 +1436 -566
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -16983,10 +16983,10 @@ var init_schemas_execution = __esm(() => {
|
|
|
16983
16983
|
autoApproveVerifier: exports_external.boolean(),
|
|
16984
16984
|
strategy: exports_external.enum(["auto", "strict", "lite", "off"]).default("auto"),
|
|
16985
16985
|
sessionTiers: exports_external.object({
|
|
16986
|
-
testWriter:
|
|
16987
|
-
|
|
16988
|
-
|
|
16989
|
-
}).
|
|
16986
|
+
testWriter: ConfiguredModelSchema.default("fast"),
|
|
16987
|
+
verifier: ConfiguredModelSchema.default("fast"),
|
|
16988
|
+
implementer: ConfiguredModelSchema.optional()
|
|
16989
|
+
}).default({ testWriter: "fast", verifier: "fast" }),
|
|
16990
16990
|
testWriterAllowedPaths: exports_external.array(exports_external.string()).optional(),
|
|
16991
16991
|
rollbackOnFailure: exports_external.boolean().optional(),
|
|
16992
16992
|
greenfieldDetection: exports_external.boolean().optional()
|
|
@@ -17221,6 +17221,12 @@ var init_schemas_review = __esm(() => {
|
|
|
17221
17221
|
substantiation: exports_external.object({
|
|
17222
17222
|
requote: exports_external.boolean().default(true),
|
|
17223
17223
|
maxRequotes: exports_external.number().int().min(0).default(5)
|
|
17224
|
+
}).optional(),
|
|
17225
|
+
nonBlockingFix: exports_external.object({
|
|
17226
|
+
enabled: exports_external.boolean().default(false),
|
|
17227
|
+
scope: exports_external.enum(["source", "both"]).default("both"),
|
|
17228
|
+
regressionAttempts: exports_external.number().int().min(0).default(1),
|
|
17229
|
+
verifierGuard: exports_external.boolean().default(true)
|
|
17224
17230
|
}).optional()
|
|
17225
17231
|
});
|
|
17226
17232
|
ReviewConfigSchema = exports_external.object({
|
|
@@ -17407,7 +17413,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17407
17413
|
autoApproveVerifier: true,
|
|
17408
17414
|
strategy: "auto",
|
|
17409
17415
|
sessionTiers: {
|
|
17410
|
-
testWriter: "
|
|
17416
|
+
testWriter: "fast",
|
|
17411
17417
|
verifier: "fast"
|
|
17412
17418
|
},
|
|
17413
17419
|
testWriterAllowedPaths: ["src/index.ts", "src/**/index.ts"],
|
|
@@ -18940,6 +18946,14 @@ ${errors3.join(`
|
|
|
18940
18946
|
}
|
|
18941
18947
|
return result.data;
|
|
18942
18948
|
}
|
|
18949
|
+
async function loadPackageOverride(repoRoot, packageDir) {
|
|
18950
|
+
const packageConfigPath = join3(repoRoot, PROJECT_NAX_DIR, "mono", packageDir, "config.json");
|
|
18951
|
+
const override = await loadJsonFile(packageConfigPath, "config");
|
|
18952
|
+
if (!override)
|
|
18953
|
+
return null;
|
|
18954
|
+
const { profile: _profile, ...fields } = override;
|
|
18955
|
+
return fields;
|
|
18956
|
+
}
|
|
18943
18957
|
async function loadConfigForWorkdir(rootConfigPath, packageDir, cliOverrides) {
|
|
18944
18958
|
const logger = getLogger();
|
|
18945
18959
|
const resolvedRootConfigPath = resolve3(rootConfigPath);
|
|
@@ -19004,6 +19018,106 @@ var init_loader = __esm(() => {
|
|
|
19004
19018
|
init_schema();
|
|
19005
19019
|
_rootConfigCache = new Map;
|
|
19006
19020
|
});
|
|
19021
|
+
|
|
19022
|
+
// src/config/validate.ts
|
|
19023
|
+
function validateConfig(config2) {
|
|
19024
|
+
const errors3 = [];
|
|
19025
|
+
if (config2.version !== 1) {
|
|
19026
|
+
errors3.push(`Invalid version: expected 1, got ${config2.version}`);
|
|
19027
|
+
}
|
|
19028
|
+
const requiredTiers = ["fast", "balanced", "powerful"];
|
|
19029
|
+
if (!config2.models) {
|
|
19030
|
+
errors3.push("models mapping is required");
|
|
19031
|
+
} else {
|
|
19032
|
+
const defaultAgent = config2.agent?.default ?? "claude";
|
|
19033
|
+
const agentModels = config2.models[defaultAgent];
|
|
19034
|
+
if (!agentModels) {
|
|
19035
|
+
errors3.push(`models.${defaultAgent} is required (default agent has no model map)`);
|
|
19036
|
+
} else {
|
|
19037
|
+
for (const tier of requiredTiers) {
|
|
19038
|
+
const entry = agentModels[tier];
|
|
19039
|
+
if (!entry) {
|
|
19040
|
+
errors3.push(`models.${defaultAgent}.${tier} is required`);
|
|
19041
|
+
} else if (typeof entry === "string") {
|
|
19042
|
+
if (entry.trim() === "") {
|
|
19043
|
+
errors3.push(`models.${defaultAgent}.${tier} must be a non-empty model identifier`);
|
|
19044
|
+
}
|
|
19045
|
+
} else {
|
|
19046
|
+
if (!entry.provider || entry.provider.trim() === "") {
|
|
19047
|
+
errors3.push(`models.${defaultAgent}.${tier}.provider must be non-empty`);
|
|
19048
|
+
}
|
|
19049
|
+
if (!entry.model || entry.model.trim() === "") {
|
|
19050
|
+
errors3.push(`models.${defaultAgent}.${tier}.model must be non-empty`);
|
|
19051
|
+
}
|
|
19052
|
+
}
|
|
19053
|
+
}
|
|
19054
|
+
}
|
|
19055
|
+
}
|
|
19056
|
+
if (config2.execution.maxIterations <= 0) {
|
|
19057
|
+
errors3.push(`maxIterations must be > 0, got ${config2.execution.maxIterations}`);
|
|
19058
|
+
}
|
|
19059
|
+
if (config2.execution.costLimit <= 0) {
|
|
19060
|
+
errors3.push(`costLimit must be > 0, got ${config2.execution.costLimit}`);
|
|
19061
|
+
}
|
|
19062
|
+
if (config2.execution.sessionTimeoutSeconds <= 0) {
|
|
19063
|
+
errors3.push(`sessionTimeoutSeconds must be > 0, got ${config2.execution.sessionTimeoutSeconds}`);
|
|
19064
|
+
}
|
|
19065
|
+
const agentDefault = config2.agent?.default;
|
|
19066
|
+
if (!agentDefault || agentDefault.trim() === "") {
|
|
19067
|
+
errors3.push("agent.default must be non-empty");
|
|
19068
|
+
}
|
|
19069
|
+
if (!config2.autoMode.escalation.tierOrder || config2.autoMode.escalation.tierOrder.length === 0) {
|
|
19070
|
+
errors3.push("escalation.tierOrder must have at least one tier");
|
|
19071
|
+
} else {
|
|
19072
|
+
for (const tc of config2.autoMode.escalation.tierOrder) {
|
|
19073
|
+
if (tc.attempts < 1 || tc.attempts > 20) {
|
|
19074
|
+
errors3.push(`escalation.tierOrder: tier "${tc.tier}" attempts must be 1-20, got ${tc.attempts}`);
|
|
19075
|
+
}
|
|
19076
|
+
}
|
|
19077
|
+
}
|
|
19078
|
+
if (config2.models && config2.agent?.fallback?.map) {
|
|
19079
|
+
const modelKeys = Object.keys(config2.models);
|
|
19080
|
+
const fallbackAgents = new Set;
|
|
19081
|
+
for (const [primary, candidates] of Object.entries(config2.agent.fallback.map)) {
|
|
19082
|
+
fallbackAgents.add(primary);
|
|
19083
|
+
for (const c of candidates)
|
|
19084
|
+
fallbackAgents.add(c);
|
|
19085
|
+
}
|
|
19086
|
+
for (const agent of fallbackAgents) {
|
|
19087
|
+
if (!modelKeys.includes(agent)) {
|
|
19088
|
+
errors3.push(`agent.fallback.map: agent "${agent}" is not a key in models (available: ${modelKeys.join(", ")})`);
|
|
19089
|
+
} else {
|
|
19090
|
+
for (const tier of requiredTiers) {
|
|
19091
|
+
if (!config2.models[agent]?.[tier]) {
|
|
19092
|
+
errors3.push(`models.${agent}.${tier} is required (fallback agent "${agent}" in agent.fallback.map)`);
|
|
19093
|
+
}
|
|
19094
|
+
}
|
|
19095
|
+
}
|
|
19096
|
+
}
|
|
19097
|
+
}
|
|
19098
|
+
if (config2.models && config2.autoMode?.escalation?.tierOrder) {
|
|
19099
|
+
const modelKeys = Object.keys(config2.models);
|
|
19100
|
+
for (const tc of config2.autoMode.escalation.tierOrder) {
|
|
19101
|
+
if (tc.agent !== undefined && !modelKeys.includes(tc.agent)) {
|
|
19102
|
+
errors3.push(`autoMode.escalation.tierOrder: tier "${tc.tier}" agent "${tc.agent}" is not a key in models (available: ${modelKeys.join(", ")})`);
|
|
19103
|
+
}
|
|
19104
|
+
}
|
|
19105
|
+
}
|
|
19106
|
+
const defaultAgentKey = config2.agent?.default ?? "claude";
|
|
19107
|
+
const configuredTiers = Object.keys(config2.models[defaultAgentKey] ?? {});
|
|
19108
|
+
const complexities = ["simple", "medium", "complex", "expert"];
|
|
19109
|
+
for (const complexity of complexities) {
|
|
19110
|
+
const tier = config2.autoMode.complexityRouting[complexity];
|
|
19111
|
+
if (!configuredTiers.includes(tier)) {
|
|
19112
|
+
errors3.push(`complexityRouting.${complexity} must be one of: ${configuredTiers.join(", ")} (got '${tier}')`);
|
|
19113
|
+
}
|
|
19114
|
+
}
|
|
19115
|
+
return {
|
|
19116
|
+
valid: errors3.length === 0,
|
|
19117
|
+
errors: errors3
|
|
19118
|
+
};
|
|
19119
|
+
}
|
|
19120
|
+
|
|
19007
19121
|
// src/config/selector.ts
|
|
19008
19122
|
function pickSelector(name, ...keys) {
|
|
19009
19123
|
return {
|
|
@@ -19286,10 +19400,81 @@ var init_test_strategy = __esm(() => {
|
|
|
19286
19400
|
});
|
|
19287
19401
|
|
|
19288
19402
|
// src/config/index.ts
|
|
19403
|
+
var exports_config = {};
|
|
19404
|
+
__export(exports_config, {
|
|
19405
|
+
verifyConfigSelector: () => verifyConfigSelector,
|
|
19406
|
+
validateFilePath: () => validateFilePath,
|
|
19407
|
+
validateDirectory: () => validateDirectory,
|
|
19408
|
+
validateConfig: () => validateConfig,
|
|
19409
|
+
testPatternConfigSelector: () => testPatternConfigSelector,
|
|
19410
|
+
tddConfigSelector: () => tddConfigSelector,
|
|
19411
|
+
routingConfigSelector: () => routingConfigSelector,
|
|
19412
|
+
reviewConfigSelector: () => reviewConfigSelector,
|
|
19413
|
+
resolveTestStrategy: () => resolveTestStrategy,
|
|
19414
|
+
resolveProfileName: () => resolveProfileName,
|
|
19415
|
+
resolveModelForAgent: () => resolveModelForAgent,
|
|
19416
|
+
resolveModel: () => resolveModel,
|
|
19417
|
+
resolveConfiguredModel: () => resolveConfiguredModel,
|
|
19418
|
+
reshapeSelector: () => reshapeSelector,
|
|
19419
|
+
rectifyConfigSelector: () => rectifyConfigSelector,
|
|
19420
|
+
rectificationGateConfigSelector: () => rectificationGateConfigSelector,
|
|
19421
|
+
qualityConfigSelector: () => qualityConfigSelector,
|
|
19422
|
+
promptLoaderConfigSelector: () => promptLoaderConfigSelector,
|
|
19423
|
+
projectConfigDir: () => projectConfigDir,
|
|
19424
|
+
precheckConfigSelector: () => precheckConfigSelector,
|
|
19425
|
+
planConfigSelector: () => planConfigSelector,
|
|
19426
|
+
pickSelector: () => pickSelector,
|
|
19427
|
+
mergePackageConfig: () => mergePackageConfig,
|
|
19428
|
+
loadProfileEnv: () => loadProfileEnv,
|
|
19429
|
+
loadProfile: () => loadProfile,
|
|
19430
|
+
loadPackageOverride: () => loadPackageOverride,
|
|
19431
|
+
loadConfigForWorkdir: () => loadConfigForWorkdir,
|
|
19432
|
+
loadConfig: () => loadConfig,
|
|
19433
|
+
llmRoutingConfigSelector: () => llmRoutingConfigSelector,
|
|
19434
|
+
listProfiles: () => listProfiles,
|
|
19435
|
+
isWithinDirectory: () => isWithinDirectory,
|
|
19436
|
+
isThreeSessionStrategy: () => isThreeSessionStrategy,
|
|
19437
|
+
isSingleSessionTestOwningStrategy: () => isSingleSessionTestOwningStrategy,
|
|
19438
|
+
interactionConfigSelector: () => interactionConfigSelector,
|
|
19439
|
+
globalConfigPath: () => globalConfigPath,
|
|
19440
|
+
globalConfigDir: () => globalConfigDir,
|
|
19441
|
+
getAcQualityRules: () => getAcQualityRules,
|
|
19442
|
+
findProjectDir: () => findProjectDir,
|
|
19443
|
+
executionGatesConfigSelector: () => executionGatesConfigSelector,
|
|
19444
|
+
deepMergeConfig: () => deepMergeConfig,
|
|
19445
|
+
decomposeConfigSelector: () => decomposeConfigSelector,
|
|
19446
|
+
debateConfigSelector: () => debateConfigSelector,
|
|
19447
|
+
createConfigLoader: () => createConfigLoader,
|
|
19448
|
+
contextToolRuntimeConfigSelector: () => contextToolRuntimeConfigSelector,
|
|
19449
|
+
autofixConfigSelector: () => autofixConfigSelector,
|
|
19450
|
+
agentManagerConfigSelector: () => agentManagerConfigSelector,
|
|
19451
|
+
acceptanceGenConfigSelector: () => acceptanceGenConfigSelector,
|
|
19452
|
+
acceptanceFixConfigSelector: () => acceptanceFixConfigSelector,
|
|
19453
|
+
acceptanceConfigSelector: () => acceptanceConfigSelector,
|
|
19454
|
+
VALID_TEST_STRATEGIES: () => VALID_TEST_STRATEGIES,
|
|
19455
|
+
TddConfigSchema: () => TddConfigSchema,
|
|
19456
|
+
THREE_SESSION_STRATEGIES: () => THREE_SESSION_STRATEGIES,
|
|
19457
|
+
TEST_STRATEGY_GUIDE: () => TEST_STRATEGY_GUIDE,
|
|
19458
|
+
SPEC_ANCHOR_RULES: () => SPEC_ANCHOR_RULES,
|
|
19459
|
+
SINGLE_SESSION_TEST_OWNING_STRATEGIES: () => SINGLE_SESSION_TEST_OWNING_STRATEGIES,
|
|
19460
|
+
PlanConfigSchema: () => PlanConfigSchema,
|
|
19461
|
+
NaxConfigSchema: () => NaxConfigSchema,
|
|
19462
|
+
ModelTierSchema: () => ModelTierSchema,
|
|
19463
|
+
MAX_DIRECTORY_DEPTH: () => MAX_DIRECTORY_DEPTH,
|
|
19464
|
+
GROUPING_RULES: () => GROUPING_RULES,
|
|
19465
|
+
DebateConfigSchema: () => DebateConfigSchema,
|
|
19466
|
+
DESCRIPTION_QUALITY_RULES: () => DESCRIPTION_QUALITY_RULES,
|
|
19467
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
19468
|
+
ConfiguredModelSchema: () => ConfiguredModelSchema,
|
|
19469
|
+
COMPLEXITY_GUIDE: () => COMPLEXITY_GUIDE,
|
|
19470
|
+
AcceptanceConfigSchema: () => AcceptanceConfigSchema,
|
|
19471
|
+
AC_QUALITY_RULES: () => AC_QUALITY_RULES
|
|
19472
|
+
});
|
|
19289
19473
|
var init_config = __esm(() => {
|
|
19290
19474
|
init_schema();
|
|
19291
19475
|
init_schemas_model();
|
|
19292
19476
|
init_schemas_debate();
|
|
19477
|
+
init_schemas_execution();
|
|
19293
19478
|
init_loader();
|
|
19294
19479
|
init_path_security();
|
|
19295
19480
|
init_paths();
|
|
@@ -23786,6 +23971,7 @@ var init_detect = __esm(() => {
|
|
|
23786
23971
|
});
|
|
23787
23972
|
|
|
23788
23973
|
// src/test-runners/detect/workspace.ts
|
|
23974
|
+
import { join as join6 } from "path";
|
|
23789
23975
|
async function expandWorkspaceGlob(workdir, pattern) {
|
|
23790
23976
|
const dirs = [];
|
|
23791
23977
|
try {
|
|
@@ -23859,15 +24045,31 @@ async function detectTurboOrNx(workdir) {
|
|
|
23859
24045
|
}
|
|
23860
24046
|
async function detectNaxMonoLayout(workdir) {
|
|
23861
24047
|
const dirs = [];
|
|
23862
|
-
|
|
23863
|
-
|
|
23864
|
-
|
|
23865
|
-
|
|
23866
|
-
|
|
23867
|
-
|
|
23868
|
-
|
|
24048
|
+
const monoRoot = join6(workdir, ".nax", "mono");
|
|
24049
|
+
async function walk(currentDir, relativeParts) {
|
|
24050
|
+
const entries = [];
|
|
24051
|
+
for await (const entry of _workspaceDeps.glob("*", currentDir)) {
|
|
24052
|
+
entries.push(entry);
|
|
24053
|
+
}
|
|
24054
|
+
let hasConfig = false;
|
|
24055
|
+
for (const entry of entries) {
|
|
24056
|
+
const entryPath = join6(currentDir, entry);
|
|
24057
|
+
const stat = await _workspaceDeps.stat(entryPath);
|
|
24058
|
+
const isDirectory = (stat.mode & 61440) === 16384;
|
|
24059
|
+
if (!isDirectory && entry === "config.json") {
|
|
24060
|
+
hasConfig = true;
|
|
24061
|
+
continue;
|
|
23869
24062
|
}
|
|
24063
|
+
if (isDirectory) {
|
|
24064
|
+
await walk(entryPath, [...relativeParts, entry]);
|
|
24065
|
+
}
|
|
24066
|
+
}
|
|
24067
|
+
if (hasConfig && relativeParts.length > 0) {
|
|
24068
|
+
dirs.push(relativeParts.join("/"));
|
|
23870
24069
|
}
|
|
24070
|
+
}
|
|
24071
|
+
try {
|
|
24072
|
+
await walk(monoRoot, []);
|
|
23871
24073
|
} catch {}
|
|
23872
24074
|
return dirs;
|
|
23873
24075
|
}
|
|
@@ -23911,7 +24113,8 @@ var init_workspace = __esm(() => {
|
|
|
23911
24113
|
return f.text();
|
|
23912
24114
|
},
|
|
23913
24115
|
spawn: Bun.spawn,
|
|
23914
|
-
glob: (pattern, cwd) => new Bun.Glob(pattern).scan({ cwd, onlyFiles: false })
|
|
24116
|
+
glob: (pattern, cwd) => new Bun.Glob(pattern).scan({ cwd, onlyFiles: false }),
|
|
24117
|
+
stat: (path) => Bun.file(path).stat()
|
|
23915
24118
|
};
|
|
23916
24119
|
_workspaceCache = new Map;
|
|
23917
24120
|
});
|
|
@@ -23985,7 +24188,7 @@ var init_detector2 = __esm(() => {
|
|
|
23985
24188
|
});
|
|
23986
24189
|
|
|
23987
24190
|
// src/test-runners/resolver.ts
|
|
23988
|
-
import { dirname as dirname2, isAbsolute as isAbsolute3, join as
|
|
24191
|
+
import { dirname as dirname2, isAbsolute as isAbsolute3, join as join7, relative, resolve as resolve4 } from "path";
|
|
23989
24192
|
function buildResolved(globs, resolution) {
|
|
23990
24193
|
return {
|
|
23991
24194
|
globs,
|
|
@@ -24037,7 +24240,7 @@ async function resolveTestFilePatterns(config2, workdir, packageDir, options) {
|
|
|
24037
24240
|
validateGlobs(rootPatterns, "resolver");
|
|
24038
24241
|
return buildResolved(rootPatterns, "root-config");
|
|
24039
24242
|
}
|
|
24040
|
-
const detectionWorkdir = packageDir ?
|
|
24243
|
+
const detectionWorkdir = packageDir ? join7(workdir, packageDir) : workdir;
|
|
24041
24244
|
const detected = await _resolverDeps.detectTestFilePatterns(detectionWorkdir);
|
|
24042
24245
|
if (detected.confidence !== "empty" && detected.patterns.length > 0) {
|
|
24043
24246
|
getSafeLogger()?.info("resolver", "Test patterns auto-detected", {
|
|
@@ -24616,7 +24819,7 @@ var init_git = __esm(() => {
|
|
|
24616
24819
|
});
|
|
24617
24820
|
|
|
24618
24821
|
// src/utils/path-filters.ts
|
|
24619
|
-
import { join as
|
|
24822
|
+
import { join as join8, relative as relative2 } from "path";
|
|
24620
24823
|
function basename3(path) {
|
|
24621
24824
|
const stripped = path.startsWith("./") ? path.slice(2) : path;
|
|
24622
24825
|
const idx = stripped.lastIndexOf("/");
|
|
@@ -24706,8 +24909,8 @@ async function resolveNaxIgnorePatterns(repoRoot, packageDir) {
|
|
|
24706
24909
|
const normalizedRepoRoot = normalizePath(repoRoot);
|
|
24707
24910
|
const normalizedPackageDir = packageDir ? normalizePath(packageDir) : normalizedRepoRoot;
|
|
24708
24911
|
const packagePrefix = normalizedPackageDir !== normalizedRepoRoot ? normalizePath(relative2(repoRoot, packageDir ?? repoRoot)) : null;
|
|
24709
|
-
const rootFile =
|
|
24710
|
-
const packageFile =
|
|
24912
|
+
const rootFile = join8(repoRoot, NAX_IGNORE_FILENAME);
|
|
24913
|
+
const packageFile = join8(packageDir ?? repoRoot, NAX_IGNORE_FILENAME);
|
|
24711
24914
|
const rootPatterns = await readIgnorePatterns(rootFile);
|
|
24712
24915
|
const packagePatterns = packageDir && packageDir !== repoRoot ? await readIgnorePatterns(packageFile) : [];
|
|
24713
24916
|
return [
|
|
@@ -24776,7 +24979,7 @@ var init_path_filters = __esm(() => {
|
|
|
24776
24979
|
});
|
|
24777
24980
|
|
|
24778
24981
|
// src/verification/smart-runner.ts
|
|
24779
|
-
import { join as
|
|
24982
|
+
import { join as join9, relative as relative3 } from "path";
|
|
24780
24983
|
function clearGitRootCache() {
|
|
24781
24984
|
_gitRootCache.clear();
|
|
24782
24985
|
}
|
|
@@ -24894,7 +25097,7 @@ async function getChangedNonTestFiles(workdir, baseRef, packagePrefix, testFileR
|
|
|
24894
25097
|
const lines = stdout.trim().split(`
|
|
24895
25098
|
`).filter(Boolean);
|
|
24896
25099
|
const effectiveRepoRoot = repoRoot ?? workdir;
|
|
24897
|
-
const packageDir = packagePrefix ?
|
|
25100
|
+
const packageDir = packagePrefix ? join9(effectiveRepoRoot, packagePrefix) : undefined;
|
|
24898
25101
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(effectiveRepoRoot, packageDir);
|
|
24899
25102
|
let effectivePrefix = packagePrefix;
|
|
24900
25103
|
if (packagePrefix && repoRoot) {
|
|
@@ -24923,7 +25126,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
24923
25126
|
return [];
|
|
24924
25127
|
const lines = stdout.trim().split(`
|
|
24925
25128
|
`).filter(Boolean);
|
|
24926
|
-
const packageDir = packagePrefix ?
|
|
25129
|
+
const packageDir = packagePrefix ? join9(repoRoot, packagePrefix) : undefined;
|
|
24927
25130
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
|
|
24928
25131
|
const gitRoot = await getGitRootMemo(workdir);
|
|
24929
25132
|
const extraPrefix = gitRoot && gitRoot !== repoRoot ? relative3(gitRoot, repoRoot) : "";
|
|
@@ -24931,7 +25134,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
24931
25134
|
const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
|
|
24932
25135
|
const scoped = filterNaxInternalPaths(scopedRaw, ignoreMatchers);
|
|
24933
25136
|
const stripped = extraPrefix ? scoped.map((f) => f.slice(`${extraPrefix}/`.length)) : scoped;
|
|
24934
|
-
return stripped.filter((f) => testFileRegex.some((re) => re.test(f))).map((f) =>
|
|
25137
|
+
return stripped.filter((f) => testFileRegex.some((re) => re.test(f))).map((f) => join9(repoRoot, f));
|
|
24935
25138
|
} catch {
|
|
24936
25139
|
return [];
|
|
24937
25140
|
}
|
|
@@ -25063,6 +25266,7 @@ __export(exports_test_runners, {
|
|
|
25063
25266
|
formatFailureSummary: () => formatFailureSummary,
|
|
25064
25267
|
findPackageDir: () => findPackageDir,
|
|
25065
25268
|
extractTestDirs: () => extractTestDirs,
|
|
25269
|
+
discoverWorkspacePackages: () => discoverWorkspacePackages,
|
|
25066
25270
|
detectTestFilePatterns: () => detectTestFilePatterns,
|
|
25067
25271
|
detectManifestFrameworksFromPackageJson: () => detectManifestFrameworksFromPackageJson,
|
|
25068
25272
|
detectFramework: () => detectFramework,
|
|
@@ -25084,6 +25288,7 @@ var init_test_runners = __esm(() => {
|
|
|
25084
25288
|
init_detector2();
|
|
25085
25289
|
init_resolver();
|
|
25086
25290
|
init_parser();
|
|
25291
|
+
init_workspace();
|
|
25087
25292
|
init_ac_parser();
|
|
25088
25293
|
init_scoped_selection();
|
|
25089
25294
|
});
|
|
@@ -25135,7 +25340,7 @@ var init_project = __esm(() => {
|
|
|
25135
25340
|
|
|
25136
25341
|
// src/utils/path-security.ts
|
|
25137
25342
|
import { realpathSync as realpathSync2 } from "fs";
|
|
25138
|
-
import { dirname as dirname3, isAbsolute as isAbsolute4, join as
|
|
25343
|
+
import { dirname as dirname3, isAbsolute as isAbsolute4, join as join10, normalize as normalize3, resolve as resolve5 } from "path";
|
|
25139
25344
|
function safeRealpathForComparison(p) {
|
|
25140
25345
|
try {
|
|
25141
25346
|
return realpathSync2(p);
|
|
@@ -25144,7 +25349,7 @@ function safeRealpathForComparison(p) {
|
|
|
25144
25349
|
if (parent === p)
|
|
25145
25350
|
return normalize3(p);
|
|
25146
25351
|
const resolvedParent = safeRealpathForComparison(parent);
|
|
25147
|
-
return
|
|
25352
|
+
return join10(resolvedParent, p.split("/").pop() ?? "");
|
|
25148
25353
|
}
|
|
25149
25354
|
}
|
|
25150
25355
|
function isRelativeAndSafe(filePath) {
|
|
@@ -25172,7 +25377,7 @@ function validateModulePath(modulePath, allowedRoots) {
|
|
|
25172
25377
|
} else {
|
|
25173
25378
|
for (let i = 0;i < allowedRoots.length; i++) {
|
|
25174
25379
|
const originalRoot = resolve5(allowedRoots[i]);
|
|
25175
|
-
const absoluteInput = resolve5(
|
|
25380
|
+
const absoluteInput = resolve5(join10(originalRoot, modulePath));
|
|
25176
25381
|
const resolved = safeRealpathForComparison(absoluteInput);
|
|
25177
25382
|
const resolvedRoot = resolvedRoots[i];
|
|
25178
25383
|
if (resolved.startsWith(`${resolvedRoot}/`) || resolved === resolvedRoot) {
|
|
@@ -25189,7 +25394,7 @@ var init_path_security2 = () => {};
|
|
|
25189
25394
|
|
|
25190
25395
|
// src/context/engine/providers/code-neighbor.ts
|
|
25191
25396
|
import { createHash as createHash3 } from "crypto";
|
|
25192
|
-
import { join as
|
|
25397
|
+
import { join as join11, relative as relative4, resolve as resolve6 } from "path";
|
|
25193
25398
|
function isExcludedPath(file3, ignoreMatchers) {
|
|
25194
25399
|
for (const prefix of EXCLUDED_DIR_PREFIXES2) {
|
|
25195
25400
|
if (file3.startsWith(prefix) || file3.includes(`/${prefix}`))
|
|
@@ -25327,7 +25532,7 @@ async function readCached(absolutePath, cache) {
|
|
|
25327
25532
|
async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, siblingTestContext) {
|
|
25328
25533
|
const neighbors = new Set;
|
|
25329
25534
|
let anyTruncated = false;
|
|
25330
|
-
const ownAbsPath =
|
|
25535
|
+
const ownAbsPath = join11(workdir, filePath);
|
|
25331
25536
|
if (await _codeNeighborDeps.fileExists(ownAbsPath)) {
|
|
25332
25537
|
const ownContent = await readCached(ownAbsPath, contentCache);
|
|
25333
25538
|
if (ownContent !== null && ownContent.length > 0) {
|
|
@@ -25349,7 +25554,7 @@ async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, si
|
|
|
25349
25554
|
break outer;
|
|
25350
25555
|
if (srcFile === filePath)
|
|
25351
25556
|
continue;
|
|
25352
|
-
const content = await readCached(
|
|
25557
|
+
const content = await readCached(join11(scanWorkdir, srcFile), contentCache);
|
|
25353
25558
|
if (content?.includes(fileBaseName)) {
|
|
25354
25559
|
for (const spec of parseImportSpecifiers(content)) {
|
|
25355
25560
|
const resolved = resolveImport(spec, srcFile, scanWorkdir);
|
|
@@ -25365,7 +25570,7 @@ async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, si
|
|
|
25365
25570
|
const candidates = deriveSiblingTestCandidates(filePath, siblingTestContext.globs);
|
|
25366
25571
|
let chosen = null;
|
|
25367
25572
|
for (const candidate of candidates) {
|
|
25368
|
-
if (await _codeNeighborDeps.fileExists(
|
|
25573
|
+
if (await _codeNeighborDeps.fileExists(join11(workdir, candidate))) {
|
|
25369
25574
|
chosen = candidate;
|
|
25370
25575
|
break;
|
|
25371
25576
|
}
|
|
@@ -25389,7 +25594,7 @@ async function resolveExtraGlobWorkdirs(neighborScope, crossPackageDepth, repoRo
|
|
|
25389
25594
|
const relPkgDirs = await _codeNeighborDeps.discoverWorkspacePackages(repoRoot);
|
|
25390
25595
|
if (relPkgDirs.length === 0)
|
|
25391
25596
|
return [repoRoot];
|
|
25392
|
-
return relPkgDirs.map((rel) =>
|
|
25597
|
+
return relPkgDirs.map((rel) => join11(repoRoot, rel)).filter((abs) => abs !== packageDir);
|
|
25393
25598
|
} catch {
|
|
25394
25599
|
return [repoRoot];
|
|
25395
25600
|
}
|
|
@@ -26686,7 +26891,7 @@ var init_orchestrator = __esm(() => {
|
|
|
26686
26891
|
});
|
|
26687
26892
|
|
|
26688
26893
|
// src/context/rules/canonical-loader.ts
|
|
26689
|
-
import { basename as basename4, join as
|
|
26894
|
+
import { basename as basename4, join as join12 } from "path";
|
|
26690
26895
|
function parseRuleAllowMarker(line) {
|
|
26691
26896
|
const allowed = new Set;
|
|
26692
26897
|
RULE_ALLOW_MARKER.lastIndex = 0;
|
|
@@ -26821,7 +27026,7 @@ function applyCanonicalRulesBudget(rules, budgetTokens) {
|
|
|
26821
27026
|
}
|
|
26822
27027
|
async function loadCanonicalRules(workdir, options = {}) {
|
|
26823
27028
|
const logger = _canonicalLoaderDeps.getLogger();
|
|
26824
|
-
const rulesDir =
|
|
27029
|
+
const rulesDir = join12(workdir, CANONICAL_RULES_DIR);
|
|
26825
27030
|
const allFilePaths = _canonicalLoaderDeps.globInDir(rulesDir);
|
|
26826
27031
|
const filePaths = allFilePaths.filter((filePath) => {
|
|
26827
27032
|
const normalized = filePath.replaceAll("\\", "/");
|
|
@@ -26922,7 +27127,7 @@ var init_canonical_loader = __esm(() => {
|
|
|
26922
27127
|
for (const rel of files) {
|
|
26923
27128
|
const depth = rel.split("/").length - 1;
|
|
26924
27129
|
if (depth <= 1) {
|
|
26925
|
-
kept.push(
|
|
27130
|
+
kept.push(join12(dir, rel));
|
|
26926
27131
|
} else {
|
|
26927
27132
|
ignored.push(rel);
|
|
26928
27133
|
}
|
|
@@ -27204,7 +27409,7 @@ var init_session_scratch = __esm(() => {
|
|
|
27204
27409
|
|
|
27205
27410
|
// src/context/engine/providers/static-rules.ts
|
|
27206
27411
|
import { createHash as createHash8 } from "crypto";
|
|
27207
|
-
import { join as
|
|
27412
|
+
import { join as join13, relative as relative5 } from "path";
|
|
27208
27413
|
function contentHash85(content) {
|
|
27209
27414
|
return createHash8("sha256").update(content).digest("hex").slice(0, 8);
|
|
27210
27415
|
}
|
|
@@ -27412,7 +27617,7 @@ ${rule.content}`,
|
|
|
27412
27617
|
const existingCandidates = [];
|
|
27413
27618
|
for (const fileName of LEGACY_CANDIDATE_FILES) {
|
|
27414
27619
|
try {
|
|
27415
|
-
if (await _staticRulesDeps.fileExists(
|
|
27620
|
+
if (await _staticRulesDeps.fileExists(join13(rootDir, fileName))) {
|
|
27416
27621
|
existingCandidates.push(fileName);
|
|
27417
27622
|
}
|
|
27418
27623
|
} catch {}
|
|
@@ -27428,11 +27633,11 @@ ${rule.content}`,
|
|
|
27428
27633
|
for (const fileName of LEGACY_CANDIDATE_FILES) {
|
|
27429
27634
|
legacySources.push({
|
|
27430
27635
|
sourceId: fileName,
|
|
27431
|
-
filePath:
|
|
27636
|
+
filePath: join13(rootDir, fileName),
|
|
27432
27637
|
heading: fileName
|
|
27433
27638
|
});
|
|
27434
27639
|
}
|
|
27435
|
-
const rulesDir =
|
|
27640
|
+
const rulesDir = join13(rootDir, LEGACY_RULES_DIR);
|
|
27436
27641
|
const nestedRulePaths = _staticRulesDeps.globInDir(rulesDir);
|
|
27437
27642
|
for (const filePath of nestedRulePaths) {
|
|
27438
27643
|
const normalized = normalizePath2(filePath);
|
|
@@ -27491,7 +27696,7 @@ var init_static_rules = __esm(() => {
|
|
|
27491
27696
|
fileExists: async (path) => Bun.file(path).exists(),
|
|
27492
27697
|
globInDir: (dir) => {
|
|
27493
27698
|
try {
|
|
27494
|
-
return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) =>
|
|
27699
|
+
return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) => join13(dir, f));
|
|
27495
27700
|
} catch {
|
|
27496
27701
|
return [];
|
|
27497
27702
|
}
|
|
@@ -27830,7 +28035,7 @@ function validateStory(raw, index, allIds) {
|
|
|
27830
28035
|
routing: {
|
|
27831
28036
|
complexity,
|
|
27832
28037
|
testStrategy,
|
|
27833
|
-
reasoning: "validated from LLM output",
|
|
28038
|
+
reasoning: typeof routing.reasoning === "string" && routing.reasoning.trim().length > 0 ? routing.reasoning.trim() : "validated from LLM output",
|
|
27834
28039
|
...noTestJustification !== undefined ? { noTestJustification } : {}
|
|
27835
28040
|
},
|
|
27836
28041
|
...workdir !== undefined ? { workdir } : {},
|
|
@@ -28381,7 +28586,7 @@ var init_orchestrator_factory = __esm(() => {
|
|
|
28381
28586
|
});
|
|
28382
28587
|
|
|
28383
28588
|
// src/context/engine/providers/plugin-loader.ts
|
|
28384
|
-
import { isAbsolute as isAbsolute5, join as
|
|
28589
|
+
import { isAbsolute as isAbsolute5, join as join14, resolve as resolve7 } from "path";
|
|
28385
28590
|
function isInitialisable(p) {
|
|
28386
28591
|
return typeof p.init === "function";
|
|
28387
28592
|
}
|
|
@@ -28409,7 +28614,7 @@ function resolveModuleSpecifier(specifier, workdir) {
|
|
|
28409
28614
|
}
|
|
28410
28615
|
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
28411
28616
|
const resolvedWorkdir = resolve7(workdir);
|
|
28412
|
-
const resolved = resolve7(
|
|
28617
|
+
const resolved = resolve7(join14(workdir, specifier));
|
|
28413
28618
|
if (resolved !== resolvedWorkdir && !resolved.startsWith(`${resolvedWorkdir}/`)) {
|
|
28414
28619
|
throw new Error(`Plugin module path escapes project workdir: "${specifier}" resolves to "${resolved}" (workdir: "${resolvedWorkdir}")`);
|
|
28415
28620
|
}
|
|
@@ -28555,15 +28760,15 @@ var init_available_budget = __esm(() => {
|
|
|
28555
28760
|
|
|
28556
28761
|
// src/context/engine/manifest-store.ts
|
|
28557
28762
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
28558
|
-
import { dirname as dirname5, isAbsolute as isAbsolute6, join as
|
|
28763
|
+
import { dirname as dirname5, isAbsolute as isAbsolute6, join as join15, relative as relative7, resolve as resolve8 } from "path";
|
|
28559
28764
|
function contextStoryDir(projectDir, featureId, storyId) {
|
|
28560
|
-
return
|
|
28765
|
+
return join15(projectDir, ".nax", "features", featureId, "stories", storyId);
|
|
28561
28766
|
}
|
|
28562
28767
|
function contextManifestPath(projectDir, featureId, storyId, stage) {
|
|
28563
|
-
return
|
|
28768
|
+
return join15(contextStoryDir(projectDir, featureId, storyId), `context-manifest-${stage}.json`);
|
|
28564
28769
|
}
|
|
28565
28770
|
function rebuildManifestPath(projectDir, featureId, storyId) {
|
|
28566
|
-
return
|
|
28771
|
+
return join15(contextStoryDir(projectDir, featureId, storyId), "rebuild-manifest.json");
|
|
28567
28772
|
}
|
|
28568
28773
|
function toStoredPath(projectDir, pathValue) {
|
|
28569
28774
|
const relativePath = isAbsolute6(pathValue) ? relative7(projectDir, pathValue) : pathValue;
|
|
@@ -28619,7 +28824,7 @@ async function loadContextManifests(projectDir, storyId, featureId) {
|
|
|
28619
28824
|
const storyDir = contextStoryDir(projectDir, feature, storyId);
|
|
28620
28825
|
const manifestFiles = await _manifestStoreDeps.listManifestFiles(storyDir);
|
|
28621
28826
|
for (const fileName of manifestFiles) {
|
|
28622
|
-
const fullPath =
|
|
28827
|
+
const fullPath = join15(storyDir, fileName);
|
|
28623
28828
|
if (!await _manifestStoreDeps.fileExists(fullPath))
|
|
28624
28829
|
continue;
|
|
28625
28830
|
try {
|
|
@@ -28644,7 +28849,7 @@ var init_manifest_store = __esm(() => {
|
|
|
28644
28849
|
fileExists: (path2) => Bun.file(path2).exists(),
|
|
28645
28850
|
readFile: (path2) => Bun.file(path2).text(),
|
|
28646
28851
|
listFeatureDirs: async (projectDir) => {
|
|
28647
|
-
const baseDir =
|
|
28852
|
+
const baseDir = join15(projectDir, ".nax", "features");
|
|
28648
28853
|
try {
|
|
28649
28854
|
const dirs = [];
|
|
28650
28855
|
for await (const entry of new Bun.Glob("*").scan({ cwd: baseDir, absolute: false })) {
|
|
@@ -28671,7 +28876,7 @@ var init_manifest_store = __esm(() => {
|
|
|
28671
28876
|
|
|
28672
28877
|
// src/context/engine/stage-assembler.ts
|
|
28673
28878
|
import { readdir } from "fs/promises";
|
|
28674
|
-
import { isAbsolute as isAbsolute7, join as
|
|
28879
|
+
import { isAbsolute as isAbsolute7, join as join16, resolve as resolve9 } from "path";
|
|
28675
28880
|
function dedupeScratchDirs(dirs) {
|
|
28676
28881
|
return [...new Set(dirs.filter((dir) => Boolean(dir)))];
|
|
28677
28882
|
}
|
|
@@ -28680,7 +28885,7 @@ function toAbsolutePath2(projectDir, pathValue) {
|
|
|
28680
28885
|
}
|
|
28681
28886
|
async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId, ttlMs) {
|
|
28682
28887
|
const logger = getLogger();
|
|
28683
|
-
const sessionsRoot =
|
|
28888
|
+
const sessionsRoot = join16(projectDir, ".nax", "features", featureName, "sessions");
|
|
28684
28889
|
let entries;
|
|
28685
28890
|
try {
|
|
28686
28891
|
entries = await _stageAssemblerDeps.readdir(sessionsRoot);
|
|
@@ -28690,7 +28895,7 @@ async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId
|
|
|
28690
28895
|
const cutoff = _stageAssemblerDeps.now() - ttlMs;
|
|
28691
28896
|
const found = [];
|
|
28692
28897
|
for (const entry of entries) {
|
|
28693
|
-
const descriptorPath =
|
|
28898
|
+
const descriptorPath = join16(sessionsRoot, entry, "descriptor.json");
|
|
28694
28899
|
try {
|
|
28695
28900
|
const parsed = await _stageAssemblerDeps.readDescriptor(descriptorPath);
|
|
28696
28901
|
if (!parsed || parsed.storyId !== storyId || !parsed.scratchDir)
|
|
@@ -30355,13 +30560,13 @@ var exports_loader = {};
|
|
|
30355
30560
|
__export(exports_loader, {
|
|
30356
30561
|
loadOverride: () => loadOverride
|
|
30357
30562
|
});
|
|
30358
|
-
import { join as
|
|
30563
|
+
import { join as join17 } from "path";
|
|
30359
30564
|
async function loadOverride(role, workdir, config2) {
|
|
30360
30565
|
const overridePath = config2.prompts?.overrides?.[role];
|
|
30361
30566
|
if (!overridePath) {
|
|
30362
30567
|
return null;
|
|
30363
30568
|
}
|
|
30364
|
-
const absolutePath =
|
|
30569
|
+
const absolutePath = join17(workdir, overridePath);
|
|
30365
30570
|
const file3 = Bun.file(absolutePath);
|
|
30366
30571
|
if (!await file3.exists()) {
|
|
30367
30572
|
return null;
|
|
@@ -32641,12 +32846,14 @@ var init_adversarial_review = __esm(() => {
|
|
|
32641
32846
|
});
|
|
32642
32847
|
const { accepted, dropped } = filterByAcQuote(substantiated, input.story.acceptanceCriteria);
|
|
32643
32848
|
const blocking = accepted.filter((f) => isBlockingSeverity(f.severity, threshold));
|
|
32849
|
+
const advisory = accepted.filter((f) => !isBlockingSeverity(f.severity, threshold));
|
|
32644
32850
|
const passed = parsed.passed && blocking.length === 0;
|
|
32645
32851
|
return {
|
|
32646
32852
|
...parsed,
|
|
32647
32853
|
passed,
|
|
32648
32854
|
findings: accepted,
|
|
32649
32855
|
normalizedFindings: toAdversarialReviewFindings(blocking),
|
|
32856
|
+
advisoryFindings: toAdversarialReviewFindings(advisory),
|
|
32650
32857
|
acDropped: dropped
|
|
32651
32858
|
};
|
|
32652
32859
|
}
|
|
@@ -33026,11 +33233,11 @@ var init_prepare_inputs = __esm(() => {
|
|
|
33026
33233
|
});
|
|
33027
33234
|
|
|
33028
33235
|
// src/utils/nax-project-root.ts
|
|
33029
|
-
import { dirname as dirname6, join as
|
|
33236
|
+
import { dirname as dirname6, join as join18, resolve as resolve10 } from "path";
|
|
33030
33237
|
async function findNaxProjectRoot(startDir) {
|
|
33031
33238
|
let dir = resolve10(startDir);
|
|
33032
33239
|
for (let depth = 0;depth < MAX_NAX_WALK_DEPTH; depth++) {
|
|
33033
|
-
if (await _naxProjectRootDeps.exists(
|
|
33240
|
+
if (await _naxProjectRootDeps.exists(join18(dir, ".nax", "config.json"))) {
|
|
33034
33241
|
return dir;
|
|
33035
33242
|
}
|
|
33036
33243
|
const parent = dirname6(dir);
|
|
@@ -33051,7 +33258,7 @@ var init_nax_project_root = __esm(() => {
|
|
|
33051
33258
|
|
|
33052
33259
|
// src/review/review-audit.ts
|
|
33053
33260
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
33054
|
-
import { join as
|
|
33261
|
+
import { join as join19 } from "path";
|
|
33055
33262
|
function auditKey(reviewer, storyId) {
|
|
33056
33263
|
return `${reviewer}:${storyId ?? "_feature"}`;
|
|
33057
33264
|
}
|
|
@@ -33084,15 +33291,15 @@ function toPersistedEntry(entry, epochMs) {
|
|
|
33084
33291
|
async function persistReviewAudit(entry) {
|
|
33085
33292
|
let resolvedDir;
|
|
33086
33293
|
if (entry.outputDir) {
|
|
33087
|
-
resolvedDir =
|
|
33294
|
+
resolvedDir = join19(entry.outputDir, "review-audit", entry.featureName ?? "_unknown");
|
|
33088
33295
|
} else {
|
|
33089
33296
|
const projectRoot = entry.projectDir ?? await _reviewAuditDeps.findNaxProjectRoot(entry.workdir);
|
|
33090
|
-
resolvedDir =
|
|
33297
|
+
resolvedDir = join19(projectRoot, ".nax", "review-audit", entry.featureName ?? "_unknown");
|
|
33091
33298
|
}
|
|
33092
33299
|
await _reviewAuditDeps.mkdir(resolvedDir);
|
|
33093
33300
|
const epochMs = _reviewAuditDeps.now();
|
|
33094
33301
|
const filename = `${epochMs}-${entry.sessionName}.json`;
|
|
33095
|
-
await _reviewAuditDeps.writeFile(
|
|
33302
|
+
await _reviewAuditDeps.writeFile(join19(resolvedDir, filename), toPersistedEntry(entry, epochMs));
|
|
33096
33303
|
}
|
|
33097
33304
|
function createNoOpReviewAuditor() {
|
|
33098
33305
|
return {
|
|
@@ -33693,6 +33900,17 @@ function createDrainDeadline(deadlineMs) {
|
|
|
33693
33900
|
}
|
|
33694
33901
|
async function runQualityCommand(opts) {
|
|
33695
33902
|
const { commandName, command, workdir, storyId, timeoutMs = DEFAULT_TIMEOUT_MS, env: env2, stripEnvVars } = opts;
|
|
33903
|
+
if (!command || command.trim() === "") {
|
|
33904
|
+
return {
|
|
33905
|
+
commandName,
|
|
33906
|
+
command,
|
|
33907
|
+
success: false,
|
|
33908
|
+
exitCode: -1,
|
|
33909
|
+
output: `[nax] ${commandName} skipped: empty command`,
|
|
33910
|
+
durationMs: 0,
|
|
33911
|
+
timedOut: false
|
|
33912
|
+
};
|
|
33913
|
+
}
|
|
33696
33914
|
const startTime = Date.now();
|
|
33697
33915
|
const logger = getSafeLogger();
|
|
33698
33916
|
logger?.info("quality", `Running ${commandName}`, { storyId, commandName, command, workdir });
|
|
@@ -33807,7 +34025,7 @@ __export(exports_command_resolver, {
|
|
|
33807
34025
|
resolveQualityTestCommands: () => resolveQualityTestCommands,
|
|
33808
34026
|
_commandResolverDeps: () => _commandResolverDeps
|
|
33809
34027
|
});
|
|
33810
|
-
import { join as
|
|
34028
|
+
import { join as join20 } from "path";
|
|
33811
34029
|
async function resolveQualityTestCommands(config2, workdir, storyWorkdir) {
|
|
33812
34030
|
const rawTestCommand = config2.review?.commands?.test ?? config2.quality?.commands?.test;
|
|
33813
34031
|
const rawScopedTemplate = config2.quality?.commands?.testScoped;
|
|
@@ -33834,7 +34052,7 @@ var init_command_resolver = __esm(() => {
|
|
|
33834
34052
|
_commandResolverDeps = {
|
|
33835
34053
|
readPackageName: async (dir) => {
|
|
33836
34054
|
try {
|
|
33837
|
-
const content = await Bun.file(
|
|
34055
|
+
const content = await Bun.file(join20(dir, "package.json")).json();
|
|
33838
34056
|
return typeof content.name === "string" ? content.name : null;
|
|
33839
34057
|
} catch {
|
|
33840
34058
|
return null;
|
|
@@ -34283,7 +34501,7 @@ ${outputFormat}`, overridable: false }
|
|
|
34283
34501
|
});
|
|
34284
34502
|
|
|
34285
34503
|
// src/operations/plan-refine.ts
|
|
34286
|
-
import { join as
|
|
34504
|
+
import { join as join21 } from "path";
|
|
34287
34505
|
function hasToken(text, tokens) {
|
|
34288
34506
|
const lower = text.toLowerCase();
|
|
34289
34507
|
return tokens.some((token) => lower.includes(token));
|
|
@@ -34375,7 +34593,7 @@ async function normalizeStoryFiles(story, workdir, fileExists, upstreamProduced)
|
|
|
34375
34593
|
for (const entry of contextFiles) {
|
|
34376
34594
|
const filePath = typeof entry === "string" ? entry : entry.path;
|
|
34377
34595
|
const factId = typeof entry === "string" ? undefined : entry.factId;
|
|
34378
|
-
if (expected.has(filePath) || await fileExists(
|
|
34596
|
+
if (expected.has(filePath) || await fileExists(join21(workdir, filePath))) {
|
|
34379
34597
|
kept.push(entry);
|
|
34380
34598
|
continue;
|
|
34381
34599
|
}
|
|
@@ -35527,11 +35745,11 @@ function extractTestCode(output) {
|
|
|
35527
35745
|
|
|
35528
35746
|
// src/acceptance/generator.ts
|
|
35529
35747
|
import { existsSync as existsSync5 } from "fs";
|
|
35530
|
-
import { join as
|
|
35748
|
+
import { join as join22 } from "path";
|
|
35531
35749
|
function resolvePytestBin(packageDir) {
|
|
35532
35750
|
if (packageDir) {
|
|
35533
35751
|
for (const venvDir of [".venv", "venv", "env"]) {
|
|
35534
|
-
const candidate =
|
|
35752
|
+
const candidate = join22(packageDir, venvDir, "bin", "pytest");
|
|
35535
35753
|
if (existsSync5(candidate))
|
|
35536
35754
|
return candidate;
|
|
35537
35755
|
}
|
|
@@ -37117,6 +37335,7 @@ var init_write_test = __esm(() => {
|
|
|
37117
37335
|
stage: "run",
|
|
37118
37336
|
session: { role: "test-writer", lifetime: "warm" },
|
|
37119
37337
|
config: tddConfigSelector,
|
|
37338
|
+
model: (_input, ctx) => ctx.config.tdd?.sessionTiers?.testWriter,
|
|
37120
37339
|
keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "test-writer"),
|
|
37121
37340
|
build(input, _ctx) {
|
|
37122
37341
|
if (input.promptMarkdown?.trim()) {
|
|
@@ -37177,6 +37396,7 @@ var init_implement = __esm(() => {
|
|
|
37177
37396
|
stage: "run",
|
|
37178
37397
|
session: { role: "implementer", lifetime: "warm" },
|
|
37179
37398
|
config: tddConfigSelector,
|
|
37399
|
+
model: (input) => input.story.routing?.modelTier,
|
|
37180
37400
|
keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "implementer"),
|
|
37181
37401
|
build(input, _ctx) {
|
|
37182
37402
|
if (input.promptMarkdown?.trim()) {
|
|
@@ -37574,6 +37794,7 @@ var init_verify = __esm(() => {
|
|
|
37574
37794
|
stage: "verify",
|
|
37575
37795
|
session: { role: "verifier", lifetime: "fresh" },
|
|
37576
37796
|
config: tddConfigSelector,
|
|
37797
|
+
model: (_input, ctx) => ctx.config.tdd?.sessionTiers?.verifier,
|
|
37577
37798
|
retry: makeParseRetryStrategy({
|
|
37578
37799
|
validate: (parsed) => {
|
|
37579
37800
|
if (!parsed || typeof parsed !== "object")
|
|
@@ -38140,14 +38361,14 @@ var init_plan_critic_llm = __esm(() => {
|
|
|
38140
38361
|
|
|
38141
38362
|
// src/context/greenfield.ts
|
|
38142
38363
|
import { readdir as readdir2 } from "fs/promises";
|
|
38143
|
-
import { join as
|
|
38364
|
+
import { join as join23 } from "path";
|
|
38144
38365
|
async function scanForTestFiles(dir, testPatterns, isRootCall = true) {
|
|
38145
38366
|
const results = [];
|
|
38146
38367
|
const ignoreDirs = new Set(["node_modules", "dist", "build", ".next", ".git"]);
|
|
38147
38368
|
try {
|
|
38148
38369
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
38149
38370
|
for (const entry of entries) {
|
|
38150
|
-
const fullPath =
|
|
38371
|
+
const fullPath = join23(dir, entry.name);
|
|
38151
38372
|
if (entry.isDirectory()) {
|
|
38152
38373
|
if (ignoreDirs.has(entry.name))
|
|
38153
38374
|
continue;
|
|
@@ -38352,13 +38573,13 @@ __export(exports_runners, {
|
|
|
38352
38573
|
_regressionRunnerDeps: () => _regressionRunnerDeps
|
|
38353
38574
|
});
|
|
38354
38575
|
import { existsSync as existsSync6 } from "fs";
|
|
38355
|
-
import { join as
|
|
38576
|
+
import { join as join24 } from "path";
|
|
38356
38577
|
async function verifyAssets(workingDirectory, expectedFiles) {
|
|
38357
38578
|
if (!expectedFiles || expectedFiles.length === 0)
|
|
38358
38579
|
return { success: true, missingFiles: [] };
|
|
38359
38580
|
const missingFiles = [];
|
|
38360
38581
|
for (const file3 of expectedFiles) {
|
|
38361
|
-
if (!existsSync6(
|
|
38582
|
+
if (!existsSync6(join24(workingDirectory, file3)))
|
|
38362
38583
|
missingFiles.push(file3);
|
|
38363
38584
|
}
|
|
38364
38585
|
if (missingFiles.length > 0) {
|
|
@@ -38484,7 +38705,7 @@ var init_full_suite_gate = __esm(() => {
|
|
|
38484
38705
|
_fullSuiteGateDeps = {
|
|
38485
38706
|
resolveGateContext: async (input, ctx) => {
|
|
38486
38707
|
const { resolveQualityTestCommands: resolveQualityTestCommands2 } = await Promise.resolve().then(() => (init_command_resolver(), exports_command_resolver));
|
|
38487
|
-
const config2 = ctx.
|
|
38708
|
+
const config2 = ctx.packageView.config;
|
|
38488
38709
|
const fullSuiteTimeout = config2.execution?.regressionGate?.timeoutSeconds ?? config2.execution?.rectification?.fullSuiteTimeoutSeconds ?? 300;
|
|
38489
38710
|
const { testCommand: resolvedTestCmd } = await resolveQualityTestCommands2(config2, input.workdir, input.story.workdir);
|
|
38490
38711
|
if (!resolvedTestCmd) {
|
|
@@ -38533,7 +38754,7 @@ var init_full_suite_gate = __esm(() => {
|
|
|
38533
38754
|
config: fullSuiteGateConfigSelector,
|
|
38534
38755
|
async execute(input, ctx, deps = _fullSuiteGateDeps) {
|
|
38535
38756
|
const logger = getLogger();
|
|
38536
|
-
const ctxConfig = ctx.config;
|
|
38757
|
+
const ctxConfig = ctx.packageView.config;
|
|
38537
38758
|
const enabled = ctxConfig?.execution?.regressionGate?.enabled ?? true;
|
|
38538
38759
|
if (!enabled) {
|
|
38539
38760
|
logger.info("verify[regression]", "Regression gate disabled \u2014 skipping full-suite run", {
|
|
@@ -38804,7 +39025,7 @@ var init_apply_test_edit_declarations = __esm(() => {
|
|
|
38804
39025
|
});
|
|
38805
39026
|
|
|
38806
39027
|
// src/operations/validate-mock-structure-files.ts
|
|
38807
|
-
import { join as
|
|
39028
|
+
import { join as join25 } from "path";
|
|
38808
39029
|
async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
|
|
38809
39030
|
const fileExists = deps?.fileExists ?? defaultFileExists;
|
|
38810
39031
|
const valid = [];
|
|
@@ -38817,7 +39038,7 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
|
|
|
38817
39038
|
const files = d.files ?? [d.file];
|
|
38818
39039
|
let allValid = true;
|
|
38819
39040
|
for (const file3 of files) {
|
|
38820
|
-
const absolutePath =
|
|
39041
|
+
const absolutePath = join25(packageDir, file3);
|
|
38821
39042
|
const exists = await fileExists(absolutePath);
|
|
38822
39043
|
if (!exists) {
|
|
38823
39044
|
allValid = false;
|
|
@@ -38840,6 +39061,159 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
|
|
|
38840
39061
|
var defaultFileExists = (p) => Bun.file(p).exists();
|
|
38841
39062
|
var init_validate_mock_structure_files = () => {};
|
|
38842
39063
|
|
|
39064
|
+
// src/prompts/builders/setup-builder.ts
|
|
39065
|
+
function formatPackageFacts(pkg) {
|
|
39066
|
+
const lines = [` Package: ${pkg.relativeDir || "(root)"}`];
|
|
39067
|
+
if (pkg.testFramework)
|
|
39068
|
+
lines.push(` Test framework: ${pkg.testFramework}`);
|
|
39069
|
+
if (pkg.testFilePatterns.length > 0) {
|
|
39070
|
+
lines.push(` Test patterns: ${pkg.testFilePatterns.slice(0, 4).join(", ")}`);
|
|
39071
|
+
}
|
|
39072
|
+
if (pkg.missingScripts.length > 0) {
|
|
39073
|
+
lines.push(` Missing scripts: ${pkg.missingScripts.join(", ")}`);
|
|
39074
|
+
} else {
|
|
39075
|
+
lines.push(" Missing scripts: (none \u2014 all canonical scripts present)");
|
|
39076
|
+
}
|
|
39077
|
+
return lines.join(`
|
|
39078
|
+
`);
|
|
39079
|
+
}
|
|
39080
|
+
|
|
39081
|
+
class SetupPromptBuilder {
|
|
39082
|
+
build(analysis) {
|
|
39083
|
+
const isMonoRepo = analysis.shape === "mono";
|
|
39084
|
+
const packageFacts = analysis.packages.map(formatPackageFacts).join(`
|
|
39085
|
+
|
|
39086
|
+
`);
|
|
39087
|
+
return {
|
|
39088
|
+
role: {
|
|
39089
|
+
id: "setup-role",
|
|
39090
|
+
content: "You are an expert nax configuration generator. Generate a valid nax config JSON based on the repository analysis provided. Only reference scripts that actually exist \u2014 any script listed under 'Missing scripts' must NOT appear in quality.commands.",
|
|
39091
|
+
overridable: false
|
|
39092
|
+
},
|
|
39093
|
+
task: {
|
|
39094
|
+
id: "setup-task",
|
|
39095
|
+
content: [
|
|
39096
|
+
`Generate a nax configuration for this ${isMonoRepo ? "monorepo" : "single-package"} repository.`,
|
|
39097
|
+
"",
|
|
39098
|
+
"Repository facts:",
|
|
39099
|
+
`- Shape: ${analysis.shape}`,
|
|
39100
|
+
`- Package manager: ${analysis.pmRunPrefix}`,
|
|
39101
|
+
`- DLX runner: ${analysis.pmDlx}`,
|
|
39102
|
+
`- Orchestrator: ${analysis.orchestrator}`,
|
|
39103
|
+
"",
|
|
39104
|
+
"Per-package facts:",
|
|
39105
|
+
packageFacts,
|
|
39106
|
+
"",
|
|
39107
|
+
"IMPORTANT: A script listed under 'Missing scripts' does NOT exist in that package's package.json.",
|
|
39108
|
+
"Do NOT include commands for missing scripts in quality.commands.",
|
|
39109
|
+
"",
|
|
39110
|
+
isMonoRepo ? 'Respond with a JSON code block: { "config": <root NaxConfig>, "monoConfigs": [{ "relativeDir": "<pkg>", "config": <partial NaxConfig> }] }' : 'Respond with a JSON code block: { "config": <NaxConfig> }'
|
|
39111
|
+
].join(`
|
|
39112
|
+
`),
|
|
39113
|
+
overridable: false
|
|
39114
|
+
}
|
|
39115
|
+
};
|
|
39116
|
+
}
|
|
39117
|
+
}
|
|
39118
|
+
|
|
39119
|
+
// src/operations/setup-generate.ts
|
|
39120
|
+
function throwSetupPlanError(message) {
|
|
39121
|
+
throw new NaxError(`[setup-generate] ${message}`, "SETUP_PLAN_INVALID");
|
|
39122
|
+
}
|
|
39123
|
+
function validateSetupOutput(parsed) {
|
|
39124
|
+
const config2 = parsed?.config ?? parsed;
|
|
39125
|
+
return NaxConfigSchema.safeParse(config2).success;
|
|
39126
|
+
}
|
|
39127
|
+
function crossCheckCommands(config2, analysis) {
|
|
39128
|
+
const rootPkg = analysis.packages.find((p) => p.relativeDir === "") ?? analysis.packages[0];
|
|
39129
|
+
const missing = new Set(rootPkg?.missingScripts ?? []);
|
|
39130
|
+
if (missing.size === 0)
|
|
39131
|
+
return { config: config2, gaps: [] };
|
|
39132
|
+
const quality = config2.quality;
|
|
39133
|
+
if (!quality?.commands)
|
|
39134
|
+
return { config: config2, gaps: [] };
|
|
39135
|
+
const gaps = [];
|
|
39136
|
+
const commands = {};
|
|
39137
|
+
for (const [key, value] of Object.entries(quality.commands)) {
|
|
39138
|
+
if (missing.has(key)) {
|
|
39139
|
+
gaps.push(`Script "${key}" in quality.commands.${key} is missing from package.json`);
|
|
39140
|
+
} else {
|
|
39141
|
+
commands[key] = value;
|
|
39142
|
+
}
|
|
39143
|
+
}
|
|
39144
|
+
if (gaps.length === 0)
|
|
39145
|
+
return { config: config2, gaps: [] };
|
|
39146
|
+
return { config: { ...config2, quality: { ...quality, commands } }, gaps };
|
|
39147
|
+
}
|
|
39148
|
+
function buildMonoConfigs(parsed, analysis) {
|
|
39149
|
+
if (analysis.shape !== "mono")
|
|
39150
|
+
return [];
|
|
39151
|
+
const rawMonoConfigs = parsed.monoConfigs ?? [];
|
|
39152
|
+
return analysis.packages.map((pkg) => {
|
|
39153
|
+
const rawMono = rawMonoConfigs.find((m) => m.relativeDir === pkg.relativeDir);
|
|
39154
|
+
const validated = rawMono ? NaxConfigSchema.safeParse(rawMono.config) : undefined;
|
|
39155
|
+
if (rawMono && !validated?.success) {
|
|
39156
|
+
getLogger().warn("setup-generate", "Per-package config failed schema validation \u2014 using empty config", {
|
|
39157
|
+
storyId: "setup",
|
|
39158
|
+
relativeDir: pkg.relativeDir
|
|
39159
|
+
});
|
|
39160
|
+
}
|
|
39161
|
+
const config2 = (validated?.success ? validated.data : undefined) ?? {};
|
|
39162
|
+
return { relativeDir: pkg.relativeDir, config: config2 };
|
|
39163
|
+
});
|
|
39164
|
+
}
|
|
39165
|
+
var MAX_SETUP_LLM_ATTEMPTS = 2, SetupPlanError, setupRetryStrategy, setupGenerateOp;
|
|
39166
|
+
var init_setup_generate = __esm(() => {
|
|
39167
|
+
init_retry();
|
|
39168
|
+
init_schemas3();
|
|
39169
|
+
init_errors();
|
|
39170
|
+
init_logger2();
|
|
39171
|
+
SetupPlanError = class SetupPlanError extends ParseValidationError {
|
|
39172
|
+
code = "SETUP_PLAN_INVALID";
|
|
39173
|
+
};
|
|
39174
|
+
setupRetryStrategy = makeParseRetryStrategy({
|
|
39175
|
+
reviewerKind: "setup-generate",
|
|
39176
|
+
maxAttempts: MAX_SETUP_LLM_ATTEMPTS,
|
|
39177
|
+
validate: validateSetupOutput,
|
|
39178
|
+
prompts: {
|
|
39179
|
+
invalid: () => "The response was not valid JSON or failed schema validation. Please respond with a valid JSON object.",
|
|
39180
|
+
truncated: () => "The response was truncated. Please provide the complete JSON config."
|
|
39181
|
+
},
|
|
39182
|
+
exhaustedFallback: () => {
|
|
39183
|
+
throwSetupPlanError("LLM failed to generate a valid setup plan after exhausting retries");
|
|
39184
|
+
}
|
|
39185
|
+
});
|
|
39186
|
+
setupGenerateOp = {
|
|
39187
|
+
kind: "run",
|
|
39188
|
+
name: "setup-generate",
|
|
39189
|
+
stage: "setup",
|
|
39190
|
+
session: { role: "setup", lifetime: "fresh" },
|
|
39191
|
+
noFallback: true,
|
|
39192
|
+
config: ["quality"],
|
|
39193
|
+
retry: setupRetryStrategy,
|
|
39194
|
+
build(analysis, _ctx) {
|
|
39195
|
+
return new SetupPromptBuilder().build(analysis);
|
|
39196
|
+
},
|
|
39197
|
+
parse(output, analysis, _ctx) {
|
|
39198
|
+
let parsedRaw;
|
|
39199
|
+
try {
|
|
39200
|
+
parsedRaw = parseLLMJson(output);
|
|
39201
|
+
} catch {
|
|
39202
|
+
throw new SetupPlanError("Failed to parse LLM output as JSON");
|
|
39203
|
+
}
|
|
39204
|
+
const parsedObj = parsedRaw;
|
|
39205
|
+
const rawConfig = parsedObj?.config ?? parsedRaw;
|
|
39206
|
+
const result = NaxConfigSchema.safeParse(rawConfig);
|
|
39207
|
+
if (!result.success) {
|
|
39208
|
+
throw new SetupPlanError(`Config failed NaxConfigSchema: ${result.error.message}`);
|
|
39209
|
+
}
|
|
39210
|
+
const { config: config2, gaps } = crossCheckCommands(result.data, analysis);
|
|
39211
|
+
const monoConfigs = buildMonoConfigs(parsedObj ?? { config: rawConfig }, analysis);
|
|
39212
|
+
return { config: config2, monoConfigs, gaps };
|
|
39213
|
+
}
|
|
39214
|
+
};
|
|
39215
|
+
});
|
|
39216
|
+
|
|
38843
39217
|
// src/operations/declaration-sink.ts
|
|
38844
39218
|
function makeDeclarationSink() {
|
|
38845
39219
|
return { testEdits: [], mockHandoffs: [] };
|
|
@@ -38886,9 +39260,9 @@ var init_mechanical_lintfix_strategy = __esm(() => {
|
|
|
38886
39260
|
stage: "rectification",
|
|
38887
39261
|
config: qualityConfigSelector,
|
|
38888
39262
|
async execute(input, ctx, deps = _mechanicalLintFixDeps) {
|
|
38889
|
-
const
|
|
38890
|
-
const broad =
|
|
38891
|
-
const scoped2 =
|
|
39263
|
+
const quality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
39264
|
+
const broad = quality?.commands?.lintFix;
|
|
39265
|
+
const scoped2 = quality?.commands?.lintFixScoped;
|
|
38892
39266
|
const command = buildCommand(broad, scoped2, input.scopeFiles);
|
|
38893
39267
|
if (!command)
|
|
38894
39268
|
return { applied: true, exitCode: 0 };
|
|
@@ -38897,7 +39271,7 @@ var init_mechanical_lintfix_strategy = __esm(() => {
|
|
|
38897
39271
|
command,
|
|
38898
39272
|
workdir: input.workdir,
|
|
38899
39273
|
storyId: input.storyId,
|
|
38900
|
-
stripEnvVars:
|
|
39274
|
+
stripEnvVars: quality?.stripEnvVars ?? []
|
|
38901
39275
|
});
|
|
38902
39276
|
return { applied: true, exitCode: result.exitCode };
|
|
38903
39277
|
}
|
|
@@ -38945,9 +39319,9 @@ var init_mechanical_formatfix_strategy = __esm(() => {
|
|
|
38945
39319
|
stage: "rectification",
|
|
38946
39320
|
config: qualityConfigSelector,
|
|
38947
39321
|
async execute(input, ctx, deps = _mechanicalFormatFixDeps) {
|
|
38948
|
-
const
|
|
38949
|
-
const broad =
|
|
38950
|
-
const scoped2 =
|
|
39322
|
+
const quality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
39323
|
+
const broad = quality?.commands?.formatFix;
|
|
39324
|
+
const scoped2 = quality?.commands?.formatFixScoped;
|
|
38951
39325
|
const command = buildCommand2(broad, scoped2, input.scopeFiles);
|
|
38952
39326
|
if (!command)
|
|
38953
39327
|
return { applied: true, exitCode: 0 };
|
|
@@ -38956,7 +39330,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
|
|
|
38956
39330
|
command,
|
|
38957
39331
|
workdir: input.workdir,
|
|
38958
39332
|
storyId: input.storyId,
|
|
38959
|
-
stripEnvVars:
|
|
39333
|
+
stripEnvVars: quality?.stripEnvVars ?? []
|
|
38960
39334
|
});
|
|
38961
39335
|
return { applied: true, exitCode: result.exitCode };
|
|
38962
39336
|
}
|
|
@@ -38967,6 +39341,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
|
|
|
38967
39341
|
var _lintCheckDeps, lintCheckOp;
|
|
38968
39342
|
var init_lint_check = __esm(() => {
|
|
38969
39343
|
init_config();
|
|
39344
|
+
init_logger2();
|
|
38970
39345
|
init_runner();
|
|
38971
39346
|
init_lint_parsing();
|
|
38972
39347
|
_lintCheckDeps = {
|
|
@@ -38979,21 +39354,25 @@ var init_lint_check = __esm(() => {
|
|
|
38979
39354
|
stage: "review",
|
|
38980
39355
|
config: qualityConfigSelector,
|
|
38981
39356
|
async execute(input, ctx, deps = _lintCheckDeps) {
|
|
38982
|
-
const
|
|
38983
|
-
const command =
|
|
38984
|
-
if (
|
|
38985
|
-
|
|
39357
|
+
const quality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
39358
|
+
const command = quality?.commands?.lint;
|
|
39359
|
+
if (!command) {
|
|
39360
|
+
getSafeLogger()?.warn("quality", "No lint command configured \u2014 skipping lint gate", {
|
|
39361
|
+
storyId: input.storyId,
|
|
39362
|
+
packageDir: ctx.packageView.packageDir
|
|
39363
|
+
});
|
|
39364
|
+
return { success: true, status: "skipped", findings: [], durationMs: 0 };
|
|
38986
39365
|
}
|
|
38987
39366
|
const start = Date.now();
|
|
38988
39367
|
const result = await deps.runQualityCommand({
|
|
38989
39368
|
commandName: "lint",
|
|
38990
|
-
command
|
|
39369
|
+
command,
|
|
38991
39370
|
workdir: input.workdir,
|
|
38992
39371
|
storyId: input.storyId,
|
|
38993
|
-
stripEnvVars:
|
|
39372
|
+
stripEnvVars: quality?.stripEnvVars ?? []
|
|
38994
39373
|
});
|
|
38995
39374
|
if (result.exitCode === 0) {
|
|
38996
|
-
return { success: true, findings: [], durationMs: Date.now() - start };
|
|
39375
|
+
return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
|
|
38997
39376
|
}
|
|
38998
39377
|
const parsed = deps.parseLintOutput(result.output, "auto", { workdir: input.workdir });
|
|
38999
39378
|
return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
|
|
@@ -39217,6 +39596,7 @@ var init_typecheck_parsing = __esm(() => {
|
|
|
39217
39596
|
var _typecheckCheckDeps, typecheckCheckOp;
|
|
39218
39597
|
var init_typecheck_check = __esm(() => {
|
|
39219
39598
|
init_config();
|
|
39599
|
+
init_logger2();
|
|
39220
39600
|
init_runner();
|
|
39221
39601
|
init_typecheck_parsing();
|
|
39222
39602
|
_typecheckCheckDeps = {
|
|
@@ -39229,21 +39609,25 @@ var init_typecheck_check = __esm(() => {
|
|
|
39229
39609
|
stage: "review",
|
|
39230
39610
|
config: qualityConfigSelector,
|
|
39231
39611
|
async execute(input, ctx, deps = _typecheckCheckDeps) {
|
|
39232
|
-
const
|
|
39233
|
-
const command =
|
|
39234
|
-
if (
|
|
39235
|
-
|
|
39612
|
+
const quality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
39613
|
+
const command = quality?.commands?.typecheck;
|
|
39614
|
+
if (!command) {
|
|
39615
|
+
getSafeLogger()?.warn("quality", "No typecheck command configured \u2014 skipping typecheck gate", {
|
|
39616
|
+
storyId: input.storyId,
|
|
39617
|
+
packageDir: ctx.packageView.packageDir
|
|
39618
|
+
});
|
|
39619
|
+
return { success: true, status: "skipped", findings: [], durationMs: 0 };
|
|
39236
39620
|
}
|
|
39237
39621
|
const start = Date.now();
|
|
39238
39622
|
const result = await deps.runQualityCommand({
|
|
39239
39623
|
commandName: "typecheck",
|
|
39240
|
-
command
|
|
39624
|
+
command,
|
|
39241
39625
|
workdir: input.workdir,
|
|
39242
39626
|
storyId: input.storyId,
|
|
39243
|
-
stripEnvVars:
|
|
39627
|
+
stripEnvVars: quality?.stripEnvVars ?? []
|
|
39244
39628
|
});
|
|
39245
39629
|
if (result.exitCode === 0) {
|
|
39246
|
-
return { success: true, findings: [], durationMs: Date.now() - start };
|
|
39630
|
+
return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
|
|
39247
39631
|
}
|
|
39248
39632
|
const parsed = deps.parseTypecheckOutput(result.output, "auto", { workdir: input.workdir });
|
|
39249
39633
|
return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
|
|
@@ -39272,12 +39656,16 @@ var init_verify_scoped = __esm(() => {
|
|
|
39272
39656
|
config: qualityConfigSelector,
|
|
39273
39657
|
async execute(input, ctx, deps = _verifyScopedDeps) {
|
|
39274
39658
|
const logger = getLogger();
|
|
39275
|
-
const
|
|
39276
|
-
const baseCommand =
|
|
39277
|
-
if (!
|
|
39659
|
+
const quality = ctx.packageView.select(qualityConfigSelector);
|
|
39660
|
+
const baseCommand = quality.quality?.commands?.test;
|
|
39661
|
+
if (!baseCommand) {
|
|
39662
|
+
logger.warn("quality", "No test command configured \u2014 skipping scoped verify", {
|
|
39663
|
+
storyId: input.storyId,
|
|
39664
|
+
packageDir: ctx.packageView.packageDir
|
|
39665
|
+
});
|
|
39278
39666
|
return {
|
|
39279
39667
|
success: true,
|
|
39280
|
-
status: "
|
|
39668
|
+
status: "skipped",
|
|
39281
39669
|
findings: [],
|
|
39282
39670
|
durationMs: 0,
|
|
39283
39671
|
passCount: 0,
|
|
@@ -39289,11 +39677,11 @@ var init_verify_scoped = __esm(() => {
|
|
|
39289
39677
|
workdir: input.workdir,
|
|
39290
39678
|
storyId: input.storyId,
|
|
39291
39679
|
storyGitRef: input.storyGitRef,
|
|
39292
|
-
testCommand: baseCommand
|
|
39293
|
-
testScopedTemplate:
|
|
39294
|
-
smartRunnerConfig:
|
|
39295
|
-
scopeTestThreshold:
|
|
39296
|
-
fallbackFullSuiteCommand:
|
|
39680
|
+
testCommand: baseCommand,
|
|
39681
|
+
testScopedTemplate: quality.quality?.commands?.testScoped,
|
|
39682
|
+
smartRunnerConfig: quality.execution?.smartTestRunner,
|
|
39683
|
+
scopeTestThreshold: quality.quality?.scopeTestThreshold,
|
|
39684
|
+
fallbackFullSuiteCommand: quality.quality?.commands?.test,
|
|
39297
39685
|
naxIgnoreIndex: input.naxIgnoreIndex
|
|
39298
39686
|
});
|
|
39299
39687
|
if (selection.isFullSuite && regressionMode === "deferred" && !selection.isMonorepoOrchestrator && !selection.thresholdFallback) {
|
|
@@ -39320,7 +39708,7 @@ var init_verify_scoped = __esm(() => {
|
|
|
39320
39708
|
command: selection.effectiveCommand
|
|
39321
39709
|
});
|
|
39322
39710
|
}
|
|
39323
|
-
const scopedTimeout =
|
|
39711
|
+
const scopedTimeout = quality.execution?.regressionGate?.timeoutSeconds ?? 600;
|
|
39324
39712
|
logger.info("verify[scoped]", "Running scoped tests", {
|
|
39325
39713
|
storyId: input.storyId,
|
|
39326
39714
|
packageDir: input.packageDir,
|
|
@@ -39333,14 +39721,14 @@ var init_verify_scoped = __esm(() => {
|
|
|
39333
39721
|
const result = await deps.regression({
|
|
39334
39722
|
workdir: input.workdir,
|
|
39335
39723
|
command: selection.effectiveCommand,
|
|
39336
|
-
timeoutSeconds:
|
|
39337
|
-
forceExit:
|
|
39338
|
-
detectOpenHandles:
|
|
39339
|
-
detectOpenHandlesRetries:
|
|
39340
|
-
gracePeriodMs:
|
|
39341
|
-
drainTimeoutMs:
|
|
39342
|
-
shell:
|
|
39343
|
-
stripEnvVars:
|
|
39724
|
+
timeoutSeconds: quality.execution?.regressionGate?.timeoutSeconds ?? 600,
|
|
39725
|
+
forceExit: quality.quality?.forceExit,
|
|
39726
|
+
detectOpenHandles: quality.quality?.detectOpenHandles,
|
|
39727
|
+
detectOpenHandlesRetries: quality.quality?.detectOpenHandlesRetries,
|
|
39728
|
+
gracePeriodMs: quality.quality?.gracePeriodMs,
|
|
39729
|
+
drainTimeoutMs: quality.quality?.drainTimeoutMs,
|
|
39730
|
+
shell: quality.quality?.shell,
|
|
39731
|
+
stripEnvVars: quality.quality?.stripEnvVars
|
|
39344
39732
|
});
|
|
39345
39733
|
const durationMs = Date.now() - start;
|
|
39346
39734
|
const parsed = result.output ? deps.parseTestOutput(result.output) : { passed: 0, failed: 0, failures: [] };
|
|
@@ -39441,6 +39829,7 @@ var init_operations = __esm(() => {
|
|
|
39441
39829
|
init_autofix_test_writer_strategy();
|
|
39442
39830
|
init_apply_test_edit_declarations();
|
|
39443
39831
|
init_validate_mock_structure_files();
|
|
39832
|
+
init_setup_generate();
|
|
39444
39833
|
init__finding_to_check();
|
|
39445
39834
|
init_mechanical_lintfix_strategy();
|
|
39446
39835
|
init_mechanical_formatfix_strategy();
|
|
@@ -40111,7 +40500,7 @@ var init_lint_parsing = __esm(() => {
|
|
|
40111
40500
|
});
|
|
40112
40501
|
|
|
40113
40502
|
// src/review/scoped-lint.ts
|
|
40114
|
-
import { join as
|
|
40503
|
+
import { join as join26, relative as relative10 } from "path";
|
|
40115
40504
|
function shellQuotePath4(path5) {
|
|
40116
40505
|
return `'${path5.replaceAll("'", "'\\''")}'`;
|
|
40117
40506
|
}
|
|
@@ -40159,7 +40548,7 @@ function uniqueFiles(files) {
|
|
|
40159
40548
|
async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
|
|
40160
40549
|
const inScope = [];
|
|
40161
40550
|
for (const relPath of files) {
|
|
40162
|
-
const absPath =
|
|
40551
|
+
const absPath = join26(workdir, relPath);
|
|
40163
40552
|
const exists = await _scopedLintDeps.fileExists(absPath);
|
|
40164
40553
|
if (!exists)
|
|
40165
40554
|
continue;
|
|
@@ -43533,7 +43922,7 @@ var init_call = __esm(() => {
|
|
|
43533
43922
|
|
|
43534
43923
|
// src/runtime/cost-aggregator.ts
|
|
43535
43924
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
43536
|
-
import { join as
|
|
43925
|
+
import { join as join27 } from "path";
|
|
43537
43926
|
function makeCorrelationId() {
|
|
43538
43927
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
43539
43928
|
}
|
|
@@ -43724,7 +44113,7 @@ class CostAggregator {
|
|
|
43724
44113
|
if (events.length === 0 && errors3.length === 0)
|
|
43725
44114
|
return;
|
|
43726
44115
|
mkdirSync2(this._drainDir, { recursive: true });
|
|
43727
|
-
const path5 =
|
|
44116
|
+
const path5 = join27(this._drainDir, `${this._runId}.jsonl`);
|
|
43728
44117
|
const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
|
|
43729
44118
|
await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
|
|
43730
44119
|
`)}
|
|
@@ -43764,7 +44153,7 @@ var init_cost_aggregator = __esm(() => {
|
|
|
43764
44153
|
// src/runtime/prompt-auditor.ts
|
|
43765
44154
|
import { appendFileSync } from "fs";
|
|
43766
44155
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
43767
|
-
import { join as
|
|
44156
|
+
import { join as join28 } from "path";
|
|
43768
44157
|
function createNoOpPromptAuditor() {
|
|
43769
44158
|
return {
|
|
43770
44159
|
record() {},
|
|
@@ -43830,8 +44219,8 @@ class PromptAuditor {
|
|
|
43830
44219
|
_jsonlPath;
|
|
43831
44220
|
_featureDir;
|
|
43832
44221
|
constructor(runId, flushDir, featureName) {
|
|
43833
|
-
this._featureDir =
|
|
43834
|
-
this._jsonlPath =
|
|
44222
|
+
this._featureDir = join28(flushDir, featureName);
|
|
44223
|
+
this._jsonlPath = join28(this._featureDir, `${runId}.jsonl`);
|
|
43835
44224
|
}
|
|
43836
44225
|
record(entry) {
|
|
43837
44226
|
this._enqueue(entry);
|
|
@@ -43880,7 +44269,7 @@ class PromptAuditor {
|
|
|
43880
44269
|
const auditEntry = entry;
|
|
43881
44270
|
const filename = deriveTxtFilename(auditEntry);
|
|
43882
44271
|
try {
|
|
43883
|
-
await _promptAuditorDeps.write(
|
|
44272
|
+
await _promptAuditorDeps.write(join28(this._featureDir, filename), buildTxtContent(auditEntry));
|
|
43884
44273
|
} catch (err) {
|
|
43885
44274
|
throw tagAuditError(err, "txt");
|
|
43886
44275
|
}
|
|
@@ -44015,17 +44404,44 @@ function createPackageView(config2, packageDir, repoRoot) {
|
|
|
44015
44404
|
}
|
|
44016
44405
|
function createPackageRegistry(loader, repoRoot) {
|
|
44017
44406
|
const cache = new Map;
|
|
44407
|
+
const mergedConfigs = new Map;
|
|
44408
|
+
function toRelativeKey(packageDir) {
|
|
44409
|
+
if (!packageDir)
|
|
44410
|
+
return "";
|
|
44411
|
+
const prefix = repoRoot.endsWith("/") ? repoRoot : `${repoRoot}/`;
|
|
44412
|
+
if (packageDir.startsWith(prefix))
|
|
44413
|
+
return packageDir.slice(prefix.length);
|
|
44414
|
+
if (packageDir === repoRoot)
|
|
44415
|
+
return "";
|
|
44416
|
+
return packageDir;
|
|
44417
|
+
}
|
|
44018
44418
|
function resolve12(packageDir) {
|
|
44019
|
-
const key = packageDir
|
|
44419
|
+
const key = toRelativeKey(packageDir);
|
|
44020
44420
|
const cached2 = cache.get(key);
|
|
44021
44421
|
if (cached2 !== undefined) {
|
|
44022
44422
|
return cached2;
|
|
44023
44423
|
}
|
|
44024
|
-
const config2 = loader.current();
|
|
44424
|
+
const config2 = mergedConfigs.get(key) ?? loader.current();
|
|
44025
44425
|
const view = createPackageView(config2, key, repoRoot);
|
|
44026
44426
|
cache.set(key, view);
|
|
44027
44427
|
return view;
|
|
44028
44428
|
}
|
|
44429
|
+
async function hydrate(packageDirs, loadOverride2) {
|
|
44430
|
+
const load = loadOverride2 ?? (await Promise.resolve().then(() => (init_config(), exports_config))).loadPackageOverride;
|
|
44431
|
+
for (const dir of packageDirs) {
|
|
44432
|
+
if (!dir) {
|
|
44433
|
+
continue;
|
|
44434
|
+
}
|
|
44435
|
+
if (mergedConfigs.has(dir)) {
|
|
44436
|
+
continue;
|
|
44437
|
+
}
|
|
44438
|
+
const override = await load(repoRoot, dir);
|
|
44439
|
+
if (override !== null) {
|
|
44440
|
+
mergedConfigs.set(dir, mergePackageConfig(loader.current(), override));
|
|
44441
|
+
cache.delete(dir);
|
|
44442
|
+
}
|
|
44443
|
+
}
|
|
44444
|
+
}
|
|
44029
44445
|
return {
|
|
44030
44446
|
all() {
|
|
44031
44447
|
return [...cache.values()];
|
|
@@ -44033,9 +44449,13 @@ function createPackageRegistry(loader, repoRoot) {
|
|
|
44033
44449
|
resolve: resolve12,
|
|
44034
44450
|
repo() {
|
|
44035
44451
|
return resolve12(undefined);
|
|
44036
|
-
}
|
|
44452
|
+
},
|
|
44453
|
+
hydrate
|
|
44037
44454
|
};
|
|
44038
44455
|
}
|
|
44456
|
+
var init_packages = __esm(() => {
|
|
44457
|
+
init_config();
|
|
44458
|
+
});
|
|
44039
44459
|
|
|
44040
44460
|
// src/runtime/agent-stream-events.ts
|
|
44041
44461
|
class AgentStreamEventBus {
|
|
@@ -44655,7 +45075,8 @@ var init_session_role = __esm(() => {
|
|
|
44655
45075
|
"fix-gen",
|
|
44656
45076
|
"auto",
|
|
44657
45077
|
"synthesis",
|
|
44658
|
-
"judge"
|
|
45078
|
+
"judge",
|
|
45079
|
+
"setup"
|
|
44659
45080
|
];
|
|
44660
45081
|
});
|
|
44661
45082
|
|
|
@@ -44992,7 +45413,7 @@ var init_pid_registry = __esm(() => {
|
|
|
44992
45413
|
// src/session/manager-deps.ts
|
|
44993
45414
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
44994
45415
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
44995
|
-
import { isAbsolute as isAbsolute9, join as
|
|
45416
|
+
import { isAbsolute as isAbsolute9, join as join29, relative as relative11, sep as sep2 } from "path";
|
|
44996
45417
|
function resolveProjectDirFromScratchDir(scratchDir) {
|
|
44997
45418
|
const marker = `${sep2}.nax${sep2}features${sep2}`;
|
|
44998
45419
|
const markerIdx = scratchDir.lastIndexOf(marker);
|
|
@@ -45013,7 +45434,7 @@ var init_manager_deps = __esm(() => {
|
|
|
45013
45434
|
now: () => new Date().toISOString(),
|
|
45014
45435
|
nowMs: () => Date.now(),
|
|
45015
45436
|
uuid: () => randomUUID3(),
|
|
45016
|
-
sessionScratchDir: (projectDir, featureName, sessionId) =>
|
|
45437
|
+
sessionScratchDir: (projectDir, featureName, sessionId) => join29(projectDir, ".nax", "features", featureName, "sessions", sessionId),
|
|
45017
45438
|
writeDescriptor: async (scratchDir, descriptor, projectDir) => {
|
|
45018
45439
|
await mkdir5(scratchDir, { recursive: true });
|
|
45019
45440
|
const { handle: _handle, ...persistable } = descriptor;
|
|
@@ -45024,7 +45445,7 @@ var init_manager_deps = __esm(() => {
|
|
|
45024
45445
|
persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
|
|
45025
45446
|
}
|
|
45026
45447
|
}
|
|
45027
|
-
await Bun.write(
|
|
45448
|
+
await Bun.write(join29(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
|
|
45028
45449
|
}
|
|
45029
45450
|
};
|
|
45030
45451
|
});
|
|
@@ -45775,7 +46196,7 @@ __export(exports_runtime, {
|
|
|
45775
46196
|
CostAggregator: () => CostAggregator,
|
|
45776
46197
|
AgentStreamEventBus: () => AgentStreamEventBus
|
|
45777
46198
|
});
|
|
45778
|
-
import { basename as basename5, join as
|
|
46199
|
+
import { basename as basename5, join as join30 } from "path";
|
|
45779
46200
|
function createRuntime(config2, workdir, opts) {
|
|
45780
46201
|
const runId = crypto.randomUUID();
|
|
45781
46202
|
const controller = new AbortController;
|
|
@@ -45791,10 +46212,10 @@ function createRuntime(config2, workdir, opts) {
|
|
|
45791
46212
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
45792
46213
|
const globalDir = globalOutputDir();
|
|
45793
46214
|
const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
|
|
45794
|
-
const costDir =
|
|
46215
|
+
const costDir = join30(outputDir, "cost");
|
|
45795
46216
|
const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
|
|
45796
46217
|
const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
|
|
45797
|
-
const auditDir = config2.agent?.promptAudit?.dir ??
|
|
46218
|
+
const auditDir = config2.agent?.promptAudit?.dir ?? join30(outputDir, "prompt-audit");
|
|
45798
46219
|
let promptAuditor;
|
|
45799
46220
|
if (opts?.promptAuditor) {
|
|
45800
46221
|
promptAuditor = opts.promptAuditor;
|
|
@@ -45900,6 +46321,7 @@ var init_runtime = __esm(() => {
|
|
|
45900
46321
|
init_prompt_auditor();
|
|
45901
46322
|
init_review_audit();
|
|
45902
46323
|
init_paths2();
|
|
46324
|
+
init_packages();
|
|
45903
46325
|
init_dispatch_events();
|
|
45904
46326
|
init_agent_stream_events();
|
|
45905
46327
|
init_middleware();
|
|
@@ -45917,6 +46339,7 @@ var init_runtime = __esm(() => {
|
|
|
45917
46339
|
init_cost_aggregator();
|
|
45918
46340
|
init_dispatch_events();
|
|
45919
46341
|
init_middleware();
|
|
46342
|
+
init_packages();
|
|
45920
46343
|
init_paths2();
|
|
45921
46344
|
init_prompt_auditor();
|
|
45922
46345
|
init_session_run_hop();
|
|
@@ -45945,9 +46368,9 @@ async function allSettledBounded(tasks, limit) {
|
|
|
45945
46368
|
|
|
45946
46369
|
// src/context/injector.ts
|
|
45947
46370
|
import { existsSync as existsSync8 } from "fs";
|
|
45948
|
-
import { join as
|
|
46371
|
+
import { join as join31 } from "path";
|
|
45949
46372
|
async function detectNode(workdir) {
|
|
45950
|
-
const pkgPath =
|
|
46373
|
+
const pkgPath = join31(workdir, "package.json");
|
|
45951
46374
|
if (!existsSync8(pkgPath))
|
|
45952
46375
|
return null;
|
|
45953
46376
|
try {
|
|
@@ -45964,7 +46387,7 @@ async function detectNode(workdir) {
|
|
|
45964
46387
|
}
|
|
45965
46388
|
}
|
|
45966
46389
|
async function detectGo(workdir) {
|
|
45967
|
-
const goMod =
|
|
46390
|
+
const goMod = join31(workdir, "go.mod");
|
|
45968
46391
|
if (!existsSync8(goMod))
|
|
45969
46392
|
return null;
|
|
45970
46393
|
try {
|
|
@@ -45988,7 +46411,7 @@ async function detectGo(workdir) {
|
|
|
45988
46411
|
}
|
|
45989
46412
|
}
|
|
45990
46413
|
async function detectRust(workdir) {
|
|
45991
|
-
const cargoPath =
|
|
46414
|
+
const cargoPath = join31(workdir, "Cargo.toml");
|
|
45992
46415
|
if (!existsSync8(cargoPath))
|
|
45993
46416
|
return null;
|
|
45994
46417
|
try {
|
|
@@ -46004,8 +46427,8 @@ async function detectRust(workdir) {
|
|
|
46004
46427
|
}
|
|
46005
46428
|
}
|
|
46006
46429
|
async function detectPython(workdir) {
|
|
46007
|
-
const pyproject =
|
|
46008
|
-
const requirements =
|
|
46430
|
+
const pyproject = join31(workdir, "pyproject.toml");
|
|
46431
|
+
const requirements = join31(workdir, "requirements.txt");
|
|
46009
46432
|
if (!existsSync8(pyproject) && !existsSync8(requirements))
|
|
46010
46433
|
return null;
|
|
46011
46434
|
try {
|
|
@@ -46024,7 +46447,7 @@ async function detectPython(workdir) {
|
|
|
46024
46447
|
}
|
|
46025
46448
|
}
|
|
46026
46449
|
async function detectPhp(workdir) {
|
|
46027
|
-
const composerPath =
|
|
46450
|
+
const composerPath = join31(workdir, "composer.json");
|
|
46028
46451
|
if (!existsSync8(composerPath))
|
|
46029
46452
|
return null;
|
|
46030
46453
|
try {
|
|
@@ -46037,7 +46460,7 @@ async function detectPhp(workdir) {
|
|
|
46037
46460
|
}
|
|
46038
46461
|
}
|
|
46039
46462
|
async function detectRuby(workdir) {
|
|
46040
|
-
const gemfile =
|
|
46463
|
+
const gemfile = join31(workdir, "Gemfile");
|
|
46041
46464
|
if (!existsSync8(gemfile))
|
|
46042
46465
|
return null;
|
|
46043
46466
|
try {
|
|
@@ -46049,9 +46472,9 @@ async function detectRuby(workdir) {
|
|
|
46049
46472
|
}
|
|
46050
46473
|
}
|
|
46051
46474
|
async function detectJvm(workdir) {
|
|
46052
|
-
const pom =
|
|
46053
|
-
const gradle =
|
|
46054
|
-
const gradleKts =
|
|
46475
|
+
const pom = join31(workdir, "pom.xml");
|
|
46476
|
+
const gradle = join31(workdir, "build.gradle");
|
|
46477
|
+
const gradleKts = join31(workdir, "build.gradle.kts");
|
|
46055
46478
|
if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
|
|
46056
46479
|
return null;
|
|
46057
46480
|
try {
|
|
@@ -46059,7 +46482,7 @@ async function detectJvm(workdir) {
|
|
|
46059
46482
|
const content2 = await Bun.file(pom).text();
|
|
46060
46483
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
46061
46484
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
46062
|
-
const lang2 = existsSync8(
|
|
46485
|
+
const lang2 = existsSync8(join31(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
46063
46486
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
46064
46487
|
}
|
|
46065
46488
|
const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
|
|
@@ -46313,7 +46736,7 @@ var init_windsurf = __esm(() => {
|
|
|
46313
46736
|
|
|
46314
46737
|
// src/context/generator.ts
|
|
46315
46738
|
import { existsSync as existsSync9 } from "fs";
|
|
46316
|
-
import { join as
|
|
46739
|
+
import { join as join32, relative as relative12 } from "path";
|
|
46317
46740
|
async function loadContextContent(options, config2) {
|
|
46318
46741
|
if (!_generatorDeps.existsSync(options.contextPath)) {
|
|
46319
46742
|
throw new Error(`Context file not found: ${options.contextPath}`);
|
|
@@ -46331,7 +46754,7 @@ async function generateFor(agent, options, config2) {
|
|
|
46331
46754
|
try {
|
|
46332
46755
|
const context = await loadContextContent(options, config2);
|
|
46333
46756
|
const content = generator.generate(context);
|
|
46334
|
-
const outputPath =
|
|
46757
|
+
const outputPath = join32(options.outputDir, generator.outputFile);
|
|
46335
46758
|
validateFilePath(outputPath, options.outputDir);
|
|
46336
46759
|
if (!options.dryRun) {
|
|
46337
46760
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -46349,7 +46772,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
46349
46772
|
for (const [agentKey, generator] of entries) {
|
|
46350
46773
|
try {
|
|
46351
46774
|
const content = generator.generate(context);
|
|
46352
|
-
const outputPath =
|
|
46775
|
+
const outputPath = join32(options.outputDir, generator.outputFile);
|
|
46353
46776
|
validateFilePath(outputPath, options.outputDir);
|
|
46354
46777
|
if (!options.dryRun) {
|
|
46355
46778
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -46369,7 +46792,7 @@ async function discoverPackages(repoRoot) {
|
|
|
46369
46792
|
const glob = new Bun.Glob(pattern);
|
|
46370
46793
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
46371
46794
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
46372
|
-
const pkgAbsolute =
|
|
46795
|
+
const pkgAbsolute = join32(repoRoot, pkgRelative);
|
|
46373
46796
|
if (!seen.has(pkgAbsolute)) {
|
|
46374
46797
|
seen.add(pkgAbsolute);
|
|
46375
46798
|
packages.push(pkgAbsolute);
|
|
@@ -46401,14 +46824,14 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
46401
46824
|
}
|
|
46402
46825
|
}
|
|
46403
46826
|
}
|
|
46404
|
-
const turboPath =
|
|
46827
|
+
const turboPath = join32(repoRoot, "turbo.json");
|
|
46405
46828
|
try {
|
|
46406
46829
|
const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
|
|
46407
46830
|
if (Array.isArray(turbo.packages)) {
|
|
46408
46831
|
await resolveGlobs(turbo.packages);
|
|
46409
46832
|
}
|
|
46410
46833
|
} catch {}
|
|
46411
|
-
const pkgPath =
|
|
46834
|
+
const pkgPath = join32(repoRoot, "package.json");
|
|
46412
46835
|
try {
|
|
46413
46836
|
const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
|
|
46414
46837
|
const ws = pkg.workspaces;
|
|
@@ -46416,7 +46839,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
46416
46839
|
if (patterns.length > 0)
|
|
46417
46840
|
await resolveGlobs(patterns);
|
|
46418
46841
|
} catch {}
|
|
46419
|
-
const pnpmPath =
|
|
46842
|
+
const pnpmPath = join32(repoRoot, "pnpm-workspace.yaml");
|
|
46420
46843
|
try {
|
|
46421
46844
|
const raw = await _generatorDeps.readTextFile(pnpmPath);
|
|
46422
46845
|
const lines = raw.split(`
|
|
@@ -46442,7 +46865,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
46442
46865
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
46443
46866
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
46444
46867
|
const relativePkgPath = relative12(resolvedRepoRoot, packageDir);
|
|
46445
|
-
const contextPath =
|
|
46868
|
+
const contextPath = join32(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
46446
46869
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
46447
46870
|
return [
|
|
46448
46871
|
{
|
|
@@ -46510,7 +46933,7 @@ var init_generator2 = __esm(() => {
|
|
|
46510
46933
|
});
|
|
46511
46934
|
|
|
46512
46935
|
// src/analyze/scanner.ts
|
|
46513
|
-
import { join as
|
|
46936
|
+
import { join as join33 } from "path";
|
|
46514
46937
|
function resolveFrameworkAndRunner(language, pkg) {
|
|
46515
46938
|
if (language === "go")
|
|
46516
46939
|
return { framework: "", testRunner: "go-test" };
|
|
@@ -46532,7 +46955,7 @@ async function scanSourceRoots(workdir) {
|
|
|
46532
46955
|
});
|
|
46533
46956
|
try {
|
|
46534
46957
|
const language = await deps.detectLanguage(workdir);
|
|
46535
|
-
const pkg = await deps.readPackageJson(
|
|
46958
|
+
const pkg = await deps.readPackageJson(join33(workdir, "package.json"));
|
|
46536
46959
|
const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
|
|
46537
46960
|
return [{ path: ".", language, framework, testRunner }];
|
|
46538
46961
|
} catch {
|
|
@@ -46550,9 +46973,9 @@ async function scanSourceRoots(workdir) {
|
|
|
46550
46973
|
packages = packages.slice(0, MAX_SOURCE_ROOTS);
|
|
46551
46974
|
}
|
|
46552
46975
|
return Promise.all(packages.map(async (pkgPath) => {
|
|
46553
|
-
const pkgDir = pkgPath === "." ? workdir :
|
|
46976
|
+
const pkgDir = pkgPath === "." ? workdir : join33(workdir, pkgPath);
|
|
46554
46977
|
const language = await deps.detectLanguage(pkgDir);
|
|
46555
|
-
const pkg = await deps.readPackageJson(
|
|
46978
|
+
const pkg = await deps.readPackageJson(join33(pkgDir, "package.json"));
|
|
46556
46979
|
const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
|
|
46557
46980
|
return { path: pkgPath, language, framework, testRunner };
|
|
46558
46981
|
}));
|
|
@@ -46585,7 +47008,7 @@ var init_analyze = __esm(() => {
|
|
|
46585
47008
|
});
|
|
46586
47009
|
|
|
46587
47010
|
// src/debate/pre-phase/grounder.ts
|
|
46588
|
-
import { join as
|
|
47011
|
+
import { join as join34 } from "path";
|
|
46589
47012
|
async function buildCodebaseContext(workdir) {
|
|
46590
47013
|
const roots = await _grounderDeps.scanSourceRoots(workdir);
|
|
46591
47014
|
return buildSourceRootsSection(normalizeRoots(workdir, roots));
|
|
@@ -46597,7 +47020,7 @@ function normalizeRoots(workdir, roots) {
|
|
|
46597
47020
|
}));
|
|
46598
47021
|
}
|
|
46599
47022
|
async function writeManifestArtifact(ctx, manifest) {
|
|
46600
|
-
const manifestPath =
|
|
47023
|
+
const manifestPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
|
|
46601
47024
|
await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
|
|
46602
47025
|
}
|
|
46603
47026
|
var _grounderDeps, grounderStrategy = async (ctx) => {
|
|
@@ -46940,7 +47363,7 @@ function formatSpecDeltas(blockers, manifest) {
|
|
|
46940
47363
|
|
|
46941
47364
|
// src/debate/verifiers/checks.ts
|
|
46942
47365
|
import { existsSync as defaultExistsSync } from "fs";
|
|
46943
|
-
import { join as
|
|
47366
|
+
import { join as join35 } from "path";
|
|
46944
47367
|
function checkFilesExist(prd, workdir, deps) {
|
|
46945
47368
|
const existsSync10 = deps?.existsSync ?? defaultExistsSync;
|
|
46946
47369
|
const findings = [];
|
|
@@ -46950,7 +47373,7 @@ function checkFilesExist(prd, workdir, deps) {
|
|
|
46950
47373
|
for (const entry of story.contextFiles) {
|
|
46951
47374
|
const filePath = typeof entry === "string" ? entry : entry.path;
|
|
46952
47375
|
const factId = typeof entry === "string" ? undefined : entry.factId;
|
|
46953
|
-
const absPath =
|
|
47376
|
+
const absPath = join35(workdir, filePath);
|
|
46954
47377
|
if (existsSync10(absPath))
|
|
46955
47378
|
continue;
|
|
46956
47379
|
if (factId) {
|
|
@@ -47061,7 +47484,7 @@ var init_checks3 = () => {};
|
|
|
47061
47484
|
|
|
47062
47485
|
// src/debate/verifiers/plan-checklist.ts
|
|
47063
47486
|
import { existsSync as existsSync10 } from "fs";
|
|
47064
|
-
import { join as
|
|
47487
|
+
import { join as join36 } from "path";
|
|
47065
47488
|
function parsePrd(output) {
|
|
47066
47489
|
if (!output)
|
|
47067
47490
|
return null;
|
|
@@ -47072,7 +47495,7 @@ function parsePrd(output) {
|
|
|
47072
47495
|
}
|
|
47073
47496
|
}
|
|
47074
47497
|
async function loadManifest(ctx) {
|
|
47075
|
-
const manifestPath =
|
|
47498
|
+
const manifestPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
|
|
47076
47499
|
const raw = await _planChecklistDeps.readFile(manifestPath);
|
|
47077
47500
|
if (!raw)
|
|
47078
47501
|
return null;
|
|
@@ -47085,7 +47508,7 @@ async function loadManifest(ctx) {
|
|
|
47085
47508
|
}
|
|
47086
47509
|
}
|
|
47087
47510
|
async function emitSpecDeltas(ctx, blockers, manifest) {
|
|
47088
|
-
const artifactPath =
|
|
47511
|
+
const artifactPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
|
|
47089
47512
|
const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
|
|
47090
47513
|
await _planChecklistDeps.write(artifactPath, content);
|
|
47091
47514
|
return artifactPath;
|
|
@@ -47431,7 +47854,7 @@ var init_runner_plan_helpers = __esm(() => {
|
|
|
47431
47854
|
});
|
|
47432
47855
|
|
|
47433
47856
|
// src/debate/runner-plan.ts
|
|
47434
|
-
import { join as
|
|
47857
|
+
import { join as join37 } from "path";
|
|
47435
47858
|
async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
47436
47859
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
47437
47860
|
const config2 = ctx.stageConfig;
|
|
@@ -47490,7 +47913,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
47490
47913
|
sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
|
|
47491
47914
|
proposers: ctx.stageConfig.proposers
|
|
47492
47915
|
});
|
|
47493
|
-
const outputPaths = resolved.map((_, i) =>
|
|
47916
|
+
const outputPaths = resolved.map((_, i) => join37(opts.outputDir, `prd-debate-${i}.json`));
|
|
47494
47917
|
const successful = [];
|
|
47495
47918
|
let rebuttalList;
|
|
47496
47919
|
if (selectorKind === "verifier-pick") {
|
|
@@ -49733,9 +50156,9 @@ function validateFeatureName(feature) {
|
|
|
49733
50156
|
|
|
49734
50157
|
// src/plan/critic.ts
|
|
49735
50158
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
49736
|
-
import { dirname as dirname7, join as
|
|
50159
|
+
import { dirname as dirname7, join as join40 } from "path";
|
|
49737
50160
|
async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
|
|
49738
|
-
const path7 =
|
|
50161
|
+
const path7 = join40(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
|
|
49739
50162
|
await mkdir6(dirname7(path7), { recursive: true });
|
|
49740
50163
|
await Bun.write(path7, formatSpecDeltas(findings, manifest));
|
|
49741
50164
|
return path7;
|
|
@@ -50948,9 +51371,9 @@ __export(exports_plan_decompose, {
|
|
|
50948
51371
|
runReplanLoop: () => runReplanLoop,
|
|
50949
51372
|
planDecomposeCommand: () => planDecomposeCommand
|
|
50950
51373
|
});
|
|
50951
|
-
import { join as
|
|
51374
|
+
import { join as join41 } from "path";
|
|
50952
51375
|
async function planDecomposeCommand(workdir, config2, options) {
|
|
50953
|
-
const prdPath =
|
|
51376
|
+
const prdPath = join41(workdir, ".nax", "features", options.feature, "prd.json");
|
|
50954
51377
|
if (!_planDeps.existsSync(prdPath)) {
|
|
50955
51378
|
throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
|
|
50956
51379
|
stage: "decompose",
|
|
@@ -51124,7 +51547,7 @@ var init_plan_decompose = __esm(() => {
|
|
|
51124
51547
|
|
|
51125
51548
|
// src/cli/plan-runtime.ts
|
|
51126
51549
|
import { existsSync as existsSync15 } from "fs";
|
|
51127
|
-
import { join as
|
|
51550
|
+
import { join as join42 } from "path";
|
|
51128
51551
|
function isRuntimeWithAgentManager(value) {
|
|
51129
51552
|
return typeof value === "object" && value !== null && "agentManager" in value;
|
|
51130
51553
|
}
|
|
@@ -51176,7 +51599,7 @@ var init_plan_runtime = __esm(() => {
|
|
|
51176
51599
|
writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
|
|
51177
51600
|
scanSourceRoots: (workdir) => scanSourceRoots(workdir),
|
|
51178
51601
|
createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
|
|
51179
|
-
readPackageJson: (workdir) => Bun.file(
|
|
51602
|
+
readPackageJson: (workdir) => Bun.file(join42(workdir, "package.json")).json().catch(() => null),
|
|
51180
51603
|
spawnSync: (cmd, opts) => {
|
|
51181
51604
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
51182
51605
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
@@ -51561,7 +51984,7 @@ var init_metrics = __esm(() => {
|
|
|
51561
51984
|
|
|
51562
51985
|
// src/commands/common.ts
|
|
51563
51986
|
import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
|
|
51564
|
-
import { join as
|
|
51987
|
+
import { join as join43, resolve as resolve13 } from "path";
|
|
51565
51988
|
function resolveProject(options = {}) {
|
|
51566
51989
|
const { dir, feature } = options;
|
|
51567
51990
|
let projectRoot;
|
|
@@ -51569,12 +51992,12 @@ function resolveProject(options = {}) {
|
|
|
51569
51992
|
let configPath;
|
|
51570
51993
|
if (dir) {
|
|
51571
51994
|
projectRoot = realpathSync3(resolve13(dir));
|
|
51572
|
-
naxDir =
|
|
51995
|
+
naxDir = join43(projectRoot, ".nax");
|
|
51573
51996
|
if (!existsSync16(naxDir)) {
|
|
51574
51997
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
51575
51998
|
Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
|
|
51576
51999
|
}
|
|
51577
|
-
configPath =
|
|
52000
|
+
configPath = join43(naxDir, "config.json");
|
|
51578
52001
|
if (!existsSync16(configPath)) {
|
|
51579
52002
|
throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
|
|
51580
52003
|
Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
@@ -51582,17 +52005,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
|
51582
52005
|
} else {
|
|
51583
52006
|
const found = findProjectRoot(process.cwd());
|
|
51584
52007
|
if (!found) {
|
|
51585
|
-
const cwdNaxDir =
|
|
52008
|
+
const cwdNaxDir = join43(process.cwd(), ".nax");
|
|
51586
52009
|
if (existsSync16(cwdNaxDir)) {
|
|
51587
|
-
const cwdConfigPath =
|
|
52010
|
+
const cwdConfigPath = join43(cwdNaxDir, "config.json");
|
|
51588
52011
|
throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
|
|
51589
52012
|
Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
|
|
51590
52013
|
}
|
|
51591
52014
|
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() });
|
|
51592
52015
|
}
|
|
51593
52016
|
projectRoot = found;
|
|
51594
|
-
naxDir =
|
|
51595
|
-
configPath =
|
|
52017
|
+
naxDir = join43(projectRoot, ".nax");
|
|
52018
|
+
configPath = join43(naxDir, "config.json");
|
|
51596
52019
|
}
|
|
51597
52020
|
let featureDir;
|
|
51598
52021
|
if (feature) {
|
|
@@ -51601,8 +52024,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
|
|
|
51601
52024
|
} catch (error48) {
|
|
51602
52025
|
throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
|
|
51603
52026
|
}
|
|
51604
|
-
const featuresDir =
|
|
51605
|
-
featureDir =
|
|
52027
|
+
const featuresDir = join43(naxDir, "features");
|
|
52028
|
+
featureDir = join43(featuresDir, feature);
|
|
51606
52029
|
if (!existsSync16(featureDir)) {
|
|
51607
52030
|
const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
|
|
51608
52031
|
const availableMsg = availableFeatures.length > 0 ? `
|
|
@@ -51635,7 +52058,7 @@ async function resolveProjectAsync(options = {}) {
|
|
|
51635
52058
|
}
|
|
51636
52059
|
const isPlainName = !dir.includes("/") && !dir.includes("\\");
|
|
51637
52060
|
if (isPlainName) {
|
|
51638
|
-
const registryIdentityPath =
|
|
52061
|
+
const registryIdentityPath = join43(globalConfigDir(), dir, ".identity");
|
|
51639
52062
|
const identityFile = Bun.file(registryIdentityPath);
|
|
51640
52063
|
if (await identityFile.exists()) {
|
|
51641
52064
|
try {
|
|
@@ -51667,12 +52090,12 @@ function findProjectRoot(startDir) {
|
|
|
51667
52090
|
let current = resolve13(startDir);
|
|
51668
52091
|
let depth = 0;
|
|
51669
52092
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
51670
|
-
const naxDir =
|
|
51671
|
-
const configPath =
|
|
52093
|
+
const naxDir = join43(current, ".nax");
|
|
52094
|
+
const configPath = join43(naxDir, "config.json");
|
|
51672
52095
|
if (existsSync16(configPath)) {
|
|
51673
52096
|
return realpathSync3(current);
|
|
51674
52097
|
}
|
|
51675
|
-
const parent =
|
|
52098
|
+
const parent = join43(current, "..");
|
|
51676
52099
|
if (parent === current) {
|
|
51677
52100
|
break;
|
|
51678
52101
|
}
|
|
@@ -52831,10 +53254,10 @@ var init_effectiveness = __esm(() => {
|
|
|
52831
53254
|
|
|
52832
53255
|
// src/execution/progress.ts
|
|
52833
53256
|
import { appendFile as appendFile2, mkdir as mkdir7 } from "fs/promises";
|
|
52834
|
-
import { join as
|
|
53257
|
+
import { join as join46 } from "path";
|
|
52835
53258
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
52836
53259
|
await mkdir7(featureDir, { recursive: true });
|
|
52837
|
-
const progressPath =
|
|
53260
|
+
const progressPath = join46(featureDir, "progress.txt");
|
|
52838
53261
|
const timestamp = new Date().toISOString();
|
|
52839
53262
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
52840
53263
|
`;
|
|
@@ -53028,7 +53451,7 @@ var init_completion = __esm(() => {
|
|
|
53028
53451
|
|
|
53029
53452
|
// src/constitution/loader.ts
|
|
53030
53453
|
import { existsSync as existsSync19 } from "fs";
|
|
53031
|
-
import { join as
|
|
53454
|
+
import { join as join47 } from "path";
|
|
53032
53455
|
function truncateToTokens(text, maxTokens) {
|
|
53033
53456
|
const maxChars = maxTokens * 3;
|
|
53034
53457
|
if (text.length <= maxChars) {
|
|
@@ -53050,7 +53473,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
53050
53473
|
}
|
|
53051
53474
|
let combinedContent = "";
|
|
53052
53475
|
if (!config2.skipGlobal) {
|
|
53053
|
-
const globalPath =
|
|
53476
|
+
const globalPath = join47(globalConfigDir(), config2.path);
|
|
53054
53477
|
if (existsSync19(globalPath)) {
|
|
53055
53478
|
const validatedPath = validateFilePath(globalPath, globalConfigDir());
|
|
53056
53479
|
const globalFile = Bun.file(validatedPath);
|
|
@@ -53060,7 +53483,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
53060
53483
|
}
|
|
53061
53484
|
}
|
|
53062
53485
|
}
|
|
53063
|
-
const projectPath =
|
|
53486
|
+
const projectPath = join47(projectDir, config2.path);
|
|
53064
53487
|
if (existsSync19(projectPath)) {
|
|
53065
53488
|
const validatedPath = validateFilePath(projectPath, projectDir);
|
|
53066
53489
|
const projectFile = Bun.file(validatedPath);
|
|
@@ -53532,6 +53955,166 @@ var init_context2 = __esm(() => {
|
|
|
53532
53955
|
};
|
|
53533
53956
|
});
|
|
53534
53957
|
|
|
53958
|
+
// src/tdd/rollback.ts
|
|
53959
|
+
async function rollbackToRef(workdir, ref) {
|
|
53960
|
+
const logger = getLogger();
|
|
53961
|
+
logger.warn("tdd", "Rolling back git changes", { ref });
|
|
53962
|
+
const resetProc = _rollbackDeps.spawn(["git", "reset", "--hard", ref], {
|
|
53963
|
+
cwd: workdir,
|
|
53964
|
+
stdout: "pipe",
|
|
53965
|
+
stderr: "pipe"
|
|
53966
|
+
});
|
|
53967
|
+
const exitCode = await resetProc.exited;
|
|
53968
|
+
if (exitCode !== 0) {
|
|
53969
|
+
const stderr = await new Response(resetProc.stderr).text();
|
|
53970
|
+
logger.error("tdd", "Failed to rollback git changes", { ref, stderr });
|
|
53971
|
+
throw new Error(`Git rollback failed: ${stderr}`);
|
|
53972
|
+
}
|
|
53973
|
+
const cleanProc = _rollbackDeps.spawn(["git", "clean", "-fd"], {
|
|
53974
|
+
cwd: workdir,
|
|
53975
|
+
stdout: "pipe",
|
|
53976
|
+
stderr: "pipe"
|
|
53977
|
+
});
|
|
53978
|
+
const cleanExitCode = await cleanProc.exited;
|
|
53979
|
+
if (cleanExitCode !== 0) {
|
|
53980
|
+
const stderr = await new Response(cleanProc.stderr).text();
|
|
53981
|
+
logger.warn("tdd", "Failed to clean untracked files", { stderr });
|
|
53982
|
+
}
|
|
53983
|
+
logger.info("tdd", "Successfully rolled back git changes", { ref });
|
|
53984
|
+
}
|
|
53985
|
+
async function captureSnapshotRef(workdir, storyId) {
|
|
53986
|
+
await _rollbackDeps.autoCommitIfDirty(workdir, "non-blocking-fix-snapshot", "snapshot", storyId);
|
|
53987
|
+
const proc = _rollbackDeps.spawn(["git", "rev-parse", "HEAD"], { cwd: workdir, stdout: "pipe", stderr: "pipe" });
|
|
53988
|
+
const sha = (await new Response(proc.stdout).text()).trim();
|
|
53989
|
+
const exitCode = await proc.exited;
|
|
53990
|
+
if (exitCode !== 0) {
|
|
53991
|
+
throw new NaxError("git rev-parse HEAD failed in non-blocking-fix snapshot", "SNAPSHOT_REF_FAILED", {
|
|
53992
|
+
storyId,
|
|
53993
|
+
workdir,
|
|
53994
|
+
stage: "non-blocking-fix-snapshot"
|
|
53995
|
+
});
|
|
53996
|
+
}
|
|
53997
|
+
return sha;
|
|
53998
|
+
}
|
|
53999
|
+
var _rollbackDeps;
|
|
54000
|
+
var init_rollback = __esm(() => {
|
|
54001
|
+
init_errors();
|
|
54002
|
+
init_logger2();
|
|
54003
|
+
init_git();
|
|
54004
|
+
_rollbackDeps = {
|
|
54005
|
+
spawn: Bun.spawn,
|
|
54006
|
+
autoCommitIfDirty
|
|
54007
|
+
};
|
|
54008
|
+
});
|
|
54009
|
+
|
|
54010
|
+
// src/execution/non-blocking-fix.ts
|
|
54011
|
+
function shouldRunNonBlockingFix(cfg, advisoryCount) {
|
|
54012
|
+
return cfg?.enabled === true && advisoryCount > 0;
|
|
54013
|
+
}
|
|
54014
|
+
function nonBlockingExcludePhases() {
|
|
54015
|
+
return REVIEW_PHASE_KINDS;
|
|
54016
|
+
}
|
|
54017
|
+
function nonBlockingExtraPhases(cfg) {
|
|
54018
|
+
return cfg.scope === "both" && cfg.verifierGuard ? ["verifier"] : [];
|
|
54019
|
+
}
|
|
54020
|
+
async function runNonBlockingFix(args, _deps = DEFAULT_DEPS) {
|
|
54021
|
+
const logger = getSafeLogger();
|
|
54022
|
+
if (!shouldRunNonBlockingFix(args.cfg, args.advisoryFindings.length)) {
|
|
54023
|
+
return { ran: false, kept: false, restored: false };
|
|
54024
|
+
}
|
|
54025
|
+
const phaseOutputsSnapshot = { ...args.phaseOutputs };
|
|
54026
|
+
const restoreRef = await _deps.captureSnapshotRef(args.workdir, args.storyId);
|
|
54027
|
+
const maxAttempts = 1 + args.cfg.regressionAttempts;
|
|
54028
|
+
let exhausted = false;
|
|
54029
|
+
try {
|
|
54030
|
+
const result = await args.runRectify(maxAttempts);
|
|
54031
|
+
exhausted = result.rectificationExhausted === true;
|
|
54032
|
+
} catch (err) {
|
|
54033
|
+
logger?.warn("non-blocking-fix", "best-effort pass threw \u2014 restoring", {
|
|
54034
|
+
storyId: args.storyId,
|
|
54035
|
+
error: err instanceof Error ? err.message : String(err)
|
|
54036
|
+
});
|
|
54037
|
+
exhausted = true;
|
|
54038
|
+
}
|
|
54039
|
+
if (!exhausted) {
|
|
54040
|
+
logger?.info("non-blocking-fix", "best-effort fix kept", { storyId: args.storyId });
|
|
54041
|
+
return { ran: true, kept: true, restored: false };
|
|
54042
|
+
}
|
|
54043
|
+
await _deps.rollbackToRef(args.workdir, restoreRef);
|
|
54044
|
+
for (const key of Object.keys(args.phaseOutputs))
|
|
54045
|
+
delete args.phaseOutputs[key];
|
|
54046
|
+
Object.assign(args.phaseOutputs, phaseOutputsSnapshot);
|
|
54047
|
+
logger?.info("non-blocking-fix", "best-effort fix exhausted \u2014 restored to adversarial-passed", {
|
|
54048
|
+
storyId: args.storyId
|
|
54049
|
+
});
|
|
54050
|
+
return { ran: true, kept: false, restored: true };
|
|
54051
|
+
}
|
|
54052
|
+
var REVIEW_PHASE_KINDS, DEFAULT_DEPS;
|
|
54053
|
+
var init_non_blocking_fix = __esm(() => {
|
|
54054
|
+
init_logger2();
|
|
54055
|
+
init_rollback();
|
|
54056
|
+
REVIEW_PHASE_KINDS = ["semantic-review", "adversarial-review"];
|
|
54057
|
+
DEFAULT_DEPS = { captureSnapshotRef, rollbackToRef };
|
|
54058
|
+
});
|
|
54059
|
+
|
|
54060
|
+
// src/execution/story-orchestrator-logging.ts
|
|
54061
|
+
function formatPhaseResultMessage(opName, success2, stage, status) {
|
|
54062
|
+
if (opName === "greenfield-gate") {
|
|
54063
|
+
return success2 ? "Greenfield-gate: pre-existing tests detected (not greenfield) \u2014 proceeding with normal TDD" : "Greenfield-gate: no pre-existing tests \u2014 greenfield run, pausing TDD test-writer";
|
|
54064
|
+
}
|
|
54065
|
+
if (status === "skipped") {
|
|
54066
|
+
return `Phase skipped: ${opName}`;
|
|
54067
|
+
}
|
|
54068
|
+
if (stage === "rectification") {
|
|
54069
|
+
return `Rectification strategy completed: ${opName}`;
|
|
54070
|
+
}
|
|
54071
|
+
return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
|
|
54072
|
+
}
|
|
54073
|
+
function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
|
|
54074
|
+
if (output === null || output === undefined || typeof output !== "object")
|
|
54075
|
+
return null;
|
|
54076
|
+
const r = output;
|
|
54077
|
+
const success2 = r.success === true || r.passed === true;
|
|
54078
|
+
const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
|
|
54079
|
+
const status = typeof r.status === "string" ? r.status : undefined;
|
|
54080
|
+
const data = { storyId, phase: opName, durationMs };
|
|
54081
|
+
if (findingsCount !== undefined)
|
|
54082
|
+
data.findingsCount = findingsCount;
|
|
54083
|
+
if (status !== undefined)
|
|
54084
|
+
data.status = status;
|
|
54085
|
+
if (typeof r.failureCategory === "string")
|
|
54086
|
+
data.failureCategory = r.failureCategory;
|
|
54087
|
+
if (typeof r.reviewReason === "string")
|
|
54088
|
+
data.reviewReason = r.reviewReason;
|
|
54089
|
+
return { success: success2, data };
|
|
54090
|
+
}
|
|
54091
|
+
function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
|
|
54092
|
+
if (isTddPhase)
|
|
54093
|
+
return;
|
|
54094
|
+
if (opName === "semantic-review" || opName === "adversarial-review")
|
|
54095
|
+
return;
|
|
54096
|
+
const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
|
|
54097
|
+
if (!built)
|
|
54098
|
+
return;
|
|
54099
|
+
const { success: success2 } = built;
|
|
54100
|
+
const data = { ...built.data, ...progressData };
|
|
54101
|
+
const status = typeof built.data.status === "string" ? built.data.status : undefined;
|
|
54102
|
+
const logger = getSafeLogger();
|
|
54103
|
+
const message = formatPhaseResultMessage(opName, success2, stage, status);
|
|
54104
|
+
if (stage === "rectification") {
|
|
54105
|
+
logger?.info("story-orchestrator", message, data);
|
|
54106
|
+
return;
|
|
54107
|
+
}
|
|
54108
|
+
if (success2) {
|
|
54109
|
+
logger?.info("story-orchestrator", message, data);
|
|
54110
|
+
} else {
|
|
54111
|
+
logger?.warn("story-orchestrator", message, data);
|
|
54112
|
+
}
|
|
54113
|
+
}
|
|
54114
|
+
var init_story_orchestrator_logging = __esm(() => {
|
|
54115
|
+
init_logger2();
|
|
54116
|
+
});
|
|
54117
|
+
|
|
53535
54118
|
// src/execution/story-orchestrator.ts
|
|
53536
54119
|
async function refreshReviewInputForDispatch(opName, input) {
|
|
53537
54120
|
if (opName !== "semantic-review" && opName !== "adversarial-review")
|
|
@@ -53592,15 +54175,6 @@ async function refreshReviewInputForDispatch(opName, input) {
|
|
|
53592
54175
|
return fallback;
|
|
53593
54176
|
}
|
|
53594
54177
|
}
|
|
53595
|
-
function formatPhaseResultMessage(opName, success2, stage) {
|
|
53596
|
-
if (opName === "greenfield-gate") {
|
|
53597
|
-
return success2 ? "Greenfield-gate: pre-existing tests detected (not greenfield) \u2014 proceeding with normal TDD" : "Greenfield-gate: no pre-existing tests \u2014 greenfield run, pausing TDD test-writer";
|
|
53598
|
-
}
|
|
53599
|
-
if (stage === "rectification") {
|
|
53600
|
-
return `Rectification strategy completed: ${opName}`;
|
|
53601
|
-
}
|
|
53602
|
-
return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
|
|
53603
|
-
}
|
|
53604
54178
|
function isSlot(value) {
|
|
53605
54179
|
return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
|
|
53606
54180
|
}
|
|
@@ -53796,46 +54370,6 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
|
|
|
53796
54370
|
logger?.info("review", "Running adversarial check", { storyId });
|
|
53797
54371
|
}
|
|
53798
54372
|
}
|
|
53799
|
-
function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
|
|
53800
|
-
if (output === null || output === undefined || typeof output !== "object")
|
|
53801
|
-
return null;
|
|
53802
|
-
const r = output;
|
|
53803
|
-
const success2 = r.success === true || r.passed === true;
|
|
53804
|
-
const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
|
|
53805
|
-
const status = typeof r.status === "string" ? r.status : undefined;
|
|
53806
|
-
const data = { storyId, phase: opName, durationMs };
|
|
53807
|
-
if (findingsCount !== undefined)
|
|
53808
|
-
data.findingsCount = findingsCount;
|
|
53809
|
-
if (status !== undefined)
|
|
53810
|
-
data.status = status;
|
|
53811
|
-
if (typeof r.failureCategory === "string")
|
|
53812
|
-
data.failureCategory = r.failureCategory;
|
|
53813
|
-
if (typeof r.reviewReason === "string")
|
|
53814
|
-
data.reviewReason = r.reviewReason;
|
|
53815
|
-
return { success: success2, data };
|
|
53816
|
-
}
|
|
53817
|
-
function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
|
|
53818
|
-
if (isTddPhase)
|
|
53819
|
-
return;
|
|
53820
|
-
if (opName === "semantic-review" || opName === "adversarial-review")
|
|
53821
|
-
return;
|
|
53822
|
-
const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
|
|
53823
|
-
if (!built)
|
|
53824
|
-
return;
|
|
53825
|
-
const { success: success2 } = built;
|
|
53826
|
-
const data = { ...built.data, ...progressData };
|
|
53827
|
-
const logger = getSafeLogger();
|
|
53828
|
-
const message = formatPhaseResultMessage(opName, success2, stage);
|
|
53829
|
-
if (stage === "rectification") {
|
|
53830
|
-
logger?.info("story-orchestrator", message, data);
|
|
53831
|
-
return;
|
|
53832
|
-
}
|
|
53833
|
-
if (success2) {
|
|
53834
|
-
logger?.info("story-orchestrator", message, data);
|
|
53835
|
-
} else {
|
|
53836
|
-
logger?.warn("story-orchestrator", message, data);
|
|
53837
|
-
}
|
|
53838
|
-
}
|
|
53839
54373
|
function logUnifiedReviewPhaseResult(storyId, opName, output) {
|
|
53840
54374
|
const logger = getSafeLogger();
|
|
53841
54375
|
const payload = toReviewDecisionPayload(opName, output);
|
|
@@ -53971,16 +54505,17 @@ function withIncreasingFailuresBail(strategies, enabled, consecutiveIncreases) {
|
|
|
53971
54505
|
}
|
|
53972
54506
|
}));
|
|
53973
54507
|
}
|
|
53974
|
-
async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
54508
|
+
async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides) {
|
|
53975
54509
|
const rectification = state.rectification;
|
|
53976
|
-
const
|
|
54510
|
+
const baseValidationPhases = collectRectificationPhases(state);
|
|
54511
|
+
const validationPhases = overrides?.excludePhaseKinds ? baseValidationPhases.filter((p) => !overrides.excludePhaseKinds?.includes(p.kind)) : baseValidationPhases;
|
|
53977
54512
|
if (!rectification || validationPhases.length === 0) {
|
|
53978
54513
|
return {};
|
|
53979
54514
|
}
|
|
53980
54515
|
if (ctx.runtime.signal?.aborted) {
|
|
53981
54516
|
return {};
|
|
53982
54517
|
}
|
|
53983
|
-
const initialFindings = gatherRectificationFindings(phaseOutputs, validationPhases, state);
|
|
54518
|
+
const initialFindings = overrides?.initialFindings ? [...overrides.initialFindings] : gatherRectificationFindings(phaseOutputs, validationPhases, state);
|
|
53984
54519
|
if (initialFindings.length === 0) {
|
|
53985
54520
|
return {};
|
|
53986
54521
|
}
|
|
@@ -53995,14 +54530,16 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
|
53995
54530
|
const cycle = {
|
|
53996
54531
|
findings: [...initialFindings],
|
|
53997
54532
|
iterations: [],
|
|
53998
|
-
strategies: withIncreasingFailuresBail(rectification.strategies, rectification.abortOnIncreasingFailures, rectification.consecutiveIncreasesToBail ?? 1),
|
|
53999
|
-
config: { maxAttemptsTotal: rectification.maxAttempts, validatorRetries: 1 },
|
|
54533
|
+
strategies: withIncreasingFailuresBail(overrides?.strategies ?? rectification.strategies, rectification.abortOnIncreasingFailures, rectification.consecutiveIncreasesToBail ?? 1),
|
|
54534
|
+
config: { maxAttemptsTotal: overrides?.maxAttempts ?? rectification.maxAttempts, validatorRetries: 1 },
|
|
54000
54535
|
validate: async (_validateCtx, opts) => {
|
|
54001
54536
|
if (ctx.runtime.signal?.aborted)
|
|
54002
54537
|
return { findings: [], shortCircuited: false };
|
|
54003
54538
|
const lite = (opts?.mode ?? "full") === "lite";
|
|
54004
54539
|
const selected = phasesToRevalidate(opts?.strategiesRun, validationPhases);
|
|
54005
|
-
const
|
|
54540
|
+
const extra = overrides?.extraRevalidationKinds ? validationPhases.filter((p) => overrides.extraRevalidationKinds?.includes(p.kind) && !selected.some((s) => s.kind === p.kind)) : [];
|
|
54541
|
+
const selectedWithExtra = [...selected, ...extra];
|
|
54542
|
+
const phases = lite ? orderGateLast(selectedWithExtra) : selectedWithExtra;
|
|
54006
54543
|
getSafeLogger()?.debug("story-orchestrator", "rectification validate scope", {
|
|
54007
54544
|
storyId: ctx.storyId,
|
|
54008
54545
|
mode: opts?.mode ?? "full",
|
|
@@ -54154,6 +54691,25 @@ class ExecutionPlan {
|
|
|
54154
54691
|
}
|
|
54155
54692
|
}
|
|
54156
54693
|
}
|
|
54694
|
+
const advCfg = this.state.adversarialReview ? this.state.nonBlockingFix : undefined;
|
|
54695
|
+
const advisoryOut = phaseOutputs["adversarial-review"];
|
|
54696
|
+
const advisoryFindings = advisoryOut?.advisoryFindings ?? [];
|
|
54697
|
+
if (advCfg && this.state.rectification && this.ctx.storyId && shouldRunNonBlockingFix(advCfg, advisoryFindings.length)) {
|
|
54698
|
+
await runNonBlockingFix({
|
|
54699
|
+
workdir: this.ctx.packageDir,
|
|
54700
|
+
storyId: this.ctx.storyId,
|
|
54701
|
+
advisoryFindings,
|
|
54702
|
+
cfg: advCfg,
|
|
54703
|
+
phaseOutputs,
|
|
54704
|
+
runRectify: (maxAttempts) => runRectification(this.ctx, this.state, phaseCosts, phaseOutputs, {
|
|
54705
|
+
initialFindings: advisoryFindings,
|
|
54706
|
+
strategies: this.state.nonBlockingFixStrategies ?? [],
|
|
54707
|
+
excludePhaseKinds: nonBlockingExcludePhases(),
|
|
54708
|
+
extraRevalidationKinds: nonBlockingExtraPhases(advCfg),
|
|
54709
|
+
maxAttempts
|
|
54710
|
+
})
|
|
54711
|
+
});
|
|
54712
|
+
}
|
|
54157
54713
|
const verifierName = this.state.verifier?.slot.op.name;
|
|
54158
54714
|
const gateName = this.state.fullSuiteGate?.slot.op.name;
|
|
54159
54715
|
const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
|
|
@@ -54246,6 +54802,11 @@ class StoryOrchestratorBuilder {
|
|
|
54246
54802
|
this.state.rectification = opts;
|
|
54247
54803
|
return this;
|
|
54248
54804
|
}
|
|
54805
|
+
addNonBlockingFix(cfg, strategies) {
|
|
54806
|
+
this.state.nonBlockingFix = cfg;
|
|
54807
|
+
this.state.nonBlockingFixStrategies = strategies;
|
|
54808
|
+
return this;
|
|
54809
|
+
}
|
|
54249
54810
|
build(ctx, opts = {}) {
|
|
54250
54811
|
if (!this.state.implementer) {
|
|
54251
54812
|
throw new NaxError("StoryOrchestratorBuilder.build(): addImplementer() must be called before build()", "ORCHESTRATOR_NO_IMPLEMENTER", { stage: "execution" });
|
|
@@ -54263,6 +54824,8 @@ var init_story_orchestrator = __esm(() => {
|
|
|
54263
54824
|
init_event_bus();
|
|
54264
54825
|
init_prepare_inputs();
|
|
54265
54826
|
init_git();
|
|
54827
|
+
init_non_blocking_fix();
|
|
54828
|
+
init_story_orchestrator_logging();
|
|
54266
54829
|
_storyOrchestratorDeps = {
|
|
54267
54830
|
callOp,
|
|
54268
54831
|
runFixCycle,
|
|
@@ -54327,7 +54890,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
54327
54890
|
});
|
|
54328
54891
|
|
|
54329
54892
|
// src/execution/build-plan-for-strategy.ts
|
|
54330
|
-
import { join as
|
|
54893
|
+
import { join as join48 } from "path";
|
|
54331
54894
|
function requiresInitialRefCapture(strategy) {
|
|
54332
54895
|
return isThreeSessionStrategy(strategy);
|
|
54333
54896
|
}
|
|
@@ -54373,13 +54936,14 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
54373
54936
|
}
|
|
54374
54937
|
if (shouldRunRectification(config2) && inputs.rectification) {
|
|
54375
54938
|
const sink = makeDeclarationSink();
|
|
54376
|
-
const packageDir =
|
|
54939
|
+
const packageDir = join48(ctx.packageDir, story.workdir ?? "");
|
|
54377
54940
|
const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
|
|
54378
54941
|
const strategies = [];
|
|
54379
|
-
|
|
54942
|
+
const pkgQuality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
54943
|
+
if (pkgQuality?.commands?.lintFix || pkgQuality?.commands?.lintFixScoped) {
|
|
54380
54944
|
strategies.push(makeMechanicalLintFixStrategy());
|
|
54381
54945
|
}
|
|
54382
|
-
if (
|
|
54946
|
+
if (pkgQuality?.commands?.formatFix || pkgQuality?.commands?.formatFixScoped) {
|
|
54383
54947
|
strategies.push(makeMechanicalFormatFixStrategy());
|
|
54384
54948
|
}
|
|
54385
54949
|
if (inputs.fullSuiteGate && (isThreeSession || regressionMode === "per-story")) {
|
|
@@ -54415,6 +54979,22 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
54415
54979
|
};
|
|
54416
54980
|
builder.addRectification(rectOpts);
|
|
54417
54981
|
}
|
|
54982
|
+
const nbf = config2.review?.adversarial?.nonBlockingFix;
|
|
54983
|
+
const nbStrategies = [];
|
|
54984
|
+
if (nbf?.enabled && inputs.adversarialReview) {
|
|
54985
|
+
const nbSink = makeDeclarationSink();
|
|
54986
|
+
if (nbf.scope === "source") {
|
|
54987
|
+
nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
|
|
54988
|
+
includeAdversarialReview: true
|
|
54989
|
+
}));
|
|
54990
|
+
} else {
|
|
54991
|
+
nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
|
|
54992
|
+
includeAdversarialReview: false
|
|
54993
|
+
}), makeAutofixTestWriterStrategy(story, config2, nbSink));
|
|
54994
|
+
}
|
|
54995
|
+
nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2));
|
|
54996
|
+
builder.addNonBlockingFix(nbf, nbStrategies);
|
|
54997
|
+
}
|
|
54418
54998
|
return builder.build(ctx, { isThreeSession });
|
|
54419
54999
|
}
|
|
54420
55000
|
var init_build_plan_for_strategy = __esm(() => {
|
|
@@ -54678,41 +55258,6 @@ var init_execution_helpers = __esm(() => {
|
|
|
54678
55258
|
init_errors();
|
|
54679
55259
|
});
|
|
54680
55260
|
|
|
54681
|
-
// src/tdd/rollback.ts
|
|
54682
|
-
async function rollbackToRef(workdir, ref) {
|
|
54683
|
-
const logger = getLogger();
|
|
54684
|
-
logger.warn("tdd", "Rolling back git changes", { ref });
|
|
54685
|
-
const resetProc = _rollbackDeps.spawn(["git", "reset", "--hard", ref], {
|
|
54686
|
-
cwd: workdir,
|
|
54687
|
-
stdout: "pipe",
|
|
54688
|
-
stderr: "pipe"
|
|
54689
|
-
});
|
|
54690
|
-
const exitCode = await resetProc.exited;
|
|
54691
|
-
if (exitCode !== 0) {
|
|
54692
|
-
const stderr = await new Response(resetProc.stderr).text();
|
|
54693
|
-
logger.error("tdd", "Failed to rollback git changes", { ref, stderr });
|
|
54694
|
-
throw new Error(`Git rollback failed: ${stderr}`);
|
|
54695
|
-
}
|
|
54696
|
-
const cleanProc = _rollbackDeps.spawn(["git", "clean", "-fd"], {
|
|
54697
|
-
cwd: workdir,
|
|
54698
|
-
stdout: "pipe",
|
|
54699
|
-
stderr: "pipe"
|
|
54700
|
-
});
|
|
54701
|
-
const cleanExitCode = await cleanProc.exited;
|
|
54702
|
-
if (cleanExitCode !== 0) {
|
|
54703
|
-
const stderr = await new Response(cleanProc.stderr).text();
|
|
54704
|
-
logger.warn("tdd", "Failed to clean untracked files", { stderr });
|
|
54705
|
-
}
|
|
54706
|
-
logger.info("tdd", "Successfully rolled back git changes", { ref });
|
|
54707
|
-
}
|
|
54708
|
-
var _rollbackDeps;
|
|
54709
|
-
var init_rollback = __esm(() => {
|
|
54710
|
-
init_logger2();
|
|
54711
|
-
_rollbackDeps = {
|
|
54712
|
-
spawn: Bun.spawn
|
|
54713
|
-
};
|
|
54714
|
-
});
|
|
54715
|
-
|
|
54716
55261
|
// src/execution/session-manager-runtime.ts
|
|
54717
55262
|
async function closePhysicalSession(descriptor, agentGetFn, force) {
|
|
54718
55263
|
if (!descriptor.handle)
|
|
@@ -55925,7 +56470,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
55925
56470
|
}
|
|
55926
56471
|
|
|
55927
56472
|
// src/cli/prompts-tdd.ts
|
|
55928
|
-
import { join as
|
|
56473
|
+
import { join as join49 } from "path";
|
|
55929
56474
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
55930
56475
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
55931
56476
|
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(),
|
|
@@ -55944,7 +56489,7 @@ ${frontmatter}---
|
|
|
55944
56489
|
|
|
55945
56490
|
${session.prompt}`;
|
|
55946
56491
|
if (outputDir) {
|
|
55947
|
-
const promptFile =
|
|
56492
|
+
const promptFile = join49(outputDir, `${story.id}.${session.role}.md`);
|
|
55948
56493
|
await Bun.write(promptFile, fullOutput);
|
|
55949
56494
|
logger.info("cli", "Written TDD prompt file", {
|
|
55950
56495
|
storyId: story.id,
|
|
@@ -55960,7 +56505,7 @@ ${"=".repeat(80)}`);
|
|
|
55960
56505
|
}
|
|
55961
56506
|
}
|
|
55962
56507
|
if (outputDir && ctx.contextMarkdown) {
|
|
55963
|
-
const contextFile =
|
|
56508
|
+
const contextFile = join49(outputDir, `${story.id}.context.md`);
|
|
55964
56509
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
55965
56510
|
const contextOutput = `---
|
|
55966
56511
|
${frontmatter}---
|
|
@@ -55975,16 +56520,16 @@ var init_prompts_tdd = __esm(() => {
|
|
|
55975
56520
|
|
|
55976
56521
|
// src/cli/prompts-main.ts
|
|
55977
56522
|
import { existsSync as existsSync20, mkdirSync as mkdirSync3 } from "fs";
|
|
55978
|
-
import { join as
|
|
56523
|
+
import { join as join50 } from "path";
|
|
55979
56524
|
async function promptsCommand(options) {
|
|
55980
56525
|
const logger = getLogger();
|
|
55981
56526
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
55982
|
-
const naxDir =
|
|
56527
|
+
const naxDir = join50(workdir, ".nax");
|
|
55983
56528
|
if (!existsSync20(naxDir)) {
|
|
55984
56529
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
55985
56530
|
}
|
|
55986
|
-
const featureDir =
|
|
55987
|
-
const prdPath =
|
|
56531
|
+
const featureDir = join50(naxDir, "features", feature);
|
|
56532
|
+
const prdPath = join50(featureDir, "prd.json");
|
|
55988
56533
|
if (!existsSync20(prdPath)) {
|
|
55989
56534
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
55990
56535
|
}
|
|
@@ -56051,10 +56596,10 @@ ${frontmatter}---
|
|
|
56051
56596
|
|
|
56052
56597
|
${ctx.prompt}`;
|
|
56053
56598
|
if (outputDir) {
|
|
56054
|
-
const promptFile =
|
|
56599
|
+
const promptFile = join50(outputDir, `${story.id}.prompt.md`);
|
|
56055
56600
|
await Bun.write(promptFile, fullOutput);
|
|
56056
56601
|
if (ctx.contextMarkdown) {
|
|
56057
|
-
const contextFile =
|
|
56602
|
+
const contextFile = join50(outputDir, `${story.id}.context.md`);
|
|
56058
56603
|
const contextOutput = `---
|
|
56059
56604
|
${frontmatter}---
|
|
56060
56605
|
|
|
@@ -56090,12 +56635,12 @@ var init_prompts_main = __esm(() => {
|
|
|
56090
56635
|
|
|
56091
56636
|
// src/cli/prompts-init.ts
|
|
56092
56637
|
import { existsSync as existsSync21, mkdirSync as mkdirSync4 } from "fs";
|
|
56093
|
-
import { join as
|
|
56638
|
+
import { join as join51 } from "path";
|
|
56094
56639
|
async function promptsInitCommand(options) {
|
|
56095
56640
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
56096
|
-
const templatesDir =
|
|
56641
|
+
const templatesDir = join51(workdir, ".nax", "templates");
|
|
56097
56642
|
mkdirSync4(templatesDir, { recursive: true });
|
|
56098
|
-
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(
|
|
56643
|
+
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join51(templatesDir, f)));
|
|
56099
56644
|
if (existingFiles.length > 0 && !force) {
|
|
56100
56645
|
_promptsInitDeps.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
56101
56646
|
Pass --force to overwrite existing templates.`);
|
|
@@ -56103,7 +56648,7 @@ async function promptsInitCommand(options) {
|
|
|
56103
56648
|
}
|
|
56104
56649
|
const written = [];
|
|
56105
56650
|
for (const template of TEMPLATE_ROLES) {
|
|
56106
|
-
const filePath =
|
|
56651
|
+
const filePath = join51(templatesDir, template.file);
|
|
56107
56652
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
56108
56653
|
const content = TEMPLATE_HEADER + roleBody;
|
|
56109
56654
|
await Bun.write(filePath, content);
|
|
@@ -56119,7 +56664,7 @@ async function promptsInitCommand(options) {
|
|
|
56119
56664
|
return written;
|
|
56120
56665
|
}
|
|
56121
56666
|
async function autoWirePromptsConfig(workdir) {
|
|
56122
|
-
const configPath =
|
|
56667
|
+
const configPath = join51(workdir, "nax.config.json");
|
|
56123
56668
|
if (!existsSync21(configPath)) {
|
|
56124
56669
|
const exampleConfig = JSON.stringify({
|
|
56125
56670
|
prompts: {
|
|
@@ -56287,9 +56832,14 @@ __export(exports_init_context, {
|
|
|
56287
56832
|
generateContextTemplate: () => generateContextTemplate,
|
|
56288
56833
|
_initContextDeps: () => _initContextDeps
|
|
56289
56834
|
});
|
|
56290
|
-
import {
|
|
56291
|
-
|
|
56292
|
-
|
|
56835
|
+
import { basename as basename9, join as join52 } from "path";
|
|
56836
|
+
async function bunFileExists(path13) {
|
|
56837
|
+
return Bun.file(path13).exists();
|
|
56838
|
+
}
|
|
56839
|
+
async function bunMkdirp(path13) {
|
|
56840
|
+
const proc = Bun.spawn(["mkdir", "-p", path13]);
|
|
56841
|
+
await proc.exited;
|
|
56842
|
+
}
|
|
56293
56843
|
async function findFiles(dir, maxFiles = 200) {
|
|
56294
56844
|
try {
|
|
56295
56845
|
const proc = Bun.spawnSync([
|
|
@@ -56317,8 +56867,8 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
56317
56867
|
return [];
|
|
56318
56868
|
}
|
|
56319
56869
|
async function readPackageManifest(projectRoot) {
|
|
56320
|
-
const packageJsonPath =
|
|
56321
|
-
if (!
|
|
56870
|
+
const packageJsonPath = join52(projectRoot, "package.json");
|
|
56871
|
+
if (!await bunFileExists(packageJsonPath)) {
|
|
56322
56872
|
return null;
|
|
56323
56873
|
}
|
|
56324
56874
|
try {
|
|
@@ -56335,8 +56885,8 @@ async function readPackageManifest(projectRoot) {
|
|
|
56335
56885
|
}
|
|
56336
56886
|
}
|
|
56337
56887
|
async function readReadmeSnippet(projectRoot) {
|
|
56338
|
-
const readmePath =
|
|
56339
|
-
if (!
|
|
56888
|
+
const readmePath = join52(projectRoot, "README.md");
|
|
56889
|
+
if (!await bunFileExists(readmePath)) {
|
|
56340
56890
|
return null;
|
|
56341
56891
|
}
|
|
56342
56892
|
try {
|
|
@@ -56353,8 +56903,8 @@ async function detectEntryPoints(projectRoot) {
|
|
|
56353
56903
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
56354
56904
|
const found = [];
|
|
56355
56905
|
for (const candidate of candidates) {
|
|
56356
|
-
const path13 =
|
|
56357
|
-
if (
|
|
56906
|
+
const path13 = join52(projectRoot, candidate);
|
|
56907
|
+
if (await bunFileExists(path13)) {
|
|
56358
56908
|
found.push(candidate);
|
|
56359
56909
|
}
|
|
56360
56910
|
}
|
|
@@ -56364,8 +56914,8 @@ async function detectConfigFiles(projectRoot) {
|
|
|
56364
56914
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
56365
56915
|
const found = [];
|
|
56366
56916
|
for (const candidate of candidates) {
|
|
56367
|
-
const path13 =
|
|
56368
|
-
if (
|
|
56917
|
+
const path13 = join52(projectRoot, candidate);
|
|
56918
|
+
if (await bunFileExists(path13)) {
|
|
56369
56919
|
found.push(candidate);
|
|
56370
56920
|
}
|
|
56371
56921
|
}
|
|
@@ -56495,10 +57045,10 @@ Keep it under 2000 tokens. Use markdown formatting. Be specific to the detected
|
|
|
56495
57045
|
`;
|
|
56496
57046
|
try {
|
|
56497
57047
|
const result = await _initContextDeps.callLLM(prompt);
|
|
56498
|
-
logger.info("init", "Generated context.md with LLM");
|
|
57048
|
+
logger.info("init", "Generated context.md with LLM", { storyId: "init-context" });
|
|
56499
57049
|
return result;
|
|
56500
57050
|
} catch (err) {
|
|
56501
|
-
logger.warn("init", `LLM context generation failed, falling back to template: ${err instanceof Error ? err.message : String(err)}
|
|
57051
|
+
logger.warn("init", `LLM context generation failed, falling back to template: ${err instanceof Error ? err.message : String(err)}`, { storyId: "init-context" });
|
|
56502
57052
|
return generateContextTemplate(scan);
|
|
56503
57053
|
}
|
|
56504
57054
|
}
|
|
@@ -56525,29 +57075,35 @@ function generatePackageContextTemplate(packagePath) {
|
|
|
56525
57075
|
}
|
|
56526
57076
|
async function initPackage(repoRoot, packagePath, force = false) {
|
|
56527
57077
|
const logger = getLogger();
|
|
56528
|
-
const naxDir =
|
|
56529
|
-
const contextPath =
|
|
56530
|
-
if (
|
|
56531
|
-
logger.info("init", "Package context.md already exists (use --force to overwrite)", {
|
|
57078
|
+
const naxDir = join52(repoRoot, ".nax", "mono", packagePath);
|
|
57079
|
+
const contextPath = join52(naxDir, "context.md");
|
|
57080
|
+
if (await bunFileExists(contextPath) && !force) {
|
|
57081
|
+
logger.info("init", "Package context.md already exists (use --force to overwrite)", {
|
|
57082
|
+
storyId: "init-context",
|
|
57083
|
+
path: contextPath
|
|
57084
|
+
});
|
|
56532
57085
|
return;
|
|
56533
57086
|
}
|
|
56534
|
-
if (!
|
|
56535
|
-
await
|
|
57087
|
+
if (!await bunFileExists(naxDir)) {
|
|
57088
|
+
await bunMkdirp(naxDir);
|
|
56536
57089
|
}
|
|
56537
57090
|
const content = generatePackageContextTemplate(packagePath);
|
|
56538
57091
|
await Bun.write(contextPath, content);
|
|
56539
|
-
logger.info("init", "Created package context.md", { path: contextPath });
|
|
57092
|
+
logger.info("init", "Created package context.md", { storyId: "init-context", path: contextPath });
|
|
56540
57093
|
}
|
|
56541
57094
|
async function initContext(projectRoot, options = {}) {
|
|
56542
57095
|
const logger = getLogger();
|
|
56543
|
-
const naxDir =
|
|
56544
|
-
const contextPath =
|
|
56545
|
-
if (
|
|
56546
|
-
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", {
|
|
57096
|
+
const naxDir = join52(projectRoot, ".nax");
|
|
57097
|
+
const contextPath = join52(naxDir, "context.md");
|
|
57098
|
+
if (await bunFileExists(contextPath) && !options.force) {
|
|
57099
|
+
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", {
|
|
57100
|
+
storyId: "init-context",
|
|
57101
|
+
path: contextPath
|
|
57102
|
+
});
|
|
56547
57103
|
return;
|
|
56548
57104
|
}
|
|
56549
|
-
if (!
|
|
56550
|
-
await
|
|
57105
|
+
if (!await bunFileExists(naxDir)) {
|
|
57106
|
+
await bunMkdirp(naxDir);
|
|
56551
57107
|
}
|
|
56552
57108
|
const scan = await scanProject(projectRoot);
|
|
56553
57109
|
let content;
|
|
@@ -56557,7 +57113,10 @@ async function initContext(projectRoot, options = {}) {
|
|
|
56557
57113
|
content = generateContextTemplate(scan);
|
|
56558
57114
|
}
|
|
56559
57115
|
await Bun.write(contextPath, content);
|
|
56560
|
-
logger.info("init", "Generated .nax/context.md template from project scan", {
|
|
57116
|
+
logger.info("init", "Generated .nax/context.md template from project scan", {
|
|
57117
|
+
storyId: "init-context",
|
|
57118
|
+
path: contextPath
|
|
57119
|
+
});
|
|
56561
57120
|
}
|
|
56562
57121
|
var _initContextDeps;
|
|
56563
57122
|
var init_init_context = __esm(() => {
|
|
@@ -56570,11 +57129,11 @@ var init_init_context = __esm(() => {
|
|
|
56570
57129
|
});
|
|
56571
57130
|
|
|
56572
57131
|
// src/cli/init-detect.ts
|
|
56573
|
-
import { existsSync as
|
|
56574
|
-
import { join as
|
|
57132
|
+
import { existsSync as existsSync22, readFileSync } from "fs";
|
|
57133
|
+
import { join as join53 } from "path";
|
|
56575
57134
|
function readPackageJson(projectRoot) {
|
|
56576
|
-
const pkgPath =
|
|
56577
|
-
if (!
|
|
57135
|
+
const pkgPath = join53(projectRoot, "package.json");
|
|
57136
|
+
if (!existsSync22(pkgPath))
|
|
56578
57137
|
return;
|
|
56579
57138
|
try {
|
|
56580
57139
|
return JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -56616,41 +57175,41 @@ function detectStack(projectRoot) {
|
|
|
56616
57175
|
};
|
|
56617
57176
|
}
|
|
56618
57177
|
function detectRuntime(projectRoot) {
|
|
56619
|
-
if (
|
|
57178
|
+
if (existsSync22(join53(projectRoot, "bun.lockb")) || existsSync22(join53(projectRoot, "bunfig.toml"))) {
|
|
56620
57179
|
return "bun";
|
|
56621
57180
|
}
|
|
56622
|
-
if (
|
|
57181
|
+
if (existsSync22(join53(projectRoot, "package-lock.json")) || existsSync22(join53(projectRoot, "yarn.lock")) || existsSync22(join53(projectRoot, "pnpm-lock.yaml"))) {
|
|
56623
57182
|
return "node";
|
|
56624
57183
|
}
|
|
56625
57184
|
return "unknown";
|
|
56626
57185
|
}
|
|
56627
57186
|
function detectLanguage2(projectRoot) {
|
|
56628
|
-
if (
|
|
57187
|
+
if (existsSync22(join53(projectRoot, "tsconfig.json")))
|
|
56629
57188
|
return "typescript";
|
|
56630
|
-
if (
|
|
57189
|
+
if (existsSync22(join53(projectRoot, "pyproject.toml")) || existsSync22(join53(projectRoot, "setup.py"))) {
|
|
56631
57190
|
return "python";
|
|
56632
57191
|
}
|
|
56633
|
-
if (
|
|
57192
|
+
if (existsSync22(join53(projectRoot, "Cargo.toml")))
|
|
56634
57193
|
return "rust";
|
|
56635
|
-
if (
|
|
57194
|
+
if (existsSync22(join53(projectRoot, "go.mod")))
|
|
56636
57195
|
return "go";
|
|
56637
57196
|
return "unknown";
|
|
56638
57197
|
}
|
|
56639
57198
|
function detectLinter(projectRoot) {
|
|
56640
|
-
if (
|
|
57199
|
+
if (existsSync22(join53(projectRoot, "biome.json")) || existsSync22(join53(projectRoot, "biome.jsonc"))) {
|
|
56641
57200
|
return "biome";
|
|
56642
57201
|
}
|
|
56643
|
-
if (
|
|
57202
|
+
if (existsSync22(join53(projectRoot, ".eslintrc.json")) || existsSync22(join53(projectRoot, ".eslintrc.js")) || existsSync22(join53(projectRoot, "eslint.config.js"))) {
|
|
56644
57203
|
return "eslint";
|
|
56645
57204
|
}
|
|
56646
57205
|
return "unknown";
|
|
56647
57206
|
}
|
|
56648
57207
|
function detectMonorepo(projectRoot) {
|
|
56649
|
-
if (
|
|
57208
|
+
if (existsSync22(join53(projectRoot, "turbo.json")))
|
|
56650
57209
|
return "turborepo";
|
|
56651
|
-
if (
|
|
57210
|
+
if (existsSync22(join53(projectRoot, "nx.json")))
|
|
56652
57211
|
return "nx";
|
|
56653
|
-
if (
|
|
57212
|
+
if (existsSync22(join53(projectRoot, "pnpm-workspace.yaml")))
|
|
56654
57213
|
return "pnpm-workspaces";
|
|
56655
57214
|
const pkg = readPackageJson(projectRoot);
|
|
56656
57215
|
if (pkg?.workspaces)
|
|
@@ -56792,9 +57351,9 @@ __export(exports_init, {
|
|
|
56792
57351
|
checkInitCollision: () => checkInitCollision,
|
|
56793
57352
|
_initDeps: () => _initDeps
|
|
56794
57353
|
});
|
|
56795
|
-
import { existsSync as
|
|
56796
|
-
import { mkdir as
|
|
56797
|
-
import { join as
|
|
57354
|
+
import { existsSync as existsSync23 } from "fs";
|
|
57355
|
+
import { mkdir as mkdir8 } from "fs/promises";
|
|
57356
|
+
import { join as join54 } from "path";
|
|
56798
57357
|
function validateProjectName(name) {
|
|
56799
57358
|
if (!name)
|
|
56800
57359
|
return { valid: false, error: "name must be non-empty" };
|
|
@@ -56830,9 +57389,9 @@ async function checkInitCollision(name, currentWorkdir, currentRemote) {
|
|
|
56830
57389
|
}
|
|
56831
57390
|
async function updateGitignore(projectRoot) {
|
|
56832
57391
|
const logger = getLogger();
|
|
56833
|
-
const gitignorePath =
|
|
57392
|
+
const gitignorePath = join54(projectRoot, ".gitignore");
|
|
56834
57393
|
let existing = "";
|
|
56835
|
-
if (
|
|
57394
|
+
if (existsSync23(gitignorePath)) {
|
|
56836
57395
|
existing = await Bun.file(gitignorePath).text();
|
|
56837
57396
|
}
|
|
56838
57397
|
const missingEntries = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
|
|
@@ -56912,28 +57471,28 @@ function buildConstitution(stack) {
|
|
|
56912
57471
|
async function initGlobal() {
|
|
56913
57472
|
const logger = getLogger();
|
|
56914
57473
|
const globalDir = globalConfigDir();
|
|
56915
|
-
if (!
|
|
56916
|
-
await
|
|
57474
|
+
if (!existsSync23(globalDir)) {
|
|
57475
|
+
await mkdir8(globalDir, { recursive: true });
|
|
56917
57476
|
logger.info("init", "Created global config directory", { path: globalDir });
|
|
56918
57477
|
}
|
|
56919
|
-
const configPath =
|
|
56920
|
-
if (!
|
|
57478
|
+
const configPath = join54(globalDir, "config.json");
|
|
57479
|
+
if (!existsSync23(configPath)) {
|
|
56921
57480
|
await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
|
|
56922
57481
|
`);
|
|
56923
57482
|
logger.info("init", "Created global config", { path: configPath });
|
|
56924
57483
|
} else {
|
|
56925
57484
|
logger.info("init", "Global config already exists", { path: configPath });
|
|
56926
57485
|
}
|
|
56927
|
-
const constitutionPath =
|
|
56928
|
-
if (!
|
|
57486
|
+
const constitutionPath = join54(globalDir, "constitution.md");
|
|
57487
|
+
if (!existsSync23(constitutionPath)) {
|
|
56929
57488
|
await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
|
|
56930
57489
|
logger.info("init", "Created global constitution", { path: constitutionPath });
|
|
56931
57490
|
} else {
|
|
56932
57491
|
logger.info("init", "Global constitution already exists", { path: constitutionPath });
|
|
56933
57492
|
}
|
|
56934
|
-
const hooksDir =
|
|
56935
|
-
if (!
|
|
56936
|
-
await
|
|
57493
|
+
const hooksDir = join54(globalDir, "hooks");
|
|
57494
|
+
if (!existsSync23(hooksDir)) {
|
|
57495
|
+
await mkdir8(hooksDir, { recursive: true });
|
|
56937
57496
|
logger.info("init", "Created global hooks directory", { path: hooksDir });
|
|
56938
57497
|
} else {
|
|
56939
57498
|
logger.info("init", "Global hooks directory already exists", { path: hooksDir });
|
|
@@ -56964,7 +57523,7 @@ async function initProject(projectRoot, options) {
|
|
|
56964
57523
|
if (detectedName && !options?.force) {
|
|
56965
57524
|
const collision = await checkInitCollision(detectedName, projectRoot, currentRemote);
|
|
56966
57525
|
if (collision.collision && collision.existing) {
|
|
56967
|
-
const configPath2 =
|
|
57526
|
+
const configPath2 = join54(projectDir, "config.json");
|
|
56968
57527
|
throw new NaxError([
|
|
56969
57528
|
`Project name collision: "${detectedName}"`,
|
|
56970
57529
|
` This project: ${projectRoot}`,
|
|
@@ -56977,8 +57536,8 @@ async function initProject(projectRoot, options) {
|
|
|
56977
57536
|
`), "INIT_NAME_COLLISION", { stage: "init", name: detectedName });
|
|
56978
57537
|
}
|
|
56979
57538
|
}
|
|
56980
|
-
if (!
|
|
56981
|
-
await
|
|
57539
|
+
if (!existsSync23(projectDir)) {
|
|
57540
|
+
await mkdir8(projectDir, { recursive: true });
|
|
56982
57541
|
logger.info("init", "Created project config directory", { path: projectDir });
|
|
56983
57542
|
}
|
|
56984
57543
|
const stack = detectStack(projectRoot);
|
|
@@ -56992,8 +57551,8 @@ async function initProject(projectRoot, options) {
|
|
|
56992
57551
|
linter: stack.linter,
|
|
56993
57552
|
monorepo: stack.monorepo
|
|
56994
57553
|
});
|
|
56995
|
-
const configPath =
|
|
56996
|
-
if (!
|
|
57554
|
+
const configPath = join54(projectDir, "config.json");
|
|
57555
|
+
if (!existsSync23(configPath)) {
|
|
56997
57556
|
await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
|
|
56998
57557
|
`);
|
|
56999
57558
|
logger.info("init", "Created project config", { path: configPath });
|
|
@@ -57001,16 +57560,16 @@ async function initProject(projectRoot, options) {
|
|
|
57001
57560
|
logger.info("init", "Project config already exists", { path: configPath });
|
|
57002
57561
|
}
|
|
57003
57562
|
await initContext(projectRoot, { ai: options?.ai, force: options?.force });
|
|
57004
|
-
const constitutionPath =
|
|
57005
|
-
if (!
|
|
57563
|
+
const constitutionPath = join54(projectDir, "constitution.md");
|
|
57564
|
+
if (!existsSync23(constitutionPath) || options?.force) {
|
|
57006
57565
|
await Bun.write(constitutionPath, buildConstitution(stack));
|
|
57007
57566
|
logger.info("init", "Created project constitution", { path: constitutionPath });
|
|
57008
57567
|
} else {
|
|
57009
57568
|
logger.info("init", "Project constitution already exists", { path: constitutionPath });
|
|
57010
57569
|
}
|
|
57011
|
-
const hooksDir =
|
|
57012
|
-
if (!
|
|
57013
|
-
await
|
|
57570
|
+
const hooksDir = join54(projectDir, "hooks");
|
|
57571
|
+
if (!existsSync23(hooksDir)) {
|
|
57572
|
+
await mkdir8(hooksDir, { recursive: true });
|
|
57014
57573
|
logger.info("init", "Created project hooks directory", { path: hooksDir });
|
|
57015
57574
|
} else {
|
|
57016
57575
|
logger.info("init", "Project hooks directory already exists", { path: hooksDir });
|
|
@@ -57069,6 +57628,286 @@ var init_init2 = __esm(() => {
|
|
|
57069
57628
|
};
|
|
57070
57629
|
});
|
|
57071
57630
|
|
|
57631
|
+
// src/cli/setup-analyze.ts
|
|
57632
|
+
import { join as join55 } from "path";
|
|
57633
|
+
async function detectPackageManager(workdir) {
|
|
57634
|
+
const { fileExists } = _analyzeRepoDeps;
|
|
57635
|
+
if (await fileExists(join55(workdir, "bun.lock")))
|
|
57636
|
+
return { pmRunPrefix: "bun run", pmDlx: "bunx" };
|
|
57637
|
+
if (await fileExists(join55(workdir, "bun.lockb")))
|
|
57638
|
+
return { pmRunPrefix: "bun run", pmDlx: "bunx" };
|
|
57639
|
+
if (await fileExists(join55(workdir, "package-lock.json")))
|
|
57640
|
+
return { pmRunPrefix: "npm run", pmDlx: "npx" };
|
|
57641
|
+
if (await fileExists(join55(workdir, "yarn.lock")))
|
|
57642
|
+
return { pmRunPrefix: "yarn", pmDlx: "yarn dlx" };
|
|
57643
|
+
if (await fileExists(join55(workdir, "pnpm-lock.yaml")))
|
|
57644
|
+
return { pmRunPrefix: "pnpm run", pmDlx: "pnpx" };
|
|
57645
|
+
return { pmRunPrefix: "npm run", pmDlx: "npx" };
|
|
57646
|
+
}
|
|
57647
|
+
async function detectOrchestrator(workdir) {
|
|
57648
|
+
const { fileExists } = _analyzeRepoDeps;
|
|
57649
|
+
if (await fileExists(join55(workdir, "turbo.json")))
|
|
57650
|
+
return "turbo";
|
|
57651
|
+
if (await fileExists(join55(workdir, "nx.json")))
|
|
57652
|
+
return "nx";
|
|
57653
|
+
return "none";
|
|
57654
|
+
}
|
|
57655
|
+
async function getMissingScripts(packageDir) {
|
|
57656
|
+
const pkg = await _analyzeRepoDeps.readJson(join55(packageDir, "package.json"));
|
|
57657
|
+
const scripts = pkg?.scripts ?? {};
|
|
57658
|
+
return CANONICAL_SCRIPTS.filter((s) => !(s in scripts));
|
|
57659
|
+
}
|
|
57660
|
+
async function buildPackageFacts(workdir, relativeDir, testPatternMap) {
|
|
57661
|
+
const packageDir = relativeDir === "" ? workdir : join55(workdir, relativeDir);
|
|
57662
|
+
const [profile, missingScripts] = await Promise.all([
|
|
57663
|
+
_analyzeRepoDeps.detectProjectProfile(packageDir, {}),
|
|
57664
|
+
getMissingScripts(packageDir)
|
|
57665
|
+
]);
|
|
57666
|
+
const detection = testPatternMap[relativeDir] ?? {
|
|
57667
|
+
patterns: [],
|
|
57668
|
+
confidence: "empty",
|
|
57669
|
+
sources: []
|
|
57670
|
+
};
|
|
57671
|
+
return {
|
|
57672
|
+
relativeDir,
|
|
57673
|
+
testFramework: profile.testFramework,
|
|
57674
|
+
testFilePatterns: detection.patterns,
|
|
57675
|
+
missingScripts
|
|
57676
|
+
};
|
|
57677
|
+
}
|
|
57678
|
+
async function analyzeRepo(workdir) {
|
|
57679
|
+
const { discoverWorkspacePackages: discover, detectTestFilePatternsForWorkspace: detectPatterns } = _analyzeRepoDeps;
|
|
57680
|
+
const [pmInfo, orchestrator, packageDirs] = await Promise.all([
|
|
57681
|
+
detectPackageManager(workdir),
|
|
57682
|
+
detectOrchestrator(workdir),
|
|
57683
|
+
discover(workdir)
|
|
57684
|
+
]);
|
|
57685
|
+
const shape = packageDirs.length > 0 ? "mono" : "single";
|
|
57686
|
+
const dirsToAnalyze = shape === "single" ? [""] : packageDirs;
|
|
57687
|
+
const testPatternMap = await detectPatterns(workdir, packageDirs);
|
|
57688
|
+
const packages = await Promise.all(dirsToAnalyze.map((dir) => buildPackageFacts(workdir, dir, testPatternMap)));
|
|
57689
|
+
return {
|
|
57690
|
+
shape,
|
|
57691
|
+
packages,
|
|
57692
|
+
pmRunPrefix: pmInfo.pmRunPrefix,
|
|
57693
|
+
pmDlx: pmInfo.pmDlx,
|
|
57694
|
+
orchestrator
|
|
57695
|
+
};
|
|
57696
|
+
}
|
|
57697
|
+
var CANONICAL_SCRIPTS, _analyzeRepoDeps;
|
|
57698
|
+
var init_setup_analyze = __esm(() => {
|
|
57699
|
+
init_project();
|
|
57700
|
+
init_detect2();
|
|
57701
|
+
init_workspace();
|
|
57702
|
+
CANONICAL_SCRIPTS = ["build", "test", "lint", "type-check", "lint:fix"];
|
|
57703
|
+
_analyzeRepoDeps = {
|
|
57704
|
+
fileExists: async (path13) => Bun.file(path13).exists(),
|
|
57705
|
+
readJson: async (path13) => {
|
|
57706
|
+
try {
|
|
57707
|
+
const f = Bun.file(path13);
|
|
57708
|
+
if (!await f.exists())
|
|
57709
|
+
return null;
|
|
57710
|
+
return JSON.parse(await f.text());
|
|
57711
|
+
} catch {
|
|
57712
|
+
return null;
|
|
57713
|
+
}
|
|
57714
|
+
},
|
|
57715
|
+
discoverWorkspacePackages,
|
|
57716
|
+
detectProjectProfile,
|
|
57717
|
+
detectTestFilePatternsForWorkspace
|
|
57718
|
+
};
|
|
57719
|
+
});
|
|
57720
|
+
|
|
57721
|
+
// src/cli/setup-fill.ts
|
|
57722
|
+
import { join as join56 } from "path";
|
|
57723
|
+
async function addScriptToPackageJson(pkgJsonPath, key, value) {
|
|
57724
|
+
const pkg = await _fillScriptsDeps.readJson(pkgJsonPath) ?? {};
|
|
57725
|
+
const scripts = pkg.scripts ?? {};
|
|
57726
|
+
if (key in scripts)
|
|
57727
|
+
return;
|
|
57728
|
+
const updated = { ...pkg, scripts: { ...scripts, [key]: value } };
|
|
57729
|
+
await _fillScriptsDeps.writeFile(pkgJsonPath, JSON.stringify(updated, null, 2));
|
|
57730
|
+
}
|
|
57731
|
+
async function addTurboTask(turboJsonPath, taskKey) {
|
|
57732
|
+
const turbo = await _fillScriptsDeps.readJson(turboJsonPath) ?? {};
|
|
57733
|
+
const field = "pipeline" in turbo ? "pipeline" : "tasks";
|
|
57734
|
+
const existing = turbo[field] ?? {};
|
|
57735
|
+
if (taskKey in existing)
|
|
57736
|
+
return;
|
|
57737
|
+
const updated = { ...turbo, [field]: { ...existing, [taskKey]: {} } };
|
|
57738
|
+
await _fillScriptsDeps.writeFile(turboJsonPath, JSON.stringify(updated, null, 2));
|
|
57739
|
+
}
|
|
57740
|
+
async function fillScripts(workdir, analysis) {
|
|
57741
|
+
const { shape, packages, orchestrator } = analysis;
|
|
57742
|
+
if (shape === "single") {
|
|
57743
|
+
const rootPkg = packages[0];
|
|
57744
|
+
if (rootPkg?.missingScripts.includes(TYPE_CHECK_KEY)) {
|
|
57745
|
+
await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
|
|
57746
|
+
}
|
|
57747
|
+
return;
|
|
57748
|
+
}
|
|
57749
|
+
for (const pkg of packages) {
|
|
57750
|
+
if (!pkg.missingScripts.includes(TYPE_CHECK_KEY))
|
|
57751
|
+
continue;
|
|
57752
|
+
await addScriptToPackageJson(join56(workdir, pkg.relativeDir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
|
|
57753
|
+
}
|
|
57754
|
+
if (orchestrator === "turbo" && packages.some((p) => p.missingScripts.includes(TYPE_CHECK_KEY))) {
|
|
57755
|
+
await addTurboTask(join56(workdir, "turbo.json"), TYPE_CHECK_KEY);
|
|
57756
|
+
await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_TURBO_PASSTHROUGH);
|
|
57757
|
+
}
|
|
57758
|
+
}
|
|
57759
|
+
var TYPE_CHECK_KEY = "type-check", TYPE_CHECK_SCRIPT = "tsc --noEmit -p tsconfig.json", TYPE_CHECK_TURBO_PASSTHROUGH = "turbo run type-check", _fillScriptsDeps;
|
|
57760
|
+
var init_setup_fill = __esm(() => {
|
|
57761
|
+
_fillScriptsDeps = {
|
|
57762
|
+
readJson: async (path13) => {
|
|
57763
|
+
try {
|
|
57764
|
+
const f = Bun.file(path13);
|
|
57765
|
+
if (!await f.exists())
|
|
57766
|
+
return null;
|
|
57767
|
+
return JSON.parse(await f.text());
|
|
57768
|
+
} catch {
|
|
57769
|
+
return null;
|
|
57770
|
+
}
|
|
57771
|
+
},
|
|
57772
|
+
writeFile: async (path13, content) => {
|
|
57773
|
+
await Bun.write(path13, content);
|
|
57774
|
+
}
|
|
57775
|
+
};
|
|
57776
|
+
});
|
|
57777
|
+
|
|
57778
|
+
// src/cli/setup-llm.ts
|
|
57779
|
+
var generateSetupPlan = (ctx, analysis) => callOp(ctx, setupGenerateOp, analysis);
|
|
57780
|
+
var init_setup_llm = __esm(() => {
|
|
57781
|
+
init_operations();
|
|
57782
|
+
init_setup_generate();
|
|
57783
|
+
});
|
|
57784
|
+
|
|
57785
|
+
// src/cli/setup-verify.ts
|
|
57786
|
+
async function runSetupGate(workdir, config2) {
|
|
57787
|
+
const logger = getLogger();
|
|
57788
|
+
const quality = config2.quality;
|
|
57789
|
+
const testCmd = quality?.commands?.test;
|
|
57790
|
+
if (!testCmd) {
|
|
57791
|
+
logger.info("setup-verify", "No test command configured \u2014 skipping verification gate", {
|
|
57792
|
+
storyId: "setup"
|
|
57793
|
+
});
|
|
57794
|
+
return 0;
|
|
57795
|
+
}
|
|
57796
|
+
logger.info("setup-verify", "Running verification gate", { storyId: "setup", cmd: testCmd });
|
|
57797
|
+
const parts = testCmd.trim().split(/\s+/).filter(Boolean);
|
|
57798
|
+
if (parts.length === 0)
|
|
57799
|
+
return 0;
|
|
57800
|
+
const proc = _setupVerifyDeps.spawn(parts, { cwd: workdir });
|
|
57801
|
+
return await proc.exited;
|
|
57802
|
+
}
|
|
57803
|
+
var _setupVerifyDeps;
|
|
57804
|
+
var init_setup_verify = __esm(() => {
|
|
57805
|
+
init_logger2();
|
|
57806
|
+
_setupVerifyDeps = {
|
|
57807
|
+
spawn: Bun.spawn.bind(Bun)
|
|
57808
|
+
};
|
|
57809
|
+
});
|
|
57810
|
+
|
|
57811
|
+
// src/cli/setup.ts
|
|
57812
|
+
var exports_setup = {};
|
|
57813
|
+
__export(exports_setup, {
|
|
57814
|
+
setupCommand: () => setupCommand,
|
|
57815
|
+
_setupDeps: () => _setupDeps
|
|
57816
|
+
});
|
|
57817
|
+
import { join as join57 } from "path";
|
|
57818
|
+
async function setupCommand(options = {}) {
|
|
57819
|
+
const workdir = options.dir ?? process.cwd();
|
|
57820
|
+
const naxDir = join57(workdir, ".nax");
|
|
57821
|
+
const naxConfigPath = join57(naxDir, "config.json");
|
|
57822
|
+
const exists = await _setupDeps.fileExists(naxConfigPath);
|
|
57823
|
+
if (exists && !options.force) {
|
|
57824
|
+
_setupDeps.stderr("[setup] .nax/config.json already exists. Use --force to overwrite.");
|
|
57825
|
+
return 1;
|
|
57826
|
+
}
|
|
57827
|
+
const analysis = await _setupDeps.analyzeRepo(workdir);
|
|
57828
|
+
const { ctx, close } = await _setupDeps.buildCallContext(workdir, options.agent);
|
|
57829
|
+
let plan;
|
|
57830
|
+
try {
|
|
57831
|
+
try {
|
|
57832
|
+
plan = await _setupDeps.generateSetupPlan(ctx, analysis);
|
|
57833
|
+
} catch (err) {
|
|
57834
|
+
if (err instanceof NaxError && err.code === "SETUP_PLAN_INVALID") {
|
|
57835
|
+
_setupDeps.stderr(`[setup] ${err.message}`);
|
|
57836
|
+
return 1;
|
|
57837
|
+
}
|
|
57838
|
+
throw err;
|
|
57839
|
+
}
|
|
57840
|
+
if (options.dryRun) {
|
|
57841
|
+
_setupDeps.stdout(`[setup] Dry run \u2014 planned root config:
|
|
57842
|
+
${JSON.stringify(plan.config, null, 2)}`);
|
|
57843
|
+
return 0;
|
|
57844
|
+
}
|
|
57845
|
+
for (const gap of plan.gaps) {
|
|
57846
|
+
_setupDeps.stderr(`[setup] gap: ${gap}`);
|
|
57847
|
+
}
|
|
57848
|
+
if (options.fillScripts) {
|
|
57849
|
+
await _setupDeps.fillScripts(workdir, analysis);
|
|
57850
|
+
}
|
|
57851
|
+
await _setupDeps.mkdir(naxDir);
|
|
57852
|
+
await _setupDeps.writeFile(naxConfigPath, JSON.stringify(plan.config, null, 2));
|
|
57853
|
+
for (const mc of plan.monoConfigs) {
|
|
57854
|
+
const monoDir = join57(naxDir, "mono", mc.relativeDir);
|
|
57855
|
+
await _setupDeps.mkdir(monoDir);
|
|
57856
|
+
await _setupDeps.writeFile(join57(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
|
|
57857
|
+
}
|
|
57858
|
+
const gateResult = await _setupDeps.runGate(workdir, plan.config);
|
|
57859
|
+
if (gateResult !== 0) {
|
|
57860
|
+
return gateResult;
|
|
57861
|
+
}
|
|
57862
|
+
return 0;
|
|
57863
|
+
} finally {
|
|
57864
|
+
await close();
|
|
57865
|
+
}
|
|
57866
|
+
}
|
|
57867
|
+
var _setupDeps;
|
|
57868
|
+
var init_setup = __esm(() => {
|
|
57869
|
+
init_errors();
|
|
57870
|
+
init_setup_analyze();
|
|
57871
|
+
init_setup_fill();
|
|
57872
|
+
init_setup_llm();
|
|
57873
|
+
init_setup_verify();
|
|
57874
|
+
_setupDeps = {
|
|
57875
|
+
analyzeRepo,
|
|
57876
|
+
fillScripts,
|
|
57877
|
+
buildCallContext: async (workdir, agentName) => {
|
|
57878
|
+
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
57879
|
+
const { createRuntime: createRuntime2 } = await Promise.resolve().then(() => (init_runtime(), exports_runtime));
|
|
57880
|
+
const config2 = await loadConfig2(workdir);
|
|
57881
|
+
const rt = createRuntime2(config2, workdir);
|
|
57882
|
+
return {
|
|
57883
|
+
ctx: {
|
|
57884
|
+
runtime: rt,
|
|
57885
|
+
packageView: rt.packages.resolve(),
|
|
57886
|
+
packageDir: workdir,
|
|
57887
|
+
agentName: agentName ?? rt.agentManager.getDefault()
|
|
57888
|
+
},
|
|
57889
|
+
close: () => rt.close()
|
|
57890
|
+
};
|
|
57891
|
+
},
|
|
57892
|
+
generateSetupPlan: (ctx, analysis) => generateSetupPlan(ctx, analysis),
|
|
57893
|
+
runGate: (workdir, config2) => runSetupGate(workdir, config2),
|
|
57894
|
+
fileExists: (path13) => Bun.file(path13).exists(),
|
|
57895
|
+
writeFile: (path13, content) => Bun.write(path13, content).then(() => {}),
|
|
57896
|
+
mkdir: async (path13) => {
|
|
57897
|
+
const proc = Bun.spawn(["mkdir", "-p", path13]);
|
|
57898
|
+
await proc.exited;
|
|
57899
|
+
},
|
|
57900
|
+
stdout: (msg) => {
|
|
57901
|
+
process.stdout.write(`${msg}
|
|
57902
|
+
`);
|
|
57903
|
+
},
|
|
57904
|
+
stderr: (msg) => {
|
|
57905
|
+
process.stderr.write(`${msg}
|
|
57906
|
+
`);
|
|
57907
|
+
}
|
|
57908
|
+
};
|
|
57909
|
+
});
|
|
57910
|
+
|
|
57072
57911
|
// src/plugins/builtin/curator/collect.ts
|
|
57073
57912
|
import * as path13 from "path";
|
|
57074
57913
|
function now() {
|
|
@@ -57770,12 +58609,12 @@ function renderProposals(proposals, runId, observationCount) {
|
|
|
57770
58609
|
}
|
|
57771
58610
|
|
|
57772
58611
|
// src/plugins/builtin/curator/rollup.ts
|
|
57773
|
-
import { appendFile as appendFile3, mkdir as
|
|
58612
|
+
import { appendFile as appendFile3, mkdir as mkdir9, writeFile } from "fs/promises";
|
|
57774
58613
|
import * as path14 from "path";
|
|
57775
58614
|
async function appendToRollup(observations, rollupPath) {
|
|
57776
58615
|
try {
|
|
57777
58616
|
const dir = path14.dirname(rollupPath);
|
|
57778
|
-
await
|
|
58617
|
+
await mkdir9(dir, { recursive: true });
|
|
57779
58618
|
if (observations.length === 0) {
|
|
57780
58619
|
const f = Bun.file(rollupPath);
|
|
57781
58620
|
if (!await f.exists()) {
|
|
@@ -57792,7 +58631,7 @@ async function appendToRollup(observations, rollupPath) {
|
|
|
57792
58631
|
var init_rollup = () => {};
|
|
57793
58632
|
|
|
57794
58633
|
// src/plugins/builtin/curator/index.ts
|
|
57795
|
-
import { mkdir as
|
|
58634
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
57796
58635
|
import * as path15 from "path";
|
|
57797
58636
|
function getCuratorEnabled(context) {
|
|
57798
58637
|
const cfg = context.config;
|
|
@@ -57867,7 +58706,7 @@ var init_curator = __esm(() => {
|
|
|
57867
58706
|
if (context.outputDir) {
|
|
57868
58707
|
const { observationsPath, rollupPath } = resolveCuratorOutputs(curatorContext);
|
|
57869
58708
|
const runDir = path15.dirname(observationsPath);
|
|
57870
|
-
await
|
|
58709
|
+
await mkdir10(runDir, { recursive: true });
|
|
57871
58710
|
await Bun.write(observationsPath, observations.map((o) => JSON.stringify(o)).join(`
|
|
57872
58711
|
`) + (observations.length > 0 ? `
|
|
57873
58712
|
` : ""));
|
|
@@ -58443,12 +59282,12 @@ var init_loader4 = __esm(() => {
|
|
|
58443
59282
|
});
|
|
58444
59283
|
|
|
58445
59284
|
// src/utils/paths.ts
|
|
58446
|
-
import { join as
|
|
59285
|
+
import { join as join68 } from "path";
|
|
58447
59286
|
function getRunsDir() {
|
|
58448
|
-
return process.env.NAX_RUNS_DIR ??
|
|
59287
|
+
return process.env.NAX_RUNS_DIR ?? join68(globalConfigDir(), "runs");
|
|
58449
59288
|
}
|
|
58450
59289
|
function getEventsRootDir() {
|
|
58451
|
-
return
|
|
59290
|
+
return join68(globalConfigDir(), "events");
|
|
58452
59291
|
}
|
|
58453
59292
|
var init_paths3 = __esm(() => {
|
|
58454
59293
|
init_paths();
|
|
@@ -58508,7 +59347,7 @@ var init_command_argv = __esm(() => {
|
|
|
58508
59347
|
});
|
|
58509
59348
|
|
|
58510
59349
|
// src/hooks/runner.ts
|
|
58511
|
-
import { join as
|
|
59350
|
+
import { join as join75 } from "path";
|
|
58512
59351
|
function createDrainDeadline2(deadlineMs) {
|
|
58513
59352
|
let timeoutId;
|
|
58514
59353
|
const promise2 = new Promise((resolve16) => {
|
|
@@ -58527,14 +59366,14 @@ async function loadHooksConfig(projectDir, globalDir) {
|
|
|
58527
59366
|
let globalHooks = { hooks: {} };
|
|
58528
59367
|
let projectHooks = { hooks: {} };
|
|
58529
59368
|
let skipGlobal = false;
|
|
58530
|
-
const projectPath =
|
|
59369
|
+
const projectPath = join75(projectDir, "hooks.json");
|
|
58531
59370
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
58532
59371
|
if (projectData) {
|
|
58533
59372
|
projectHooks = projectData;
|
|
58534
59373
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
58535
59374
|
}
|
|
58536
59375
|
if (!skipGlobal && globalDir) {
|
|
58537
|
-
const globalPath =
|
|
59376
|
+
const globalPath = join75(globalDir, "hooks.json");
|
|
58538
59377
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
58539
59378
|
if (globalData) {
|
|
58540
59379
|
globalHooks = globalData;
|
|
@@ -58704,7 +59543,7 @@ var package_default;
|
|
|
58704
59543
|
var init_package = __esm(() => {
|
|
58705
59544
|
package_default = {
|
|
58706
59545
|
name: "@nathapp/nax",
|
|
58707
|
-
version: "0.69.
|
|
59546
|
+
version: "0.69.2",
|
|
58708
59547
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
58709
59548
|
type: "module",
|
|
58710
59549
|
bin: {
|
|
@@ -58799,8 +59638,8 @@ var init_version = __esm(() => {
|
|
|
58799
59638
|
NAX_VERSION = package_default.version;
|
|
58800
59639
|
NAX_COMMIT = (() => {
|
|
58801
59640
|
try {
|
|
58802
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
58803
|
-
return "
|
|
59641
|
+
if (/^[0-9a-f]{6,10}$/.test("99db370f"))
|
|
59642
|
+
return "99db370f";
|
|
58804
59643
|
} catch {}
|
|
58805
59644
|
try {
|
|
58806
59645
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -59676,16 +60515,16 @@ var init_acceptance_loop = __esm(() => {
|
|
|
59676
60515
|
});
|
|
59677
60516
|
|
|
59678
60517
|
// src/session/scratch-purge.ts
|
|
59679
|
-
import { mkdir as
|
|
59680
|
-
import { dirname as dirname12, join as
|
|
60518
|
+
import { mkdir as mkdir12, rename, rm } from "fs/promises";
|
|
60519
|
+
import { dirname as dirname12, join as join76 } from "path";
|
|
59681
60520
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
59682
|
-
const sessionsDir =
|
|
60521
|
+
const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
|
|
59683
60522
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
59684
60523
|
const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
|
|
59685
60524
|
let purged = 0;
|
|
59686
60525
|
for (const sessionId of sessionIds) {
|
|
59687
|
-
const sessionDir =
|
|
59688
|
-
const descriptorPath =
|
|
60526
|
+
const sessionDir = join76(sessionsDir, sessionId);
|
|
60527
|
+
const descriptorPath = join76(sessionDir, "descriptor.json");
|
|
59689
60528
|
if (!await _scratchPurgeDeps.fileExists(descriptorPath))
|
|
59690
60529
|
continue;
|
|
59691
60530
|
let lastActivityAt;
|
|
@@ -59701,7 +60540,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
|
|
|
59701
60540
|
if (new Date(lastActivityAt).getTime() >= cutoffMs)
|
|
59702
60541
|
continue;
|
|
59703
60542
|
if (archiveInsteadOfDelete) {
|
|
59704
|
-
const archiveDest =
|
|
60543
|
+
const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
|
|
59705
60544
|
await _scratchPurgeDeps.move(sessionDir, archiveDest);
|
|
59706
60545
|
} else {
|
|
59707
60546
|
await _scratchPurgeDeps.remove(sessionDir);
|
|
@@ -59728,7 +60567,7 @@ var init_scratch_purge = __esm(() => {
|
|
|
59728
60567
|
readFile: (path19) => Bun.file(path19).text(),
|
|
59729
60568
|
remove: (path19) => rm(path19, { recursive: true, force: true }),
|
|
59730
60569
|
move: async (src, dest) => {
|
|
59731
|
-
await
|
|
60570
|
+
await mkdir12(dirname12(dest), { recursive: true });
|
|
59732
60571
|
await rename(src, dest);
|
|
59733
60572
|
},
|
|
59734
60573
|
now: () => Date.now()
|
|
@@ -60438,19 +61277,19 @@ function precomputeBatchPlan(stories, maxBatchSize = DEFAULT_MAX_BATCH_SIZE) {
|
|
|
60438
61277
|
var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
60439
61278
|
|
|
60440
61279
|
// src/pipeline/subscribers/events-writer.ts
|
|
60441
|
-
import { appendFile as appendFile4, mkdir as
|
|
60442
|
-
import { basename as basename13, join as
|
|
61280
|
+
import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
|
|
61281
|
+
import { basename as basename13, join as join77 } from "path";
|
|
60443
61282
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
60444
61283
|
const logger = getSafeLogger();
|
|
60445
61284
|
const project = basename13(workdir);
|
|
60446
|
-
const eventsDir =
|
|
60447
|
-
const eventsFile =
|
|
61285
|
+
const eventsDir = join77(getEventsRootDir(), project);
|
|
61286
|
+
const eventsFile = join77(eventsDir, "events.jsonl");
|
|
60448
61287
|
let dirReady = false;
|
|
60449
61288
|
const write = (line) => {
|
|
60450
61289
|
return (async () => {
|
|
60451
61290
|
try {
|
|
60452
61291
|
if (!dirReady) {
|
|
60453
|
-
await
|
|
61292
|
+
await mkdir13(eventsDir, { recursive: true });
|
|
60454
61293
|
dirReady = true;
|
|
60455
61294
|
}
|
|
60456
61295
|
await appendFile4(eventsFile, `${JSON.stringify(line)}
|
|
@@ -60624,24 +61463,24 @@ var init_interaction2 = __esm(() => {
|
|
|
60624
61463
|
});
|
|
60625
61464
|
|
|
60626
61465
|
// src/pipeline/subscribers/registry.ts
|
|
60627
|
-
import { mkdir as
|
|
60628
|
-
import { basename as basename14, join as
|
|
61466
|
+
import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
|
|
61467
|
+
import { basename as basename14, join as join78 } from "path";
|
|
60629
61468
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
60630
61469
|
const logger = getSafeLogger();
|
|
60631
61470
|
const project = basename14(workdir);
|
|
60632
|
-
const runDir =
|
|
60633
|
-
const metaFile =
|
|
61471
|
+
const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
61472
|
+
const metaFile = join78(runDir, "meta.json");
|
|
60634
61473
|
const unsub = bus.on("run:started", (_ev) => {
|
|
60635
61474
|
return (async () => {
|
|
60636
61475
|
try {
|
|
60637
|
-
await
|
|
61476
|
+
await mkdir14(runDir, { recursive: true });
|
|
60638
61477
|
const meta3 = {
|
|
60639
61478
|
runId,
|
|
60640
61479
|
project,
|
|
60641
61480
|
feature,
|
|
60642
61481
|
workdir,
|
|
60643
|
-
statusPath:
|
|
60644
|
-
eventsDir:
|
|
61482
|
+
statusPath: join78(outputDir, "features", feature, "status.json"),
|
|
61483
|
+
eventsDir: join78(outputDir, "features", feature, "runs"),
|
|
60645
61484
|
registeredAt: new Date().toISOString()
|
|
60646
61485
|
};
|
|
60647
61486
|
await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -60886,8 +61725,8 @@ var init_types9 = __esm(() => {
|
|
|
60886
61725
|
});
|
|
60887
61726
|
|
|
60888
61727
|
// src/worktree/dependencies.ts
|
|
60889
|
-
import { existsSync as
|
|
60890
|
-
import { join as
|
|
61728
|
+
import { existsSync as existsSync30 } from "fs";
|
|
61729
|
+
import { join as join79 } from "path";
|
|
60891
61730
|
async function prepareWorktreeDependencies(options) {
|
|
60892
61731
|
const mode = options.config.execution.worktreeDependencies.mode;
|
|
60893
61732
|
const resolvedCwd = resolveDependencyCwd(options);
|
|
@@ -60901,7 +61740,7 @@ async function prepareWorktreeDependencies(options) {
|
|
|
60901
61740
|
}
|
|
60902
61741
|
}
|
|
60903
61742
|
function resolveDependencyCwd(options) {
|
|
60904
|
-
return options.storyWorkdir ?
|
|
61743
|
+
return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
|
|
60905
61744
|
}
|
|
60906
61745
|
function resolveInheritedDependencies(options, resolvedCwd) {
|
|
60907
61746
|
if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
|
|
@@ -60911,14 +61750,14 @@ function resolveInheritedDependencies(options, resolvedCwd) {
|
|
|
60911
61750
|
}
|
|
60912
61751
|
function hasDependencyManifests(worktreeRoot, resolvedCwd) {
|
|
60913
61752
|
const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
|
|
60914
|
-
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(
|
|
61753
|
+
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
|
|
60915
61754
|
}
|
|
60916
61755
|
async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
|
|
60917
|
-
const
|
|
60918
|
-
if (!
|
|
61756
|
+
const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
|
|
61757
|
+
if (!setupCommand2) {
|
|
60919
61758
|
throw new WorktreeDependencyPreparationError("[worktree-deps] provision mode requires execution.worktreeDependencies.setupCommand in phase 1.", "provision");
|
|
60920
61759
|
}
|
|
60921
|
-
const argv = parseCommandToArgv(
|
|
61760
|
+
const argv = parseCommandToArgv(setupCommand2);
|
|
60922
61761
|
if (argv.length === 0) {
|
|
60923
61762
|
throw new WorktreeDependencyPreparationError("[worktree-deps] setupCommand cannot be empty.", "provision");
|
|
60924
61763
|
}
|
|
@@ -60962,7 +61801,7 @@ var init_dependencies = __esm(() => {
|
|
|
60962
61801
|
"build.gradle.kts"
|
|
60963
61802
|
];
|
|
60964
61803
|
_worktreeDependencyDeps = {
|
|
60965
|
-
existsSync:
|
|
61804
|
+
existsSync: existsSync30,
|
|
60966
61805
|
spawn
|
|
60967
61806
|
};
|
|
60968
61807
|
});
|
|
@@ -60973,19 +61812,19 @@ __export(exports_manager, {
|
|
|
60973
61812
|
_managerDeps: () => _managerDeps,
|
|
60974
61813
|
WorktreeManager: () => WorktreeManager
|
|
60975
61814
|
});
|
|
60976
|
-
import { existsSync as
|
|
60977
|
-
import { mkdir as
|
|
60978
|
-
import { join as
|
|
61815
|
+
import { existsSync as existsSync31, symlinkSync } from "fs";
|
|
61816
|
+
import { mkdir as mkdir15 } from "fs/promises";
|
|
61817
|
+
import { join as join80 } from "path";
|
|
60979
61818
|
|
|
60980
61819
|
class WorktreeManager {
|
|
60981
61820
|
async ensureGitExcludes(projectRoot) {
|
|
60982
61821
|
const logger = getSafeLogger();
|
|
60983
|
-
const infoDir =
|
|
60984
|
-
const excludePath =
|
|
61822
|
+
const infoDir = join80(projectRoot, ".git", "info");
|
|
61823
|
+
const excludePath = join80(infoDir, "exclude");
|
|
60985
61824
|
try {
|
|
60986
|
-
await
|
|
61825
|
+
await mkdir15(infoDir, { recursive: true });
|
|
60987
61826
|
let existing = "";
|
|
60988
|
-
if (
|
|
61827
|
+
if (existsSync31(excludePath)) {
|
|
60989
61828
|
existing = await Bun.file(excludePath).text();
|
|
60990
61829
|
}
|
|
60991
61830
|
const missing = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
|
|
@@ -61008,7 +61847,7 @@ ${missing.join(`
|
|
|
61008
61847
|
}
|
|
61009
61848
|
async create(projectRoot, storyId) {
|
|
61010
61849
|
validateStoryId(storyId);
|
|
61011
|
-
const worktreePath =
|
|
61850
|
+
const worktreePath = join80(projectRoot, ".nax-wt", storyId);
|
|
61012
61851
|
const branchName = `nax/${storyId}`;
|
|
61013
61852
|
try {
|
|
61014
61853
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -61049,9 +61888,9 @@ ${missing.join(`
|
|
|
61049
61888
|
}
|
|
61050
61889
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
61051
61890
|
}
|
|
61052
|
-
const envSource =
|
|
61053
|
-
if (
|
|
61054
|
-
const envTarget =
|
|
61891
|
+
const envSource = join80(projectRoot, ".env");
|
|
61892
|
+
if (existsSync31(envSource)) {
|
|
61893
|
+
const envTarget = join80(worktreePath, ".env");
|
|
61055
61894
|
try {
|
|
61056
61895
|
symlinkSync(envSource, envTarget, "file");
|
|
61057
61896
|
} catch (error48) {
|
|
@@ -61062,7 +61901,7 @@ ${missing.join(`
|
|
|
61062
61901
|
}
|
|
61063
61902
|
async remove(projectRoot, storyId) {
|
|
61064
61903
|
validateStoryId(storyId);
|
|
61065
|
-
const worktreePath =
|
|
61904
|
+
const worktreePath = join80(projectRoot, ".nax-wt", storyId);
|
|
61066
61905
|
const branchName = `nax/${storyId}`;
|
|
61067
61906
|
try {
|
|
61068
61907
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -61874,10 +62713,10 @@ var init_merge_conflict_rectify = __esm(() => {
|
|
|
61874
62713
|
});
|
|
61875
62714
|
|
|
61876
62715
|
// src/execution/pipeline-result-handler.ts
|
|
61877
|
-
import { join as
|
|
62716
|
+
import { join as join81 } from "path";
|
|
61878
62717
|
async function removeWorktreeDirectory(projectRoot, storyId) {
|
|
61879
62718
|
const logger = getSafeLogger();
|
|
61880
|
-
const worktreePath =
|
|
62719
|
+
const worktreePath = join81(projectRoot, ".nax-wt", storyId);
|
|
61881
62720
|
try {
|
|
61882
62721
|
const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
61883
62722
|
cwd: projectRoot,
|
|
@@ -62093,8 +62932,8 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
62093
62932
|
});
|
|
62094
62933
|
|
|
62095
62934
|
// src/execution/iteration-runner.ts
|
|
62096
|
-
import { existsSync as
|
|
62097
|
-
import { join as
|
|
62935
|
+
import { existsSync as existsSync32 } from "fs";
|
|
62936
|
+
import { join as join82 } from "path";
|
|
62098
62937
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
62099
62938
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
62100
62939
|
if (ctx.dryRun) {
|
|
@@ -62119,7 +62958,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62119
62958
|
const storyStartTime = Date.now();
|
|
62120
62959
|
let effectiveWorkdir = ctx.workdir;
|
|
62121
62960
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
62122
|
-
const worktreePath =
|
|
62961
|
+
const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
|
|
62123
62962
|
const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
|
|
62124
62963
|
if (!worktreeExists) {
|
|
62125
62964
|
await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
|
|
@@ -62139,7 +62978,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62139
62978
|
}
|
|
62140
62979
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
62141
62980
|
const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
|
|
62142
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
62981
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join82(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
|
|
62143
62982
|
let dependencyContext;
|
|
62144
62983
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
62145
62984
|
try {
|
|
@@ -62166,7 +63005,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62166
63005
|
};
|
|
62167
63006
|
}
|
|
62168
63007
|
}
|
|
62169
|
-
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ?
|
|
63008
|
+
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join82(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join82(ctx.workdir, story.workdir) : ctx.workdir;
|
|
62170
63009
|
const pipelineContext = {
|
|
62171
63010
|
config: effectiveConfig,
|
|
62172
63011
|
rootConfig: ctx.config,
|
|
@@ -62298,7 +63137,7 @@ var init_iteration_runner = __esm(() => {
|
|
|
62298
63137
|
loadConfigForWorkdir,
|
|
62299
63138
|
prepareWorktreeDependencies,
|
|
62300
63139
|
runPipeline,
|
|
62301
|
-
existsSync:
|
|
63140
|
+
existsSync: existsSync32,
|
|
62302
63141
|
worktreeManager: new WorktreeManager
|
|
62303
63142
|
};
|
|
62304
63143
|
});
|
|
@@ -62368,7 +63207,7 @@ __export(exports_parallel_worker, {
|
|
|
62368
63207
|
buildWorktreePipelineContext: () => buildWorktreePipelineContext,
|
|
62369
63208
|
_parallelWorkerDeps: () => _parallelWorkerDeps
|
|
62370
63209
|
});
|
|
62371
|
-
import { join as
|
|
63210
|
+
import { join as join83 } from "path";
|
|
62372
63211
|
function buildWorktreePipelineContext(base, _story) {
|
|
62373
63212
|
return { ...base, prd: structuredClone(base.prd) };
|
|
62374
63213
|
}
|
|
@@ -62391,7 +63230,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
|
|
|
62391
63230
|
story,
|
|
62392
63231
|
stories: [story],
|
|
62393
63232
|
projectDir: context.projectDir,
|
|
62394
|
-
workdir: dependencyContext.cwd ?? (story.workdir ?
|
|
63233
|
+
workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
|
|
62395
63234
|
worktreeDependencyContext: dependencyContext,
|
|
62396
63235
|
routing,
|
|
62397
63236
|
storyGitRef: storyGitRef ?? undefined
|
|
@@ -63278,7 +64117,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
63278
64117
|
var init_status_file = () => {};
|
|
63279
64118
|
|
|
63280
64119
|
// src/execution/status-writer.ts
|
|
63281
|
-
import { join as
|
|
64120
|
+
import { join as join84 } from "path";
|
|
63282
64121
|
|
|
63283
64122
|
class StatusWriter {
|
|
63284
64123
|
statusFile;
|
|
@@ -63397,7 +64236,7 @@ class StatusWriter {
|
|
|
63397
64236
|
if (!this._prd)
|
|
63398
64237
|
return;
|
|
63399
64238
|
const safeLogger = getSafeLogger();
|
|
63400
|
-
const featureStatusPath =
|
|
64239
|
+
const featureStatusPath = join84(featureDir, "status.json");
|
|
63401
64240
|
const write = async () => {
|
|
63402
64241
|
try {
|
|
63403
64242
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -63428,11 +64267,11 @@ __export(exports_migrate, {
|
|
|
63428
64267
|
migrateCommand: () => migrateCommand,
|
|
63429
64268
|
detectGeneratedContent: () => detectGeneratedContent
|
|
63430
64269
|
});
|
|
63431
|
-
import { existsSync as
|
|
63432
|
-
import { mkdir as
|
|
64270
|
+
import { existsSync as existsSync33 } from "fs";
|
|
64271
|
+
import { mkdir as mkdir16, readdir as readdir6, rename as rename3 } from "fs/promises";
|
|
63433
64272
|
import path22 from "path";
|
|
63434
64273
|
async function detectGeneratedContent(naxDir) {
|
|
63435
|
-
if (!
|
|
64274
|
+
if (!existsSync33(naxDir))
|
|
63436
64275
|
return [];
|
|
63437
64276
|
const candidates = [];
|
|
63438
64277
|
let entries = [];
|
|
@@ -63447,7 +64286,7 @@ async function detectGeneratedContent(naxDir) {
|
|
|
63447
64286
|
}
|
|
63448
64287
|
}
|
|
63449
64288
|
const featuresDir = path22.join(naxDir, "features");
|
|
63450
|
-
if (
|
|
64289
|
+
if (existsSync33(featuresDir)) {
|
|
63451
64290
|
let featureDirs = [];
|
|
63452
64291
|
try {
|
|
63453
64292
|
featureDirs = await readdir6(featuresDir);
|
|
@@ -63509,7 +64348,7 @@ async function migrateCommand(options) {
|
|
|
63509
64348
|
});
|
|
63510
64349
|
}
|
|
63511
64350
|
const src = path22.join(globalConfigDir(), options.reclaim);
|
|
63512
|
-
if (!
|
|
64351
|
+
if (!existsSync33(src)) {
|
|
63513
64352
|
throw new NaxError(`Nothing to reclaim: ~/.nax/${options.reclaim} does not exist`, "MIGRATE_RECLAIM_NOT_FOUND", {
|
|
63514
64353
|
stage: "migrate",
|
|
63515
64354
|
name: options.reclaim
|
|
@@ -63517,7 +64356,7 @@ async function migrateCommand(options) {
|
|
|
63517
64356
|
}
|
|
63518
64357
|
const archiveBase = path22.join(globalConfigDir(), "_archive");
|
|
63519
64358
|
const archiveDest = path22.join(archiveBase, `${options.reclaim}-${Date.now()}`);
|
|
63520
|
-
await
|
|
64359
|
+
await mkdir16(archiveBase, { recursive: true });
|
|
63521
64360
|
await rename3(src, archiveDest);
|
|
63522
64361
|
logger.info("migrate", `Reclaimed: archived to ${archiveDest}`, { storyId: "_migrate" });
|
|
63523
64362
|
return;
|
|
@@ -63555,7 +64394,7 @@ async function migrateCommand(options) {
|
|
|
63555
64394
|
}
|
|
63556
64395
|
const naxDir = path22.join(options.workdir, ".nax");
|
|
63557
64396
|
const configPath = path22.join(naxDir, "config.json");
|
|
63558
|
-
if (!
|
|
64397
|
+
if (!existsSync33(configPath)) {
|
|
63559
64398
|
throw new NaxError("No .nax/config.json found \u2014 run nax init first", "MIGRATE_NO_CONFIG", {
|
|
63560
64399
|
stage: "migrate",
|
|
63561
64400
|
workdir: options.workdir
|
|
@@ -63585,12 +64424,12 @@ async function migrateCommand(options) {
|
|
|
63585
64424
|
}
|
|
63586
64425
|
return;
|
|
63587
64426
|
}
|
|
63588
|
-
await
|
|
64427
|
+
await mkdir16(destBase, { recursive: true });
|
|
63589
64428
|
let moved = 0;
|
|
63590
64429
|
for (const candidate of candidates) {
|
|
63591
64430
|
const dest = path22.join(destBase, candidate.name);
|
|
63592
|
-
await
|
|
63593
|
-
if (
|
|
64431
|
+
await mkdir16(path22.dirname(dest), { recursive: true });
|
|
64432
|
+
if (existsSync33(dest)) {
|
|
63594
64433
|
throw new NaxError(`Migration conflict: destination already exists.
|
|
63595
64434
|
Source: ${candidate.srcPath}
|
|
63596
64435
|
Destination: ${dest}
|
|
@@ -63831,7 +64670,7 @@ __export(exports_run_initialization, {
|
|
|
63831
64670
|
initializeRun: () => initializeRun,
|
|
63832
64671
|
_reconcileDeps: () => _reconcileDeps
|
|
63833
64672
|
});
|
|
63834
|
-
import { join as
|
|
64673
|
+
import { join as join85 } from "path";
|
|
63835
64674
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
63836
64675
|
const logger = getSafeLogger();
|
|
63837
64676
|
let reconciledCount = 0;
|
|
@@ -63848,7 +64687,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
63848
64687
|
});
|
|
63849
64688
|
continue;
|
|
63850
64689
|
}
|
|
63851
|
-
const effectiveWorkdir = story.workdir ?
|
|
64690
|
+
const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
|
|
63852
64691
|
try {
|
|
63853
64692
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
63854
64693
|
if (!reviewResult.success) {
|
|
@@ -64114,6 +64953,17 @@ async function setupRun(options) {
|
|
|
64114
64953
|
featureName: options.feature,
|
|
64115
64954
|
agentStreamEvents: options.agentStreamEvents
|
|
64116
64955
|
});
|
|
64956
|
+
try {
|
|
64957
|
+
const workspacePackages = await discoverWorkspacePackages(workdir);
|
|
64958
|
+
if (workspacePackages.length > 0) {
|
|
64959
|
+
await runtime.packages.hydrate(workspacePackages);
|
|
64960
|
+
}
|
|
64961
|
+
} catch (err) {
|
|
64962
|
+
getSafeLogger()?.warn("run-setup", "Per-package config hydration failed \u2014 using root config", {
|
|
64963
|
+
storyId: "_setup",
|
|
64964
|
+
error: errorMessage(err)
|
|
64965
|
+
});
|
|
64966
|
+
}
|
|
64117
64967
|
await runtime.pidRegistry.cleanupStale();
|
|
64118
64968
|
const cleanupCrashHandlers = installCrashHandlers({
|
|
64119
64969
|
statusWriter,
|
|
@@ -64280,6 +65130,7 @@ async function setupRun(options) {
|
|
|
64280
65130
|
var _runSetupDeps;
|
|
64281
65131
|
var init_run_setup = __esm(() => {
|
|
64282
65132
|
init_pipeline();
|
|
65133
|
+
init_test_runners();
|
|
64283
65134
|
init_paths();
|
|
64284
65135
|
init_errors();
|
|
64285
65136
|
init_interaction();
|
|
@@ -64289,7 +65140,6 @@ var init_run_setup = __esm(() => {
|
|
|
64289
65140
|
init_project();
|
|
64290
65141
|
init_runtime();
|
|
64291
65142
|
init_session();
|
|
64292
|
-
init_resolver();
|
|
64293
65143
|
init_version();
|
|
64294
65144
|
init_crash_recovery();
|
|
64295
65145
|
init_helpers();
|
|
@@ -93641,7 +94491,7 @@ __export(exports_curator, {
|
|
|
93641
94491
|
});
|
|
93642
94492
|
import { readdirSync as readdirSync8 } from "fs";
|
|
93643
94493
|
import { unlink as unlink4 } from "fs/promises";
|
|
93644
|
-
import { basename as basename15, join as
|
|
94494
|
+
import { basename as basename15, join as join87 } from "path";
|
|
93645
94495
|
function getProjectKey(config2, projectDir) {
|
|
93646
94496
|
return config2.name?.trim() || basename15(projectDir);
|
|
93647
94497
|
}
|
|
@@ -93724,7 +94574,7 @@ async function curatorStatus(options) {
|
|
|
93724
94574
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93725
94575
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93726
94576
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93727
|
-
const runsDir =
|
|
94577
|
+
const runsDir = join87(outputDir, "runs");
|
|
93728
94578
|
const runIds = listRunIds(runsDir);
|
|
93729
94579
|
let runId;
|
|
93730
94580
|
if (options.run) {
|
|
@@ -93741,8 +94591,8 @@ async function curatorStatus(options) {
|
|
|
93741
94591
|
runId = runIds[runIds.length - 1];
|
|
93742
94592
|
}
|
|
93743
94593
|
console.log(`Run: ${runId}`);
|
|
93744
|
-
const runDir =
|
|
93745
|
-
const observationsPath =
|
|
94594
|
+
const runDir = join87(runsDir, runId);
|
|
94595
|
+
const observationsPath = join87(runDir, "observations.jsonl");
|
|
93746
94596
|
const observations = await parseObservations(observationsPath);
|
|
93747
94597
|
const counts = new Map;
|
|
93748
94598
|
for (const obs of observations) {
|
|
@@ -93752,7 +94602,7 @@ async function curatorStatus(options) {
|
|
|
93752
94602
|
for (const [kind, count] of counts.entries()) {
|
|
93753
94603
|
console.log(` ${kind}: ${count}`);
|
|
93754
94604
|
}
|
|
93755
|
-
const proposalsPath =
|
|
94605
|
+
const proposalsPath = join87(runDir, "curator-proposals.md");
|
|
93756
94606
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
93757
94607
|
if (proposalText !== null) {
|
|
93758
94608
|
console.log("");
|
|
@@ -93766,8 +94616,8 @@ async function curatorCommit(options) {
|
|
|
93766
94616
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93767
94617
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93768
94618
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93769
|
-
const runDir =
|
|
93770
|
-
const proposalsPath =
|
|
94619
|
+
const runDir = join87(outputDir, "runs", options.runId);
|
|
94620
|
+
const proposalsPath = join87(runDir, "curator-proposals.md");
|
|
93771
94621
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
93772
94622
|
if (proposalText === null) {
|
|
93773
94623
|
console.log(`curator-proposals.md not found for run ${options.runId}.`);
|
|
@@ -93783,7 +94633,7 @@ async function curatorCommit(options) {
|
|
|
93783
94633
|
const dropFileState = new Map;
|
|
93784
94634
|
const skippedDrops = new Set;
|
|
93785
94635
|
for (const drop2 of drops) {
|
|
93786
|
-
const targetPath =
|
|
94636
|
+
const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
|
|
93787
94637
|
if (!dropFileState.has(targetPath)) {
|
|
93788
94638
|
const fileExists2 = await Bun.file(targetPath).exists();
|
|
93789
94639
|
const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
|
|
@@ -93817,7 +94667,7 @@ async function curatorCommit(options) {
|
|
|
93817
94667
|
if (skippedDrops.has(drop2)) {
|
|
93818
94668
|
continue;
|
|
93819
94669
|
}
|
|
93820
|
-
const targetPath =
|
|
94670
|
+
const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
|
|
93821
94671
|
const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
|
|
93822
94672
|
const filtered = filterDropContent(existing, drop2.description);
|
|
93823
94673
|
await _curatorCmdDeps.writeFile(targetPath, filtered);
|
|
@@ -93826,7 +94676,7 @@ async function curatorCommit(options) {
|
|
|
93826
94676
|
}
|
|
93827
94677
|
const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
|
|
93828
94678
|
for (const add2 of adds) {
|
|
93829
|
-
const targetPath =
|
|
94679
|
+
const targetPath = join87(resolved.projectDir, add2.canonicalFile);
|
|
93830
94680
|
const content = buildAddContent(add2);
|
|
93831
94681
|
await _curatorCmdDeps.appendFile(targetPath, content);
|
|
93832
94682
|
modifiedFiles.add(targetPath);
|
|
@@ -93863,7 +94713,7 @@ async function curatorDryrun(options) {
|
|
|
93863
94713
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93864
94714
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93865
94715
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93866
|
-
const runsDir =
|
|
94716
|
+
const runsDir = join87(outputDir, "runs");
|
|
93867
94717
|
const runIds = listRunIds(runsDir);
|
|
93868
94718
|
if (runIds.length === 0) {
|
|
93869
94719
|
console.log("No runs found.");
|
|
@@ -93874,7 +94724,7 @@ async function curatorDryrun(options) {
|
|
|
93874
94724
|
console.log(`Run ${options.run} not found in ${runsDir}.`);
|
|
93875
94725
|
return;
|
|
93876
94726
|
}
|
|
93877
|
-
const observationsPath =
|
|
94727
|
+
const observationsPath = join87(runsDir, runId, "observations.jsonl");
|
|
93878
94728
|
const observations = await parseObservations(observationsPath);
|
|
93879
94729
|
const thresholds = getThresholds(config2);
|
|
93880
94730
|
const proposals = runHeuristics(observations, thresholds);
|
|
@@ -93915,12 +94765,12 @@ async function curatorGc(options) {
|
|
|
93915
94765
|
await _curatorCmdDeps.writeFile(rollupPath, newContent);
|
|
93916
94766
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93917
94767
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93918
|
-
const perRunsDir =
|
|
94768
|
+
const perRunsDir = join87(outputDir, "runs");
|
|
93919
94769
|
for (const runId of uniqueRunIds) {
|
|
93920
94770
|
if (!keepSet.has(runId)) {
|
|
93921
|
-
const runDir =
|
|
93922
|
-
await _curatorCmdDeps.removeFile(
|
|
93923
|
-
await _curatorCmdDeps.removeFile(
|
|
94771
|
+
const runDir = join87(perRunsDir, runId);
|
|
94772
|
+
await _curatorCmdDeps.removeFile(join87(runDir, "observations.jsonl"));
|
|
94773
|
+
await _curatorCmdDeps.removeFile(join87(runDir, "curator-proposals.md"));
|
|
93924
94774
|
}
|
|
93925
94775
|
}
|
|
93926
94776
|
console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
|
|
@@ -93963,9 +94813,9 @@ var init_curator2 = __esm(() => {
|
|
|
93963
94813
|
|
|
93964
94814
|
// bin/nax.ts
|
|
93965
94815
|
init_source();
|
|
93966
|
-
import { existsSync as
|
|
94816
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
|
|
93967
94817
|
import { homedir as homedir3 } from "os";
|
|
93968
|
-
import { basename as basename16, join as
|
|
94818
|
+
import { basename as basename16, join as join88 } from "path";
|
|
93969
94819
|
|
|
93970
94820
|
// node_modules/commander/esm.mjs
|
|
93971
94821
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -93989,12 +94839,12 @@ init_errors();
|
|
|
93989
94839
|
init_operations();
|
|
93990
94840
|
|
|
93991
94841
|
// src/plan/strategies/context-builder.ts
|
|
93992
|
-
import { join as
|
|
94842
|
+
import { join as join39 } from "path";
|
|
93993
94843
|
init_config();
|
|
93994
94844
|
init_errors();
|
|
93995
94845
|
init_interaction();
|
|
93996
94846
|
async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
93997
|
-
const naxDir =
|
|
94847
|
+
const naxDir = join39(workdir, ".nax");
|
|
93998
94848
|
if (!deps.existsSync(naxDir)) {
|
|
93999
94849
|
throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
|
|
94000
94850
|
stage: "plan",
|
|
@@ -94002,8 +94852,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
94002
94852
|
});
|
|
94003
94853
|
}
|
|
94004
94854
|
validateFeatureName(options.feature);
|
|
94005
|
-
const outputDir =
|
|
94006
|
-
const outputPath =
|
|
94855
|
+
const outputDir = join39(naxDir, "features", options.feature);
|
|
94856
|
+
const outputPath = join39(outputDir, "prd.json");
|
|
94007
94857
|
const [specContent, sourceRoots, pkg] = await Promise.all([
|
|
94008
94858
|
deps.readFile(options.from),
|
|
94009
94859
|
deps.scanSourceRoots(workdir),
|
|
@@ -94018,7 +94868,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
94018
94868
|
...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
|
|
94019
94869
|
];
|
|
94020
94870
|
const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
|
|
94021
|
-
const packageJson = await deps.readPackageJsonAt(
|
|
94871
|
+
const packageJson = await deps.readPackageJsonAt(join39(workdir, relativePath, "package.json"));
|
|
94022
94872
|
return buildPackageSummary(relativePath, packageJson);
|
|
94023
94873
|
}));
|
|
94024
94874
|
const projectName = detectProjectName(workdir, pkg);
|
|
@@ -94621,7 +95471,7 @@ init_interaction();
|
|
|
94621
95471
|
init_prd();
|
|
94622
95472
|
init_runtime();
|
|
94623
95473
|
import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
|
|
94624
|
-
import { basename as basename7, join as
|
|
95474
|
+
import { basename as basename7, join as join44, resolve as resolve14 } from "path";
|
|
94625
95475
|
var _statusFeaturesDeps = {
|
|
94626
95476
|
projectOutputDir,
|
|
94627
95477
|
loadConfig
|
|
@@ -94635,7 +95485,7 @@ function isPidAlive(pid) {
|
|
|
94635
95485
|
}
|
|
94636
95486
|
}
|
|
94637
95487
|
async function loadStatusFile(featureDir) {
|
|
94638
|
-
const statusPath =
|
|
95488
|
+
const statusPath = join44(featureDir, "status.json");
|
|
94639
95489
|
if (!existsSync17(statusPath)) {
|
|
94640
95490
|
return null;
|
|
94641
95491
|
}
|
|
@@ -94650,7 +95500,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
94650
95500
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
94651
95501
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
94652
95502
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
94653
|
-
const statusPath =
|
|
95503
|
+
const statusPath = join44(outputDir, "status.json");
|
|
94654
95504
|
if (!existsSync17(statusPath)) {
|
|
94655
95505
|
return null;
|
|
94656
95506
|
}
|
|
@@ -94662,7 +95512,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
94662
95512
|
}
|
|
94663
95513
|
}
|
|
94664
95514
|
async function getFeatureSummary(featureName, featureDir) {
|
|
94665
|
-
const prdPath =
|
|
95515
|
+
const prdPath = join44(featureDir, "prd.json");
|
|
94666
95516
|
if (!existsSync17(prdPath)) {
|
|
94667
95517
|
return {
|
|
94668
95518
|
name: featureName,
|
|
@@ -94705,7 +95555,7 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
94705
95555
|
};
|
|
94706
95556
|
}
|
|
94707
95557
|
}
|
|
94708
|
-
const runsDir =
|
|
95558
|
+
const runsDir = join44(featureDir, "runs");
|
|
94709
95559
|
if (existsSync17(runsDir)) {
|
|
94710
95560
|
const runs = readdirSync3(runsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl") && e.name !== "latest.jsonl").map((e) => e.name).sort().reverse();
|
|
94711
95561
|
if (runs.length > 0) {
|
|
@@ -94719,7 +95569,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
94719
95569
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
94720
95570
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
94721
95571
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
94722
|
-
const featuresDir =
|
|
95572
|
+
const featuresDir = join44(outputDir, "features");
|
|
94723
95573
|
if (!existsSync17(featuresDir)) {
|
|
94724
95574
|
console.log(source_default.dim("No features found."));
|
|
94725
95575
|
return;
|
|
@@ -94760,7 +95610,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
94760
95610
|
console.log();
|
|
94761
95611
|
}
|
|
94762
95612
|
}
|
|
94763
|
-
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name,
|
|
95613
|
+
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join44(featuresDir, name))));
|
|
94764
95614
|
console.log(source_default.bold(`\uD83D\uDCCA Features
|
|
94765
95615
|
`));
|
|
94766
95616
|
const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
|
|
@@ -94786,7 +95636,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
94786
95636
|
console.log();
|
|
94787
95637
|
}
|
|
94788
95638
|
async function displayFeatureDetails(featureName, featureDir) {
|
|
94789
|
-
const prdPath =
|
|
95639
|
+
const prdPath = join44(featureDir, "prd.json");
|
|
94790
95640
|
if (!existsSync17(prdPath)) {
|
|
94791
95641
|
console.log(source_default.bold(`
|
|
94792
95642
|
\uD83D\uDCCA ${featureName}
|
|
@@ -94932,7 +95782,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
94932
95782
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
94933
95783
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
94934
95784
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
94935
|
-
featureDir =
|
|
95785
|
+
featureDir = join44(outputDir, "features", options.feature);
|
|
94936
95786
|
} else {
|
|
94937
95787
|
const resolved = resolveProject({ feature: options.feature });
|
|
94938
95788
|
if (!resolved.featureDir) {
|
|
@@ -94952,7 +95802,7 @@ init_errors();
|
|
|
94952
95802
|
init_logger2();
|
|
94953
95803
|
init_runtime();
|
|
94954
95804
|
import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
|
|
94955
|
-
import { basename as basename8, join as
|
|
95805
|
+
import { basename as basename8, join as join45 } from "path";
|
|
94956
95806
|
async function resolveOutputDir2(workdir, override) {
|
|
94957
95807
|
if (override)
|
|
94958
95808
|
return override;
|
|
@@ -94976,7 +95826,7 @@ async function runsListCommand(options) {
|
|
|
94976
95826
|
const logger = getLogger();
|
|
94977
95827
|
const { feature, workdir } = options;
|
|
94978
95828
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
94979
|
-
const runsDir =
|
|
95829
|
+
const runsDir = join45(outputDir, "features", feature, "runs");
|
|
94980
95830
|
if (!existsSync18(runsDir)) {
|
|
94981
95831
|
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
94982
95832
|
return;
|
|
@@ -94988,7 +95838,7 @@ async function runsListCommand(options) {
|
|
|
94988
95838
|
}
|
|
94989
95839
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
94990
95840
|
for (const file3 of files.sort().reverse()) {
|
|
94991
|
-
const logPath =
|
|
95841
|
+
const logPath = join45(runsDir, file3);
|
|
94992
95842
|
const entries = await parseRunLog(logPath);
|
|
94993
95843
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
94994
95844
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
@@ -95015,7 +95865,7 @@ async function runsShowCommand(options) {
|
|
|
95015
95865
|
const logger = getLogger();
|
|
95016
95866
|
const { runId, feature, workdir } = options;
|
|
95017
95867
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
95018
|
-
const logPath =
|
|
95868
|
+
const logPath = join45(outputDir, "features", feature, "runs", `${runId}.jsonl`);
|
|
95019
95869
|
if (!existsSync18(logPath)) {
|
|
95020
95870
|
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
95021
95871
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
@@ -95055,6 +95905,7 @@ async function runsShowCommand(options) {
|
|
|
95055
95905
|
// src/cli/index.ts
|
|
95056
95906
|
init_prompts2();
|
|
95057
95907
|
init_init2();
|
|
95908
|
+
init_setup();
|
|
95058
95909
|
|
|
95059
95910
|
// src/cli/plugins.ts
|
|
95060
95911
|
init_paths();
|
|
@@ -95129,8 +95980,8 @@ init_interaction();
|
|
|
95129
95980
|
init_source();
|
|
95130
95981
|
init_loader();
|
|
95131
95982
|
init_generator2();
|
|
95132
|
-
import { existsSync as
|
|
95133
|
-
import { join as
|
|
95983
|
+
import { existsSync as existsSync24 } from "fs";
|
|
95984
|
+
import { join as join62 } from "path";
|
|
95134
95985
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
95135
95986
|
async function generateCommand(options) {
|
|
95136
95987
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -95173,7 +96024,7 @@ async function generateCommand(options) {
|
|
|
95173
96024
|
return;
|
|
95174
96025
|
}
|
|
95175
96026
|
if (options.package) {
|
|
95176
|
-
const packageDir =
|
|
96027
|
+
const packageDir = join62(workdir, options.package);
|
|
95177
96028
|
if (dryRun) {
|
|
95178
96029
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
95179
96030
|
}
|
|
@@ -95193,10 +96044,10 @@ async function generateCommand(options) {
|
|
|
95193
96044
|
process.exit(1);
|
|
95194
96045
|
return;
|
|
95195
96046
|
}
|
|
95196
|
-
const contextPath = options.context ?
|
|
95197
|
-
const outputDir = options.output ?
|
|
96047
|
+
const contextPath = options.context ? join62(workdir, options.context) : join62(workdir, ".nax/context.md");
|
|
96048
|
+
const outputDir = options.output ? join62(workdir, options.output) : workdir;
|
|
95198
96049
|
const autoInject = !options.noAutoInject;
|
|
95199
|
-
if (!
|
|
96050
|
+
if (!existsSync24(contextPath)) {
|
|
95200
96051
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
95201
96052
|
console.error(source_default.yellow(" Create .nax/context.md first, or run `nax init` to scaffold it."));
|
|
95202
96053
|
process.exit(1);
|
|
@@ -95299,8 +96150,8 @@ async function generateCommand(options) {
|
|
|
95299
96150
|
}
|
|
95300
96151
|
// src/cli/config-display.ts
|
|
95301
96152
|
init_loader();
|
|
95302
|
-
import { existsSync as
|
|
95303
|
-
import { join as
|
|
96153
|
+
import { existsSync as existsSync26 } from "fs";
|
|
96154
|
+
import { join as join64 } from "path";
|
|
95304
96155
|
|
|
95305
96156
|
// src/cli/config-descriptions.ts
|
|
95306
96157
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -95551,10 +96402,10 @@ function deepEqual(a, b) {
|
|
|
95551
96402
|
// src/cli/config-get.ts
|
|
95552
96403
|
init_defaults();
|
|
95553
96404
|
init_loader();
|
|
95554
|
-
import { existsSync as
|
|
95555
|
-
import { join as
|
|
96405
|
+
import { existsSync as existsSync25 } from "fs";
|
|
96406
|
+
import { join as join63 } from "path";
|
|
95556
96407
|
async function loadConfigFile(path18) {
|
|
95557
|
-
if (!
|
|
96408
|
+
if (!existsSync25(path18))
|
|
95558
96409
|
return null;
|
|
95559
96410
|
try {
|
|
95560
96411
|
return await Bun.file(path18).json();
|
|
@@ -95574,7 +96425,7 @@ async function loadProjectConfig() {
|
|
|
95574
96425
|
const projectDir = findProjectDir();
|
|
95575
96426
|
if (!projectDir)
|
|
95576
96427
|
return null;
|
|
95577
|
-
const projectPath =
|
|
96428
|
+
const projectPath = join63(projectDir, "config.json");
|
|
95578
96429
|
return await loadConfigFile(projectPath);
|
|
95579
96430
|
}
|
|
95580
96431
|
|
|
@@ -95634,14 +96485,14 @@ async function configCommand(config2, options = {}) {
|
|
|
95634
96485
|
function determineConfigSources() {
|
|
95635
96486
|
const globalPath = globalConfigPath();
|
|
95636
96487
|
const projectDir = findProjectDir();
|
|
95637
|
-
const projectPath = projectDir ?
|
|
96488
|
+
const projectPath = projectDir ? join64(projectDir, "config.json") : null;
|
|
95638
96489
|
return {
|
|
95639
96490
|
global: fileExists(globalPath) ? globalPath : null,
|
|
95640
96491
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
95641
96492
|
};
|
|
95642
96493
|
}
|
|
95643
96494
|
function fileExists(path18) {
|
|
95644
|
-
return
|
|
96495
|
+
return existsSync26(path18);
|
|
95645
96496
|
}
|
|
95646
96497
|
function displayConfigWithDescriptions(obj, path18, sources, indent = 0) {
|
|
95647
96498
|
const indentStr = " ".repeat(indent);
|
|
@@ -95783,15 +96634,15 @@ init_paths();
|
|
|
95783
96634
|
init_profile();
|
|
95784
96635
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
95785
96636
|
import { readdirSync as readdirSync5 } from "fs";
|
|
95786
|
-
import { join as
|
|
96637
|
+
import { join as join65 } from "path";
|
|
95787
96638
|
var _profileCLIDeps = {
|
|
95788
96639
|
env: process.env
|
|
95789
96640
|
};
|
|
95790
96641
|
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
95791
96642
|
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
95792
96643
|
async function profileListCommand(startDir) {
|
|
95793
|
-
const globalProfilesDir =
|
|
95794
|
-
const projectProfilesDir =
|
|
96644
|
+
const globalProfilesDir = join65(globalConfigDir(), "profiles");
|
|
96645
|
+
const projectProfilesDir = join65(projectConfigDir(startDir), "profiles");
|
|
95795
96646
|
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
95796
96647
|
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
95797
96648
|
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
@@ -95850,7 +96701,7 @@ function maskProfileValues(obj) {
|
|
|
95850
96701
|
return result;
|
|
95851
96702
|
}
|
|
95852
96703
|
async function profileUseCommand(profileName, startDir) {
|
|
95853
|
-
const configPath =
|
|
96704
|
+
const configPath = join65(projectConfigDir(startDir), "config.json");
|
|
95854
96705
|
const configFile = Bun.file(configPath);
|
|
95855
96706
|
let existing = {};
|
|
95856
96707
|
if (await configFile.exists()) {
|
|
@@ -95869,8 +96720,8 @@ async function profileCurrentCommand(startDir) {
|
|
|
95869
96720
|
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
95870
96721
|
}
|
|
95871
96722
|
async function profileCreateCommand(profileName, startDir) {
|
|
95872
|
-
const profilesDir =
|
|
95873
|
-
const profilePath =
|
|
96723
|
+
const profilesDir = join65(projectConfigDir(startDir), "profiles");
|
|
96724
|
+
const profilePath = join65(profilesDir, `${profileName}.json`);
|
|
95874
96725
|
const profileFile = Bun.file(profilePath);
|
|
95875
96726
|
if (await profileFile.exists()) {
|
|
95876
96727
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
@@ -95991,8 +96842,8 @@ async function contextInspectCommand(options) {
|
|
|
95991
96842
|
// src/cli/rules.ts
|
|
95992
96843
|
init_canonical_loader();
|
|
95993
96844
|
init_errors();
|
|
95994
|
-
import { mkdir as
|
|
95995
|
-
import { basename as basename12, join as
|
|
96845
|
+
import { mkdir as mkdir11 } from "fs/promises";
|
|
96846
|
+
import { basename as basename12, join as join66 } from "path";
|
|
95996
96847
|
var _rulesCLIDeps = {
|
|
95997
96848
|
readFile: async (path18) => Bun.file(path18).text(),
|
|
95998
96849
|
writeFile: async (path18, content) => {
|
|
@@ -96001,13 +96852,13 @@ var _rulesCLIDeps = {
|
|
|
96001
96852
|
fileExists: async (path18) => Bun.file(path18).exists(),
|
|
96002
96853
|
globInDir: (dir) => {
|
|
96003
96854
|
try {
|
|
96004
|
-
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) =>
|
|
96855
|
+
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join66(dir, f));
|
|
96005
96856
|
} catch {
|
|
96006
96857
|
return [];
|
|
96007
96858
|
}
|
|
96008
96859
|
},
|
|
96009
96860
|
mkdir: async (path18) => {
|
|
96010
|
-
await
|
|
96861
|
+
await mkdir11(path18, { recursive: true });
|
|
96011
96862
|
},
|
|
96012
96863
|
globCanonicalRuleFiles: (workdir) => {
|
|
96013
96864
|
try {
|
|
@@ -96050,7 +96901,7 @@ ${r.content}`).join(`
|
|
|
96050
96901
|
`);
|
|
96051
96902
|
const shimContent = `${header + body}
|
|
96052
96903
|
`;
|
|
96053
|
-
const shimPath =
|
|
96904
|
+
const shimPath = join66(workdir, shimFileName);
|
|
96054
96905
|
if (options.dryRun) {
|
|
96055
96906
|
console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
|
|
96056
96907
|
return;
|
|
@@ -96079,14 +96930,14 @@ function neutralizeContent(content) {
|
|
|
96079
96930
|
}
|
|
96080
96931
|
async function collectMigrationSources(workdir) {
|
|
96081
96932
|
const sources = [];
|
|
96082
|
-
const claudeMdPath =
|
|
96933
|
+
const claudeMdPath = join66(workdir, "CLAUDE.md");
|
|
96083
96934
|
if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
|
|
96084
96935
|
const content = await _rulesCLIDeps.readFile(claudeMdPath);
|
|
96085
96936
|
if (content.trim()) {
|
|
96086
96937
|
sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
|
|
96087
96938
|
}
|
|
96088
96939
|
}
|
|
96089
|
-
const rulesDir =
|
|
96940
|
+
const rulesDir = join66(workdir, ".claude", "rules");
|
|
96090
96941
|
const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
|
|
96091
96942
|
for (const filePath of ruleFiles) {
|
|
96092
96943
|
try {
|
|
@@ -96106,7 +96957,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96106
96957
|
console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
|
|
96107
96958
|
return;
|
|
96108
96959
|
}
|
|
96109
|
-
const targetDir =
|
|
96960
|
+
const targetDir = join66(workdir, CANONICAL_RULES_DIR);
|
|
96110
96961
|
if (!options.dryRun) {
|
|
96111
96962
|
try {
|
|
96112
96963
|
await _rulesCLIDeps.mkdir(targetDir);
|
|
@@ -96117,7 +96968,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96117
96968
|
let written = 0;
|
|
96118
96969
|
let skipped = 0;
|
|
96119
96970
|
for (const { sourcePath, targetFileName, content } of sources) {
|
|
96120
|
-
const targetPath =
|
|
96971
|
+
const targetPath = join66(targetDir, targetFileName);
|
|
96121
96972
|
if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
|
|
96122
96973
|
console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
|
|
96123
96974
|
skipped++;
|
|
@@ -96156,7 +97007,7 @@ function collectCanonicalRuleRoots(workdir) {
|
|
|
96156
97007
|
const packageRel = normalized.slice(0, idx);
|
|
96157
97008
|
if (!packageRel)
|
|
96158
97009
|
continue;
|
|
96159
|
-
roots.add(
|
|
97010
|
+
roots.add(join66(workdir, packageRel));
|
|
96160
97011
|
}
|
|
96161
97012
|
return [...roots].sort();
|
|
96162
97013
|
}
|
|
@@ -96178,7 +97029,7 @@ init_logger2();
|
|
|
96178
97029
|
init_detect2();
|
|
96179
97030
|
init_workspace();
|
|
96180
97031
|
init_common();
|
|
96181
|
-
import { join as
|
|
97032
|
+
import { join as join67 } from "path";
|
|
96182
97033
|
function resolveEffective(detected, configPatterns) {
|
|
96183
97034
|
if (configPatterns !== undefined)
|
|
96184
97035
|
return "config";
|
|
@@ -96263,7 +97114,7 @@ async function detectCommand(options) {
|
|
|
96263
97114
|
const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96264
97115
|
const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
|
|
96265
97116
|
const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96266
|
-
const pkgConfigPath =
|
|
97117
|
+
const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
|
|
96267
97118
|
const pkgRaw = await loadRawConfig(pkgConfigPath);
|
|
96268
97119
|
const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
|
|
96269
97120
|
const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
|
|
@@ -96317,13 +97168,13 @@ async function detectCommand(options) {
|
|
|
96317
97168
|
if (rootDetected.confidence === "empty") {
|
|
96318
97169
|
console.log(source_default.yellow(" root: skipped (empty detection)"));
|
|
96319
97170
|
} else {
|
|
96320
|
-
const rootConfigPath =
|
|
97171
|
+
const rootConfigPath = join67(workdir, ".nax", "config.json");
|
|
96321
97172
|
try {
|
|
96322
97173
|
const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
|
|
96323
97174
|
if (status === "skipped") {
|
|
96324
97175
|
console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
|
|
96325
97176
|
} else {
|
|
96326
|
-
console.log(source_default.green(` root: ${status} \u2192 ${
|
|
97177
|
+
console.log(source_default.green(` root: ${status} \u2192 ${join67(".nax", "config.json")}`));
|
|
96327
97178
|
}
|
|
96328
97179
|
} catch (err) {
|
|
96329
97180
|
console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
|
|
@@ -96336,13 +97187,13 @@ async function detectCommand(options) {
|
|
|
96336
97187
|
console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
|
|
96337
97188
|
continue;
|
|
96338
97189
|
}
|
|
96339
|
-
const pkgConfigPath =
|
|
97190
|
+
const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
|
|
96340
97191
|
try {
|
|
96341
97192
|
const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
|
|
96342
97193
|
if (status === "skipped") {
|
|
96343
97194
|
console.log(source_default.dim(` ${dir}: skipped (already set)`));
|
|
96344
97195
|
} else {
|
|
96345
|
-
console.log(source_default.green(` ${dir}: ${status} \u2192 ${
|
|
97196
|
+
console.log(source_default.green(` ${dir}: ${status} \u2192 ${join67(".nax", "mono", dir, "config.json")}`));
|
|
96346
97197
|
}
|
|
96347
97198
|
} catch (err) {
|
|
96348
97199
|
console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
|
|
@@ -96359,20 +97210,20 @@ async function detectCommand(options) {
|
|
|
96359
97210
|
|
|
96360
97211
|
// src/commands/logs.ts
|
|
96361
97212
|
init_common();
|
|
96362
|
-
import { existsSync as
|
|
96363
|
-
import { join as
|
|
97213
|
+
import { existsSync as existsSync28 } from "fs";
|
|
97214
|
+
import { join as join71 } from "path";
|
|
96364
97215
|
|
|
96365
97216
|
// src/commands/logs-formatter.ts
|
|
96366
97217
|
init_source();
|
|
96367
97218
|
init_formatter();
|
|
96368
97219
|
import { readdirSync as readdirSync7 } from "fs";
|
|
96369
|
-
import { join as
|
|
97220
|
+
import { join as join70 } from "path";
|
|
96370
97221
|
|
|
96371
97222
|
// src/commands/logs-reader.ts
|
|
96372
97223
|
init_paths3();
|
|
96373
|
-
import { existsSync as
|
|
97224
|
+
import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
|
|
96374
97225
|
import { readdir as readdir4 } from "fs/promises";
|
|
96375
|
-
import { join as
|
|
97226
|
+
import { join as join69 } from "path";
|
|
96376
97227
|
var _logsReaderDeps = {
|
|
96377
97228
|
getRunsDir
|
|
96378
97229
|
};
|
|
@@ -96386,7 +97237,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96386
97237
|
}
|
|
96387
97238
|
let matched = null;
|
|
96388
97239
|
for (const entry of entries) {
|
|
96389
|
-
const metaPath =
|
|
97240
|
+
const metaPath = join69(runsDir, entry, "meta.json");
|
|
96390
97241
|
try {
|
|
96391
97242
|
const meta3 = await Bun.file(metaPath).json();
|
|
96392
97243
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -96398,7 +97249,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96398
97249
|
if (!matched) {
|
|
96399
97250
|
throw new Error(`Run not found in registry: ${runId}`);
|
|
96400
97251
|
}
|
|
96401
|
-
if (!
|
|
97252
|
+
if (!existsSync27(matched.eventsDir)) {
|
|
96402
97253
|
console.log(`Log directory unavailable for run: ${runId}`);
|
|
96403
97254
|
return null;
|
|
96404
97255
|
}
|
|
@@ -96408,14 +97259,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96408
97259
|
return null;
|
|
96409
97260
|
}
|
|
96410
97261
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
96411
|
-
return
|
|
97262
|
+
return join69(matched.eventsDir, specificFile ?? files[0]);
|
|
96412
97263
|
}
|
|
96413
97264
|
async function selectRunFile(runsDir) {
|
|
96414
97265
|
const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
96415
97266
|
if (files.length === 0) {
|
|
96416
97267
|
return null;
|
|
96417
97268
|
}
|
|
96418
|
-
return
|
|
97269
|
+
return join69(runsDir, files[0]);
|
|
96419
97270
|
}
|
|
96420
97271
|
async function extractRunSummary(filePath) {
|
|
96421
97272
|
const file3 = Bun.file(filePath);
|
|
@@ -96501,7 +97352,7 @@ Runs:
|
|
|
96501
97352
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
96502
97353
|
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"));
|
|
96503
97354
|
for (const file3 of files) {
|
|
96504
|
-
const filePath =
|
|
97355
|
+
const filePath = join70(runsDir, file3);
|
|
96505
97356
|
const summary = await extractRunSummary(filePath);
|
|
96506
97357
|
const timestamp = file3.replace(".jsonl", "");
|
|
96507
97358
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -96615,7 +97466,7 @@ async function logsCommand(options) {
|
|
|
96615
97466
|
return;
|
|
96616
97467
|
}
|
|
96617
97468
|
const resolved = resolveProject({ dir: options.dir });
|
|
96618
|
-
const naxDir =
|
|
97469
|
+
const naxDir = join71(resolved.projectDir, ".nax");
|
|
96619
97470
|
const configPath = resolved.configPath;
|
|
96620
97471
|
const configFile = Bun.file(configPath);
|
|
96621
97472
|
const config2 = await configFile.json();
|
|
@@ -96623,9 +97474,9 @@ async function logsCommand(options) {
|
|
|
96623
97474
|
if (!featureName) {
|
|
96624
97475
|
throw new Error("No feature specified in config.json");
|
|
96625
97476
|
}
|
|
96626
|
-
const featureDir =
|
|
96627
|
-
const runsDir =
|
|
96628
|
-
if (!
|
|
97477
|
+
const featureDir = join71(naxDir, "features", featureName);
|
|
97478
|
+
const runsDir = join71(featureDir, "runs");
|
|
97479
|
+
if (!existsSync28(runsDir)) {
|
|
96629
97480
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
96630
97481
|
}
|
|
96631
97482
|
if (options.list) {
|
|
@@ -96649,8 +97500,8 @@ init_config();
|
|
|
96649
97500
|
init_prd();
|
|
96650
97501
|
init_precheck();
|
|
96651
97502
|
init_common();
|
|
96652
|
-
import { existsSync as
|
|
96653
|
-
import { join as
|
|
97503
|
+
import { existsSync as existsSync29 } from "fs";
|
|
97504
|
+
import { join as join72 } from "path";
|
|
96654
97505
|
async function precheckCommand(options) {
|
|
96655
97506
|
const resolved = resolveProject({
|
|
96656
97507
|
dir: options.dir,
|
|
@@ -96672,14 +97523,14 @@ async function precheckCommand(options) {
|
|
|
96672
97523
|
process.exit(1);
|
|
96673
97524
|
}
|
|
96674
97525
|
}
|
|
96675
|
-
const naxDir =
|
|
96676
|
-
const featureDir =
|
|
96677
|
-
const prdPath =
|
|
96678
|
-
if (!
|
|
97526
|
+
const naxDir = join72(resolved.projectDir, ".nax");
|
|
97527
|
+
const featureDir = join72(naxDir, "features", featureName);
|
|
97528
|
+
const prdPath = join72(featureDir, "prd.json");
|
|
97529
|
+
if (!existsSync29(featureDir)) {
|
|
96679
97530
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
96680
97531
|
process.exit(1);
|
|
96681
97532
|
}
|
|
96682
|
-
if (!
|
|
97533
|
+
if (!existsSync29(prdPath)) {
|
|
96683
97534
|
console.error(source_default.red(`Missing prd.json for feature: ${featureName}`));
|
|
96684
97535
|
console.error(source_default.dim(`Run: nax plan -f ${featureName} --from spec.md --auto`));
|
|
96685
97536
|
process.exit(EXIT_CODES.INVALID_PRD);
|
|
@@ -96697,7 +97548,7 @@ async function precheckCommand(options) {
|
|
|
96697
97548
|
init_source();
|
|
96698
97549
|
init_paths3();
|
|
96699
97550
|
import { readdir as readdir5 } from "fs/promises";
|
|
96700
|
-
import { join as
|
|
97551
|
+
import { join as join73 } from "path";
|
|
96701
97552
|
var DEFAULT_LIMIT = 20;
|
|
96702
97553
|
var _runsCmdDeps = {
|
|
96703
97554
|
getRunsDir
|
|
@@ -96752,7 +97603,7 @@ async function runsCommand(options = {}) {
|
|
|
96752
97603
|
}
|
|
96753
97604
|
const rows = [];
|
|
96754
97605
|
for (const entry of entries) {
|
|
96755
|
-
const metaPath =
|
|
97606
|
+
const metaPath = join73(runsDir, entry, "meta.json");
|
|
96756
97607
|
let meta3;
|
|
96757
97608
|
try {
|
|
96758
97609
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -96829,7 +97680,7 @@ async function runsCommand(options = {}) {
|
|
|
96829
97680
|
|
|
96830
97681
|
// src/commands/unlock.ts
|
|
96831
97682
|
init_source();
|
|
96832
|
-
import { join as
|
|
97683
|
+
import { join as join74 } from "path";
|
|
96833
97684
|
function isProcessAlive2(pid) {
|
|
96834
97685
|
try {
|
|
96835
97686
|
process.kill(pid, 0);
|
|
@@ -96844,7 +97695,7 @@ function formatLockAge(ageMs) {
|
|
|
96844
97695
|
}
|
|
96845
97696
|
async function unlockCommand(options) {
|
|
96846
97697
|
const workdir = options.dir ?? process.cwd();
|
|
96847
|
-
const lockPath =
|
|
97698
|
+
const lockPath = join74(workdir, "nax.lock");
|
|
96848
97699
|
const lockFile = Bun.file(lockPath);
|
|
96849
97700
|
const exists = await lockFile.exists();
|
|
96850
97701
|
if (!exists) {
|
|
@@ -97392,6 +98243,7 @@ init_run_regression();
|
|
|
97392
98243
|
|
|
97393
98244
|
// src/execution/index.ts
|
|
97394
98245
|
init_story_orchestrator();
|
|
98246
|
+
init_story_orchestrator_logging();
|
|
97395
98247
|
init_plan_inputs();
|
|
97396
98248
|
init_build_plan_for_strategy();
|
|
97397
98249
|
init_post_run();
|
|
@@ -105268,8 +106120,8 @@ Next: nax generate --package ${options.package}`));
|
|
|
105268
106120
|
}
|
|
105269
106121
|
return;
|
|
105270
106122
|
}
|
|
105271
|
-
const naxDir =
|
|
105272
|
-
if (
|
|
106123
|
+
const naxDir = join88(workdir, ".nax");
|
|
106124
|
+
if (existsSync35(naxDir) && !options.force) {
|
|
105273
106125
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
105274
106126
|
return;
|
|
105275
106127
|
}
|
|
@@ -105297,11 +106149,11 @@ Next: nax generate --package ${options.package}`));
|
|
|
105297
106149
|
}
|
|
105298
106150
|
}
|
|
105299
106151
|
}
|
|
105300
|
-
mkdirSync7(
|
|
105301
|
-
mkdirSync7(
|
|
106152
|
+
mkdirSync7(join88(naxDir, "features"), { recursive: true });
|
|
106153
|
+
mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
|
|
105302
106154
|
const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
|
|
105303
|
-
await Bun.write(
|
|
105304
|
-
await Bun.write(
|
|
106155
|
+
await Bun.write(join88(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
|
|
106156
|
+
await Bun.write(join88(naxDir, "hooks.json"), JSON.stringify({
|
|
105305
106157
|
hooks: {
|
|
105306
106158
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
105307
106159
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -105309,12 +106161,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
105309
106161
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
105310
106162
|
}
|
|
105311
106163
|
}, null, 2));
|
|
105312
|
-
await Bun.write(
|
|
106164
|
+
await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
|
|
105313
106165
|
*.tmp
|
|
105314
106166
|
.paused.json
|
|
105315
106167
|
.nax-verifier-verdict.json
|
|
105316
106168
|
`);
|
|
105317
|
-
await Bun.write(
|
|
106169
|
+
await Bun.write(join88(naxDir, "context.md"), `# Project Context
|
|
105318
106170
|
|
|
105319
106171
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
105320
106172
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -105399,6 +106251,24 @@ Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cu
|
|
|
105399
106251
|
console.log(source_default.dim(`
|
|
105400
106252
|
Next: nax features create <name>`));
|
|
105401
106253
|
});
|
|
106254
|
+
program2.command("setup").description("Analyze repo and generate .nax/config.json via LLM").option("-d, --dir <path>", "Project directory", process.cwd()).option("-a, --agent <name>", "Force a specific agent").option("--fill-scripts", "Add missing quality-gate scripts to package.json", false).option("--dry-run", "Preview planned config without writing files", false).option("--force", "Overwrite existing .nax/config.json", false).action(async (options) => {
|
|
106255
|
+
let workdir;
|
|
106256
|
+
try {
|
|
106257
|
+
workdir = validateDirectory(options.dir);
|
|
106258
|
+
} catch (err) {
|
|
106259
|
+
console.error(source_default.red(`Invalid directory: ${err.message}`));
|
|
106260
|
+
process.exit(1);
|
|
106261
|
+
}
|
|
106262
|
+
const { setupCommand: setupCommand2 } = await Promise.resolve().then(() => (init_setup(), exports_setup));
|
|
106263
|
+
const exitCode = await setupCommand2({
|
|
106264
|
+
dir: workdir,
|
|
106265
|
+
agent: options.agent,
|
|
106266
|
+
fillScripts: options.fillScripts,
|
|
106267
|
+
dryRun: options.dryRun,
|
|
106268
|
+
force: options.force
|
|
106269
|
+
});
|
|
106270
|
+
process.exit(exitCode);
|
|
106271
|
+
});
|
|
105402
106272
|
program2.command("run").description("Run the orchestration loop for a feature").requiredOption("-f, --feature <name>", "Feature name").option("-a, --agent <name>", "Force a specific agent").option("-m, --max-iterations <n>", "Max iterations", "20").option("--dry-run", "Show plan without executing", false).option("--no-context", "Disable context builder (skip file context in prompts)").option("--no-batch", "Disable story batching (execute all stories individually)").option("--parallel <n>", "Max parallel sessions (0=auto, omit=sequential)").option("--plan", "Run plan phase first before execution", false).option("--from <spec-path>", "Path to spec file (required when --plan is used)").option("--one-shot", "Skip interactive planning Q&A, use single LLM call (ACP only)", false).option("--force", "Force overwrite existing prd.json when using --plan", false).option("--headless", "Force headless mode (disable TUI, use pipe mode)", false).option("--verbose", "Enable verbose logging (debug level)", false).option("--quiet", "Quiet mode (warnings and errors only)", false).option("--silent", "Silent mode (errors only)", false).option("--json", "JSON mode (raw JSONL output to stdout)", false).option("-d, --dir <path>", "Working directory", process.cwd()).option("--skip-precheck", "Skip precheck validations (advanced users only)", false).option("--profile <name>", "Profile to use (overrides config.json profile)").action(async (options) => {
|
|
105403
106273
|
let workdir;
|
|
105404
106274
|
try {
|
|
@@ -105411,7 +106281,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105411
106281
|
console.error(source_default.red("Error: --plan requires --from <spec-path>"));
|
|
105412
106282
|
process.exit(1);
|
|
105413
106283
|
}
|
|
105414
|
-
if (options.from && !
|
|
106284
|
+
if (options.from && !existsSync35(options.from)) {
|
|
105415
106285
|
console.error(source_default.red(`Error: File not found: ${options.from} (required with --plan)`));
|
|
105416
106286
|
process.exit(1);
|
|
105417
106287
|
}
|
|
@@ -105444,10 +106314,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105444
106314
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105445
106315
|
process.exit(1);
|
|
105446
106316
|
}
|
|
105447
|
-
const featureDir =
|
|
105448
|
-
const prdPath =
|
|
106317
|
+
const featureDir = join88(naxDir, "features", options.feature);
|
|
106318
|
+
const prdPath = join88(featureDir, "prd.json");
|
|
105449
106319
|
if (options.plan && options.from) {
|
|
105450
|
-
if (
|
|
106320
|
+
if (existsSync35(prdPath) && !options.force) {
|
|
105451
106321
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
105452
106322
|
console.error(source_default.dim(" Use --force to overwrite, or run without --plan to use the existing PRD."));
|
|
105453
106323
|
process.exit(1);
|
|
@@ -105467,10 +106337,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105467
106337
|
}
|
|
105468
106338
|
}
|
|
105469
106339
|
try {
|
|
105470
|
-
const planLogDir =
|
|
106340
|
+
const planLogDir = join88(featureDir, "plan");
|
|
105471
106341
|
mkdirSync7(planLogDir, { recursive: true });
|
|
105472
106342
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105473
|
-
const planLogPath =
|
|
106343
|
+
const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
|
|
105474
106344
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
105475
106345
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
105476
106346
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -105509,17 +106379,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105509
106379
|
process.exit(1);
|
|
105510
106380
|
}
|
|
105511
106381
|
}
|
|
105512
|
-
if (!
|
|
106382
|
+
if (!existsSync35(prdPath)) {
|
|
105513
106383
|
console.error(source_default.red(`Feature "${options.feature}" not found or missing prd.json`));
|
|
105514
106384
|
process.exit(1);
|
|
105515
106385
|
}
|
|
105516
106386
|
resetLogger();
|
|
105517
106387
|
const projectKey = config2.name?.trim() || basename16(workdir);
|
|
105518
106388
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
105519
|
-
const runsDir =
|
|
106389
|
+
const runsDir = join88(outputDir, "features", options.feature, "runs");
|
|
105520
106390
|
mkdirSync7(runsDir, { recursive: true });
|
|
105521
106391
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105522
|
-
const logFilePath =
|
|
106392
|
+
const logFilePath = join88(runsDir, `${runId}.jsonl`);
|
|
105523
106393
|
const isTTY = process.stdout.isTTY ?? false;
|
|
105524
106394
|
const headlessFlag = options.headless ?? false;
|
|
105525
106395
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -105537,7 +106407,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105537
106407
|
config2.agent.default = options.agent;
|
|
105538
106408
|
}
|
|
105539
106409
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
105540
|
-
const globalNaxDir =
|
|
106410
|
+
const globalNaxDir = join88(homedir3(), ".nax");
|
|
105541
106411
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
105542
106412
|
const eventEmitter = new PipelineEventEmitter;
|
|
105543
106413
|
const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
|
|
@@ -105557,12 +106427,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105557
106427
|
events: eventEmitter,
|
|
105558
106428
|
ptyOptions: null,
|
|
105559
106429
|
agentStreamEvents,
|
|
105560
|
-
queueFilePath:
|
|
106430
|
+
queueFilePath: join88(workdir, ".queue.txt")
|
|
105561
106431
|
});
|
|
105562
106432
|
} else {
|
|
105563
106433
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
105564
106434
|
}
|
|
105565
|
-
const statusFilePath =
|
|
106435
|
+
const statusFilePath = join88(outputDir, "status.json");
|
|
105566
106436
|
let parallel;
|
|
105567
106437
|
if (options.parallel !== undefined) {
|
|
105568
106438
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -105589,9 +106459,9 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105589
106459
|
skipPrecheck: options.skipPrecheck ?? false,
|
|
105590
106460
|
agentStreamEvents
|
|
105591
106461
|
});
|
|
105592
|
-
const latestSymlink =
|
|
106462
|
+
const latestSymlink = join88(runsDir, "latest.jsonl");
|
|
105593
106463
|
try {
|
|
105594
|
-
if (
|
|
106464
|
+
if (existsSync35(latestSymlink)) {
|
|
105595
106465
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
105596
106466
|
}
|
|
105597
106467
|
Bun.spawnSync(["ln", "-s", `${runId}.jsonl`, latestSymlink], {
|
|
@@ -105650,9 +106520,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
105650
106520
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105651
106521
|
process.exit(1);
|
|
105652
106522
|
}
|
|
105653
|
-
const featureDir =
|
|
106523
|
+
const featureDir = join88(naxDir, "features", name);
|
|
105654
106524
|
mkdirSync7(featureDir, { recursive: true });
|
|
105655
|
-
await Bun.write(
|
|
106525
|
+
await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
|
|
105656
106526
|
|
|
105657
106527
|
## Overview
|
|
105658
106528
|
|
|
@@ -105685,7 +106555,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
105685
106555
|
|
|
105686
106556
|
<!-- What this feature explicitly does NOT cover. -->
|
|
105687
106557
|
`);
|
|
105688
|
-
await Bun.write(
|
|
106558
|
+
await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
|
|
105689
106559
|
|
|
105690
106560
|
Created: ${new Date().toISOString()}
|
|
105691
106561
|
|
|
@@ -105711,8 +106581,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
105711
106581
|
console.error(source_default.red("nax not initialized."));
|
|
105712
106582
|
process.exit(1);
|
|
105713
106583
|
}
|
|
105714
|
-
const featuresDir =
|
|
105715
|
-
if (!
|
|
106584
|
+
const featuresDir = join88(naxDir, "features");
|
|
106585
|
+
if (!existsSync35(featuresDir)) {
|
|
105716
106586
|
console.log(source_default.dim("No features yet."));
|
|
105717
106587
|
return;
|
|
105718
106588
|
}
|
|
@@ -105726,8 +106596,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
105726
106596
|
Features:
|
|
105727
106597
|
`));
|
|
105728
106598
|
for (const name of entries) {
|
|
105729
|
-
const prdPath =
|
|
105730
|
-
if (
|
|
106599
|
+
const prdPath = join88(featuresDir, name, "prd.json");
|
|
106600
|
+
if (existsSync35(prdPath)) {
|
|
105731
106601
|
const prd = await loadPRD(prdPath);
|
|
105732
106602
|
const c = countStories(prd);
|
|
105733
106603
|
console.log(` ${name} \u2014 ${c.passed}/${c.total} stories done`);
|
|
@@ -105761,10 +106631,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
105761
106631
|
cliOverrides.profile = options.profile;
|
|
105762
106632
|
}
|
|
105763
106633
|
const config2 = await loadConfig(workdir, cliOverrides);
|
|
105764
|
-
const featureLogDir =
|
|
106634
|
+
const featureLogDir = join88(naxDir, "features", options.feature, "plan");
|
|
105765
106635
|
mkdirSync7(featureLogDir, { recursive: true });
|
|
105766
106636
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105767
|
-
const planLogPath =
|
|
106637
|
+
const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
|
|
105768
106638
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
105769
106639
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
105770
106640
|
try {
|