@nathapp/nax 0.68.8 → 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 +1278 -397
- 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();
|
|
@@ -20944,12 +21117,21 @@ function parseAcpxJsonLine(line, state) {
|
|
|
20944
21117
|
}
|
|
20945
21118
|
if (update.sessionUpdate === "usage_update") {
|
|
20946
21119
|
const activity = { kind: "usage_update" };
|
|
20947
|
-
|
|
21120
|
+
const metaUsage = update._meta != null && typeof update._meta === "object" ? update._meta.usage : undefined;
|
|
21121
|
+
if (metaUsage != null && typeof metaUsage === "object") {
|
|
21122
|
+
const inp = metaUsage.inputTokens ?? metaUsage.input_tokens;
|
|
21123
|
+
if (typeof inp === "number")
|
|
21124
|
+
activity.inputTokens = inp;
|
|
21125
|
+
const out = metaUsage.outputTokens ?? metaUsage.output_tokens;
|
|
21126
|
+
if (typeof out === "number")
|
|
21127
|
+
activity.outputTokens = out;
|
|
21128
|
+
}
|
|
21129
|
+
if (activity.outputTokens == null && typeof update.used === "number") {
|
|
20948
21130
|
activity.outputTokens = update.used;
|
|
20949
21131
|
}
|
|
20950
21132
|
if (typeof update.cost?.amount === "number") {
|
|
20951
21133
|
activity.costUsd = update.cost.amount;
|
|
20952
|
-
state.exactCostUsd =
|
|
21134
|
+
state.exactCostUsd = activity.costUsd;
|
|
20953
21135
|
}
|
|
20954
21136
|
return activity;
|
|
20955
21137
|
}
|
|
@@ -32632,12 +32814,14 @@ var init_adversarial_review = __esm(() => {
|
|
|
32632
32814
|
});
|
|
32633
32815
|
const { accepted, dropped } = filterByAcQuote(substantiated, input.story.acceptanceCriteria);
|
|
32634
32816
|
const blocking = accepted.filter((f) => isBlockingSeverity(f.severity, threshold));
|
|
32817
|
+
const advisory = accepted.filter((f) => !isBlockingSeverity(f.severity, threshold));
|
|
32635
32818
|
const passed = parsed.passed && blocking.length === 0;
|
|
32636
32819
|
return {
|
|
32637
32820
|
...parsed,
|
|
32638
32821
|
passed,
|
|
32639
32822
|
findings: accepted,
|
|
32640
32823
|
normalizedFindings: toAdversarialReviewFindings(blocking),
|
|
32824
|
+
advisoryFindings: toAdversarialReviewFindings(advisory),
|
|
32641
32825
|
acDropped: dropped
|
|
32642
32826
|
};
|
|
32643
32827
|
}
|
|
@@ -34337,7 +34521,25 @@ async function readSpecDriftViolations(input) {
|
|
|
34337
34521
|
return [];
|
|
34338
34522
|
}
|
|
34339
34523
|
}
|
|
34340
|
-
|
|
34524
|
+
function collectUpstreamProducedFiles(story, byId) {
|
|
34525
|
+
const produced = new Set;
|
|
34526
|
+
const seen = new Set;
|
|
34527
|
+
const stack = [...story.dependencies ?? []];
|
|
34528
|
+
while (stack.length > 0) {
|
|
34529
|
+
const depId = stack.pop();
|
|
34530
|
+
if (!depId || seen.has(depId))
|
|
34531
|
+
continue;
|
|
34532
|
+
seen.add(depId);
|
|
34533
|
+
const dep = byId.get(depId);
|
|
34534
|
+
if (!dep)
|
|
34535
|
+
continue;
|
|
34536
|
+
for (const filePath of getExpectedFiles(dep))
|
|
34537
|
+
produced.add(filePath);
|
|
34538
|
+
stack.push(...dep.dependencies ?? []);
|
|
34539
|
+
}
|
|
34540
|
+
return produced;
|
|
34541
|
+
}
|
|
34542
|
+
async function normalizeStoryFiles(story, workdir, fileExists, upstreamProduced) {
|
|
34341
34543
|
const contextFiles = story.contextFiles ?? [];
|
|
34342
34544
|
if (contextFiles.length === 0)
|
|
34343
34545
|
return { story, changed: false };
|
|
@@ -34352,6 +34554,14 @@ async function normalizeStoryFiles(story, workdir, fileExists) {
|
|
|
34352
34554
|
kept.push(entry);
|
|
34353
34555
|
continue;
|
|
34354
34556
|
}
|
|
34557
|
+
if (upstreamProduced.has(filePath)) {
|
|
34558
|
+
kept.push(entry);
|
|
34559
|
+
logger?.debug("plan", "Kept cross-story produced file in contextFiles (upstream dependency creates it)", {
|
|
34560
|
+
storyId: story.id,
|
|
34561
|
+
filePath
|
|
34562
|
+
});
|
|
34563
|
+
continue;
|
|
34564
|
+
}
|
|
34355
34565
|
if (factId) {
|
|
34356
34566
|
logger?.warn("plan", "Context file cites a manifest fact but is absent on disk", {
|
|
34357
34567
|
storyId: story.id,
|
|
@@ -34379,7 +34589,8 @@ async function normalizeStoryFiles(story, workdir, fileExists) {
|
|
|
34379
34589
|
async function normalizeCreatedContextFiles(prd, workdir, fileExists) {
|
|
34380
34590
|
if (!workdir)
|
|
34381
34591
|
return prd;
|
|
34382
|
-
const
|
|
34592
|
+
const byId = new Map(prd.userStories.map((story) => [story.id, story]));
|
|
34593
|
+
const results = await Promise.all(prd.userStories.map((story) => normalizeStoryFiles(story, workdir, fileExists, collectUpstreamProducedFiles(story, byId))));
|
|
34383
34594
|
if (!results.some((r) => r.changed))
|
|
34384
34595
|
return prd;
|
|
34385
34596
|
return { ...prd, userStories: results.map((r) => r.story) };
|
|
@@ -38804,6 +39015,159 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
|
|
|
38804
39015
|
var defaultFileExists = (p) => Bun.file(p).exists();
|
|
38805
39016
|
var init_validate_mock_structure_files = () => {};
|
|
38806
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
|
+
|
|
38807
39171
|
// src/operations/declaration-sink.ts
|
|
38808
39172
|
function makeDeclarationSink() {
|
|
38809
39173
|
return { testEdits: [], mockHandoffs: [] };
|
|
@@ -39405,6 +39769,7 @@ var init_operations = __esm(() => {
|
|
|
39405
39769
|
init_autofix_test_writer_strategy();
|
|
39406
39770
|
init_apply_test_edit_declarations();
|
|
39407
39771
|
init_validate_mock_structure_files();
|
|
39772
|
+
init_setup_generate();
|
|
39408
39773
|
init__finding_to_check();
|
|
39409
39774
|
init_mechanical_lintfix_strategy();
|
|
39410
39775
|
init_mechanical_formatfix_strategy();
|
|
@@ -42566,9 +42931,9 @@ ${rows.join(`
|
|
|
42566
42931
|
`)}
|
|
42567
42932
|
`;
|
|
42568
42933
|
}
|
|
42569
|
-
var CONTEXT_VS_EXPECTED_FILES_RULE = `**\`contextFiles\` rule \u2014
|
|
42934
|
+
var CONTEXT_VS_EXPECTED_FILES_RULE = `**\`contextFiles\` rule \u2014 files readable when this story runs.** List paths that already exist in the repo today, PLUS any file an UPSTREAM dependency story creates (it does not exist now but will exist by the time this story runs, because dependencies execute first). The pipeline verifies every \`contextFiles\` entry against the filesystem; a path that exists neither on disk nor in an upstream dependency's outputs is treated as a missing-context warning.
|
|
42570
42935
|
|
|
42571
|
-
**\`expectedFiles\` rule \u2014 files
|
|
42936
|
+
**\`expectedFiles\` rule \u2014 files THIS story CREATES.** List every NEW file this story authors (relative paths). A file this story creates belongs here, NEVER in \`contextFiles\` \u2014 these are the story's outputs, not files to read first. A file created by an upstream dependency and only read/modified here belongs in \`contextFiles\`, NOT here (this story does not author it). A single path may appear in \`contextFiles\` (an existing sibling to mirror) AND \`expectedFiles\` (the new file itself), but the same path must never be in both.`, EXPECTED_FILES_SCHEMA_FIELD = `"expectedFiles": ["string \u2014 NEW files this story creates (relative paths, omit if none)"],`;
|
|
42572
42937
|
var init_plan_builder = __esm(() => {
|
|
42573
42938
|
init_config();
|
|
42574
42939
|
});
|
|
@@ -44619,7 +44984,8 @@ var init_session_role = __esm(() => {
|
|
|
44619
44984
|
"fix-gen",
|
|
44620
44985
|
"auto",
|
|
44621
44986
|
"synthesis",
|
|
44622
|
-
"judge"
|
|
44987
|
+
"judge",
|
|
44988
|
+
"setup"
|
|
44623
44989
|
];
|
|
44624
44990
|
});
|
|
44625
44991
|
|
|
@@ -53496,6 +53862,108 @@ var init_context2 = __esm(() => {
|
|
|
53496
53862
|
};
|
|
53497
53863
|
});
|
|
53498
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
|
+
|
|
53499
53967
|
// src/execution/story-orchestrator.ts
|
|
53500
53968
|
async function refreshReviewInputForDispatch(opName, input) {
|
|
53501
53969
|
if (opName !== "semantic-review" && opName !== "adversarial-review")
|
|
@@ -53935,16 +54403,17 @@ function withIncreasingFailuresBail(strategies, enabled, consecutiveIncreases) {
|
|
|
53935
54403
|
}
|
|
53936
54404
|
}));
|
|
53937
54405
|
}
|
|
53938
|
-
async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
54406
|
+
async function runRectification(ctx, state, phaseCosts, phaseOutputs, overrides) {
|
|
53939
54407
|
const rectification = state.rectification;
|
|
53940
|
-
const
|
|
54408
|
+
const baseValidationPhases = collectRectificationPhases(state);
|
|
54409
|
+
const validationPhases = overrides?.excludePhaseKinds ? baseValidationPhases.filter((p) => !overrides.excludePhaseKinds?.includes(p.kind)) : baseValidationPhases;
|
|
53941
54410
|
if (!rectification || validationPhases.length === 0) {
|
|
53942
54411
|
return {};
|
|
53943
54412
|
}
|
|
53944
54413
|
if (ctx.runtime.signal?.aborted) {
|
|
53945
54414
|
return {};
|
|
53946
54415
|
}
|
|
53947
|
-
const initialFindings = gatherRectificationFindings(phaseOutputs, validationPhases, state);
|
|
54416
|
+
const initialFindings = overrides?.initialFindings ? [...overrides.initialFindings] : gatherRectificationFindings(phaseOutputs, validationPhases, state);
|
|
53948
54417
|
if (initialFindings.length === 0) {
|
|
53949
54418
|
return {};
|
|
53950
54419
|
}
|
|
@@ -53959,14 +54428,16 @@ async function runRectification(ctx, state, phaseCosts, phaseOutputs) {
|
|
|
53959
54428
|
const cycle = {
|
|
53960
54429
|
findings: [...initialFindings],
|
|
53961
54430
|
iterations: [],
|
|
53962
|
-
strategies: withIncreasingFailuresBail(rectification.strategies, rectification.abortOnIncreasingFailures, rectification.consecutiveIncreasesToBail ?? 1),
|
|
53963
|
-
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 },
|
|
53964
54433
|
validate: async (_validateCtx, opts) => {
|
|
53965
54434
|
if (ctx.runtime.signal?.aborted)
|
|
53966
54435
|
return { findings: [], shortCircuited: false };
|
|
53967
54436
|
const lite = (opts?.mode ?? "full") === "lite";
|
|
53968
54437
|
const selected = phasesToRevalidate(opts?.strategiesRun, validationPhases);
|
|
53969
|
-
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;
|
|
53970
54441
|
getSafeLogger()?.debug("story-orchestrator", "rectification validate scope", {
|
|
53971
54442
|
storyId: ctx.storyId,
|
|
53972
54443
|
mode: opts?.mode ?? "full",
|
|
@@ -54118,6 +54589,25 @@ class ExecutionPlan {
|
|
|
54118
54589
|
}
|
|
54119
54590
|
}
|
|
54120
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
|
+
}
|
|
54121
54611
|
const verifierName = this.state.verifier?.slot.op.name;
|
|
54122
54612
|
const gateName = this.state.fullSuiteGate?.slot.op.name;
|
|
54123
54613
|
const verifierPassedSsot = verifierName !== undefined && phaseExplicitlyPassed(phaseOutputs[verifierName]);
|
|
@@ -54210,6 +54700,11 @@ class StoryOrchestratorBuilder {
|
|
|
54210
54700
|
this.state.rectification = opts;
|
|
54211
54701
|
return this;
|
|
54212
54702
|
}
|
|
54703
|
+
addNonBlockingFix(cfg, strategies) {
|
|
54704
|
+
this.state.nonBlockingFix = cfg;
|
|
54705
|
+
this.state.nonBlockingFixStrategies = strategies;
|
|
54706
|
+
return this;
|
|
54707
|
+
}
|
|
54213
54708
|
build(ctx, opts = {}) {
|
|
54214
54709
|
if (!this.state.implementer) {
|
|
54215
54710
|
throw new NaxError("StoryOrchestratorBuilder.build(): addImplementer() must be called before build()", "ORCHESTRATOR_NO_IMPLEMENTER", { stage: "execution" });
|
|
@@ -54227,6 +54722,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
54227
54722
|
init_event_bus();
|
|
54228
54723
|
init_prepare_inputs();
|
|
54229
54724
|
init_git();
|
|
54725
|
+
init_non_blocking_fix();
|
|
54230
54726
|
_storyOrchestratorDeps = {
|
|
54231
54727
|
callOp,
|
|
54232
54728
|
runFixCycle,
|
|
@@ -54379,6 +54875,22 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
54379
54875
|
};
|
|
54380
54876
|
builder.addRectification(rectOpts);
|
|
54381
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
|
+
}
|
|
54382
54894
|
return builder.build(ctx, { isThreeSession });
|
|
54383
54895
|
}
|
|
54384
54896
|
var init_build_plan_for_strategy = __esm(() => {
|
|
@@ -54642,41 +55154,6 @@ var init_execution_helpers = __esm(() => {
|
|
|
54642
55154
|
init_errors();
|
|
54643
55155
|
});
|
|
54644
55156
|
|
|
54645
|
-
// src/tdd/rollback.ts
|
|
54646
|
-
async function rollbackToRef(workdir, ref) {
|
|
54647
|
-
const logger = getLogger();
|
|
54648
|
-
logger.warn("tdd", "Rolling back git changes", { ref });
|
|
54649
|
-
const resetProc = _rollbackDeps.spawn(["git", "reset", "--hard", ref], {
|
|
54650
|
-
cwd: workdir,
|
|
54651
|
-
stdout: "pipe",
|
|
54652
|
-
stderr: "pipe"
|
|
54653
|
-
});
|
|
54654
|
-
const exitCode = await resetProc.exited;
|
|
54655
|
-
if (exitCode !== 0) {
|
|
54656
|
-
const stderr = await new Response(resetProc.stderr).text();
|
|
54657
|
-
logger.error("tdd", "Failed to rollback git changes", { ref, stderr });
|
|
54658
|
-
throw new Error(`Git rollback failed: ${stderr}`);
|
|
54659
|
-
}
|
|
54660
|
-
const cleanProc = _rollbackDeps.spawn(["git", "clean", "-fd"], {
|
|
54661
|
-
cwd: workdir,
|
|
54662
|
-
stdout: "pipe",
|
|
54663
|
-
stderr: "pipe"
|
|
54664
|
-
});
|
|
54665
|
-
const cleanExitCode = await cleanProc.exited;
|
|
54666
|
-
if (cleanExitCode !== 0) {
|
|
54667
|
-
const stderr = await new Response(cleanProc.stderr).text();
|
|
54668
|
-
logger.warn("tdd", "Failed to clean untracked files", { stderr });
|
|
54669
|
-
}
|
|
54670
|
-
logger.info("tdd", "Successfully rolled back git changes", { ref });
|
|
54671
|
-
}
|
|
54672
|
-
var _rollbackDeps;
|
|
54673
|
-
var init_rollback = __esm(() => {
|
|
54674
|
-
init_logger2();
|
|
54675
|
-
_rollbackDeps = {
|
|
54676
|
-
spawn: Bun.spawn
|
|
54677
|
-
};
|
|
54678
|
-
});
|
|
54679
|
-
|
|
54680
55157
|
// src/execution/session-manager-runtime.ts
|
|
54681
55158
|
async function closePhysicalSession(descriptor, agentGetFn, force) {
|
|
54682
55159
|
if (!descriptor.handle)
|
|
@@ -56251,9 +56728,14 @@ __export(exports_init_context, {
|
|
|
56251
56728
|
generateContextTemplate: () => generateContextTemplate,
|
|
56252
56729
|
_initContextDeps: () => _initContextDeps
|
|
56253
56730
|
});
|
|
56254
|
-
import { existsSync as existsSync22 } from "fs";
|
|
56255
|
-
import { mkdir as mkdir8 } from "fs/promises";
|
|
56256
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
|
+
}
|
|
56257
56739
|
async function findFiles(dir, maxFiles = 200) {
|
|
56258
56740
|
try {
|
|
56259
56741
|
const proc = Bun.spawnSync([
|
|
@@ -56282,7 +56764,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
56282
56764
|
}
|
|
56283
56765
|
async function readPackageManifest(projectRoot) {
|
|
56284
56766
|
const packageJsonPath = join51(projectRoot, "package.json");
|
|
56285
|
-
if (!
|
|
56767
|
+
if (!await bunFileExists(packageJsonPath)) {
|
|
56286
56768
|
return null;
|
|
56287
56769
|
}
|
|
56288
56770
|
try {
|
|
@@ -56300,7 +56782,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
56300
56782
|
}
|
|
56301
56783
|
async function readReadmeSnippet(projectRoot) {
|
|
56302
56784
|
const readmePath = join51(projectRoot, "README.md");
|
|
56303
|
-
if (!
|
|
56785
|
+
if (!await bunFileExists(readmePath)) {
|
|
56304
56786
|
return null;
|
|
56305
56787
|
}
|
|
56306
56788
|
try {
|
|
@@ -56318,7 +56800,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
56318
56800
|
const found = [];
|
|
56319
56801
|
for (const candidate of candidates) {
|
|
56320
56802
|
const path13 = join51(projectRoot, candidate);
|
|
56321
|
-
if (
|
|
56803
|
+
if (await bunFileExists(path13)) {
|
|
56322
56804
|
found.push(candidate);
|
|
56323
56805
|
}
|
|
56324
56806
|
}
|
|
@@ -56329,7 +56811,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
56329
56811
|
const found = [];
|
|
56330
56812
|
for (const candidate of candidates) {
|
|
56331
56813
|
const path13 = join51(projectRoot, candidate);
|
|
56332
|
-
if (
|
|
56814
|
+
if (await bunFileExists(path13)) {
|
|
56333
56815
|
found.push(candidate);
|
|
56334
56816
|
}
|
|
56335
56817
|
}
|
|
@@ -56459,10 +56941,10 @@ Keep it under 2000 tokens. Use markdown formatting. Be specific to the detected
|
|
|
56459
56941
|
`;
|
|
56460
56942
|
try {
|
|
56461
56943
|
const result = await _initContextDeps.callLLM(prompt);
|
|
56462
|
-
logger.info("init", "Generated context.md with LLM");
|
|
56944
|
+
logger.info("init", "Generated context.md with LLM", { storyId: "init-context" });
|
|
56463
56945
|
return result;
|
|
56464
56946
|
} catch (err) {
|
|
56465
|
-
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" });
|
|
56466
56948
|
return generateContextTemplate(scan);
|
|
56467
56949
|
}
|
|
56468
56950
|
}
|
|
@@ -56491,27 +56973,33 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
56491
56973
|
const logger = getLogger();
|
|
56492
56974
|
const naxDir = join51(repoRoot, ".nax", "mono", packagePath);
|
|
56493
56975
|
const contextPath = join51(naxDir, "context.md");
|
|
56494
|
-
if (
|
|
56495
|
-
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
|
+
});
|
|
56496
56981
|
return;
|
|
56497
56982
|
}
|
|
56498
|
-
if (!
|
|
56499
|
-
await
|
|
56983
|
+
if (!await bunFileExists(naxDir)) {
|
|
56984
|
+
await bunMkdirp(naxDir);
|
|
56500
56985
|
}
|
|
56501
56986
|
const content = generatePackageContextTemplate(packagePath);
|
|
56502
56987
|
await Bun.write(contextPath, content);
|
|
56503
|
-
logger.info("init", "Created package context.md", { path: contextPath });
|
|
56988
|
+
logger.info("init", "Created package context.md", { storyId: "init-context", path: contextPath });
|
|
56504
56989
|
}
|
|
56505
56990
|
async function initContext(projectRoot, options = {}) {
|
|
56506
56991
|
const logger = getLogger();
|
|
56507
56992
|
const naxDir = join51(projectRoot, ".nax");
|
|
56508
56993
|
const contextPath = join51(naxDir, "context.md");
|
|
56509
|
-
if (
|
|
56510
|
-
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
|
+
});
|
|
56511
56999
|
return;
|
|
56512
57000
|
}
|
|
56513
|
-
if (!
|
|
56514
|
-
await
|
|
57001
|
+
if (!await bunFileExists(naxDir)) {
|
|
57002
|
+
await bunMkdirp(naxDir);
|
|
56515
57003
|
}
|
|
56516
57004
|
const scan = await scanProject(projectRoot);
|
|
56517
57005
|
let content;
|
|
@@ -56521,7 +57009,10 @@ async function initContext(projectRoot, options = {}) {
|
|
|
56521
57009
|
content = generateContextTemplate(scan);
|
|
56522
57010
|
}
|
|
56523
57011
|
await Bun.write(contextPath, content);
|
|
56524
|
-
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
|
+
});
|
|
56525
57016
|
}
|
|
56526
57017
|
var _initContextDeps;
|
|
56527
57018
|
var init_init_context = __esm(() => {
|
|
@@ -56534,11 +57025,11 @@ var init_init_context = __esm(() => {
|
|
|
56534
57025
|
});
|
|
56535
57026
|
|
|
56536
57027
|
// src/cli/init-detect.ts
|
|
56537
|
-
import { existsSync as
|
|
57028
|
+
import { existsSync as existsSync22, readFileSync } from "fs";
|
|
56538
57029
|
import { join as join52 } from "path";
|
|
56539
57030
|
function readPackageJson(projectRoot) {
|
|
56540
57031
|
const pkgPath = join52(projectRoot, "package.json");
|
|
56541
|
-
if (!
|
|
57032
|
+
if (!existsSync22(pkgPath))
|
|
56542
57033
|
return;
|
|
56543
57034
|
try {
|
|
56544
57035
|
return JSON.parse(readFileSync(pkgPath, "utf-8"));
|
|
@@ -56580,41 +57071,41 @@ function detectStack(projectRoot) {
|
|
|
56580
57071
|
};
|
|
56581
57072
|
}
|
|
56582
57073
|
function detectRuntime(projectRoot) {
|
|
56583
|
-
if (
|
|
57074
|
+
if (existsSync22(join52(projectRoot, "bun.lockb")) || existsSync22(join52(projectRoot, "bunfig.toml"))) {
|
|
56584
57075
|
return "bun";
|
|
56585
57076
|
}
|
|
56586
|
-
if (
|
|
57077
|
+
if (existsSync22(join52(projectRoot, "package-lock.json")) || existsSync22(join52(projectRoot, "yarn.lock")) || existsSync22(join52(projectRoot, "pnpm-lock.yaml"))) {
|
|
56587
57078
|
return "node";
|
|
56588
57079
|
}
|
|
56589
57080
|
return "unknown";
|
|
56590
57081
|
}
|
|
56591
57082
|
function detectLanguage2(projectRoot) {
|
|
56592
|
-
if (
|
|
57083
|
+
if (existsSync22(join52(projectRoot, "tsconfig.json")))
|
|
56593
57084
|
return "typescript";
|
|
56594
|
-
if (
|
|
57085
|
+
if (existsSync22(join52(projectRoot, "pyproject.toml")) || existsSync22(join52(projectRoot, "setup.py"))) {
|
|
56595
57086
|
return "python";
|
|
56596
57087
|
}
|
|
56597
|
-
if (
|
|
57088
|
+
if (existsSync22(join52(projectRoot, "Cargo.toml")))
|
|
56598
57089
|
return "rust";
|
|
56599
|
-
if (
|
|
57090
|
+
if (existsSync22(join52(projectRoot, "go.mod")))
|
|
56600
57091
|
return "go";
|
|
56601
57092
|
return "unknown";
|
|
56602
57093
|
}
|
|
56603
57094
|
function detectLinter(projectRoot) {
|
|
56604
|
-
if (
|
|
57095
|
+
if (existsSync22(join52(projectRoot, "biome.json")) || existsSync22(join52(projectRoot, "biome.jsonc"))) {
|
|
56605
57096
|
return "biome";
|
|
56606
57097
|
}
|
|
56607
|
-
if (
|
|
57098
|
+
if (existsSync22(join52(projectRoot, ".eslintrc.json")) || existsSync22(join52(projectRoot, ".eslintrc.js")) || existsSync22(join52(projectRoot, "eslint.config.js"))) {
|
|
56608
57099
|
return "eslint";
|
|
56609
57100
|
}
|
|
56610
57101
|
return "unknown";
|
|
56611
57102
|
}
|
|
56612
57103
|
function detectMonorepo(projectRoot) {
|
|
56613
|
-
if (
|
|
57104
|
+
if (existsSync22(join52(projectRoot, "turbo.json")))
|
|
56614
57105
|
return "turborepo";
|
|
56615
|
-
if (
|
|
57106
|
+
if (existsSync22(join52(projectRoot, "nx.json")))
|
|
56616
57107
|
return "nx";
|
|
56617
|
-
if (
|
|
57108
|
+
if (existsSync22(join52(projectRoot, "pnpm-workspace.yaml")))
|
|
56618
57109
|
return "pnpm-workspaces";
|
|
56619
57110
|
const pkg = readPackageJson(projectRoot);
|
|
56620
57111
|
if (pkg?.workspaces)
|
|
@@ -56756,8 +57247,8 @@ __export(exports_init, {
|
|
|
56756
57247
|
checkInitCollision: () => checkInitCollision,
|
|
56757
57248
|
_initDeps: () => _initDeps
|
|
56758
57249
|
});
|
|
56759
|
-
import { existsSync as
|
|
56760
|
-
import { mkdir as
|
|
57250
|
+
import { existsSync as existsSync23 } from "fs";
|
|
57251
|
+
import { mkdir as mkdir8 } from "fs/promises";
|
|
56761
57252
|
import { join as join53 } from "path";
|
|
56762
57253
|
function validateProjectName(name) {
|
|
56763
57254
|
if (!name)
|
|
@@ -56796,7 +57287,7 @@ async function updateGitignore(projectRoot) {
|
|
|
56796
57287
|
const logger = getLogger();
|
|
56797
57288
|
const gitignorePath = join53(projectRoot, ".gitignore");
|
|
56798
57289
|
let existing = "";
|
|
56799
|
-
if (
|
|
57290
|
+
if (existsSync23(gitignorePath)) {
|
|
56800
57291
|
existing = await Bun.file(gitignorePath).text();
|
|
56801
57292
|
}
|
|
56802
57293
|
const missingEntries = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
|
|
@@ -56876,12 +57367,12 @@ function buildConstitution(stack) {
|
|
|
56876
57367
|
async function initGlobal() {
|
|
56877
57368
|
const logger = getLogger();
|
|
56878
57369
|
const globalDir = globalConfigDir();
|
|
56879
|
-
if (!
|
|
56880
|
-
await
|
|
57370
|
+
if (!existsSync23(globalDir)) {
|
|
57371
|
+
await mkdir8(globalDir, { recursive: true });
|
|
56881
57372
|
logger.info("init", "Created global config directory", { path: globalDir });
|
|
56882
57373
|
}
|
|
56883
57374
|
const configPath = join53(globalDir, "config.json");
|
|
56884
|
-
if (!
|
|
57375
|
+
if (!existsSync23(configPath)) {
|
|
56885
57376
|
await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
|
|
56886
57377
|
`);
|
|
56887
57378
|
logger.info("init", "Created global config", { path: configPath });
|
|
@@ -56889,15 +57380,15 @@ async function initGlobal() {
|
|
|
56889
57380
|
logger.info("init", "Global config already exists", { path: configPath });
|
|
56890
57381
|
}
|
|
56891
57382
|
const constitutionPath = join53(globalDir, "constitution.md");
|
|
56892
|
-
if (!
|
|
57383
|
+
if (!existsSync23(constitutionPath)) {
|
|
56893
57384
|
await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
|
|
56894
57385
|
logger.info("init", "Created global constitution", { path: constitutionPath });
|
|
56895
57386
|
} else {
|
|
56896
57387
|
logger.info("init", "Global constitution already exists", { path: constitutionPath });
|
|
56897
57388
|
}
|
|
56898
57389
|
const hooksDir = join53(globalDir, "hooks");
|
|
56899
|
-
if (!
|
|
56900
|
-
await
|
|
57390
|
+
if (!existsSync23(hooksDir)) {
|
|
57391
|
+
await mkdir8(hooksDir, { recursive: true });
|
|
56901
57392
|
logger.info("init", "Created global hooks directory", { path: hooksDir });
|
|
56902
57393
|
} else {
|
|
56903
57394
|
logger.info("init", "Global hooks directory already exists", { path: hooksDir });
|
|
@@ -56941,8 +57432,8 @@ async function initProject(projectRoot, options) {
|
|
|
56941
57432
|
`), "INIT_NAME_COLLISION", { stage: "init", name: detectedName });
|
|
56942
57433
|
}
|
|
56943
57434
|
}
|
|
56944
|
-
if (!
|
|
56945
|
-
await
|
|
57435
|
+
if (!existsSync23(projectDir)) {
|
|
57436
|
+
await mkdir8(projectDir, { recursive: true });
|
|
56946
57437
|
logger.info("init", "Created project config directory", { path: projectDir });
|
|
56947
57438
|
}
|
|
56948
57439
|
const stack = detectStack(projectRoot);
|
|
@@ -56957,7 +57448,7 @@ async function initProject(projectRoot, options) {
|
|
|
56957
57448
|
monorepo: stack.monorepo
|
|
56958
57449
|
});
|
|
56959
57450
|
const configPath = join53(projectDir, "config.json");
|
|
56960
|
-
if (!
|
|
57451
|
+
if (!existsSync23(configPath)) {
|
|
56961
57452
|
await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
|
|
56962
57453
|
`);
|
|
56963
57454
|
logger.info("init", "Created project config", { path: configPath });
|
|
@@ -56966,15 +57457,15 @@ async function initProject(projectRoot, options) {
|
|
|
56966
57457
|
}
|
|
56967
57458
|
await initContext(projectRoot, { ai: options?.ai, force: options?.force });
|
|
56968
57459
|
const constitutionPath = join53(projectDir, "constitution.md");
|
|
56969
|
-
if (!
|
|
57460
|
+
if (!existsSync23(constitutionPath) || options?.force) {
|
|
56970
57461
|
await Bun.write(constitutionPath, buildConstitution(stack));
|
|
56971
57462
|
logger.info("init", "Created project constitution", { path: constitutionPath });
|
|
56972
57463
|
} else {
|
|
56973
57464
|
logger.info("init", "Project constitution already exists", { path: constitutionPath });
|
|
56974
57465
|
}
|
|
56975
57466
|
const hooksDir = join53(projectDir, "hooks");
|
|
56976
|
-
if (!
|
|
56977
|
-
await
|
|
57467
|
+
if (!existsSync23(hooksDir)) {
|
|
57468
|
+
await mkdir8(hooksDir, { recursive: true });
|
|
56978
57469
|
logger.info("init", "Created project hooks directory", { path: hooksDir });
|
|
56979
57470
|
} else {
|
|
56980
57471
|
logger.info("init", "Project hooks directory already exists", { path: hooksDir });
|
|
@@ -57033,6 +57524,286 @@ var init_init2 = __esm(() => {
|
|
|
57033
57524
|
};
|
|
57034
57525
|
});
|
|
57035
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
|
+
|
|
57036
57807
|
// src/plugins/builtin/curator/collect.ts
|
|
57037
57808
|
import * as path13 from "path";
|
|
57038
57809
|
function now() {
|
|
@@ -57734,12 +58505,12 @@ function renderProposals(proposals, runId, observationCount) {
|
|
|
57734
58505
|
}
|
|
57735
58506
|
|
|
57736
58507
|
// src/plugins/builtin/curator/rollup.ts
|
|
57737
|
-
import { appendFile as appendFile3, mkdir as
|
|
58508
|
+
import { appendFile as appendFile3, mkdir as mkdir9, writeFile } from "fs/promises";
|
|
57738
58509
|
import * as path14 from "path";
|
|
57739
58510
|
async function appendToRollup(observations, rollupPath) {
|
|
57740
58511
|
try {
|
|
57741
58512
|
const dir = path14.dirname(rollupPath);
|
|
57742
|
-
await
|
|
58513
|
+
await mkdir9(dir, { recursive: true });
|
|
57743
58514
|
if (observations.length === 0) {
|
|
57744
58515
|
const f = Bun.file(rollupPath);
|
|
57745
58516
|
if (!await f.exists()) {
|
|
@@ -57756,7 +58527,7 @@ async function appendToRollup(observations, rollupPath) {
|
|
|
57756
58527
|
var init_rollup = () => {};
|
|
57757
58528
|
|
|
57758
58529
|
// src/plugins/builtin/curator/index.ts
|
|
57759
|
-
import { mkdir as
|
|
58530
|
+
import { mkdir as mkdir10 } from "fs/promises";
|
|
57760
58531
|
import * as path15 from "path";
|
|
57761
58532
|
function getCuratorEnabled(context) {
|
|
57762
58533
|
const cfg = context.config;
|
|
@@ -57831,7 +58602,7 @@ var init_curator = __esm(() => {
|
|
|
57831
58602
|
if (context.outputDir) {
|
|
57832
58603
|
const { observationsPath, rollupPath } = resolveCuratorOutputs(curatorContext);
|
|
57833
58604
|
const runDir = path15.dirname(observationsPath);
|
|
57834
|
-
await
|
|
58605
|
+
await mkdir10(runDir, { recursive: true });
|
|
57835
58606
|
await Bun.write(observationsPath, observations.map((o) => JSON.stringify(o)).join(`
|
|
57836
58607
|
`) + (observations.length > 0 ? `
|
|
57837
58608
|
` : ""));
|
|
@@ -58407,12 +59178,12 @@ var init_loader4 = __esm(() => {
|
|
|
58407
59178
|
});
|
|
58408
59179
|
|
|
58409
59180
|
// src/utils/paths.ts
|
|
58410
|
-
import { join as
|
|
59181
|
+
import { join as join67 } from "path";
|
|
58411
59182
|
function getRunsDir() {
|
|
58412
|
-
return process.env.NAX_RUNS_DIR ??
|
|
59183
|
+
return process.env.NAX_RUNS_DIR ?? join67(globalConfigDir(), "runs");
|
|
58413
59184
|
}
|
|
58414
59185
|
function getEventsRootDir() {
|
|
58415
|
-
return
|
|
59186
|
+
return join67(globalConfigDir(), "events");
|
|
58416
59187
|
}
|
|
58417
59188
|
var init_paths3 = __esm(() => {
|
|
58418
59189
|
init_paths();
|
|
@@ -58472,7 +59243,7 @@ var init_command_argv = __esm(() => {
|
|
|
58472
59243
|
});
|
|
58473
59244
|
|
|
58474
59245
|
// src/hooks/runner.ts
|
|
58475
|
-
import { join as
|
|
59246
|
+
import { join as join74 } from "path";
|
|
58476
59247
|
function createDrainDeadline2(deadlineMs) {
|
|
58477
59248
|
let timeoutId;
|
|
58478
59249
|
const promise2 = new Promise((resolve16) => {
|
|
@@ -58491,14 +59262,14 @@ async function loadHooksConfig(projectDir, globalDir) {
|
|
|
58491
59262
|
let globalHooks = { hooks: {} };
|
|
58492
59263
|
let projectHooks = { hooks: {} };
|
|
58493
59264
|
let skipGlobal = false;
|
|
58494
|
-
const projectPath =
|
|
59265
|
+
const projectPath = join74(projectDir, "hooks.json");
|
|
58495
59266
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
58496
59267
|
if (projectData) {
|
|
58497
59268
|
projectHooks = projectData;
|
|
58498
59269
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
58499
59270
|
}
|
|
58500
59271
|
if (!skipGlobal && globalDir) {
|
|
58501
|
-
const globalPath =
|
|
59272
|
+
const globalPath = join74(globalDir, "hooks.json");
|
|
58502
59273
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
58503
59274
|
if (globalData) {
|
|
58504
59275
|
globalHooks = globalData;
|
|
@@ -58668,7 +59439,7 @@ var package_default;
|
|
|
58668
59439
|
var init_package = __esm(() => {
|
|
58669
59440
|
package_default = {
|
|
58670
59441
|
name: "@nathapp/nax",
|
|
58671
|
-
version: "0.
|
|
59442
|
+
version: "0.69.1",
|
|
58672
59443
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
58673
59444
|
type: "module",
|
|
58674
59445
|
bin: {
|
|
@@ -58763,8 +59534,8 @@ var init_version = __esm(() => {
|
|
|
58763
59534
|
NAX_VERSION = package_default.version;
|
|
58764
59535
|
NAX_COMMIT = (() => {
|
|
58765
59536
|
try {
|
|
58766
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
58767
|
-
return "
|
|
59537
|
+
if (/^[0-9a-f]{6,10}$/.test("3b2af55b"))
|
|
59538
|
+
return "3b2af55b";
|
|
58768
59539
|
} catch {}
|
|
58769
59540
|
try {
|
|
58770
59541
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -59640,16 +60411,16 @@ var init_acceptance_loop = __esm(() => {
|
|
|
59640
60411
|
});
|
|
59641
60412
|
|
|
59642
60413
|
// src/session/scratch-purge.ts
|
|
59643
|
-
import { mkdir as
|
|
59644
|
-
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";
|
|
59645
60416
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
59646
|
-
const sessionsDir =
|
|
60417
|
+
const sessionsDir = join75(projectDir, ".nax", "features", featureName, "sessions");
|
|
59647
60418
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
59648
60419
|
const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
|
|
59649
60420
|
let purged = 0;
|
|
59650
60421
|
for (const sessionId of sessionIds) {
|
|
59651
|
-
const sessionDir =
|
|
59652
|
-
const descriptorPath =
|
|
60422
|
+
const sessionDir = join75(sessionsDir, sessionId);
|
|
60423
|
+
const descriptorPath = join75(sessionDir, "descriptor.json");
|
|
59653
60424
|
if (!await _scratchPurgeDeps.fileExists(descriptorPath))
|
|
59654
60425
|
continue;
|
|
59655
60426
|
let lastActivityAt;
|
|
@@ -59665,7 +60436,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
|
|
|
59665
60436
|
if (new Date(lastActivityAt).getTime() >= cutoffMs)
|
|
59666
60437
|
continue;
|
|
59667
60438
|
if (archiveInsteadOfDelete) {
|
|
59668
|
-
const archiveDest =
|
|
60439
|
+
const archiveDest = join75(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
|
|
59669
60440
|
await _scratchPurgeDeps.move(sessionDir, archiveDest);
|
|
59670
60441
|
} else {
|
|
59671
60442
|
await _scratchPurgeDeps.remove(sessionDir);
|
|
@@ -59692,7 +60463,7 @@ var init_scratch_purge = __esm(() => {
|
|
|
59692
60463
|
readFile: (path19) => Bun.file(path19).text(),
|
|
59693
60464
|
remove: (path19) => rm(path19, { recursive: true, force: true }),
|
|
59694
60465
|
move: async (src, dest) => {
|
|
59695
|
-
await
|
|
60466
|
+
await mkdir12(dirname12(dest), { recursive: true });
|
|
59696
60467
|
await rename(src, dest);
|
|
59697
60468
|
},
|
|
59698
60469
|
now: () => Date.now()
|
|
@@ -60402,19 +61173,19 @@ function precomputeBatchPlan(stories, maxBatchSize = DEFAULT_MAX_BATCH_SIZE) {
|
|
|
60402
61173
|
var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
60403
61174
|
|
|
60404
61175
|
// src/pipeline/subscribers/events-writer.ts
|
|
60405
|
-
import { appendFile as appendFile4, mkdir as
|
|
60406
|
-
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";
|
|
60407
61178
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
60408
61179
|
const logger = getSafeLogger();
|
|
60409
61180
|
const project = basename13(workdir);
|
|
60410
|
-
const eventsDir =
|
|
60411
|
-
const eventsFile =
|
|
61181
|
+
const eventsDir = join76(getEventsRootDir(), project);
|
|
61182
|
+
const eventsFile = join76(eventsDir, "events.jsonl");
|
|
60412
61183
|
let dirReady = false;
|
|
60413
61184
|
const write = (line) => {
|
|
60414
61185
|
return (async () => {
|
|
60415
61186
|
try {
|
|
60416
61187
|
if (!dirReady) {
|
|
60417
|
-
await
|
|
61188
|
+
await mkdir13(eventsDir, { recursive: true });
|
|
60418
61189
|
dirReady = true;
|
|
60419
61190
|
}
|
|
60420
61191
|
await appendFile4(eventsFile, `${JSON.stringify(line)}
|
|
@@ -60588,24 +61359,24 @@ var init_interaction2 = __esm(() => {
|
|
|
60588
61359
|
});
|
|
60589
61360
|
|
|
60590
61361
|
// src/pipeline/subscribers/registry.ts
|
|
60591
|
-
import { mkdir as
|
|
60592
|
-
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";
|
|
60593
61364
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
60594
61365
|
const logger = getSafeLogger();
|
|
60595
61366
|
const project = basename14(workdir);
|
|
60596
|
-
const runDir =
|
|
60597
|
-
const metaFile =
|
|
61367
|
+
const runDir = join77(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
61368
|
+
const metaFile = join77(runDir, "meta.json");
|
|
60598
61369
|
const unsub = bus.on("run:started", (_ev) => {
|
|
60599
61370
|
return (async () => {
|
|
60600
61371
|
try {
|
|
60601
|
-
await
|
|
61372
|
+
await mkdir14(runDir, { recursive: true });
|
|
60602
61373
|
const meta3 = {
|
|
60603
61374
|
runId,
|
|
60604
61375
|
project,
|
|
60605
61376
|
feature,
|
|
60606
61377
|
workdir,
|
|
60607
|
-
statusPath:
|
|
60608
|
-
eventsDir:
|
|
61378
|
+
statusPath: join77(outputDir, "features", feature, "status.json"),
|
|
61379
|
+
eventsDir: join77(outputDir, "features", feature, "runs"),
|
|
60609
61380
|
registeredAt: new Date().toISOString()
|
|
60610
61381
|
};
|
|
60611
61382
|
await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -60850,8 +61621,8 @@ var init_types9 = __esm(() => {
|
|
|
60850
61621
|
});
|
|
60851
61622
|
|
|
60852
61623
|
// src/worktree/dependencies.ts
|
|
60853
|
-
import { existsSync as
|
|
60854
|
-
import { join as
|
|
61624
|
+
import { existsSync as existsSync30 } from "fs";
|
|
61625
|
+
import { join as join78 } from "path";
|
|
60855
61626
|
async function prepareWorktreeDependencies(options) {
|
|
60856
61627
|
const mode = options.config.execution.worktreeDependencies.mode;
|
|
60857
61628
|
const resolvedCwd = resolveDependencyCwd(options);
|
|
@@ -60865,7 +61636,7 @@ async function prepareWorktreeDependencies(options) {
|
|
|
60865
61636
|
}
|
|
60866
61637
|
}
|
|
60867
61638
|
function resolveDependencyCwd(options) {
|
|
60868
|
-
return options.storyWorkdir ?
|
|
61639
|
+
return options.storyWorkdir ? join78(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
|
|
60869
61640
|
}
|
|
60870
61641
|
function resolveInheritedDependencies(options, resolvedCwd) {
|
|
60871
61642
|
if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
|
|
@@ -60875,14 +61646,14 @@ function resolveInheritedDependencies(options, resolvedCwd) {
|
|
|
60875
61646
|
}
|
|
60876
61647
|
function hasDependencyManifests(worktreeRoot, resolvedCwd) {
|
|
60877
61648
|
const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
|
|
60878
|
-
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))));
|
|
60879
61650
|
}
|
|
60880
61651
|
async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
|
|
60881
|
-
const
|
|
60882
|
-
if (!
|
|
61652
|
+
const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
|
|
61653
|
+
if (!setupCommand2) {
|
|
60883
61654
|
throw new WorktreeDependencyPreparationError("[worktree-deps] provision mode requires execution.worktreeDependencies.setupCommand in phase 1.", "provision");
|
|
60884
61655
|
}
|
|
60885
|
-
const argv = parseCommandToArgv(
|
|
61656
|
+
const argv = parseCommandToArgv(setupCommand2);
|
|
60886
61657
|
if (argv.length === 0) {
|
|
60887
61658
|
throw new WorktreeDependencyPreparationError("[worktree-deps] setupCommand cannot be empty.", "provision");
|
|
60888
61659
|
}
|
|
@@ -60926,7 +61697,7 @@ var init_dependencies = __esm(() => {
|
|
|
60926
61697
|
"build.gradle.kts"
|
|
60927
61698
|
];
|
|
60928
61699
|
_worktreeDependencyDeps = {
|
|
60929
|
-
existsSync:
|
|
61700
|
+
existsSync: existsSync30,
|
|
60930
61701
|
spawn
|
|
60931
61702
|
};
|
|
60932
61703
|
});
|
|
@@ -60937,19 +61708,19 @@ __export(exports_manager, {
|
|
|
60937
61708
|
_managerDeps: () => _managerDeps,
|
|
60938
61709
|
WorktreeManager: () => WorktreeManager
|
|
60939
61710
|
});
|
|
60940
|
-
import { existsSync as
|
|
60941
|
-
import { mkdir as
|
|
60942
|
-
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";
|
|
60943
61714
|
|
|
60944
61715
|
class WorktreeManager {
|
|
60945
61716
|
async ensureGitExcludes(projectRoot) {
|
|
60946
61717
|
const logger = getSafeLogger();
|
|
60947
|
-
const infoDir =
|
|
60948
|
-
const excludePath =
|
|
61718
|
+
const infoDir = join79(projectRoot, ".git", "info");
|
|
61719
|
+
const excludePath = join79(infoDir, "exclude");
|
|
60949
61720
|
try {
|
|
60950
|
-
await
|
|
61721
|
+
await mkdir15(infoDir, { recursive: true });
|
|
60951
61722
|
let existing = "";
|
|
60952
|
-
if (
|
|
61723
|
+
if (existsSync31(excludePath)) {
|
|
60953
61724
|
existing = await Bun.file(excludePath).text();
|
|
60954
61725
|
}
|
|
60955
61726
|
const missing = NAX_GITIGNORE_ENTRIES.filter((entry) => !existing.includes(entry));
|
|
@@ -60972,7 +61743,7 @@ ${missing.join(`
|
|
|
60972
61743
|
}
|
|
60973
61744
|
async create(projectRoot, storyId) {
|
|
60974
61745
|
validateStoryId(storyId);
|
|
60975
|
-
const worktreePath =
|
|
61746
|
+
const worktreePath = join79(projectRoot, ".nax-wt", storyId);
|
|
60976
61747
|
const branchName = `nax/${storyId}`;
|
|
60977
61748
|
try {
|
|
60978
61749
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -61013,9 +61784,9 @@ ${missing.join(`
|
|
|
61013
61784
|
}
|
|
61014
61785
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
61015
61786
|
}
|
|
61016
|
-
const envSource =
|
|
61017
|
-
if (
|
|
61018
|
-
const envTarget =
|
|
61787
|
+
const envSource = join79(projectRoot, ".env");
|
|
61788
|
+
if (existsSync31(envSource)) {
|
|
61789
|
+
const envTarget = join79(worktreePath, ".env");
|
|
61019
61790
|
try {
|
|
61020
61791
|
symlinkSync(envSource, envTarget, "file");
|
|
61021
61792
|
} catch (error48) {
|
|
@@ -61026,7 +61797,7 @@ ${missing.join(`
|
|
|
61026
61797
|
}
|
|
61027
61798
|
async remove(projectRoot, storyId) {
|
|
61028
61799
|
validateStoryId(storyId);
|
|
61029
|
-
const worktreePath =
|
|
61800
|
+
const worktreePath = join79(projectRoot, ".nax-wt", storyId);
|
|
61030
61801
|
const branchName = `nax/${storyId}`;
|
|
61031
61802
|
try {
|
|
61032
61803
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -61838,10 +62609,10 @@ var init_merge_conflict_rectify = __esm(() => {
|
|
|
61838
62609
|
});
|
|
61839
62610
|
|
|
61840
62611
|
// src/execution/pipeline-result-handler.ts
|
|
61841
|
-
import { join as
|
|
62612
|
+
import { join as join80 } from "path";
|
|
61842
62613
|
async function removeWorktreeDirectory(projectRoot, storyId) {
|
|
61843
62614
|
const logger = getSafeLogger();
|
|
61844
|
-
const worktreePath =
|
|
62615
|
+
const worktreePath = join80(projectRoot, ".nax-wt", storyId);
|
|
61845
62616
|
try {
|
|
61846
62617
|
const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
61847
62618
|
cwd: projectRoot,
|
|
@@ -62057,8 +62828,8 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
62057
62828
|
});
|
|
62058
62829
|
|
|
62059
62830
|
// src/execution/iteration-runner.ts
|
|
62060
|
-
import { existsSync as
|
|
62061
|
-
import { join as
|
|
62831
|
+
import { existsSync as existsSync32 } from "fs";
|
|
62832
|
+
import { join as join81 } from "path";
|
|
62062
62833
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
62063
62834
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
62064
62835
|
if (ctx.dryRun) {
|
|
@@ -62083,7 +62854,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62083
62854
|
const storyStartTime = Date.now();
|
|
62084
62855
|
let effectiveWorkdir = ctx.workdir;
|
|
62085
62856
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
62086
|
-
const worktreePath =
|
|
62857
|
+
const worktreePath = join81(ctx.workdir, ".nax-wt", story.id);
|
|
62087
62858
|
const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
|
|
62088
62859
|
if (!worktreeExists) {
|
|
62089
62860
|
await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
|
|
@@ -62103,7 +62874,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62103
62874
|
}
|
|
62104
62875
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
62105
62876
|
const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
|
|
62106
|
-
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;
|
|
62107
62878
|
let dependencyContext;
|
|
62108
62879
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
62109
62880
|
try {
|
|
@@ -62130,7 +62901,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62130
62901
|
};
|
|
62131
62902
|
}
|
|
62132
62903
|
}
|
|
62133
|
-
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;
|
|
62134
62905
|
const pipelineContext = {
|
|
62135
62906
|
config: effectiveConfig,
|
|
62136
62907
|
rootConfig: ctx.config,
|
|
@@ -62262,7 +63033,7 @@ var init_iteration_runner = __esm(() => {
|
|
|
62262
63033
|
loadConfigForWorkdir,
|
|
62263
63034
|
prepareWorktreeDependencies,
|
|
62264
63035
|
runPipeline,
|
|
62265
|
-
existsSync:
|
|
63036
|
+
existsSync: existsSync32,
|
|
62266
63037
|
worktreeManager: new WorktreeManager
|
|
62267
63038
|
};
|
|
62268
63039
|
});
|
|
@@ -62332,7 +63103,7 @@ __export(exports_parallel_worker, {
|
|
|
62332
63103
|
buildWorktreePipelineContext: () => buildWorktreePipelineContext,
|
|
62333
63104
|
_parallelWorkerDeps: () => _parallelWorkerDeps
|
|
62334
63105
|
});
|
|
62335
|
-
import { join as
|
|
63106
|
+
import { join as join82 } from "path";
|
|
62336
63107
|
function buildWorktreePipelineContext(base, _story) {
|
|
62337
63108
|
return { ...base, prd: structuredClone(base.prd) };
|
|
62338
63109
|
}
|
|
@@ -62355,7 +63126,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
|
|
|
62355
63126
|
story,
|
|
62356
63127
|
stories: [story],
|
|
62357
63128
|
projectDir: context.projectDir,
|
|
62358
|
-
workdir: dependencyContext.cwd ?? (story.workdir ?
|
|
63129
|
+
workdir: dependencyContext.cwd ?? (story.workdir ? join82(worktreePath, story.workdir) : worktreePath),
|
|
62359
63130
|
worktreeDependencyContext: dependencyContext,
|
|
62360
63131
|
routing,
|
|
62361
63132
|
storyGitRef: storyGitRef ?? undefined
|
|
@@ -63242,7 +64013,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
63242
64013
|
var init_status_file = () => {};
|
|
63243
64014
|
|
|
63244
64015
|
// src/execution/status-writer.ts
|
|
63245
|
-
import { join as
|
|
64016
|
+
import { join as join83 } from "path";
|
|
63246
64017
|
|
|
63247
64018
|
class StatusWriter {
|
|
63248
64019
|
statusFile;
|
|
@@ -63361,7 +64132,7 @@ class StatusWriter {
|
|
|
63361
64132
|
if (!this._prd)
|
|
63362
64133
|
return;
|
|
63363
64134
|
const safeLogger = getSafeLogger();
|
|
63364
|
-
const featureStatusPath =
|
|
64135
|
+
const featureStatusPath = join83(featureDir, "status.json");
|
|
63365
64136
|
const write = async () => {
|
|
63366
64137
|
try {
|
|
63367
64138
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -63392,11 +64163,11 @@ __export(exports_migrate, {
|
|
|
63392
64163
|
migrateCommand: () => migrateCommand,
|
|
63393
64164
|
detectGeneratedContent: () => detectGeneratedContent
|
|
63394
64165
|
});
|
|
63395
|
-
import { existsSync as
|
|
63396
|
-
import { mkdir as
|
|
64166
|
+
import { existsSync as existsSync33 } from "fs";
|
|
64167
|
+
import { mkdir as mkdir16, readdir as readdir6, rename as rename3 } from "fs/promises";
|
|
63397
64168
|
import path22 from "path";
|
|
63398
64169
|
async function detectGeneratedContent(naxDir) {
|
|
63399
|
-
if (!
|
|
64170
|
+
if (!existsSync33(naxDir))
|
|
63400
64171
|
return [];
|
|
63401
64172
|
const candidates = [];
|
|
63402
64173
|
let entries = [];
|
|
@@ -63411,7 +64182,7 @@ async function detectGeneratedContent(naxDir) {
|
|
|
63411
64182
|
}
|
|
63412
64183
|
}
|
|
63413
64184
|
const featuresDir = path22.join(naxDir, "features");
|
|
63414
|
-
if (
|
|
64185
|
+
if (existsSync33(featuresDir)) {
|
|
63415
64186
|
let featureDirs = [];
|
|
63416
64187
|
try {
|
|
63417
64188
|
featureDirs = await readdir6(featuresDir);
|
|
@@ -63473,7 +64244,7 @@ async function migrateCommand(options) {
|
|
|
63473
64244
|
});
|
|
63474
64245
|
}
|
|
63475
64246
|
const src = path22.join(globalConfigDir(), options.reclaim);
|
|
63476
|
-
if (!
|
|
64247
|
+
if (!existsSync33(src)) {
|
|
63477
64248
|
throw new NaxError(`Nothing to reclaim: ~/.nax/${options.reclaim} does not exist`, "MIGRATE_RECLAIM_NOT_FOUND", {
|
|
63478
64249
|
stage: "migrate",
|
|
63479
64250
|
name: options.reclaim
|
|
@@ -63481,7 +64252,7 @@ async function migrateCommand(options) {
|
|
|
63481
64252
|
}
|
|
63482
64253
|
const archiveBase = path22.join(globalConfigDir(), "_archive");
|
|
63483
64254
|
const archiveDest = path22.join(archiveBase, `${options.reclaim}-${Date.now()}`);
|
|
63484
|
-
await
|
|
64255
|
+
await mkdir16(archiveBase, { recursive: true });
|
|
63485
64256
|
await rename3(src, archiveDest);
|
|
63486
64257
|
logger.info("migrate", `Reclaimed: archived to ${archiveDest}`, { storyId: "_migrate" });
|
|
63487
64258
|
return;
|
|
@@ -63519,7 +64290,7 @@ async function migrateCommand(options) {
|
|
|
63519
64290
|
}
|
|
63520
64291
|
const naxDir = path22.join(options.workdir, ".nax");
|
|
63521
64292
|
const configPath = path22.join(naxDir, "config.json");
|
|
63522
|
-
if (!
|
|
64293
|
+
if (!existsSync33(configPath)) {
|
|
63523
64294
|
throw new NaxError("No .nax/config.json found \u2014 run nax init first", "MIGRATE_NO_CONFIG", {
|
|
63524
64295
|
stage: "migrate",
|
|
63525
64296
|
workdir: options.workdir
|
|
@@ -63549,12 +64320,12 @@ async function migrateCommand(options) {
|
|
|
63549
64320
|
}
|
|
63550
64321
|
return;
|
|
63551
64322
|
}
|
|
63552
|
-
await
|
|
64323
|
+
await mkdir16(destBase, { recursive: true });
|
|
63553
64324
|
let moved = 0;
|
|
63554
64325
|
for (const candidate of candidates) {
|
|
63555
64326
|
const dest = path22.join(destBase, candidate.name);
|
|
63556
|
-
await
|
|
63557
|
-
if (
|
|
64327
|
+
await mkdir16(path22.dirname(dest), { recursive: true });
|
|
64328
|
+
if (existsSync33(dest)) {
|
|
63558
64329
|
throw new NaxError(`Migration conflict: destination already exists.
|
|
63559
64330
|
Source: ${candidate.srcPath}
|
|
63560
64331
|
Destination: ${dest}
|
|
@@ -63795,7 +64566,7 @@ __export(exports_run_initialization, {
|
|
|
63795
64566
|
initializeRun: () => initializeRun,
|
|
63796
64567
|
_reconcileDeps: () => _reconcileDeps
|
|
63797
64568
|
});
|
|
63798
|
-
import { join as
|
|
64569
|
+
import { join as join84 } from "path";
|
|
63799
64570
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
63800
64571
|
const logger = getSafeLogger();
|
|
63801
64572
|
let reconciledCount = 0;
|
|
@@ -63812,7 +64583,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
63812
64583
|
});
|
|
63813
64584
|
continue;
|
|
63814
64585
|
}
|
|
63815
|
-
const effectiveWorkdir = story.workdir ?
|
|
64586
|
+
const effectiveWorkdir = story.workdir ? join84(workdir, story.workdir) : workdir;
|
|
63816
64587
|
try {
|
|
63817
64588
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
63818
64589
|
if (!reviewResult.success) {
|
|
@@ -93605,7 +94376,7 @@ __export(exports_curator, {
|
|
|
93605
94376
|
});
|
|
93606
94377
|
import { readdirSync as readdirSync8 } from "fs";
|
|
93607
94378
|
import { unlink as unlink4 } from "fs/promises";
|
|
93608
|
-
import { basename as basename15, join as
|
|
94379
|
+
import { basename as basename15, join as join86 } from "path";
|
|
93609
94380
|
function getProjectKey(config2, projectDir) {
|
|
93610
94381
|
return config2.name?.trim() || basename15(projectDir);
|
|
93611
94382
|
}
|
|
@@ -93688,7 +94459,7 @@ async function curatorStatus(options) {
|
|
|
93688
94459
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93689
94460
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93690
94461
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93691
|
-
const runsDir =
|
|
94462
|
+
const runsDir = join86(outputDir, "runs");
|
|
93692
94463
|
const runIds = listRunIds(runsDir);
|
|
93693
94464
|
let runId;
|
|
93694
94465
|
if (options.run) {
|
|
@@ -93705,8 +94476,8 @@ async function curatorStatus(options) {
|
|
|
93705
94476
|
runId = runIds[runIds.length - 1];
|
|
93706
94477
|
}
|
|
93707
94478
|
console.log(`Run: ${runId}`);
|
|
93708
|
-
const runDir =
|
|
93709
|
-
const observationsPath =
|
|
94479
|
+
const runDir = join86(runsDir, runId);
|
|
94480
|
+
const observationsPath = join86(runDir, "observations.jsonl");
|
|
93710
94481
|
const observations = await parseObservations(observationsPath);
|
|
93711
94482
|
const counts = new Map;
|
|
93712
94483
|
for (const obs of observations) {
|
|
@@ -93716,7 +94487,7 @@ async function curatorStatus(options) {
|
|
|
93716
94487
|
for (const [kind, count] of counts.entries()) {
|
|
93717
94488
|
console.log(` ${kind}: ${count}`);
|
|
93718
94489
|
}
|
|
93719
|
-
const proposalsPath =
|
|
94490
|
+
const proposalsPath = join86(runDir, "curator-proposals.md");
|
|
93720
94491
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
93721
94492
|
if (proposalText !== null) {
|
|
93722
94493
|
console.log("");
|
|
@@ -93730,8 +94501,8 @@ async function curatorCommit(options) {
|
|
|
93730
94501
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93731
94502
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93732
94503
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93733
|
-
const runDir =
|
|
93734
|
-
const proposalsPath =
|
|
94504
|
+
const runDir = join86(outputDir, "runs", options.runId);
|
|
94505
|
+
const proposalsPath = join86(runDir, "curator-proposals.md");
|
|
93735
94506
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
93736
94507
|
if (proposalText === null) {
|
|
93737
94508
|
console.log(`curator-proposals.md not found for run ${options.runId}.`);
|
|
@@ -93747,7 +94518,7 @@ async function curatorCommit(options) {
|
|
|
93747
94518
|
const dropFileState = new Map;
|
|
93748
94519
|
const skippedDrops = new Set;
|
|
93749
94520
|
for (const drop2 of drops) {
|
|
93750
|
-
const targetPath =
|
|
94521
|
+
const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
|
|
93751
94522
|
if (!dropFileState.has(targetPath)) {
|
|
93752
94523
|
const fileExists2 = await Bun.file(targetPath).exists();
|
|
93753
94524
|
const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
|
|
@@ -93781,7 +94552,7 @@ async function curatorCommit(options) {
|
|
|
93781
94552
|
if (skippedDrops.has(drop2)) {
|
|
93782
94553
|
continue;
|
|
93783
94554
|
}
|
|
93784
|
-
const targetPath =
|
|
94555
|
+
const targetPath = join86(resolved.projectDir, drop2.canonicalFile);
|
|
93785
94556
|
const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
|
|
93786
94557
|
const filtered = filterDropContent(existing, drop2.description);
|
|
93787
94558
|
await _curatorCmdDeps.writeFile(targetPath, filtered);
|
|
@@ -93790,7 +94561,7 @@ async function curatorCommit(options) {
|
|
|
93790
94561
|
}
|
|
93791
94562
|
const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
|
|
93792
94563
|
for (const add2 of adds) {
|
|
93793
|
-
const targetPath =
|
|
94564
|
+
const targetPath = join86(resolved.projectDir, add2.canonicalFile);
|
|
93794
94565
|
const content = buildAddContent(add2);
|
|
93795
94566
|
await _curatorCmdDeps.appendFile(targetPath, content);
|
|
93796
94567
|
modifiedFiles.add(targetPath);
|
|
@@ -93827,7 +94598,7 @@ async function curatorDryrun(options) {
|
|
|
93827
94598
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
93828
94599
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93829
94600
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93830
|
-
const runsDir =
|
|
94601
|
+
const runsDir = join86(outputDir, "runs");
|
|
93831
94602
|
const runIds = listRunIds(runsDir);
|
|
93832
94603
|
if (runIds.length === 0) {
|
|
93833
94604
|
console.log("No runs found.");
|
|
@@ -93838,7 +94609,7 @@ async function curatorDryrun(options) {
|
|
|
93838
94609
|
console.log(`Run ${options.run} not found in ${runsDir}.`);
|
|
93839
94610
|
return;
|
|
93840
94611
|
}
|
|
93841
|
-
const observationsPath =
|
|
94612
|
+
const observationsPath = join86(runsDir, runId, "observations.jsonl");
|
|
93842
94613
|
const observations = await parseObservations(observationsPath);
|
|
93843
94614
|
const thresholds = getThresholds(config2);
|
|
93844
94615
|
const proposals = runHeuristics(observations, thresholds);
|
|
@@ -93879,12 +94650,12 @@ async function curatorGc(options) {
|
|
|
93879
94650
|
await _curatorCmdDeps.writeFile(rollupPath, newContent);
|
|
93880
94651
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
93881
94652
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
93882
|
-
const perRunsDir =
|
|
94653
|
+
const perRunsDir = join86(outputDir, "runs");
|
|
93883
94654
|
for (const runId of uniqueRunIds) {
|
|
93884
94655
|
if (!keepSet.has(runId)) {
|
|
93885
|
-
const runDir =
|
|
93886
|
-
await _curatorCmdDeps.removeFile(
|
|
93887
|
-
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"));
|
|
93888
94659
|
}
|
|
93889
94660
|
}
|
|
93890
94661
|
console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
|
|
@@ -93927,9 +94698,9 @@ var init_curator2 = __esm(() => {
|
|
|
93927
94698
|
|
|
93928
94699
|
// bin/nax.ts
|
|
93929
94700
|
init_source();
|
|
93930
|
-
import { existsSync as
|
|
94701
|
+
import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
|
|
93931
94702
|
import { homedir as homedir3 } from "os";
|
|
93932
|
-
import { basename as basename16, join as
|
|
94703
|
+
import { basename as basename16, join as join87 } from "path";
|
|
93933
94704
|
|
|
93934
94705
|
// node_modules/commander/esm.mjs
|
|
93935
94706
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -95019,6 +95790,7 @@ async function runsShowCommand(options) {
|
|
|
95019
95790
|
// src/cli/index.ts
|
|
95020
95791
|
init_prompts2();
|
|
95021
95792
|
init_init2();
|
|
95793
|
+
init_setup();
|
|
95022
95794
|
|
|
95023
95795
|
// src/cli/plugins.ts
|
|
95024
95796
|
init_paths();
|
|
@@ -95093,8 +95865,8 @@ init_interaction();
|
|
|
95093
95865
|
init_source();
|
|
95094
95866
|
init_loader();
|
|
95095
95867
|
init_generator2();
|
|
95096
|
-
import { existsSync as
|
|
95097
|
-
import { join as
|
|
95868
|
+
import { existsSync as existsSync24 } from "fs";
|
|
95869
|
+
import { join as join61 } from "path";
|
|
95098
95870
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
95099
95871
|
async function generateCommand(options) {
|
|
95100
95872
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -95137,7 +95909,7 @@ async function generateCommand(options) {
|
|
|
95137
95909
|
return;
|
|
95138
95910
|
}
|
|
95139
95911
|
if (options.package) {
|
|
95140
|
-
const packageDir =
|
|
95912
|
+
const packageDir = join61(workdir, options.package);
|
|
95141
95913
|
if (dryRun) {
|
|
95142
95914
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
95143
95915
|
}
|
|
@@ -95157,10 +95929,10 @@ async function generateCommand(options) {
|
|
|
95157
95929
|
process.exit(1);
|
|
95158
95930
|
return;
|
|
95159
95931
|
}
|
|
95160
|
-
const contextPath = options.context ?
|
|
95161
|
-
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;
|
|
95162
95934
|
const autoInject = !options.noAutoInject;
|
|
95163
|
-
if (!
|
|
95935
|
+
if (!existsSync24(contextPath)) {
|
|
95164
95936
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
95165
95937
|
console.error(source_default.yellow(" Create .nax/context.md first, or run `nax init` to scaffold it."));
|
|
95166
95938
|
process.exit(1);
|
|
@@ -95263,8 +96035,8 @@ async function generateCommand(options) {
|
|
|
95263
96035
|
}
|
|
95264
96036
|
// src/cli/config-display.ts
|
|
95265
96037
|
init_loader();
|
|
95266
|
-
import { existsSync as
|
|
95267
|
-
import { join as
|
|
96038
|
+
import { existsSync as existsSync26 } from "fs";
|
|
96039
|
+
import { join as join63 } from "path";
|
|
95268
96040
|
|
|
95269
96041
|
// src/cli/config-descriptions.ts
|
|
95270
96042
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -95515,10 +96287,10 @@ function deepEqual(a, b) {
|
|
|
95515
96287
|
// src/cli/config-get.ts
|
|
95516
96288
|
init_defaults();
|
|
95517
96289
|
init_loader();
|
|
95518
|
-
import { existsSync as
|
|
95519
|
-
import { join as
|
|
96290
|
+
import { existsSync as existsSync25 } from "fs";
|
|
96291
|
+
import { join as join62 } from "path";
|
|
95520
96292
|
async function loadConfigFile(path18) {
|
|
95521
|
-
if (!
|
|
96293
|
+
if (!existsSync25(path18))
|
|
95522
96294
|
return null;
|
|
95523
96295
|
try {
|
|
95524
96296
|
return await Bun.file(path18).json();
|
|
@@ -95538,7 +96310,7 @@ async function loadProjectConfig() {
|
|
|
95538
96310
|
const projectDir = findProjectDir();
|
|
95539
96311
|
if (!projectDir)
|
|
95540
96312
|
return null;
|
|
95541
|
-
const projectPath =
|
|
96313
|
+
const projectPath = join62(projectDir, "config.json");
|
|
95542
96314
|
return await loadConfigFile(projectPath);
|
|
95543
96315
|
}
|
|
95544
96316
|
|
|
@@ -95598,14 +96370,14 @@ async function configCommand(config2, options = {}) {
|
|
|
95598
96370
|
function determineConfigSources() {
|
|
95599
96371
|
const globalPath = globalConfigPath();
|
|
95600
96372
|
const projectDir = findProjectDir();
|
|
95601
|
-
const projectPath = projectDir ?
|
|
96373
|
+
const projectPath = projectDir ? join63(projectDir, "config.json") : null;
|
|
95602
96374
|
return {
|
|
95603
96375
|
global: fileExists(globalPath) ? globalPath : null,
|
|
95604
96376
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
95605
96377
|
};
|
|
95606
96378
|
}
|
|
95607
96379
|
function fileExists(path18) {
|
|
95608
|
-
return
|
|
96380
|
+
return existsSync26(path18);
|
|
95609
96381
|
}
|
|
95610
96382
|
function displayConfigWithDescriptions(obj, path18, sources, indent = 0) {
|
|
95611
96383
|
const indentStr = " ".repeat(indent);
|
|
@@ -95747,15 +96519,15 @@ init_paths();
|
|
|
95747
96519
|
init_profile();
|
|
95748
96520
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
95749
96521
|
import { readdirSync as readdirSync5 } from "fs";
|
|
95750
|
-
import { join as
|
|
96522
|
+
import { join as join64 } from "path";
|
|
95751
96523
|
var _profileCLIDeps = {
|
|
95752
96524
|
env: process.env
|
|
95753
96525
|
};
|
|
95754
96526
|
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
95755
96527
|
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
95756
96528
|
async function profileListCommand(startDir) {
|
|
95757
|
-
const globalProfilesDir =
|
|
95758
|
-
const projectProfilesDir =
|
|
96529
|
+
const globalProfilesDir = join64(globalConfigDir(), "profiles");
|
|
96530
|
+
const projectProfilesDir = join64(projectConfigDir(startDir), "profiles");
|
|
95759
96531
|
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
95760
96532
|
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
95761
96533
|
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
@@ -95814,7 +96586,7 @@ function maskProfileValues(obj) {
|
|
|
95814
96586
|
return result;
|
|
95815
96587
|
}
|
|
95816
96588
|
async function profileUseCommand(profileName, startDir) {
|
|
95817
|
-
const configPath =
|
|
96589
|
+
const configPath = join64(projectConfigDir(startDir), "config.json");
|
|
95818
96590
|
const configFile = Bun.file(configPath);
|
|
95819
96591
|
let existing = {};
|
|
95820
96592
|
if (await configFile.exists()) {
|
|
@@ -95833,8 +96605,8 @@ async function profileCurrentCommand(startDir) {
|
|
|
95833
96605
|
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
95834
96606
|
}
|
|
95835
96607
|
async function profileCreateCommand(profileName, startDir) {
|
|
95836
|
-
const profilesDir =
|
|
95837
|
-
const profilePath =
|
|
96608
|
+
const profilesDir = join64(projectConfigDir(startDir), "profiles");
|
|
96609
|
+
const profilePath = join64(profilesDir, `${profileName}.json`);
|
|
95838
96610
|
const profileFile = Bun.file(profilePath);
|
|
95839
96611
|
if (await profileFile.exists()) {
|
|
95840
96612
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
@@ -95955,8 +96727,8 @@ async function contextInspectCommand(options) {
|
|
|
95955
96727
|
// src/cli/rules.ts
|
|
95956
96728
|
init_canonical_loader();
|
|
95957
96729
|
init_errors();
|
|
95958
|
-
import { mkdir as
|
|
95959
|
-
import { basename as basename12, join as
|
|
96730
|
+
import { mkdir as mkdir11 } from "fs/promises";
|
|
96731
|
+
import { basename as basename12, join as join65 } from "path";
|
|
95960
96732
|
var _rulesCLIDeps = {
|
|
95961
96733
|
readFile: async (path18) => Bun.file(path18).text(),
|
|
95962
96734
|
writeFile: async (path18, content) => {
|
|
@@ -95965,13 +96737,13 @@ var _rulesCLIDeps = {
|
|
|
95965
96737
|
fileExists: async (path18) => Bun.file(path18).exists(),
|
|
95966
96738
|
globInDir: (dir) => {
|
|
95967
96739
|
try {
|
|
95968
|
-
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));
|
|
95969
96741
|
} catch {
|
|
95970
96742
|
return [];
|
|
95971
96743
|
}
|
|
95972
96744
|
},
|
|
95973
96745
|
mkdir: async (path18) => {
|
|
95974
|
-
await
|
|
96746
|
+
await mkdir11(path18, { recursive: true });
|
|
95975
96747
|
},
|
|
95976
96748
|
globCanonicalRuleFiles: (workdir) => {
|
|
95977
96749
|
try {
|
|
@@ -96014,7 +96786,7 @@ ${r.content}`).join(`
|
|
|
96014
96786
|
`);
|
|
96015
96787
|
const shimContent = `${header + body}
|
|
96016
96788
|
`;
|
|
96017
|
-
const shimPath =
|
|
96789
|
+
const shimPath = join65(workdir, shimFileName);
|
|
96018
96790
|
if (options.dryRun) {
|
|
96019
96791
|
console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
|
|
96020
96792
|
return;
|
|
@@ -96043,14 +96815,14 @@ function neutralizeContent(content) {
|
|
|
96043
96815
|
}
|
|
96044
96816
|
async function collectMigrationSources(workdir) {
|
|
96045
96817
|
const sources = [];
|
|
96046
|
-
const claudeMdPath =
|
|
96818
|
+
const claudeMdPath = join65(workdir, "CLAUDE.md");
|
|
96047
96819
|
if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
|
|
96048
96820
|
const content = await _rulesCLIDeps.readFile(claudeMdPath);
|
|
96049
96821
|
if (content.trim()) {
|
|
96050
96822
|
sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
|
|
96051
96823
|
}
|
|
96052
96824
|
}
|
|
96053
|
-
const rulesDir =
|
|
96825
|
+
const rulesDir = join65(workdir, ".claude", "rules");
|
|
96054
96826
|
const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
|
|
96055
96827
|
for (const filePath of ruleFiles) {
|
|
96056
96828
|
try {
|
|
@@ -96070,7 +96842,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96070
96842
|
console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
|
|
96071
96843
|
return;
|
|
96072
96844
|
}
|
|
96073
|
-
const targetDir =
|
|
96845
|
+
const targetDir = join65(workdir, CANONICAL_RULES_DIR);
|
|
96074
96846
|
if (!options.dryRun) {
|
|
96075
96847
|
try {
|
|
96076
96848
|
await _rulesCLIDeps.mkdir(targetDir);
|
|
@@ -96081,7 +96853,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96081
96853
|
let written = 0;
|
|
96082
96854
|
let skipped = 0;
|
|
96083
96855
|
for (const { sourcePath, targetFileName, content } of sources) {
|
|
96084
|
-
const targetPath =
|
|
96856
|
+
const targetPath = join65(targetDir, targetFileName);
|
|
96085
96857
|
if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
|
|
96086
96858
|
console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
|
|
96087
96859
|
skipped++;
|
|
@@ -96120,7 +96892,7 @@ function collectCanonicalRuleRoots(workdir) {
|
|
|
96120
96892
|
const packageRel = normalized.slice(0, idx);
|
|
96121
96893
|
if (!packageRel)
|
|
96122
96894
|
continue;
|
|
96123
|
-
roots.add(
|
|
96895
|
+
roots.add(join65(workdir, packageRel));
|
|
96124
96896
|
}
|
|
96125
96897
|
return [...roots].sort();
|
|
96126
96898
|
}
|
|
@@ -96142,7 +96914,7 @@ init_logger2();
|
|
|
96142
96914
|
init_detect2();
|
|
96143
96915
|
init_workspace();
|
|
96144
96916
|
init_common();
|
|
96145
|
-
import { join as
|
|
96917
|
+
import { join as join66 } from "path";
|
|
96146
96918
|
function resolveEffective(detected, configPatterns) {
|
|
96147
96919
|
if (configPatterns !== undefined)
|
|
96148
96920
|
return "config";
|
|
@@ -96227,7 +96999,7 @@ async function detectCommand(options) {
|
|
|
96227
96999
|
const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96228
97000
|
const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
|
|
96229
97001
|
const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
96230
|
-
const pkgConfigPath =
|
|
97002
|
+
const pkgConfigPath = join66(workdir, ".nax", "mono", dir, "config.json");
|
|
96231
97003
|
const pkgRaw = await loadRawConfig(pkgConfigPath);
|
|
96232
97004
|
const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
|
|
96233
97005
|
const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
|
|
@@ -96281,13 +97053,13 @@ async function detectCommand(options) {
|
|
|
96281
97053
|
if (rootDetected.confidence === "empty") {
|
|
96282
97054
|
console.log(source_default.yellow(" root: skipped (empty detection)"));
|
|
96283
97055
|
} else {
|
|
96284
|
-
const rootConfigPath =
|
|
97056
|
+
const rootConfigPath = join66(workdir, ".nax", "config.json");
|
|
96285
97057
|
try {
|
|
96286
97058
|
const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
|
|
96287
97059
|
if (status === "skipped") {
|
|
96288
97060
|
console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
|
|
96289
97061
|
} else {
|
|
96290
|
-
console.log(source_default.green(` root: ${status} \u2192 ${
|
|
97062
|
+
console.log(source_default.green(` root: ${status} \u2192 ${join66(".nax", "config.json")}`));
|
|
96291
97063
|
}
|
|
96292
97064
|
} catch (err) {
|
|
96293
97065
|
console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
|
|
@@ -96300,13 +97072,13 @@ async function detectCommand(options) {
|
|
|
96300
97072
|
console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
|
|
96301
97073
|
continue;
|
|
96302
97074
|
}
|
|
96303
|
-
const pkgConfigPath =
|
|
97075
|
+
const pkgConfigPath = join66(workdir, ".nax", "mono", dir, "config.json");
|
|
96304
97076
|
try {
|
|
96305
97077
|
const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
|
|
96306
97078
|
if (status === "skipped") {
|
|
96307
97079
|
console.log(source_default.dim(` ${dir}: skipped (already set)`));
|
|
96308
97080
|
} else {
|
|
96309
|
-
console.log(source_default.green(` ${dir}: ${status} \u2192 ${
|
|
97081
|
+
console.log(source_default.green(` ${dir}: ${status} \u2192 ${join66(".nax", "mono", dir, "config.json")}`));
|
|
96310
97082
|
}
|
|
96311
97083
|
} catch (err) {
|
|
96312
97084
|
console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
|
|
@@ -96323,20 +97095,20 @@ async function detectCommand(options) {
|
|
|
96323
97095
|
|
|
96324
97096
|
// src/commands/logs.ts
|
|
96325
97097
|
init_common();
|
|
96326
|
-
import { existsSync as
|
|
96327
|
-
import { join as
|
|
97098
|
+
import { existsSync as existsSync28 } from "fs";
|
|
97099
|
+
import { join as join70 } from "path";
|
|
96328
97100
|
|
|
96329
97101
|
// src/commands/logs-formatter.ts
|
|
96330
97102
|
init_source();
|
|
96331
97103
|
init_formatter();
|
|
96332
97104
|
import { readdirSync as readdirSync7 } from "fs";
|
|
96333
|
-
import { join as
|
|
97105
|
+
import { join as join69 } from "path";
|
|
96334
97106
|
|
|
96335
97107
|
// src/commands/logs-reader.ts
|
|
96336
97108
|
init_paths3();
|
|
96337
|
-
import { existsSync as
|
|
97109
|
+
import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
|
|
96338
97110
|
import { readdir as readdir4 } from "fs/promises";
|
|
96339
|
-
import { join as
|
|
97111
|
+
import { join as join68 } from "path";
|
|
96340
97112
|
var _logsReaderDeps = {
|
|
96341
97113
|
getRunsDir
|
|
96342
97114
|
};
|
|
@@ -96350,7 +97122,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96350
97122
|
}
|
|
96351
97123
|
let matched = null;
|
|
96352
97124
|
for (const entry of entries) {
|
|
96353
|
-
const metaPath =
|
|
97125
|
+
const metaPath = join68(runsDir, entry, "meta.json");
|
|
96354
97126
|
try {
|
|
96355
97127
|
const meta3 = await Bun.file(metaPath).json();
|
|
96356
97128
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -96362,7 +97134,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96362
97134
|
if (!matched) {
|
|
96363
97135
|
throw new Error(`Run not found in registry: ${runId}`);
|
|
96364
97136
|
}
|
|
96365
|
-
if (!
|
|
97137
|
+
if (!existsSync27(matched.eventsDir)) {
|
|
96366
97138
|
console.log(`Log directory unavailable for run: ${runId}`);
|
|
96367
97139
|
return null;
|
|
96368
97140
|
}
|
|
@@ -96372,14 +97144,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
96372
97144
|
return null;
|
|
96373
97145
|
}
|
|
96374
97146
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
96375
|
-
return
|
|
97147
|
+
return join68(matched.eventsDir, specificFile ?? files[0]);
|
|
96376
97148
|
}
|
|
96377
97149
|
async function selectRunFile(runsDir) {
|
|
96378
97150
|
const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
96379
97151
|
if (files.length === 0) {
|
|
96380
97152
|
return null;
|
|
96381
97153
|
}
|
|
96382
|
-
return
|
|
97154
|
+
return join68(runsDir, files[0]);
|
|
96383
97155
|
}
|
|
96384
97156
|
async function extractRunSummary(filePath) {
|
|
96385
97157
|
const file3 = Bun.file(filePath);
|
|
@@ -96465,7 +97237,7 @@ Runs:
|
|
|
96465
97237
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
96466
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"));
|
|
96467
97239
|
for (const file3 of files) {
|
|
96468
|
-
const filePath =
|
|
97240
|
+
const filePath = join69(runsDir, file3);
|
|
96469
97241
|
const summary = await extractRunSummary(filePath);
|
|
96470
97242
|
const timestamp = file3.replace(".jsonl", "");
|
|
96471
97243
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -96579,7 +97351,7 @@ async function logsCommand(options) {
|
|
|
96579
97351
|
return;
|
|
96580
97352
|
}
|
|
96581
97353
|
const resolved = resolveProject({ dir: options.dir });
|
|
96582
|
-
const naxDir =
|
|
97354
|
+
const naxDir = join70(resolved.projectDir, ".nax");
|
|
96583
97355
|
const configPath = resolved.configPath;
|
|
96584
97356
|
const configFile = Bun.file(configPath);
|
|
96585
97357
|
const config2 = await configFile.json();
|
|
@@ -96587,9 +97359,9 @@ async function logsCommand(options) {
|
|
|
96587
97359
|
if (!featureName) {
|
|
96588
97360
|
throw new Error("No feature specified in config.json");
|
|
96589
97361
|
}
|
|
96590
|
-
const featureDir =
|
|
96591
|
-
const runsDir =
|
|
96592
|
-
if (!
|
|
97362
|
+
const featureDir = join70(naxDir, "features", featureName);
|
|
97363
|
+
const runsDir = join70(featureDir, "runs");
|
|
97364
|
+
if (!existsSync28(runsDir)) {
|
|
96593
97365
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
96594
97366
|
}
|
|
96595
97367
|
if (options.list) {
|
|
@@ -96613,8 +97385,8 @@ init_config();
|
|
|
96613
97385
|
init_prd();
|
|
96614
97386
|
init_precheck();
|
|
96615
97387
|
init_common();
|
|
96616
|
-
import { existsSync as
|
|
96617
|
-
import { join as
|
|
97388
|
+
import { existsSync as existsSync29 } from "fs";
|
|
97389
|
+
import { join as join71 } from "path";
|
|
96618
97390
|
async function precheckCommand(options) {
|
|
96619
97391
|
const resolved = resolveProject({
|
|
96620
97392
|
dir: options.dir,
|
|
@@ -96636,14 +97408,14 @@ async function precheckCommand(options) {
|
|
|
96636
97408
|
process.exit(1);
|
|
96637
97409
|
}
|
|
96638
97410
|
}
|
|
96639
|
-
const naxDir =
|
|
96640
|
-
const featureDir =
|
|
96641
|
-
const prdPath =
|
|
96642
|
-
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)) {
|
|
96643
97415
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
96644
97416
|
process.exit(1);
|
|
96645
97417
|
}
|
|
96646
|
-
if (!
|
|
97418
|
+
if (!existsSync29(prdPath)) {
|
|
96647
97419
|
console.error(source_default.red(`Missing prd.json for feature: ${featureName}`));
|
|
96648
97420
|
console.error(source_default.dim(`Run: nax plan -f ${featureName} --from spec.md --auto`));
|
|
96649
97421
|
process.exit(EXIT_CODES.INVALID_PRD);
|
|
@@ -96661,7 +97433,7 @@ async function precheckCommand(options) {
|
|
|
96661
97433
|
init_source();
|
|
96662
97434
|
init_paths3();
|
|
96663
97435
|
import { readdir as readdir5 } from "fs/promises";
|
|
96664
|
-
import { join as
|
|
97436
|
+
import { join as join72 } from "path";
|
|
96665
97437
|
var DEFAULT_LIMIT = 20;
|
|
96666
97438
|
var _runsCmdDeps = {
|
|
96667
97439
|
getRunsDir
|
|
@@ -96716,7 +97488,7 @@ async function runsCommand(options = {}) {
|
|
|
96716
97488
|
}
|
|
96717
97489
|
const rows = [];
|
|
96718
97490
|
for (const entry of entries) {
|
|
96719
|
-
const metaPath =
|
|
97491
|
+
const metaPath = join72(runsDir, entry, "meta.json");
|
|
96720
97492
|
let meta3;
|
|
96721
97493
|
try {
|
|
96722
97494
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -96793,7 +97565,7 @@ async function runsCommand(options = {}) {
|
|
|
96793
97565
|
|
|
96794
97566
|
// src/commands/unlock.ts
|
|
96795
97567
|
init_source();
|
|
96796
|
-
import { join as
|
|
97568
|
+
import { join as join73 } from "path";
|
|
96797
97569
|
function isProcessAlive2(pid) {
|
|
96798
97570
|
try {
|
|
96799
97571
|
process.kill(pid, 0);
|
|
@@ -96808,7 +97580,7 @@ function formatLockAge(ageMs) {
|
|
|
96808
97580
|
}
|
|
96809
97581
|
async function unlockCommand(options) {
|
|
96810
97582
|
const workdir = options.dir ?? process.cwd();
|
|
96811
|
-
const lockPath =
|
|
97583
|
+
const lockPath = join73(workdir, "nax.lock");
|
|
96812
97584
|
const lockFile = Bun.file(lockPath);
|
|
96813
97585
|
const exists = await lockFile.exists();
|
|
96814
97586
|
if (!exists) {
|
|
@@ -103850,7 +104622,8 @@ function LiveActivityPanel({
|
|
|
103850
104622
|
storySteps,
|
|
103851
104623
|
runSummary,
|
|
103852
104624
|
runErrored,
|
|
103853
|
-
escalationLog = []
|
|
104625
|
+
escalationLog = [],
|
|
104626
|
+
currentStage
|
|
103854
104627
|
}) {
|
|
103855
104628
|
const borderColor = focused ? "cyan" : "gray";
|
|
103856
104629
|
const activeCallList = activeCalls ? Array.from(activeCalls.values()) : [];
|
|
@@ -103907,7 +104680,8 @@ function LiveActivityPanel({
|
|
|
103907
104680
|
paddingY: 1,
|
|
103908
104681
|
children: activeCallList.map((call) => /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(ActiveCallRow, {
|
|
103909
104682
|
call,
|
|
103910
|
-
step: call.storyId ? storySteps?.[call.storyId] : undefined
|
|
104683
|
+
step: call.storyId ? storySteps?.[call.storyId] : undefined,
|
|
104684
|
+
currentStage
|
|
103911
104685
|
}, call.callId, false, undefined, this))
|
|
103912
104686
|
}, undefined, false, undefined, this),
|
|
103913
104687
|
hasEscalations && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
@@ -103948,7 +104722,24 @@ function LiveActivityPanel({
|
|
|
103948
104722
|
!hasActiveCalls && !hasSummary && !hasError && (!storySteps || Object.keys(storySteps).length === 0) && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
103949
104723
|
paddingX: 1,
|
|
103950
104724
|
paddingY: 1,
|
|
103951
|
-
children: /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(
|
|
104725
|
+
children: currentStage ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
104726
|
+
flexDirection: "row",
|
|
104727
|
+
gap: 1,
|
|
104728
|
+
children: [
|
|
104729
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104730
|
+
color: "yellow",
|
|
104731
|
+
children: [
|
|
104732
|
+
"[",
|
|
104733
|
+
currentStage,
|
|
104734
|
+
"]"
|
|
104735
|
+
]
|
|
104736
|
+
}, undefined, true, undefined, this),
|
|
104737
|
+
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104738
|
+
dimColor: true,
|
|
104739
|
+
children: "preparing..."
|
|
104740
|
+
}, undefined, false, undefined, this)
|
|
104741
|
+
]
|
|
104742
|
+
}, undefined, true, undefined, this) : /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
103952
104743
|
dimColor: true,
|
|
103953
104744
|
children: "Waiting for agent..."
|
|
103954
104745
|
}, undefined, false, undefined, this)
|
|
@@ -103956,7 +104747,29 @@ function LiveActivityPanel({
|
|
|
103956
104747
|
]
|
|
103957
104748
|
}, undefined, true, undefined, this);
|
|
103958
104749
|
}
|
|
103959
|
-
function ActiveCallRow({ call, step }) {
|
|
104750
|
+
function ActiveCallRow({ call, step, currentStage }) {
|
|
104751
|
+
const stageLabel = step ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104752
|
+
color: "yellow",
|
|
104753
|
+
children: [
|
|
104754
|
+
"[",
|
|
104755
|
+
step,
|
|
104756
|
+
"]"
|
|
104757
|
+
]
|
|
104758
|
+
}, undefined, true, undefined, this) : call.stage ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104759
|
+
dimColor: true,
|
|
104760
|
+
children: [
|
|
104761
|
+
"[",
|
|
104762
|
+
call.stage,
|
|
104763
|
+
"]"
|
|
104764
|
+
]
|
|
104765
|
+
}, undefined, true, undefined, this) : currentStage ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
104766
|
+
dimColor: true,
|
|
104767
|
+
children: [
|
|
104768
|
+
"[",
|
|
104769
|
+
currentStage,
|
|
104770
|
+
"]"
|
|
104771
|
+
]
|
|
104772
|
+
}, undefined, true, undefined, this) : null;
|
|
103960
104773
|
return /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
103961
104774
|
flexDirection: "column",
|
|
103962
104775
|
marginBottom: 1,
|
|
@@ -103972,21 +104785,7 @@ function ActiveCallRow({ call, step }) {
|
|
|
103972
104785
|
call.storyId && /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
103973
104786
|
children: call.storyId
|
|
103974
104787
|
}, undefined, false, undefined, this),
|
|
103975
|
-
|
|
103976
|
-
color: "yellow",
|
|
103977
|
-
children: [
|
|
103978
|
-
"[",
|
|
103979
|
-
step,
|
|
103980
|
-
"]"
|
|
103981
|
-
]
|
|
103982
|
-
}, undefined, true, undefined, this) : call.stage ? /* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Text, {
|
|
103983
|
-
dimColor: true,
|
|
103984
|
-
children: [
|
|
103985
|
-
"[",
|
|
103986
|
-
call.stage,
|
|
103987
|
-
"]"
|
|
103988
|
-
]
|
|
103989
|
-
}, undefined, true, undefined, this) : null
|
|
104788
|
+
stageLabel
|
|
103990
104789
|
]
|
|
103991
104790
|
}, undefined, true, undefined, this),
|
|
103992
104791
|
/* @__PURE__ */ jsx_dev_runtime3.jsxDEV(Box_default, {
|
|
@@ -104193,7 +104992,14 @@ function getStatusIcon(status) {
|
|
|
104193
104992
|
return "\u23F8\uFE0F";
|
|
104194
104993
|
}
|
|
104195
104994
|
}
|
|
104196
|
-
function StoriesPanel({
|
|
104995
|
+
function StoriesPanel({
|
|
104996
|
+
stories,
|
|
104997
|
+
preRunPhases,
|
|
104998
|
+
postRunPhases,
|
|
104999
|
+
width,
|
|
105000
|
+
compact: compact2 = false,
|
|
105001
|
+
maxHeight
|
|
105002
|
+
}) {
|
|
104197
105003
|
const maxVisible = compact2 ? COMPACT_MAX_VISIBLE_STORIES : MAX_VISIBLE_STORIES;
|
|
104198
105004
|
const needsScrolling = stories.length > maxVisible;
|
|
104199
105005
|
const [scrollOffset, setScrollOffset] = import_react30.useState(0);
|
|
@@ -104225,7 +105031,7 @@ function StoriesPanel({ stories, postRunPhases, width, compact: compact2 = false
|
|
|
104225
105031
|
children: [
|
|
104226
105032
|
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
104227
105033
|
bold: true,
|
|
104228
|
-
children: "
|
|
105034
|
+
children: "Progress"
|
|
104229
105035
|
}, undefined, false, undefined, this),
|
|
104230
105036
|
needsScrolling && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
104231
105037
|
dimColor: true,
|
|
@@ -104237,6 +105043,16 @@ function StoriesPanel({ stories, postRunPhases, width, compact: compact2 = false
|
|
|
104237
105043
|
}, undefined, true, undefined, this)
|
|
104238
105044
|
]
|
|
104239
105045
|
}, undefined, true, undefined, this),
|
|
105046
|
+
preRunPhases && Object.keys(preRunPhases).length > 0 && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
105047
|
+
flexDirection: "column",
|
|
105048
|
+
paddingX: 1,
|
|
105049
|
+
paddingTop: 1,
|
|
105050
|
+
children: Object.entries(preRunPhases).map(([name, phase]) => /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(PreRunPhaseRow, {
|
|
105051
|
+
label: name,
|
|
105052
|
+
phase,
|
|
105053
|
+
compact: compact2
|
|
105054
|
+
}, name, false, undefined, this))
|
|
105055
|
+
}, undefined, false, undefined, this),
|
|
104240
105056
|
needsScrolling && canScrollUp && /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
104241
105057
|
paddingX: 1,
|
|
104242
105058
|
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
@@ -104315,11 +105131,6 @@ function StoriesPanel({ stories, postRunPhases, width, compact: compact2 = false
|
|
|
104315
105131
|
paddingX: 1,
|
|
104316
105132
|
paddingTop: 1,
|
|
104317
105133
|
children: [
|
|
104318
|
-
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
104319
|
-
borderStyle: "single",
|
|
104320
|
-
borderTop: true,
|
|
104321
|
-
borderColor: "gray"
|
|
104322
|
-
}, undefined, false, undefined, this),
|
|
104323
105134
|
/* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
104324
105135
|
dimColor: true,
|
|
104325
105136
|
children: "Post-Run"
|
|
@@ -104344,6 +105155,21 @@ function StoriesPanel({ stories, postRunPhases, width, compact: compact2 = false
|
|
|
104344
105155
|
]
|
|
104345
105156
|
}, undefined, true, undefined, this);
|
|
104346
105157
|
}
|
|
105158
|
+
function PreRunPhaseRow({ label, phase, compact: compact2 }) {
|
|
105159
|
+
const icon = phase.status === "running" ? "\u25CF" : phase.status === "passed" ? "\u2713" : "\u2717";
|
|
105160
|
+
const color = phase.status === "running" ? "yellow" : phase.status === "passed" ? "green" : "red";
|
|
105161
|
+
const displayLabel = compact2 ? label.slice(0, 6) : label;
|
|
105162
|
+
return /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Box_default, {
|
|
105163
|
+
children: /* @__PURE__ */ jsx_dev_runtime5.jsxDEV(Text, {
|
|
105164
|
+
color,
|
|
105165
|
+
children: [
|
|
105166
|
+
icon,
|
|
105167
|
+
" ",
|
|
105168
|
+
displayLabel
|
|
105169
|
+
]
|
|
105170
|
+
}, undefined, true, undefined, this)
|
|
105171
|
+
}, undefined, false, undefined, this);
|
|
105172
|
+
}
|
|
104347
105173
|
function PostRunPhaseRow({ label, phase, compact: compact2 }) {
|
|
104348
105174
|
const icon = phase.status === "running" ? ">" : phase.status === "passed" ? "[OK]" : "[X]";
|
|
104349
105175
|
const color = phase.status === "running" ? "cyan" : phase.status === "passed" ? "green" : "red";
|
|
@@ -104361,110 +105187,120 @@ function PostRunPhaseRow({ label, phase, compact: compact2 }) {
|
|
|
104361
105187
|
|
|
104362
105188
|
// src/tui/hooks/useAgentStreamEvents.ts
|
|
104363
105189
|
var import_react31 = __toESM(require_react(), 1);
|
|
105190
|
+
var RENDER_INTERVAL_MS = 150;
|
|
104364
105191
|
function useAgentStreamEvents(bus) {
|
|
105192
|
+
const activeCallsRef = import_react31.useRef(new Map);
|
|
105193
|
+
const inputTokensRef = import_react31.useRef(0);
|
|
105194
|
+
const outputTokensRef = import_react31.useRef(0);
|
|
105195
|
+
const lastTokensRef = import_react31.useRef(new Map);
|
|
105196
|
+
const dirtyRef = import_react31.useRef(false);
|
|
104365
105197
|
const [activeCalls, setActiveCalls] = import_react31.useState(new Map);
|
|
104366
105198
|
const [inputTokens, setInputTokens] = import_react31.useState(0);
|
|
104367
105199
|
const [outputTokens, setOutputTokens] = import_react31.useState(0);
|
|
104368
|
-
const lastTokensRef = import_react31.useRef(new Map);
|
|
104369
105200
|
import_react31.useEffect(() => {
|
|
104370
105201
|
if (!bus)
|
|
104371
105202
|
return;
|
|
104372
105203
|
const unsubscribe = bus.onAgentStream((event) => {
|
|
104373
|
-
|
|
104374
|
-
|
|
104375
|
-
|
|
104376
|
-
|
|
104377
|
-
|
|
105204
|
+
const next = new Map(activeCallsRef.current);
|
|
105205
|
+
switch (event.kind) {
|
|
105206
|
+
case "agent.call_started": {
|
|
105207
|
+
next.set(event.callId, {
|
|
105208
|
+
callId: event.callId,
|
|
105209
|
+
agentName: event.agentName,
|
|
105210
|
+
storyId: event.storyId,
|
|
105211
|
+
stage: event.stage,
|
|
105212
|
+
startedAt: event.timestamp,
|
|
105213
|
+
lastActivityAt: event.timestamp,
|
|
105214
|
+
messageUpdates: 0,
|
|
105215
|
+
thinkingUpdates: 0,
|
|
105216
|
+
usageUpdates: 0,
|
|
105217
|
+
toolCallUpdates: 0,
|
|
105218
|
+
status: "active",
|
|
105219
|
+
model: event.model
|
|
105220
|
+
});
|
|
105221
|
+
break;
|
|
105222
|
+
}
|
|
105223
|
+
case "agent.message_update": {
|
|
105224
|
+
const state = next.get(event.callId);
|
|
105225
|
+
if (state) {
|
|
104378
105226
|
next.set(event.callId, {
|
|
104379
|
-
|
|
104380
|
-
|
|
104381
|
-
|
|
104382
|
-
stage: event.stage,
|
|
104383
|
-
startedAt: now3,
|
|
104384
|
-
lastActivityAt: now3,
|
|
104385
|
-
messageUpdates: 0,
|
|
104386
|
-
thinkingUpdates: 0,
|
|
104387
|
-
usageUpdates: 0,
|
|
104388
|
-
toolCallUpdates: 0,
|
|
104389
|
-
status: "active",
|
|
104390
|
-
model: event.model
|
|
105227
|
+
...state,
|
|
105228
|
+
messageUpdates: state.messageUpdates + 1,
|
|
105229
|
+
lastActivityAt: event.timestamp
|
|
104391
105230
|
});
|
|
104392
|
-
break;
|
|
104393
|
-
}
|
|
104394
|
-
case "agent.message_update": {
|
|
104395
|
-
const state = next.get(event.callId);
|
|
104396
|
-
if (state) {
|
|
104397
|
-
next.set(event.callId, {
|
|
104398
|
-
...state,
|
|
104399
|
-
messageUpdates: state.messageUpdates + 1,
|
|
104400
|
-
lastActivityAt: event.timestamp
|
|
104401
|
-
});
|
|
104402
|
-
}
|
|
104403
|
-
break;
|
|
104404
105231
|
}
|
|
104405
|
-
|
|
104406
|
-
|
|
104407
|
-
|
|
104408
|
-
|
|
104409
|
-
|
|
104410
|
-
|
|
104411
|
-
|
|
104412
|
-
|
|
104413
|
-
|
|
104414
|
-
|
|
105232
|
+
break;
|
|
105233
|
+
}
|
|
105234
|
+
case "agent.thinking_update": {
|
|
105235
|
+
const state = next.get(event.callId);
|
|
105236
|
+
if (state) {
|
|
105237
|
+
next.set(event.callId, {
|
|
105238
|
+
...state,
|
|
105239
|
+
thinkingUpdates: state.thinkingUpdates + 1,
|
|
105240
|
+
lastActivityAt: event.timestamp
|
|
105241
|
+
});
|
|
104415
105242
|
}
|
|
104416
|
-
|
|
104417
|
-
|
|
104418
|
-
|
|
104419
|
-
|
|
104420
|
-
|
|
104421
|
-
|
|
104422
|
-
|
|
104423
|
-
|
|
104424
|
-
|
|
104425
|
-
|
|
104426
|
-
const last2 = lastTokensRef.current.get(event.callId) ?? { input: 0, output: 0 };
|
|
104427
|
-
const newInput = event.inputTokens ?? last2.input;
|
|
104428
|
-
const newOutput = event.outputTokens ?? last2.output;
|
|
104429
|
-
const deltaIn = newInput - last2.input;
|
|
104430
|
-
const deltaOut = newOutput - last2.output;
|
|
104431
|
-
lastTokensRef.current.set(event.callId, { input: newInput, output: newOutput });
|
|
104432
|
-
if (deltaIn > 0)
|
|
104433
|
-
setInputTokens((prev2) => prev2 + deltaIn);
|
|
104434
|
-
if (deltaOut > 0)
|
|
104435
|
-
setOutputTokens((prev2) => prev2 + deltaOut);
|
|
104436
|
-
}
|
|
104437
|
-
break;
|
|
105243
|
+
break;
|
|
105244
|
+
}
|
|
105245
|
+
case "agent.usage_update": {
|
|
105246
|
+
const state = next.get(event.callId);
|
|
105247
|
+
if (state) {
|
|
105248
|
+
next.set(event.callId, {
|
|
105249
|
+
...state,
|
|
105250
|
+
usageUpdates: state.usageUpdates + 1,
|
|
105251
|
+
lastActivityAt: event.timestamp
|
|
105252
|
+
});
|
|
104438
105253
|
}
|
|
104439
|
-
|
|
104440
|
-
const
|
|
104441
|
-
|
|
104442
|
-
|
|
104443
|
-
|
|
104444
|
-
|
|
104445
|
-
|
|
104446
|
-
|
|
104447
|
-
|
|
104448
|
-
|
|
104449
|
-
|
|
105254
|
+
{
|
|
105255
|
+
const last2 = lastTokensRef.current.get(event.callId) ?? { input: 0, output: 0 };
|
|
105256
|
+
const newInput = event.inputTokens ?? last2.input;
|
|
105257
|
+
const newOutput = event.outputTokens ?? last2.output;
|
|
105258
|
+
const deltaIn = newInput - last2.input;
|
|
105259
|
+
const deltaOut = newOutput - last2.output;
|
|
105260
|
+
lastTokensRef.current.set(event.callId, { input: newInput, output: newOutput });
|
|
105261
|
+
if (deltaIn > 0)
|
|
105262
|
+
inputTokensRef.current += deltaIn;
|
|
105263
|
+
if (deltaOut > 0)
|
|
105264
|
+
outputTokensRef.current += deltaOut;
|
|
104450
105265
|
}
|
|
104451
|
-
|
|
104452
|
-
|
|
104453
|
-
|
|
104454
|
-
|
|
104455
|
-
|
|
104456
|
-
|
|
104457
|
-
|
|
104458
|
-
|
|
105266
|
+
break;
|
|
105267
|
+
}
|
|
105268
|
+
case "agent.tool_call_update": {
|
|
105269
|
+
const state = next.get(event.callId);
|
|
105270
|
+
if (state) {
|
|
105271
|
+
next.set(event.callId, {
|
|
105272
|
+
...state,
|
|
105273
|
+
toolCallUpdates: state.toolCallUpdates + 1,
|
|
105274
|
+
lastActivityAt: event.timestamp,
|
|
105275
|
+
lastToolName: event.toolName
|
|
105276
|
+
});
|
|
104459
105277
|
}
|
|
104460
|
-
|
|
104461
|
-
break;
|
|
105278
|
+
break;
|
|
104462
105279
|
}
|
|
104463
|
-
|
|
104464
|
-
|
|
105280
|
+
case "agent.call_ended": {
|
|
105281
|
+
next.delete(event.callId);
|
|
105282
|
+
lastTokensRef.current.delete(event.callId);
|
|
105283
|
+
break;
|
|
105284
|
+
}
|
|
105285
|
+
default:
|
|
105286
|
+
break;
|
|
105287
|
+
}
|
|
105288
|
+
activeCallsRef.current = next;
|
|
105289
|
+
dirtyRef.current = true;
|
|
104465
105290
|
});
|
|
104466
105291
|
return unsubscribe;
|
|
104467
105292
|
}, [bus]);
|
|
105293
|
+
import_react31.useEffect(() => {
|
|
105294
|
+
const interval = setInterval(() => {
|
|
105295
|
+
if (!dirtyRef.current)
|
|
105296
|
+
return;
|
|
105297
|
+
dirtyRef.current = false;
|
|
105298
|
+
setActiveCalls(new Map(activeCallsRef.current));
|
|
105299
|
+
setInputTokens(inputTokensRef.current);
|
|
105300
|
+
setOutputTokens(outputTokensRef.current);
|
|
105301
|
+
}, RENDER_INTERVAL_MS);
|
|
105302
|
+
return () => clearInterval(interval);
|
|
105303
|
+
}, []);
|
|
104468
105304
|
return { activeCalls, inputTokens, outputTokens };
|
|
104469
105305
|
}
|
|
104470
105306
|
|
|
@@ -104660,14 +105496,33 @@ function usePipelineBusEvents(initialStories) {
|
|
|
104660
105496
|
|
|
104661
105497
|
// src/tui/hooks/usePipelineEvents.ts
|
|
104662
105498
|
var import_react33 = __toESM(require_react(), 1);
|
|
105499
|
+
var PRE_RUN_STAGES = new Set(["acceptance-setup"]);
|
|
104663
105500
|
function usePipelineEvents(events) {
|
|
104664
105501
|
const [currentStage, setCurrentStage] = import_react33.useState(undefined);
|
|
105502
|
+
const [preRunPhases, setPreRunPhases] = import_react33.useState({});
|
|
104665
105503
|
import_react33.useEffect(() => {
|
|
104666
|
-
const onStageEnter = (stage) =>
|
|
105504
|
+
const onStageEnter = (stage) => {
|
|
105505
|
+
setCurrentStage(stage);
|
|
105506
|
+
if (PRE_RUN_STAGES.has(stage)) {
|
|
105507
|
+
setPreRunPhases((prev) => ({ ...prev, [stage]: { status: "running" } }));
|
|
105508
|
+
}
|
|
105509
|
+
};
|
|
105510
|
+
const onStageExit = (stage, result2) => {
|
|
105511
|
+
if (PRE_RUN_STAGES.has(stage)) {
|
|
105512
|
+
setPreRunPhases((prev) => ({
|
|
105513
|
+
...prev,
|
|
105514
|
+
[stage]: { status: result2.action === "fail" ? "failed" : "passed" }
|
|
105515
|
+
}));
|
|
105516
|
+
}
|
|
105517
|
+
};
|
|
104667
105518
|
events.on("stage:enter", onStageEnter);
|
|
104668
|
-
|
|
105519
|
+
events.on("stage:exit", onStageExit);
|
|
105520
|
+
return () => {
|
|
105521
|
+
events.off("stage:enter", onStageEnter);
|
|
105522
|
+
events.off("stage:exit", onStageExit);
|
|
105523
|
+
};
|
|
104669
105524
|
}, [events]);
|
|
104670
|
-
return { currentStage };
|
|
105525
|
+
return { currentStage, preRunPhases };
|
|
104671
105526
|
}
|
|
104672
105527
|
|
|
104673
105528
|
// src/tui/hooks/usePty.ts
|
|
@@ -104799,7 +105654,7 @@ function App2({
|
|
|
104799
105654
|
}) {
|
|
104800
105655
|
const layout = useLayout();
|
|
104801
105656
|
const busState = usePipelineBusEvents(initialStories);
|
|
104802
|
-
const { currentStage } = usePipelineEvents(events);
|
|
105657
|
+
const { currentStage, preRunPhases } = usePipelineEvents(events);
|
|
104803
105658
|
const { exit } = use_app_default();
|
|
104804
105659
|
const startTimeRef = import_react35.useRef(Date.now());
|
|
104805
105660
|
const [elapsedMs, setElapsedMs] = import_react35.useState(0);
|
|
@@ -104820,6 +105675,8 @@ function App2({
|
|
|
104820
105675
|
const runningStories = busState.stories.filter((s) => s.status === "running");
|
|
104821
105676
|
const isParallel = runningStories.length > 1;
|
|
104822
105677
|
const currentRunningStory = runningStories[0];
|
|
105678
|
+
const runningPostRunPhase = busState.postRunPhases.acceptance?.status === "running" ? "post-run: acceptance" : busState.postRunPhases.regression?.status === "running" ? "post-run: regression" : busState.postRunPhases.review?.status === "running" ? "post-run: review" : undefined;
|
|
105679
|
+
const currentPhaseLabel = runningPostRunPhase ?? currentStage;
|
|
104823
105680
|
const runErroredForPanel = busState.runErrored ? "Run encountered an error" : undefined;
|
|
104824
105681
|
const handleKeyboardAction = async (action) => {
|
|
104825
105682
|
switch (action.type) {
|
|
@@ -104903,7 +105760,11 @@ function App2({
|
|
|
104903
105760
|
const isTooSmall = layout.width < MIN_TERMINAL_WIDTH;
|
|
104904
105761
|
const activeCount = runningStories.length;
|
|
104905
105762
|
const displayElapsed = busState.runSummary ? busState.runSummary.durationMs : elapsedMs;
|
|
104906
|
-
const
|
|
105763
|
+
const tokenParts = [
|
|
105764
|
+
inputTokens > 0 ? `${formatTokens(inputTokens)} in` : null,
|
|
105765
|
+
outputTokens > 0 ? `${formatTokens(outputTokens)} out` : null
|
|
105766
|
+
].filter(Boolean);
|
|
105767
|
+
const tokensStr = tokenParts.length > 0 ? tokenParts.join(" / ") : null;
|
|
104907
105768
|
const headerRight = [
|
|
104908
105769
|
activeCount > 0 ? `${activeCount} running` : null,
|
|
104909
105770
|
formatCost3(busState.totalCost),
|
|
@@ -104964,6 +105825,7 @@ function App2({
|
|
|
104964
105825
|
children: [
|
|
104965
105826
|
/* @__PURE__ */ jsx_dev_runtime6.jsxDEV(MemoStoriesPanel, {
|
|
104966
105827
|
stories: busState.stories,
|
|
105828
|
+
preRunPhases,
|
|
104967
105829
|
postRunPhases: busState.postRunPhases,
|
|
104968
105830
|
width: layout.mode === "single" ? layout.width : layout.storiesPanelWidth,
|
|
104969
105831
|
compact: layout.mode === "single",
|
|
@@ -104975,7 +105837,8 @@ function App2({
|
|
|
104975
105837
|
storySteps: busState.storySteps,
|
|
104976
105838
|
runSummary: busState.runSummary,
|
|
104977
105839
|
runErrored: runErroredForPanel,
|
|
104978
|
-
escalationLog: busState.escalationLog
|
|
105840
|
+
escalationLog: busState.escalationLog,
|
|
105841
|
+
currentStage: currentPhaseLabel
|
|
104979
105842
|
}, undefined, false, undefined, this)
|
|
104980
105843
|
]
|
|
104981
105844
|
}, undefined, true, undefined, this),
|
|
@@ -105141,8 +106004,8 @@ Next: nax generate --package ${options.package}`));
|
|
|
105141
106004
|
}
|
|
105142
106005
|
return;
|
|
105143
106006
|
}
|
|
105144
|
-
const naxDir =
|
|
105145
|
-
if (
|
|
106007
|
+
const naxDir = join87(workdir, ".nax");
|
|
106008
|
+
if (existsSync35(naxDir) && !options.force) {
|
|
105146
106009
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
105147
106010
|
return;
|
|
105148
106011
|
}
|
|
@@ -105170,11 +106033,11 @@ Next: nax generate --package ${options.package}`));
|
|
|
105170
106033
|
}
|
|
105171
106034
|
}
|
|
105172
106035
|
}
|
|
105173
|
-
mkdirSync7(
|
|
105174
|
-
mkdirSync7(
|
|
106036
|
+
mkdirSync7(join87(naxDir, "features"), { recursive: true });
|
|
106037
|
+
mkdirSync7(join87(naxDir, "hooks"), { recursive: true });
|
|
105175
106038
|
const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
|
|
105176
|
-
await Bun.write(
|
|
105177
|
-
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({
|
|
105178
106041
|
hooks: {
|
|
105179
106042
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
105180
106043
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -105182,12 +106045,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
105182
106045
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
105183
106046
|
}
|
|
105184
106047
|
}, null, 2));
|
|
105185
|
-
await Bun.write(
|
|
106048
|
+
await Bun.write(join87(naxDir, ".gitignore"), `# nax temp files
|
|
105186
106049
|
*.tmp
|
|
105187
106050
|
.paused.json
|
|
105188
106051
|
.nax-verifier-verdict.json
|
|
105189
106052
|
`);
|
|
105190
|
-
await Bun.write(
|
|
106053
|
+
await Bun.write(join87(naxDir, "context.md"), `# Project Context
|
|
105191
106054
|
|
|
105192
106055
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
105193
106056
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -105272,6 +106135,24 @@ Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cu
|
|
|
105272
106135
|
console.log(source_default.dim(`
|
|
105273
106136
|
Next: nax features create <name>`));
|
|
105274
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
|
+
});
|
|
105275
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) => {
|
|
105276
106157
|
let workdir;
|
|
105277
106158
|
try {
|
|
@@ -105284,7 +106165,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105284
106165
|
console.error(source_default.red("Error: --plan requires --from <spec-path>"));
|
|
105285
106166
|
process.exit(1);
|
|
105286
106167
|
}
|
|
105287
|
-
if (options.from && !
|
|
106168
|
+
if (options.from && !existsSync35(options.from)) {
|
|
105288
106169
|
console.error(source_default.red(`Error: File not found: ${options.from} (required with --plan)`));
|
|
105289
106170
|
process.exit(1);
|
|
105290
106171
|
}
|
|
@@ -105317,10 +106198,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105317
106198
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105318
106199
|
process.exit(1);
|
|
105319
106200
|
}
|
|
105320
|
-
const featureDir =
|
|
105321
|
-
const prdPath =
|
|
106201
|
+
const featureDir = join87(naxDir, "features", options.feature);
|
|
106202
|
+
const prdPath = join87(featureDir, "prd.json");
|
|
105322
106203
|
if (options.plan && options.from) {
|
|
105323
|
-
if (
|
|
106204
|
+
if (existsSync35(prdPath) && !options.force) {
|
|
105324
106205
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
105325
106206
|
console.error(source_default.dim(" Use --force to overwrite, or run without --plan to use the existing PRD."));
|
|
105326
106207
|
process.exit(1);
|
|
@@ -105340,10 +106221,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105340
106221
|
}
|
|
105341
106222
|
}
|
|
105342
106223
|
try {
|
|
105343
|
-
const planLogDir =
|
|
106224
|
+
const planLogDir = join87(featureDir, "plan");
|
|
105344
106225
|
mkdirSync7(planLogDir, { recursive: true });
|
|
105345
106226
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105346
|
-
const planLogPath =
|
|
106227
|
+
const planLogPath = join87(planLogDir, `${planLogId}.jsonl`);
|
|
105347
106228
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
105348
106229
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
105349
106230
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -105382,17 +106263,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105382
106263
|
process.exit(1);
|
|
105383
106264
|
}
|
|
105384
106265
|
}
|
|
105385
|
-
if (!
|
|
106266
|
+
if (!existsSync35(prdPath)) {
|
|
105386
106267
|
console.error(source_default.red(`Feature "${options.feature}" not found or missing prd.json`));
|
|
105387
106268
|
process.exit(1);
|
|
105388
106269
|
}
|
|
105389
106270
|
resetLogger();
|
|
105390
106271
|
const projectKey = config2.name?.trim() || basename16(workdir);
|
|
105391
106272
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
105392
|
-
const runsDir =
|
|
106273
|
+
const runsDir = join87(outputDir, "features", options.feature, "runs");
|
|
105393
106274
|
mkdirSync7(runsDir, { recursive: true });
|
|
105394
106275
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105395
|
-
const logFilePath =
|
|
106276
|
+
const logFilePath = join87(runsDir, `${runId}.jsonl`);
|
|
105396
106277
|
const isTTY = process.stdout.isTTY ?? false;
|
|
105397
106278
|
const headlessFlag = options.headless ?? false;
|
|
105398
106279
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -105410,7 +106291,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105410
106291
|
config2.agent.default = options.agent;
|
|
105411
106292
|
}
|
|
105412
106293
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
105413
|
-
const globalNaxDir =
|
|
106294
|
+
const globalNaxDir = join87(homedir3(), ".nax");
|
|
105414
106295
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
105415
106296
|
const eventEmitter = new PipelineEventEmitter;
|
|
105416
106297
|
const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
|
|
@@ -105430,12 +106311,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105430
106311
|
events: eventEmitter,
|
|
105431
106312
|
ptyOptions: null,
|
|
105432
106313
|
agentStreamEvents,
|
|
105433
|
-
queueFilePath:
|
|
106314
|
+
queueFilePath: join87(workdir, ".queue.txt")
|
|
105434
106315
|
});
|
|
105435
106316
|
} else {
|
|
105436
106317
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
105437
106318
|
}
|
|
105438
|
-
const statusFilePath =
|
|
106319
|
+
const statusFilePath = join87(outputDir, "status.json");
|
|
105439
106320
|
let parallel;
|
|
105440
106321
|
if (options.parallel !== undefined) {
|
|
105441
106322
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -105462,9 +106343,9 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
105462
106343
|
skipPrecheck: options.skipPrecheck ?? false,
|
|
105463
106344
|
agentStreamEvents
|
|
105464
106345
|
});
|
|
105465
|
-
const latestSymlink =
|
|
106346
|
+
const latestSymlink = join87(runsDir, "latest.jsonl");
|
|
105466
106347
|
try {
|
|
105467
|
-
if (
|
|
106348
|
+
if (existsSync35(latestSymlink)) {
|
|
105468
106349
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
105469
106350
|
}
|
|
105470
106351
|
Bun.spawnSync(["ln", "-s", `${runId}.jsonl`, latestSymlink], {
|
|
@@ -105523,9 +106404,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
105523
106404
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
105524
106405
|
process.exit(1);
|
|
105525
106406
|
}
|
|
105526
|
-
const featureDir =
|
|
106407
|
+
const featureDir = join87(naxDir, "features", name);
|
|
105527
106408
|
mkdirSync7(featureDir, { recursive: true });
|
|
105528
|
-
await Bun.write(
|
|
106409
|
+
await Bun.write(join87(featureDir, "spec.md"), `# Feature: ${name}
|
|
105529
106410
|
|
|
105530
106411
|
## Overview
|
|
105531
106412
|
|
|
@@ -105558,7 +106439,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
105558
106439
|
|
|
105559
106440
|
<!-- What this feature explicitly does NOT cover. -->
|
|
105560
106441
|
`);
|
|
105561
|
-
await Bun.write(
|
|
106442
|
+
await Bun.write(join87(featureDir, "progress.txt"), `# Progress: ${name}
|
|
105562
106443
|
|
|
105563
106444
|
Created: ${new Date().toISOString()}
|
|
105564
106445
|
|
|
@@ -105584,8 +106465,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
105584
106465
|
console.error(source_default.red("nax not initialized."));
|
|
105585
106466
|
process.exit(1);
|
|
105586
106467
|
}
|
|
105587
|
-
const featuresDir =
|
|
105588
|
-
if (!
|
|
106468
|
+
const featuresDir = join87(naxDir, "features");
|
|
106469
|
+
if (!existsSync35(featuresDir)) {
|
|
105589
106470
|
console.log(source_default.dim("No features yet."));
|
|
105590
106471
|
return;
|
|
105591
106472
|
}
|
|
@@ -105599,8 +106480,8 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
105599
106480
|
Features:
|
|
105600
106481
|
`));
|
|
105601
106482
|
for (const name of entries) {
|
|
105602
|
-
const prdPath =
|
|
105603
|
-
if (
|
|
106483
|
+
const prdPath = join87(featuresDir, name, "prd.json");
|
|
106484
|
+
if (existsSync35(prdPath)) {
|
|
105604
106485
|
const prd = await loadPRD(prdPath);
|
|
105605
106486
|
const c = countStories(prd);
|
|
105606
106487
|
console.log(` ${name} \u2014 ${c.passed}/${c.total} stories done`);
|
|
@@ -105634,10 +106515,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
105634
106515
|
cliOverrides.profile = options.profile;
|
|
105635
106516
|
}
|
|
105636
106517
|
const config2 = await loadConfig(workdir, cliOverrides);
|
|
105637
|
-
const featureLogDir =
|
|
106518
|
+
const featureLogDir = join87(naxDir, "features", options.feature, "plan");
|
|
105638
106519
|
mkdirSync7(featureLogDir, { recursive: true });
|
|
105639
106520
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
105640
|
-
const planLogPath =
|
|
106521
|
+
const planLogPath = join87(featureLogDir, `${planLogId}.jsonl`);
|
|
105641
106522
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
105642
106523
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
105643
106524
|
try {
|