@nathapp/nax 0.69.0 → 0.69.1
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 +1028 -274
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -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({
|
|
@@ -19004,6 +19010,106 @@ var init_loader = __esm(() => {
|
|
|
19004
19010
|
init_schema();
|
|
19005
19011
|
_rootConfigCache = new Map;
|
|
19006
19012
|
});
|
|
19013
|
+
|
|
19014
|
+
// src/config/validate.ts
|
|
19015
|
+
function validateConfig(config2) {
|
|
19016
|
+
const errors3 = [];
|
|
19017
|
+
if (config2.version !== 1) {
|
|
19018
|
+
errors3.push(`Invalid version: expected 1, got ${config2.version}`);
|
|
19019
|
+
}
|
|
19020
|
+
const requiredTiers = ["fast", "balanced", "powerful"];
|
|
19021
|
+
if (!config2.models) {
|
|
19022
|
+
errors3.push("models mapping is required");
|
|
19023
|
+
} else {
|
|
19024
|
+
const defaultAgent = config2.agent?.default ?? "claude";
|
|
19025
|
+
const agentModels = config2.models[defaultAgent];
|
|
19026
|
+
if (!agentModels) {
|
|
19027
|
+
errors3.push(`models.${defaultAgent} is required (default agent has no model map)`);
|
|
19028
|
+
} else {
|
|
19029
|
+
for (const tier of requiredTiers) {
|
|
19030
|
+
const entry = agentModels[tier];
|
|
19031
|
+
if (!entry) {
|
|
19032
|
+
errors3.push(`models.${defaultAgent}.${tier} is required`);
|
|
19033
|
+
} else if (typeof entry === "string") {
|
|
19034
|
+
if (entry.trim() === "") {
|
|
19035
|
+
errors3.push(`models.${defaultAgent}.${tier} must be a non-empty model identifier`);
|
|
19036
|
+
}
|
|
19037
|
+
} else {
|
|
19038
|
+
if (!entry.provider || entry.provider.trim() === "") {
|
|
19039
|
+
errors3.push(`models.${defaultAgent}.${tier}.provider must be non-empty`);
|
|
19040
|
+
}
|
|
19041
|
+
if (!entry.model || entry.model.trim() === "") {
|
|
19042
|
+
errors3.push(`models.${defaultAgent}.${tier}.model must be non-empty`);
|
|
19043
|
+
}
|
|
19044
|
+
}
|
|
19045
|
+
}
|
|
19046
|
+
}
|
|
19047
|
+
}
|
|
19048
|
+
if (config2.execution.maxIterations <= 0) {
|
|
19049
|
+
errors3.push(`maxIterations must be > 0, got ${config2.execution.maxIterations}`);
|
|
19050
|
+
}
|
|
19051
|
+
if (config2.execution.costLimit <= 0) {
|
|
19052
|
+
errors3.push(`costLimit must be > 0, got ${config2.execution.costLimit}`);
|
|
19053
|
+
}
|
|
19054
|
+
if (config2.execution.sessionTimeoutSeconds <= 0) {
|
|
19055
|
+
errors3.push(`sessionTimeoutSeconds must be > 0, got ${config2.execution.sessionTimeoutSeconds}`);
|
|
19056
|
+
}
|
|
19057
|
+
const agentDefault = config2.agent?.default;
|
|
19058
|
+
if (!agentDefault || agentDefault.trim() === "") {
|
|
19059
|
+
errors3.push("agent.default must be non-empty");
|
|
19060
|
+
}
|
|
19061
|
+
if (!config2.autoMode.escalation.tierOrder || config2.autoMode.escalation.tierOrder.length === 0) {
|
|
19062
|
+
errors3.push("escalation.tierOrder must have at least one tier");
|
|
19063
|
+
} else {
|
|
19064
|
+
for (const tc of config2.autoMode.escalation.tierOrder) {
|
|
19065
|
+
if (tc.attempts < 1 || tc.attempts > 20) {
|
|
19066
|
+
errors3.push(`escalation.tierOrder: tier "${tc.tier}" attempts must be 1-20, got ${tc.attempts}`);
|
|
19067
|
+
}
|
|
19068
|
+
}
|
|
19069
|
+
}
|
|
19070
|
+
if (config2.models && config2.agent?.fallback?.map) {
|
|
19071
|
+
const modelKeys = Object.keys(config2.models);
|
|
19072
|
+
const fallbackAgents = new Set;
|
|
19073
|
+
for (const [primary, candidates] of Object.entries(config2.agent.fallback.map)) {
|
|
19074
|
+
fallbackAgents.add(primary);
|
|
19075
|
+
for (const c of candidates)
|
|
19076
|
+
fallbackAgents.add(c);
|
|
19077
|
+
}
|
|
19078
|
+
for (const agent of fallbackAgents) {
|
|
19079
|
+
if (!modelKeys.includes(agent)) {
|
|
19080
|
+
errors3.push(`agent.fallback.map: agent "${agent}" is not a key in models (available: ${modelKeys.join(", ")})`);
|
|
19081
|
+
} else {
|
|
19082
|
+
for (const tier of requiredTiers) {
|
|
19083
|
+
if (!config2.models[agent]?.[tier]) {
|
|
19084
|
+
errors3.push(`models.${agent}.${tier} is required (fallback agent "${agent}" in agent.fallback.map)`);
|
|
19085
|
+
}
|
|
19086
|
+
}
|
|
19087
|
+
}
|
|
19088
|
+
}
|
|
19089
|
+
}
|
|
19090
|
+
if (config2.models && config2.autoMode?.escalation?.tierOrder) {
|
|
19091
|
+
const modelKeys = Object.keys(config2.models);
|
|
19092
|
+
for (const tc of config2.autoMode.escalation.tierOrder) {
|
|
19093
|
+
if (tc.agent !== undefined && !modelKeys.includes(tc.agent)) {
|
|
19094
|
+
errors3.push(`autoMode.escalation.tierOrder: tier "${tc.tier}" agent "${tc.agent}" is not a key in models (available: ${modelKeys.join(", ")})`);
|
|
19095
|
+
}
|
|
19096
|
+
}
|
|
19097
|
+
}
|
|
19098
|
+
const defaultAgentKey = config2.agent?.default ?? "claude";
|
|
19099
|
+
const configuredTiers = Object.keys(config2.models[defaultAgentKey] ?? {});
|
|
19100
|
+
const complexities = ["simple", "medium", "complex", "expert"];
|
|
19101
|
+
for (const complexity of complexities) {
|
|
19102
|
+
const tier = config2.autoMode.complexityRouting[complexity];
|
|
19103
|
+
if (!configuredTiers.includes(tier)) {
|
|
19104
|
+
errors3.push(`complexityRouting.${complexity} must be one of: ${configuredTiers.join(", ")} (got '${tier}')`);
|
|
19105
|
+
}
|
|
19106
|
+
}
|
|
19107
|
+
return {
|
|
19108
|
+
valid: errors3.length === 0,
|
|
19109
|
+
errors: errors3
|
|
19110
|
+
};
|
|
19111
|
+
}
|
|
19112
|
+
|
|
19007
19113
|
// src/config/selector.ts
|
|
19008
19114
|
function pickSelector(name, ...keys) {
|
|
19009
19115
|
return {
|
|
@@ -19286,6 +19392,73 @@ var init_test_strategy = __esm(() => {
|
|
|
19286
19392
|
});
|
|
19287
19393
|
|
|
19288
19394
|
// src/config/index.ts
|
|
19395
|
+
var exports_config = {};
|
|
19396
|
+
__export(exports_config, {
|
|
19397
|
+
verifyConfigSelector: () => verifyConfigSelector,
|
|
19398
|
+
validateFilePath: () => validateFilePath,
|
|
19399
|
+
validateDirectory: () => validateDirectory,
|
|
19400
|
+
validateConfig: () => validateConfig,
|
|
19401
|
+
testPatternConfigSelector: () => testPatternConfigSelector,
|
|
19402
|
+
tddConfigSelector: () => tddConfigSelector,
|
|
19403
|
+
routingConfigSelector: () => routingConfigSelector,
|
|
19404
|
+
reviewConfigSelector: () => reviewConfigSelector,
|
|
19405
|
+
resolveTestStrategy: () => resolveTestStrategy,
|
|
19406
|
+
resolveProfileName: () => resolveProfileName,
|
|
19407
|
+
resolveModelForAgent: () => resolveModelForAgent,
|
|
19408
|
+
resolveModel: () => resolveModel,
|
|
19409
|
+
resolveConfiguredModel: () => resolveConfiguredModel,
|
|
19410
|
+
reshapeSelector: () => reshapeSelector,
|
|
19411
|
+
rectifyConfigSelector: () => rectifyConfigSelector,
|
|
19412
|
+
rectificationGateConfigSelector: () => rectificationGateConfigSelector,
|
|
19413
|
+
qualityConfigSelector: () => qualityConfigSelector,
|
|
19414
|
+
promptLoaderConfigSelector: () => promptLoaderConfigSelector,
|
|
19415
|
+
projectConfigDir: () => projectConfigDir,
|
|
19416
|
+
precheckConfigSelector: () => precheckConfigSelector,
|
|
19417
|
+
planConfigSelector: () => planConfigSelector,
|
|
19418
|
+
pickSelector: () => pickSelector,
|
|
19419
|
+
loadProfileEnv: () => loadProfileEnv,
|
|
19420
|
+
loadProfile: () => loadProfile,
|
|
19421
|
+
loadConfigForWorkdir: () => loadConfigForWorkdir,
|
|
19422
|
+
loadConfig: () => loadConfig,
|
|
19423
|
+
llmRoutingConfigSelector: () => llmRoutingConfigSelector,
|
|
19424
|
+
listProfiles: () => listProfiles,
|
|
19425
|
+
isWithinDirectory: () => isWithinDirectory,
|
|
19426
|
+
isThreeSessionStrategy: () => isThreeSessionStrategy,
|
|
19427
|
+
isSingleSessionTestOwningStrategy: () => isSingleSessionTestOwningStrategy,
|
|
19428
|
+
interactionConfigSelector: () => interactionConfigSelector,
|
|
19429
|
+
globalConfigPath: () => globalConfigPath,
|
|
19430
|
+
globalConfigDir: () => globalConfigDir,
|
|
19431
|
+
getAcQualityRules: () => getAcQualityRules,
|
|
19432
|
+
findProjectDir: () => findProjectDir,
|
|
19433
|
+
executionGatesConfigSelector: () => executionGatesConfigSelector,
|
|
19434
|
+
deepMergeConfig: () => deepMergeConfig,
|
|
19435
|
+
decomposeConfigSelector: () => decomposeConfigSelector,
|
|
19436
|
+
debateConfigSelector: () => debateConfigSelector,
|
|
19437
|
+
createConfigLoader: () => createConfigLoader,
|
|
19438
|
+
contextToolRuntimeConfigSelector: () => contextToolRuntimeConfigSelector,
|
|
19439
|
+
autofixConfigSelector: () => autofixConfigSelector,
|
|
19440
|
+
agentManagerConfigSelector: () => agentManagerConfigSelector,
|
|
19441
|
+
acceptanceGenConfigSelector: () => acceptanceGenConfigSelector,
|
|
19442
|
+
acceptanceFixConfigSelector: () => acceptanceFixConfigSelector,
|
|
19443
|
+
acceptanceConfigSelector: () => acceptanceConfigSelector,
|
|
19444
|
+
VALID_TEST_STRATEGIES: () => VALID_TEST_STRATEGIES,
|
|
19445
|
+
THREE_SESSION_STRATEGIES: () => THREE_SESSION_STRATEGIES,
|
|
19446
|
+
TEST_STRATEGY_GUIDE: () => TEST_STRATEGY_GUIDE,
|
|
19447
|
+
SPEC_ANCHOR_RULES: () => SPEC_ANCHOR_RULES,
|
|
19448
|
+
SINGLE_SESSION_TEST_OWNING_STRATEGIES: () => SINGLE_SESSION_TEST_OWNING_STRATEGIES,
|
|
19449
|
+
PlanConfigSchema: () => PlanConfigSchema,
|
|
19450
|
+
NaxConfigSchema: () => NaxConfigSchema,
|
|
19451
|
+
ModelTierSchema: () => ModelTierSchema,
|
|
19452
|
+
MAX_DIRECTORY_DEPTH: () => MAX_DIRECTORY_DEPTH,
|
|
19453
|
+
GROUPING_RULES: () => GROUPING_RULES,
|
|
19454
|
+
DebateConfigSchema: () => DebateConfigSchema,
|
|
19455
|
+
DESCRIPTION_QUALITY_RULES: () => DESCRIPTION_QUALITY_RULES,
|
|
19456
|
+
DEFAULT_CONFIG: () => DEFAULT_CONFIG,
|
|
19457
|
+
ConfiguredModelSchema: () => ConfiguredModelSchema,
|
|
19458
|
+
COMPLEXITY_GUIDE: () => COMPLEXITY_GUIDE,
|
|
19459
|
+
AcceptanceConfigSchema: () => AcceptanceConfigSchema,
|
|
19460
|
+
AC_QUALITY_RULES: () => AC_QUALITY_RULES
|
|
19461
|
+
});
|
|
19289
19462
|
var init_config = __esm(() => {
|
|
19290
19463
|
init_schema();
|
|
19291
19464
|
init_schemas_model();
|
|
@@ -32641,12 +32814,14 @@ var init_adversarial_review = __esm(() => {
|
|
|
32641
32814
|
});
|
|
32642
32815
|
const { accepted, dropped } = filterByAcQuote(substantiated, input.story.acceptanceCriteria);
|
|
32643
32816
|
const blocking = accepted.filter((f) => isBlockingSeverity(f.severity, threshold));
|
|
32817
|
+
const advisory = accepted.filter((f) => !isBlockingSeverity(f.severity, threshold));
|
|
32644
32818
|
const passed = parsed.passed && blocking.length === 0;
|
|
32645
32819
|
return {
|
|
32646
32820
|
...parsed,
|
|
32647
32821
|
passed,
|
|
32648
32822
|
findings: accepted,
|
|
32649
32823
|
normalizedFindings: toAdversarialReviewFindings(blocking),
|
|
32824
|
+
advisoryFindings: toAdversarialReviewFindings(advisory),
|
|
32650
32825
|
acDropped: dropped
|
|
32651
32826
|
};
|
|
32652
32827
|
}
|
|
@@ -38840,6 +39015,159 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
|
|
|
38840
39015
|
var defaultFileExists = (p) => Bun.file(p).exists();
|
|
38841
39016
|
var init_validate_mock_structure_files = () => {};
|
|
38842
39017
|
|
|
39018
|
+
// src/prompts/builders/setup-builder.ts
|
|
39019
|
+
function formatPackageFacts(pkg) {
|
|
39020
|
+
const lines = [` Package: ${pkg.relativeDir || "(root)"}`];
|
|
39021
|
+
if (pkg.testFramework)
|
|
39022
|
+
lines.push(` Test framework: ${pkg.testFramework}`);
|
|
39023
|
+
if (pkg.testFilePatterns.length > 0) {
|
|
39024
|
+
lines.push(` Test patterns: ${pkg.testFilePatterns.slice(0, 4).join(", ")}`);
|
|
39025
|
+
}
|
|
39026
|
+
if (pkg.missingScripts.length > 0) {
|
|
39027
|
+
lines.push(` Missing scripts: ${pkg.missingScripts.join(", ")}`);
|
|
39028
|
+
} else {
|
|
39029
|
+
lines.push(" Missing scripts: (none \u2014 all canonical scripts present)");
|
|
39030
|
+
}
|
|
39031
|
+
return lines.join(`
|
|
39032
|
+
`);
|
|
39033
|
+
}
|
|
39034
|
+
|
|
39035
|
+
class SetupPromptBuilder {
|
|
39036
|
+
build(analysis) {
|
|
39037
|
+
const isMonoRepo = analysis.shape === "mono";
|
|
39038
|
+
const packageFacts = analysis.packages.map(formatPackageFacts).join(`
|
|
39039
|
+
|
|
39040
|
+
`);
|
|
39041
|
+
return {
|
|
39042
|
+
role: {
|
|
39043
|
+
id: "setup-role",
|
|
39044
|
+
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.",
|
|
39045
|
+
overridable: false
|
|
39046
|
+
},
|
|
39047
|
+
task: {
|
|
39048
|
+
id: "setup-task",
|
|
39049
|
+
content: [
|
|
39050
|
+
`Generate a nax configuration for this ${isMonoRepo ? "monorepo" : "single-package"} repository.`,
|
|
39051
|
+
"",
|
|
39052
|
+
"Repository facts:",
|
|
39053
|
+
`- Shape: ${analysis.shape}`,
|
|
39054
|
+
`- Package manager: ${analysis.pmRunPrefix}`,
|
|
39055
|
+
`- DLX runner: ${analysis.pmDlx}`,
|
|
39056
|
+
`- Orchestrator: ${analysis.orchestrator}`,
|
|
39057
|
+
"",
|
|
39058
|
+
"Per-package facts:",
|
|
39059
|
+
packageFacts,
|
|
39060
|
+
"",
|
|
39061
|
+
"IMPORTANT: A script listed under 'Missing scripts' does NOT exist in that package's package.json.",
|
|
39062
|
+
"Do NOT include commands for missing scripts in quality.commands.",
|
|
39063
|
+
"",
|
|
39064
|
+
isMonoRepo ? 'Respond with a JSON code block: { "config": <root NaxConfig>, "monoConfigs": [{ "relativeDir": "<pkg>", "config": <partial NaxConfig> }] }' : 'Respond with a JSON code block: { "config": <NaxConfig> }'
|
|
39065
|
+
].join(`
|
|
39066
|
+
`),
|
|
39067
|
+
overridable: false
|
|
39068
|
+
}
|
|
39069
|
+
};
|
|
39070
|
+
}
|
|
39071
|
+
}
|
|
39072
|
+
|
|
39073
|
+
// src/operations/setup-generate.ts
|
|
39074
|
+
function throwSetupPlanError(message) {
|
|
39075
|
+
throw new NaxError(`[setup-generate] ${message}`, "SETUP_PLAN_INVALID");
|
|
39076
|
+
}
|
|
39077
|
+
function validateSetupOutput(parsed) {
|
|
39078
|
+
const config2 = parsed?.config ?? parsed;
|
|
39079
|
+
return NaxConfigSchema.safeParse(config2).success;
|
|
39080
|
+
}
|
|
39081
|
+
function crossCheckCommands(config2, analysis) {
|
|
39082
|
+
const rootPkg = analysis.packages.find((p) => p.relativeDir === "") ?? analysis.packages[0];
|
|
39083
|
+
const missing = new Set(rootPkg?.missingScripts ?? []);
|
|
39084
|
+
if (missing.size === 0)
|
|
39085
|
+
return { config: config2, gaps: [] };
|
|
39086
|
+
const quality = config2.quality;
|
|
39087
|
+
if (!quality?.commands)
|
|
39088
|
+
return { config: config2, gaps: [] };
|
|
39089
|
+
const gaps = [];
|
|
39090
|
+
const commands = {};
|
|
39091
|
+
for (const [key, value] of Object.entries(quality.commands)) {
|
|
39092
|
+
if (missing.has(key)) {
|
|
39093
|
+
gaps.push(`Script "${key}" in quality.commands.${key} is missing from package.json`);
|
|
39094
|
+
} else {
|
|
39095
|
+
commands[key] = value;
|
|
39096
|
+
}
|
|
39097
|
+
}
|
|
39098
|
+
if (gaps.length === 0)
|
|
39099
|
+
return { config: config2, gaps: [] };
|
|
39100
|
+
return { config: { ...config2, quality: { ...quality, commands } }, gaps };
|
|
39101
|
+
}
|
|
39102
|
+
function buildMonoConfigs(parsed, analysis) {
|
|
39103
|
+
if (analysis.shape !== "mono")
|
|
39104
|
+
return [];
|
|
39105
|
+
const rawMonoConfigs = parsed.monoConfigs ?? [];
|
|
39106
|
+
return analysis.packages.map((pkg) => {
|
|
39107
|
+
const rawMono = rawMonoConfigs.find((m) => m.relativeDir === pkg.relativeDir);
|
|
39108
|
+
const validated = rawMono ? NaxConfigSchema.safeParse(rawMono.config) : undefined;
|
|
39109
|
+
if (rawMono && !validated?.success) {
|
|
39110
|
+
getLogger().warn("setup-generate", "Per-package config failed schema validation \u2014 using empty config", {
|
|
39111
|
+
storyId: "setup",
|
|
39112
|
+
relativeDir: pkg.relativeDir
|
|
39113
|
+
});
|
|
39114
|
+
}
|
|
39115
|
+
const config2 = (validated?.success ? validated.data : undefined) ?? {};
|
|
39116
|
+
return { relativeDir: pkg.relativeDir, config: config2 };
|
|
39117
|
+
});
|
|
39118
|
+
}
|
|
39119
|
+
var MAX_SETUP_LLM_ATTEMPTS = 2, SetupPlanError, setupRetryStrategy, setupGenerateOp;
|
|
39120
|
+
var init_setup_generate = __esm(() => {
|
|
39121
|
+
init_retry();
|
|
39122
|
+
init_schemas3();
|
|
39123
|
+
init_errors();
|
|
39124
|
+
init_logger2();
|
|
39125
|
+
SetupPlanError = class SetupPlanError extends ParseValidationError {
|
|
39126
|
+
code = "SETUP_PLAN_INVALID";
|
|
39127
|
+
};
|
|
39128
|
+
setupRetryStrategy = makeParseRetryStrategy({
|
|
39129
|
+
reviewerKind: "setup-generate",
|
|
39130
|
+
maxAttempts: MAX_SETUP_LLM_ATTEMPTS,
|
|
39131
|
+
validate: validateSetupOutput,
|
|
39132
|
+
prompts: {
|
|
39133
|
+
invalid: () => "The response was not valid JSON or failed schema validation. Please respond with a valid JSON object.",
|
|
39134
|
+
truncated: () => "The response was truncated. Please provide the complete JSON config."
|
|
39135
|
+
},
|
|
39136
|
+
exhaustedFallback: () => {
|
|
39137
|
+
throwSetupPlanError("LLM failed to generate a valid setup plan after exhausting retries");
|
|
39138
|
+
}
|
|
39139
|
+
});
|
|
39140
|
+
setupGenerateOp = {
|
|
39141
|
+
kind: "run",
|
|
39142
|
+
name: "setup-generate",
|
|
39143
|
+
stage: "setup",
|
|
39144
|
+
session: { role: "setup", lifetime: "fresh" },
|
|
39145
|
+
noFallback: true,
|
|
39146
|
+
config: ["quality"],
|
|
39147
|
+
retry: setupRetryStrategy,
|
|
39148
|
+
build(analysis, _ctx) {
|
|
39149
|
+
return new SetupPromptBuilder().build(analysis);
|
|
39150
|
+
},
|
|
39151
|
+
parse(output, analysis, _ctx) {
|
|
39152
|
+
let parsedRaw;
|
|
39153
|
+
try {
|
|
39154
|
+
parsedRaw = parseLLMJson(output);
|
|
39155
|
+
} catch {
|
|
39156
|
+
throw new SetupPlanError("Failed to parse LLM output as JSON");
|
|
39157
|
+
}
|
|
39158
|
+
const parsedObj = parsedRaw;
|
|
39159
|
+
const rawConfig = parsedObj?.config ?? parsedRaw;
|
|
39160
|
+
const result = NaxConfigSchema.safeParse(rawConfig);
|
|
39161
|
+
if (!result.success) {
|
|
39162
|
+
throw new SetupPlanError(`Config failed NaxConfigSchema: ${result.error.message}`);
|
|
39163
|
+
}
|
|
39164
|
+
const { config: config2, gaps } = crossCheckCommands(result.data, analysis);
|
|
39165
|
+
const monoConfigs = buildMonoConfigs(parsedObj ?? { config: rawConfig }, analysis);
|
|
39166
|
+
return { config: config2, monoConfigs, gaps };
|
|
39167
|
+
}
|
|
39168
|
+
};
|
|
39169
|
+
});
|
|
39170
|
+
|
|
38843
39171
|
// src/operations/declaration-sink.ts
|
|
38844
39172
|
function makeDeclarationSink() {
|
|
38845
39173
|
return { testEdits: [], mockHandoffs: [] };
|
|
@@ -39441,6 +39769,7 @@ var init_operations = __esm(() => {
|
|
|
39441
39769
|
init_autofix_test_writer_strategy();
|
|
39442
39770
|
init_apply_test_edit_declarations();
|
|
39443
39771
|
init_validate_mock_structure_files();
|
|
39772
|
+
init_setup_generate();
|
|
39444
39773
|
init__finding_to_check();
|
|
39445
39774
|
init_mechanical_lintfix_strategy();
|
|
39446
39775
|
init_mechanical_formatfix_strategy();
|
|
@@ -44655,7 +44984,8 @@ var init_session_role = __esm(() => {
|
|
|
44655
44984
|
"fix-gen",
|
|
44656
44985
|
"auto",
|
|
44657
44986
|
"synthesis",
|
|
44658
|
-
"judge"
|
|
44987
|
+
"judge",
|
|
44988
|
+
"setup"
|
|
44659
44989
|
];
|
|
44660
44990
|
});
|
|
44661
44991
|
|
|
@@ -53532,6 +53862,108 @@ var init_context2 = __esm(() => {
|
|
|
53532
53862
|
};
|
|
53533
53863
|
});
|
|
53534
53864
|
|
|
53865
|
+
// src/tdd/rollback.ts
|
|
53866
|
+
async function rollbackToRef(workdir, ref) {
|
|
53867
|
+
const logger = getLogger();
|
|
53868
|
+
logger.warn("tdd", "Rolling back git changes", { ref });
|
|
53869
|
+
const resetProc = _rollbackDeps.spawn(["git", "reset", "--hard", ref], {
|
|
53870
|
+
cwd: workdir,
|
|
53871
|
+
stdout: "pipe",
|
|
53872
|
+
stderr: "pipe"
|
|
53873
|
+
});
|
|
53874
|
+
const exitCode = await resetProc.exited;
|
|
53875
|
+
if (exitCode !== 0) {
|
|
53876
|
+
const stderr = await new Response(resetProc.stderr).text();
|
|
53877
|
+
logger.error("tdd", "Failed to rollback git changes", { ref, stderr });
|
|
53878
|
+
throw new Error(`Git rollback failed: ${stderr}`);
|
|
53879
|
+
}
|
|
53880
|
+
const cleanProc = _rollbackDeps.spawn(["git", "clean", "-fd"], {
|
|
53881
|
+
cwd: workdir,
|
|
53882
|
+
stdout: "pipe",
|
|
53883
|
+
stderr: "pipe"
|
|
53884
|
+
});
|
|
53885
|
+
const cleanExitCode = await cleanProc.exited;
|
|
53886
|
+
if (cleanExitCode !== 0) {
|
|
53887
|
+
const stderr = await new Response(cleanProc.stderr).text();
|
|
53888
|
+
logger.warn("tdd", "Failed to clean untracked files", { stderr });
|
|
53889
|
+
}
|
|
53890
|
+
logger.info("tdd", "Successfully rolled back git changes", { ref });
|
|
53891
|
+
}
|
|
53892
|
+
async function captureSnapshotRef(workdir, storyId) {
|
|
53893
|
+
await _rollbackDeps.autoCommitIfDirty(workdir, "non-blocking-fix-snapshot", "snapshot", storyId);
|
|
53894
|
+
const proc = _rollbackDeps.spawn(["git", "rev-parse", "HEAD"], { cwd: workdir, stdout: "pipe", stderr: "pipe" });
|
|
53895
|
+
const sha = (await new Response(proc.stdout).text()).trim();
|
|
53896
|
+
const exitCode = await proc.exited;
|
|
53897
|
+
if (exitCode !== 0) {
|
|
53898
|
+
throw new NaxError("git rev-parse HEAD failed in non-blocking-fix snapshot", "SNAPSHOT_REF_FAILED", {
|
|
53899
|
+
storyId,
|
|
53900
|
+
workdir,
|
|
53901
|
+
stage: "non-blocking-fix-snapshot"
|
|
53902
|
+
});
|
|
53903
|
+
}
|
|
53904
|
+
return sha;
|
|
53905
|
+
}
|
|
53906
|
+
var _rollbackDeps;
|
|
53907
|
+
var init_rollback = __esm(() => {
|
|
53908
|
+
init_errors();
|
|
53909
|
+
init_logger2();
|
|
53910
|
+
init_git();
|
|
53911
|
+
_rollbackDeps = {
|
|
53912
|
+
spawn: Bun.spawn,
|
|
53913
|
+
autoCommitIfDirty
|
|
53914
|
+
};
|
|
53915
|
+
});
|
|
53916
|
+
|
|
53917
|
+
// src/execution/non-blocking-fix.ts
|
|
53918
|
+
function shouldRunNonBlockingFix(cfg, advisoryCount) {
|
|
53919
|
+
return cfg?.enabled === true && advisoryCount > 0;
|
|
53920
|
+
}
|
|
53921
|
+
function nonBlockingExcludePhases() {
|
|
53922
|
+
return REVIEW_PHASE_KINDS;
|
|
53923
|
+
}
|
|
53924
|
+
function nonBlockingExtraPhases(cfg) {
|
|
53925
|
+
return cfg.scope === "both" && cfg.verifierGuard ? ["verifier"] : [];
|
|
53926
|
+
}
|
|
53927
|
+
async function runNonBlockingFix(args, _deps = DEFAULT_DEPS) {
|
|
53928
|
+
const logger = getSafeLogger();
|
|
53929
|
+
if (!shouldRunNonBlockingFix(args.cfg, args.advisoryFindings.length)) {
|
|
53930
|
+
return { ran: false, kept: false, restored: false };
|
|
53931
|
+
}
|
|
53932
|
+
const phaseOutputsSnapshot = { ...args.phaseOutputs };
|
|
53933
|
+
const restoreRef = await _deps.captureSnapshotRef(args.workdir, args.storyId);
|
|
53934
|
+
const maxAttempts = 1 + args.cfg.regressionAttempts;
|
|
53935
|
+
let exhausted = false;
|
|
53936
|
+
try {
|
|
53937
|
+
const result = await args.runRectify(maxAttempts);
|
|
53938
|
+
exhausted = result.rectificationExhausted === true;
|
|
53939
|
+
} catch (err) {
|
|
53940
|
+
logger?.warn("non-blocking-fix", "best-effort pass threw \u2014 restoring", {
|
|
53941
|
+
storyId: args.storyId,
|
|
53942
|
+
error: err instanceof Error ? err.message : String(err)
|
|
53943
|
+
});
|
|
53944
|
+
exhausted = true;
|
|
53945
|
+
}
|
|
53946
|
+
if (!exhausted) {
|
|
53947
|
+
logger?.info("non-blocking-fix", "best-effort fix kept", { storyId: args.storyId });
|
|
53948
|
+
return { ran: true, kept: true, restored: false };
|
|
53949
|
+
}
|
|
53950
|
+
await _deps.rollbackToRef(args.workdir, restoreRef);
|
|
53951
|
+
for (const key of Object.keys(args.phaseOutputs))
|
|
53952
|
+
delete args.phaseOutputs[key];
|
|
53953
|
+
Object.assign(args.phaseOutputs, phaseOutputsSnapshot);
|
|
53954
|
+
logger?.info("non-blocking-fix", "best-effort fix exhausted \u2014 restored to adversarial-passed", {
|
|
53955
|
+
storyId: args.storyId
|
|
53956
|
+
});
|
|
53957
|
+
return { ran: true, kept: false, restored: true };
|
|
53958
|
+
}
|
|
53959
|
+
var REVIEW_PHASE_KINDS, DEFAULT_DEPS;
|
|
53960
|
+
var init_non_blocking_fix = __esm(() => {
|
|
53961
|
+
init_logger2();
|
|
53962
|
+
init_rollback();
|
|
53963
|
+
REVIEW_PHASE_KINDS = ["semantic-review", "adversarial-review"];
|
|
53964
|
+
DEFAULT_DEPS = { captureSnapshotRef, rollbackToRef };
|
|
53965
|
+
});
|
|
53966
|
+
|
|
53535
53967
|
// src/execution/story-orchestrator.ts
|
|
53536
53968
|
async function refreshReviewInputForDispatch(opName, input) {
|
|
53537
53969
|
if (opName !== "semantic-review" && opName !== "adversarial-review")
|
|
@@ -53971,16 +54403,17 @@ function withIncreasingFailuresBail(strategies, enabled, consecutiveIncreases) {
|
|
|
53971
54403
|
}
|
|
53972
54404
|
}));
|
|
53973
54405
|
}
|
|
53974
|
-
async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
54406
|
+
async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides) {
|
|
53975
54407
|
const rectification = state.rectification;
|
|
53976
|
-
const
|
|
54408
|
+
const baseValidationPhases = collectRectificationPhases(state);
|
|
54409
|
+
const validationPhases = overrides?.excludePhaseKinds ? baseValidationPhases.filter((p) => !overrides.excludePhaseKinds?.includes(p.kind)) : baseValidationPhases;
|
|
53977
54410
|
if (!rectification || validationPhases.length === 0) {
|
|
53978
54411
|
return {};
|
|
53979
54412
|
}
|
|
53980
54413
|
if (ctx.runtime.signal?.aborted) {
|
|
53981
54414
|
return {};
|
|
53982
54415
|
}
|
|
53983
|
-
const initialFindings = gatherRectificationFindings(phaseOutputs, validationPhases, state);
|
|
54416
|
+
const initialFindings = overrides?.initialFindings ? [...overrides.initialFindings] : gatherRectificationFindings(phaseOutputs, validationPhases, state);
|
|
53984
54417
|
if (initialFindings.length === 0) {
|
|
53985
54418
|
return {};
|
|
53986
54419
|
}
|
|
@@ -53995,14 +54428,16 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
|
53995
54428
|
const cycle = {
|
|
53996
54429
|
findings: [...initialFindings],
|
|
53997
54430
|
iterations: [],
|
|
53998
|
-
strategies: withIncreasingFailuresBail(rectification.strategies, rectification.abortOnIncreasingFailures, rectification.consecutiveIncreasesToBail ?? 1),
|
|
53999
|
-
config: { maxAttemptsTotal: rectification.maxAttempts, validatorRetries: 1 },
|
|
54431
|
+
strategies: withIncreasingFailuresBail(overrides?.strategies ?? rectification.strategies, rectification.abortOnIncreasingFailures, rectification.consecutiveIncreasesToBail ?? 1),
|
|
54432
|
+
config: { maxAttemptsTotal: overrides?.maxAttempts ?? rectification.maxAttempts, validatorRetries: 1 },
|
|
54000
54433
|
validate: async (_validateCtx, opts) => {
|
|
54001
54434
|
if (ctx.runtime.signal?.aborted)
|
|
54002
54435
|
return { findings: [], shortCircuited: false };
|
|
54003
54436
|
const lite = (opts?.mode ?? "full") === "lite";
|
|
54004
54437
|
const selected = phasesToRevalidate(opts?.strategiesRun, validationPhases);
|
|
54005
|
-
const
|
|
54438
|
+
const extra = overrides?.extraRevalidationKinds ? validationPhases.filter((p) => overrides.extraRevalidationKinds?.includes(p.kind) && !selected.some((s) => s.kind === p.kind)) : [];
|
|
54439
|
+
const selectedWithExtra = [...selected, ...extra];
|
|
54440
|
+
const phases = lite ? orderGateLast(selectedWithExtra) : selectedWithExtra;
|
|
54006
54441
|
getSafeLogger()?.debug("story-orchestrator", "rectification validate scope", {
|
|
54007
54442
|
storyId: ctx.storyId,
|
|
54008
54443
|
mode: opts?.mode ?? "full",
|
|
@@ -54154,6 +54589,25 @@ class ExecutionPlan {
|
|
|
54154
54589
|
}
|
|
54155
54590
|
}
|
|
54156
54591
|
}
|
|
54592
|
+
const advCfg = this.state.adversarialReview ? this.state.nonBlockingFix : undefined;
|
|
54593
|
+
const advisoryOut = phaseOutputs["adversarial-review"];
|
|
54594
|
+
const advisoryFindings = advisoryOut?.advisoryFindings ?? [];
|
|
54595
|
+
if (advCfg && this.state.rectification && this.ctx.storyId && shouldRunNonBlockingFix(advCfg, advisoryFindings.length)) {
|
|
54596
|
+
await runNonBlockingFix({
|
|
54597
|
+
workdir: this.ctx.packageDir,
|
|
54598
|
+
storyId: this.ctx.storyId,
|
|
54599
|
+
advisoryFindings,
|
|
54600
|
+
cfg: advCfg,
|
|
54601
|
+
phaseOutputs,
|
|
54602
|
+
runRectify: (maxAttempts) => runRectification(this.ctx, this.state, phaseCosts, phaseOutputs, {
|
|
54603
|
+
initialFindings: advisoryFindings,
|
|
54604
|
+
strategies: this.state.nonBlockingFixStrategies ?? [],
|
|
54605
|
+
excludePhaseKinds: nonBlockingExcludePhases(),
|
|
54606
|
+
extraRevalidationKinds: nonBlockingExtraPhases(advCfg),
|
|
54607
|
+
maxAttempts
|
|
54608
|
+
})
|
|
54609
|
+
});
|
|
54610
|
+
}
|
|
54157
54611
|
const verifierName = this.state.verifier?.slot.op.name;
|
|
54158
54612
|
const gateName = this.state.fullSuiteGate?.slot.op.name;
|
|
54159
54613
|
const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
|
|
@@ -54246,6 +54700,11 @@ class StoryOrchestratorBuilder {
|
|
|
54246
54700
|
this.state.rectification = opts;
|
|
54247
54701
|
return this;
|
|
54248
54702
|
}
|
|
54703
|
+
addNonBlockingFix(cfg, strategies) {
|
|
54704
|
+
this.state.nonBlockingFix = cfg;
|
|
54705
|
+
this.state.nonBlockingFixStrategies = strategies;
|
|
54706
|
+
return this;
|
|
54707
|
+
}
|
|
54249
54708
|
build(ctx, opts = {}) {
|
|
54250
54709
|
if (!this.state.implementer) {
|
|
54251
54710
|
throw new NaxError("StoryOrchestratorBuilder.build(): addImplementer() must be called before build()", "ORCHESTRATOR_NO_IMPLEMENTER", { stage: "execution" });
|
|
@@ -54263,6 +54722,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
54263
54722
|
init_event_bus();
|
|
54264
54723
|
init_prepare_inputs();
|
|
54265
54724
|
init_git();
|
|
54725
|
+
init_non_blocking_fix();
|
|
54266
54726
|
_storyOrchestratorDeps = {
|
|
54267
54727
|
callOp,
|
|
54268
54728
|
runFixCycle,
|
|
@@ -54415,6 +54875,22 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
54415
54875
|
};
|
|
54416
54876
|
builder.addRectification(rectOpts);
|
|
54417
54877
|
}
|
|
54878
|
+
const nbf = config2.review?.adversarial?.nonBlockingFix;
|
|
54879
|
+
const nbStrategies = [];
|
|
54880
|
+
if (nbf?.enabled && inputs.adversarialReview) {
|
|
54881
|
+
const nbSink = makeDeclarationSink();
|
|
54882
|
+
if (nbf.scope === "source") {
|
|
54883
|
+
nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
|
|
54884
|
+
includeAdversarialReview: true
|
|
54885
|
+
}));
|
|
54886
|
+
} else {
|
|
54887
|
+
nbStrategies.push(makeAutofixImplementerStrategy(story, config2, nbSink, {
|
|
54888
|
+
includeAdversarialReview: false
|
|
54889
|
+
}), makeAutofixTestWriterStrategy(story, config2, nbSink));
|
|
54890
|
+
}
|
|
54891
|
+
nbStrategies.push(makeFullSuiteRectifyStrategy(story, config2));
|
|
54892
|
+
builder.addNonBlockingFix(nbf, nbStrategies);
|
|
54893
|
+
}
|
|
54418
54894
|
return builder.build(ctx, { isThreeSession });
|
|
54419
54895
|
}
|
|
54420
54896
|
var init_build_plan_for_strategy = __esm(() => {
|
|
@@ -54678,41 +55154,6 @@ var init_execution_helpers = __esm(() => {
|
|
|
54678
55154
|
init_errors();
|
|
54679
55155
|
});
|
|
54680
55156
|
|
|
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
55157
|
// src/execution/session-manager-runtime.ts
|
|
54717
55158
|
async function closePhysicalSession(descriptor, agentGetFn, force) {
|
|
54718
55159
|
if (!descriptor.handle)
|
|
@@ -56287,9 +56728,14 @@ __export(exports_init_context, {
|
|
|
56287
56728
|
generateContextTemplate: () => generateContextTemplate,
|
|
56288
56729
|
_initContextDeps: () => _initContextDeps
|
|
56289
56730
|
});
|
|
56290
|
-
import { existsSync as existsSync22 } from "fs";
|
|
56291
|
-
import { mkdir as mkdir8 } from "fs/promises";
|
|
56292
56731
|
import { basename as basename9, join as join51 } from "path";
|
|
56732
|
+
async function bunFileExists(path13) {
|
|
56733
|
+
return Bun.file(path13).exists();
|
|
56734
|
+
}
|
|
56735
|
+
async function bunMkdirp(path13) {
|
|
56736
|
+
const proc = Bun.spawn(["mkdir", "-p", path13]);
|
|
56737
|
+
await proc.exited;
|
|
56738
|
+
}
|
|
56293
56739
|
async function findFiles(dir, maxFiles = 200) {
|
|
56294
56740
|
try {
|
|
56295
56741
|
const proc = Bun.spawnSync([
|
|
@@ -56318,7 +56764,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
56318
56764
|
}
|
|
56319
56765
|
async function readPackageManifest(projectRoot) {
|
|
56320
56766
|
const packageJsonPath = join51(projectRoot, "package.json");
|
|
56321
|
-
if (!
|
|
56767
|
+
if (!await bunFileExists(packageJsonPath)) {
|
|
56322
56768
|
return null;
|
|
56323
56769
|
}
|
|
56324
56770
|
try {
|
|
@@ -56336,7 +56782,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
56336
56782
|
}
|
|
56337
56783
|
async function readReadmeSnippet(projectRoot) {
|
|
56338
56784
|
const readmePath = join51(projectRoot, "README.md");
|
|
56339
|
-
if (!
|
|
56785
|
+
if (!await bunFileExists(readmePath)) {
|
|
56340
56786
|
return null;
|
|
56341
56787
|
}
|
|
56342
56788
|
try {
|
|
@@ -56354,7 +56800,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
56354
56800
|
const found = [];
|
|
56355
56801
|
for (const candidate of candidates) {
|
|
56356
56802
|
const path13 = join51(projectRoot, candidate);
|
|
56357
|
-
if (
|
|
56803
|
+
if (await bunFileExists(path13)) {
|
|
56358
56804
|
found.push(candidate);
|
|
56359
56805
|
}
|
|
56360
56806
|
}
|
|
@@ -56365,7 +56811,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
56365
56811
|
const found = [];
|
|
56366
56812
|
for (const candidate of candidates) {
|
|
56367
56813
|
const path13 = join51(projectRoot, candidate);
|
|
56368
|
-
if (
|
|
56814
|
+
if (await bunFileExists(path13)) {
|
|
56369
56815
|
found.push(candidate);
|
|
56370
56816
|
}
|
|
56371
56817
|
}
|
|
@@ -56495,10 +56941,10 @@ Keep it under 2000 tokens. Use markdown formatting. Be specific to the detected
|
|
|
56495
56941
|
`;
|
|
56496
56942
|
try {
|
|
56497
56943
|
const result = await _initContextDeps.callLLM(prompt);
|
|
56498
|
-
logger.info("init", "Generated context.md with LLM");
|
|
56944
|
+
logger.info("init", "Generated context.md with LLM", { storyId: "init-context" });
|
|
56499
56945
|
return result;
|
|
56500
56946
|
} catch (err) {
|
|
56501
|
-
logger.warn("init", `LLM context generation failed, falling back to template: ${err instanceof Error ? err.message : String(err)}
|
|
56947
|
+
logger.warn("init", `LLM context generation failed, falling back to template: ${err instanceof Error ? err.message : String(err)}`, { storyId: "init-context" });
|
|
56502
56948
|
return generateContextTemplate(scan);
|
|
56503
56949
|
}
|
|
56504
56950
|
}
|
|
@@ -56527,27 +56973,33 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
56527
56973
|
const logger = getLogger();
|
|
56528
56974
|
const naxDir = join51(repoRoot, ".nax", "mono", packagePath);
|
|
56529
56975
|
const contextPath = join51(naxDir, "context.md");
|
|
56530
|
-
if (
|
|
56531
|
-
logger.info("init", "Package context.md already exists (use --force to overwrite)", {
|
|
56976
|
+
if (await bunFileExists(contextPath) && !force) {
|
|
56977
|
+
logger.info("init", "Package context.md already exists (use --force to overwrite)", {
|
|
56978
|
+
storyId: "init-context",
|
|
56979
|
+
path: contextPath
|
|
56980
|
+
});
|
|
56532
56981
|
return;
|
|
56533
56982
|
}
|
|
56534
|
-
if (!
|
|
56535
|
-
await
|
|
56983
|
+
if (!await bunFileExists(naxDir)) {
|
|
56984
|
+
await bunMkdirp(naxDir);
|
|
56536
56985
|
}
|
|
56537
56986
|
const content = generatePackageContextTemplate(packagePath);
|
|
56538
56987
|
await Bun.write(contextPath, content);
|
|
56539
|
-
logger.info("init", "Created package context.md", { path: contextPath });
|
|
56988
|
+
logger.info("init", "Created package context.md", { storyId: "init-context", path: contextPath });
|
|
56540
56989
|
}
|
|
56541
56990
|
async function initContext(projectRoot, options = {}) {
|
|
56542
56991
|
const logger = getLogger();
|
|
56543
56992
|
const naxDir = join51(projectRoot, ".nax");
|
|
56544
56993
|
const contextPath = join51(naxDir, "context.md");
|
|
56545
|
-
if (
|
|
56546
|
-
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", {
|
|
56994
|
+
if (await bunFileExists(contextPath) && !options.force) {
|
|
56995
|
+
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", {
|
|
56996
|
+
storyId: "init-context",
|
|
56997
|
+
path: contextPath
|
|
56998
|
+
});
|
|
56547
56999
|
return;
|
|
56548
57000
|
}
|
|
56549
|
-
if (!
|
|
56550
|
-
await
|
|
57001
|
+
if (!await bunFileExists(naxDir)) {
|
|
57002
|
+
await bunMkdirp(naxDir);
|
|
56551
57003
|
}
|
|
56552
57004
|
const scan = await scanProject(projectRoot);
|
|
56553
57005
|
let content;
|
|
@@ -56557,7 +57009,10 @@ async function initContext(projectRoot, options = {}) {
|
|
|
56557
57009
|
content = generateContextTemplate(scan);
|
|
56558
57010
|
}
|
|
56559
57011
|
await Bun.write(contextPath, content);
|
|
56560
|
-
logger.info("init", "Generated .nax/context.md template from project scan", {
|
|
57012
|
+
logger.info("init", "Generated .nax/context.md template from project scan", {
|
|
57013
|
+
storyId: "init-context",
|
|
57014
|
+
path: contextPath
|
|
57015
|
+
});
|
|
56561
57016
|
}
|
|
56562
57017
|
var _initContextDeps;
|
|
56563
57018
|
var init_init_context = __esm(() => {
|
|
@@ -56570,11 +57025,11 @@ var init_init_context = __esm(() => {
|
|
|
56570
57025
|
});
|
|
56571
57026
|
|
|
56572
57027
|
// src/cli/init-detect.ts
|
|
56573
|
-
import { existsSync as
|
|
57028
|
+
import { existsSync as existsSync22, readFileSync } from "fs";
|
|
56574
57029
|
import { join as join52 } from "path";
|
|
56575
57030
|
function readPackageJson(projectRoot) {
|
|
56576
57031
|
const pkgPath = join52(projectRoot, "package.json");
|
|
56577
|
-
if (!
|
|
57032
|
+
if (!existsSync22(pkgPath))
|
|
56578
57033
|
return;
|
|
56579
57034
|
try {
|
|
56580
57035
|
return JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -56616,41 +57071,41 @@ function detectStack(projectRoot) {
|
|
|
56616
57071
|
};
|
|
56617
57072
|
}
|
|
56618
57073
|
function detectRuntime(projectRoot) {
|
|
56619
|
-
if (
|
|
57074
|
+
if (existsSync22(join52(projectRoot, "bun.lockb")) || existsSync22(join52(projectRoot, "bunfig.toml"))) {
|
|
56620
57075
|
return "bun";
|
|
56621
57076
|
}
|
|
56622
|
-
if (
|
|
57077
|
+
if (existsSync22(join52(projectRoot, "package-lock.json")) || existsSync22(join52(projectRoot, "yarn.lock")) || existsSync22(join52(projectRoot, "pnpm-lock.yaml"))) {
|
|
56623
57078
|
return "node";
|
|
56624
57079
|
}
|
|
56625
57080
|
return "unknown";
|
|
56626
57081
|
}
|
|
56627
57082
|
function detectLanguage2(projectRoot) {
|
|
56628
|
-
if (
|
|
57083
|
+
if (existsSync22(join52(projectRoot, "tsconfig.json")))
|
|
56629
57084
|
return "typescript";
|
|
56630
|
-
if (
|
|
57085
|
+
if (existsSync22(join52(projectRoot, "pyproject.toml")) || existsSync22(join52(projectRoot, "setup.py"))) {
|
|
56631
57086
|
return "python";
|
|
56632
57087
|
}
|
|
56633
|
-
if (
|
|
57088
|
+
if (existsSync22(join52(projectRoot, "Cargo.toml")))
|
|
56634
57089
|
return "rust";
|
|
56635
|
-
if (
|
|
57090
|
+
if (existsSync22(join52(projectRoot, "go.mod")))
|
|
56636
57091
|
return "go";
|
|
56637
57092
|
return "unknown";
|
|
56638
57093
|
}
|
|
56639
57094
|
function detectLinter(projectRoot) {
|
|
56640
|
-
if (
|
|
57095
|
+
if (existsSync22(join52(projectRoot, "biome.json")) || existsSync22(join52(projectRoot, "biome.jsonc"))) {
|
|
56641
57096
|
return "biome";
|
|
56642
57097
|
}
|
|
56643
|
-
if (
|
|
57098
|
+
if (existsSync22(join52(projectRoot, ".eslintrc.json")) || existsSync22(join52(projectRoot, ".eslintrc.js")) || existsSync22(join52(projectRoot, "eslint.config.js"))) {
|
|
56644
57099
|
return "eslint";
|
|
56645
57100
|
}
|
|
56646
57101
|
return "unknown";
|
|
56647
57102
|
}
|
|
56648
57103
|
function detectMonorepo(projectRoot) {
|
|
56649
|
-
if (
|
|
57104
|
+
if (existsSync22(join52(projectRoot, "turbo.json")))
|
|
56650
57105
|
return "turborepo";
|
|
56651
|
-
if (
|
|
57106
|
+
if (existsSync22(join52(projectRoot, "nx.json")))
|
|
56652
57107
|
return "nx";
|
|
56653
|
-
if (
|
|
57108
|
+
if (existsSync22(join52(projectRoot, "pnpm-workspace.yaml")))
|
|
56654
57109
|
return "pnpm-workspaces";
|
|
56655
57110
|
const pkg = readPackageJson(projectRoot);
|
|
56656
57111
|
if (pkg?.workspaces)
|
|
@@ -56792,8 +57247,8 @@ __export(exports_init, {
|
|
|
56792
57247
|
checkInitCollision: () => checkInitCollision,
|
|
56793
57248
|
_initDeps: () => _initDeps
|
|
56794
57249
|
});
|
|
56795
|
-
import { existsSync as
|
|
56796
|
-
import { mkdir as
|
|
57250
|
+
import { existsSync as existsSync23 } from "fs";
|
|
57251
|
+
import { mkdir as mkdir8 } from "fs/promises";
|
|
56797
57252
|
import { join as join53 } from "path";
|
|
56798
57253
|
function validateProjectName(name) {
|
|
56799
57254
|
if (!name)
|
|
@@ -56832,7 +57287,7 @@ async function updateGitignore(projectRoot) {
|
|
|
56832
57287
|
const logger = getLogger();
|
|
56833
57288
|
const gitignorePath = join53(projectRoot, ".gitignore");
|
|
56834
57289
|
let existing = "";
|
|
56835
|
-
if (
|
|
57290
|
+
if (existsSync23(gitignorePath)) {
|
|
56836
57291
|
existing = await Bun.file(gitignorePath).text();
|
|
56837
57292
|
}
|
|
56838
57293
|
const missingEntries = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
|
|
@@ -56912,12 +57367,12 @@ function buildConstitution(stack) {
|
|
|
56912
57367
|
async function initGlobal() {
|
|
56913
57368
|
const logger = getLogger();
|
|
56914
57369
|
const globalDir = globalConfigDir();
|
|
56915
|
-
if (!
|
|
56916
|
-
await
|
|
57370
|
+
if (!existsSync23(globalDir)) {
|
|
57371
|
+
await mkdir8(globalDir, { recursive: true });
|
|
56917
57372
|
logger.info("init", "Created global config directory", { path: globalDir });
|
|
56918
57373
|
}
|
|
56919
57374
|
const configPath = join53(globalDir, "config.json");
|
|
56920
|
-
if (!
|
|
57375
|
+
if (!existsSync23(configPath)) {
|
|
56921
57376
|
await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
|
|
56922
57377
|
`);
|
|
56923
57378
|
logger.info("init", "Created global config", { path: configPath });
|
|
@@ -56925,15 +57380,15 @@ async function initGlobal() {
|
|
|
56925
57380
|
logger.info("init", "Global config already exists", { path: configPath });
|
|
56926
57381
|
}
|
|
56927
57382
|
const constitutionPath = join53(globalDir, "constitution.md");
|
|
56928
|
-
if (!
|
|
57383
|
+
if (!existsSync23(constitutionPath)) {
|
|
56929
57384
|
await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
|
|
56930
57385
|
logger.info("init", "Created global constitution", { path: constitutionPath });
|
|
56931
57386
|
} else {
|
|
56932
57387
|
logger.info("init", "Global constitution already exists", { path: constitutionPath });
|
|
56933
57388
|
}
|
|
56934
57389
|
const hooksDir = join53(globalDir, "hooks");
|
|
56935
|
-
if (!
|
|
56936
|
-
await
|
|
57390
|
+
if (!existsSync23(hooksDir)) {
|
|
57391
|
+
await mkdir8(hooksDir, { recursive: true });
|
|
56937
57392
|
logger.info("init", "Created global hooks directory", { path: hooksDir });
|
|
56938
57393
|
} else {
|
|
56939
57394
|
logger.info("init", "Global hooks directory already exists", { path: hooksDir });
|
|
@@ -56977,8 +57432,8 @@ async function initProject(projectRoot, options) {
|
|
|
56977
57432
|
`), "INIT_NAME_COLLISION", { stage: "init", name: detectedName });
|
|
56978
57433
|
}
|
|
56979
57434
|
}
|
|
56980
|
-
if (!
|
|
56981
|
-
await
|
|
57435
|
+
if (!existsSync23(projectDir)) {
|
|
57436
|
+
await mkdir8(projectDir, { recursive: true });
|
|
56982
57437
|
logger.info("init", "Created project config directory", { path: projectDir });
|
|
56983
57438
|
}
|
|
56984
57439
|
const stack = detectStack(projectRoot);
|
|
@@ -56993,7 +57448,7 @@ async function initProject(projectRoot, options) {
|
|
|
56993
57448
|
monorepo: stack.monorepo
|
|
56994
57449
|
});
|
|
56995
57450
|
const configPath = join53(projectDir, "config.json");
|
|
56996
|
-
if (!
|
|
57451
|
+
if (!existsSync23(configPath)) {
|
|
56997
57452
|
await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
|
|
56998
57453
|
`);
|
|
56999
57454
|
logger.info("init", "Created project config", { path: configPath });
|
|
@@ -57002,15 +57457,15 @@ async function initProject(projectRoot, options) {
|
|
|
57002
57457
|
}
|
|
57003
57458
|
await initContext(projectRoot, { ai: options?.ai, force: options?.force });
|
|
57004
57459
|
const constitutionPath = join53(projectDir, "constitution.md");
|
|
57005
|
-
if (!
|
|
57460
|
+
if (!existsSync23(constitutionPath) || options?.force) {
|
|
57006
57461
|
await Bun.write(constitutionPath, buildConstitution(stack));
|
|
57007
57462
|
logger.info("init", "Created project constitution", { path: constitutionPath });
|
|
57008
57463
|
} else {
|
|
57009
57464
|
logger.info("init", "Project constitution already exists", { path: constitutionPath });
|
|
57010
57465
|
}
|
|
57011
57466
|
const hooksDir = join53(projectDir, "hooks");
|
|
57012
|
-
if (!
|
|
57013
|
-
await
|
|
57467
|
+
if (!existsSync23(hooksDir)) {
|
|
57468
|
+
await mkdir8(hooksDir, { recursive: true });
|
|
57014
57469
|
logger.info("init", "Created project hooks directory", { path: hooksDir });
|
|
57015
57470
|
} else {
|
|
57016
57471
|
logger.info("init", "Project hooks directory already exists", { path: hooksDir });
|
|
@@ -57069,6 +57524,286 @@ var init_init2 = __esm(() => {
|
|
|
57069
57524
|
};
|
|
57070
57525
|
});
|
|
57071
57526
|
|
|
57527
|
+
// src/cli/setup-analyze.ts
|
|
57528
|
+
import { join as join54 } from "path";
|
|
57529
|
+
async function detectPackageManager(workdir) {
|
|
57530
|
+
const { fileExists } = _analyzeRepoDeps;
|
|
57531
|
+
if (await fileExists(join54(workdir, "bun.lock")))
|
|
57532
|
+
return { pmRunPrefix: "bun run", pmDlx: "bunx" };
|
|
57533
|
+
if (await fileExists(join54(workdir, "bun.lockb")))
|
|
57534
|
+
return { pmRunPrefix: "bun run", pmDlx: "bunx" };
|
|
57535
|
+
if (await fileExists(join54(workdir, "package-lock.json")))
|
|
57536
|
+
return { pmRunPrefix: "npm run", pmDlx: "npx" };
|
|
57537
|
+
if (await fileExists(join54(workdir, "yarn.lock")))
|
|
57538
|
+
return { pmRunPrefix: "yarn", pmDlx: "yarn dlx" };
|
|
57539
|
+
if (await fileExists(join54(workdir, "pnpm-lock.yaml")))
|
|
57540
|
+
return { pmRunPrefix: "pnpm run", pmDlx: "pnpx" };
|
|
57541
|
+
return { pmRunPrefix: "npm run", pmDlx: "npx" };
|
|
57542
|
+
}
|
|
57543
|
+
async function detectOrchestrator(workdir) {
|
|
57544
|
+
const { fileExists } = _analyzeRepoDeps;
|
|
57545
|
+
if (await fileExists(join54(workdir, "turbo.json")))
|
|
57546
|
+
return "turbo";
|
|
57547
|
+
if (await fileExists(join54(workdir, "nx.json")))
|
|
57548
|
+
return "nx";
|
|
57549
|
+
return "none";
|
|
57550
|
+
}
|
|
57551
|
+
async function getMissingScripts(packageDir) {
|
|
57552
|
+
const pkg = await _analyzeRepoDeps.readJson(join54(packageDir, "package.json"));
|
|
57553
|
+
const scripts = pkg?.scripts ?? {};
|
|
57554
|
+
return CANONICAL_SCRIPTS.filter((s) => !(s in scripts));
|
|
57555
|
+
}
|
|
57556
|
+
async function buildPackageFacts(workdir, relativeDir, testPatternMap) {
|
|
57557
|
+
const packageDir = relativeDir === "" ? workdir : join54(workdir, relativeDir);
|
|
57558
|
+
const [profile, missingScripts] = await Promise.all([
|
|
57559
|
+
_analyzeRepoDeps.detectProjectProfile(packageDir, {}),
|
|
57560
|
+
getMissingScripts(packageDir)
|
|
57561
|
+
]);
|
|
57562
|
+
const detection = testPatternMap[relativeDir] ?? {
|
|
57563
|
+
patterns: [],
|
|
57564
|
+
confidence: "empty",
|
|
57565
|
+
sources: []
|
|
57566
|
+
};
|
|
57567
|
+
return {
|
|
57568
|
+
relativeDir,
|
|
57569
|
+
testFramework: profile.testFramework,
|
|
57570
|
+
testFilePatterns: detection.patterns,
|
|
57571
|
+
missingScripts
|
|
57572
|
+
};
|
|
57573
|
+
}
|
|
57574
|
+
async function analyzeRepo(workdir) {
|
|
57575
|
+
const { discoverWorkspacePackages: discover, detectTestFilePatternsForWorkspace: detectPatterns } = _analyzeRepoDeps;
|
|
57576
|
+
const [pmInfo, orchestrator, packageDirs] = await Promise.all([
|
|
57577
|
+
detectPackageManager(workdir),
|
|
57578
|
+
detectOrchestrator(workdir),
|
|
57579
|
+
discover(workdir)
|
|
57580
|
+
]);
|
|
57581
|
+
const shape = packageDirs.length > 0 ? "mono" : "single";
|
|
57582
|
+
const dirsToAnalyze = shape === "single" ? [""] : packageDirs;
|
|
57583
|
+
const testPatternMap = await detectPatterns(workdir, packageDirs);
|
|
57584
|
+
const packages = await Promise.all(dirsToAnalyze.map((dir) => buildPackageFacts(workdir, dir, testPatternMap)));
|
|
57585
|
+
return {
|
|
57586
|
+
shape,
|
|
57587
|
+
packages,
|
|
57588
|
+
pmRunPrefix: pmInfo.pmRunPrefix,
|
|
57589
|
+
pmDlx: pmInfo.pmDlx,
|
|
57590
|
+
orchestrator
|
|
57591
|
+
};
|
|
57592
|
+
}
|
|
57593
|
+
var CANONICAL_SCRIPTS, _analyzeRepoDeps;
|
|
57594
|
+
var init_setup_analyze = __esm(() => {
|
|
57595
|
+
init_project();
|
|
57596
|
+
init_detect2();
|
|
57597
|
+
init_workspace();
|
|
57598
|
+
CANONICAL_SCRIPTS = ["build", "test", "lint", "type-check", "lint:fix"];
|
|
57599
|
+
_analyzeRepoDeps = {
|
|
57600
|
+
fileExists: async (path13) => Bun.file(path13).exists(),
|
|
57601
|
+
readJson: async (path13) => {
|
|
57602
|
+
try {
|
|
57603
|
+
const f = Bun.file(path13);
|
|
57604
|
+
if (!await f.exists())
|
|
57605
|
+
return null;
|
|
57606
|
+
return JSON.parse(await f.text());
|
|
57607
|
+
} catch {
|
|
57608
|
+
return null;
|
|
57609
|
+
}
|
|
57610
|
+
},
|
|
57611
|
+
discoverWorkspacePackages,
|
|
57612
|
+
detectProjectProfile,
|
|
57613
|
+
detectTestFilePatternsForWorkspace
|
|
57614
|
+
};
|
|
57615
|
+
});
|
|
57616
|
+
|
|
57617
|
+
// src/cli/setup-fill.ts
|
|
57618
|
+
import { join as join55 } from "path";
|
|
57619
|
+
async function addScriptToPackageJson(pkgJsonPath, key, value) {
|
|
57620
|
+
const pkg = await _fillScriptsDeps.readJson(pkgJsonPath) ?? {};
|
|
57621
|
+
const scripts = pkg.scripts ?? {};
|
|
57622
|
+
if (key in scripts)
|
|
57623
|
+
return;
|
|
57624
|
+
const updated = { ...pkg, scripts: { ...scripts, [key]: value } };
|
|
57625
|
+
await _fillScriptsDeps.writeFile(pkgJsonPath, JSON.stringify(updated, null, 2));
|
|
57626
|
+
}
|
|
57627
|
+
async function addTurboTask(turboJsonPath, taskKey) {
|
|
57628
|
+
const turbo = await _fillScriptsDeps.readJson(turboJsonPath) ?? {};
|
|
57629
|
+
const field = "pipeline" in turbo ? "pipeline" : "tasks";
|
|
57630
|
+
const existing = turbo[field] ?? {};
|
|
57631
|
+
if (taskKey in existing)
|
|
57632
|
+
return;
|
|
57633
|
+
const updated = { ...turbo, [field]: { ...existing, [taskKey]: {} } };
|
|
57634
|
+
await _fillScriptsDeps.writeFile(turboJsonPath, JSON.stringify(updated, null, 2));
|
|
57635
|
+
}
|
|
57636
|
+
async function fillScripts(workdir, analysis) {
|
|
57637
|
+
const { shape, packages, orchestrator } = analysis;
|
|
57638
|
+
if (shape === "single") {
|
|
57639
|
+
const rootPkg = packages[0];
|
|
57640
|
+
if (rootPkg?.missingScripts.includes(TYPE_CHECK_KEY)) {
|
|
57641
|
+
await addScriptToPackageJson(join55(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
|
|
57642
|
+
}
|
|
57643
|
+
return;
|
|
57644
|
+
}
|
|
57645
|
+
for (const pkg of packages) {
|
|
57646
|
+
if (!pkg.missingScripts.includes(TYPE_CHECK_KEY))
|
|
57647
|
+
continue;
|
|
57648
|
+
await addScriptToPackageJson(join55(workdir, pkg.relativeDir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
|
|
57649
|
+
}
|
|
57650
|
+
if (orchestrator === "turbo" && packages.some((p) => p.missingScripts.includes(TYPE_CHECK_KEY))) {
|
|
57651
|
+
await addTurboTask(join55(workdir, "turbo.json"), TYPE_CHECK_KEY);
|
|
57652
|
+
await addScriptToPackageJson(join55(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_TURBO_PASSTHROUGH);
|
|
57653
|
+
}
|
|
57654
|
+
}
|
|
57655
|
+
var TYPE_CHECK_KEY = "type-check", TYPE_CHECK_SCRIPT = "tsc --noEmit -p tsconfig.json", TYPE_CHECK_TURBO_PASSTHROUGH = "turbo run type-check", _fillScriptsDeps;
|
|
57656
|
+
var init_setup_fill = __esm(() => {
|
|
57657
|
+
_fillScriptsDeps = {
|
|
57658
|
+
readJson: async (path13) => {
|
|
57659
|
+
try {
|
|
57660
|
+
const f = Bun.file(path13);
|
|
57661
|
+
if (!await f.exists())
|
|
57662
|
+
return null;
|
|
57663
|
+
return JSON.parse(await f.text());
|
|
57664
|
+
} catch {
|
|
57665
|
+
return null;
|
|
57666
|
+
}
|
|
57667
|
+
},
|
|
57668
|
+
writeFile: async (path13, content) => {
|
|
57669
|
+
await Bun.write(path13, content);
|
|
57670
|
+
}
|
|
57671
|
+
};
|
|
57672
|
+
});
|
|
57673
|
+
|
|
57674
|
+
// src/cli/setup-llm.ts
|
|
57675
|
+
var generateSetupPlan = (ctx, analysis) => callOp(ctx, setupGenerateOp, analysis);
|
|
57676
|
+
var init_setup_llm = __esm(() => {
|
|
57677
|
+
init_operations();
|
|
57678
|
+
init_setup_generate();
|
|
57679
|
+
});
|
|
57680
|
+
|
|
57681
|
+
// src/cli/setup-verify.ts
|
|
57682
|
+
async function runSetupGate(workdir, config2) {
|
|
57683
|
+
const logger = getLogger();
|
|
57684
|
+
const quality = config2.quality;
|
|
57685
|
+
const testCmd = quality?.commands?.test;
|
|
57686
|
+
if (!testCmd) {
|
|
57687
|
+
logger.info("setup-verify", "No test command configured \u2014 skipping verification gate", {
|
|
57688
|
+
storyId: "setup"
|
|
57689
|
+
});
|
|
57690
|
+
return 0;
|
|
57691
|
+
}
|
|
57692
|
+
logger.info("setup-verify", "Running verification gate", { storyId: "setup", cmd: testCmd });
|
|
57693
|
+
const parts = testCmd.trim().split(/\s+/).filter(Boolean);
|
|
57694
|
+
if (parts.length === 0)
|
|
57695
|
+
return 0;
|
|
57696
|
+
const proc = _setupVerifyDeps.spawn(parts, { cwd: workdir });
|
|
57697
|
+
return await proc.exited;
|
|
57698
|
+
}
|
|
57699
|
+
var _setupVerifyDeps;
|
|
57700
|
+
var init_setup_verify = __esm(() => {
|
|
57701
|
+
init_logger2();
|
|
57702
|
+
_setupVerifyDeps = {
|
|
57703
|
+
spawn: Bun.spawn.bind(Bun)
|
|
57704
|
+
};
|
|
57705
|
+
});
|
|
57706
|
+
|
|
57707
|
+
// src/cli/setup.ts
|
|
57708
|
+
var exports_setup = {};
|
|
57709
|
+
__export(exports_setup, {
|
|
57710
|
+
setupCommand: () => setupCommand,
|
|
57711
|
+
_setupDeps: () => _setupDeps
|
|
57712
|
+
});
|
|
57713
|
+
import { join as join56 } from "path";
|
|
57714
|
+
async function setupCommand(options = {}) {
|
|
57715
|
+
const workdir = options.dir ?? process.cwd();
|
|
57716
|
+
const naxDir = join56(workdir, ".nax");
|
|
57717
|
+
const naxConfigPath = join56(naxDir, "config.json");
|
|
57718
|
+
const exists = await _setupDeps.fileExists(naxConfigPath);
|
|
57719
|
+
if (exists && !options.force) {
|
|
57720
|
+
_setupDeps.stderr("[setup] .nax/config.json already exists. Use --force to overwrite.");
|
|
57721
|
+
return 1;
|
|
57722
|
+
}
|
|
57723
|
+
const analysis = await _setupDeps.analyzeRepo(workdir);
|
|
57724
|
+
const { ctx, close } = await _setupDeps.buildCallContext(workdir, options.agent);
|
|
57725
|
+
let plan;
|
|
57726
|
+
try {
|
|
57727
|
+
try {
|
|
57728
|
+
plan = await _setupDeps.generateSetupPlan(ctx, analysis);
|
|
57729
|
+
} catch (err) {
|
|
57730
|
+
if (err instanceof NaxError && err.code === "SETUP_PLAN_INVALID") {
|
|
57731
|
+
_setupDeps.stderr(`[setup] ${err.message}`);
|
|
57732
|
+
return 1;
|
|
57733
|
+
}
|
|
57734
|
+
throw err;
|
|
57735
|
+
}
|
|
57736
|
+
if (options.dryRun) {
|
|
57737
|
+
_setupDeps.stdout(`[setup] Dry run \u2014 planned root config:
|
|
57738
|
+
${JSON.stringify(plan.config, null, 2)}`);
|
|
57739
|
+
return 0;
|
|
57740
|
+
}
|
|
57741
|
+
for (const gap of plan.gaps) {
|
|
57742
|
+
_setupDeps.stderr(`[setup] gap: ${gap}`);
|
|
57743
|
+
}
|
|
57744
|
+
if (options.fillScripts) {
|
|
57745
|
+
await _setupDeps.fillScripts(workdir, analysis);
|
|
57746
|
+
}
|
|
57747
|
+
await _setupDeps.mkdir(naxDir);
|
|
57748
|
+
await _setupDeps.writeFile(naxConfigPath, JSON.stringify(plan.config, null, 2));
|
|
57749
|
+
for (const mc of plan.monoConfigs) {
|
|
57750
|
+
const monoDir = join56(naxDir, "mono", mc.relativeDir);
|
|
57751
|
+
await _setupDeps.mkdir(monoDir);
|
|
57752
|
+
await _setupDeps.writeFile(join56(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
|
|
57753
|
+
}
|
|
57754
|
+
const gateResult = await _setupDeps.runGate(workdir, plan.config);
|
|
57755
|
+
if (gateResult !== 0) {
|
|
57756
|
+
return gateResult;
|
|
57757
|
+
}
|
|
57758
|
+
return 0;
|
|
57759
|
+
} finally {
|
|
57760
|
+
await close();
|
|
57761
|
+
}
|
|
57762
|
+
}
|
|
57763
|
+
var _setupDeps;
|
|
57764
|
+
var init_setup = __esm(() => {
|
|
57765
|
+
init_errors();
|
|
57766
|
+
init_setup_analyze();
|
|
57767
|
+
init_setup_fill();
|
|
57768
|
+
init_setup_llm();
|
|
57769
|
+
init_setup_verify();
|
|
57770
|
+
_setupDeps = {
|
|
57771
|
+
analyzeRepo,
|
|
57772
|
+
fillScripts,
|
|
57773
|
+
buildCallContext: async (workdir, agentName) => {
|
|
57774
|
+
const { loadConfig: loadConfig2 } = await Promise.resolve().then(() => (init_config(), exports_config));
|
|
57775
|
+
const { createRuntime: createRuntime2 } = await Promise.resolve().then(() => (init_runtime(), exports_runtime));
|
|
57776
|
+
const config2 = await loadConfig2(workdir);
|
|
57777
|
+
const rt = createRuntime2(config2, workdir);
|
|
57778
|
+
return {
|
|
57779
|
+
ctx: {
|
|
57780
|
+
runtime: rt,
|
|
57781
|
+
packageView: rt.packages.resolve(),
|
|
57782
|
+
packageDir: workdir,
|
|
57783
|
+
agentName: agentName ?? rt.agentManager.getDefault()
|
|
57784
|
+
},
|
|
57785
|
+
close: () => rt.close()
|
|
57786
|
+
};
|
|
57787
|
+
},
|
|
57788
|
+
generateSetupPlan: (ctx, analysis) => generateSetupPlan(ctx, analysis),
|
|
57789
|
+
runGate: (workdir, config2) => runSetupGate(workdir, config2),
|
|
57790
|
+
fileExists: (path13) => Bun.file(path13).exists(),
|
|
57791
|
+
writeFile: (path13, content) => Bun.write(path13, content).then(() => {}),
|
|
57792
|
+
mkdir: async (path13) => {
|
|
57793
|
+
const proc = Bun.spawn(["mkdir", "-p", path13]);
|
|
57794
|
+
await proc.exited;
|
|
57795
|
+
},
|
|
57796
|
+
stdout: (msg) => {
|
|
57797
|
+
process.stdout.write(`${msg}
|
|
57798
|
+
`);
|
|
57799
|
+
},
|
|
57800
|
+
stderr: (msg) => {
|
|
57801
|
+
process.stderr.write(`${msg}
|
|
57802
|
+
`);
|
|
57803
|
+
}
|
|
57804
|
+
};
|
|
57805
|
+
});
|
|
57806
|
+
|
|
57072
57807
|
// src/plugins/builtin/curator/collect.ts
|
|
57073
57808
|
import * as path13 from "path";
|
|
57074
57809
|
function now() {
|
|
@@ -57770,12 +58505,12 @@ function renderProposals(proposals, runId, observationCount) {
|
|
|
57770
58505
|
}
|
|
57771
58506
|
|
|
57772
58507
|
// src/plugins/builtin/curator/rollup.ts
|
|
57773
|
-
import { appendFile as appendFile3, mkdir as
|
|
58508
|
+
import { appendFile as appendFile3, mkdir as mkdir9, writeFile } from "fs/promises";
|
|
57774
58509
|
import * as path14 from "path";
|
|
57775
58510
|
async function appendToRollup(observations, rollupPath) {
|
|
57776
58511
|
try {
|
|
57777
58512
|
const dir = path14.dirname(rollupPath);
|
|
57778
|
-
await
|
|
58513
|
+
await mkdir9(dir, { recursive: true });
|
|
57779
58514
|
if (observations.length === 0) {
|
|
57780
58515
|
const f = Bun.file(rollupPath);
|
|
57781
58516
|
if (!await f.exists()) {
|
|
@@ -57792,7 +58527,7 @@ async function appendToRollup(observations, rollupPath) {
|
|
|
57792
58527
|
var init_rollup = () => {};
|
|
57793
58528
|
|
|
57794
58529
|
// src/plugins/builtin/curator/index.ts
|
|
57795
|
-
import { mkdir as
|
|
58530
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
57796
58531
|
import * as path15 from "path";
|
|
57797
58532
|
function getCuratorEnabled(context) {
|
|
57798
58533
|
const cfg = context.config;
|
|
@@ -57867,7 +58602,7 @@ var init_curator = __esm(() => {
|
|
|
57867
58602
|
if (context.outputDir) {
|
|
57868
58603
|
const { observationsPath, rollupPath } = resolveCuratorOutputs(curatorContext);
|
|
57869
58604
|
const runDir = path15.dirname(observationsPath);
|
|
57870
|
-
await
|
|
58605
|
+
await mkdir10(runDir, { recursive: true });
|
|
57871
58606
|
await Bun.write(observationsPath, observations.map((o) => JSON.stringify(o)).join(`
|
|
57872
58607
|
`) + (observations.length > 0 ? `
|
|
57873
58608
|
` : ""));
|
|
@@ -58443,12 +59178,12 @@ var init_loader4 = __esm(() => {
|
|
|
58443
59178
|
});
|
|
58444
59179
|
|
|
58445
59180
|
// src/utils/paths.ts
|
|
58446
|
-
import { join as
|
|
59181
|
+
import { join as join67 } from "path";
|
|
58447
59182
|
function getRunsDir() {
|
|
58448
|
-
return process.env.NAX_RUNS_DIR ??
|
|
59183
|
+
return process.env.NAX_RUNS_DIR ?? join67(globalConfigDir(), "runs");
|
|
58449
59184
|
}
|
|
58450
59185
|
function getEventsRootDir() {
|
|
58451
|
-
return
|
|
59186
|
+
return join67(globalConfigDir(), "events");
|
|
58452
59187
|
}
|
|
58453
59188
|
var init_paths3 = __esm(() => {
|
|
58454
59189
|
init_paths();
|
|
@@ -58508,7 +59243,7 @@ var init_command_argv = __esm(() => {
|
|
|
58508
59243
|
});
|
|
58509
59244
|
|
|
58510
59245
|
// src/hooks/runner.ts
|
|
58511
|
-
import { join as
|
|
59246
|
+
import { join as join74 } from "path";
|
|
58512
59247
|
function createDrainDeadline2(deadlineMs) {
|
|
58513
59248
|
let timeoutId;
|
|
58514
59249
|
const promise2 = new Promise((resolve16) => {
|
|
@@ -58527,14 +59262,14 @@ async function loadHooksConfig(projectDir, globalDir) {
|
|
|
58527
59262
|
let globalHooks = { hooks: {} };
|
|
58528
59263
|
let projectHooks = { hooks: {} };
|
|
58529
59264
|
let skipGlobal = false;
|
|
58530
|
-
const projectPath =
|
|
59265
|
+
const projectPath = join74(projectDir, "hooks.json");
|
|
58531
59266
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
58532
59267
|
if (projectData) {
|
|
58533
59268
|
projectHooks = projectData;
|
|
58534
59269
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
58535
59270
|
}
|
|
58536
59271
|
if (!skipGlobal && globalDir) {
|
|
58537
|
-
const globalPath =
|
|
59272
|
+
const globalPath = join74(globalDir, "hooks.json");
|
|
58538
59273
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
58539
59274
|
if (globalData) {
|
|
58540
59275
|
globalHooks = globalData;
|
|
@@ -58704,7 +59439,7 @@ var package_default;
|
|
|
58704
59439
|
var init_package = __esm(() => {
|
|
58705
59440
|
package_default = {
|
|
58706
59441
|
name: "@nathapp/nax",
|
|
58707
|
-
version: "0.69.
|
|
59442
|
+
version: "0.69.1",
|
|
58708
59443
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
58709
59444
|
type: "module",
|
|
58710
59445
|
bin: {
|
|
@@ -58799,8 +59534,8 @@ var init_version = __esm(() => {
|
|
|
58799
59534
|
NAX_VERSION = package_default.version;
|
|
58800
59535
|
NAX_COMMIT = (() => {
|
|
58801
59536
|
try {
|
|
58802
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
58803
|
-
return "
|
|
59537
|
+
if (/^[0-9a-f]{6,10}$/.test("3b2af55b"))
|
|
59538
|
+
return "3b2af55b";
|
|
58804
59539
|
} catch {}
|
|
58805
59540
|
try {
|
|
58806
59541
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -59676,16 +60411,16 @@ var init_acceptance_loop = __esm(() => {
|
|
|
59676
60411
|
});
|
|
59677
60412
|
|
|
59678
60413
|
// src/session/scratch-purge.ts
|
|
59679
|
-
import { mkdir as
|
|
59680
|
-
import { dirname as dirname12, join as
|
|
60414
|
+
import { mkdir as mkdir12, rename, rm } from "fs/promises";
|
|
60415
|
+
import { dirname as dirname12, join as join75 } from "path";
|
|
59681
60416
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
59682
|
-
const sessionsDir =
|
|
60417
|
+
const sessionsDir = join75(projectDir, ".nax", "features", featureName, "sessions");
|
|
59683
60418
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
59684
60419
|
const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
|
|
59685
60420
|
let purged = 0;
|
|
59686
60421
|
for (const sessionId of sessionIds) {
|
|
59687
|
-
const sessionDir =
|
|
59688
|
-
const descriptorPath =
|
|
60422
|
+
const sessionDir = join75(sessionsDir, sessionId);
|
|
60423
|
+
const descriptorPath = join75(sessionDir, "descriptor.json");
|
|
59689
60424
|
if (!await _scratchPurgeDeps.fileExists(descriptorPath))
|
|
59690
60425
|
continue;
|
|
59691
60426
|
let lastActivityAt;
|
|
@@ -59701,7 +60436,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
|
|
|
59701
60436
|
if (new Date(lastActivityAt).getTime() >= cutoffMs)
|
|
59702
60437
|
continue;
|
|
59703
60438
|
if (archiveInsteadOfDelete) {
|
|
59704
|
-
const archiveDest =
|
|
60439
|
+
const archiveDest = join75(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
|
|
59705
60440
|
await _scratchPurgeDeps.move(sessionDir, archiveDest);
|
|
59706
60441
|
} else {
|
|
59707
60442
|
await _scratchPurgeDeps.remove(sessionDir);
|
|
@@ -59728,7 +60463,7 @@ var init_scratch_purge = __esm(() => {
|
|
|
59728
60463
|
readFile: (path19) => Bun.file(path19).text(),
|
|
59729
60464
|
remove: (path19) => rm(path19, { recursive: true, force: true }),
|
|
59730
60465
|
move: async (src, dest) => {
|
|
59731
|
-
await
|
|
60466
|
+
await mkdir12(dirname12(dest), { recursive: true });
|
|
59732
60467
|
await rename(src, dest);
|
|
59733
60468
|
},
|
|
59734
60469
|
now: () => Date.now()
|
|
@@ -60438,19 +61173,19 @@ function precomputeBatchPlan(stories, maxBatchSize = DEFAULT_MAX_BATCH_SIZE) {
|
|
|
60438
61173
|
var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
60439
61174
|
|
|
60440
61175
|
// src/pipeline/subscribers/events-writer.ts
|
|
60441
|
-
import { appendFile as appendFile4, mkdir as
|
|
60442
|
-
import { basename as basename13, join as
|
|
61176
|
+
import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
|
|
61177
|
+
import { basename as basename13, join as join76 } from "path";
|
|
60443
61178
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
60444
61179
|
const logger = getSafeLogger();
|
|
60445
61180
|
const project = basename13(workdir);
|
|
60446
|
-
const eventsDir =
|
|
60447
|
-
const eventsFile =
|
|
61181
|
+
const eventsDir = join76(getEventsRootDir(), project);
|
|
61182
|
+
const eventsFile = join76(eventsDir, "events.jsonl");
|
|
60448
61183
|
let dirReady = false;
|
|
60449
61184
|
const write = (line) => {
|
|
60450
61185
|
return (async () => {
|
|
60451
61186
|
try {
|
|
60452
61187
|
if (!dirReady) {
|
|
60453
|
-
await
|
|
61188
|
+
await mkdir13(eventsDir, { recursive: true });
|
|
60454
61189
|
dirReady = true;
|
|
60455
61190
|
}
|
|
60456
61191
|
await appendFile4(eventsFile, `${JSON.stringify(line)}
|
|
@@ -60624,24 +61359,24 @@ var init_interaction2 = __esm(() => {
|
|
|
60624
61359
|
});
|
|
60625
61360
|
|
|
60626
61361
|
// src/pipeline/subscribers/registry.ts
|
|
60627
|
-
import { mkdir as
|
|
60628
|
-
import { basename as basename14, join as
|
|
61362
|
+
import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
|
|
61363
|
+
import { basename as basename14, join as join77 } from "path";
|
|
60629
61364
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
60630
61365
|
const logger = getSafeLogger();
|
|
60631
61366
|
const project = basename14(workdir);
|
|
60632
|
-
const runDir =
|
|
60633
|
-
const metaFile =
|
|
61367
|
+
const runDir = join77(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
61368
|
+
const metaFile = join77(runDir, "meta.json");
|
|
60634
61369
|
const unsub = bus.on("run:started", (_ev) => {
|
|
60635
61370
|
return (async () => {
|
|
60636
61371
|
try {
|
|
60637
|
-
await
|
|
61372
|
+
await mkdir14(runDir, { recursive: true });
|
|
60638
61373
|
const meta3 = {
|
|
60639
61374
|
runId,
|
|
60640
61375
|
project,
|
|
60641
61376
|
feature,
|
|
60642
61377
|
workdir,
|
|
60643
|
-
statusPath:
|
|
60644
|
-
eventsDir:
|
|
61378
|
+
statusPath: join77(outputDir, "features", feature, "status.json"),
|
|
61379
|
+
eventsDir: join77(outputDir, "features", feature, "runs"),
|
|
60645
61380
|
registeredAt: new Date().toISOString()
|
|
60646
61381
|
};
|
|
60647
61382
|
await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -60886,8 +61621,8 @@ var init_types9 = __esm(() => {
|
|
|
60886
61621
|
});
|
|
60887
61622
|
|
|
60888
61623
|
// src/worktree/dependencies.ts
|
|
60889
|
-
import { existsSync as
|
|
60890
|
-
import { join as
|
|
61624
|
+
import { existsSync as existsSync30 } from "fs";
|
|
61625
|
+
import { join as join78 } from "path";
|
|
60891
61626
|
async function prepareWorktreeDependencies(options) {
|
|
60892
61627
|
const mode = options.config.execution.worktreeDependencies.mode;
|
|
60893
61628
|
const resolvedCwd = resolveDependencyCwd(options);
|
|
@@ -60901,7 +61636,7 @@ async function prepareWorktreeDependencies(options) {
|
|
|
60901
61636
|
}
|
|
60902
61637
|
}
|
|
60903
61638
|
function resolveDependencyCwd(options) {
|
|
60904
|
-
return options.storyWorkdir ?
|
|
61639
|
+
return options.storyWorkdir ? join78(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
|
|
60905
61640
|
}
|
|
60906
61641
|
function resolveInheritedDependencies(options, resolvedCwd) {
|
|
60907
61642
|
if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
|
|
@@ -60911,14 +61646,14 @@ function resolveInheritedDependencies(options, resolvedCwd) {
|
|
|
60911
61646
|
}
|
|
60912
61647
|
function hasDependencyManifests(worktreeRoot, resolvedCwd) {
|
|
60913
61648
|
const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
|
|
60914
|
-
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(
|
|
61649
|
+
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join78(directory, filename))));
|
|
60915
61650
|
}
|
|
60916
61651
|
async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
|
|
60917
|
-
const
|
|
60918
|
-
if (!
|
|
61652
|
+
const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
|
|
61653
|
+
if (!setupCommand2) {
|
|
60919
61654
|
throw new WorktreeDependencyPreparationError("[worktree-deps] provision mode requires execution.worktreeDependencies.setupCommand in phase 1.", "provision");
|
|
60920
61655
|
}
|
|
60921
|
-
const argv = parseCommandToArgv(
|
|
61656
|
+
const argv = parseCommandToArgv(setupCommand2);
|
|
60922
61657
|
if (argv.length === 0) {
|
|
60923
61658
|
throw new WorktreeDependencyPreparationError("[worktree-deps] setupCommand cannot be empty.", "provision");
|
|
60924
61659
|
}
|
|
@@ -60962,7 +61697,7 @@ var init_dependencies = __esm(() => {
|
|
|
60962
61697
|
"build.gradle.kts"
|
|
60963
61698
|
];
|
|
60964
61699
|
_worktreeDependencyDeps = {
|
|
60965
|
-
existsSync:
|
|
61700
|
+
existsSync: existsSync30,
|
|
60966
61701
|
spawn
|
|
60967
61702
|
};
|
|
60968
61703
|
});
|
|
@@ -60973,19 +61708,19 @@ __export(exports_manager, {
|
|
|
60973
61708
|
_managerDeps: () => _managerDeps,
|
|
60974
61709
|
WorktreeManager: () => WorktreeManager
|
|
60975
61710
|
});
|
|
60976
|
-
import { existsSync as
|
|
60977
|
-
import { mkdir as
|
|
60978
|
-
import { join as
|
|
61711
|
+
import { existsSync as existsSync31, symlinkSync } from "fs";
|
|
61712
|
+
import { mkdir as mkdir15 } from "fs/promises";
|
|
61713
|
+
import { join as join79 } from "path";
|
|
60979
61714
|
|
|
60980
61715
|
class WorktreeManager {
|
|
60981
61716
|
async ensureGitExcludes(projectRoot) {
|
|
60982
61717
|
const logger = getSafeLogger();
|
|
60983
|
-
const infoDir =
|
|
60984
|
-
const excludePath =
|
|
61718
|
+
const infoDir = join79(projectRoot, ".git", "info");
|
|
61719
|
+
const excludePath = join79(infoDir, "exclude");
|
|
60985
61720
|
try {
|
|
60986
|
-
await
|
|
61721
|
+
await mkdir15(infoDir, { recursive: true });
|
|
60987
61722
|
let existing = "";
|
|
60988
|
-
if (
|
|
61723
|
+
if (existsSync31(excludePath)) {
|
|
60989
61724
|
existing = await Bun.file(excludePath).text();
|
|
60990
61725
|
}
|
|
60991
61726
|
const missing = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
|
|
@@ -61008,7 +61743,7 @@ ${missing.join(`
|
|
|
61008
61743
|
}
|
|
61009
61744
|
async create(projectRoot, storyId) {
|
|
61010
61745
|
validateStoryId(storyId);
|
|
61011
|
-
const worktreePath =
|
|
61746
|
+
const worktreePath = join79(projectRoot, ".nax-wt", storyId);
|
|
61012
61747
|
const branchName = `nax/${storyId}`;
|
|
61013
61748
|
try {
|
|
61014
61749
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -61049,9 +61784,9 @@ ${missing.join(`
|
|
|
61049
61784
|
}
|
|
61050
61785
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
61051
61786
|
}
|
|
61052
|
-
const envSource =
|
|
61053
|
-
if (
|
|
61054
|
-
const envTarget =
|
|
61787
|
+
const envSource = join79(projectRoot, ".env");
|
|
61788
|
+
if (existsSync31(envSource)) {
|
|
61789
|
+
const envTarget = join79(worktreePath, ".env");
|
|
61055
61790
|
try {
|
|
61056
61791
|
symlinkSync(envSource, envTarget, "file");
|
|
61057
61792
|
} catch (error48) {
|
|
@@ -61062,7 +61797,7 @@ ${missing.join(`
|
|
|
61062
61797
|
}
|
|
61063
61798
|
async remove(projectRoot, storyId) {
|
|
61064
61799
|
validateStoryId(storyId);
|
|
61065
|
-
const worktreePath =
|
|
61800
|
+
const worktreePath = join79(projectRoot, ".nax-wt", storyId);
|
|
61066
61801
|
const branchName = `nax/${storyId}`;
|
|
61067
61802
|
try {
|
|
61068
61803
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -61874,10 +62609,10 @@ var init_merge_conflict_rectify = __esm(() => {
|
|
|
61874
62609
|
});
|
|
61875
62610
|
|
|
61876
62611
|
// src/execution/pipeline-result-handler.ts
|
|
61877
|
-
import { join as
|
|
62612
|
+
import { join as join80 } from "path";
|
|
61878
62613
|
async function removeWorktreeDirectory(projectRoot, storyId) {
|
|
61879
62614
|
const logger = getSafeLogger();
|
|
61880
|
-
const worktreePath =
|
|
62615
|
+
const worktreePath = join80(projectRoot, ".nax-wt", storyId);
|
|
61881
62616
|
try {
|
|
61882
62617
|
const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
61883
62618
|
cwd: projectRoot,
|
|
@@ -62093,8 +62828,8 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
62093
62828
|
});
|
|
62094
62829
|
|
|
62095
62830
|
// src/execution/iteration-runner.ts
|
|
62096
|
-
import { existsSync as
|
|
62097
|
-
import { join as
|
|
62831
|
+
import { existsSync as existsSync32 } from "fs";
|
|
62832
|
+
import { join as join81 } from "path";
|
|
62098
62833
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
62099
62834
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
62100
62835
|
if (ctx.dryRun) {
|
|
@@ -62119,7 +62854,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62119
62854
|
const storyStartTime = Date.now();
|
|
62120
62855
|
let effectiveWorkdir = ctx.workdir;
|
|
62121
62856
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
62122
|
-
const worktreePath =
|
|
62857
|
+
const worktreePath = join81(ctx.workdir, ".nax-wt", story.id);
|
|
62123
62858
|
const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
|
|
62124
62859
|
if (!worktreeExists) {
|
|
62125
62860
|
await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
|
|
@@ -62139,7 +62874,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62139
62874
|
}
|
|
62140
62875
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
62141
62876
|
const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
|
|
62142
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
62877
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join81(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
|
|
62143
62878
|
let dependencyContext;
|
|
62144
62879
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
62145
62880
|
try {
|
|
@@ -62166,7 +62901,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62166
62901
|
};
|
|
62167
62902
|
}
|
|
62168
62903
|
}
|
|
62169
|
-
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ?
|
|
62904
|
+
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join81(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join81(ctx.workdir, story.workdir) : ctx.workdir;
|
|
62170
62905
|
const pipelineContext = {
|
|
62171
62906
|
config: effectiveConfig,
|
|
62172
62907
|
rootConfig: ctx.config,
|
|
@@ -62298,7 +63033,7 @@ var init_iteration_runner = __esm(() => {
|
|
|
62298
63033
|
loadConfigForWorkdir,
|
|
62299
63034
|
prepareWorktreeDependencies,
|
|
62300
63035
|
runPipeline,
|
|
62301
|
-
existsSync:
|
|
63036
|
+
existsSync: existsSync32,
|
|
62302
63037
|
worktreeManager: new WorktreeManager
|
|
62303
63038
|
};
|
|
62304
63039
|
});
|
|
@@ -62368,7 +63103,7 @@ __export(exports_parallel_worker, {
|
|
|
62368
63103
|
buildWorktreePipelineContext: () => buildWorktreePipelineContext,
|
|
62369
63104
|
_parallelWorkerDeps: () => _parallelWorkerDeps
|
|
62370
63105
|
});
|
|
62371
|
-
import { join as
|
|
63106
|
+
import { join as join82 } from "path";
|
|
62372
63107
|
function buildWorktreePipelineContext(base, _story) {
|
|
62373
63108
|
return { ...base, prd: structuredClone(base.prd) };
|
|
62374
63109
|
}
|
|
@@ -62391,7 +63126,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
|
|
|
62391
63126
|
story,
|
|
62392
63127
|
stories: [story],
|
|
62393
63128
|
projectDir: context.projectDir,
|
|
62394
|
-
workdir: dependencyContext.cwd ?? (story.workdir ?
|
|
63129
|
+
workdir: dependencyContext.cwd ?? (story.workdir ? join82(worktreePath, story.workdir) : worktreePath),
|
|
62395
63130
|
worktreeDependencyContext: dependencyContext,
|
|
62396
63131
|
routing,
|
|
62397
63132
|
storyGitRef: storyGitRef ?? undefined
|
|
@@ -63278,7 +64013,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
63278
64013
|
var init_status_file = () => {};
|
|
63279
64014
|
|
|
63280
64015
|
// src/execution/status-writer.ts
|
|
63281
|
-
import { join as
|
|
64016
|
+
import { join as join83 } from "path";
|
|
63282
64017
|
|
|
63283
64018
|
class StatusWriter {
|
|
63284
64019
|
statusFile;
|
|
@@ -63397,7 +64132,7 @@ class StatusWriter {
|
|
|
63397
64132
|
if (!this._prd)
|
|
63398
64133
|
return;
|
|
63399
64134
|
const safeLogger = getSafeLogger();
|
|
63400
|
-
const featureStatusPath =
|
|
64135
|
+
const featureStatusPath = join83(featureDir, "status.json");
|
|
63401
64136
|
const write = async () => {
|
|
63402
64137
|
try {
|
|
63403
64138
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -63428,11 +64163,11 @@ __export(exports_migrate, {
|
|
|
63428
64163
|
migrateCommand: () => migrateCommand,
|
|
63429
64164
|
detectGeneratedContent: () => detectGeneratedContent
|
|
63430
64165
|
});
|
|
63431
|
-
import { existsSync as
|
|
63432
|
-
import { mkdir as
|
|
64166
|
+
import { existsSync as existsSync33 } from "fs";
|
|
64167
|
+
import { mkdir as mkdir16, readdir as readdir6, rename as rename3 } from "fs/promises";
|
|
63433
64168
|
import path22 from "path";
|
|
63434
64169
|
async function detectGeneratedContent(naxDir) {
|
|
63435
|
-
if (!
|
|
64170
|
+
if (!existsSync33(naxDir))
|
|
63436
64171
|
return [];
|
|
63437
64172
|
const candidates = [];
|
|
63438
64173
|
let entries = [];
|
|
@@ -63447,7 +64182,7 @@ async function detectGeneratedContent(naxDir) {
|
|
|
63447
64182
|
}
|
|
63448
64183
|
}
|
|
63449
64184
|
const featuresDir = path22.join(naxDir, "features");
|
|
63450
|
-
if (
|
|
64185
|
+
if (existsSync33(featuresDir)) {
|
|
63451
64186
|
let featureDirs = [];
|
|
63452
64187
|
try {
|
|
63453
64188
|
featureDirs = await readdir6(featuresDir);
|
|
@@ -63509,7 +64244,7 @@ async function migrateCommand(options) {
|
|
|
63509
64244
|
});
|
|
63510
64245
|
}
|
|
63511
64246
|
const src = path22.join(globalConfigDir(), options.reclaim);
|
|
63512
|
-
if (!
|
|
64247
|
+
if (!existsSync33(src)) {
|
|
63513
64248
|
throw new NaxError(`Nothing to reclaim: ~/.nax/${options.reclaim} does not exist`, "MIGRATE_RECLAIM_NOT_FOUND", {
|
|
63514
64249
|
stage: "migrate",
|
|
63515
64250
|
name: options.reclaim
|
|
@@ -63517,7 +64252,7 @@ async function migrateCommand(options) {
|
|
|
63517
64252
|
}
|
|
63518
64253
|
const archiveBase = path22.join(globalConfigDir(), "_archive");
|
|
63519
64254
|
const archiveDest = path22.join(archiveBase, `${options.reclaim}-${Date.now()}`);
|
|
63520
|
-
await
|
|
64255
|
+
await mkdir16(archiveBase, { recursive: true });
|
|
63521
64256
|
await rename3(src, archiveDest);
|
|
63522
64257
|
logger.info("migrate", `Reclaimed: archived to ${archiveDest}`, { storyId: "_migrate" });
|
|
63523
64258
|
return;
|
|
@@ -63555,7 +64290,7 @@ async function migrateCommand(options) {
|
|
|
63555
64290
|
}
|
|
63556
64291
|
const naxDir = path22.join(options.workdir, ".nax");
|
|
63557
64292
|
const configPath = path22.join(naxDir, "config.json");
|
|
63558
|
-
if (!
|
|
64293
|
+
if (!existsSync33(configPath)) {
|
|
63559
64294
|
throw new NaxError("No .nax/config.json found \u2014 run nax init first", "MIGRATE_NO_CONFIG", {
|
|
63560
64295
|
stage: "migrate",
|
|
63561
64296
|
workdir: options.workdir
|
|
@@ -63585,12 +64320,12 @@ async function migrateCommand(options) {
|
|
|
63585
64320
|
}
|
|
63586
64321
|
return;
|
|
63587
64322
|
}
|
|
63588
|
-
await
|
|
64323
|
+
await mkdir16(destBase, { recursive: true });
|
|
63589
64324
|
let moved = 0;
|
|
63590
64325
|
for (const candidate of candidates) {
|
|
63591
64326
|
const dest = path22.join(destBase, candidate.name);
|
|
63592
|
-
await
|
|
63593
|
-
if (
|
|
64327
|
+
await mkdir16(path22.dirname(dest), { recursive: true });
|
|
64328
|
+
if (existsSync33(dest)) {
|
|
63594
64329
|
throw new NaxError(`Migration conflict: destination already exists.
|
|
63595
64330
|
Source: ${candidate.srcPath}
|
|
63596
64331
|
Destination: ${dest}
|
|
@@ -63831,7 +64566,7 @@ __export(exports_run_initialization, {
|
|
|
63831
64566
|
initializeRun: () => initializeRun,
|
|
63832
64567
|
_reconcileDeps: () => _reconcileDeps
|
|
63833
64568
|
});
|
|
63834
|
-
import { join as
|
|
64569
|
+
import { join as join84 } from "path";
|
|
63835
64570
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
63836
64571
|
const logger = getSafeLogger();
|
|
63837
64572
|
let reconciledCount = 0;
|
|
@@ -63848,7 +64583,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
63848
64583
|
});
|
|
63849
64584
|
continue;
|
|
63850
64585
|
}
|
|
63851
|
-
const effectiveWorkdir = story.workdir ?
|
|
64586
|
+
const effectiveWorkdir = story.workdir ? join84(workdir, story.workdir) : workdir;
|
|
63852
64587
|
try {
|
|
63853
64588
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
63854
64589
|
if (!reviewResult.success) {
|
|
@@ -93641,7 +94376,7 @@ __export(exports_curator, {
|
|
|
93641
94376
|
});
|
|
93642
94377
|
import { readdirSync as readdirSync8 } from "fs";
|
|
93643
94378
|
import { unlink as unlink4 } from "fs/promises";
|
|
93644
|
-
import { basename as basename15, join as
|
|
94379
|
+
import { basename as basename15, join as join86 } from "path";
|
|
93645
94380
|
function getProjectKey(config2, projectDir) {
|
|
93646
94381
|
return config2.name?.trim() || basename15(projectDir);
|
|
93647
94382
|
}
|
|
@@ -93724,7 +94459,7 @@ async function curatorStatus(options) {
|
|
|
93724
94459
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93725
94460
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93726
94461
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93727
|
-
const runsDir =
|
|
94462
|
+
const runsDir = join86(outputDir, "runs");
|
|
93728
94463
|
const runIds = listRunIds(runsDir);
|
|
93729
94464
|
let runId;
|
|
93730
94465
|
if (options.run) {
|
|
@@ -93741,8 +94476,8 @@ async function curatorStatus(options) {
|
|
|
93741
94476
|
runId = runIds[runIds.length - 1];
|
|
93742
94477
|
}
|
|
93743
94478
|
console.log(`Run: ${runId}`);
|
|
93744
|
-
const runDir =
|
|
93745
|
-
const observationsPath =
|
|
94479
|
+
const runDir = join86(runsDir, runId);
|
|
94480
|
+
const observationsPath = join86(runDir, "observations.jsonl");
|
|
93746
94481
|
const observations = await parseObservations(observationsPath);
|
|
93747
94482
|
const counts = new Map;
|
|
93748
94483
|
for (const obs of observations) {
|
|
@@ -93752,7 +94487,7 @@ async function curatorStatus(options) {
|
|
|
93752
94487
|
for (const [kind, count] of counts.entries()) {
|
|
93753
94488
|
console.log(` ${kind}: ${count}`);
|
|
93754
94489
|
}
|
|
93755
|
-
const proposalsPath =
|
|
94490
|
+
const proposalsPath = join86(runDir, "curator-proposals.md");
|
|
93756
94491
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
93757
94492
|
if (proposalText !== null) {
|
|
93758
94493
|
console.log("");
|
|
@@ -93766,8 +94501,8 @@ async function curatorCommit(options) {
|
|
|
93766
94501
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93767
94502
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93768
94503
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93769
|
-
const runDir =
|
|
93770
|
-
const proposalsPath =
|
|
94504
|
+
const runDir = join86(outputDir, "runs", options.runId);
|
|
94505
|
+
const proposalsPath = join86(runDir, "curator-proposals.md");
|
|
93771
94506
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
93772
94507
|
if (proposalText === null) {
|
|
93773
94508
|
console.log(`curator-proposals.md not found for run ${options.runId}.`);
|
|
@@ -93783,7 +94518,7 @@ async function curatorCommit(options) {
|
|
|
93783
94518
|
const dropFileState = new Map;
|
|
93784
94519
|
const skippedDrops = new Set;
|
|
93785
94520
|
for (const drop2 of drops) {
|
|
93786
|
-
const targetPath =
|
|
94521
|
+
const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
|
|
93787
94522
|
if (!dropFileState.has(targetPath)) {
|
|
93788
94523
|
const fileExists2 = await Bun.file(targetPath).exists();
|
|
93789
94524
|
const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
|
|
@@ -93817,7 +94552,7 @@ async function curatorCommit(options) {
|
|
|
93817
94552
|
if (skippedDrops.has(drop2)) {
|
|
93818
94553
|
continue;
|
|
93819
94554
|
}
|
|
93820
|
-
const targetPath =
|
|
94555
|
+
const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
|
|
93821
94556
|
const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
|
|
93822
94557
|
const filtered = filterDropContent(existing, drop2.description);
|
|
93823
94558
|
await _curatorCmdDeps.writeFile(targetPath, filtered);
|
|
@@ -93826,7 +94561,7 @@ async function curatorCommit(options) {
|
|
|
93826
94561
|
}
|
|
93827
94562
|
const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
|
|
93828
94563
|
for (const add2 of adds) {
|
|
93829
|
-
const targetPath =
|
|
94564
|
+
const targetPath = join86(resolved.projectDir, add2.canonicalFile);
|
|
93830
94565
|
const content = buildAddContent(add2);
|
|
93831
94566
|
await _curatorCmdDeps.appendFile(targetPath, content);
|
|
93832
94567
|
modifiedFiles.add(targetPath);
|
|
@@ -93863,7 +94598,7 @@ async function curatorDryrun(options) {
|
|
|
93863
94598
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93864
94599
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93865
94600
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93866
|
-
const runsDir =
|
|
94601
|
+
const runsDir = join86(outputDir, "runs");
|
|
93867
94602
|
const runIds = listRunIds(runsDir);
|
|
93868
94603
|
if (runIds.length === 0) {
|
|
93869
94604
|
console.log("No runs found.");
|
|
@@ -93874,7 +94609,7 @@ async function curatorDryrun(options) {
|
|
|
93874
94609
|
console.log(`Run ${options.run} not found in ${runsDir}.`);
|
|
93875
94610
|
return;
|
|
93876
94611
|
}
|
|
93877
|
-
const observationsPath =
|
|
94612
|
+
const observationsPath = join86(runsDir, runId, "observations.jsonl");
|
|
93878
94613
|
const observations = await parseObservations(observationsPath);
|
|
93879
94614
|
const thresholds = getThresholds(config2);
|
|
93880
94615
|
const proposals = runHeuristics(observations, thresholds);
|
|
@@ -93915,12 +94650,12 @@ async function curatorGc(options) {
|
|
|
93915
94650
|
await _curatorCmdDeps.writeFile(rollupPath, newContent);
|
|
93916
94651
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93917
94652
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93918
|
-
const perRunsDir =
|
|
94653
|
+
const perRunsDir = join86(outputDir, "runs");
|
|
93919
94654
|
for (const runId of uniqueRunIds) {
|
|
93920
94655
|
if (!keepSet.has(runId)) {
|
|
93921
|
-
const runDir =
|
|
93922
|
-
await _curatorCmdDeps.removeFile(
|
|
93923
|
-
await _curatorCmdDeps.removeFile(
|
|
94656
|
+
const runDir = join86(perRunsDir, runId);
|
|
94657
|
+
await _curatorCmdDeps.removeFile(join86(runDir, "observations.jsonl"));
|
|
94658
|
+
await _curatorCmdDeps.removeFile(join86(runDir, "curator-proposals.md"));
|
|
93924
94659
|
}
|
|
93925
94660
|
}
|
|
93926
94661
|
console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
|
|
@@ -93963,9 +94698,9 @@ var init_curator2 = __esm(() => {
|
|
|
93963
94698
|
|
|
93964
94699
|
// bin/nax.ts
|
|
93965
94700
|
init_source();
|
|
93966
|
-
import { existsSync as
|
|
94701
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
|
|
93967
94702
|
import { homedir as homedir3 } from "os";
|
|
93968
|
-
import { basename as basename16, join as
|
|
94703
|
+
import { basename as basename16, join as join87 } from "path";
|
|
93969
94704
|
|
|
93970
94705
|
// node_modules/commander/esm.mjs
|
|
93971
94706
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -95055,6 +95790,7 @@ async function runsShowCommand(options) {
|
|
|
95055
95790
|
// src/cli/index.ts
|
|
95056
95791
|
init_prompts2();
|
|
95057
95792
|
init_init2();
|
|
95793
|
+
init_setup();
|
|
95058
95794
|
|
|
95059
95795
|
// src/cli/plugins.ts
|
|
95060
95796
|
init_paths();
|
|
@@ -95129,8 +95865,8 @@ init_interaction();
|
|
|
95129
95865
|
init_source();
|
|
95130
95866
|
init_loader();
|
|
95131
95867
|
init_generator2();
|
|
95132
|
-
import { existsSync as
|
|
95133
|
-
import { join as
|
|
95868
|
+
import { existsSync as existsSync24 } from "fs";
|
|
95869
|
+
import { join as join61 } from "path";
|
|
95134
95870
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
95135
95871
|
async function generateCommand(options) {
|
|
95136
95872
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -95173,7 +95909,7 @@ async function generateCommand(options) {
|
|
|
95173
95909
|
return;
|
|
95174
95910
|
}
|
|
95175
95911
|
if (options.package) {
|
|
95176
|
-
const packageDir =
|
|
95912
|
+
const packageDir = join61(workdir, options.package);
|
|
95177
95913
|
if (dryRun) {
|
|
95178
95914
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
95179
95915
|
}
|
|
@@ -95193,10 +95929,10 @@ async function generateCommand(options) {
|
|
|
95193
95929
|
process.exit(1);
|
|
95194
95930
|
return;
|
|
95195
95931
|
}
|
|
95196
|
-
const contextPath = options.context ?
|
|
95197
|
-
const outputDir = options.output ?
|
|
95932
|
+
const contextPath = options.context ? join61(workdir, options.context) : join61(workdir, ".nax/context.md");
|
|
95933
|
+
const outputDir = options.output ? join61(workdir, options.output) : workdir;
|
|
95198
95934
|
const autoInject = !options.noAutoInject;
|
|
95199
|
-
if (!
|
|
95935
|
+
if (!existsSync24(contextPath)) {
|
|
95200
95936
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
95201
95937
|
console.error(source_default.yellow(" Create .nax/context.md first, or run `nax init` to scaffold it."));
|
|
95202
95938
|
process.exit(1);
|
|
@@ -95299,8 +96035,8 @@ async function generateCommand(options) {
|
|
|
95299
96035
|
}
|
|
95300
96036
|
// src/cli/config-display.ts
|
|
95301
96037
|
init_loader();
|
|
95302
|
-
import { existsSync as
|
|
95303
|
-
import { join as
|
|
96038
|
+
import { existsSync as existsSync26 } from "fs";
|
|
96039
|
+
import { join as join63 } from "path";
|
|
95304
96040
|
|
|
95305
96041
|
// src/cli/config-descriptions.ts
|
|
95306
96042
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -95551,10 +96287,10 @@ function deepEqual(a, b) {
|
|
|
95551
96287
|
// src/cli/config-get.ts
|
|
95552
96288
|
init_defaults();
|
|
95553
96289
|
init_loader();
|
|
95554
|
-
import { existsSync as
|
|
95555
|
-
import { join as
|
|
96290
|
+
import { existsSync as existsSync25 } from "fs";
|
|
96291
|
+
import { join as join62 } from "path";
|
|
95556
96292
|
async function loadConfigFile(path18) {
|
|
95557
|
-
if (!
|
|
96293
|
+
if (!existsSync25(path18))
|
|
95558
96294
|
return null;
|
|
95559
96295
|
try {
|
|
95560
96296
|
return await Bun.file(path18).json();
|
|
@@ -95574,7 +96310,7 @@ async function loadProjectConfig() {
|
|
|
95574
96310
|
const projectDir = findProjectDir();
|
|
95575
96311
|
if (!projectDir)
|
|
95576
96312
|
return null;
|
|
95577
|
-
const projectPath =
|
|
96313
|
+
const projectPath = join62(projectDir, "config.json");
|
|
95578
96314
|
return await loadConfigFile(projectPath);
|
|
95579
96315
|
}
|
|
95580
96316
|
|
|
@@ -95634,14 +96370,14 @@ async function configCommand(config2, options = {}) {
|
|
|
95634
96370
|
function determineConfigSources() {
|
|
95635
96371
|
const globalPath = globalConfigPath();
|
|
95636
96372
|
const projectDir = findProjectDir();
|
|
95637
|
-
const projectPath = projectDir ?
|
|
96373
|
+
const projectPath = projectDir ? join63(projectDir, "config.json") : null;
|
|
95638
96374
|
return {
|
|
95639
96375
|
global: fileExists(globalPath) ? globalPath : null,
|
|
95640
96376
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
95641
96377
|
};
|
|
95642
96378
|
}
|
|
95643
96379
|
function fileExists(path18) {
|
|
95644
|
-
return
|
|
96380
|
+
return existsSync26(path18);
|
|
95645
96381
|
}
|
|
95646
96382
|
function displayConfigWithDescriptions(obj, path18, sources, indent = 0) {
|
|
95647
96383
|
const indentStr = " ".repeat(indent);
|
|
@@ -95783,15 +96519,15 @@ init_paths();
|
|
|
95783
96519
|
init_profile();
|
|
95784
96520
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
95785
96521
|
import { readdirSync as readdirSync5 } from "fs";
|
|
95786
|
-
import { join as
|
|
96522
|
+
import { join as join64 } from "path";
|
|
95787
96523
|
var _profileCLIDeps = {
|
|
95788
96524
|
env: process.env
|
|
95789
96525
|
};
|
|
95790
96526
|
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
95791
96527
|
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
95792
96528
|
async function profileListCommand(startDir) {
|
|
95793
|
-
const globalProfilesDir =
|
|
95794
|
-
const projectProfilesDir =
|
|
96529
|
+
const globalProfilesDir = join64(globalConfigDir(), "profiles");
|
|
96530
|
+
const projectProfilesDir = join64(projectConfigDir(startDir), "profiles");
|
|
95795
96531
|
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
95796
96532
|
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
95797
96533
|
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
@@ -95850,7 +96586,7 @@ function maskProfileValues(obj) {
|
|
|
95850
96586
|
return result;
|
|
95851
96587
|
}
|
|
95852
96588
|
async function profileUseCommand(profileName, startDir) {
|
|
95853
|
-
const configPath =
|
|
96589
|
+
const configPath = join64(projectConfigDir(startDir), "config.json");
|
|
95854
96590
|
const configFile = Bun.file(configPath);
|
|
95855
96591
|
let existing = {};
|
|
95856
96592
|
if (await configFile.exists()) {
|
|
@@ -95869,8 +96605,8 @@ async function profileCurrentCommand(startDir) {
|
|
|
95869
96605
|
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
95870
96606
|
}
|
|
95871
96607
|
async function profileCreateCommand(profileName, startDir) {
|
|
95872
|
-
const profilesDir =
|
|
95873
|
-
const profilePath =
|
|
96608
|
+
const profilesDir = join64(projectConfigDir(startDir), "profiles");
|
|
96609
|
+
const profilePath = join64(profilesDir, `${profileName}.json`);
|
|
95874
96610
|
const profileFile = Bun.file(profilePath);
|
|
95875
96611
|
if (await profileFile.exists()) {
|
|
95876
96612
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
@@ -95991,8 +96727,8 @@ async function contextInspectCommand(options) {
|
|
|
95991
96727
|
// src/cli/rules.ts
|
|
95992
96728
|
init_canonical_loader();
|
|
95993
96729
|
init_errors();
|
|
95994
|
-
import { mkdir as
|
|
95995
|
-
import { basename as basename12, join as
|
|
96730
|
+
import { mkdir as mkdir11 } from "fs/promises";
|
|
96731
|
+
import { basename as basename12, join as join65 } from "path";
|
|
95996
96732
|
var _rulesCLIDeps = {
|
|
95997
96733
|
readFile: async (path18) => Bun.file(path18).text(),
|
|
95998
96734
|
writeFile: async (path18, content) => {
|
|
@@ -96001,13 +96737,13 @@ var _rulesCLIDeps = {
|
|
|
96001
96737
|
fileExists: async (path18) => Bun.file(path18).exists(),
|
|
96002
96738
|
globInDir: (dir) => {
|
|
96003
96739
|
try {
|
|
96004
|
-
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) =>
|
|
96740
|
+
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join65(dir, f));
|
|
96005
96741
|
} catch {
|
|
96006
96742
|
return [];
|
|
96007
96743
|
}
|
|
96008
96744
|
},
|
|
96009
96745
|
mkdir: async (path18) => {
|
|
96010
|
-
await
|
|
96746
|
+
await mkdir11(path18, { recursive: true });
|
|
96011
96747
|
},
|
|
96012
96748
|
globCanonicalRuleFiles: (workdir) => {
|
|
96013
96749
|
try {
|
|
@@ -96050,7 +96786,7 @@ ${r.content}`).join(`
|
|
|
96050
96786
|
`);
|
|
96051
96787
|
const shimContent = `${header + body}
|
|
96052
96788
|
`;
|
|
96053
|
-
const shimPath =
|
|
96789
|
+
const shimPath = join65(workdir, shimFileName);
|
|
96054
96790
|
if (options.dryRun) {
|
|
96055
96791
|
console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
|
|
96056
96792
|
return;
|
|
@@ -96079,14 +96815,14 @@ function neutralizeContent(content) {
|
|
|
96079
96815
|
}
|
|
96080
96816
|
async function collectMigrationSources(workdir) {
|
|
96081
96817
|
const sources = [];
|
|
96082
|
-
const claudeMdPath =
|
|
96818
|
+
const claudeMdPath = join65(workdir, "CLAUDE.md");
|
|
96083
96819
|
if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
|
|
96084
96820
|
const content = await _rulesCLIDeps.readFile(claudeMdPath);
|
|
96085
96821
|
if (content.trim()) {
|
|
96086
96822
|
sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
|
|
96087
96823
|
}
|
|
96088
96824
|
}
|
|
96089
|
-
const rulesDir =
|
|
96825
|
+
const rulesDir = join65(workdir, ".claude", "rules");
|
|
96090
96826
|
const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
|
|
96091
96827
|
for (const filePath of ruleFiles) {
|
|
96092
96828
|
try {
|
|
@@ -96106,7 +96842,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96106
96842
|
console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
|
|
96107
96843
|
return;
|
|
96108
96844
|
}
|
|
96109
|
-
const targetDir =
|
|
96845
|
+
const targetDir = join65(workdir, CANONICAL_RULES_DIR);
|
|
96110
96846
|
if (!options.dryRun) {
|
|
96111
96847
|
try {
|
|
96112
96848
|
await _rulesCLIDeps.mkdir(targetDir);
|
|
@@ -96117,7 +96853,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96117
96853
|
let written = 0;
|
|
96118
96854
|
let skipped = 0;
|
|
96119
96855
|
for (const { sourcePath, targetFileName, content } of sources) {
|
|
96120
|
-
const targetPath =
|
|
96856
|
+
const targetPath = join65(targetDir, targetFileName);
|
|
96121
96857
|
if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
|
|
96122
96858
|
console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
|
|
96123
96859
|
skipped++;
|
|
@@ -96156,7 +96892,7 @@ function collectCanonicalRuleRoots(workdir) {
|
|
|
96156
96892
|
const packageRel = normalized.slice(0, idx);
|
|
96157
96893
|
if (!packageRel)
|
|
96158
96894
|
continue;
|
|
96159
|
-
roots.add(
|
|
96895
|
+
roots.add(join65(workdir, packageRel));
|
|
96160
96896
|
}
|
|
96161
96897
|
return [...roots].sort();
|
|
96162
96898
|
}
|
|
@@ -96178,7 +96914,7 @@ init_logger2();
|
|
|
96178
96914
|
init_detect2();
|
|
96179
96915
|
init_workspace();
|
|
96180
96916
|
init_common();
|
|
96181
|
-
import { join as
|
|
96917
|
+
import { join as join66 } from "path";
|
|
96182
96918
|
function resolveEffective(detected, configPatterns) {
|
|
96183
96919
|
if (configPatterns !== undefined)
|
|
96184
96920
|
return "config";
|
|
@@ -96263,7 +96999,7 @@ async function detectCommand(options) {
|
|
|
96263
96999
|
const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96264
97000
|
const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
|
|
96265
97001
|
const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96266
|
-
const pkgConfigPath =
|
|
97002
|
+
const pkgConfigPath = join66(workdir, ".nax", "mono", dir, "config.json");
|
|
96267
97003
|
const pkgRaw = await loadRawConfig(pkgConfigPath);
|
|
96268
97004
|
const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
|
|
96269
97005
|
const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
|
|
@@ -96317,13 +97053,13 @@ async function detectCommand(options) {
|
|
|
96317
97053
|
if (rootDetected.confidence === "empty") {
|
|
96318
97054
|
console.log(source_default.yellow(" root: skipped (empty detection)"));
|
|
96319
97055
|
} else {
|
|
96320
|
-
const rootConfigPath =
|
|
97056
|
+
const rootConfigPath = join66(workdir, ".nax", "config.json");
|
|
96321
97057
|
try {
|
|
96322
97058
|
const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
|
|
96323
97059
|
if (status === "skipped") {
|
|
96324
97060
|
console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
|
|
96325
97061
|
} else {
|
|
96326
|
-
console.log(source_default.green(` root: ${status} \u2192 ${
|
|
97062
|
+
console.log(source_default.green(` root: ${status} \u2192 ${join66(".nax", "config.json")}`));
|
|
96327
97063
|
}
|
|
96328
97064
|
} catch (err) {
|
|
96329
97065
|
console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
|
|
@@ -96336,13 +97072,13 @@ async function detectCommand(options) {
|
|
|
96336
97072
|
console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
|
|
96337
97073
|
continue;
|
|
96338
97074
|
}
|
|
96339
|
-
const pkgConfigPath =
|
|
97075
|
+
const pkgConfigPath = join66(workdir, ".nax", "mono", dir, "config.json");
|
|
96340
97076
|
try {
|
|
96341
97077
|
const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
|
|
96342
97078
|
if (status === "skipped") {
|
|
96343
97079
|
console.log(source_default.dim(` ${dir}: skipped (already set)`));
|
|
96344
97080
|
} else {
|
|
96345
|
-
console.log(source_default.green(` ${dir}: ${status} \u2192 ${
|
|
97081
|
+
console.log(source_default.green(` ${dir}: ${status} \u2192 ${join66(".nax", "mono", dir, "config.json")}`));
|
|
96346
97082
|
}
|
|
96347
97083
|
} catch (err) {
|
|
96348
97084
|
console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
|
|
@@ -96359,20 +97095,20 @@ async function detectCommand(options) {
|
|
|
96359
97095
|
|
|
96360
97096
|
// src/commands/logs.ts
|
|
96361
97097
|
init_common();
|
|
96362
|
-
import { existsSync as
|
|
96363
|
-
import { join as
|
|
97098
|
+
import { existsSync as existsSync28 } from "fs";
|
|
97099
|
+
import { join as join70 } from "path";
|
|
96364
97100
|
|
|
96365
97101
|
// src/commands/logs-formatter.ts
|
|
96366
97102
|
init_source();
|
|
96367
97103
|
init_formatter();
|
|
96368
97104
|
import { readdirSync as readdirSync7 } from "fs";
|
|
96369
|
-
import { join as
|
|
97105
|
+
import { join as join69 } from "path";
|
|
96370
97106
|
|
|
96371
97107
|
// src/commands/logs-reader.ts
|
|
96372
97108
|
init_paths3();
|
|
96373
|
-
import { existsSync as
|
|
97109
|
+
import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
|
|
96374
97110
|
import { readdir as readdir4 } from "fs/promises";
|
|
96375
|
-
import { join as
|
|
97111
|
+
import { join as join68 } from "path";
|
|
96376
97112
|
var _logsReaderDeps = {
|
|
96377
97113
|
getRunsDir
|
|
96378
97114
|
};
|
|
@@ -96386,7 +97122,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96386
97122
|
}
|
|
96387
97123
|
let matched = null;
|
|
96388
97124
|
for (const entry of entries) {
|
|
96389
|
-
const metaPath =
|
|
97125
|
+
const metaPath = join68(runsDir, entry, "meta.json");
|
|
96390
97126
|
try {
|
|
96391
97127
|
const meta3 = await Bun.file(metaPath).json();
|
|
96392
97128
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -96398,7 +97134,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96398
97134
|
if (!matched) {
|
|
96399
97135
|
throw new Error(`Run not found in registry: ${runId}`);
|
|
96400
97136
|
}
|
|
96401
|
-
if (!
|
|
97137
|
+
if (!existsSync27(matched.eventsDir)) {
|
|
96402
97138
|
console.log(`Log directory unavailable for run: ${runId}`);
|
|
96403
97139
|
return null;
|
|
96404
97140
|
}
|
|
@@ -96408,14 +97144,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96408
97144
|
return null;
|
|
96409
97145
|
}
|
|
96410
97146
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
96411
|
-
return
|
|
97147
|
+
return join68(matched.eventsDir, specificFile ?? files[0]);
|
|
96412
97148
|
}
|
|
96413
97149
|
async function selectRunFile(runsDir) {
|
|
96414
97150
|
const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
96415
97151
|
if (files.length === 0) {
|
|
96416
97152
|
return null;
|
|
96417
97153
|
}
|
|
96418
|
-
return
|
|
97154
|
+
return join68(runsDir, files[0]);
|
|
96419
97155
|
}
|
|
96420
97156
|
async function extractRunSummary(filePath) {
|
|
96421
97157
|
const file3 = Bun.file(filePath);
|
|
@@ -96501,7 +97237,7 @@ Runs:
|
|
|
96501
97237
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
96502
97238
|
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
97239
|
for (const file3 of files) {
|
|
96504
|
-
const filePath =
|
|
97240
|
+
const filePath = join69(runsDir, file3);
|
|
96505
97241
|
const summary = await extractRunSummary(filePath);
|
|
96506
97242
|
const timestamp = file3.replace(".jsonl", "");
|
|
96507
97243
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -96615,7 +97351,7 @@ async function logsCommand(options) {
|
|
|
96615
97351
|
return;
|
|
96616
97352
|
}
|
|
96617
97353
|
const resolved = resolveProject({ dir: options.dir });
|
|
96618
|
-
const naxDir =
|
|
97354
|
+
const naxDir = join70(resolved.projectDir, ".nax");
|
|
96619
97355
|
const configPath = resolved.configPath;
|
|
96620
97356
|
const configFile = Bun.file(configPath);
|
|
96621
97357
|
const config2 = await configFile.json();
|
|
@@ -96623,9 +97359,9 @@ async function logsCommand(options) {
|
|
|
96623
97359
|
if (!featureName) {
|
|
96624
97360
|
throw new Error("No feature specified in config.json");
|
|
96625
97361
|
}
|
|
96626
|
-
const featureDir =
|
|
96627
|
-
const runsDir =
|
|
96628
|
-
if (!
|
|
97362
|
+
const featureDir = join70(naxDir, "features", featureName);
|
|
97363
|
+
const runsDir = join70(featureDir, "runs");
|
|
97364
|
+
if (!existsSync28(runsDir)) {
|
|
96629
97365
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
96630
97366
|
}
|
|
96631
97367
|
if (options.list) {
|
|
@@ -96649,8 +97385,8 @@ init_config();
|
|
|
96649
97385
|
init_prd();
|
|
96650
97386
|
init_precheck();
|
|
96651
97387
|
init_common();
|
|
96652
|
-
import { existsSync as
|
|
96653
|
-
import { join as
|
|
97388
|
+
import { existsSync as existsSync29 } from "fs";
|
|
97389
|
+
import { join as join71 } from "path";
|
|
96654
97390
|
async function precheckCommand(options) {
|
|
96655
97391
|
const resolved = resolveProject({
|
|
96656
97392
|
dir: options.dir,
|
|
@@ -96672,14 +97408,14 @@ async function precheckCommand(options) {
|
|
|
96672
97408
|
process.exit(1);
|
|
96673
97409
|
}
|
|
96674
97410
|
}
|
|
96675
|
-
const naxDir =
|
|
96676
|
-
const featureDir =
|
|
96677
|
-
const prdPath =
|
|
96678
|
-
if (!
|
|
97411
|
+
const naxDir = join71(resolved.projectDir, ".nax");
|
|
97412
|
+
const featureDir = join71(naxDir, "features", featureName);
|
|
97413
|
+
const prdPath = join71(featureDir, "prd.json");
|
|
97414
|
+
if (!existsSync29(featureDir)) {
|
|
96679
97415
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
96680
97416
|
process.exit(1);
|
|
96681
97417
|
}
|
|
96682
|
-
if (!
|
|
97418
|
+
if (!existsSync29(prdPath)) {
|
|
96683
97419
|
console.error(source_default.red(`Missing prd.json for feature: ${featureName}`));
|
|
96684
97420
|
console.error(source_default.dim(`Run: nax plan -f ${featureName} --from spec.md --auto`));
|
|
96685
97421
|
process.exit(EXIT_CODES.INVALID_PRD);
|
|
@@ -96697,7 +97433,7 @@ async function precheckCommand(options) {
|
|
|
96697
97433
|
init_source();
|
|
96698
97434
|
init_paths3();
|
|
96699
97435
|
import { readdir as readdir5 } from "fs/promises";
|
|
96700
|
-
import { join as
|
|
97436
|
+
import { join as join72 } from "path";
|
|
96701
97437
|
var DEFAULT_LIMIT = 20;
|
|
96702
97438
|
var _runsCmdDeps = {
|
|
96703
97439
|
getRunsDir
|
|
@@ -96752,7 +97488,7 @@ async function runsCommand(options = {}) {
|
|
|
96752
97488
|
}
|
|
96753
97489
|
const rows = [];
|
|
96754
97490
|
for (const entry of entries) {
|
|
96755
|
-
const metaPath =
|
|
97491
|
+
const metaPath = join72(runsDir, entry, "meta.json");
|
|
96756
97492
|
let meta3;
|
|
96757
97493
|
try {
|
|
96758
97494
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -96829,7 +97565,7 @@ async function runsCommand(options = {}) {
|
|
|
96829
97565
|
|
|
96830
97566
|
// src/commands/unlock.ts
|
|
96831
97567
|
init_source();
|
|
96832
|
-
import { join as
|
|
97568
|
+
import { join as join73 } from "path";
|
|
96833
97569
|
function isProcessAlive2(pid) {
|
|
96834
97570
|
try {
|
|
96835
97571
|
process.kill(pid, 0);
|
|
@@ -96844,7 +97580,7 @@ function formatLockAge(ageMs) {
|
|
|
96844
97580
|
}
|
|
96845
97581
|
async function unlockCommand(options) {
|
|
96846
97582
|
const workdir = options.dir ?? process.cwd();
|
|
96847
|
-
const lockPath =
|
|
97583
|
+
const lockPath = join73(workdir, "nax.lock");
|
|
96848
97584
|
const lockFile = Bun.file(lockPath);
|
|
96849
97585
|
const exists = await lockFile.exists();
|
|
96850
97586
|
if (!exists) {
|
|
@@ -105268,8 +106004,8 @@ Next: nax generate --package ${options.package}`));
|
|
|
105268
106004
|
}
|
|
105269
106005
|
return;
|
|
105270
106006
|
}
|
|
105271
|
-
const naxDir =
|
|
105272
|
-
if (
|
|
106007
|
+
const naxDir = join87(workdir, ".nax");
|
|
106008
|
+
if (existsSync35(naxDir) && !options.force) {
|
|
105273
106009
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
105274
106010
|
return;
|
|
105275
106011
|
}
|
|
@@ -105297,11 +106033,11 @@ Next: nax generate --package ${options.package}`));
|
|
|
105297
106033
|
}
|
|
105298
106034
|
}
|
|
105299
106035
|
}
|
|
105300
|
-
mkdirSync7(
|
|
105301
|
-
mkdirSync7(
|
|
106036
|
+
mkdirSync7(join87(naxDir, "features"), { recursive: true });
|
|
106037
|
+
mkdirSync7(join87(naxDir, "hooks"), { recursive: true });
|
|
105302
106038
|
const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
|
|
105303
|
-
await Bun.write(
|
|
105304
|
-
await Bun.write(
|
|
106039
|
+
await Bun.write(join87(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
|
|
106040
|
+
await Bun.write(join87(naxDir, "hooks.json"), JSON.stringify({
|
|
105305
106041
|
hooks: {
|
|
105306
106042
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
105307
106043
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -105309,12 +106045,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
105309
106045
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
105310
106046
|
}
|
|
105311
106047
|
}, null, 2));
|
|
105312
|
-
await Bun.write(
|
|
106048
|
+
await Bun.write(join87(naxDir, ".gitignore"), `# nax temp files
|
|
105313
106049
|
*.tmp
|
|
105314
106050
|
.paused.json
|
|
105315
106051
|
.nax-verifier-verdict.json
|
|
105316
106052
|
`);
|
|
105317
|
-
await Bun.write(
|
|
106053
|
+
await Bun.write(join87(naxDir, "context.md"), `# Project Context
|
|
105318
106054
|
|
|
105319
106055
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
105320
106056
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -105399,6 +106135,24 @@ Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cu
|
|
|
105399
106135
|
console.log(source_default.dim(`
|
|
105400
106136
|
Next: nax features create <name>`));
|
|
105401
106137
|
});
|
|
106138
|
+
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) => {
|
|
106139
|
+
let workdir;
|
|
106140
|
+
try {
|
|
106141
|
+
workdir = validateDirectory(options.dir);
|
|
106142
|
+
} catch (err) {
|
|
106143
|
+
console.error(source_default.red(`Invalid directory: ${err.message}`));
|
|
106144
|
+
process.exit(1);
|
|
106145
|
+
}
|
|
106146
|
+
const { setupCommand: setupCommand2 } = await Promise.resolve().then(() => (init_setup(), exports_setup));
|
|
106147
|
+
const exitCode = await setupCommand2({
|
|
106148
|
+
dir: workdir,
|
|
106149
|
+
agent: options.agent,
|
|
106150
|
+
fillScripts: options.fillScripts,
|
|
106151
|
+
dryRun: options.dryRun,
|
|
106152
|
+
force: options.force
|
|
106153
|
+
});
|
|
106154
|
+
process.exit(exitCode);
|
|
106155
|
+
});
|
|
105402
106156
|
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
106157
|
let workdir;
|
|
105404
106158
|
try {
|
|
@@ -105411,7 +106165,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105411
106165
|
console.error(source_default.red("Error: --plan requires --from <spec-path>"));
|
|
105412
106166
|
process.exit(1);
|
|
105413
106167
|
}
|
|
105414
|
-
if (options.from && !
|
|
106168
|
+
if (options.from && !existsSync35(options.from)) {
|
|
105415
106169
|
console.error(source_default.red(`Error: File not found: ${options.from} (required with --plan)`));
|
|
105416
106170
|
process.exit(1);
|
|
105417
106171
|
}
|
|
@@ -105444,10 +106198,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105444
106198
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105445
106199
|
process.exit(1);
|
|
105446
106200
|
}
|
|
105447
|
-
const featureDir =
|
|
105448
|
-
const prdPath =
|
|
106201
|
+
const featureDir = join87(naxDir, "features", options.feature);
|
|
106202
|
+
const prdPath = join87(featureDir, "prd.json");
|
|
105449
106203
|
if (options.plan && options.from) {
|
|
105450
|
-
if (
|
|
106204
|
+
if (existsSync35(prdPath) && !options.force) {
|
|
105451
106205
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
105452
106206
|
console.error(source_default.dim(" Use --force to overwrite, or run without --plan to use the existing PRD."));
|
|
105453
106207
|
process.exit(1);
|
|
@@ -105467,10 +106221,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105467
106221
|
}
|
|
105468
106222
|
}
|
|
105469
106223
|
try {
|
|
105470
|
-
const planLogDir =
|
|
106224
|
+
const planLogDir = join87(featureDir, "plan");
|
|
105471
106225
|
mkdirSync7(planLogDir, { recursive: true });
|
|
105472
106226
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105473
|
-
const planLogPath =
|
|
106227
|
+
const planLogPath = join87(planLogDir, `${planLogId}.jsonl`);
|
|
105474
106228
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
105475
106229
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
105476
106230
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -105509,17 +106263,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105509
106263
|
process.exit(1);
|
|
105510
106264
|
}
|
|
105511
106265
|
}
|
|
105512
|
-
if (!
|
|
106266
|
+
if (!existsSync35(prdPath)) {
|
|
105513
106267
|
console.error(source_default.red(`Feature "${options.feature}" not found or missing prd.json`));
|
|
105514
106268
|
process.exit(1);
|
|
105515
106269
|
}
|
|
105516
106270
|
resetLogger();
|
|
105517
106271
|
const projectKey = config2.name?.trim() || basename16(workdir);
|
|
105518
106272
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
105519
|
-
const runsDir =
|
|
106273
|
+
const runsDir = join87(outputDir, "features", options.feature, "runs");
|
|
105520
106274
|
mkdirSync7(runsDir, { recursive: true });
|
|
105521
106275
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105522
|
-
const logFilePath =
|
|
106276
|
+
const logFilePath = join87(runsDir, `${runId}.jsonl`);
|
|
105523
106277
|
const isTTY = process.stdout.isTTY ?? false;
|
|
105524
106278
|
const headlessFlag = options.headless ?? false;
|
|
105525
106279
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -105537,7 +106291,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105537
106291
|
config2.agent.default = options.agent;
|
|
105538
106292
|
}
|
|
105539
106293
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
105540
|
-
const globalNaxDir =
|
|
106294
|
+
const globalNaxDir = join87(homedir3(), ".nax");
|
|
105541
106295
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
105542
106296
|
const eventEmitter = new PipelineEventEmitter;
|
|
105543
106297
|
const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
|
|
@@ -105557,12 +106311,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105557
106311
|
events: eventEmitter,
|
|
105558
106312
|
ptyOptions: null,
|
|
105559
106313
|
agentStreamEvents,
|
|
105560
|
-
queueFilePath:
|
|
106314
|
+
queueFilePath: join87(workdir, ".queue.txt")
|
|
105561
106315
|
});
|
|
105562
106316
|
} else {
|
|
105563
106317
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
105564
106318
|
}
|
|
105565
|
-
const statusFilePath =
|
|
106319
|
+
const statusFilePath = join87(outputDir, "status.json");
|
|
105566
106320
|
let parallel;
|
|
105567
106321
|
if (options.parallel !== undefined) {
|
|
105568
106322
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -105589,9 +106343,9 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105589
106343
|
skipPrecheck: options.skipPrecheck ?? false,
|
|
105590
106344
|
agentStreamEvents
|
|
105591
106345
|
});
|
|
105592
|
-
const latestSymlink =
|
|
106346
|
+
const latestSymlink = join87(runsDir, "latest.jsonl");
|
|
105593
106347
|
try {
|
|
105594
|
-
if (
|
|
106348
|
+
if (existsSync35(latestSymlink)) {
|
|
105595
106349
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
105596
106350
|
}
|
|
105597
106351
|
Bun.spawnSync(["ln", "-s", `${runId}.jsonl`, latestSymlink], {
|
|
@@ -105650,9 +106404,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
105650
106404
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105651
106405
|
process.exit(1);
|
|
105652
106406
|
}
|
|
105653
|
-
const featureDir =
|
|
106407
|
+
const featureDir = join87(naxDir, "features", name);
|
|
105654
106408
|
mkdirSync7(featureDir, { recursive: true });
|
|
105655
|
-
await Bun.write(
|
|
106409
|
+
await Bun.write(join87(featureDir, "spec.md"), `# Feature: ${name}
|
|
105656
106410
|
|
|
105657
106411
|
## Overview
|
|
105658
106412
|
|
|
@@ -105685,7 +106439,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
105685
106439
|
|
|
105686
106440
|
<!-- What this feature explicitly does NOT cover. -->
|
|
105687
106441
|
`);
|
|
105688
|
-
await Bun.write(
|
|
106442
|
+
await Bun.write(join87(featureDir, "progress.txt"), `# Progress: ${name}
|
|
105689
106443
|
|
|
105690
106444
|
Created: ${new Date().toISOString()}
|
|
105691
106445
|
|
|
@@ -105711,8 +106465,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
105711
106465
|
console.error(source_default.red("nax not initialized."));
|
|
105712
106466
|
process.exit(1);
|
|
105713
106467
|
}
|
|
105714
|
-
const featuresDir =
|
|
105715
|
-
if (!
|
|
106468
|
+
const featuresDir = join87(naxDir, "features");
|
|
106469
|
+
if (!existsSync35(featuresDir)) {
|
|
105716
106470
|
console.log(source_default.dim("No features yet."));
|
|
105717
106471
|
return;
|
|
105718
106472
|
}
|
|
@@ -105726,8 +106480,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
105726
106480
|
Features:
|
|
105727
106481
|
`));
|
|
105728
106482
|
for (const name of entries) {
|
|
105729
|
-
const prdPath =
|
|
105730
|
-
if (
|
|
106483
|
+
const prdPath = join87(featuresDir, name, "prd.json");
|
|
106484
|
+
if (existsSync35(prdPath)) {
|
|
105731
106485
|
const prd = await loadPRD(prdPath);
|
|
105732
106486
|
const c = countStories(prd);
|
|
105733
106487
|
console.log(` ${name} \u2014 ${c.passed}/${c.total} stories done`);
|
|
@@ -105761,10 +106515,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
105761
106515
|
cliOverrides.profile = options.profile;
|
|
105762
106516
|
}
|
|
105763
106517
|
const config2 = await loadConfig(workdir, cliOverrides);
|
|
105764
|
-
const featureLogDir =
|
|
106518
|
+
const featureLogDir = join87(naxDir, "features", options.feature, "plan");
|
|
105765
106519
|
mkdirSync7(featureLogDir, { recursive: true });
|
|
105766
106520
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105767
|
-
const planLogPath =
|
|
106521
|
+
const planLogPath = join87(featureLogDir, `${planLogId}.jsonl`);
|
|
105768
106522
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
105769
106523
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
105770
106524
|
try {
|