@nathapp/nax 0.69.1 → 0.69.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nax.js +588 -464
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -16983,10 +16983,10 @@ var init_schemas_execution = __esm(() => {
|
|
|
16983
16983
|
autoApproveVerifier: exports_external.boolean(),
|
|
16984
16984
|
strategy: exports_external.enum(["auto", "strict", "lite", "off"]).default("auto"),
|
|
16985
16985
|
sessionTiers: exports_external.object({
|
|
16986
|
-
testWriter:
|
|
16987
|
-
|
|
16988
|
-
|
|
16989
|
-
}).
|
|
16986
|
+
testWriter: ConfiguredModelSchema.default("fast"),
|
|
16987
|
+
verifier: ConfiguredModelSchema.default("fast"),
|
|
16988
|
+
implementer: ConfiguredModelSchema.optional()
|
|
16989
|
+
}).default({ testWriter: "fast", verifier: "fast" }),
|
|
16990
16990
|
testWriterAllowedPaths: exports_external.array(exports_external.string()).optional(),
|
|
16991
16991
|
rollbackOnFailure: exports_external.boolean().optional(),
|
|
16992
16992
|
greenfieldDetection: exports_external.boolean().optional()
|
|
@@ -17413,7 +17413,7 @@ var init_schemas3 = __esm(() => {
|
|
|
17413
17413
|
autoApproveVerifier: true,
|
|
17414
17414
|
strategy: "auto",
|
|
17415
17415
|
sessionTiers: {
|
|
17416
|
-
testWriter: "
|
|
17416
|
+
testWriter: "fast",
|
|
17417
17417
|
verifier: "fast"
|
|
17418
17418
|
},
|
|
17419
17419
|
testWriterAllowedPaths: ["src/index.ts", "src/**/index.ts"],
|
|
@@ -18946,6 +18946,14 @@ ${errors3.join(`
|
|
|
18946
18946
|
}
|
|
18947
18947
|
return result.data;
|
|
18948
18948
|
}
|
|
18949
|
+
async function loadPackageOverride(repoRoot, packageDir) {
|
|
18950
|
+
const packageConfigPath = join3(repoRoot, PROJECT_NAX_DIR, "mono", packageDir, "config.json");
|
|
18951
|
+
const override = await loadJsonFile(packageConfigPath, "config");
|
|
18952
|
+
if (!override)
|
|
18953
|
+
return null;
|
|
18954
|
+
const { profile: _profile, ...fields } = override;
|
|
18955
|
+
return fields;
|
|
18956
|
+
}
|
|
18949
18957
|
async function loadConfigForWorkdir(rootConfigPath, packageDir, cliOverrides) {
|
|
18950
18958
|
const logger = getLogger();
|
|
18951
18959
|
const resolvedRootConfigPath = resolve3(rootConfigPath);
|
|
@@ -19416,8 +19424,10 @@ __export(exports_config, {
|
|
|
19416
19424
|
precheckConfigSelector: () => precheckConfigSelector,
|
|
19417
19425
|
planConfigSelector: () => planConfigSelector,
|
|
19418
19426
|
pickSelector: () => pickSelector,
|
|
19427
|
+
mergePackageConfig: () => mergePackageConfig,
|
|
19419
19428
|
loadProfileEnv: () => loadProfileEnv,
|
|
19420
19429
|
loadProfile: () => loadProfile,
|
|
19430
|
+
loadPackageOverride: () => loadPackageOverride,
|
|
19421
19431
|
loadConfigForWorkdir: () => loadConfigForWorkdir,
|
|
19422
19432
|
loadConfig: () => loadConfig,
|
|
19423
19433
|
llmRoutingConfigSelector: () => llmRoutingConfigSelector,
|
|
@@ -19442,6 +19452,7 @@ __export(exports_config, {
|
|
|
19442
19452
|
acceptanceFixConfigSelector: () => acceptanceFixConfigSelector,
|
|
19443
19453
|
acceptanceConfigSelector: () => acceptanceConfigSelector,
|
|
19444
19454
|
VALID_TEST_STRATEGIES: () => VALID_TEST_STRATEGIES,
|
|
19455
|
+
TddConfigSchema: () => TddConfigSchema,
|
|
19445
19456
|
THREE_SESSION_STRATEGIES: () => THREE_SESSION_STRATEGIES,
|
|
19446
19457
|
TEST_STRATEGY_GUIDE: () => TEST_STRATEGY_GUIDE,
|
|
19447
19458
|
SPEC_ANCHOR_RULES: () => SPEC_ANCHOR_RULES,
|
|
@@ -19463,6 +19474,7 @@ var init_config = __esm(() => {
|
|
|
19463
19474
|
init_schema();
|
|
19464
19475
|
init_schemas_model();
|
|
19465
19476
|
init_schemas_debate();
|
|
19477
|
+
init_schemas_execution();
|
|
19466
19478
|
init_loader();
|
|
19467
19479
|
init_path_security();
|
|
19468
19480
|
init_paths();
|
|
@@ -23959,6 +23971,7 @@ var init_detect = __esm(() => {
|
|
|
23959
23971
|
});
|
|
23960
23972
|
|
|
23961
23973
|
// src/test-runners/detect/workspace.ts
|
|
23974
|
+
import { join as join6 } from "path";
|
|
23962
23975
|
async function expandWorkspaceGlob(workdir, pattern) {
|
|
23963
23976
|
const dirs = [];
|
|
23964
23977
|
try {
|
|
@@ -24032,15 +24045,31 @@ async function detectTurboOrNx(workdir) {
|
|
|
24032
24045
|
}
|
|
24033
24046
|
async function detectNaxMonoLayout(workdir) {
|
|
24034
24047
|
const dirs = [];
|
|
24035
|
-
|
|
24036
|
-
|
|
24037
|
-
|
|
24038
|
-
|
|
24039
|
-
|
|
24040
|
-
|
|
24041
|
-
|
|
24048
|
+
const monoRoot = join6(workdir, ".nax", "mono");
|
|
24049
|
+
async function walk(currentDir, relativeParts) {
|
|
24050
|
+
const entries = [];
|
|
24051
|
+
for await (const entry of _workspaceDeps.glob("*", currentDir)) {
|
|
24052
|
+
entries.push(entry);
|
|
24053
|
+
}
|
|
24054
|
+
let hasConfig = false;
|
|
24055
|
+
for (const entry of entries) {
|
|
24056
|
+
const entryPath = join6(currentDir, entry);
|
|
24057
|
+
const stat = await _workspaceDeps.stat(entryPath);
|
|
24058
|
+
const isDirectory = (stat.mode & 61440) === 16384;
|
|
24059
|
+
if (!isDirectory && entry === "config.json") {
|
|
24060
|
+
hasConfig = true;
|
|
24061
|
+
continue;
|
|
24062
|
+
}
|
|
24063
|
+
if (isDirectory) {
|
|
24064
|
+
await walk(entryPath, [...relativeParts, entry]);
|
|
24042
24065
|
}
|
|
24043
24066
|
}
|
|
24067
|
+
if (hasConfig && relativeParts.length > 0) {
|
|
24068
|
+
dirs.push(relativeParts.join("/"));
|
|
24069
|
+
}
|
|
24070
|
+
}
|
|
24071
|
+
try {
|
|
24072
|
+
await walk(monoRoot, []);
|
|
24044
24073
|
} catch {}
|
|
24045
24074
|
return dirs;
|
|
24046
24075
|
}
|
|
@@ -24084,7 +24113,8 @@ var init_workspace = __esm(() => {
|
|
|
24084
24113
|
return f.text();
|
|
24085
24114
|
},
|
|
24086
24115
|
spawn: Bun.spawn,
|
|
24087
|
-
glob: (pattern, cwd) => new Bun.Glob(pattern).scan({ cwd, onlyFiles: false })
|
|
24116
|
+
glob: (pattern, cwd) => new Bun.Glob(pattern).scan({ cwd, onlyFiles: false }),
|
|
24117
|
+
stat: (path) => Bun.file(path).stat()
|
|
24088
24118
|
};
|
|
24089
24119
|
_workspaceCache = new Map;
|
|
24090
24120
|
});
|
|
@@ -24158,7 +24188,7 @@ var init_detector2 = __esm(() => {
|
|
|
24158
24188
|
});
|
|
24159
24189
|
|
|
24160
24190
|
// src/test-runners/resolver.ts
|
|
24161
|
-
import { dirname as dirname2, isAbsolute as isAbsolute3, join as
|
|
24191
|
+
import { dirname as dirname2, isAbsolute as isAbsolute3, join as join7, relative, resolve as resolve4 } from "path";
|
|
24162
24192
|
function buildResolved(globs, resolution) {
|
|
24163
24193
|
return {
|
|
24164
24194
|
globs,
|
|
@@ -24210,7 +24240,7 @@ async function resolveTestFilePatterns(config2, workdir, packageDir, options) {
|
|
|
24210
24240
|
validateGlobs(rootPatterns, "resolver");
|
|
24211
24241
|
return buildResolved(rootPatterns, "root-config");
|
|
24212
24242
|
}
|
|
24213
|
-
const detectionWorkdir = packageDir ?
|
|
24243
|
+
const detectionWorkdir = packageDir ? join7(workdir, packageDir) : workdir;
|
|
24214
24244
|
const detected = await _resolverDeps.detectTestFilePatterns(detectionWorkdir);
|
|
24215
24245
|
if (detected.confidence !== "empty" && detected.patterns.length > 0) {
|
|
24216
24246
|
getSafeLogger()?.info("resolver", "Test patterns auto-detected", {
|
|
@@ -24789,7 +24819,7 @@ var init_git = __esm(() => {
|
|
|
24789
24819
|
});
|
|
24790
24820
|
|
|
24791
24821
|
// src/utils/path-filters.ts
|
|
24792
|
-
import { join as
|
|
24822
|
+
import { join as join8, relative as relative2 } from "path";
|
|
24793
24823
|
function basename3(path) {
|
|
24794
24824
|
const stripped = path.startsWith("./") ? path.slice(2) : path;
|
|
24795
24825
|
const idx = stripped.lastIndexOf("/");
|
|
@@ -24879,8 +24909,8 @@ async function resolveNaxIgnorePatterns(repoRoot, packageDir) {
|
|
|
24879
24909
|
const normalizedRepoRoot = normalizePath(repoRoot);
|
|
24880
24910
|
const normalizedPackageDir = packageDir ? normalizePath(packageDir) : normalizedRepoRoot;
|
|
24881
24911
|
const packagePrefix = normalizedPackageDir !== normalizedRepoRoot ? normalizePath(relative2(repoRoot, packageDir ?? repoRoot)) : null;
|
|
24882
|
-
const rootFile =
|
|
24883
|
-
const packageFile =
|
|
24912
|
+
const rootFile = join8(repoRoot, NAX_IGNORE_FILENAME);
|
|
24913
|
+
const packageFile = join8(packageDir ?? repoRoot, NAX_IGNORE_FILENAME);
|
|
24884
24914
|
const rootPatterns = await readIgnorePatterns(rootFile);
|
|
24885
24915
|
const packagePatterns = packageDir && packageDir !== repoRoot ? await readIgnorePatterns(packageFile) : [];
|
|
24886
24916
|
return [
|
|
@@ -24949,7 +24979,7 @@ var init_path_filters = __esm(() => {
|
|
|
24949
24979
|
});
|
|
24950
24980
|
|
|
24951
24981
|
// src/verification/smart-runner.ts
|
|
24952
|
-
import { join as
|
|
24982
|
+
import { join as join9, relative as relative3 } from "path";
|
|
24953
24983
|
function clearGitRootCache() {
|
|
24954
24984
|
_gitRootCache.clear();
|
|
24955
24985
|
}
|
|
@@ -25067,7 +25097,7 @@ async function getChangedNonTestFiles(workdir, baseRef, packagePrefix, testFileR
|
|
|
25067
25097
|
const lines = stdout.trim().split(`
|
|
25068
25098
|
`).filter(Boolean);
|
|
25069
25099
|
const effectiveRepoRoot = repoRoot ?? workdir;
|
|
25070
|
-
const packageDir = packagePrefix ?
|
|
25100
|
+
const packageDir = packagePrefix ? join9(effectiveRepoRoot, packagePrefix) : undefined;
|
|
25071
25101
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(effectiveRepoRoot, packageDir);
|
|
25072
25102
|
let effectivePrefix = packagePrefix;
|
|
25073
25103
|
if (packagePrefix && repoRoot) {
|
|
@@ -25096,7 +25126,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
25096
25126
|
return [];
|
|
25097
25127
|
const lines = stdout.trim().split(`
|
|
25098
25128
|
`).filter(Boolean);
|
|
25099
|
-
const packageDir = packagePrefix ?
|
|
25129
|
+
const packageDir = packagePrefix ? join9(repoRoot, packagePrefix) : undefined;
|
|
25100
25130
|
const ignoreMatchers = naxIgnoreIndex?.getMatchers(packageDir) ?? await resolveNaxIgnorePatterns(repoRoot, packageDir);
|
|
25101
25131
|
const gitRoot = await getGitRootMemo(workdir);
|
|
25102
25132
|
const extraPrefix = gitRoot && gitRoot !== repoRoot ? relative3(gitRoot, repoRoot) : "";
|
|
@@ -25104,7 +25134,7 @@ async function getChangedTestFiles(workdir, repoRoot, baseRef, packagePrefix, te
|
|
|
25104
25134
|
const scopedRaw = effectivePrefix ? lines.filter((f) => f.startsWith(`${effectivePrefix}/`)) : lines;
|
|
25105
25135
|
const scoped = filterNaxInternalPaths(scopedRaw, ignoreMatchers);
|
|
25106
25136
|
const stripped = extraPrefix ? scoped.map((f) => f.slice(`${extraPrefix}/`.length)) : scoped;
|
|
25107
|
-
return stripped.filter((f) => testFileRegex.some((re) => re.test(f))).map((f) =>
|
|
25137
|
+
return stripped.filter((f) => testFileRegex.some((re) => re.test(f))).map((f) => join9(repoRoot, f));
|
|
25108
25138
|
} catch {
|
|
25109
25139
|
return [];
|
|
25110
25140
|
}
|
|
@@ -25236,6 +25266,7 @@ __export(exports_test_runners, {
|
|
|
25236
25266
|
formatFailureSummary: () => formatFailureSummary,
|
|
25237
25267
|
findPackageDir: () => findPackageDir,
|
|
25238
25268
|
extractTestDirs: () => extractTestDirs,
|
|
25269
|
+
discoverWorkspacePackages: () => discoverWorkspacePackages,
|
|
25239
25270
|
detectTestFilePatterns: () => detectTestFilePatterns,
|
|
25240
25271
|
detectManifestFrameworksFromPackageJson: () => detectManifestFrameworksFromPackageJson,
|
|
25241
25272
|
detectFramework: () => detectFramework,
|
|
@@ -25257,6 +25288,7 @@ var init_test_runners = __esm(() => {
|
|
|
25257
25288
|
init_detector2();
|
|
25258
25289
|
init_resolver();
|
|
25259
25290
|
init_parser();
|
|
25291
|
+
init_workspace();
|
|
25260
25292
|
init_ac_parser();
|
|
25261
25293
|
init_scoped_selection();
|
|
25262
25294
|
});
|
|
@@ -25308,7 +25340,7 @@ var init_project = __esm(() => {
|
|
|
25308
25340
|
|
|
25309
25341
|
// src/utils/path-security.ts
|
|
25310
25342
|
import { realpathSync as realpathSync2 } from "fs";
|
|
25311
|
-
import { dirname as dirname3, isAbsolute as isAbsolute4, join as
|
|
25343
|
+
import { dirname as dirname3, isAbsolute as isAbsolute4, join as join10, normalize as normalize3, resolve as resolve5 } from "path";
|
|
25312
25344
|
function safeRealpathForComparison(p) {
|
|
25313
25345
|
try {
|
|
25314
25346
|
return realpathSync2(p);
|
|
@@ -25317,7 +25349,7 @@ function safeRealpathForComparison(p) {
|
|
|
25317
25349
|
if (parent === p)
|
|
25318
25350
|
return normalize3(p);
|
|
25319
25351
|
const resolvedParent = safeRealpathForComparison(parent);
|
|
25320
|
-
return
|
|
25352
|
+
return join10(resolvedParent, p.split("/").pop() ?? "");
|
|
25321
25353
|
}
|
|
25322
25354
|
}
|
|
25323
25355
|
function isRelativeAndSafe(filePath) {
|
|
@@ -25345,7 +25377,7 @@ function validateModulePath(modulePath, allowedRoots) {
|
|
|
25345
25377
|
} else {
|
|
25346
25378
|
for (let i = 0;i < allowedRoots.length; i++) {
|
|
25347
25379
|
const originalRoot = resolve5(allowedRoots[i]);
|
|
25348
|
-
const absoluteInput = resolve5(
|
|
25380
|
+
const absoluteInput = resolve5(join10(originalRoot, modulePath));
|
|
25349
25381
|
const resolved = safeRealpathForComparison(absoluteInput);
|
|
25350
25382
|
const resolvedRoot = resolvedRoots[i];
|
|
25351
25383
|
if (resolved.startsWith(`${resolvedRoot}/`) || resolved === resolvedRoot) {
|
|
@@ -25362,7 +25394,7 @@ var init_path_security2 = () => {};
|
|
|
25362
25394
|
|
|
25363
25395
|
// src/context/engine/providers/code-neighbor.ts
|
|
25364
25396
|
import { createHash as createHash3 } from "crypto";
|
|
25365
|
-
import { join as
|
|
25397
|
+
import { join as join11, relative as relative4, resolve as resolve6 } from "path";
|
|
25366
25398
|
function isExcludedPath(file3, ignoreMatchers) {
|
|
25367
25399
|
for (const prefix of EXCLUDED_DIR_PREFIXES2) {
|
|
25368
25400
|
if (file3.startsWith(prefix) || file3.includes(`/${prefix}`))
|
|
@@ -25500,7 +25532,7 @@ async function readCached(absolutePath, cache) {
|
|
|
25500
25532
|
async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, siblingTestContext) {
|
|
25501
25533
|
const neighbors = new Set;
|
|
25502
25534
|
let anyTruncated = false;
|
|
25503
|
-
const ownAbsPath =
|
|
25535
|
+
const ownAbsPath = join11(workdir, filePath);
|
|
25504
25536
|
if (await _codeNeighborDeps.fileExists(ownAbsPath)) {
|
|
25505
25537
|
const ownContent = await readCached(ownAbsPath, contentCache);
|
|
25506
25538
|
if (ownContent !== null && ownContent.length > 0) {
|
|
@@ -25522,7 +25554,7 @@ async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, si
|
|
|
25522
25554
|
break outer;
|
|
25523
25555
|
if (srcFile === filePath)
|
|
25524
25556
|
continue;
|
|
25525
|
-
const content = await readCached(
|
|
25557
|
+
const content = await readCached(join11(scanWorkdir, srcFile), contentCache);
|
|
25526
25558
|
if (content?.includes(fileBaseName)) {
|
|
25527
25559
|
for (const spec of parseImportSpecifiers(content)) {
|
|
25528
25560
|
const resolved = resolveImport(spec, srcFile, scanWorkdir);
|
|
@@ -25538,7 +25570,7 @@ async function collectNeighbors(filePath, workdir, scannedDirs, contentCache, si
|
|
|
25538
25570
|
const candidates = deriveSiblingTestCandidates(filePath, siblingTestContext.globs);
|
|
25539
25571
|
let chosen = null;
|
|
25540
25572
|
for (const candidate of candidates) {
|
|
25541
|
-
if (await _codeNeighborDeps.fileExists(
|
|
25573
|
+
if (await _codeNeighborDeps.fileExists(join11(workdir, candidate))) {
|
|
25542
25574
|
chosen = candidate;
|
|
25543
25575
|
break;
|
|
25544
25576
|
}
|
|
@@ -25562,7 +25594,7 @@ async function resolveExtraGlobWorkdirs(neighborScope, crossPackageDepth, repoRo
|
|
|
25562
25594
|
const relPkgDirs = await _codeNeighborDeps.discoverWorkspacePackages(repoRoot);
|
|
25563
25595
|
if (relPkgDirs.length === 0)
|
|
25564
25596
|
return [repoRoot];
|
|
25565
|
-
return relPkgDirs.map((rel) =>
|
|
25597
|
+
return relPkgDirs.map((rel) => join11(repoRoot, rel)).filter((abs) => abs !== packageDir);
|
|
25566
25598
|
} catch {
|
|
25567
25599
|
return [repoRoot];
|
|
25568
25600
|
}
|
|
@@ -26859,7 +26891,7 @@ var init_orchestrator = __esm(() => {
|
|
|
26859
26891
|
});
|
|
26860
26892
|
|
|
26861
26893
|
// src/context/rules/canonical-loader.ts
|
|
26862
|
-
import { basename as basename4, join as
|
|
26894
|
+
import { basename as basename4, join as join12 } from "path";
|
|
26863
26895
|
function parseRuleAllowMarker(line) {
|
|
26864
26896
|
const allowed = new Set;
|
|
26865
26897
|
RULE_ALLOW_MARKER.lastIndex = 0;
|
|
@@ -26994,7 +27026,7 @@ function applyCanonicalRulesBudget(rules, budgetTokens) {
|
|
|
26994
27026
|
}
|
|
26995
27027
|
async function loadCanonicalRules(workdir, options = {}) {
|
|
26996
27028
|
const logger = _canonicalLoaderDeps.getLogger();
|
|
26997
|
-
const rulesDir =
|
|
27029
|
+
const rulesDir = join12(workdir, CANONICAL_RULES_DIR);
|
|
26998
27030
|
const allFilePaths = _canonicalLoaderDeps.globInDir(rulesDir);
|
|
26999
27031
|
const filePaths = allFilePaths.filter((filePath) => {
|
|
27000
27032
|
const normalized = filePath.replaceAll("\\", "/");
|
|
@@ -27095,7 +27127,7 @@ var init_canonical_loader = __esm(() => {
|
|
|
27095
27127
|
for (const rel of files) {
|
|
27096
27128
|
const depth = rel.split("/").length - 1;
|
|
27097
27129
|
if (depth <= 1) {
|
|
27098
|
-
kept.push(
|
|
27130
|
+
kept.push(join12(dir, rel));
|
|
27099
27131
|
} else {
|
|
27100
27132
|
ignored.push(rel);
|
|
27101
27133
|
}
|
|
@@ -27377,7 +27409,7 @@ var init_session_scratch = __esm(() => {
|
|
|
27377
27409
|
|
|
27378
27410
|
// src/context/engine/providers/static-rules.ts
|
|
27379
27411
|
import { createHash as createHash8 } from "crypto";
|
|
27380
|
-
import { join as
|
|
27412
|
+
import { join as join13, relative as relative5 } from "path";
|
|
27381
27413
|
function contentHash85(content) {
|
|
27382
27414
|
return createHash8("sha256").update(content).digest("hex").slice(0, 8);
|
|
27383
27415
|
}
|
|
@@ -27585,7 +27617,7 @@ ${rule.content}`,
|
|
|
27585
27617
|
const existingCandidates = [];
|
|
27586
27618
|
for (const fileName of LEGACY_CANDIDATE_FILES) {
|
|
27587
27619
|
try {
|
|
27588
|
-
if (await _staticRulesDeps.fileExists(
|
|
27620
|
+
if (await _staticRulesDeps.fileExists(join13(rootDir, fileName))) {
|
|
27589
27621
|
existingCandidates.push(fileName);
|
|
27590
27622
|
}
|
|
27591
27623
|
} catch {}
|
|
@@ -27601,11 +27633,11 @@ ${rule.content}`,
|
|
|
27601
27633
|
for (const fileName of LEGACY_CANDIDATE_FILES) {
|
|
27602
27634
|
legacySources.push({
|
|
27603
27635
|
sourceId: fileName,
|
|
27604
|
-
filePath:
|
|
27636
|
+
filePath: join13(rootDir, fileName),
|
|
27605
27637
|
heading: fileName
|
|
27606
27638
|
});
|
|
27607
27639
|
}
|
|
27608
|
-
const rulesDir =
|
|
27640
|
+
const rulesDir = join13(rootDir, LEGACY_RULES_DIR);
|
|
27609
27641
|
const nestedRulePaths = _staticRulesDeps.globInDir(rulesDir);
|
|
27610
27642
|
for (const filePath of nestedRulePaths) {
|
|
27611
27643
|
const normalized = normalizePath2(filePath);
|
|
@@ -27664,7 +27696,7 @@ var init_static_rules = __esm(() => {
|
|
|
27664
27696
|
fileExists: async (path) => Bun.file(path).exists(),
|
|
27665
27697
|
globInDir: (dir) => {
|
|
27666
27698
|
try {
|
|
27667
|
-
return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) =>
|
|
27699
|
+
return [...new Bun.Glob("**/*.md").scanSync({ cwd: dir, absolute: false })].sort().map((f) => join13(dir, f));
|
|
27668
27700
|
} catch {
|
|
27669
27701
|
return [];
|
|
27670
27702
|
}
|
|
@@ -28003,7 +28035,7 @@ function validateStory(raw, index, allIds) {
|
|
|
28003
28035
|
routing: {
|
|
28004
28036
|
complexity,
|
|
28005
28037
|
testStrategy,
|
|
28006
|
-
reasoning: "validated from LLM output",
|
|
28038
|
+
reasoning: typeof routing.reasoning === "string" && routing.reasoning.trim().length > 0 ? routing.reasoning.trim() : "validated from LLM output",
|
|
28007
28039
|
...noTestJustification !== undefined ? { noTestJustification } : {}
|
|
28008
28040
|
},
|
|
28009
28041
|
...workdir !== undefined ? { workdir } : {},
|
|
@@ -28554,7 +28586,7 @@ var init_orchestrator_factory = __esm(() => {
|
|
|
28554
28586
|
});
|
|
28555
28587
|
|
|
28556
28588
|
// src/context/engine/providers/plugin-loader.ts
|
|
28557
|
-
import { isAbsolute as isAbsolute5, join as
|
|
28589
|
+
import { isAbsolute as isAbsolute5, join as join14, resolve as resolve7 } from "path";
|
|
28558
28590
|
function isInitialisable(p) {
|
|
28559
28591
|
return typeof p.init === "function";
|
|
28560
28592
|
}
|
|
@@ -28582,7 +28614,7 @@ function resolveModuleSpecifier(specifier, workdir) {
|
|
|
28582
28614
|
}
|
|
28583
28615
|
if (specifier.startsWith("./") || specifier.startsWith("../")) {
|
|
28584
28616
|
const resolvedWorkdir = resolve7(workdir);
|
|
28585
|
-
const resolved = resolve7(
|
|
28617
|
+
const resolved = resolve7(join14(workdir, specifier));
|
|
28586
28618
|
if (resolved !== resolvedWorkdir && !resolved.startsWith(`${resolvedWorkdir}/`)) {
|
|
28587
28619
|
throw new Error(`Plugin module path escapes project workdir: "${specifier}" resolves to "${resolved}" (workdir: "${resolvedWorkdir}")`);
|
|
28588
28620
|
}
|
|
@@ -28728,15 +28760,15 @@ var init_available_budget = __esm(() => {
|
|
|
28728
28760
|
|
|
28729
28761
|
// src/context/engine/manifest-store.ts
|
|
28730
28762
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
28731
|
-
import { dirname as dirname5, isAbsolute as isAbsolute6, join as
|
|
28763
|
+
import { dirname as dirname5, isAbsolute as isAbsolute6, join as join15, relative as relative7, resolve as resolve8 } from "path";
|
|
28732
28764
|
function contextStoryDir(projectDir, featureId, storyId) {
|
|
28733
|
-
return
|
|
28765
|
+
return join15(projectDir, ".nax", "features", featureId, "stories", storyId);
|
|
28734
28766
|
}
|
|
28735
28767
|
function contextManifestPath(projectDir, featureId, storyId, stage) {
|
|
28736
|
-
return
|
|
28768
|
+
return join15(contextStoryDir(projectDir, featureId, storyId), `context-manifest-${stage}.json`);
|
|
28737
28769
|
}
|
|
28738
28770
|
function rebuildManifestPath(projectDir, featureId, storyId) {
|
|
28739
|
-
return
|
|
28771
|
+
return join15(contextStoryDir(projectDir, featureId, storyId), "rebuild-manifest.json");
|
|
28740
28772
|
}
|
|
28741
28773
|
function toStoredPath(projectDir, pathValue) {
|
|
28742
28774
|
const relativePath = isAbsolute6(pathValue) ? relative7(projectDir, pathValue) : pathValue;
|
|
@@ -28792,7 +28824,7 @@ async function loadContextManifests(projectDir, storyId, featureId) {
|
|
|
28792
28824
|
const storyDir = contextStoryDir(projectDir, feature, storyId);
|
|
28793
28825
|
const manifestFiles = await _manifestStoreDeps.listManifestFiles(storyDir);
|
|
28794
28826
|
for (const fileName of manifestFiles) {
|
|
28795
|
-
const fullPath =
|
|
28827
|
+
const fullPath = join15(storyDir, fileName);
|
|
28796
28828
|
if (!await _manifestStoreDeps.fileExists(fullPath))
|
|
28797
28829
|
continue;
|
|
28798
28830
|
try {
|
|
@@ -28817,7 +28849,7 @@ var init_manifest_store = __esm(() => {
|
|
|
28817
28849
|
fileExists: (path2) => Bun.file(path2).exists(),
|
|
28818
28850
|
readFile: (path2) => Bun.file(path2).text(),
|
|
28819
28851
|
listFeatureDirs: async (projectDir) => {
|
|
28820
|
-
const baseDir =
|
|
28852
|
+
const baseDir = join15(projectDir, ".nax", "features");
|
|
28821
28853
|
try {
|
|
28822
28854
|
const dirs = [];
|
|
28823
28855
|
for await (const entry of new Bun.Glob("*").scan({ cwd: baseDir, absolute: false })) {
|
|
@@ -28844,7 +28876,7 @@ var init_manifest_store = __esm(() => {
|
|
|
28844
28876
|
|
|
28845
28877
|
// src/context/engine/stage-assembler.ts
|
|
28846
28878
|
import { readdir } from "fs/promises";
|
|
28847
|
-
import { isAbsolute as isAbsolute7, join as
|
|
28879
|
+
import { isAbsolute as isAbsolute7, join as join16, resolve as resolve9 } from "path";
|
|
28848
28880
|
function dedupeScratchDirs(dirs) {
|
|
28849
28881
|
return [...new Set(dirs.filter((dir) => Boolean(dir)))];
|
|
28850
28882
|
}
|
|
@@ -28853,7 +28885,7 @@ function toAbsolutePath2(projectDir, pathValue) {
|
|
|
28853
28885
|
}
|
|
28854
28886
|
async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId, ttlMs) {
|
|
28855
28887
|
const logger = getLogger();
|
|
28856
|
-
const sessionsRoot =
|
|
28888
|
+
const sessionsRoot = join16(projectDir, ".nax", "features", featureName, "sessions");
|
|
28857
28889
|
let entries;
|
|
28858
28890
|
try {
|
|
28859
28891
|
entries = await _stageAssemblerDeps.readdir(sessionsRoot);
|
|
@@ -28863,7 +28895,7 @@ async function discoverSessionScratchDirsOnDisk(projectDir, featureName, storyId
|
|
|
28863
28895
|
const cutoff = _stageAssemblerDeps.now() - ttlMs;
|
|
28864
28896
|
const found = [];
|
|
28865
28897
|
for (const entry of entries) {
|
|
28866
|
-
const descriptorPath =
|
|
28898
|
+
const descriptorPath = join16(sessionsRoot, entry, "descriptor.json");
|
|
28867
28899
|
try {
|
|
28868
28900
|
const parsed = await _stageAssemblerDeps.readDescriptor(descriptorPath);
|
|
28869
28901
|
if (!parsed || parsed.storyId !== storyId || !parsed.scratchDir)
|
|
@@ -30528,13 +30560,13 @@ var exports_loader = {};
|
|
|
30528
30560
|
__export(exports_loader, {
|
|
30529
30561
|
loadOverride: () => loadOverride
|
|
30530
30562
|
});
|
|
30531
|
-
import { join as
|
|
30563
|
+
import { join as join17 } from "path";
|
|
30532
30564
|
async function loadOverride(role, workdir, config2) {
|
|
30533
30565
|
const overridePath = config2.prompts?.overrides?.[role];
|
|
30534
30566
|
if (!overridePath) {
|
|
30535
30567
|
return null;
|
|
30536
30568
|
}
|
|
30537
|
-
const absolutePath =
|
|
30569
|
+
const absolutePath = join17(workdir, overridePath);
|
|
30538
30570
|
const file3 = Bun.file(absolutePath);
|
|
30539
30571
|
if (!await file3.exists()) {
|
|
30540
30572
|
return null;
|
|
@@ -33201,11 +33233,11 @@ var init_prepare_inputs = __esm(() => {
|
|
|
33201
33233
|
});
|
|
33202
33234
|
|
|
33203
33235
|
// src/utils/nax-project-root.ts
|
|
33204
|
-
import { dirname as dirname6, join as
|
|
33236
|
+
import { dirname as dirname6, join as join18, resolve as resolve10 } from "path";
|
|
33205
33237
|
async function findNaxProjectRoot(startDir) {
|
|
33206
33238
|
let dir = resolve10(startDir);
|
|
33207
33239
|
for (let depth = 0;depth < MAX_NAX_WALK_DEPTH; depth++) {
|
|
33208
|
-
if (await _naxProjectRootDeps.exists(
|
|
33240
|
+
if (await _naxProjectRootDeps.exists(join18(dir, ".nax", "config.json"))) {
|
|
33209
33241
|
return dir;
|
|
33210
33242
|
}
|
|
33211
33243
|
const parent = dirname6(dir);
|
|
@@ -33226,7 +33258,7 @@ var init_nax_project_root = __esm(() => {
|
|
|
33226
33258
|
|
|
33227
33259
|
// src/review/review-audit.ts
|
|
33228
33260
|
import { mkdir as mkdir3 } from "fs/promises";
|
|
33229
|
-
import { join as
|
|
33261
|
+
import { join as join19 } from "path";
|
|
33230
33262
|
function auditKey(reviewer, storyId) {
|
|
33231
33263
|
return `${reviewer}:${storyId ?? "_feature"}`;
|
|
33232
33264
|
}
|
|
@@ -33259,15 +33291,15 @@ function toPersistedEntry(entry, epochMs) {
|
|
|
33259
33291
|
async function persistReviewAudit(entry) {
|
|
33260
33292
|
let resolvedDir;
|
|
33261
33293
|
if (entry.outputDir) {
|
|
33262
|
-
resolvedDir =
|
|
33294
|
+
resolvedDir = join19(entry.outputDir, "review-audit", entry.featureName ?? "_unknown");
|
|
33263
33295
|
} else {
|
|
33264
33296
|
const projectRoot = entry.projectDir ?? await _reviewAuditDeps.findNaxProjectRoot(entry.workdir);
|
|
33265
|
-
resolvedDir =
|
|
33297
|
+
resolvedDir = join19(projectRoot, ".nax", "review-audit", entry.featureName ?? "_unknown");
|
|
33266
33298
|
}
|
|
33267
33299
|
await _reviewAuditDeps.mkdir(resolvedDir);
|
|
33268
33300
|
const epochMs = _reviewAuditDeps.now();
|
|
33269
33301
|
const filename = `${epochMs}-${entry.sessionName}.json`;
|
|
33270
|
-
await _reviewAuditDeps.writeFile(
|
|
33302
|
+
await _reviewAuditDeps.writeFile(join19(resolvedDir, filename), toPersistedEntry(entry, epochMs));
|
|
33271
33303
|
}
|
|
33272
33304
|
function createNoOpReviewAuditor() {
|
|
33273
33305
|
return {
|
|
@@ -33868,6 +33900,17 @@ function createDrainDeadline(deadlineMs) {
|
|
|
33868
33900
|
}
|
|
33869
33901
|
async function runQualityCommand(opts) {
|
|
33870
33902
|
const { commandName, command, workdir, storyId, timeoutMs = DEFAULT_TIMEOUT_MS, env: env2, stripEnvVars } = opts;
|
|
33903
|
+
if (!command || command.trim() === "") {
|
|
33904
|
+
return {
|
|
33905
|
+
commandName,
|
|
33906
|
+
command,
|
|
33907
|
+
success: false,
|
|
33908
|
+
exitCode: -1,
|
|
33909
|
+
output: `[nax] ${commandName} skipped: empty command`,
|
|
33910
|
+
durationMs: 0,
|
|
33911
|
+
timedOut: false
|
|
33912
|
+
};
|
|
33913
|
+
}
|
|
33871
33914
|
const startTime = Date.now();
|
|
33872
33915
|
const logger = getSafeLogger();
|
|
33873
33916
|
logger?.info("quality", `Running ${commandName}`, { storyId, commandName, command, workdir });
|
|
@@ -33982,7 +34025,7 @@ __export(exports_command_resolver, {
|
|
|
33982
34025
|
resolveQualityTestCommands: () => resolveQualityTestCommands,
|
|
33983
34026
|
_commandResolverDeps: () => _commandResolverDeps
|
|
33984
34027
|
});
|
|
33985
|
-
import { join as
|
|
34028
|
+
import { join as join20 } from "path";
|
|
33986
34029
|
async function resolveQualityTestCommands(config2, workdir, storyWorkdir) {
|
|
33987
34030
|
const rawTestCommand = config2.review?.commands?.test ?? config2.quality?.commands?.test;
|
|
33988
34031
|
const rawScopedTemplate = config2.quality?.commands?.testScoped;
|
|
@@ -34009,7 +34052,7 @@ var init_command_resolver = __esm(() => {
|
|
|
34009
34052
|
_commandResolverDeps = {
|
|
34010
34053
|
readPackageName: async (dir) => {
|
|
34011
34054
|
try {
|
|
34012
|
-
const content = await Bun.file(
|
|
34055
|
+
const content = await Bun.file(join20(dir, "package.json")).json();
|
|
34013
34056
|
return typeof content.name === "string" ? content.name : null;
|
|
34014
34057
|
} catch {
|
|
34015
34058
|
return null;
|
|
@@ -34458,7 +34501,7 @@ ${outputFormat}`, overridable: false }
|
|
|
34458
34501
|
});
|
|
34459
34502
|
|
|
34460
34503
|
// src/operations/plan-refine.ts
|
|
34461
|
-
import { join as
|
|
34504
|
+
import { join as join21 } from "path";
|
|
34462
34505
|
function hasToken(text, tokens) {
|
|
34463
34506
|
const lower = text.toLowerCase();
|
|
34464
34507
|
return tokens.some((token) => lower.includes(token));
|
|
@@ -34550,7 +34593,7 @@ async function normalizeStoryFiles(story, workdir, fileExists, upstreamProduced)
|
|
|
34550
34593
|
for (const entry of contextFiles) {
|
|
34551
34594
|
const filePath = typeof entry === "string" ? entry : entry.path;
|
|
34552
34595
|
const factId = typeof entry === "string" ? undefined : entry.factId;
|
|
34553
|
-
if (expected.has(filePath) || await fileExists(
|
|
34596
|
+
if (expected.has(filePath) || await fileExists(join21(workdir, filePath))) {
|
|
34554
34597
|
kept.push(entry);
|
|
34555
34598
|
continue;
|
|
34556
34599
|
}
|
|
@@ -35702,11 +35745,11 @@ function extractTestCode(output) {
|
|
|
35702
35745
|
|
|
35703
35746
|
// src/acceptance/generator.ts
|
|
35704
35747
|
import { existsSync as existsSync5 } from "fs";
|
|
35705
|
-
import { join as
|
|
35748
|
+
import { join as join22 } from "path";
|
|
35706
35749
|
function resolvePytestBin(packageDir) {
|
|
35707
35750
|
if (packageDir) {
|
|
35708
35751
|
for (const venvDir of [".venv", "venv", "env"]) {
|
|
35709
|
-
const candidate =
|
|
35752
|
+
const candidate = join22(packageDir, venvDir, "bin", "pytest");
|
|
35710
35753
|
if (existsSync5(candidate))
|
|
35711
35754
|
return candidate;
|
|
35712
35755
|
}
|
|
@@ -37292,6 +37335,7 @@ var init_write_test = __esm(() => {
|
|
|
37292
37335
|
stage: "run",
|
|
37293
37336
|
session: { role: "test-writer", lifetime: "warm" },
|
|
37294
37337
|
config: tddConfigSelector,
|
|
37338
|
+
model: (_input, ctx) => ctx.config.tdd?.sessionTiers?.testWriter,
|
|
37295
37339
|
keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "test-writer"),
|
|
37296
37340
|
build(input, _ctx) {
|
|
37297
37341
|
if (input.promptMarkdown?.trim()) {
|
|
@@ -37352,6 +37396,7 @@ var init_implement = __esm(() => {
|
|
|
37352
37396
|
stage: "run",
|
|
37353
37397
|
session: { role: "implementer", lifetime: "warm" },
|
|
37354
37398
|
config: tddConfigSelector,
|
|
37399
|
+
model: (input) => input.story.routing?.modelTier,
|
|
37355
37400
|
keepOpen: (_input, ctx) => shouldKeepSessionOpen(ctx.config, "implementer"),
|
|
37356
37401
|
build(input, _ctx) {
|
|
37357
37402
|
if (input.promptMarkdown?.trim()) {
|
|
@@ -37749,6 +37794,7 @@ var init_verify = __esm(() => {
|
|
|
37749
37794
|
stage: "verify",
|
|
37750
37795
|
session: { role: "verifier", lifetime: "fresh" },
|
|
37751
37796
|
config: tddConfigSelector,
|
|
37797
|
+
model: (_input, ctx) => ctx.config.tdd?.sessionTiers?.verifier,
|
|
37752
37798
|
retry: makeParseRetryStrategy({
|
|
37753
37799
|
validate: (parsed) => {
|
|
37754
37800
|
if (!parsed || typeof parsed !== "object")
|
|
@@ -38315,14 +38361,14 @@ var init_plan_critic_llm = __esm(() => {
|
|
|
38315
38361
|
|
|
38316
38362
|
// src/context/greenfield.ts
|
|
38317
38363
|
import { readdir as readdir2 } from "fs/promises";
|
|
38318
|
-
import { join as
|
|
38364
|
+
import { join as join23 } from "path";
|
|
38319
38365
|
async function scanForTestFiles(dir, testPatterns, isRootCall = true) {
|
|
38320
38366
|
const results = [];
|
|
38321
38367
|
const ignoreDirs = new Set(["node_modules", "dist", "build", ".next", ".git"]);
|
|
38322
38368
|
try {
|
|
38323
38369
|
const entries = await readdir2(dir, { withFileTypes: true });
|
|
38324
38370
|
for (const entry of entries) {
|
|
38325
|
-
const fullPath =
|
|
38371
|
+
const fullPath = join23(dir, entry.name);
|
|
38326
38372
|
if (entry.isDirectory()) {
|
|
38327
38373
|
if (ignoreDirs.has(entry.name))
|
|
38328
38374
|
continue;
|
|
@@ -38527,13 +38573,13 @@ __export(exports_runners, {
|
|
|
38527
38573
|
_regressionRunnerDeps: () => _regressionRunnerDeps
|
|
38528
38574
|
});
|
|
38529
38575
|
import { existsSync as existsSync6 } from "fs";
|
|
38530
|
-
import { join as
|
|
38576
|
+
import { join as join24 } from "path";
|
|
38531
38577
|
async function verifyAssets(workingDirectory, expectedFiles) {
|
|
38532
38578
|
if (!expectedFiles || expectedFiles.length === 0)
|
|
38533
38579
|
return { success: true, missingFiles: [] };
|
|
38534
38580
|
const missingFiles = [];
|
|
38535
38581
|
for (const file3 of expectedFiles) {
|
|
38536
|
-
if (!existsSync6(
|
|
38582
|
+
if (!existsSync6(join24(workingDirectory, file3)))
|
|
38537
38583
|
missingFiles.push(file3);
|
|
38538
38584
|
}
|
|
38539
38585
|
if (missingFiles.length > 0) {
|
|
@@ -38659,7 +38705,7 @@ var init_full_suite_gate = __esm(() => {
|
|
|
38659
38705
|
_fullSuiteGateDeps = {
|
|
38660
38706
|
resolveGateContext: async (input, ctx) => {
|
|
38661
38707
|
const { resolveQualityTestCommands: resolveQualityTestCommands2 } = await Promise.resolve().then(() => (init_command_resolver(), exports_command_resolver));
|
|
38662
|
-
const config2 = ctx.
|
|
38708
|
+
const config2 = ctx.packageView.config;
|
|
38663
38709
|
const fullSuiteTimeout = config2.execution?.regressionGate?.timeoutSeconds ?? config2.execution?.rectification?.fullSuiteTimeoutSeconds ?? 300;
|
|
38664
38710
|
const { testCommand: resolvedTestCmd } = await resolveQualityTestCommands2(config2, input.workdir, input.story.workdir);
|
|
38665
38711
|
if (!resolvedTestCmd) {
|
|
@@ -38671,13 +38717,14 @@ var init_full_suite_gate = __esm(() => {
|
|
|
38671
38717
|
workdir: input.workdir
|
|
38672
38718
|
});
|
|
38673
38719
|
}
|
|
38674
|
-
|
|
38720
|
+
const cmdWorkdir = ctx.packageView.hasOverride ? input.workdir : ctx.packageView.repoRoot;
|
|
38721
|
+
return { config: config2, testCmd: resolvedTestCmd, fullSuiteTimeout, cmdWorkdir };
|
|
38675
38722
|
},
|
|
38676
|
-
runTests: async (
|
|
38723
|
+
runTests: async (_input, gateCtx) => {
|
|
38677
38724
|
const { regression: regression2 } = await Promise.resolve().then(() => (init_runners(), exports_runners));
|
|
38678
38725
|
const { parseTestOutput: parseTestOutput2 } = await Promise.resolve().then(() => (init_test_runners(), exports_test_runners));
|
|
38679
38726
|
const result = await regression2({
|
|
38680
|
-
workdir:
|
|
38727
|
+
workdir: gateCtx.cmdWorkdir,
|
|
38681
38728
|
command: gateCtx.testCmd,
|
|
38682
38729
|
timeoutSeconds: gateCtx.fullSuiteTimeout,
|
|
38683
38730
|
acceptOnTimeout: false,
|
|
@@ -38708,7 +38755,7 @@ var init_full_suite_gate = __esm(() => {
|
|
|
38708
38755
|
config: fullSuiteGateConfigSelector,
|
|
38709
38756
|
async execute(input, ctx, deps = _fullSuiteGateDeps) {
|
|
38710
38757
|
const logger = getLogger();
|
|
38711
|
-
const ctxConfig = ctx.config;
|
|
38758
|
+
const ctxConfig = ctx.packageView.config;
|
|
38712
38759
|
const enabled = ctxConfig?.execution?.regressionGate?.enabled ?? true;
|
|
38713
38760
|
if (!enabled) {
|
|
38714
38761
|
logger.info("verify[regression]", "Regression gate disabled \u2014 skipping full-suite run", {
|
|
@@ -38727,7 +38774,7 @@ var init_full_suite_gate = __esm(() => {
|
|
|
38727
38774
|
logger.info("verify[regression]", "Running full-suite gate", {
|
|
38728
38775
|
storyId: input.story.id,
|
|
38729
38776
|
packageDir: input.story.workdir,
|
|
38730
|
-
cwd:
|
|
38777
|
+
cwd: gateCtx.cmdWorkdir,
|
|
38731
38778
|
command: gateCtx.testCmd,
|
|
38732
38779
|
timeoutSeconds: gateCtx.fullSuiteTimeout
|
|
38733
38780
|
});
|
|
@@ -38979,7 +39026,7 @@ var init_apply_test_edit_declarations = __esm(() => {
|
|
|
38979
39026
|
});
|
|
38980
39027
|
|
|
38981
39028
|
// src/operations/validate-mock-structure-files.ts
|
|
38982
|
-
import { join as
|
|
39029
|
+
import { join as join25 } from "path";
|
|
38983
39030
|
async function validateMockStructureFiles(declarations, resolvedTestPatterns, packageDir, deps) {
|
|
38984
39031
|
const fileExists = deps?.fileExists ?? defaultFileExists;
|
|
38985
39032
|
const valid = [];
|
|
@@ -38992,7 +39039,7 @@ async function validateMockStructureFiles(declarations, resolvedTestPatterns, pa
|
|
|
38992
39039
|
const files = d.files ?? [d.file];
|
|
38993
39040
|
let allValid = true;
|
|
38994
39041
|
for (const file3 of files) {
|
|
38995
|
-
const absolutePath =
|
|
39042
|
+
const absolutePath = join25(packageDir, file3);
|
|
38996
39043
|
const exists = await fileExists(absolutePath);
|
|
38997
39044
|
if (!exists) {
|
|
38998
39045
|
allValid = false;
|
|
@@ -39214,9 +39261,9 @@ var init_mechanical_lintfix_strategy = __esm(() => {
|
|
|
39214
39261
|
stage: "rectification",
|
|
39215
39262
|
config: qualityConfigSelector,
|
|
39216
39263
|
async execute(input, ctx, deps = _mechanicalLintFixDeps) {
|
|
39217
|
-
const
|
|
39218
|
-
const broad =
|
|
39219
|
-
const scoped2 =
|
|
39264
|
+
const quality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
39265
|
+
const broad = quality?.commands?.lintFix;
|
|
39266
|
+
const scoped2 = quality?.commands?.lintFixScoped;
|
|
39220
39267
|
const command = buildCommand(broad, scoped2, input.scopeFiles);
|
|
39221
39268
|
if (!command)
|
|
39222
39269
|
return { applied: true, exitCode: 0 };
|
|
@@ -39225,7 +39272,7 @@ var init_mechanical_lintfix_strategy = __esm(() => {
|
|
|
39225
39272
|
command,
|
|
39226
39273
|
workdir: input.workdir,
|
|
39227
39274
|
storyId: input.storyId,
|
|
39228
|
-
stripEnvVars:
|
|
39275
|
+
stripEnvVars: quality?.stripEnvVars ?? []
|
|
39229
39276
|
});
|
|
39230
39277
|
return { applied: true, exitCode: result.exitCode };
|
|
39231
39278
|
}
|
|
@@ -39273,9 +39320,9 @@ var init_mechanical_formatfix_strategy = __esm(() => {
|
|
|
39273
39320
|
stage: "rectification",
|
|
39274
39321
|
config: qualityConfigSelector,
|
|
39275
39322
|
async execute(input, ctx, deps = _mechanicalFormatFixDeps) {
|
|
39276
|
-
const
|
|
39277
|
-
const broad =
|
|
39278
|
-
const scoped2 =
|
|
39323
|
+
const quality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
39324
|
+
const broad = quality?.commands?.formatFix;
|
|
39325
|
+
const scoped2 = quality?.commands?.formatFixScoped;
|
|
39279
39326
|
const command = buildCommand2(broad, scoped2, input.scopeFiles);
|
|
39280
39327
|
if (!command)
|
|
39281
39328
|
return { applied: true, exitCode: 0 };
|
|
@@ -39284,7 +39331,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
|
|
|
39284
39331
|
command,
|
|
39285
39332
|
workdir: input.workdir,
|
|
39286
39333
|
storyId: input.storyId,
|
|
39287
|
-
stripEnvVars:
|
|
39334
|
+
stripEnvVars: quality?.stripEnvVars ?? []
|
|
39288
39335
|
});
|
|
39289
39336
|
return { applied: true, exitCode: result.exitCode };
|
|
39290
39337
|
}
|
|
@@ -39295,6 +39342,7 @@ var init_mechanical_formatfix_strategy = __esm(() => {
|
|
|
39295
39342
|
var _lintCheckDeps, lintCheckOp;
|
|
39296
39343
|
var init_lint_check = __esm(() => {
|
|
39297
39344
|
init_config();
|
|
39345
|
+
init_logger2();
|
|
39298
39346
|
init_runner();
|
|
39299
39347
|
init_lint_parsing();
|
|
39300
39348
|
_lintCheckDeps = {
|
|
@@ -39307,21 +39355,26 @@ var init_lint_check = __esm(() => {
|
|
|
39307
39355
|
stage: "review",
|
|
39308
39356
|
config: qualityConfigSelector,
|
|
39309
39357
|
async execute(input, ctx, deps = _lintCheckDeps) {
|
|
39310
|
-
const
|
|
39311
|
-
const command =
|
|
39312
|
-
if (
|
|
39313
|
-
|
|
39358
|
+
const quality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
39359
|
+
const command = quality?.commands?.lint;
|
|
39360
|
+
if (!command) {
|
|
39361
|
+
getSafeLogger()?.warn("quality", "No lint command configured \u2014 skipping lint gate", {
|
|
39362
|
+
storyId: input.storyId,
|
|
39363
|
+
packageDir: ctx.packageView.packageDir
|
|
39364
|
+
});
|
|
39365
|
+
return { success: true, status: "skipped", findings: [], durationMs: 0 };
|
|
39314
39366
|
}
|
|
39367
|
+
const cmdWorkdir = ctx.packageView.hasOverride ? input.workdir : ctx.packageView.repoRoot;
|
|
39315
39368
|
const start = Date.now();
|
|
39316
39369
|
const result = await deps.runQualityCommand({
|
|
39317
39370
|
commandName: "lint",
|
|
39318
|
-
command
|
|
39319
|
-
workdir:
|
|
39371
|
+
command,
|
|
39372
|
+
workdir: cmdWorkdir,
|
|
39320
39373
|
storyId: input.storyId,
|
|
39321
|
-
stripEnvVars:
|
|
39374
|
+
stripEnvVars: quality?.stripEnvVars ?? []
|
|
39322
39375
|
});
|
|
39323
39376
|
if (result.exitCode === 0) {
|
|
39324
|
-
return { success: true, findings: [], durationMs: Date.now() - start };
|
|
39377
|
+
return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
|
|
39325
39378
|
}
|
|
39326
39379
|
const parsed = deps.parseLintOutput(result.output, "auto", { workdir: input.workdir });
|
|
39327
39380
|
return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
|
|
@@ -39545,6 +39598,7 @@ var init_typecheck_parsing = __esm(() => {
|
|
|
39545
39598
|
var _typecheckCheckDeps, typecheckCheckOp;
|
|
39546
39599
|
var init_typecheck_check = __esm(() => {
|
|
39547
39600
|
init_config();
|
|
39601
|
+
init_logger2();
|
|
39548
39602
|
init_runner();
|
|
39549
39603
|
init_typecheck_parsing();
|
|
39550
39604
|
_typecheckCheckDeps = {
|
|
@@ -39557,21 +39611,26 @@ var init_typecheck_check = __esm(() => {
|
|
|
39557
39611
|
stage: "review",
|
|
39558
39612
|
config: qualityConfigSelector,
|
|
39559
39613
|
async execute(input, ctx, deps = _typecheckCheckDeps) {
|
|
39560
|
-
const
|
|
39561
|
-
const command =
|
|
39562
|
-
if (
|
|
39563
|
-
|
|
39614
|
+
const quality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
39615
|
+
const command = quality?.commands?.typecheck;
|
|
39616
|
+
if (!command) {
|
|
39617
|
+
getSafeLogger()?.warn("quality", "No typecheck command configured \u2014 skipping typecheck gate", {
|
|
39618
|
+
storyId: input.storyId,
|
|
39619
|
+
packageDir: ctx.packageView.packageDir
|
|
39620
|
+
});
|
|
39621
|
+
return { success: true, status: "skipped", findings: [], durationMs: 0 };
|
|
39564
39622
|
}
|
|
39623
|
+
const cmdWorkdir = ctx.packageView.hasOverride ? input.workdir : ctx.packageView.repoRoot;
|
|
39565
39624
|
const start = Date.now();
|
|
39566
39625
|
const result = await deps.runQualityCommand({
|
|
39567
39626
|
commandName: "typecheck",
|
|
39568
|
-
command
|
|
39569
|
-
workdir:
|
|
39627
|
+
command,
|
|
39628
|
+
workdir: cmdWorkdir,
|
|
39570
39629
|
storyId: input.storyId,
|
|
39571
|
-
stripEnvVars:
|
|
39630
|
+
stripEnvVars: quality?.stripEnvVars ?? []
|
|
39572
39631
|
});
|
|
39573
39632
|
if (result.exitCode === 0) {
|
|
39574
|
-
return { success: true, findings: [], durationMs: Date.now() - start };
|
|
39633
|
+
return { success: true, status: "passed", findings: [], durationMs: Date.now() - start };
|
|
39575
39634
|
}
|
|
39576
39635
|
const parsed = deps.parseTypecheckOutput(result.output, "auto", { workdir: input.workdir });
|
|
39577
39636
|
return { success: false, findings: parsed?.findings ?? [], durationMs: Date.now() - start };
|
|
@@ -39600,12 +39659,16 @@ var init_verify_scoped = __esm(() => {
|
|
|
39600
39659
|
config: qualityConfigSelector,
|
|
39601
39660
|
async execute(input, ctx, deps = _verifyScopedDeps) {
|
|
39602
39661
|
const logger = getLogger();
|
|
39603
|
-
const
|
|
39604
|
-
const baseCommand =
|
|
39605
|
-
if (!
|
|
39662
|
+
const quality = ctx.packageView.select(qualityConfigSelector);
|
|
39663
|
+
const baseCommand = quality.quality?.commands?.test;
|
|
39664
|
+
if (!baseCommand) {
|
|
39665
|
+
logger.warn("quality", "No test command configured \u2014 skipping scoped verify", {
|
|
39666
|
+
storyId: input.storyId,
|
|
39667
|
+
packageDir: ctx.packageView.packageDir
|
|
39668
|
+
});
|
|
39606
39669
|
return {
|
|
39607
39670
|
success: true,
|
|
39608
|
-
status: "
|
|
39671
|
+
status: "skipped",
|
|
39609
39672
|
findings: [],
|
|
39610
39673
|
durationMs: 0,
|
|
39611
39674
|
passCount: 0,
|
|
@@ -39617,11 +39680,11 @@ var init_verify_scoped = __esm(() => {
|
|
|
39617
39680
|
workdir: input.workdir,
|
|
39618
39681
|
storyId: input.storyId,
|
|
39619
39682
|
storyGitRef: input.storyGitRef,
|
|
39620
|
-
testCommand: baseCommand
|
|
39621
|
-
testScopedTemplate:
|
|
39622
|
-
smartRunnerConfig:
|
|
39623
|
-
scopeTestThreshold:
|
|
39624
|
-
fallbackFullSuiteCommand:
|
|
39683
|
+
testCommand: baseCommand,
|
|
39684
|
+
testScopedTemplate: quality.quality?.commands?.testScoped,
|
|
39685
|
+
smartRunnerConfig: quality.execution?.smartTestRunner,
|
|
39686
|
+
scopeTestThreshold: quality.quality?.scopeTestThreshold,
|
|
39687
|
+
fallbackFullSuiteCommand: quality.quality?.commands?.test,
|
|
39625
39688
|
naxIgnoreIndex: input.naxIgnoreIndex
|
|
39626
39689
|
});
|
|
39627
39690
|
if (selection.isFullSuite && regressionMode === "deferred" && !selection.isMonorepoOrchestrator && !selection.thresholdFallback) {
|
|
@@ -39648,27 +39711,28 @@ var init_verify_scoped = __esm(() => {
|
|
|
39648
39711
|
command: selection.effectiveCommand
|
|
39649
39712
|
});
|
|
39650
39713
|
}
|
|
39651
|
-
const scopedTimeout =
|
|
39714
|
+
const scopedTimeout = quality.execution?.regressionGate?.timeoutSeconds ?? 600;
|
|
39715
|
+
const cmdWorkdir = ctx.packageView.hasOverride ? input.workdir : ctx.packageView.repoRoot;
|
|
39652
39716
|
logger.info("verify[scoped]", "Running scoped tests", {
|
|
39653
39717
|
storyId: input.storyId,
|
|
39654
39718
|
packageDir: input.packageDir,
|
|
39655
|
-
cwd:
|
|
39719
|
+
cwd: cmdWorkdir,
|
|
39656
39720
|
command: selection.effectiveCommand,
|
|
39657
39721
|
timeoutSeconds: scopedTimeout,
|
|
39658
39722
|
isFullSuite: selection.isFullSuite
|
|
39659
39723
|
});
|
|
39660
39724
|
const start = Date.now();
|
|
39661
39725
|
const result = await deps.regression({
|
|
39662
|
-
workdir:
|
|
39726
|
+
workdir: cmdWorkdir,
|
|
39663
39727
|
command: selection.effectiveCommand,
|
|
39664
|
-
timeoutSeconds:
|
|
39665
|
-
forceExit:
|
|
39666
|
-
detectOpenHandles:
|
|
39667
|
-
detectOpenHandlesRetries:
|
|
39668
|
-
gracePeriodMs:
|
|
39669
|
-
drainTimeoutMs:
|
|
39670
|
-
shell:
|
|
39671
|
-
stripEnvVars:
|
|
39728
|
+
timeoutSeconds: quality.execution?.regressionGate?.timeoutSeconds ?? 600,
|
|
39729
|
+
forceExit: quality.quality?.forceExit,
|
|
39730
|
+
detectOpenHandles: quality.quality?.detectOpenHandles,
|
|
39731
|
+
detectOpenHandlesRetries: quality.quality?.detectOpenHandlesRetries,
|
|
39732
|
+
gracePeriodMs: quality.quality?.gracePeriodMs,
|
|
39733
|
+
drainTimeoutMs: quality.quality?.drainTimeoutMs,
|
|
39734
|
+
shell: quality.quality?.shell,
|
|
39735
|
+
stripEnvVars: quality.quality?.stripEnvVars
|
|
39672
39736
|
});
|
|
39673
39737
|
const durationMs = Date.now() - start;
|
|
39674
39738
|
const parsed = result.output ? deps.parseTestOutput(result.output) : { passed: 0, failed: 0, failures: [] };
|
|
@@ -40440,7 +40504,7 @@ var init_lint_parsing = __esm(() => {
|
|
|
40440
40504
|
});
|
|
40441
40505
|
|
|
40442
40506
|
// src/review/scoped-lint.ts
|
|
40443
|
-
import { join as
|
|
40507
|
+
import { join as join26, relative as relative10 } from "path";
|
|
40444
40508
|
function shellQuotePath4(path5) {
|
|
40445
40509
|
return `'${path5.replaceAll("'", "'\\''")}'`;
|
|
40446
40510
|
}
|
|
@@ -40488,7 +40552,7 @@ function uniqueFiles(files) {
|
|
|
40488
40552
|
async function filterFilesToScope(files, workdir, projectDir, activePackageDir) {
|
|
40489
40553
|
const inScope = [];
|
|
40490
40554
|
for (const relPath of files) {
|
|
40491
|
-
const absPath =
|
|
40555
|
+
const absPath = join26(workdir, relPath);
|
|
40492
40556
|
const exists = await _scopedLintDeps.fileExists(absPath);
|
|
40493
40557
|
if (!exists)
|
|
40494
40558
|
continue;
|
|
@@ -43862,7 +43926,7 @@ var init_call = __esm(() => {
|
|
|
43862
43926
|
|
|
43863
43927
|
// src/runtime/cost-aggregator.ts
|
|
43864
43928
|
import { mkdirSync as mkdirSync2 } from "fs";
|
|
43865
|
-
import { join as
|
|
43929
|
+
import { join as join27 } from "path";
|
|
43866
43930
|
function makeCorrelationId() {
|
|
43867
43931
|
return `${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 8)}`;
|
|
43868
43932
|
}
|
|
@@ -44053,7 +44117,7 @@ class CostAggregator {
|
|
|
44053
44117
|
if (events.length === 0 && errors3.length === 0)
|
|
44054
44118
|
return;
|
|
44055
44119
|
mkdirSync2(this._drainDir, { recursive: true });
|
|
44056
|
-
const path5 =
|
|
44120
|
+
const path5 = join27(this._drainDir, `${this._runId}.jsonl`);
|
|
44057
44121
|
const sorted = [...events, ...errors3].sort((a, b) => a.ts - b.ts);
|
|
44058
44122
|
await _costAggDeps.write(path5, `${sorted.map((e) => JSON.stringify(e)).join(`
|
|
44059
44123
|
`)}
|
|
@@ -44093,7 +44157,7 @@ var init_cost_aggregator = __esm(() => {
|
|
|
44093
44157
|
// src/runtime/prompt-auditor.ts
|
|
44094
44158
|
import { appendFileSync } from "fs";
|
|
44095
44159
|
import { mkdir as mkdir4 } from "fs/promises";
|
|
44096
|
-
import { join as
|
|
44160
|
+
import { join as join28 } from "path";
|
|
44097
44161
|
function createNoOpPromptAuditor() {
|
|
44098
44162
|
return {
|
|
44099
44163
|
record() {},
|
|
@@ -44159,8 +44223,8 @@ class PromptAuditor {
|
|
|
44159
44223
|
_jsonlPath;
|
|
44160
44224
|
_featureDir;
|
|
44161
44225
|
constructor(runId, flushDir, featureName) {
|
|
44162
|
-
this._featureDir =
|
|
44163
|
-
this._jsonlPath =
|
|
44226
|
+
this._featureDir = join28(flushDir, featureName);
|
|
44227
|
+
this._jsonlPath = join28(this._featureDir, `${runId}.jsonl`);
|
|
44164
44228
|
}
|
|
44165
44229
|
record(entry) {
|
|
44166
44230
|
this._enqueue(entry);
|
|
@@ -44209,7 +44273,7 @@ class PromptAuditor {
|
|
|
44209
44273
|
const auditEntry = entry;
|
|
44210
44274
|
const filename = deriveTxtFilename(auditEntry);
|
|
44211
44275
|
try {
|
|
44212
|
-
await _promptAuditorDeps.write(
|
|
44276
|
+
await _promptAuditorDeps.write(join28(this._featureDir, filename), buildTxtContent(auditEntry));
|
|
44213
44277
|
} catch (err) {
|
|
44214
44278
|
throw tagAuditError(err, "txt");
|
|
44215
44279
|
}
|
|
@@ -44325,12 +44389,14 @@ var init_paths2 = __esm(() => {
|
|
|
44325
44389
|
});
|
|
44326
44390
|
|
|
44327
44391
|
// src/runtime/packages.ts
|
|
44328
|
-
function createPackageView(config2, packageDir, repoRoot) {
|
|
44392
|
+
function createPackageView(config2, packageDir, repoRoot, hasOverride) {
|
|
44329
44393
|
const memo = new Map;
|
|
44330
44394
|
const relativeFromRoot = packageDir ? packageDir.startsWith(repoRoot) ? packageDir.slice(repoRoot.length).replace(/^\//, "") : packageDir : "";
|
|
44331
44395
|
return {
|
|
44332
44396
|
packageDir,
|
|
44333
44397
|
relativeFromRoot,
|
|
44398
|
+
repoRoot,
|
|
44399
|
+
hasOverride,
|
|
44334
44400
|
config: config2,
|
|
44335
44401
|
select(selector) {
|
|
44336
44402
|
if (memo.has(selector.name)) {
|
|
@@ -44344,17 +44410,46 @@ function createPackageView(config2, packageDir, repoRoot) {
|
|
|
44344
44410
|
}
|
|
44345
44411
|
function createPackageRegistry(loader, repoRoot) {
|
|
44346
44412
|
const cache = new Map;
|
|
44413
|
+
const mergedConfigs = new Map;
|
|
44414
|
+
function toRelativeKey(packageDir) {
|
|
44415
|
+
if (!packageDir)
|
|
44416
|
+
return "";
|
|
44417
|
+
const prefix = repoRoot.endsWith("/") ? repoRoot : `${repoRoot}/`;
|
|
44418
|
+
if (packageDir.startsWith(prefix))
|
|
44419
|
+
return packageDir.slice(prefix.length);
|
|
44420
|
+
if (packageDir === repoRoot)
|
|
44421
|
+
return "";
|
|
44422
|
+
return packageDir;
|
|
44423
|
+
}
|
|
44347
44424
|
function resolve12(packageDir) {
|
|
44348
|
-
const key = packageDir
|
|
44425
|
+
const key = toRelativeKey(packageDir);
|
|
44349
44426
|
const cached2 = cache.get(key);
|
|
44350
44427
|
if (cached2 !== undefined) {
|
|
44351
44428
|
return cached2;
|
|
44352
44429
|
}
|
|
44353
|
-
const
|
|
44354
|
-
const
|
|
44430
|
+
const overrideConfig = mergedConfigs.get(key);
|
|
44431
|
+
const hasOverride = overrideConfig !== undefined;
|
|
44432
|
+
const config2 = overrideConfig ?? loader.current();
|
|
44433
|
+
const view = createPackageView(config2, key, repoRoot, hasOverride);
|
|
44355
44434
|
cache.set(key, view);
|
|
44356
44435
|
return view;
|
|
44357
44436
|
}
|
|
44437
|
+
async function hydrate(packageDirs, loadOverride2) {
|
|
44438
|
+
const load = loadOverride2 ?? (await Promise.resolve().then(() => (init_config(), exports_config))).loadPackageOverride;
|
|
44439
|
+
for (const dir of packageDirs) {
|
|
44440
|
+
if (!dir) {
|
|
44441
|
+
continue;
|
|
44442
|
+
}
|
|
44443
|
+
if (mergedConfigs.has(dir)) {
|
|
44444
|
+
continue;
|
|
44445
|
+
}
|
|
44446
|
+
const override = await load(repoRoot, dir);
|
|
44447
|
+
if (override !== null) {
|
|
44448
|
+
mergedConfigs.set(dir, mergePackageConfig(loader.current(), override));
|
|
44449
|
+
cache.delete(dir);
|
|
44450
|
+
}
|
|
44451
|
+
}
|
|
44452
|
+
}
|
|
44358
44453
|
return {
|
|
44359
44454
|
all() {
|
|
44360
44455
|
return [...cache.values()];
|
|
@@ -44362,9 +44457,13 @@ function createPackageRegistry(loader, repoRoot) {
|
|
|
44362
44457
|
resolve: resolve12,
|
|
44363
44458
|
repo() {
|
|
44364
44459
|
return resolve12(undefined);
|
|
44365
|
-
}
|
|
44460
|
+
},
|
|
44461
|
+
hydrate
|
|
44366
44462
|
};
|
|
44367
44463
|
}
|
|
44464
|
+
var init_packages = __esm(() => {
|
|
44465
|
+
init_config();
|
|
44466
|
+
});
|
|
44368
44467
|
|
|
44369
44468
|
// src/runtime/agent-stream-events.ts
|
|
44370
44469
|
class AgentStreamEventBus {
|
|
@@ -45322,7 +45421,7 @@ var init_pid_registry = __esm(() => {
|
|
|
45322
45421
|
// src/session/manager-deps.ts
|
|
45323
45422
|
import { randomUUID as randomUUID3 } from "crypto";
|
|
45324
45423
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
45325
|
-
import { isAbsolute as isAbsolute9, join as
|
|
45424
|
+
import { isAbsolute as isAbsolute9, join as join29, relative as relative11, sep as sep2 } from "path";
|
|
45326
45425
|
function resolveProjectDirFromScratchDir(scratchDir) {
|
|
45327
45426
|
const marker = `${sep2}.nax${sep2}features${sep2}`;
|
|
45328
45427
|
const markerIdx = scratchDir.lastIndexOf(marker);
|
|
@@ -45343,7 +45442,7 @@ var init_manager_deps = __esm(() => {
|
|
|
45343
45442
|
now: () => new Date().toISOString(),
|
|
45344
45443
|
nowMs: () => Date.now(),
|
|
45345
45444
|
uuid: () => randomUUID3(),
|
|
45346
|
-
sessionScratchDir: (projectDir, featureName, sessionId) =>
|
|
45445
|
+
sessionScratchDir: (projectDir, featureName, sessionId) => join29(projectDir, ".nax", "features", featureName, "sessions", sessionId),
|
|
45347
45446
|
writeDescriptor: async (scratchDir, descriptor, projectDir) => {
|
|
45348
45447
|
await mkdir5(scratchDir, { recursive: true });
|
|
45349
45448
|
const { handle: _handle, ...persistable } = descriptor;
|
|
@@ -45354,7 +45453,7 @@ var init_manager_deps = __esm(() => {
|
|
|
45354
45453
|
persistable.scratchDir = toProjectRelativePath(derivedProjectDir, persistable.scratchDir);
|
|
45355
45454
|
}
|
|
45356
45455
|
}
|
|
45357
|
-
await Bun.write(
|
|
45456
|
+
await Bun.write(join29(scratchDir, "descriptor.json"), JSON.stringify(persistable, null, 2));
|
|
45358
45457
|
}
|
|
45359
45458
|
};
|
|
45360
45459
|
});
|
|
@@ -46105,7 +46204,7 @@ __export(exports_runtime, {
|
|
|
46105
46204
|
CostAggregator: () => CostAggregator,
|
|
46106
46205
|
AgentStreamEventBus: () => AgentStreamEventBus
|
|
46107
46206
|
});
|
|
46108
|
-
import { basename as basename5, join as
|
|
46207
|
+
import { basename as basename5, join as join30 } from "path";
|
|
46109
46208
|
function createRuntime(config2, workdir, opts) {
|
|
46110
46209
|
const runId = crypto.randomUUID();
|
|
46111
46210
|
const controller = new AbortController;
|
|
@@ -46121,10 +46220,10 @@ function createRuntime(config2, workdir, opts) {
|
|
|
46121
46220
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
46122
46221
|
const globalDir = globalOutputDir();
|
|
46123
46222
|
const curatorRollupPathValue = curatorRollupPath(globalDir, config2.curator?.rollupPath);
|
|
46124
|
-
const costDir =
|
|
46223
|
+
const costDir = join30(outputDir, "cost");
|
|
46125
46224
|
const costAggregator = opts?.costAggregator ?? new CostAggregator(runId, costDir);
|
|
46126
46225
|
const auditEnabled = config2.agent?.promptAudit?.enabled ?? false;
|
|
46127
|
-
const auditDir = config2.agent?.promptAudit?.dir ??
|
|
46226
|
+
const auditDir = config2.agent?.promptAudit?.dir ?? join30(outputDir, "prompt-audit");
|
|
46128
46227
|
let promptAuditor;
|
|
46129
46228
|
if (opts?.promptAuditor) {
|
|
46130
46229
|
promptAuditor = opts.promptAuditor;
|
|
@@ -46230,6 +46329,7 @@ var init_runtime = __esm(() => {
|
|
|
46230
46329
|
init_prompt_auditor();
|
|
46231
46330
|
init_review_audit();
|
|
46232
46331
|
init_paths2();
|
|
46332
|
+
init_packages();
|
|
46233
46333
|
init_dispatch_events();
|
|
46234
46334
|
init_agent_stream_events();
|
|
46235
46335
|
init_middleware();
|
|
@@ -46247,6 +46347,7 @@ var init_runtime = __esm(() => {
|
|
|
46247
46347
|
init_cost_aggregator();
|
|
46248
46348
|
init_dispatch_events();
|
|
46249
46349
|
init_middleware();
|
|
46350
|
+
init_packages();
|
|
46250
46351
|
init_paths2();
|
|
46251
46352
|
init_prompt_auditor();
|
|
46252
46353
|
init_session_run_hop();
|
|
@@ -46275,9 +46376,9 @@ async function allSettledBounded(tasks, limit) {
|
|
|
46275
46376
|
|
|
46276
46377
|
// src/context/injector.ts
|
|
46277
46378
|
import { existsSync as existsSync8 } from "fs";
|
|
46278
|
-
import { join as
|
|
46379
|
+
import { join as join31 } from "path";
|
|
46279
46380
|
async function detectNode(workdir) {
|
|
46280
|
-
const pkgPath =
|
|
46381
|
+
const pkgPath = join31(workdir, "package.json");
|
|
46281
46382
|
if (!existsSync8(pkgPath))
|
|
46282
46383
|
return null;
|
|
46283
46384
|
try {
|
|
@@ -46294,7 +46395,7 @@ async function detectNode(workdir) {
|
|
|
46294
46395
|
}
|
|
46295
46396
|
}
|
|
46296
46397
|
async function detectGo(workdir) {
|
|
46297
|
-
const goMod =
|
|
46398
|
+
const goMod = join31(workdir, "go.mod");
|
|
46298
46399
|
if (!existsSync8(goMod))
|
|
46299
46400
|
return null;
|
|
46300
46401
|
try {
|
|
@@ -46318,7 +46419,7 @@ async function detectGo(workdir) {
|
|
|
46318
46419
|
}
|
|
46319
46420
|
}
|
|
46320
46421
|
async function detectRust(workdir) {
|
|
46321
|
-
const cargoPath =
|
|
46422
|
+
const cargoPath = join31(workdir, "Cargo.toml");
|
|
46322
46423
|
if (!existsSync8(cargoPath))
|
|
46323
46424
|
return null;
|
|
46324
46425
|
try {
|
|
@@ -46334,8 +46435,8 @@ async function detectRust(workdir) {
|
|
|
46334
46435
|
}
|
|
46335
46436
|
}
|
|
46336
46437
|
async function detectPython(workdir) {
|
|
46337
|
-
const pyproject =
|
|
46338
|
-
const requirements =
|
|
46438
|
+
const pyproject = join31(workdir, "pyproject.toml");
|
|
46439
|
+
const requirements = join31(workdir, "requirements.txt");
|
|
46339
46440
|
if (!existsSync8(pyproject) && !existsSync8(requirements))
|
|
46340
46441
|
return null;
|
|
46341
46442
|
try {
|
|
@@ -46354,7 +46455,7 @@ async function detectPython(workdir) {
|
|
|
46354
46455
|
}
|
|
46355
46456
|
}
|
|
46356
46457
|
async function detectPhp(workdir) {
|
|
46357
|
-
const composerPath =
|
|
46458
|
+
const composerPath = join31(workdir, "composer.json");
|
|
46358
46459
|
if (!existsSync8(composerPath))
|
|
46359
46460
|
return null;
|
|
46360
46461
|
try {
|
|
@@ -46367,7 +46468,7 @@ async function detectPhp(workdir) {
|
|
|
46367
46468
|
}
|
|
46368
46469
|
}
|
|
46369
46470
|
async function detectRuby(workdir) {
|
|
46370
|
-
const gemfile =
|
|
46471
|
+
const gemfile = join31(workdir, "Gemfile");
|
|
46371
46472
|
if (!existsSync8(gemfile))
|
|
46372
46473
|
return null;
|
|
46373
46474
|
try {
|
|
@@ -46379,9 +46480,9 @@ async function detectRuby(workdir) {
|
|
|
46379
46480
|
}
|
|
46380
46481
|
}
|
|
46381
46482
|
async function detectJvm(workdir) {
|
|
46382
|
-
const pom =
|
|
46383
|
-
const gradle =
|
|
46384
|
-
const gradleKts =
|
|
46483
|
+
const pom = join31(workdir, "pom.xml");
|
|
46484
|
+
const gradle = join31(workdir, "build.gradle");
|
|
46485
|
+
const gradleKts = join31(workdir, "build.gradle.kts");
|
|
46385
46486
|
if (!existsSync8(pom) && !existsSync8(gradle) && !existsSync8(gradleKts))
|
|
46386
46487
|
return null;
|
|
46387
46488
|
try {
|
|
@@ -46389,7 +46490,7 @@ async function detectJvm(workdir) {
|
|
|
46389
46490
|
const content2 = await Bun.file(pom).text();
|
|
46390
46491
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
46391
46492
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
46392
|
-
const lang2 = existsSync8(
|
|
46493
|
+
const lang2 = existsSync8(join31(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
46393
46494
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
46394
46495
|
}
|
|
46395
46496
|
const gradleFile = existsSync8(gradleKts) ? gradleKts : gradle;
|
|
@@ -46643,7 +46744,7 @@ var init_windsurf = __esm(() => {
|
|
|
46643
46744
|
|
|
46644
46745
|
// src/context/generator.ts
|
|
46645
46746
|
import { existsSync as existsSync9 } from "fs";
|
|
46646
|
-
import { join as
|
|
46747
|
+
import { join as join32, relative as relative12 } from "path";
|
|
46647
46748
|
async function loadContextContent(options, config2) {
|
|
46648
46749
|
if (!_generatorDeps.existsSync(options.contextPath)) {
|
|
46649
46750
|
throw new Error(`Context file not found: ${options.contextPath}`);
|
|
@@ -46661,7 +46762,7 @@ async function generateFor(agent, options, config2) {
|
|
|
46661
46762
|
try {
|
|
46662
46763
|
const context = await loadContextContent(options, config2);
|
|
46663
46764
|
const content = generator.generate(context);
|
|
46664
|
-
const outputPath =
|
|
46765
|
+
const outputPath = join32(options.outputDir, generator.outputFile);
|
|
46665
46766
|
validateFilePath(outputPath, options.outputDir);
|
|
46666
46767
|
if (!options.dryRun) {
|
|
46667
46768
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -46679,7 +46780,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
46679
46780
|
for (const [agentKey, generator] of entries) {
|
|
46680
46781
|
try {
|
|
46681
46782
|
const content = generator.generate(context);
|
|
46682
|
-
const outputPath =
|
|
46783
|
+
const outputPath = join32(options.outputDir, generator.outputFile);
|
|
46683
46784
|
validateFilePath(outputPath, options.outputDir);
|
|
46684
46785
|
if (!options.dryRun) {
|
|
46685
46786
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -46699,7 +46800,7 @@ async function discoverPackages(repoRoot) {
|
|
|
46699
46800
|
const glob = new Bun.Glob(pattern);
|
|
46700
46801
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
46701
46802
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
46702
|
-
const pkgAbsolute =
|
|
46803
|
+
const pkgAbsolute = join32(repoRoot, pkgRelative);
|
|
46703
46804
|
if (!seen.has(pkgAbsolute)) {
|
|
46704
46805
|
seen.add(pkgAbsolute);
|
|
46705
46806
|
packages.push(pkgAbsolute);
|
|
@@ -46731,14 +46832,14 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
46731
46832
|
}
|
|
46732
46833
|
}
|
|
46733
46834
|
}
|
|
46734
|
-
const turboPath =
|
|
46835
|
+
const turboPath = join32(repoRoot, "turbo.json");
|
|
46735
46836
|
try {
|
|
46736
46837
|
const turbo = JSON.parse(await _generatorDeps.readTextFile(turboPath));
|
|
46737
46838
|
if (Array.isArray(turbo.packages)) {
|
|
46738
46839
|
await resolveGlobs(turbo.packages);
|
|
46739
46840
|
}
|
|
46740
46841
|
} catch {}
|
|
46741
|
-
const pkgPath =
|
|
46842
|
+
const pkgPath = join32(repoRoot, "package.json");
|
|
46742
46843
|
try {
|
|
46743
46844
|
const pkg = JSON.parse(await _generatorDeps.readTextFile(pkgPath));
|
|
46744
46845
|
const ws = pkg.workspaces;
|
|
@@ -46746,7 +46847,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
46746
46847
|
if (patterns.length > 0)
|
|
46747
46848
|
await resolveGlobs(patterns);
|
|
46748
46849
|
} catch {}
|
|
46749
|
-
const pnpmPath =
|
|
46850
|
+
const pnpmPath = join32(repoRoot, "pnpm-workspace.yaml");
|
|
46750
46851
|
try {
|
|
46751
46852
|
const raw = await _generatorDeps.readTextFile(pnpmPath);
|
|
46752
46853
|
const lines = raw.split(`
|
|
@@ -46772,7 +46873,7 @@ async function discoverWorkspacePackages2(repoRoot) {
|
|
|
46772
46873
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
46773
46874
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
46774
46875
|
const relativePkgPath = relative12(resolvedRepoRoot, packageDir);
|
|
46775
|
-
const contextPath =
|
|
46876
|
+
const contextPath = join32(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
46776
46877
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
46777
46878
|
return [
|
|
46778
46879
|
{
|
|
@@ -46840,7 +46941,7 @@ var init_generator2 = __esm(() => {
|
|
|
46840
46941
|
});
|
|
46841
46942
|
|
|
46842
46943
|
// src/analyze/scanner.ts
|
|
46843
|
-
import { join as
|
|
46944
|
+
import { join as join33 } from "path";
|
|
46844
46945
|
function resolveFrameworkAndRunner(language, pkg) {
|
|
46845
46946
|
if (language === "go")
|
|
46846
46947
|
return { framework: "", testRunner: "go-test" };
|
|
@@ -46862,7 +46963,7 @@ async function scanSourceRoots(workdir) {
|
|
|
46862
46963
|
});
|
|
46863
46964
|
try {
|
|
46864
46965
|
const language = await deps.detectLanguage(workdir);
|
|
46865
|
-
const pkg = await deps.readPackageJson(
|
|
46966
|
+
const pkg = await deps.readPackageJson(join33(workdir, "package.json"));
|
|
46866
46967
|
const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
|
|
46867
46968
|
return [{ path: ".", language, framework, testRunner }];
|
|
46868
46969
|
} catch {
|
|
@@ -46880,9 +46981,9 @@ async function scanSourceRoots(workdir) {
|
|
|
46880
46981
|
packages = packages.slice(0, MAX_SOURCE_ROOTS);
|
|
46881
46982
|
}
|
|
46882
46983
|
return Promise.all(packages.map(async (pkgPath) => {
|
|
46883
|
-
const pkgDir = pkgPath === "." ? workdir :
|
|
46984
|
+
const pkgDir = pkgPath === "." ? workdir : join33(workdir, pkgPath);
|
|
46884
46985
|
const language = await deps.detectLanguage(pkgDir);
|
|
46885
|
-
const pkg = await deps.readPackageJson(
|
|
46986
|
+
const pkg = await deps.readPackageJson(join33(pkgDir, "package.json"));
|
|
46886
46987
|
const { framework, testRunner } = resolveFrameworkAndRunner(language, pkg);
|
|
46887
46988
|
return { path: pkgPath, language, framework, testRunner };
|
|
46888
46989
|
}));
|
|
@@ -46915,7 +47016,7 @@ var init_analyze = __esm(() => {
|
|
|
46915
47016
|
});
|
|
46916
47017
|
|
|
46917
47018
|
// src/debate/pre-phase/grounder.ts
|
|
46918
|
-
import { join as
|
|
47019
|
+
import { join as join34 } from "path";
|
|
46919
47020
|
async function buildCodebaseContext(workdir) {
|
|
46920
47021
|
const roots = await _grounderDeps.scanSourceRoots(workdir);
|
|
46921
47022
|
return buildSourceRootsSection(normalizeRoots(workdir, roots));
|
|
@@ -46927,7 +47028,7 @@ function normalizeRoots(workdir, roots) {
|
|
|
46927
47028
|
}));
|
|
46928
47029
|
}
|
|
46929
47030
|
async function writeManifestArtifact(ctx, manifest) {
|
|
46930
|
-
const manifestPath =
|
|
47031
|
+
const manifestPath = join34(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
|
|
46931
47032
|
await _grounderDeps.write(manifestPath, JSON.stringify(manifest, null, 2));
|
|
46932
47033
|
}
|
|
46933
47034
|
var _grounderDeps, grounderStrategy = async (ctx) => {
|
|
@@ -47270,7 +47371,7 @@ function formatSpecDeltas(blockers, manifest) {
|
|
|
47270
47371
|
|
|
47271
47372
|
// src/debate/verifiers/checks.ts
|
|
47272
47373
|
import { existsSync as defaultExistsSync } from "fs";
|
|
47273
|
-
import { join as
|
|
47374
|
+
import { join as join35 } from "path";
|
|
47274
47375
|
function checkFilesExist(prd, workdir, deps) {
|
|
47275
47376
|
const existsSync10 = deps?.existsSync ?? defaultExistsSync;
|
|
47276
47377
|
const findings = [];
|
|
@@ -47280,7 +47381,7 @@ function checkFilesExist(prd, workdir, deps) {
|
|
|
47280
47381
|
for (const entry of story.contextFiles) {
|
|
47281
47382
|
const filePath = typeof entry === "string" ? entry : entry.path;
|
|
47282
47383
|
const factId = typeof entry === "string" ? undefined : entry.factId;
|
|
47283
|
-
const absPath =
|
|
47384
|
+
const absPath = join35(workdir, filePath);
|
|
47284
47385
|
if (existsSync10(absPath))
|
|
47285
47386
|
continue;
|
|
47286
47387
|
if (factId) {
|
|
@@ -47391,7 +47492,7 @@ var init_checks3 = () => {};
|
|
|
47391
47492
|
|
|
47392
47493
|
// src/debate/verifiers/plan-checklist.ts
|
|
47393
47494
|
import { existsSync as existsSync10 } from "fs";
|
|
47394
|
-
import { join as
|
|
47495
|
+
import { join as join36 } from "path";
|
|
47395
47496
|
function parsePrd(output) {
|
|
47396
47497
|
if (!output)
|
|
47397
47498
|
return null;
|
|
@@ -47402,7 +47503,7 @@ function parsePrd(output) {
|
|
|
47402
47503
|
}
|
|
47403
47504
|
}
|
|
47404
47505
|
async function loadManifest(ctx) {
|
|
47405
|
-
const manifestPath =
|
|
47506
|
+
const manifestPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "facts-manifest.json");
|
|
47406
47507
|
const raw = await _planChecklistDeps.readFile(manifestPath);
|
|
47407
47508
|
if (!raw)
|
|
47408
47509
|
return null;
|
|
@@ -47415,7 +47516,7 @@ async function loadManifest(ctx) {
|
|
|
47415
47516
|
}
|
|
47416
47517
|
}
|
|
47417
47518
|
async function emitSpecDeltas(ctx, blockers, manifest) {
|
|
47418
|
-
const artifactPath =
|
|
47519
|
+
const artifactPath = join36(ctx.workdir, ".nax", "runs", ctx.ctx.runtime.runId, "plan", ctx.storyId, "spec-deltas.md");
|
|
47419
47520
|
const content = formatSpecDeltas(blockers, manifest ?? { repoFacts: [], specClaims: [], gaps: [] });
|
|
47420
47521
|
await _planChecklistDeps.write(artifactPath, content);
|
|
47421
47522
|
return artifactPath;
|
|
@@ -47761,7 +47862,7 @@ var init_runner_plan_helpers = __esm(() => {
|
|
|
47761
47862
|
});
|
|
47762
47863
|
|
|
47763
47864
|
// src/debate/runner-plan.ts
|
|
47764
|
-
import { join as
|
|
47865
|
+
import { join as join37 } from "path";
|
|
47765
47866
|
async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
47766
47867
|
const logger = _debateSessionDeps.getSafeLogger();
|
|
47767
47868
|
const config2 = ctx.stageConfig;
|
|
@@ -47820,7 +47921,7 @@ async function runPlan(ctx, taskContext, outputFormat, opts) {
|
|
|
47820
47921
|
sessionMode: ctx.stageConfig.sessionMode ?? "one-shot",
|
|
47821
47922
|
proposers: ctx.stageConfig.proposers
|
|
47822
47923
|
});
|
|
47823
|
-
const outputPaths = resolved.map((_, i) =>
|
|
47924
|
+
const outputPaths = resolved.map((_, i) => join37(opts.outputDir, `prd-debate-${i}.json`));
|
|
47824
47925
|
const successful = [];
|
|
47825
47926
|
let rebuttalList;
|
|
47826
47927
|
if (selectorKind === "verifier-pick") {
|
|
@@ -50063,9 +50164,9 @@ function validateFeatureName(feature) {
|
|
|
50063
50164
|
|
|
50064
50165
|
// src/plan/critic.ts
|
|
50065
50166
|
import { mkdir as mkdir6 } from "fs/promises";
|
|
50066
|
-
import { dirname as dirname7, join as
|
|
50167
|
+
import { dirname as dirname7, join as join40 } from "path";
|
|
50067
50168
|
async function writeSpecDeltas(findings, workdir, runId, storyId, manifest) {
|
|
50068
|
-
const path7 =
|
|
50169
|
+
const path7 = join40(workdir, ".nax", "runs", runId, "plan", storyId, "spec-deltas.md");
|
|
50069
50170
|
await mkdir6(dirname7(path7), { recursive: true });
|
|
50070
50171
|
await Bun.write(path7, formatSpecDeltas(findings, manifest));
|
|
50071
50172
|
return path7;
|
|
@@ -51278,9 +51379,9 @@ __export(exports_plan_decompose, {
|
|
|
51278
51379
|
runReplanLoop: () => runReplanLoop,
|
|
51279
51380
|
planDecomposeCommand: () => planDecomposeCommand
|
|
51280
51381
|
});
|
|
51281
|
-
import { join as
|
|
51382
|
+
import { join as join41 } from "path";
|
|
51282
51383
|
async function planDecomposeCommand(workdir, config2, options) {
|
|
51283
|
-
const prdPath =
|
|
51384
|
+
const prdPath = join41(workdir, ".nax", "features", options.feature, "prd.json");
|
|
51284
51385
|
if (!_planDeps.existsSync(prdPath)) {
|
|
51285
51386
|
throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
|
|
51286
51387
|
stage: "decompose",
|
|
@@ -51454,7 +51555,7 @@ var init_plan_decompose = __esm(() => {
|
|
|
51454
51555
|
|
|
51455
51556
|
// src/cli/plan-runtime.ts
|
|
51456
51557
|
import { existsSync as existsSync15 } from "fs";
|
|
51457
|
-
import { join as
|
|
51558
|
+
import { join as join42 } from "path";
|
|
51458
51559
|
function isRuntimeWithAgentManager(value) {
|
|
51459
51560
|
return typeof value === "object" && value !== null && "agentManager" in value;
|
|
51460
51561
|
}
|
|
@@ -51506,7 +51607,7 @@ var init_plan_runtime = __esm(() => {
|
|
|
51506
51607
|
writeFile: (path7, content) => Bun.write(path7, content).then(() => {}),
|
|
51507
51608
|
scanSourceRoots: (workdir) => scanSourceRoots(workdir),
|
|
51508
51609
|
createRuntime: (cfg, wd, featureName) => createRuntime(cfg, wd, { featureName }),
|
|
51509
|
-
readPackageJson: (workdir) => Bun.file(
|
|
51610
|
+
readPackageJson: (workdir) => Bun.file(join42(workdir, "package.json")).json().catch(() => null),
|
|
51510
51611
|
spawnSync: (cmd, opts) => {
|
|
51511
51612
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
51512
51613
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
@@ -51891,7 +51992,7 @@ var init_metrics = __esm(() => {
|
|
|
51891
51992
|
|
|
51892
51993
|
// src/commands/common.ts
|
|
51893
51994
|
import { existsSync as existsSync16, readdirSync as readdirSync2, realpathSync as realpathSync3 } from "fs";
|
|
51894
|
-
import { join as
|
|
51995
|
+
import { join as join43, resolve as resolve13 } from "path";
|
|
51895
51996
|
function resolveProject(options = {}) {
|
|
51896
51997
|
const { dir, feature } = options;
|
|
51897
51998
|
let projectRoot;
|
|
@@ -51899,12 +52000,12 @@ function resolveProject(options = {}) {
|
|
|
51899
52000
|
let configPath;
|
|
51900
52001
|
if (dir) {
|
|
51901
52002
|
projectRoot = realpathSync3(resolve13(dir));
|
|
51902
|
-
naxDir =
|
|
52003
|
+
naxDir = join43(projectRoot, ".nax");
|
|
51903
52004
|
if (!existsSync16(naxDir)) {
|
|
51904
52005
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
51905
52006
|
Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
|
|
51906
52007
|
}
|
|
51907
|
-
configPath =
|
|
52008
|
+
configPath = join43(naxDir, "config.json");
|
|
51908
52009
|
if (!existsSync16(configPath)) {
|
|
51909
52010
|
throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
|
|
51910
52011
|
Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
@@ -51912,17 +52013,17 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
|
51912
52013
|
} else {
|
|
51913
52014
|
const found = findProjectRoot(process.cwd());
|
|
51914
52015
|
if (!found) {
|
|
51915
|
-
const cwdNaxDir =
|
|
52016
|
+
const cwdNaxDir = join43(process.cwd(), ".nax");
|
|
51916
52017
|
if (existsSync16(cwdNaxDir)) {
|
|
51917
|
-
const cwdConfigPath =
|
|
52018
|
+
const cwdConfigPath = join43(cwdNaxDir, "config.json");
|
|
51918
52019
|
throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
|
|
51919
52020
|
Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
|
|
51920
52021
|
}
|
|
51921
52022
|
throw new NaxError("No nax project found. Run this command from within a nax project directory, or use -d flag to specify the project path.", "PROJECT_NOT_FOUND", { cwd: process.cwd() });
|
|
51922
52023
|
}
|
|
51923
52024
|
projectRoot = found;
|
|
51924
|
-
naxDir =
|
|
51925
|
-
configPath =
|
|
52025
|
+
naxDir = join43(projectRoot, ".nax");
|
|
52026
|
+
configPath = join43(naxDir, "config.json");
|
|
51926
52027
|
}
|
|
51927
52028
|
let featureDir;
|
|
51928
52029
|
if (feature) {
|
|
@@ -51931,8 +52032,8 @@ Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, co
|
|
|
51931
52032
|
} catch (error48) {
|
|
51932
52033
|
throw new NaxError(error48.message, "FEATURE_INVALID", { feature });
|
|
51933
52034
|
}
|
|
51934
|
-
const featuresDir =
|
|
51935
|
-
featureDir =
|
|
52035
|
+
const featuresDir = join43(naxDir, "features");
|
|
52036
|
+
featureDir = join43(featuresDir, feature);
|
|
51936
52037
|
if (!existsSync16(featureDir)) {
|
|
51937
52038
|
const availableFeatures = existsSync16(featuresDir) ? readdirSync2(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
|
|
51938
52039
|
const availableMsg = availableFeatures.length > 0 ? `
|
|
@@ -51965,7 +52066,7 @@ async function resolveProjectAsync(options = {}) {
|
|
|
51965
52066
|
}
|
|
51966
52067
|
const isPlainName = !dir.includes("/") && !dir.includes("\\");
|
|
51967
52068
|
if (isPlainName) {
|
|
51968
|
-
const registryIdentityPath =
|
|
52069
|
+
const registryIdentityPath = join43(globalConfigDir(), dir, ".identity");
|
|
51969
52070
|
const identityFile = Bun.file(registryIdentityPath);
|
|
51970
52071
|
if (await identityFile.exists()) {
|
|
51971
52072
|
try {
|
|
@@ -51997,12 +52098,12 @@ function findProjectRoot(startDir) {
|
|
|
51997
52098
|
let current = resolve13(startDir);
|
|
51998
52099
|
let depth = 0;
|
|
51999
52100
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
52000
|
-
const naxDir =
|
|
52001
|
-
const configPath =
|
|
52101
|
+
const naxDir = join43(current, ".nax");
|
|
52102
|
+
const configPath = join43(naxDir, "config.json");
|
|
52002
52103
|
if (existsSync16(configPath)) {
|
|
52003
52104
|
return realpathSync3(current);
|
|
52004
52105
|
}
|
|
52005
|
-
const parent =
|
|
52106
|
+
const parent = join43(current, "..");
|
|
52006
52107
|
if (parent === current) {
|
|
52007
52108
|
break;
|
|
52008
52109
|
}
|
|
@@ -53161,10 +53262,10 @@ var init_effectiveness = __esm(() => {
|
|
|
53161
53262
|
|
|
53162
53263
|
// src/execution/progress.ts
|
|
53163
53264
|
import { appendFile as appendFile2, mkdir as mkdir7 } from "fs/promises";
|
|
53164
|
-
import { join as
|
|
53265
|
+
import { join as join46 } from "path";
|
|
53165
53266
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
53166
53267
|
await mkdir7(featureDir, { recursive: true });
|
|
53167
|
-
const progressPath =
|
|
53268
|
+
const progressPath = join46(featureDir, "progress.txt");
|
|
53168
53269
|
const timestamp = new Date().toISOString();
|
|
53169
53270
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
53170
53271
|
`;
|
|
@@ -53358,7 +53459,7 @@ var init_completion = __esm(() => {
|
|
|
53358
53459
|
|
|
53359
53460
|
// src/constitution/loader.ts
|
|
53360
53461
|
import { existsSync as existsSync19 } from "fs";
|
|
53361
|
-
import { join as
|
|
53462
|
+
import { join as join47 } from "path";
|
|
53362
53463
|
function truncateToTokens(text, maxTokens) {
|
|
53363
53464
|
const maxChars = maxTokens * 3;
|
|
53364
53465
|
if (text.length <= maxChars) {
|
|
@@ -53380,7 +53481,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
53380
53481
|
}
|
|
53381
53482
|
let combinedContent = "";
|
|
53382
53483
|
if (!config2.skipGlobal) {
|
|
53383
|
-
const globalPath =
|
|
53484
|
+
const globalPath = join47(globalConfigDir(), config2.path);
|
|
53384
53485
|
if (existsSync19(globalPath)) {
|
|
53385
53486
|
const validatedPath = validateFilePath(globalPath, globalConfigDir());
|
|
53386
53487
|
const globalFile = Bun.file(validatedPath);
|
|
@@ -53390,7 +53491,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
53390
53491
|
}
|
|
53391
53492
|
}
|
|
53392
53493
|
}
|
|
53393
|
-
const projectPath =
|
|
53494
|
+
const projectPath = join47(projectDir, config2.path);
|
|
53394
53495
|
if (existsSync19(projectPath)) {
|
|
53395
53496
|
const validatedPath = validateFilePath(projectPath, projectDir);
|
|
53396
53497
|
const projectFile = Bun.file(validatedPath);
|
|
@@ -53964,6 +54065,64 @@ var init_non_blocking_fix = __esm(() => {
|
|
|
53964
54065
|
DEFAULT_DEPS = { captureSnapshotRef, rollbackToRef };
|
|
53965
54066
|
});
|
|
53966
54067
|
|
|
54068
|
+
// src/execution/story-orchestrator-logging.ts
|
|
54069
|
+
function formatPhaseResultMessage(opName, success2, stage, status) {
|
|
54070
|
+
if (opName === "greenfield-gate") {
|
|
54071
|
+
return success2 ? "Greenfield-gate: pre-existing tests detected (not greenfield) \u2014 proceeding with normal TDD" : "Greenfield-gate: no pre-existing tests \u2014 greenfield run, pausing TDD test-writer";
|
|
54072
|
+
}
|
|
54073
|
+
if (status === "skipped") {
|
|
54074
|
+
return `Phase skipped: ${opName}`;
|
|
54075
|
+
}
|
|
54076
|
+
if (stage === "rectification") {
|
|
54077
|
+
return `Rectification strategy completed: ${opName}`;
|
|
54078
|
+
}
|
|
54079
|
+
return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
|
|
54080
|
+
}
|
|
54081
|
+
function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
|
|
54082
|
+
if (output === null || output === undefined || typeof output !== "object")
|
|
54083
|
+
return null;
|
|
54084
|
+
const r = output;
|
|
54085
|
+
const success2 = r.success === true || r.passed === true;
|
|
54086
|
+
const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
|
|
54087
|
+
const status = typeof r.status === "string" ? r.status : undefined;
|
|
54088
|
+
const data = { storyId, phase: opName, durationMs };
|
|
54089
|
+
if (findingsCount !== undefined)
|
|
54090
|
+
data.findingsCount = findingsCount;
|
|
54091
|
+
if (status !== undefined)
|
|
54092
|
+
data.status = status;
|
|
54093
|
+
if (typeof r.failureCategory === "string")
|
|
54094
|
+
data.failureCategory = r.failureCategory;
|
|
54095
|
+
if (typeof r.reviewReason === "string")
|
|
54096
|
+
data.reviewReason = r.reviewReason;
|
|
54097
|
+
return { success: success2, data };
|
|
54098
|
+
}
|
|
54099
|
+
function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
|
|
54100
|
+
if (isTddPhase)
|
|
54101
|
+
return;
|
|
54102
|
+
if (opName === "semantic-review" || opName === "adversarial-review")
|
|
54103
|
+
return;
|
|
54104
|
+
const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
|
|
54105
|
+
if (!built)
|
|
54106
|
+
return;
|
|
54107
|
+
const { success: success2 } = built;
|
|
54108
|
+
const data = { ...built.data, ...progressData };
|
|
54109
|
+
const status = typeof built.data.status === "string" ? built.data.status : undefined;
|
|
54110
|
+
const logger = getSafeLogger();
|
|
54111
|
+
const message = formatPhaseResultMessage(opName, success2, stage, status);
|
|
54112
|
+
if (stage === "rectification") {
|
|
54113
|
+
logger?.info("story-orchestrator", message, data);
|
|
54114
|
+
return;
|
|
54115
|
+
}
|
|
54116
|
+
if (success2) {
|
|
54117
|
+
logger?.info("story-orchestrator", message, data);
|
|
54118
|
+
} else {
|
|
54119
|
+
logger?.warn("story-orchestrator", message, data);
|
|
54120
|
+
}
|
|
54121
|
+
}
|
|
54122
|
+
var init_story_orchestrator_logging = __esm(() => {
|
|
54123
|
+
init_logger2();
|
|
54124
|
+
});
|
|
54125
|
+
|
|
53967
54126
|
// src/execution/story-orchestrator.ts
|
|
53968
54127
|
async function refreshReviewInputForDispatch(opName, input) {
|
|
53969
54128
|
if (opName !== "semantic-review" && opName !== "adversarial-review")
|
|
@@ -54024,15 +54183,6 @@ async function refreshReviewInputForDispatch(opName, input) {
|
|
|
54024
54183
|
return fallback;
|
|
54025
54184
|
}
|
|
54026
54185
|
}
|
|
54027
|
-
function formatPhaseResultMessage(opName, success2, stage) {
|
|
54028
|
-
if (opName === "greenfield-gate") {
|
|
54029
|
-
return success2 ? "Greenfield-gate: pre-existing tests detected (not greenfield) \u2014 proceeding with normal TDD" : "Greenfield-gate: no pre-existing tests \u2014 greenfield run, pausing TDD test-writer";
|
|
54030
|
-
}
|
|
54031
|
-
if (stage === "rectification") {
|
|
54032
|
-
return `Rectification strategy completed: ${opName}`;
|
|
54033
|
-
}
|
|
54034
|
-
return success2 ? `Phase passed: ${opName}` : `Phase failed: ${opName}`;
|
|
54035
|
-
}
|
|
54036
54186
|
function isSlot(value) {
|
|
54037
54187
|
return value !== null && typeof value === "object" && "op" in value && "input" in value && typeof value.op?.kind === "string";
|
|
54038
54188
|
}
|
|
@@ -54228,46 +54378,6 @@ function logUnifiedReviewPhaseStart(storyId, opName) {
|
|
|
54228
54378
|
logger?.info("review", "Running adversarial check", { storyId });
|
|
54229
54379
|
}
|
|
54230
54380
|
}
|
|
54231
|
-
function buildPhaseOutcomeLogData(storyId, opName, output, durationMs) {
|
|
54232
|
-
if (output === null || output === undefined || typeof output !== "object")
|
|
54233
|
-
return null;
|
|
54234
|
-
const r = output;
|
|
54235
|
-
const success2 = r.success === true || r.passed === true;
|
|
54236
|
-
const findingsCount = Array.isArray(r.normalizedFindings) ? r.normalizedFindings.length : Array.isArray(r.findings) ? r.findings.length : undefined;
|
|
54237
|
-
const status = typeof r.status === "string" ? r.status : undefined;
|
|
54238
|
-
const data = { storyId, phase: opName, durationMs };
|
|
54239
|
-
if (findingsCount !== undefined)
|
|
54240
|
-
data.findingsCount = findingsCount;
|
|
54241
|
-
if (status !== undefined)
|
|
54242
|
-
data.status = status;
|
|
54243
|
-
if (typeof r.failureCategory === "string")
|
|
54244
|
-
data.failureCategory = r.failureCategory;
|
|
54245
|
-
if (typeof r.reviewReason === "string")
|
|
54246
|
-
data.reviewReason = r.reviewReason;
|
|
54247
|
-
return { success: success2, data };
|
|
54248
|
-
}
|
|
54249
|
-
function logDeterministicPhaseOutcome(storyId, opName, output, durationMs, isTddPhase, stage, progressData = {}) {
|
|
54250
|
-
if (isTddPhase)
|
|
54251
|
-
return;
|
|
54252
|
-
if (opName === "semantic-review" || opName === "adversarial-review")
|
|
54253
|
-
return;
|
|
54254
|
-
const built = buildPhaseOutcomeLogData(storyId, opName, output, durationMs);
|
|
54255
|
-
if (!built)
|
|
54256
|
-
return;
|
|
54257
|
-
const { success: success2 } = built;
|
|
54258
|
-
const data = { ...built.data, ...progressData };
|
|
54259
|
-
const logger = getSafeLogger();
|
|
54260
|
-
const message = formatPhaseResultMessage(opName, success2, stage);
|
|
54261
|
-
if (stage === "rectification") {
|
|
54262
|
-
logger?.info("story-orchestrator", message, data);
|
|
54263
|
-
return;
|
|
54264
|
-
}
|
|
54265
|
-
if (success2) {
|
|
54266
|
-
logger?.info("story-orchestrator", message, data);
|
|
54267
|
-
} else {
|
|
54268
|
-
logger?.warn("story-orchestrator", message, data);
|
|
54269
|
-
}
|
|
54270
|
-
}
|
|
54271
54381
|
function logUnifiedReviewPhaseResult(storyId, opName, output) {
|
|
54272
54382
|
const logger = getSafeLogger();
|
|
54273
54383
|
const payload = toReviewDecisionPayload(opName, output);
|
|
@@ -54723,6 +54833,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
54723
54833
|
init_prepare_inputs();
|
|
54724
54834
|
init_git();
|
|
54725
54835
|
init_non_blocking_fix();
|
|
54836
|
+
init_story_orchestrator_logging();
|
|
54726
54837
|
_storyOrchestratorDeps = {
|
|
54727
54838
|
callOp,
|
|
54728
54839
|
runFixCycle,
|
|
@@ -54787,7 +54898,7 @@ var init_story_orchestrator = __esm(() => {
|
|
|
54787
54898
|
});
|
|
54788
54899
|
|
|
54789
54900
|
// src/execution/build-plan-for-strategy.ts
|
|
54790
|
-
import { join as
|
|
54901
|
+
import { join as join48 } from "path";
|
|
54791
54902
|
function requiresInitialRefCapture(strategy) {
|
|
54792
54903
|
return isThreeSessionStrategy(strategy);
|
|
54793
54904
|
}
|
|
@@ -54833,13 +54944,14 @@ async function buildPlanForStrategy(ctx, story, config2, testStrategy, inputs) {
|
|
|
54833
54944
|
}
|
|
54834
54945
|
if (shouldRunRectification(config2) && inputs.rectification) {
|
|
54835
54946
|
const sink = makeDeclarationSink();
|
|
54836
|
-
const packageDir =
|
|
54947
|
+
const packageDir = join48(ctx.packageDir, story.workdir ?? "");
|
|
54837
54948
|
const resolvedTestPatterns = await resolveTestFilePatterns(config2, ctx.packageDir, story.workdir);
|
|
54838
54949
|
const strategies = [];
|
|
54839
|
-
|
|
54950
|
+
const pkgQuality = ctx.packageView.select(qualityConfigSelector).quality;
|
|
54951
|
+
if (pkgQuality?.commands?.lintFix || pkgQuality?.commands?.lintFixScoped) {
|
|
54840
54952
|
strategies.push(makeMechanicalLintFixStrategy());
|
|
54841
54953
|
}
|
|
54842
|
-
if (
|
|
54954
|
+
if (pkgQuality?.commands?.formatFix || pkgQuality?.commands?.formatFixScoped) {
|
|
54843
54955
|
strategies.push(makeMechanicalFormatFixStrategy());
|
|
54844
54956
|
}
|
|
54845
54957
|
if (inputs.fullSuiteGate && (isThreeSession || regressionMode === "per-story")) {
|
|
@@ -56366,7 +56478,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
56366
56478
|
}
|
|
56367
56479
|
|
|
56368
56480
|
// src/cli/prompts-tdd.ts
|
|
56369
|
-
import { join as
|
|
56481
|
+
import { join as join49 } from "path";
|
|
56370
56482
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
56371
56483
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
56372
56484
|
TddPromptBuilder.for("test-writer", { isolation: "strict" }).withLoader(ctx.workdir, ctx.config).story(story).context(ctx.contextMarkdown).constitution(ctx.constitution?.content).testCommand(ctx.config.quality?.commands?.test).build(),
|
|
@@ -56385,7 +56497,7 @@ ${frontmatter}---
|
|
|
56385
56497
|
|
|
56386
56498
|
${session.prompt}`;
|
|
56387
56499
|
if (outputDir) {
|
|
56388
|
-
const promptFile =
|
|
56500
|
+
const promptFile = join49(outputDir, `${story.id}.${session.role}.md`);
|
|
56389
56501
|
await Bun.write(promptFile, fullOutput);
|
|
56390
56502
|
logger.info("cli", "Written TDD prompt file", {
|
|
56391
56503
|
storyId: story.id,
|
|
@@ -56401,7 +56513,7 @@ ${"=".repeat(80)}`);
|
|
|
56401
56513
|
}
|
|
56402
56514
|
}
|
|
56403
56515
|
if (outputDir && ctx.contextMarkdown) {
|
|
56404
|
-
const contextFile =
|
|
56516
|
+
const contextFile = join49(outputDir, `${story.id}.context.md`);
|
|
56405
56517
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
56406
56518
|
const contextOutput = `---
|
|
56407
56519
|
${frontmatter}---
|
|
@@ -56416,16 +56528,16 @@ var init_prompts_tdd = __esm(() => {
|
|
|
56416
56528
|
|
|
56417
56529
|
// src/cli/prompts-main.ts
|
|
56418
56530
|
import { existsSync as existsSync20, mkdirSync as mkdirSync3 } from "fs";
|
|
56419
|
-
import { join as
|
|
56531
|
+
import { join as join50 } from "path";
|
|
56420
56532
|
async function promptsCommand(options) {
|
|
56421
56533
|
const logger = getLogger();
|
|
56422
56534
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
56423
|
-
const naxDir =
|
|
56535
|
+
const naxDir = join50(workdir, ".nax");
|
|
56424
56536
|
if (!existsSync20(naxDir)) {
|
|
56425
56537
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
56426
56538
|
}
|
|
56427
|
-
const featureDir =
|
|
56428
|
-
const prdPath =
|
|
56539
|
+
const featureDir = join50(naxDir, "features", feature);
|
|
56540
|
+
const prdPath = join50(featureDir, "prd.json");
|
|
56429
56541
|
if (!existsSync20(prdPath)) {
|
|
56430
56542
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
56431
56543
|
}
|
|
@@ -56492,10 +56604,10 @@ ${frontmatter}---
|
|
|
56492
56604
|
|
|
56493
56605
|
${ctx.prompt}`;
|
|
56494
56606
|
if (outputDir) {
|
|
56495
|
-
const promptFile =
|
|
56607
|
+
const promptFile = join50(outputDir, `${story.id}.prompt.md`);
|
|
56496
56608
|
await Bun.write(promptFile, fullOutput);
|
|
56497
56609
|
if (ctx.contextMarkdown) {
|
|
56498
|
-
const contextFile =
|
|
56610
|
+
const contextFile = join50(outputDir, `${story.id}.context.md`);
|
|
56499
56611
|
const contextOutput = `---
|
|
56500
56612
|
${frontmatter}---
|
|
56501
56613
|
|
|
@@ -56531,12 +56643,12 @@ var init_prompts_main = __esm(() => {
|
|
|
56531
56643
|
|
|
56532
56644
|
// src/cli/prompts-init.ts
|
|
56533
56645
|
import { existsSync as existsSync21, mkdirSync as mkdirSync4 } from "fs";
|
|
56534
|
-
import { join as
|
|
56646
|
+
import { join as join51 } from "path";
|
|
56535
56647
|
async function promptsInitCommand(options) {
|
|
56536
56648
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
56537
|
-
const templatesDir =
|
|
56649
|
+
const templatesDir = join51(workdir, ".nax", "templates");
|
|
56538
56650
|
mkdirSync4(templatesDir, { recursive: true });
|
|
56539
|
-
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(
|
|
56651
|
+
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync21(join51(templatesDir, f)));
|
|
56540
56652
|
if (existingFiles.length > 0 && !force) {
|
|
56541
56653
|
_promptsInitDeps.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
56542
56654
|
Pass --force to overwrite existing templates.`);
|
|
@@ -56544,7 +56656,7 @@ async function promptsInitCommand(options) {
|
|
|
56544
56656
|
}
|
|
56545
56657
|
const written = [];
|
|
56546
56658
|
for (const template of TEMPLATE_ROLES) {
|
|
56547
|
-
const filePath =
|
|
56659
|
+
const filePath = join51(templatesDir, template.file);
|
|
56548
56660
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
56549
56661
|
const content = TEMPLATE_HEADER + roleBody;
|
|
56550
56662
|
await Bun.write(filePath, content);
|
|
@@ -56560,7 +56672,7 @@ async function promptsInitCommand(options) {
|
|
|
56560
56672
|
return written;
|
|
56561
56673
|
}
|
|
56562
56674
|
async function autoWirePromptsConfig(workdir) {
|
|
56563
|
-
const configPath =
|
|
56675
|
+
const configPath = join51(workdir, "nax.config.json");
|
|
56564
56676
|
if (!existsSync21(configPath)) {
|
|
56565
56677
|
const exampleConfig = JSON.stringify({
|
|
56566
56678
|
prompts: {
|
|
@@ -56728,7 +56840,7 @@ __export(exports_init_context, {
|
|
|
56728
56840
|
generateContextTemplate: () => generateContextTemplate,
|
|
56729
56841
|
_initContextDeps: () => _initContextDeps
|
|
56730
56842
|
});
|
|
56731
|
-
import { basename as basename9, join as
|
|
56843
|
+
import { basename as basename9, join as join52 } from "path";
|
|
56732
56844
|
async function bunFileExists(path13) {
|
|
56733
56845
|
return Bun.file(path13).exists();
|
|
56734
56846
|
}
|
|
@@ -56763,7 +56875,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
56763
56875
|
return [];
|
|
56764
56876
|
}
|
|
56765
56877
|
async function readPackageManifest(projectRoot) {
|
|
56766
|
-
const packageJsonPath =
|
|
56878
|
+
const packageJsonPath = join52(projectRoot, "package.json");
|
|
56767
56879
|
if (!await bunFileExists(packageJsonPath)) {
|
|
56768
56880
|
return null;
|
|
56769
56881
|
}
|
|
@@ -56781,7 +56893,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
56781
56893
|
}
|
|
56782
56894
|
}
|
|
56783
56895
|
async function readReadmeSnippet(projectRoot) {
|
|
56784
|
-
const readmePath =
|
|
56896
|
+
const readmePath = join52(projectRoot, "README.md");
|
|
56785
56897
|
if (!await bunFileExists(readmePath)) {
|
|
56786
56898
|
return null;
|
|
56787
56899
|
}
|
|
@@ -56799,7 +56911,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
56799
56911
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
56800
56912
|
const found = [];
|
|
56801
56913
|
for (const candidate of candidates) {
|
|
56802
|
-
const path13 =
|
|
56914
|
+
const path13 = join52(projectRoot, candidate);
|
|
56803
56915
|
if (await bunFileExists(path13)) {
|
|
56804
56916
|
found.push(candidate);
|
|
56805
56917
|
}
|
|
@@ -56810,7 +56922,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
56810
56922
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
56811
56923
|
const found = [];
|
|
56812
56924
|
for (const candidate of candidates) {
|
|
56813
|
-
const path13 =
|
|
56925
|
+
const path13 = join52(projectRoot, candidate);
|
|
56814
56926
|
if (await bunFileExists(path13)) {
|
|
56815
56927
|
found.push(candidate);
|
|
56816
56928
|
}
|
|
@@ -56971,8 +57083,8 @@ function generatePackageContextTemplate(packagePath) {
|
|
|
56971
57083
|
}
|
|
56972
57084
|
async function initPackage(repoRoot, packagePath, force = false) {
|
|
56973
57085
|
const logger = getLogger();
|
|
56974
|
-
const naxDir =
|
|
56975
|
-
const contextPath =
|
|
57086
|
+
const naxDir = join52(repoRoot, ".nax", "mono", packagePath);
|
|
57087
|
+
const contextPath = join52(naxDir, "context.md");
|
|
56976
57088
|
if (await bunFileExists(contextPath) && !force) {
|
|
56977
57089
|
logger.info("init", "Package context.md already exists (use --force to overwrite)", {
|
|
56978
57090
|
storyId: "init-context",
|
|
@@ -56989,8 +57101,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
56989
57101
|
}
|
|
56990
57102
|
async function initContext(projectRoot, options = {}) {
|
|
56991
57103
|
const logger = getLogger();
|
|
56992
|
-
const naxDir =
|
|
56993
|
-
const contextPath =
|
|
57104
|
+
const naxDir = join52(projectRoot, ".nax");
|
|
57105
|
+
const contextPath = join52(naxDir, "context.md");
|
|
56994
57106
|
if (await bunFileExists(contextPath) && !options.force) {
|
|
56995
57107
|
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", {
|
|
56996
57108
|
storyId: "init-context",
|
|
@@ -57026,9 +57138,9 @@ var init_init_context = __esm(() => {
|
|
|
57026
57138
|
|
|
57027
57139
|
// src/cli/init-detect.ts
|
|
57028
57140
|
import { existsSync as existsSync22, readFileSync } from "fs";
|
|
57029
|
-
import { join as
|
|
57141
|
+
import { join as join53 } from "path";
|
|
57030
57142
|
function readPackageJson(projectRoot) {
|
|
57031
|
-
const pkgPath =
|
|
57143
|
+
const pkgPath = join53(projectRoot, "package.json");
|
|
57032
57144
|
if (!existsSync22(pkgPath))
|
|
57033
57145
|
return;
|
|
57034
57146
|
try {
|
|
@@ -57071,41 +57183,41 @@ function detectStack(projectRoot) {
|
|
|
57071
57183
|
};
|
|
57072
57184
|
}
|
|
57073
57185
|
function detectRuntime(projectRoot) {
|
|
57074
|
-
if (existsSync22(
|
|
57186
|
+
if (existsSync22(join53(projectRoot, "bun.lockb")) || existsSync22(join53(projectRoot, "bunfig.toml"))) {
|
|
57075
57187
|
return "bun";
|
|
57076
57188
|
}
|
|
57077
|
-
if (existsSync22(
|
|
57189
|
+
if (existsSync22(join53(projectRoot, "package-lock.json")) || existsSync22(join53(projectRoot, "yarn.lock")) || existsSync22(join53(projectRoot, "pnpm-lock.yaml"))) {
|
|
57078
57190
|
return "node";
|
|
57079
57191
|
}
|
|
57080
57192
|
return "unknown";
|
|
57081
57193
|
}
|
|
57082
57194
|
function detectLanguage2(projectRoot) {
|
|
57083
|
-
if (existsSync22(
|
|
57195
|
+
if (existsSync22(join53(projectRoot, "tsconfig.json")))
|
|
57084
57196
|
return "typescript";
|
|
57085
|
-
if (existsSync22(
|
|
57197
|
+
if (existsSync22(join53(projectRoot, "pyproject.toml")) || existsSync22(join53(projectRoot, "setup.py"))) {
|
|
57086
57198
|
return "python";
|
|
57087
57199
|
}
|
|
57088
|
-
if (existsSync22(
|
|
57200
|
+
if (existsSync22(join53(projectRoot, "Cargo.toml")))
|
|
57089
57201
|
return "rust";
|
|
57090
|
-
if (existsSync22(
|
|
57202
|
+
if (existsSync22(join53(projectRoot, "go.mod")))
|
|
57091
57203
|
return "go";
|
|
57092
57204
|
return "unknown";
|
|
57093
57205
|
}
|
|
57094
57206
|
function detectLinter(projectRoot) {
|
|
57095
|
-
if (existsSync22(
|
|
57207
|
+
if (existsSync22(join53(projectRoot, "biome.json")) || existsSync22(join53(projectRoot, "biome.jsonc"))) {
|
|
57096
57208
|
return "biome";
|
|
57097
57209
|
}
|
|
57098
|
-
if (existsSync22(
|
|
57210
|
+
if (existsSync22(join53(projectRoot, ".eslintrc.json")) || existsSync22(join53(projectRoot, ".eslintrc.js")) || existsSync22(join53(projectRoot, "eslint.config.js"))) {
|
|
57099
57211
|
return "eslint";
|
|
57100
57212
|
}
|
|
57101
57213
|
return "unknown";
|
|
57102
57214
|
}
|
|
57103
57215
|
function detectMonorepo(projectRoot) {
|
|
57104
|
-
if (existsSync22(
|
|
57216
|
+
if (existsSync22(join53(projectRoot, "turbo.json")))
|
|
57105
57217
|
return "turborepo";
|
|
57106
|
-
if (existsSync22(
|
|
57218
|
+
if (existsSync22(join53(projectRoot, "nx.json")))
|
|
57107
57219
|
return "nx";
|
|
57108
|
-
if (existsSync22(
|
|
57220
|
+
if (existsSync22(join53(projectRoot, "pnpm-workspace.yaml")))
|
|
57109
57221
|
return "pnpm-workspaces";
|
|
57110
57222
|
const pkg = readPackageJson(projectRoot);
|
|
57111
57223
|
if (pkg?.workspaces)
|
|
@@ -57249,7 +57361,7 @@ __export(exports_init, {
|
|
|
57249
57361
|
});
|
|
57250
57362
|
import { existsSync as existsSync23 } from "fs";
|
|
57251
57363
|
import { mkdir as mkdir8 } from "fs/promises";
|
|
57252
|
-
import { join as
|
|
57364
|
+
import { join as join54 } from "path";
|
|
57253
57365
|
function validateProjectName(name) {
|
|
57254
57366
|
if (!name)
|
|
57255
57367
|
return { valid: false, error: "name must be non-empty" };
|
|
@@ -57285,7 +57397,7 @@ async function checkInitCollision(name, currentWorkdir, currentRemote) {
|
|
|
57285
57397
|
}
|
|
57286
57398
|
async function updateGitignore(projectRoot) {
|
|
57287
57399
|
const logger = getLogger();
|
|
57288
|
-
const gitignorePath =
|
|
57400
|
+
const gitignorePath = join54(projectRoot, ".gitignore");
|
|
57289
57401
|
let existing = "";
|
|
57290
57402
|
if (existsSync23(gitignorePath)) {
|
|
57291
57403
|
existing = await Bun.file(gitignorePath).text();
|
|
@@ -57371,7 +57483,7 @@ async function initGlobal() {
|
|
|
57371
57483
|
await mkdir8(globalDir, { recursive: true });
|
|
57372
57484
|
logger.info("init", "Created global config directory", { path: globalDir });
|
|
57373
57485
|
}
|
|
57374
|
-
const configPath =
|
|
57486
|
+
const configPath = join54(globalDir, "config.json");
|
|
57375
57487
|
if (!existsSync23(configPath)) {
|
|
57376
57488
|
await Bun.write(configPath, `${JSON.stringify(MINIMAL_GLOBAL_CONFIG, null, 2)}
|
|
57377
57489
|
`);
|
|
@@ -57379,14 +57491,14 @@ async function initGlobal() {
|
|
|
57379
57491
|
} else {
|
|
57380
57492
|
logger.info("init", "Global config already exists", { path: configPath });
|
|
57381
57493
|
}
|
|
57382
|
-
const constitutionPath =
|
|
57494
|
+
const constitutionPath = join54(globalDir, "constitution.md");
|
|
57383
57495
|
if (!existsSync23(constitutionPath)) {
|
|
57384
57496
|
await Bun.write(constitutionPath, buildConstitution({ runtime: "unknown", language: "unknown", linter: "unknown", monorepo: "none" }));
|
|
57385
57497
|
logger.info("init", "Created global constitution", { path: constitutionPath });
|
|
57386
57498
|
} else {
|
|
57387
57499
|
logger.info("init", "Global constitution already exists", { path: constitutionPath });
|
|
57388
57500
|
}
|
|
57389
|
-
const hooksDir =
|
|
57501
|
+
const hooksDir = join54(globalDir, "hooks");
|
|
57390
57502
|
if (!existsSync23(hooksDir)) {
|
|
57391
57503
|
await mkdir8(hooksDir, { recursive: true });
|
|
57392
57504
|
logger.info("init", "Created global hooks directory", { path: hooksDir });
|
|
@@ -57419,7 +57531,7 @@ async function initProject(projectRoot, options) {
|
|
|
57419
57531
|
if (detectedName && !options?.force) {
|
|
57420
57532
|
const collision = await checkInitCollision(detectedName, projectRoot, currentRemote);
|
|
57421
57533
|
if (collision.collision && collision.existing) {
|
|
57422
|
-
const configPath2 =
|
|
57534
|
+
const configPath2 = join54(projectDir, "config.json");
|
|
57423
57535
|
throw new NaxError([
|
|
57424
57536
|
`Project name collision: "${detectedName}"`,
|
|
57425
57537
|
` This project: ${projectRoot}`,
|
|
@@ -57447,7 +57559,7 @@ async function initProject(projectRoot, options) {
|
|
|
57447
57559
|
linter: stack.linter,
|
|
57448
57560
|
monorepo: stack.monorepo
|
|
57449
57561
|
});
|
|
57450
|
-
const configPath =
|
|
57562
|
+
const configPath = join54(projectDir, "config.json");
|
|
57451
57563
|
if (!existsSync23(configPath)) {
|
|
57452
57564
|
await Bun.write(configPath, `${JSON.stringify(projectConfig, null, 2)}
|
|
57453
57565
|
`);
|
|
@@ -57456,14 +57568,14 @@ async function initProject(projectRoot, options) {
|
|
|
57456
57568
|
logger.info("init", "Project config already exists", { path: configPath });
|
|
57457
57569
|
}
|
|
57458
57570
|
await initContext(projectRoot, { ai: options?.ai, force: options?.force });
|
|
57459
|
-
const constitutionPath =
|
|
57571
|
+
const constitutionPath = join54(projectDir, "constitution.md");
|
|
57460
57572
|
if (!existsSync23(constitutionPath) || options?.force) {
|
|
57461
57573
|
await Bun.write(constitutionPath, buildConstitution(stack));
|
|
57462
57574
|
logger.info("init", "Created project constitution", { path: constitutionPath });
|
|
57463
57575
|
} else {
|
|
57464
57576
|
logger.info("init", "Project constitution already exists", { path: constitutionPath });
|
|
57465
57577
|
}
|
|
57466
|
-
const hooksDir =
|
|
57578
|
+
const hooksDir = join54(projectDir, "hooks");
|
|
57467
57579
|
if (!existsSync23(hooksDir)) {
|
|
57468
57580
|
await mkdir8(hooksDir, { recursive: true });
|
|
57469
57581
|
logger.info("init", "Created project hooks directory", { path: hooksDir });
|
|
@@ -57525,36 +57637,36 @@ var init_init2 = __esm(() => {
|
|
|
57525
57637
|
});
|
|
57526
57638
|
|
|
57527
57639
|
// src/cli/setup-analyze.ts
|
|
57528
|
-
import { join as
|
|
57640
|
+
import { join as join55 } from "path";
|
|
57529
57641
|
async function detectPackageManager(workdir) {
|
|
57530
57642
|
const { fileExists } = _analyzeRepoDeps;
|
|
57531
|
-
if (await fileExists(
|
|
57643
|
+
if (await fileExists(join55(workdir, "bun.lock")))
|
|
57532
57644
|
return { pmRunPrefix: "bun run", pmDlx: "bunx" };
|
|
57533
|
-
if (await fileExists(
|
|
57645
|
+
if (await fileExists(join55(workdir, "bun.lockb")))
|
|
57534
57646
|
return { pmRunPrefix: "bun run", pmDlx: "bunx" };
|
|
57535
|
-
if (await fileExists(
|
|
57647
|
+
if (await fileExists(join55(workdir, "package-lock.json")))
|
|
57536
57648
|
return { pmRunPrefix: "npm run", pmDlx: "npx" };
|
|
57537
|
-
if (await fileExists(
|
|
57649
|
+
if (await fileExists(join55(workdir, "yarn.lock")))
|
|
57538
57650
|
return { pmRunPrefix: "yarn", pmDlx: "yarn dlx" };
|
|
57539
|
-
if (await fileExists(
|
|
57651
|
+
if (await fileExists(join55(workdir, "pnpm-lock.yaml")))
|
|
57540
57652
|
return { pmRunPrefix: "pnpm run", pmDlx: "pnpx" };
|
|
57541
57653
|
return { pmRunPrefix: "npm run", pmDlx: "npx" };
|
|
57542
57654
|
}
|
|
57543
57655
|
async function detectOrchestrator(workdir) {
|
|
57544
57656
|
const { fileExists } = _analyzeRepoDeps;
|
|
57545
|
-
if (await fileExists(
|
|
57657
|
+
if (await fileExists(join55(workdir, "turbo.json")))
|
|
57546
57658
|
return "turbo";
|
|
57547
|
-
if (await fileExists(
|
|
57659
|
+
if (await fileExists(join55(workdir, "nx.json")))
|
|
57548
57660
|
return "nx";
|
|
57549
57661
|
return "none";
|
|
57550
57662
|
}
|
|
57551
57663
|
async function getMissingScripts(packageDir) {
|
|
57552
|
-
const pkg = await _analyzeRepoDeps.readJson(
|
|
57664
|
+
const pkg = await _analyzeRepoDeps.readJson(join55(packageDir, "package.json"));
|
|
57553
57665
|
const scripts = pkg?.scripts ?? {};
|
|
57554
57666
|
return CANONICAL_SCRIPTS.filter((s) => !(s in scripts));
|
|
57555
57667
|
}
|
|
57556
57668
|
async function buildPackageFacts(workdir, relativeDir, testPatternMap) {
|
|
57557
|
-
const packageDir = relativeDir === "" ? workdir :
|
|
57669
|
+
const packageDir = relativeDir === "" ? workdir : join55(workdir, relativeDir);
|
|
57558
57670
|
const [profile, missingScripts] = await Promise.all([
|
|
57559
57671
|
_analyzeRepoDeps.detectProjectProfile(packageDir, {}),
|
|
57560
57672
|
getMissingScripts(packageDir)
|
|
@@ -57615,7 +57727,7 @@ var init_setup_analyze = __esm(() => {
|
|
|
57615
57727
|
});
|
|
57616
57728
|
|
|
57617
57729
|
// src/cli/setup-fill.ts
|
|
57618
|
-
import { join as
|
|
57730
|
+
import { join as join56 } from "path";
|
|
57619
57731
|
async function addScriptToPackageJson(pkgJsonPath, key, value) {
|
|
57620
57732
|
const pkg = await _fillScriptsDeps.readJson(pkgJsonPath) ?? {};
|
|
57621
57733
|
const scripts = pkg.scripts ?? {};
|
|
@@ -57638,18 +57750,18 @@ async function fillScripts(workdir, analysis) {
|
|
|
57638
57750
|
if (shape === "single") {
|
|
57639
57751
|
const rootPkg = packages[0];
|
|
57640
57752
|
if (rootPkg?.missingScripts.includes(TYPE_CHECK_KEY)) {
|
|
57641
|
-
await addScriptToPackageJson(
|
|
57753
|
+
await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
|
|
57642
57754
|
}
|
|
57643
57755
|
return;
|
|
57644
57756
|
}
|
|
57645
57757
|
for (const pkg of packages) {
|
|
57646
57758
|
if (!pkg.missingScripts.includes(TYPE_CHECK_KEY))
|
|
57647
57759
|
continue;
|
|
57648
|
-
await addScriptToPackageJson(
|
|
57760
|
+
await addScriptToPackageJson(join56(workdir, pkg.relativeDir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_SCRIPT);
|
|
57649
57761
|
}
|
|
57650
57762
|
if (orchestrator === "turbo" && packages.some((p) => p.missingScripts.includes(TYPE_CHECK_KEY))) {
|
|
57651
|
-
await addTurboTask(
|
|
57652
|
-
await addScriptToPackageJson(
|
|
57763
|
+
await addTurboTask(join56(workdir, "turbo.json"), TYPE_CHECK_KEY);
|
|
57764
|
+
await addScriptToPackageJson(join56(workdir, "package.json"), TYPE_CHECK_KEY, TYPE_CHECK_TURBO_PASSTHROUGH);
|
|
57653
57765
|
}
|
|
57654
57766
|
}
|
|
57655
57767
|
var TYPE_CHECK_KEY = "type-check", TYPE_CHECK_SCRIPT = "tsc --noEmit -p tsconfig.json", TYPE_CHECK_TURBO_PASSTHROUGH = "turbo run type-check", _fillScriptsDeps;
|
|
@@ -57710,11 +57822,11 @@ __export(exports_setup, {
|
|
|
57710
57822
|
setupCommand: () => setupCommand,
|
|
57711
57823
|
_setupDeps: () => _setupDeps
|
|
57712
57824
|
});
|
|
57713
|
-
import { join as
|
|
57825
|
+
import { join as join57 } from "path";
|
|
57714
57826
|
async function setupCommand(options = {}) {
|
|
57715
57827
|
const workdir = options.dir ?? process.cwd();
|
|
57716
|
-
const naxDir =
|
|
57717
|
-
const naxConfigPath =
|
|
57828
|
+
const naxDir = join57(workdir, ".nax");
|
|
57829
|
+
const naxConfigPath = join57(naxDir, "config.json");
|
|
57718
57830
|
const exists = await _setupDeps.fileExists(naxConfigPath);
|
|
57719
57831
|
if (exists && !options.force) {
|
|
57720
57832
|
_setupDeps.stderr("[setup] .nax/config.json already exists. Use --force to overwrite.");
|
|
@@ -57747,9 +57859,9 @@ ${JSON.stringify(plan.config, null, 2)}`);
|
|
|
57747
57859
|
await _setupDeps.mkdir(naxDir);
|
|
57748
57860
|
await _setupDeps.writeFile(naxConfigPath, JSON.stringify(plan.config, null, 2));
|
|
57749
57861
|
for (const mc of plan.monoConfigs) {
|
|
57750
|
-
const monoDir =
|
|
57862
|
+
const monoDir = join57(naxDir, "mono", mc.relativeDir);
|
|
57751
57863
|
await _setupDeps.mkdir(monoDir);
|
|
57752
|
-
await _setupDeps.writeFile(
|
|
57864
|
+
await _setupDeps.writeFile(join57(monoDir, "config.json"), JSON.stringify(mc.config, null, 2));
|
|
57753
57865
|
}
|
|
57754
57866
|
const gateResult = await _setupDeps.runGate(workdir, plan.config);
|
|
57755
57867
|
if (gateResult !== 0) {
|
|
@@ -59178,12 +59290,12 @@ var init_loader4 = __esm(() => {
|
|
|
59178
59290
|
});
|
|
59179
59291
|
|
|
59180
59292
|
// src/utils/paths.ts
|
|
59181
|
-
import { join as
|
|
59293
|
+
import { join as join68 } from "path";
|
|
59182
59294
|
function getRunsDir() {
|
|
59183
|
-
return process.env.NAX_RUNS_DIR ??
|
|
59295
|
+
return process.env.NAX_RUNS_DIR ?? join68(globalConfigDir(), "runs");
|
|
59184
59296
|
}
|
|
59185
59297
|
function getEventsRootDir() {
|
|
59186
|
-
return
|
|
59298
|
+
return join68(globalConfigDir(), "events");
|
|
59187
59299
|
}
|
|
59188
59300
|
var init_paths3 = __esm(() => {
|
|
59189
59301
|
init_paths();
|
|
@@ -59243,7 +59355,7 @@ var init_command_argv = __esm(() => {
|
|
|
59243
59355
|
});
|
|
59244
59356
|
|
|
59245
59357
|
// src/hooks/runner.ts
|
|
59246
|
-
import { join as
|
|
59358
|
+
import { join as join75 } from "path";
|
|
59247
59359
|
function createDrainDeadline2(deadlineMs) {
|
|
59248
59360
|
let timeoutId;
|
|
59249
59361
|
const promise2 = new Promise((resolve16) => {
|
|
@@ -59262,14 +59374,14 @@ async function loadHooksConfig(projectDir, globalDir) {
|
|
|
59262
59374
|
let globalHooks = { hooks: {} };
|
|
59263
59375
|
let projectHooks = { hooks: {} };
|
|
59264
59376
|
let skipGlobal = false;
|
|
59265
|
-
const projectPath =
|
|
59377
|
+
const projectPath = join75(projectDir, "hooks.json");
|
|
59266
59378
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
59267
59379
|
if (projectData) {
|
|
59268
59380
|
projectHooks = projectData;
|
|
59269
59381
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
59270
59382
|
}
|
|
59271
59383
|
if (!skipGlobal && globalDir) {
|
|
59272
|
-
const globalPath =
|
|
59384
|
+
const globalPath = join75(globalDir, "hooks.json");
|
|
59273
59385
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
59274
59386
|
if (globalData) {
|
|
59275
59387
|
globalHooks = globalData;
|
|
@@ -59439,7 +59551,7 @@ var package_default;
|
|
|
59439
59551
|
var init_package = __esm(() => {
|
|
59440
59552
|
package_default = {
|
|
59441
59553
|
name: "@nathapp/nax",
|
|
59442
|
-
version: "0.69.
|
|
59554
|
+
version: "0.69.3",
|
|
59443
59555
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
59444
59556
|
type: "module",
|
|
59445
59557
|
bin: {
|
|
@@ -59534,8 +59646,8 @@ var init_version = __esm(() => {
|
|
|
59534
59646
|
NAX_VERSION = package_default.version;
|
|
59535
59647
|
NAX_COMMIT = (() => {
|
|
59536
59648
|
try {
|
|
59537
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
59538
|
-
return "
|
|
59649
|
+
if (/^[0-9a-f]{6,10}$/.test("7917c240"))
|
|
59650
|
+
return "7917c240";
|
|
59539
59651
|
} catch {}
|
|
59540
59652
|
try {
|
|
59541
59653
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -60412,15 +60524,15 @@ var init_acceptance_loop = __esm(() => {
|
|
|
60412
60524
|
|
|
60413
60525
|
// src/session/scratch-purge.ts
|
|
60414
60526
|
import { mkdir as mkdir12, rename, rm } from "fs/promises";
|
|
60415
|
-
import { dirname as dirname12, join as
|
|
60527
|
+
import { dirname as dirname12, join as join76 } from "path";
|
|
60416
60528
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
60417
|
-
const sessionsDir =
|
|
60529
|
+
const sessionsDir = join76(projectDir, ".nax", "features", featureName, "sessions");
|
|
60418
60530
|
const sessionIds = await _scratchPurgeDeps.listSessionDirs(sessionsDir);
|
|
60419
60531
|
const cutoffMs = _scratchPurgeDeps.now() - retentionDays * 86400000;
|
|
60420
60532
|
let purged = 0;
|
|
60421
60533
|
for (const sessionId of sessionIds) {
|
|
60422
|
-
const sessionDir =
|
|
60423
|
-
const descriptorPath =
|
|
60534
|
+
const sessionDir = join76(sessionsDir, sessionId);
|
|
60535
|
+
const descriptorPath = join76(sessionDir, "descriptor.json");
|
|
60424
60536
|
if (!await _scratchPurgeDeps.fileExists(descriptorPath))
|
|
60425
60537
|
continue;
|
|
60426
60538
|
let lastActivityAt;
|
|
@@ -60436,7 +60548,7 @@ async function purgeStaleScratch(projectDir, featureName, retentionDays, archive
|
|
|
60436
60548
|
if (new Date(lastActivityAt).getTime() >= cutoffMs)
|
|
60437
60549
|
continue;
|
|
60438
60550
|
if (archiveInsteadOfDelete) {
|
|
60439
|
-
const archiveDest =
|
|
60551
|
+
const archiveDest = join76(projectDir, ".nax", "features", featureName, "_archive", "sessions", sessionId);
|
|
60440
60552
|
await _scratchPurgeDeps.move(sessionDir, archiveDest);
|
|
60441
60553
|
} else {
|
|
60442
60554
|
await _scratchPurgeDeps.remove(sessionDir);
|
|
@@ -61174,12 +61286,12 @@ var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
|
61174
61286
|
|
|
61175
61287
|
// src/pipeline/subscribers/events-writer.ts
|
|
61176
61288
|
import { appendFile as appendFile4, mkdir as mkdir13 } from "fs/promises";
|
|
61177
|
-
import { basename as basename13, join as
|
|
61289
|
+
import { basename as basename13, join as join77 } from "path";
|
|
61178
61290
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
61179
61291
|
const logger = getSafeLogger();
|
|
61180
61292
|
const project = basename13(workdir);
|
|
61181
|
-
const eventsDir =
|
|
61182
|
-
const eventsFile =
|
|
61293
|
+
const eventsDir = join77(getEventsRootDir(), project);
|
|
61294
|
+
const eventsFile = join77(eventsDir, "events.jsonl");
|
|
61183
61295
|
let dirReady = false;
|
|
61184
61296
|
const write = (line) => {
|
|
61185
61297
|
return (async () => {
|
|
@@ -61360,12 +61472,12 @@ var init_interaction2 = __esm(() => {
|
|
|
61360
61472
|
|
|
61361
61473
|
// src/pipeline/subscribers/registry.ts
|
|
61362
61474
|
import { mkdir as mkdir14, writeFile as writeFile2 } from "fs/promises";
|
|
61363
|
-
import { basename as basename14, join as
|
|
61475
|
+
import { basename as basename14, join as join78 } from "path";
|
|
61364
61476
|
function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
61365
61477
|
const logger = getSafeLogger();
|
|
61366
61478
|
const project = basename14(workdir);
|
|
61367
|
-
const runDir =
|
|
61368
|
-
const metaFile =
|
|
61479
|
+
const runDir = join78(getRunsDir(), `${project}-${feature}-${runId}`);
|
|
61480
|
+
const metaFile = join78(runDir, "meta.json");
|
|
61369
61481
|
const unsub = bus.on("run:started", (_ev) => {
|
|
61370
61482
|
return (async () => {
|
|
61371
61483
|
try {
|
|
@@ -61375,8 +61487,8 @@ function wireRegistry(bus, feature, runId, workdir, outputDir) {
|
|
|
61375
61487
|
project,
|
|
61376
61488
|
feature,
|
|
61377
61489
|
workdir,
|
|
61378
|
-
statusPath:
|
|
61379
|
-
eventsDir:
|
|
61490
|
+
statusPath: join78(outputDir, "features", feature, "status.json"),
|
|
61491
|
+
eventsDir: join78(outputDir, "features", feature, "runs"),
|
|
61380
61492
|
registeredAt: new Date().toISOString()
|
|
61381
61493
|
};
|
|
61382
61494
|
await writeFile2(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -61622,7 +61734,7 @@ var init_types9 = __esm(() => {
|
|
|
61622
61734
|
|
|
61623
61735
|
// src/worktree/dependencies.ts
|
|
61624
61736
|
import { existsSync as existsSync30 } from "fs";
|
|
61625
|
-
import { join as
|
|
61737
|
+
import { join as join79 } from "path";
|
|
61626
61738
|
async function prepareWorktreeDependencies(options) {
|
|
61627
61739
|
const mode = options.config.execution.worktreeDependencies.mode;
|
|
61628
61740
|
const resolvedCwd = resolveDependencyCwd(options);
|
|
@@ -61636,7 +61748,7 @@ async function prepareWorktreeDependencies(options) {
|
|
|
61636
61748
|
}
|
|
61637
61749
|
}
|
|
61638
61750
|
function resolveDependencyCwd(options) {
|
|
61639
|
-
return options.storyWorkdir ?
|
|
61751
|
+
return options.storyWorkdir ? join79(options.worktreeRoot, options.storyWorkdir) : options.worktreeRoot;
|
|
61640
61752
|
}
|
|
61641
61753
|
function resolveInheritedDependencies(options, resolvedCwd) {
|
|
61642
61754
|
if (hasDependencyManifests(options.worktreeRoot, resolvedCwd)) {
|
|
@@ -61646,7 +61758,7 @@ function resolveInheritedDependencies(options, resolvedCwd) {
|
|
|
61646
61758
|
}
|
|
61647
61759
|
function hasDependencyManifests(worktreeRoot, resolvedCwd) {
|
|
61648
61760
|
const directories = resolvedCwd === worktreeRoot ? [worktreeRoot] : [worktreeRoot, resolvedCwd];
|
|
61649
|
-
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(
|
|
61761
|
+
return directories.some((directory) => PHASE_ONE_INHERIT_UNSUPPORTED_FILES.some((filename) => _worktreeDependencyDeps.existsSync(join79(directory, filename))));
|
|
61650
61762
|
}
|
|
61651
61763
|
async function provisionDependencies(config2, worktreeRoot, resolvedCwd) {
|
|
61652
61764
|
const setupCommand2 = config2.execution.worktreeDependencies.setupCommand;
|
|
@@ -61710,13 +61822,13 @@ __export(exports_manager, {
|
|
|
61710
61822
|
});
|
|
61711
61823
|
import { existsSync as existsSync31, symlinkSync } from "fs";
|
|
61712
61824
|
import { mkdir as mkdir15 } from "fs/promises";
|
|
61713
|
-
import { join as
|
|
61825
|
+
import { join as join80 } from "path";
|
|
61714
61826
|
|
|
61715
61827
|
class WorktreeManager {
|
|
61716
61828
|
async ensureGitExcludes(projectRoot) {
|
|
61717
61829
|
const logger = getSafeLogger();
|
|
61718
|
-
const infoDir =
|
|
61719
|
-
const excludePath =
|
|
61830
|
+
const infoDir = join80(projectRoot, ".git", "info");
|
|
61831
|
+
const excludePath = join80(infoDir, "exclude");
|
|
61720
61832
|
try {
|
|
61721
61833
|
await mkdir15(infoDir, { recursive: true });
|
|
61722
61834
|
let existing = "";
|
|
@@ -61743,7 +61855,7 @@ ${missing.join(`
|
|
|
61743
61855
|
}
|
|
61744
61856
|
async create(projectRoot, storyId) {
|
|
61745
61857
|
validateStoryId(storyId);
|
|
61746
|
-
const worktreePath =
|
|
61858
|
+
const worktreePath = join80(projectRoot, ".nax-wt", storyId);
|
|
61747
61859
|
const branchName = `nax/${storyId}`;
|
|
61748
61860
|
try {
|
|
61749
61861
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -61784,9 +61896,9 @@ ${missing.join(`
|
|
|
61784
61896
|
}
|
|
61785
61897
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
61786
61898
|
}
|
|
61787
|
-
const envSource =
|
|
61899
|
+
const envSource = join80(projectRoot, ".env");
|
|
61788
61900
|
if (existsSync31(envSource)) {
|
|
61789
|
-
const envTarget =
|
|
61901
|
+
const envTarget = join80(worktreePath, ".env");
|
|
61790
61902
|
try {
|
|
61791
61903
|
symlinkSync(envSource, envTarget, "file");
|
|
61792
61904
|
} catch (error48) {
|
|
@@ -61797,7 +61909,7 @@ ${missing.join(`
|
|
|
61797
61909
|
}
|
|
61798
61910
|
async remove(projectRoot, storyId) {
|
|
61799
61911
|
validateStoryId(storyId);
|
|
61800
|
-
const worktreePath =
|
|
61912
|
+
const worktreePath = join80(projectRoot, ".nax-wt", storyId);
|
|
61801
61913
|
const branchName = `nax/${storyId}`;
|
|
61802
61914
|
try {
|
|
61803
61915
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -62609,10 +62721,10 @@ var init_merge_conflict_rectify = __esm(() => {
|
|
|
62609
62721
|
});
|
|
62610
62722
|
|
|
62611
62723
|
// src/execution/pipeline-result-handler.ts
|
|
62612
|
-
import { join as
|
|
62724
|
+
import { join as join81 } from "path";
|
|
62613
62725
|
async function removeWorktreeDirectory(projectRoot, storyId) {
|
|
62614
62726
|
const logger = getSafeLogger();
|
|
62615
|
-
const worktreePath =
|
|
62727
|
+
const worktreePath = join81(projectRoot, ".nax-wt", storyId);
|
|
62616
62728
|
try {
|
|
62617
62729
|
const proc = _resultHandlerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
62618
62730
|
cwd: projectRoot,
|
|
@@ -62829,7 +62941,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
62829
62941
|
|
|
62830
62942
|
// src/execution/iteration-runner.ts
|
|
62831
62943
|
import { existsSync as existsSync32 } from "fs";
|
|
62832
|
-
import { join as
|
|
62944
|
+
import { join as join82 } from "path";
|
|
62833
62945
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
62834
62946
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
62835
62947
|
if (ctx.dryRun) {
|
|
@@ -62854,7 +62966,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62854
62966
|
const storyStartTime = Date.now();
|
|
62855
62967
|
let effectiveWorkdir = ctx.workdir;
|
|
62856
62968
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
62857
|
-
const worktreePath =
|
|
62969
|
+
const worktreePath = join82(ctx.workdir, ".nax-wt", story.id);
|
|
62858
62970
|
const worktreeExists = _iterationRunnerDeps.existsSync(worktreePath);
|
|
62859
62971
|
if (!worktreeExists) {
|
|
62860
62972
|
await _iterationRunnerDeps.worktreeManager.ensureGitExcludes(ctx.workdir);
|
|
@@ -62874,7 +62986,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62874
62986
|
}
|
|
62875
62987
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
62876
62988
|
const profileOverride = ctx.config.profile && ctx.config.profile !== "default" ? { profile: ctx.config.profile } : undefined;
|
|
62877
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
62989
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join82(ctx.workdir, ".nax", "config.json"), story.workdir, profileOverride) : ctx.config;
|
|
62878
62990
|
let dependencyContext;
|
|
62879
62991
|
if (ctx.config.execution.storyIsolation === "worktree") {
|
|
62880
62992
|
try {
|
|
@@ -62901,7 +63013,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
62901
63013
|
};
|
|
62902
63014
|
}
|
|
62903
63015
|
}
|
|
62904
|
-
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ?
|
|
63016
|
+
const resolvedWorkdir = dependencyContext?.cwd ? dependencyContext.cwd : ctx.config.execution.storyIsolation === "worktree" ? story.workdir ? join82(effectiveWorkdir, story.workdir) : effectiveWorkdir : story.workdir ? join82(ctx.workdir, story.workdir) : ctx.workdir;
|
|
62905
63017
|
const pipelineContext = {
|
|
62906
63018
|
config: effectiveConfig,
|
|
62907
63019
|
rootConfig: ctx.config,
|
|
@@ -63103,7 +63215,7 @@ __export(exports_parallel_worker, {
|
|
|
63103
63215
|
buildWorktreePipelineContext: () => buildWorktreePipelineContext,
|
|
63104
63216
|
_parallelWorkerDeps: () => _parallelWorkerDeps
|
|
63105
63217
|
});
|
|
63106
|
-
import { join as
|
|
63218
|
+
import { join as join83 } from "path";
|
|
63107
63219
|
function buildWorktreePipelineContext(base, _story) {
|
|
63108
63220
|
return { ...base, prd: structuredClone(base.prd) };
|
|
63109
63221
|
}
|
|
@@ -63126,7 +63238,7 @@ async function executeStoryInWorktree(story, worktreePath, dependencyContext, co
|
|
|
63126
63238
|
story,
|
|
63127
63239
|
stories: [story],
|
|
63128
63240
|
projectDir: context.projectDir,
|
|
63129
|
-
workdir: dependencyContext.cwd ?? (story.workdir ?
|
|
63241
|
+
workdir: dependencyContext.cwd ?? (story.workdir ? join83(worktreePath, story.workdir) : worktreePath),
|
|
63130
63242
|
worktreeDependencyContext: dependencyContext,
|
|
63131
63243
|
routing,
|
|
63132
63244
|
storyGitRef: storyGitRef ?? undefined
|
|
@@ -64013,7 +64125,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
64013
64125
|
var init_status_file = () => {};
|
|
64014
64126
|
|
|
64015
64127
|
// src/execution/status-writer.ts
|
|
64016
|
-
import { join as
|
|
64128
|
+
import { join as join84 } from "path";
|
|
64017
64129
|
|
|
64018
64130
|
class StatusWriter {
|
|
64019
64131
|
statusFile;
|
|
@@ -64132,7 +64244,7 @@ class StatusWriter {
|
|
|
64132
64244
|
if (!this._prd)
|
|
64133
64245
|
return;
|
|
64134
64246
|
const safeLogger = getSafeLogger();
|
|
64135
|
-
const featureStatusPath =
|
|
64247
|
+
const featureStatusPath = join84(featureDir, "status.json");
|
|
64136
64248
|
const write = async () => {
|
|
64137
64249
|
try {
|
|
64138
64250
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -64566,7 +64678,7 @@ __export(exports_run_initialization, {
|
|
|
64566
64678
|
initializeRun: () => initializeRun,
|
|
64567
64679
|
_reconcileDeps: () => _reconcileDeps
|
|
64568
64680
|
});
|
|
64569
|
-
import { join as
|
|
64681
|
+
import { join as join85 } from "path";
|
|
64570
64682
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
64571
64683
|
const logger = getSafeLogger();
|
|
64572
64684
|
let reconciledCount = 0;
|
|
@@ -64583,7 +64695,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
64583
64695
|
});
|
|
64584
64696
|
continue;
|
|
64585
64697
|
}
|
|
64586
|
-
const effectiveWorkdir = story.workdir ?
|
|
64698
|
+
const effectiveWorkdir = story.workdir ? join85(workdir, story.workdir) : workdir;
|
|
64587
64699
|
try {
|
|
64588
64700
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
64589
64701
|
if (!reviewResult.success) {
|
|
@@ -64849,6 +64961,17 @@ async function setupRun(options) {
|
|
|
64849
64961
|
featureName: options.feature,
|
|
64850
64962
|
agentStreamEvents: options.agentStreamEvents
|
|
64851
64963
|
});
|
|
64964
|
+
try {
|
|
64965
|
+
const workspacePackages = await discoverWorkspacePackages(workdir);
|
|
64966
|
+
if (workspacePackages.length > 0) {
|
|
64967
|
+
await runtime.packages.hydrate(workspacePackages);
|
|
64968
|
+
}
|
|
64969
|
+
} catch (err) {
|
|
64970
|
+
getSafeLogger()?.warn("run-setup", "Per-package config hydration failed \u2014 using root config", {
|
|
64971
|
+
storyId: "_setup",
|
|
64972
|
+
error: errorMessage(err)
|
|
64973
|
+
});
|
|
64974
|
+
}
|
|
64852
64975
|
await runtime.pidRegistry.cleanupStale();
|
|
64853
64976
|
const cleanupCrashHandlers = installCrashHandlers({
|
|
64854
64977
|
statusWriter,
|
|
@@ -65015,6 +65138,7 @@ async function setupRun(options) {
|
|
|
65015
65138
|
var _runSetupDeps;
|
|
65016
65139
|
var init_run_setup = __esm(() => {
|
|
65017
65140
|
init_pipeline();
|
|
65141
|
+
init_test_runners();
|
|
65018
65142
|
init_paths();
|
|
65019
65143
|
init_errors();
|
|
65020
65144
|
init_interaction();
|
|
@@ -65024,7 +65148,6 @@ var init_run_setup = __esm(() => {
|
|
|
65024
65148
|
init_project();
|
|
65025
65149
|
init_runtime();
|
|
65026
65150
|
init_session();
|
|
65027
|
-
init_resolver();
|
|
65028
65151
|
init_version();
|
|
65029
65152
|
init_crash_recovery();
|
|
65030
65153
|
init_helpers();
|
|
@@ -94376,7 +94499,7 @@ __export(exports_curator, {
|
|
|
94376
94499
|
});
|
|
94377
94500
|
import { readdirSync as readdirSync8 } from "fs";
|
|
94378
94501
|
import { unlink as unlink4 } from "fs/promises";
|
|
94379
|
-
import { basename as basename15, join as
|
|
94502
|
+
import { basename as basename15, join as join87 } from "path";
|
|
94380
94503
|
function getProjectKey(config2, projectDir) {
|
|
94381
94504
|
return config2.name?.trim() || basename15(projectDir);
|
|
94382
94505
|
}
|
|
@@ -94459,7 +94582,7 @@ async function curatorStatus(options) {
|
|
|
94459
94582
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94460
94583
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94461
94584
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94462
|
-
const runsDir =
|
|
94585
|
+
const runsDir = join87(outputDir, "runs");
|
|
94463
94586
|
const runIds = listRunIds(runsDir);
|
|
94464
94587
|
let runId;
|
|
94465
94588
|
if (options.run) {
|
|
@@ -94476,8 +94599,8 @@ async function curatorStatus(options) {
|
|
|
94476
94599
|
runId = runIds[runIds.length - 1];
|
|
94477
94600
|
}
|
|
94478
94601
|
console.log(`Run: ${runId}`);
|
|
94479
|
-
const runDir =
|
|
94480
|
-
const observationsPath =
|
|
94602
|
+
const runDir = join87(runsDir, runId);
|
|
94603
|
+
const observationsPath = join87(runDir, "observations.jsonl");
|
|
94481
94604
|
const observations = await parseObservations(observationsPath);
|
|
94482
94605
|
const counts = new Map;
|
|
94483
94606
|
for (const obs of observations) {
|
|
@@ -94487,7 +94610,7 @@ async function curatorStatus(options) {
|
|
|
94487
94610
|
for (const [kind, count] of counts.entries()) {
|
|
94488
94611
|
console.log(` ${kind}: ${count}`);
|
|
94489
94612
|
}
|
|
94490
|
-
const proposalsPath =
|
|
94613
|
+
const proposalsPath = join87(runDir, "curator-proposals.md");
|
|
94491
94614
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
94492
94615
|
if (proposalText !== null) {
|
|
94493
94616
|
console.log("");
|
|
@@ -94501,8 +94624,8 @@ async function curatorCommit(options) {
|
|
|
94501
94624
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94502
94625
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94503
94626
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94504
|
-
const runDir =
|
|
94505
|
-
const proposalsPath =
|
|
94627
|
+
const runDir = join87(outputDir, "runs", options.runId);
|
|
94628
|
+
const proposalsPath = join87(runDir, "curator-proposals.md");
|
|
94506
94629
|
const proposalText = await _curatorCmdDeps.readFile(proposalsPath).catch(() => null);
|
|
94507
94630
|
if (proposalText === null) {
|
|
94508
94631
|
console.log(`curator-proposals.md not found for run ${options.runId}.`);
|
|
@@ -94518,7 +94641,7 @@ async function curatorCommit(options) {
|
|
|
94518
94641
|
const dropFileState = new Map;
|
|
94519
94642
|
const skippedDrops = new Set;
|
|
94520
94643
|
for (const drop2 of drops) {
|
|
94521
|
-
const targetPath =
|
|
94644
|
+
const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
|
|
94522
94645
|
if (!dropFileState.has(targetPath)) {
|
|
94523
94646
|
const fileExists2 = await Bun.file(targetPath).exists();
|
|
94524
94647
|
const existing = fileExists2 ? await _curatorCmdDeps.readFile(targetPath).catch(() => "") : "";
|
|
@@ -94552,7 +94675,7 @@ async function curatorCommit(options) {
|
|
|
94552
94675
|
if (skippedDrops.has(drop2)) {
|
|
94553
94676
|
continue;
|
|
94554
94677
|
}
|
|
94555
|
-
const targetPath =
|
|
94678
|
+
const targetPath = join87(resolved.projectDir, drop2.canonicalFile);
|
|
94556
94679
|
const existing = await _curatorCmdDeps.readFile(targetPath).catch(() => "");
|
|
94557
94680
|
const filtered = filterDropContent(existing, drop2.description);
|
|
94558
94681
|
await _curatorCmdDeps.writeFile(targetPath, filtered);
|
|
@@ -94561,7 +94684,7 @@ async function curatorCommit(options) {
|
|
|
94561
94684
|
}
|
|
94562
94685
|
const adds = proposals.filter((p) => p.action === "add" || p.action === "advisory");
|
|
94563
94686
|
for (const add2 of adds) {
|
|
94564
|
-
const targetPath =
|
|
94687
|
+
const targetPath = join87(resolved.projectDir, add2.canonicalFile);
|
|
94565
94688
|
const content = buildAddContent(add2);
|
|
94566
94689
|
await _curatorCmdDeps.appendFile(targetPath, content);
|
|
94567
94690
|
modifiedFiles.add(targetPath);
|
|
@@ -94598,7 +94721,7 @@ async function curatorDryrun(options) {
|
|
|
94598
94721
|
const config2 = await _curatorCmdDeps.loadConfig(resolved.projectDir);
|
|
94599
94722
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94600
94723
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94601
|
-
const runsDir =
|
|
94724
|
+
const runsDir = join87(outputDir, "runs");
|
|
94602
94725
|
const runIds = listRunIds(runsDir);
|
|
94603
94726
|
if (runIds.length === 0) {
|
|
94604
94727
|
console.log("No runs found.");
|
|
@@ -94609,7 +94732,7 @@ async function curatorDryrun(options) {
|
|
|
94609
94732
|
console.log(`Run ${options.run} not found in ${runsDir}.`);
|
|
94610
94733
|
return;
|
|
94611
94734
|
}
|
|
94612
|
-
const observationsPath =
|
|
94735
|
+
const observationsPath = join87(runsDir, runId, "observations.jsonl");
|
|
94613
94736
|
const observations = await parseObservations(observationsPath);
|
|
94614
94737
|
const thresholds = getThresholds(config2);
|
|
94615
94738
|
const proposals = runHeuristics(observations, thresholds);
|
|
@@ -94650,12 +94773,12 @@ async function curatorGc(options) {
|
|
|
94650
94773
|
await _curatorCmdDeps.writeFile(rollupPath, newContent);
|
|
94651
94774
|
const projectKey = getProjectKey(config2, resolved.projectDir);
|
|
94652
94775
|
const outputDir = _curatorCmdDeps.projectOutputDir(projectKey, config2.outputDir);
|
|
94653
|
-
const perRunsDir =
|
|
94776
|
+
const perRunsDir = join87(outputDir, "runs");
|
|
94654
94777
|
for (const runId of uniqueRunIds) {
|
|
94655
94778
|
if (!keepSet.has(runId)) {
|
|
94656
|
-
const runDir =
|
|
94657
|
-
await _curatorCmdDeps.removeFile(
|
|
94658
|
-
await _curatorCmdDeps.removeFile(
|
|
94779
|
+
const runDir = join87(perRunsDir, runId);
|
|
94780
|
+
await _curatorCmdDeps.removeFile(join87(runDir, "observations.jsonl"));
|
|
94781
|
+
await _curatorCmdDeps.removeFile(join87(runDir, "curator-proposals.md"));
|
|
94659
94782
|
}
|
|
94660
94783
|
}
|
|
94661
94784
|
console.log(`[gc] Pruned rollup to ${keep} most recent runs (was ${uniqueRunIds.length}).`);
|
|
@@ -94700,7 +94823,7 @@ var init_curator2 = __esm(() => {
|
|
|
94700
94823
|
init_source();
|
|
94701
94824
|
import { existsSync as existsSync35, mkdirSync as mkdirSync7 } from "fs";
|
|
94702
94825
|
import { homedir as homedir3 } from "os";
|
|
94703
|
-
import { basename as basename16, join as
|
|
94826
|
+
import { basename as basename16, join as join88 } from "path";
|
|
94704
94827
|
|
|
94705
94828
|
// node_modules/commander/esm.mjs
|
|
94706
94829
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -94724,12 +94847,12 @@ init_errors();
|
|
|
94724
94847
|
init_operations();
|
|
94725
94848
|
|
|
94726
94849
|
// src/plan/strategies/context-builder.ts
|
|
94727
|
-
import { join as
|
|
94850
|
+
import { join as join39 } from "path";
|
|
94728
94851
|
init_config();
|
|
94729
94852
|
init_errors();
|
|
94730
94853
|
init_interaction();
|
|
94731
94854
|
async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
94732
|
-
const naxDir =
|
|
94855
|
+
const naxDir = join39(workdir, ".nax");
|
|
94733
94856
|
if (!deps.existsSync(naxDir)) {
|
|
94734
94857
|
throw new NaxError(`.nax directory not found. Run 'nax init' first in ${workdir}`, "PLAN_CONTEXT_NO_NAX_DIR", {
|
|
94735
94858
|
stage: "plan",
|
|
@@ -94737,8 +94860,8 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
94737
94860
|
});
|
|
94738
94861
|
}
|
|
94739
94862
|
validateFeatureName(options.feature);
|
|
94740
|
-
const outputDir =
|
|
94741
|
-
const outputPath =
|
|
94863
|
+
const outputDir = join39(naxDir, "features", options.feature);
|
|
94864
|
+
const outputPath = join39(outputDir, "prd.json");
|
|
94742
94865
|
const [specContent, sourceRoots, pkg] = await Promise.all([
|
|
94743
94866
|
deps.readFile(options.from),
|
|
94744
94867
|
deps.scanSourceRoots(workdir),
|
|
@@ -94753,7 +94876,7 @@ async function buildPlanModeContext(workdir, fullConfig, options, deps) {
|
|
|
94753
94876
|
...new Set(sourceRoots.map((root) => root.path).filter((path7) => path7 !== ".").map((path7) => path7.startsWith("/") ? path7.replace(`${workdir}/`, "") : path7))
|
|
94754
94877
|
];
|
|
94755
94878
|
const packageDetails = relativePackages.length === 0 ? [] : await Promise.all(relativePackages.map(async (relativePath) => {
|
|
94756
|
-
const packageJson = await deps.readPackageJsonAt(
|
|
94879
|
+
const packageJson = await deps.readPackageJsonAt(join39(workdir, relativePath, "package.json"));
|
|
94757
94880
|
return buildPackageSummary(relativePath, packageJson);
|
|
94758
94881
|
}));
|
|
94759
94882
|
const projectName = detectProjectName(workdir, pkg);
|
|
@@ -95356,7 +95479,7 @@ init_interaction();
|
|
|
95356
95479
|
init_prd();
|
|
95357
95480
|
init_runtime();
|
|
95358
95481
|
import { existsSync as existsSync17, readdirSync as readdirSync3 } from "fs";
|
|
95359
|
-
import { basename as basename7, join as
|
|
95482
|
+
import { basename as basename7, join as join44, resolve as resolve14 } from "path";
|
|
95360
95483
|
var _statusFeaturesDeps = {
|
|
95361
95484
|
projectOutputDir,
|
|
95362
95485
|
loadConfig
|
|
@@ -95370,7 +95493,7 @@ function isPidAlive(pid) {
|
|
|
95370
95493
|
}
|
|
95371
95494
|
}
|
|
95372
95495
|
async function loadStatusFile(featureDir) {
|
|
95373
|
-
const statusPath =
|
|
95496
|
+
const statusPath = join44(featureDir, "status.json");
|
|
95374
95497
|
if (!existsSync17(statusPath)) {
|
|
95375
95498
|
return null;
|
|
95376
95499
|
}
|
|
@@ -95385,7 +95508,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
95385
95508
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95386
95509
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95387
95510
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95388
|
-
const statusPath =
|
|
95511
|
+
const statusPath = join44(outputDir, "status.json");
|
|
95389
95512
|
if (!existsSync17(statusPath)) {
|
|
95390
95513
|
return null;
|
|
95391
95514
|
}
|
|
@@ -95397,7 +95520,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
95397
95520
|
}
|
|
95398
95521
|
}
|
|
95399
95522
|
async function getFeatureSummary(featureName, featureDir) {
|
|
95400
|
-
const prdPath =
|
|
95523
|
+
const prdPath = join44(featureDir, "prd.json");
|
|
95401
95524
|
if (!existsSync17(prdPath)) {
|
|
95402
95525
|
return {
|
|
95403
95526
|
name: featureName,
|
|
@@ -95440,7 +95563,7 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
95440
95563
|
};
|
|
95441
95564
|
}
|
|
95442
95565
|
}
|
|
95443
|
-
const runsDir =
|
|
95566
|
+
const runsDir = join44(featureDir, "runs");
|
|
95444
95567
|
if (existsSync17(runsDir)) {
|
|
95445
95568
|
const runs = readdirSync3(runsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl") && e.name !== "latest.jsonl").map((e) => e.name).sort().reverse();
|
|
95446
95569
|
if (runs.length > 0) {
|
|
@@ -95454,7 +95577,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95454
95577
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95455
95578
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95456
95579
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95457
|
-
const featuresDir =
|
|
95580
|
+
const featuresDir = join44(outputDir, "features");
|
|
95458
95581
|
if (!existsSync17(featuresDir)) {
|
|
95459
95582
|
console.log(source_default.dim("No features found."));
|
|
95460
95583
|
return;
|
|
@@ -95495,7 +95618,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95495
95618
|
console.log();
|
|
95496
95619
|
}
|
|
95497
95620
|
}
|
|
95498
|
-
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name,
|
|
95621
|
+
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join44(featuresDir, name))));
|
|
95499
95622
|
console.log(source_default.bold(`\uD83D\uDCCA Features
|
|
95500
95623
|
`));
|
|
95501
95624
|
const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
|
|
@@ -95521,7 +95644,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
95521
95644
|
console.log();
|
|
95522
95645
|
}
|
|
95523
95646
|
async function displayFeatureDetails(featureName, featureDir) {
|
|
95524
|
-
const prdPath =
|
|
95647
|
+
const prdPath = join44(featureDir, "prd.json");
|
|
95525
95648
|
if (!existsSync17(prdPath)) {
|
|
95526
95649
|
console.log(source_default.bold(`
|
|
95527
95650
|
\uD83D\uDCCA ${featureName}
|
|
@@ -95667,7 +95790,7 @@ async function displayFeatureStatus(options = {}) {
|
|
|
95667
95790
|
const config2 = await _statusFeaturesDeps.loadConfig(projectDir).catch(() => null);
|
|
95668
95791
|
const projectKey = config2?.name?.trim() || basename7(projectDir);
|
|
95669
95792
|
const outputDir = _statusFeaturesDeps.projectOutputDir(projectKey, config2?.outputDir);
|
|
95670
|
-
featureDir =
|
|
95793
|
+
featureDir = join44(outputDir, "features", options.feature);
|
|
95671
95794
|
} else {
|
|
95672
95795
|
const resolved = resolveProject({ feature: options.feature });
|
|
95673
95796
|
if (!resolved.featureDir) {
|
|
@@ -95687,7 +95810,7 @@ init_errors();
|
|
|
95687
95810
|
init_logger2();
|
|
95688
95811
|
init_runtime();
|
|
95689
95812
|
import { existsSync as existsSync18, readdirSync as readdirSync4 } from "fs";
|
|
95690
|
-
import { basename as basename8, join as
|
|
95813
|
+
import { basename as basename8, join as join45 } from "path";
|
|
95691
95814
|
async function resolveOutputDir2(workdir, override) {
|
|
95692
95815
|
if (override)
|
|
95693
95816
|
return override;
|
|
@@ -95711,7 +95834,7 @@ async function runsListCommand(options) {
|
|
|
95711
95834
|
const logger = getLogger();
|
|
95712
95835
|
const { feature, workdir } = options;
|
|
95713
95836
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
95714
|
-
const runsDir =
|
|
95837
|
+
const runsDir = join45(outputDir, "features", feature, "runs");
|
|
95715
95838
|
if (!existsSync18(runsDir)) {
|
|
95716
95839
|
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
95717
95840
|
return;
|
|
@@ -95723,7 +95846,7 @@ async function runsListCommand(options) {
|
|
|
95723
95846
|
}
|
|
95724
95847
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
95725
95848
|
for (const file3 of files.sort().reverse()) {
|
|
95726
|
-
const logPath =
|
|
95849
|
+
const logPath = join45(runsDir, file3);
|
|
95727
95850
|
const entries = await parseRunLog(logPath);
|
|
95728
95851
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
95729
95852
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
@@ -95750,7 +95873,7 @@ async function runsShowCommand(options) {
|
|
|
95750
95873
|
const logger = getLogger();
|
|
95751
95874
|
const { runId, feature, workdir } = options;
|
|
95752
95875
|
const outputDir = await resolveOutputDir2(workdir, options.outputDir);
|
|
95753
|
-
const logPath =
|
|
95876
|
+
const logPath = join45(outputDir, "features", feature, "runs", `${runId}.jsonl`);
|
|
95754
95877
|
if (!existsSync18(logPath)) {
|
|
95755
95878
|
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
95756
95879
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
@@ -95866,7 +95989,7 @@ init_source();
|
|
|
95866
95989
|
init_loader();
|
|
95867
95990
|
init_generator2();
|
|
95868
95991
|
import { existsSync as existsSync24 } from "fs";
|
|
95869
|
-
import { join as
|
|
95992
|
+
import { join as join62 } from "path";
|
|
95870
95993
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
95871
95994
|
async function generateCommand(options) {
|
|
95872
95995
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -95909,7 +96032,7 @@ async function generateCommand(options) {
|
|
|
95909
96032
|
return;
|
|
95910
96033
|
}
|
|
95911
96034
|
if (options.package) {
|
|
95912
|
-
const packageDir =
|
|
96035
|
+
const packageDir = join62(workdir, options.package);
|
|
95913
96036
|
if (dryRun) {
|
|
95914
96037
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
95915
96038
|
}
|
|
@@ -95929,8 +96052,8 @@ async function generateCommand(options) {
|
|
|
95929
96052
|
process.exit(1);
|
|
95930
96053
|
return;
|
|
95931
96054
|
}
|
|
95932
|
-
const contextPath = options.context ?
|
|
95933
|
-
const outputDir = options.output ?
|
|
96055
|
+
const contextPath = options.context ? join62(workdir, options.context) : join62(workdir, ".nax/context.md");
|
|
96056
|
+
const outputDir = options.output ? join62(workdir, options.output) : workdir;
|
|
95934
96057
|
const autoInject = !options.noAutoInject;
|
|
95935
96058
|
if (!existsSync24(contextPath)) {
|
|
95936
96059
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -96036,7 +96159,7 @@ async function generateCommand(options) {
|
|
|
96036
96159
|
// src/cli/config-display.ts
|
|
96037
96160
|
init_loader();
|
|
96038
96161
|
import { existsSync as existsSync26 } from "fs";
|
|
96039
|
-
import { join as
|
|
96162
|
+
import { join as join64 } from "path";
|
|
96040
96163
|
|
|
96041
96164
|
// src/cli/config-descriptions.ts
|
|
96042
96165
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -96288,7 +96411,7 @@ function deepEqual(a, b) {
|
|
|
96288
96411
|
init_defaults();
|
|
96289
96412
|
init_loader();
|
|
96290
96413
|
import { existsSync as existsSync25 } from "fs";
|
|
96291
|
-
import { join as
|
|
96414
|
+
import { join as join63 } from "path";
|
|
96292
96415
|
async function loadConfigFile(path18) {
|
|
96293
96416
|
if (!existsSync25(path18))
|
|
96294
96417
|
return null;
|
|
@@ -96310,7 +96433,7 @@ async function loadProjectConfig() {
|
|
|
96310
96433
|
const projectDir = findProjectDir();
|
|
96311
96434
|
if (!projectDir)
|
|
96312
96435
|
return null;
|
|
96313
|
-
const projectPath =
|
|
96436
|
+
const projectPath = join63(projectDir, "config.json");
|
|
96314
96437
|
return await loadConfigFile(projectPath);
|
|
96315
96438
|
}
|
|
96316
96439
|
|
|
@@ -96370,7 +96493,7 @@ async function configCommand(config2, options = {}) {
|
|
|
96370
96493
|
function determineConfigSources() {
|
|
96371
96494
|
const globalPath = globalConfigPath();
|
|
96372
96495
|
const projectDir = findProjectDir();
|
|
96373
|
-
const projectPath = projectDir ?
|
|
96496
|
+
const projectPath = projectDir ? join64(projectDir, "config.json") : null;
|
|
96374
96497
|
return {
|
|
96375
96498
|
global: fileExists(globalPath) ? globalPath : null,
|
|
96376
96499
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -96519,15 +96642,15 @@ init_paths();
|
|
|
96519
96642
|
init_profile();
|
|
96520
96643
|
import { mkdirSync as mkdirSync5 } from "fs";
|
|
96521
96644
|
import { readdirSync as readdirSync5 } from "fs";
|
|
96522
|
-
import { join as
|
|
96645
|
+
import { join as join65 } from "path";
|
|
96523
96646
|
var _profileCLIDeps = {
|
|
96524
96647
|
env: process.env
|
|
96525
96648
|
};
|
|
96526
96649
|
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
96527
96650
|
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
96528
96651
|
async function profileListCommand(startDir) {
|
|
96529
|
-
const globalProfilesDir =
|
|
96530
|
-
const projectProfilesDir =
|
|
96652
|
+
const globalProfilesDir = join65(globalConfigDir(), "profiles");
|
|
96653
|
+
const projectProfilesDir = join65(projectConfigDir(startDir), "profiles");
|
|
96531
96654
|
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
96532
96655
|
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
96533
96656
|
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
@@ -96586,7 +96709,7 @@ function maskProfileValues(obj) {
|
|
|
96586
96709
|
return result;
|
|
96587
96710
|
}
|
|
96588
96711
|
async function profileUseCommand(profileName, startDir) {
|
|
96589
|
-
const configPath =
|
|
96712
|
+
const configPath = join65(projectConfigDir(startDir), "config.json");
|
|
96590
96713
|
const configFile = Bun.file(configPath);
|
|
96591
96714
|
let existing = {};
|
|
96592
96715
|
if (await configFile.exists()) {
|
|
@@ -96605,8 +96728,8 @@ async function profileCurrentCommand(startDir) {
|
|
|
96605
96728
|
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
96606
96729
|
}
|
|
96607
96730
|
async function profileCreateCommand(profileName, startDir) {
|
|
96608
|
-
const profilesDir =
|
|
96609
|
-
const profilePath =
|
|
96731
|
+
const profilesDir = join65(projectConfigDir(startDir), "profiles");
|
|
96732
|
+
const profilePath = join65(profilesDir, `${profileName}.json`);
|
|
96610
96733
|
const profileFile = Bun.file(profilePath);
|
|
96611
96734
|
if (await profileFile.exists()) {
|
|
96612
96735
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
@@ -96728,7 +96851,7 @@ async function contextInspectCommand(options) {
|
|
|
96728
96851
|
init_canonical_loader();
|
|
96729
96852
|
init_errors();
|
|
96730
96853
|
import { mkdir as mkdir11 } from "fs/promises";
|
|
96731
|
-
import { basename as basename12, join as
|
|
96854
|
+
import { basename as basename12, join as join66 } from "path";
|
|
96732
96855
|
var _rulesCLIDeps = {
|
|
96733
96856
|
readFile: async (path18) => Bun.file(path18).text(),
|
|
96734
96857
|
writeFile: async (path18, content) => {
|
|
@@ -96737,7 +96860,7 @@ var _rulesCLIDeps = {
|
|
|
96737
96860
|
fileExists: async (path18) => Bun.file(path18).exists(),
|
|
96738
96861
|
globInDir: (dir) => {
|
|
96739
96862
|
try {
|
|
96740
|
-
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) =>
|
|
96863
|
+
return [...new Bun.Glob("*.md").scanSync({ cwd: dir })].sort().map((f) => join66(dir, f));
|
|
96741
96864
|
} catch {
|
|
96742
96865
|
return [];
|
|
96743
96866
|
}
|
|
@@ -96786,7 +96909,7 @@ ${r.content}`).join(`
|
|
|
96786
96909
|
`);
|
|
96787
96910
|
const shimContent = `${header + body}
|
|
96788
96911
|
`;
|
|
96789
|
-
const shimPath =
|
|
96912
|
+
const shimPath = join66(workdir, shimFileName);
|
|
96790
96913
|
if (options.dryRun) {
|
|
96791
96914
|
console.log(`[dry-run] Would write ${shimPath} (${shimContent.length} bytes)`);
|
|
96792
96915
|
return;
|
|
@@ -96815,14 +96938,14 @@ function neutralizeContent(content) {
|
|
|
96815
96938
|
}
|
|
96816
96939
|
async function collectMigrationSources(workdir) {
|
|
96817
96940
|
const sources = [];
|
|
96818
|
-
const claudeMdPath =
|
|
96941
|
+
const claudeMdPath = join66(workdir, "CLAUDE.md");
|
|
96819
96942
|
if (await _rulesCLIDeps.fileExists(claudeMdPath)) {
|
|
96820
96943
|
const content = await _rulesCLIDeps.readFile(claudeMdPath);
|
|
96821
96944
|
if (content.trim()) {
|
|
96822
96945
|
sources.push({ sourcePath: claudeMdPath, targetFileName: "project-conventions.md", content });
|
|
96823
96946
|
}
|
|
96824
96947
|
}
|
|
96825
|
-
const rulesDir =
|
|
96948
|
+
const rulesDir = join66(workdir, ".claude", "rules");
|
|
96826
96949
|
const ruleFiles = _rulesCLIDeps.globInDir(rulesDir);
|
|
96827
96950
|
for (const filePath of ruleFiles) {
|
|
96828
96951
|
try {
|
|
@@ -96842,7 +96965,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96842
96965
|
console.log("[WARN] No source files found (checked CLAUDE.md and .claude/rules/*.md). Nothing to migrate.");
|
|
96843
96966
|
return;
|
|
96844
96967
|
}
|
|
96845
|
-
const targetDir =
|
|
96968
|
+
const targetDir = join66(workdir, CANONICAL_RULES_DIR);
|
|
96846
96969
|
if (!options.dryRun) {
|
|
96847
96970
|
try {
|
|
96848
96971
|
await _rulesCLIDeps.mkdir(targetDir);
|
|
@@ -96853,7 +96976,7 @@ async function rulesMigrateCommand(options) {
|
|
|
96853
96976
|
let written = 0;
|
|
96854
96977
|
let skipped = 0;
|
|
96855
96978
|
for (const { sourcePath, targetFileName, content } of sources) {
|
|
96856
|
-
const targetPath =
|
|
96979
|
+
const targetPath = join66(targetDir, targetFileName);
|
|
96857
96980
|
if (!force && !options.dryRun && await _rulesCLIDeps.fileExists(targetPath)) {
|
|
96858
96981
|
console.log(`[skip] ${targetFileName} already exists (use --force to overwrite)`);
|
|
96859
96982
|
skipped++;
|
|
@@ -96892,7 +97015,7 @@ function collectCanonicalRuleRoots(workdir) {
|
|
|
96892
97015
|
const packageRel = normalized.slice(0, idx);
|
|
96893
97016
|
if (!packageRel)
|
|
96894
97017
|
continue;
|
|
96895
|
-
roots.add(
|
|
97018
|
+
roots.add(join66(workdir, packageRel));
|
|
96896
97019
|
}
|
|
96897
97020
|
return [...roots].sort();
|
|
96898
97021
|
}
|
|
@@ -96914,7 +97037,7 @@ init_logger2();
|
|
|
96914
97037
|
init_detect2();
|
|
96915
97038
|
init_workspace();
|
|
96916
97039
|
init_common();
|
|
96917
|
-
import { join as
|
|
97040
|
+
import { join as join67 } from "path";
|
|
96918
97041
|
function resolveEffective(detected, configPatterns) {
|
|
96919
97042
|
if (configPatterns !== undefined)
|
|
96920
97043
|
return "config";
|
|
@@ -96999,7 +97122,7 @@ async function detectCommand(options) {
|
|
|
96999
97122
|
const rootDetected = detectionMap[""] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
97000
97123
|
const pkgEntries = await Promise.all(packageDirs.map(async (dir) => {
|
|
97001
97124
|
const det = detectionMap[dir] ?? { patterns: [], confidence: "empty", sources: [] };
|
|
97002
|
-
const pkgConfigPath =
|
|
97125
|
+
const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
|
|
97003
97126
|
const pkgRaw = await loadRawConfig(pkgConfigPath);
|
|
97004
97127
|
const pkgPatterns = deepGet(pkgRaw, TEST_PATTERNS_KEY);
|
|
97005
97128
|
const effective = Array.isArray(pkgPatterns) ? pkgPatterns : undefined;
|
|
@@ -97053,13 +97176,13 @@ async function detectCommand(options) {
|
|
|
97053
97176
|
if (rootDetected.confidence === "empty") {
|
|
97054
97177
|
console.log(source_default.yellow(" root: skipped (empty detection)"));
|
|
97055
97178
|
} else {
|
|
97056
|
-
const rootConfigPath =
|
|
97179
|
+
const rootConfigPath = join67(workdir, ".nax", "config.json");
|
|
97057
97180
|
try {
|
|
97058
97181
|
const status = await applyToConfig(rootConfigPath, rootDetected.patterns, options.force ?? false);
|
|
97059
97182
|
if (status === "skipped") {
|
|
97060
97183
|
console.log(source_default.dim(" root: skipped (testFilePatterns already set; use --force to overwrite)"));
|
|
97061
97184
|
} else {
|
|
97062
|
-
console.log(source_default.green(` root: ${status} \u2192 ${
|
|
97185
|
+
console.log(source_default.green(` root: ${status} \u2192 ${join67(".nax", "config.json")}`));
|
|
97063
97186
|
}
|
|
97064
97187
|
} catch (err) {
|
|
97065
97188
|
console.error(source_default.red(` root: write failed \u2014 ${err.message}`));
|
|
@@ -97072,13 +97195,13 @@ async function detectCommand(options) {
|
|
|
97072
97195
|
console.log(source_default.dim(` ${dir}: skipped (empty detection)`));
|
|
97073
97196
|
continue;
|
|
97074
97197
|
}
|
|
97075
|
-
const pkgConfigPath =
|
|
97198
|
+
const pkgConfigPath = join67(workdir, ".nax", "mono", dir, "config.json");
|
|
97076
97199
|
try {
|
|
97077
97200
|
const status = await applyToConfig(pkgConfigPath, det.patterns, options.force ?? false);
|
|
97078
97201
|
if (status === "skipped") {
|
|
97079
97202
|
console.log(source_default.dim(` ${dir}: skipped (already set)`));
|
|
97080
97203
|
} else {
|
|
97081
|
-
console.log(source_default.green(` ${dir}: ${status} \u2192 ${
|
|
97204
|
+
console.log(source_default.green(` ${dir}: ${status} \u2192 ${join67(".nax", "mono", dir, "config.json")}`));
|
|
97082
97205
|
}
|
|
97083
97206
|
} catch (err) {
|
|
97084
97207
|
console.error(source_default.red(` ${dir}: write failed \u2014 ${err.message}`));
|
|
@@ -97096,19 +97219,19 @@ async function detectCommand(options) {
|
|
|
97096
97219
|
// src/commands/logs.ts
|
|
97097
97220
|
init_common();
|
|
97098
97221
|
import { existsSync as existsSync28 } from "fs";
|
|
97099
|
-
import { join as
|
|
97222
|
+
import { join as join71 } from "path";
|
|
97100
97223
|
|
|
97101
97224
|
// src/commands/logs-formatter.ts
|
|
97102
97225
|
init_source();
|
|
97103
97226
|
init_formatter();
|
|
97104
97227
|
import { readdirSync as readdirSync7 } from "fs";
|
|
97105
|
-
import { join as
|
|
97228
|
+
import { join as join70 } from "path";
|
|
97106
97229
|
|
|
97107
97230
|
// src/commands/logs-reader.ts
|
|
97108
97231
|
init_paths3();
|
|
97109
97232
|
import { existsSync as existsSync27, readdirSync as readdirSync6 } from "fs";
|
|
97110
97233
|
import { readdir as readdir4 } from "fs/promises";
|
|
97111
|
-
import { join as
|
|
97234
|
+
import { join as join69 } from "path";
|
|
97112
97235
|
var _logsReaderDeps = {
|
|
97113
97236
|
getRunsDir
|
|
97114
97237
|
};
|
|
@@ -97122,7 +97245,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
97122
97245
|
}
|
|
97123
97246
|
let matched = null;
|
|
97124
97247
|
for (const entry of entries) {
|
|
97125
|
-
const metaPath =
|
|
97248
|
+
const metaPath = join69(runsDir, entry, "meta.json");
|
|
97126
97249
|
try {
|
|
97127
97250
|
const meta3 = await Bun.file(metaPath).json();
|
|
97128
97251
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -97144,14 +97267,14 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
97144
97267
|
return null;
|
|
97145
97268
|
}
|
|
97146
97269
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
97147
|
-
return
|
|
97270
|
+
return join69(matched.eventsDir, specificFile ?? files[0]);
|
|
97148
97271
|
}
|
|
97149
97272
|
async function selectRunFile(runsDir) {
|
|
97150
97273
|
const files = readdirSync6(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
97151
97274
|
if (files.length === 0) {
|
|
97152
97275
|
return null;
|
|
97153
97276
|
}
|
|
97154
|
-
return
|
|
97277
|
+
return join69(runsDir, files[0]);
|
|
97155
97278
|
}
|
|
97156
97279
|
async function extractRunSummary(filePath) {
|
|
97157
97280
|
const file3 = Bun.file(filePath);
|
|
@@ -97237,7 +97360,7 @@ Runs:
|
|
|
97237
97360
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
97238
97361
|
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"));
|
|
97239
97362
|
for (const file3 of files) {
|
|
97240
|
-
const filePath =
|
|
97363
|
+
const filePath = join70(runsDir, file3);
|
|
97241
97364
|
const summary = await extractRunSummary(filePath);
|
|
97242
97365
|
const timestamp = file3.replace(".jsonl", "");
|
|
97243
97366
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -97351,7 +97474,7 @@ async function logsCommand(options) {
|
|
|
97351
97474
|
return;
|
|
97352
97475
|
}
|
|
97353
97476
|
const resolved = resolveProject({ dir: options.dir });
|
|
97354
|
-
const naxDir =
|
|
97477
|
+
const naxDir = join71(resolved.projectDir, ".nax");
|
|
97355
97478
|
const configPath = resolved.configPath;
|
|
97356
97479
|
const configFile = Bun.file(configPath);
|
|
97357
97480
|
const config2 = await configFile.json();
|
|
@@ -97359,8 +97482,8 @@ async function logsCommand(options) {
|
|
|
97359
97482
|
if (!featureName) {
|
|
97360
97483
|
throw new Error("No feature specified in config.json");
|
|
97361
97484
|
}
|
|
97362
|
-
const featureDir =
|
|
97363
|
-
const runsDir =
|
|
97485
|
+
const featureDir = join71(naxDir, "features", featureName);
|
|
97486
|
+
const runsDir = join71(featureDir, "runs");
|
|
97364
97487
|
if (!existsSync28(runsDir)) {
|
|
97365
97488
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
97366
97489
|
}
|
|
@@ -97386,7 +97509,7 @@ init_prd();
|
|
|
97386
97509
|
init_precheck();
|
|
97387
97510
|
init_common();
|
|
97388
97511
|
import { existsSync as existsSync29 } from "fs";
|
|
97389
|
-
import { join as
|
|
97512
|
+
import { join as join72 } from "path";
|
|
97390
97513
|
async function precheckCommand(options) {
|
|
97391
97514
|
const resolved = resolveProject({
|
|
97392
97515
|
dir: options.dir,
|
|
@@ -97408,9 +97531,9 @@ async function precheckCommand(options) {
|
|
|
97408
97531
|
process.exit(1);
|
|
97409
97532
|
}
|
|
97410
97533
|
}
|
|
97411
|
-
const naxDir =
|
|
97412
|
-
const featureDir =
|
|
97413
|
-
const prdPath =
|
|
97534
|
+
const naxDir = join72(resolved.projectDir, ".nax");
|
|
97535
|
+
const featureDir = join72(naxDir, "features", featureName);
|
|
97536
|
+
const prdPath = join72(featureDir, "prd.json");
|
|
97414
97537
|
if (!existsSync29(featureDir)) {
|
|
97415
97538
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
97416
97539
|
process.exit(1);
|
|
@@ -97433,7 +97556,7 @@ async function precheckCommand(options) {
|
|
|
97433
97556
|
init_source();
|
|
97434
97557
|
init_paths3();
|
|
97435
97558
|
import { readdir as readdir5 } from "fs/promises";
|
|
97436
|
-
import { join as
|
|
97559
|
+
import { join as join73 } from "path";
|
|
97437
97560
|
var DEFAULT_LIMIT = 20;
|
|
97438
97561
|
var _runsCmdDeps = {
|
|
97439
97562
|
getRunsDir
|
|
@@ -97488,7 +97611,7 @@ async function runsCommand(options = {}) {
|
|
|
97488
97611
|
}
|
|
97489
97612
|
const rows = [];
|
|
97490
97613
|
for (const entry of entries) {
|
|
97491
|
-
const metaPath =
|
|
97614
|
+
const metaPath = join73(runsDir, entry, "meta.json");
|
|
97492
97615
|
let meta3;
|
|
97493
97616
|
try {
|
|
97494
97617
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -97565,7 +97688,7 @@ async function runsCommand(options = {}) {
|
|
|
97565
97688
|
|
|
97566
97689
|
// src/commands/unlock.ts
|
|
97567
97690
|
init_source();
|
|
97568
|
-
import { join as
|
|
97691
|
+
import { join as join74 } from "path";
|
|
97569
97692
|
function isProcessAlive2(pid) {
|
|
97570
97693
|
try {
|
|
97571
97694
|
process.kill(pid, 0);
|
|
@@ -97580,7 +97703,7 @@ function formatLockAge(ageMs) {
|
|
|
97580
97703
|
}
|
|
97581
97704
|
async function unlockCommand(options) {
|
|
97582
97705
|
const workdir = options.dir ?? process.cwd();
|
|
97583
|
-
const lockPath =
|
|
97706
|
+
const lockPath = join74(workdir, "nax.lock");
|
|
97584
97707
|
const lockFile = Bun.file(lockPath);
|
|
97585
97708
|
const exists = await lockFile.exists();
|
|
97586
97709
|
if (!exists) {
|
|
@@ -98128,6 +98251,7 @@ init_run_regression();
|
|
|
98128
98251
|
|
|
98129
98252
|
// src/execution/index.ts
|
|
98130
98253
|
init_story_orchestrator();
|
|
98254
|
+
init_story_orchestrator_logging();
|
|
98131
98255
|
init_plan_inputs();
|
|
98132
98256
|
init_build_plan_for_strategy();
|
|
98133
98257
|
init_post_run();
|
|
@@ -106004,7 +106128,7 @@ Next: nax generate --package ${options.package}`));
|
|
|
106004
106128
|
}
|
|
106005
106129
|
return;
|
|
106006
106130
|
}
|
|
106007
|
-
const naxDir =
|
|
106131
|
+
const naxDir = join88(workdir, ".nax");
|
|
106008
106132
|
if (existsSync35(naxDir) && !options.force) {
|
|
106009
106133
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
106010
106134
|
return;
|
|
@@ -106033,11 +106157,11 @@ Next: nax generate --package ${options.package}`));
|
|
|
106033
106157
|
}
|
|
106034
106158
|
}
|
|
106035
106159
|
}
|
|
106036
|
-
mkdirSync7(
|
|
106037
|
-
mkdirSync7(
|
|
106160
|
+
mkdirSync7(join88(naxDir, "features"), { recursive: true });
|
|
106161
|
+
mkdirSync7(join88(naxDir, "hooks"), { recursive: true });
|
|
106038
106162
|
const initConfig = options.name ? { ...DEFAULT_CONFIG, name: options.name } : DEFAULT_CONFIG;
|
|
106039
|
-
await Bun.write(
|
|
106040
|
-
await Bun.write(
|
|
106163
|
+
await Bun.write(join88(naxDir, "config.json"), JSON.stringify(initConfig, null, 2));
|
|
106164
|
+
await Bun.write(join88(naxDir, "hooks.json"), JSON.stringify({
|
|
106041
106165
|
hooks: {
|
|
106042
106166
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
106043
106167
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -106045,12 +106169,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
106045
106169
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
106046
106170
|
}
|
|
106047
106171
|
}, null, 2));
|
|
106048
|
-
await Bun.write(
|
|
106172
|
+
await Bun.write(join88(naxDir, ".gitignore"), `# nax temp files
|
|
106049
106173
|
*.tmp
|
|
106050
106174
|
.paused.json
|
|
106051
106175
|
.nax-verifier-verdict.json
|
|
106052
106176
|
`);
|
|
106053
|
-
await Bun.write(
|
|
106177
|
+
await Bun.write(join88(naxDir, "context.md"), `# Project Context
|
|
106054
106178
|
|
|
106055
106179
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
106056
106180
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -106198,8 +106322,8 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
106198
106322
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
106199
106323
|
process.exit(1);
|
|
106200
106324
|
}
|
|
106201
|
-
const featureDir =
|
|
106202
|
-
const prdPath =
|
|
106325
|
+
const featureDir = join88(naxDir, "features", options.feature);
|
|
106326
|
+
const prdPath = join88(featureDir, "prd.json");
|
|
106203
106327
|
if (options.plan && options.from) {
|
|
106204
106328
|
if (existsSync35(prdPath) && !options.force) {
|
|
106205
106329
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
@@ -106221,10 +106345,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
106221
106345
|
}
|
|
106222
106346
|
}
|
|
106223
106347
|
try {
|
|
106224
|
-
const planLogDir =
|
|
106348
|
+
const planLogDir = join88(featureDir, "plan");
|
|
106225
106349
|
mkdirSync7(planLogDir, { recursive: true });
|
|
106226
106350
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
106227
|
-
const planLogPath =
|
|
106351
|
+
const planLogPath = join88(planLogDir, `${planLogId}.jsonl`);
|
|
106228
106352
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
106229
106353
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
106230
106354
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -106270,10 +106394,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
106270
106394
|
resetLogger();
|
|
106271
106395
|
const projectKey = config2.name?.trim() || basename16(workdir);
|
|
106272
106396
|
const outputDir = projectOutputDir(projectKey, config2.outputDir);
|
|
106273
|
-
const runsDir =
|
|
106397
|
+
const runsDir = join88(outputDir, "features", options.feature, "runs");
|
|
106274
106398
|
mkdirSync7(runsDir, { recursive: true });
|
|
106275
106399
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
106276
|
-
const logFilePath =
|
|
106400
|
+
const logFilePath = join88(runsDir, `${runId}.jsonl`);
|
|
106277
106401
|
const isTTY = process.stdout.isTTY ?? false;
|
|
106278
106402
|
const headlessFlag = options.headless ?? false;
|
|
106279
106403
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -106291,7 +106415,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
106291
106415
|
config2.agent.default = options.agent;
|
|
106292
106416
|
}
|
|
106293
106417
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
106294
|
-
const globalNaxDir =
|
|
106418
|
+
const globalNaxDir = join88(homedir3(), ".nax");
|
|
106295
106419
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
106296
106420
|
const eventEmitter = new PipelineEventEmitter;
|
|
106297
106421
|
const agentStreamEvents = useHeadless ? undefined : new AgentStreamEventBus;
|
|
@@ -106311,12 +106435,12 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
106311
106435
|
events: eventEmitter,
|
|
106312
106436
|
ptyOptions: null,
|
|
106313
106437
|
agentStreamEvents,
|
|
106314
|
-
queueFilePath:
|
|
106438
|
+
queueFilePath: join88(workdir, ".queue.txt")
|
|
106315
106439
|
});
|
|
106316
106440
|
} else {
|
|
106317
106441
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
106318
106442
|
}
|
|
106319
|
-
const statusFilePath =
|
|
106443
|
+
const statusFilePath = join88(outputDir, "status.json");
|
|
106320
106444
|
let parallel;
|
|
106321
106445
|
if (options.parallel !== undefined) {
|
|
106322
106446
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -106343,7 +106467,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
106343
106467
|
skipPrecheck: options.skipPrecheck ?? false,
|
|
106344
106468
|
agentStreamEvents
|
|
106345
106469
|
});
|
|
106346
|
-
const latestSymlink =
|
|
106470
|
+
const latestSymlink = join88(runsDir, "latest.jsonl");
|
|
106347
106471
|
try {
|
|
106348
106472
|
if (existsSync35(latestSymlink)) {
|
|
106349
106473
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
@@ -106404,9 +106528,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
106404
106528
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
106405
106529
|
process.exit(1);
|
|
106406
106530
|
}
|
|
106407
|
-
const featureDir =
|
|
106531
|
+
const featureDir = join88(naxDir, "features", name);
|
|
106408
106532
|
mkdirSync7(featureDir, { recursive: true });
|
|
106409
|
-
await Bun.write(
|
|
106533
|
+
await Bun.write(join88(featureDir, "spec.md"), `# Feature: ${name}
|
|
106410
106534
|
|
|
106411
106535
|
## Overview
|
|
106412
106536
|
|
|
@@ -106439,7 +106563,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
106439
106563
|
|
|
106440
106564
|
<!-- What this feature explicitly does NOT cover. -->
|
|
106441
106565
|
`);
|
|
106442
|
-
await Bun.write(
|
|
106566
|
+
await Bun.write(join88(featureDir, "progress.txt"), `# Progress: ${name}
|
|
106443
106567
|
|
|
106444
106568
|
Created: ${new Date().toISOString()}
|
|
106445
106569
|
|
|
@@ -106465,7 +106589,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
106465
106589
|
console.error(source_default.red("nax not initialized."));
|
|
106466
106590
|
process.exit(1);
|
|
106467
106591
|
}
|
|
106468
|
-
const featuresDir =
|
|
106592
|
+
const featuresDir = join88(naxDir, "features");
|
|
106469
106593
|
if (!existsSync35(featuresDir)) {
|
|
106470
106594
|
console.log(source_default.dim("No features yet."));
|
|
106471
106595
|
return;
|
|
@@ -106480,7 +106604,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
106480
106604
|
Features:
|
|
106481
106605
|
`));
|
|
106482
106606
|
for (const name of entries) {
|
|
106483
|
-
const prdPath =
|
|
106607
|
+
const prdPath = join88(featuresDir, name, "prd.json");
|
|
106484
106608
|
if (existsSync35(prdPath)) {
|
|
106485
106609
|
const prd = await loadPRD(prdPath);
|
|
106486
106610
|
const c = countStories(prd);
|
|
@@ -106515,10 +106639,10 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
106515
106639
|
cliOverrides.profile = options.profile;
|
|
106516
106640
|
}
|
|
106517
106641
|
const config2 = await loadConfig(workdir, cliOverrides);
|
|
106518
|
-
const featureLogDir =
|
|
106642
|
+
const featureLogDir = join88(naxDir, "features", options.feature, "plan");
|
|
106519
106643
|
mkdirSync7(featureLogDir, { recursive: true });
|
|
106520
106644
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
106521
|
-
const planLogPath =
|
|
106645
|
+
const planLogPath = join88(featureLogDir, `${planLogId}.jsonl`);
|
|
106522
106646
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
106523
106647
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
106524
106648
|
try {
|