@nathapp/nax 0.64.0-canary.2 → 0.64.0-canary.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 +144 -134
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -3660,7 +3660,8 @@ class SpawnAcpSession {
|
|
|
3660
3660
|
return {
|
|
3661
3661
|
messages: [{ role: "assistant", content: errorContent }],
|
|
3662
3662
|
stopReason: "error",
|
|
3663
|
-
retryable: parsedOnError.retryable
|
|
3663
|
+
retryable: parsedOnError.retryable,
|
|
3664
|
+
exitCode
|
|
3664
3665
|
};
|
|
3665
3666
|
}
|
|
3666
3667
|
try {
|
|
@@ -18937,6 +18938,7 @@ class AcpSessionHandleImpl {
|
|
|
18937
18938
|
_resumed;
|
|
18938
18939
|
_timeoutSeconds;
|
|
18939
18940
|
_modelDef;
|
|
18941
|
+
_permissionMode;
|
|
18940
18942
|
constructor(opts) {
|
|
18941
18943
|
this.id = opts.id;
|
|
18942
18944
|
this.agentName = opts.agentName;
|
|
@@ -18947,6 +18949,7 @@ class AcpSessionHandleImpl {
|
|
|
18947
18949
|
this._resumed = opts.resumed;
|
|
18948
18950
|
this._timeoutSeconds = opts.timeoutSeconds;
|
|
18949
18951
|
this._modelDef = opts.modelDef;
|
|
18952
|
+
this._permissionMode = opts.permissionMode;
|
|
18950
18953
|
}
|
|
18951
18954
|
}
|
|
18952
18955
|
function extractOutput(response) {
|
|
@@ -19288,7 +19291,8 @@ class AcpAgentAdapter {
|
|
|
19288
19291
|
sessionName: name,
|
|
19289
19292
|
resumed: ensured.resumed,
|
|
19290
19293
|
timeoutSeconds,
|
|
19291
|
-
modelDef
|
|
19294
|
+
modelDef,
|
|
19295
|
+
permissionMode: resolvedPermissions.mode
|
|
19292
19296
|
});
|
|
19293
19297
|
} catch (error48) {
|
|
19294
19298
|
if (session) {
|
|
@@ -19300,7 +19304,8 @@ class AcpAgentAdapter {
|
|
|
19300
19304
|
}
|
|
19301
19305
|
async sendTurn(handle, prompt, opts) {
|
|
19302
19306
|
const impl = handle;
|
|
19303
|
-
const {
|
|
19307
|
+
const { _sessionName: sessionName, _timeoutSeconds: timeoutSeconds, _modelDef: modelDef } = impl;
|
|
19308
|
+
let sessionRecreated = false;
|
|
19304
19309
|
const { interactionHandler, signal } = opts;
|
|
19305
19310
|
const MAX_TURNS = opts.maxTurns ?? 10;
|
|
19306
19311
|
let totalTokenUsage = { inputTokens: 0, outputTokens: 0 };
|
|
@@ -19321,7 +19326,7 @@ class AcpAgentAdapter {
|
|
|
19321
19326
|
while (turnCount < MAX_TURNS) {
|
|
19322
19327
|
turnCount++;
|
|
19323
19328
|
getSafeLogger()?.debug("acp-adapter", `Session turn ${turnCount}/${MAX_TURNS}`, { sessionName });
|
|
19324
|
-
const turnResult = await runSessionPrompt(
|
|
19329
|
+
const turnResult = await runSessionPrompt(impl._session, currentPrompt, timeoutSeconds * 1000, signal);
|
|
19325
19330
|
if (turnResult.timedOut) {
|
|
19326
19331
|
timedOut = true;
|
|
19327
19332
|
break;
|
|
@@ -19333,6 +19338,21 @@ class AcpAgentAdapter {
|
|
|
19333
19338
|
lastResponse = turnResult.response;
|
|
19334
19339
|
if (!lastResponse)
|
|
19335
19340
|
break;
|
|
19341
|
+
if (lastResponse.exitCode === 4 && !sessionRecreated) {
|
|
19342
|
+
sessionRecreated = true;
|
|
19343
|
+
getSafeLogger()?.info("acp-adapter", "NO_SESSION detected \u2014 re-establishing session", { sessionName });
|
|
19344
|
+
try {
|
|
19345
|
+
const ensured = await ensureAcpSession(impl._client, impl._sessionName, impl.agentName, impl._permissionMode);
|
|
19346
|
+
impl._session = ensured.session;
|
|
19347
|
+
turnCount--;
|
|
19348
|
+
continue;
|
|
19349
|
+
} catch (err) {
|
|
19350
|
+
getSafeLogger()?.warn("acp-adapter", "Session re-establishment failed after NO_SESSION", {
|
|
19351
|
+
sessionName,
|
|
19352
|
+
error: err instanceof Error ? err.message : String(err)
|
|
19353
|
+
});
|
|
19354
|
+
}
|
|
19355
|
+
}
|
|
19336
19356
|
if (lastResponse.cumulative_token_usage) {
|
|
19337
19357
|
totalTokenUsage = addTokenUsage(totalTokenUsage, this._mapper.toInternal(lastResponse.cumulative_token_usage));
|
|
19338
19358
|
}
|
|
@@ -34509,8 +34529,8 @@ var init_truncation = __esm(() => {
|
|
|
34509
34529
|
init_adapter();
|
|
34510
34530
|
});
|
|
34511
34531
|
|
|
34512
|
-
// src/operations/
|
|
34513
|
-
function
|
|
34532
|
+
// src/operations/types.ts
|
|
34533
|
+
function parseLlmReviewShape(raw) {
|
|
34514
34534
|
if (typeof raw !== "object" || raw === null)
|
|
34515
34535
|
return null;
|
|
34516
34536
|
const obj = raw;
|
|
@@ -34520,11 +34540,13 @@ function parseLLMShape(raw) {
|
|
|
34520
34540
|
return null;
|
|
34521
34541
|
return { passed: obj.passed, findings: obj.findings };
|
|
34522
34542
|
}
|
|
34543
|
+
|
|
34544
|
+
// src/operations/semantic-review.ts
|
|
34523
34545
|
var FAIL_OPEN, semanticReviewHopBody = async (initialPrompt, ctx) => {
|
|
34524
34546
|
const first = await ctx.send(initialPrompt);
|
|
34525
34547
|
const isTruncated = looksLikeTruncatedJson(first.output);
|
|
34526
34548
|
const parsed = tryParseLLMJson(first.output);
|
|
34527
|
-
if (!isTruncated && parsed &&
|
|
34549
|
+
if (!isTruncated && parsed && parseLlmReviewShape(parsed))
|
|
34528
34550
|
return first;
|
|
34529
34551
|
const retryPrompt = isTruncated ? ReviewPromptBuilder.jsonRetryCondensed() : ReviewPromptBuilder.jsonRetry();
|
|
34530
34552
|
const retry = await ctx.send(retryPrompt);
|
|
@@ -34562,7 +34584,7 @@ var init_semantic_review = __esm(() => {
|
|
|
34562
34584
|
},
|
|
34563
34585
|
parse(output, _input, _ctx) {
|
|
34564
34586
|
const raw = tryParseLLMJson(output);
|
|
34565
|
-
const parsed =
|
|
34587
|
+
const parsed = parseLlmReviewShape(raw);
|
|
34566
34588
|
if (parsed)
|
|
34567
34589
|
return parsed;
|
|
34568
34590
|
if (/"passed"\s*:\s*false/.test(output))
|
|
@@ -34573,21 +34595,11 @@ var init_semantic_review = __esm(() => {
|
|
|
34573
34595
|
});
|
|
34574
34596
|
|
|
34575
34597
|
// src/operations/adversarial-review.ts
|
|
34576
|
-
function parseLLMShape2(raw) {
|
|
34577
|
-
if (typeof raw !== "object" || raw === null)
|
|
34578
|
-
return null;
|
|
34579
|
-
const obj = raw;
|
|
34580
|
-
if (typeof obj.passed !== "boolean")
|
|
34581
|
-
return null;
|
|
34582
|
-
if (!Array.isArray(obj.findings))
|
|
34583
|
-
return null;
|
|
34584
|
-
return { passed: obj.passed, findings: obj.findings };
|
|
34585
|
-
}
|
|
34586
34598
|
var FAIL_OPEN2, adversarialReviewHopBody = async (initialPrompt, ctx) => {
|
|
34587
34599
|
const first = await ctx.send(initialPrompt);
|
|
34588
34600
|
const isTruncated = looksLikeTruncatedJson(first.output);
|
|
34589
34601
|
const parsed = tryParseLLMJson(first.output);
|
|
34590
|
-
if (!isTruncated && parsed &&
|
|
34602
|
+
if (!isTruncated && parsed && parseLlmReviewShape(parsed))
|
|
34591
34603
|
return first;
|
|
34592
34604
|
const retryPrompt = isTruncated ? ReviewPromptBuilder.jsonRetryCondensed() : ReviewPromptBuilder.jsonRetry();
|
|
34593
34605
|
const retry = await ctx.send(retryPrompt);
|
|
@@ -34627,7 +34639,7 @@ var init_adversarial_review = __esm(() => {
|
|
|
34627
34639
|
},
|
|
34628
34640
|
parse(output, _input, _ctx) {
|
|
34629
34641
|
const raw = tryParseLLMJson(output);
|
|
34630
|
-
const parsed =
|
|
34642
|
+
const parsed = parseLlmReviewShape(raw);
|
|
34631
34643
|
if (parsed)
|
|
34632
34644
|
return parsed;
|
|
34633
34645
|
if (/"passed"\s*:\s*false/.test(output))
|
|
@@ -35189,7 +35201,7 @@ var init_cost_aggregator = __esm(() => {
|
|
|
35189
35201
|
});
|
|
35190
35202
|
|
|
35191
35203
|
// src/runtime/prompt-auditor.ts
|
|
35192
|
-
import {
|
|
35204
|
+
import { appendFile as appendFile3, mkdir as mkdir3 } from "fs/promises";
|
|
35193
35205
|
import { join as join21 } from "path";
|
|
35194
35206
|
function createNoOpPromptAuditor() {
|
|
35195
35207
|
return {
|
|
@@ -35198,6 +35210,15 @@ function createNoOpPromptAuditor() {
|
|
|
35198
35210
|
async flush() {}
|
|
35199
35211
|
};
|
|
35200
35212
|
}
|
|
35213
|
+
function deriveTxtFilename(entry) {
|
|
35214
|
+
if (entry.sessionName) {
|
|
35215
|
+
return `${entry.ts}-${entry.sessionName}.txt`;
|
|
35216
|
+
}
|
|
35217
|
+
const parts = [String(entry.ts), entry.callType ?? "call", entry.stage ?? "unknown"];
|
|
35218
|
+
if (entry.storyId)
|
|
35219
|
+
parts.push(entry.storyId);
|
|
35220
|
+
return `${parts.join("-")}.txt`;
|
|
35221
|
+
}
|
|
35201
35222
|
function buildTxtContent(entry) {
|
|
35202
35223
|
const ts = new Date(entry.ts).toISOString();
|
|
35203
35224
|
const lines = [
|
|
@@ -35226,77 +35247,51 @@ function buildTxtContent(entry) {
|
|
|
35226
35247
|
}
|
|
35227
35248
|
|
|
35228
35249
|
class PromptAuditor {
|
|
35229
|
-
|
|
35230
|
-
|
|
35231
|
-
|
|
35232
|
-
|
|
35233
|
-
|
|
35234
|
-
|
|
35235
|
-
|
|
35236
|
-
this._runId = _runId;
|
|
35237
|
-
this._flushDir = _flushDir;
|
|
35238
|
-
this._featureName = _featureName;
|
|
35250
|
+
_queue = Promise.resolve();
|
|
35251
|
+
_dirCreated = false;
|
|
35252
|
+
_jsonlPath;
|
|
35253
|
+
_featureDir;
|
|
35254
|
+
constructor(runId, flushDir, featureName) {
|
|
35255
|
+
this._featureDir = join21(flushDir, featureName);
|
|
35256
|
+
this._jsonlPath = join21(this._featureDir, `${runId}.jsonl`);
|
|
35239
35257
|
}
|
|
35240
35258
|
record(entry) {
|
|
35241
|
-
|
|
35242
|
-
this._inFlightEntries.push(entry);
|
|
35243
|
-
return;
|
|
35244
|
-
}
|
|
35245
|
-
this._entries.push(entry);
|
|
35259
|
+
this._enqueue(entry);
|
|
35246
35260
|
}
|
|
35247
35261
|
recordError(entry) {
|
|
35248
|
-
|
|
35249
|
-
|
|
35250
|
-
|
|
35262
|
+
this._enqueue(entry);
|
|
35263
|
+
}
|
|
35264
|
+
_enqueue(entry) {
|
|
35265
|
+
this._queue = this._queue.then(() => this._writeEntry(entry)).catch((err) => {
|
|
35266
|
+
getSafeLogger()?.warn("audit", "prompt-audit write failed", {
|
|
35267
|
+
path: this._jsonlPath,
|
|
35268
|
+
error: errorMessage(err)
|
|
35269
|
+
});
|
|
35270
|
+
});
|
|
35271
|
+
}
|
|
35272
|
+
async _writeEntry(entry) {
|
|
35273
|
+
if (!this._dirCreated) {
|
|
35274
|
+
await mkdir3(this._featureDir, { recursive: true });
|
|
35275
|
+
this._dirCreated = true;
|
|
35251
35276
|
}
|
|
35252
|
-
this.
|
|
35277
|
+
await _promptAuditorDeps.appendLine(this._jsonlPath, `${JSON.stringify(entry)}
|
|
35278
|
+
`);
|
|
35279
|
+
if (!("prompt" in entry) || !("response" in entry))
|
|
35280
|
+
return;
|
|
35281
|
+
const auditEntry = entry;
|
|
35282
|
+
const filename = deriveTxtFilename(auditEntry);
|
|
35283
|
+
await _promptAuditorDeps.write(join21(this._featureDir, filename), buildTxtContent(auditEntry));
|
|
35253
35284
|
}
|
|
35254
35285
|
async flush() {
|
|
35255
|
-
this.
|
|
35256
|
-
try {
|
|
35257
|
-
const entries = this._entries.splice(0);
|
|
35258
|
-
if (entries.length === 0)
|
|
35259
|
-
return;
|
|
35260
|
-
const featureDir = join21(this._flushDir, this._featureName);
|
|
35261
|
-
mkdirSync3(featureDir, { recursive: true });
|
|
35262
|
-
const jsonlPath = join21(featureDir, `${this._runId}.jsonl`);
|
|
35263
|
-
await _promptAuditorDeps.write(jsonlPath, `${entries.map((e) => JSON.stringify(e)).join(`
|
|
35264
|
-
`)}
|
|
35265
|
-
`);
|
|
35266
|
-
for (const entry of entries) {
|
|
35267
|
-
if (!("prompt" in entry) || !("response" in entry))
|
|
35268
|
-
continue;
|
|
35269
|
-
const auditEntry = entry;
|
|
35270
|
-
if (!auditEntry.sessionName)
|
|
35271
|
-
continue;
|
|
35272
|
-
const filename = `${auditEntry.ts}-${auditEntry.sessionName}.txt`;
|
|
35273
|
-
await _promptAuditorDeps.write(join21(featureDir, filename), buildTxtContent(auditEntry));
|
|
35274
|
-
}
|
|
35275
|
-
const lateEntries = this._inFlightEntries.splice(0);
|
|
35276
|
-
if (lateEntries.length > 0) {
|
|
35277
|
-
const allEntries = [...entries, ...lateEntries];
|
|
35278
|
-
await _promptAuditorDeps.write(jsonlPath, `${allEntries.map((e) => JSON.stringify(e)).join(`
|
|
35279
|
-
`)}
|
|
35280
|
-
`);
|
|
35281
|
-
for (const entry of lateEntries) {
|
|
35282
|
-
if (!("prompt" in entry) || !("response" in entry))
|
|
35283
|
-
continue;
|
|
35284
|
-
const auditEntry = entry;
|
|
35285
|
-
if (!auditEntry.sessionName)
|
|
35286
|
-
continue;
|
|
35287
|
-
const filename = `${auditEntry.ts}-${auditEntry.sessionName}.txt`;
|
|
35288
|
-
await _promptAuditorDeps.write(join21(featureDir, filename), buildTxtContent(auditEntry));
|
|
35289
|
-
}
|
|
35290
|
-
}
|
|
35291
|
-
} finally {
|
|
35292
|
-
this._draining = false;
|
|
35293
|
-
}
|
|
35286
|
+
await this._queue;
|
|
35294
35287
|
}
|
|
35295
35288
|
}
|
|
35296
35289
|
var _promptAuditorDeps;
|
|
35297
35290
|
var init_prompt_auditor = __esm(() => {
|
|
35291
|
+
init_logger2();
|
|
35298
35292
|
_promptAuditorDeps = {
|
|
35299
|
-
write: (path4, data) => Bun.write(path4, data)
|
|
35293
|
+
write: (path4, data) => Bun.write(path4, data),
|
|
35294
|
+
appendLine: (path4, data) => appendFile3(path4, data, "utf8")
|
|
35300
35295
|
};
|
|
35301
35296
|
});
|
|
35302
35297
|
|
|
@@ -35358,7 +35353,7 @@ var init_types5 = __esm(() => {
|
|
|
35358
35353
|
|
|
35359
35354
|
// src/session/manager.ts
|
|
35360
35355
|
import { randomUUID as randomUUID2 } from "crypto";
|
|
35361
|
-
import { mkdir as
|
|
35356
|
+
import { mkdir as mkdir4 } from "fs/promises";
|
|
35362
35357
|
import { isAbsolute as isAbsolute8, join as join22, relative as relative8, sep } from "path";
|
|
35363
35358
|
function resolveProjectDirFromScratchDir(scratchDir) {
|
|
35364
35359
|
const marker = `${sep}.nax${sep}features${sep}`;
|
|
@@ -35678,6 +35673,10 @@ class SessionManager {
|
|
|
35678
35673
|
if (this._busySessions.has(handle.id)) {
|
|
35679
35674
|
throw new NaxError(`Session "${handle.id}" is already processing a prompt (single-flight invariant)`, "SESSION_BUSY", { stage: "session", sessionName: handle.id });
|
|
35680
35675
|
}
|
|
35676
|
+
const terminalDesc = this._findByName(handle.id);
|
|
35677
|
+
if (terminalDesc && (terminalDesc.state === "COMPLETED" || terminalDesc.state === "FAILED")) {
|
|
35678
|
+
throw new NaxError(`Session "${handle.id}" is in terminal state ${terminalDesc.state} \u2014 call openSession first to resume`, "SESSION_TERMINAL_STATE", { stage: "session", sessionName: handle.id, state: terminalDesc.state });
|
|
35679
|
+
}
|
|
35681
35680
|
const adapter = this._getAdapter(handle.agentName);
|
|
35682
35681
|
if (!adapter) {
|
|
35683
35682
|
throw new NaxError(`SessionManager.sendPrompt: no adapter found for agent "${handle.agentName}"`, "ADAPTER_NOT_FOUND", { stage: "session", agentName: handle.agentName });
|
|
@@ -35842,7 +35841,7 @@ var init_manager2 = __esm(() => {
|
|
|
35842
35841
|
uuid: () => randomUUID2(),
|
|
35843
35842
|
sessionScratchDir: (projectDir, featureName, sessionId) => join22(projectDir, ".nax", "features", featureName, "sessions", sessionId),
|
|
35844
35843
|
writeDescriptor: async (scratchDir, descriptor, projectDir) => {
|
|
35845
|
-
await
|
|
35844
|
+
await mkdir4(scratchDir, { recursive: true });
|
|
35846
35845
|
const { handle: _handle, ...persistable } = descriptor;
|
|
35847
35846
|
const derivedProjectDir = projectDir ?? resolveProjectDirFromScratchDir(scratchDir);
|
|
35848
35847
|
if (derivedProjectDir) {
|
|
@@ -38464,6 +38463,7 @@ ${stderr}` };
|
|
|
38464
38463
|
`);
|
|
38465
38464
|
const frameworkOverrideLine = ctx.config.acceptance.testFramework ? `
|
|
38466
38465
|
[FRAMEWORK OVERRIDE: Use ${ctx.config.acceptance.testFramework} as the test framework regardless of what you detect.]` : "";
|
|
38466
|
+
const groupStoryId = group.stories[0]?.id;
|
|
38467
38467
|
const genResult = await _acceptanceSetupDeps.callOp(ctx, packageDir, acceptanceGenerateOp, {
|
|
38468
38468
|
featureName: featureName ?? "",
|
|
38469
38469
|
criteriaList,
|
|
@@ -38471,7 +38471,7 @@ ${stderr}` };
|
|
|
38471
38471
|
targetTestFilePath: testPath,
|
|
38472
38472
|
..."implementationContext" in ctx && ctx.implementationContext ? { implementationContext: ctx.implementationContext } : {},
|
|
38473
38473
|
..."previousFailure" in ctx && ctx.previousFailure ? { previousFailure: ctx.previousFailure } : {}
|
|
38474
|
-
});
|
|
38474
|
+
}, groupStoryId);
|
|
38475
38475
|
let testCode = genResult.testCode;
|
|
38476
38476
|
if (!testCode) {
|
|
38477
38477
|
const skeletonCriteria = groupRefined.map((c, i) => ({
|
|
@@ -39982,7 +39982,7 @@ var init_nax_project_root = __esm(() => {
|
|
|
39982
39982
|
});
|
|
39983
39983
|
|
|
39984
39984
|
// src/review/review-audit.ts
|
|
39985
|
-
import { mkdir as
|
|
39985
|
+
import { mkdir as mkdir5 } from "fs/promises";
|
|
39986
39986
|
import { join as join33 } from "path";
|
|
39987
39987
|
async function writeReviewAudit(entry) {
|
|
39988
39988
|
try {
|
|
@@ -40015,7 +40015,7 @@ var init_review_audit = __esm(() => {
|
|
|
40015
40015
|
init_nax_project_root();
|
|
40016
40016
|
_reviewAuditDeps = {
|
|
40017
40017
|
async mkdir(path8) {
|
|
40018
|
-
await
|
|
40018
|
+
await mkdir5(path8, { recursive: true });
|
|
40019
40019
|
},
|
|
40020
40020
|
async writeFile(path8, content) {
|
|
40021
40021
|
await Bun.write(path8, content);
|
|
@@ -41466,7 +41466,7 @@ var init_runner4 = __esm(() => {
|
|
|
41466
41466
|
});
|
|
41467
41467
|
|
|
41468
41468
|
// src/review/verdict-writer.ts
|
|
41469
|
-
import { mkdir as
|
|
41469
|
+
import { mkdir as mkdir6 } from "fs/promises";
|
|
41470
41470
|
import { join as join34 } from "path";
|
|
41471
41471
|
async function writeReviewVerdict(entry) {
|
|
41472
41472
|
const logger = getSafeLogger();
|
|
@@ -41495,7 +41495,7 @@ var init_verdict_writer = __esm(() => {
|
|
|
41495
41495
|
init_nax_project_root();
|
|
41496
41496
|
_verdictWriterDeps = {
|
|
41497
41497
|
findNaxProjectRoot,
|
|
41498
|
-
mkdir:
|
|
41498
|
+
mkdir: mkdir6,
|
|
41499
41499
|
writeFile: Bun.write
|
|
41500
41500
|
};
|
|
41501
41501
|
});
|
|
@@ -42555,8 +42555,8 @@ var init_semantic_verdict = __esm(() => {
|
|
|
42555
42555
|
init_logger2();
|
|
42556
42556
|
_semanticVerdictDeps = {
|
|
42557
42557
|
mkdirp: async (dir) => {
|
|
42558
|
-
const { mkdir:
|
|
42559
|
-
await
|
|
42558
|
+
const { mkdir: mkdir7 } = await import("fs/promises");
|
|
42559
|
+
await mkdir7(dir, { recursive: true });
|
|
42560
42560
|
},
|
|
42561
42561
|
writeFile: async (filePath, content) => {
|
|
42562
42562
|
await Bun.write(filePath, content);
|
|
@@ -42703,15 +42703,15 @@ var init_effectiveness = __esm(() => {
|
|
|
42703
42703
|
});
|
|
42704
42704
|
|
|
42705
42705
|
// src/execution/progress.ts
|
|
42706
|
-
import { appendFile as
|
|
42706
|
+
import { appendFile as appendFile4, mkdir as mkdir7 } from "fs/promises";
|
|
42707
42707
|
import { join as join36 } from "path";
|
|
42708
42708
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
42709
|
-
await
|
|
42709
|
+
await mkdir7(featureDir, { recursive: true });
|
|
42710
42710
|
const progressPath = join36(featureDir, "progress.txt");
|
|
42711
42711
|
const timestamp = new Date().toISOString();
|
|
42712
42712
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
42713
42713
|
`;
|
|
42714
|
-
await
|
|
42714
|
+
await appendFile4(progressPath, entry);
|
|
42715
42715
|
}
|
|
42716
42716
|
var init_progress = () => {};
|
|
42717
42717
|
|
|
@@ -43550,7 +43550,7 @@ async function getStoryChangedFiles(workdir, fromRef) {
|
|
|
43550
43550
|
async function runFullSuiteGate(story, config2, workdir, agentManager, implementerTier, lite, logger, featureName, projectDir, storyFromRef, sessionManager, sessionId, runtime) {
|
|
43551
43551
|
const rectificationEnabled = config2.execution.rectification?.enabled ?? false;
|
|
43552
43552
|
if (!rectificationEnabled)
|
|
43553
|
-
return { passed: false, cost: 0 };
|
|
43553
|
+
return { passed: false, cost: 0, fullSuiteGatePassed: false };
|
|
43554
43554
|
const rectificationConfig = config2.execution.rectification;
|
|
43555
43555
|
const fullSuiteTimeout = rectificationConfig.fullSuiteTimeoutSeconds;
|
|
43556
43556
|
const { testCommand: resolvedTestCmd } = await _rectificationGateDeps.resolveTestCommands(config2, workdir, story.workdir);
|
|
@@ -43564,8 +43564,18 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
|
|
|
43564
43564
|
if (!fullSuitePassed && fullSuiteResult.output) {
|
|
43565
43565
|
const testSummary = _rectificationGateDeps.parseTestOutput(fullSuiteResult.output);
|
|
43566
43566
|
if (testSummary.failed > 0) {
|
|
43567
|
+
if (testSummary.failures.length === 0) {
|
|
43568
|
+
logger.warn("tdd", "Full suite gate found unattributable failures \u2014 deferring to run-level regression", {
|
|
43569
|
+
storyId: story.id,
|
|
43570
|
+
failedTests: testSummary.failed,
|
|
43571
|
+
passedTests: testSummary.passed,
|
|
43572
|
+
outputLength: fullSuiteResult.output.length,
|
|
43573
|
+
outputTail: fullSuiteResult.output.slice(-200)
|
|
43574
|
+
});
|
|
43575
|
+
return { passed: true, cost: 0, fullSuiteGatePassed: false };
|
|
43576
|
+
}
|
|
43567
43577
|
let filteredFailures = testSummary.failures;
|
|
43568
|
-
if (storyFromRef
|
|
43578
|
+
if (storyFromRef) {
|
|
43569
43579
|
const storyFiles = await getStoryChangedFiles(workdir, storyFromRef);
|
|
43570
43580
|
if (storyFiles.size > 0) {
|
|
43571
43581
|
filteredFailures = testSummary.failures.filter((f) => storyFiles.has(f.file));
|
|
@@ -43580,7 +43590,7 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
|
|
|
43580
43590
|
suppressedTestCount: testSummary.failures.length,
|
|
43581
43591
|
suppressedFiles: uniqueSuppressedFiles
|
|
43582
43592
|
});
|
|
43583
|
-
return { passed: true, cost: 0 };
|
|
43593
|
+
return { passed: true, cost: 0, fullSuiteGatePassed: true };
|
|
43584
43594
|
}
|
|
43585
43595
|
if (wasFiltered) {
|
|
43586
43596
|
logger.info("tdd", "Full suite gate: suppressed pre-existing failures", {
|
|
@@ -43603,7 +43613,7 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
|
|
|
43603
43613
|
exitCode: fullSuiteResult.exitCode,
|
|
43604
43614
|
passedTests: testSummary.passed
|
|
43605
43615
|
});
|
|
43606
|
-
return { passed: true, cost: 0 };
|
|
43616
|
+
return { passed: true, cost: 0, fullSuiteGatePassed: true };
|
|
43607
43617
|
}
|
|
43608
43618
|
logger.warn("tdd", "Full suite gate inconclusive \u2014 no test results parsed from output (possible crash/OOM)", {
|
|
43609
43619
|
storyId: story.id,
|
|
@@ -43611,17 +43621,17 @@ async function runFullSuiteGate(story, config2, workdir, agentManager, implement
|
|
|
43611
43621
|
outputLength: fullSuiteResult.output.length,
|
|
43612
43622
|
outputTail: fullSuiteResult.output.slice(-200)
|
|
43613
43623
|
});
|
|
43614
|
-
return { passed: false, cost: 0 };
|
|
43624
|
+
return { passed: false, cost: 0, fullSuiteGatePassed: false };
|
|
43615
43625
|
}
|
|
43616
43626
|
if (fullSuitePassed) {
|
|
43617
43627
|
logger.info("tdd", "Full suite gate passed", { storyId: story.id });
|
|
43618
|
-
return { passed: true, cost: 0 };
|
|
43628
|
+
return { passed: true, cost: 0, fullSuiteGatePassed: true };
|
|
43619
43629
|
}
|
|
43620
43630
|
logger.warn("tdd", "Full suite gate execution failed (no output)", {
|
|
43621
43631
|
storyId: story.id,
|
|
43622
43632
|
exitCode: fullSuiteResult.exitCode
|
|
43623
43633
|
});
|
|
43624
|
-
return { passed: false, cost: 0 };
|
|
43634
|
+
return { passed: false, cost: 0, fullSuiteGatePassed: false };
|
|
43625
43635
|
}
|
|
43626
43636
|
async function runRectificationLoop(story, config2, workdir, agentManager, implementerTier, lite, logger, testSummary, rectificationConfig, testCmd, fullSuiteTimeout, testOutput, featureName, projectDir, sessionManager, sessionId, runtime) {
|
|
43627
43637
|
logger.warn("tdd", "Full suite gate detected regressions", {
|
|
@@ -43761,13 +43771,13 @@ async function runRectificationLoop(story, config2, workdir, agentManager, imple
|
|
|
43761
43771
|
});
|
|
43762
43772
|
const fixed = outcome.outcome === "fixed";
|
|
43763
43773
|
if (fixed) {
|
|
43764
|
-
return { passed: true, cost: gateCostAccum };
|
|
43774
|
+
return { passed: true, cost: gateCostAccum, fullSuiteGatePassed: true };
|
|
43765
43775
|
}
|
|
43766
43776
|
logger.warn("tdd", "[WARN] Full suite gate failed after rectification exhausted", {
|
|
43767
43777
|
storyId: story.id,
|
|
43768
43778
|
attempts: outcome.attempts
|
|
43769
43779
|
});
|
|
43770
|
-
return { passed: false, cost: gateCostAccum };
|
|
43780
|
+
return { passed: false, cost: gateCostAccum, fullSuiteGatePassed: false };
|
|
43771
43781
|
}
|
|
43772
43782
|
var _rectificationGateDeps;
|
|
43773
43783
|
var init_rectification_gate = __esm(() => {
|
|
@@ -44236,7 +44246,7 @@ async function runThreeSessionTdd(options) {
|
|
|
44236
44246
|
};
|
|
44237
44247
|
}
|
|
44238
44248
|
const implementerBinding = getTddSessionBinding?.("implementer");
|
|
44239
|
-
const {
|
|
44249
|
+
const { cost: fullSuiteGateCost, fullSuiteGatePassed } = await runFullSuiteGate(story, config2, workdir, wrapAdapterAsManager(agent), implementerTier, lite, logger, featureName, projectDir, initialRef, implementerBinding?.sessionManager, implementerBinding?.sessionId);
|
|
44240
44250
|
const session3Ref = await captureGitRef(workdir) ?? "HEAD";
|
|
44241
44251
|
const verifierBundle = await getTddContextBundle?.("verifier") ?? tddContextBundles?.verifier;
|
|
44242
44252
|
const session3 = await runTddSessionOp(verifyTddOp, options, session3Ref, verifierBundle, getTddSessionBinding?.("verifier"));
|
|
@@ -46400,7 +46410,7 @@ __export(exports_init_context, {
|
|
|
46400
46410
|
_initContextDeps: () => _initContextDeps
|
|
46401
46411
|
});
|
|
46402
46412
|
import { existsSync as existsSync21 } from "fs";
|
|
46403
|
-
import { mkdir as
|
|
46413
|
+
import { mkdir as mkdir8 } from "fs/promises";
|
|
46404
46414
|
import { basename as basename5, join as join42 } from "path";
|
|
46405
46415
|
async function findFiles(dir, maxFiles = 200) {
|
|
46406
46416
|
try {
|
|
@@ -46644,7 +46654,7 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
46644
46654
|
return;
|
|
46645
46655
|
}
|
|
46646
46656
|
if (!existsSync21(naxDir)) {
|
|
46647
|
-
await
|
|
46657
|
+
await mkdir8(naxDir, { recursive: true });
|
|
46648
46658
|
}
|
|
46649
46659
|
const content = generatePackageContextTemplate(packagePath);
|
|
46650
46660
|
await Bun.write(contextPath, content);
|
|
@@ -46659,7 +46669,7 @@ async function initContext(projectRoot, options = {}) {
|
|
|
46659
46669
|
return;
|
|
46660
46670
|
}
|
|
46661
46671
|
if (!existsSync21(naxDir)) {
|
|
46662
|
-
await
|
|
46672
|
+
await mkdir8(naxDir, { recursive: true });
|
|
46663
46673
|
}
|
|
46664
46674
|
const scan = await scanProject(projectRoot);
|
|
46665
46675
|
let content;
|
|
@@ -47427,7 +47437,7 @@ var package_default;
|
|
|
47427
47437
|
var init_package = __esm(() => {
|
|
47428
47438
|
package_default = {
|
|
47429
47439
|
name: "@nathapp/nax",
|
|
47430
|
-
version: "0.64.0-canary.
|
|
47440
|
+
version: "0.64.0-canary.3",
|
|
47431
47441
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
47432
47442
|
type: "module",
|
|
47433
47443
|
bin: {
|
|
@@ -47509,8 +47519,8 @@ var init_version = __esm(() => {
|
|
|
47509
47519
|
NAX_VERSION = package_default.version;
|
|
47510
47520
|
NAX_COMMIT = (() => {
|
|
47511
47521
|
try {
|
|
47512
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
47513
|
-
return "
|
|
47522
|
+
if (/^[0-9a-f]{6,10}$/.test("6caa3726"))
|
|
47523
|
+
return "6caa3726";
|
|
47514
47524
|
} catch {}
|
|
47515
47525
|
try {
|
|
47516
47526
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -48331,7 +48341,7 @@ var init_acceptance_loop = __esm(() => {
|
|
|
48331
48341
|
});
|
|
48332
48342
|
|
|
48333
48343
|
// src/session/scratch-purge.ts
|
|
48334
|
-
import { mkdir as
|
|
48344
|
+
import { mkdir as mkdir10, rename, rm } from "fs/promises";
|
|
48335
48345
|
import { dirname as dirname9, join as join60 } from "path";
|
|
48336
48346
|
async function purgeStaleScratch(projectDir, featureName, retentionDays, archiveInsteadOfDelete = false) {
|
|
48337
48347
|
const sessionsDir = join60(projectDir, ".nax", "features", featureName, "sessions");
|
|
@@ -48383,7 +48393,7 @@ var init_scratch_purge = __esm(() => {
|
|
|
48383
48393
|
readFile: (path16) => Bun.file(path16).text(),
|
|
48384
48394
|
remove: (path16) => rm(path16, { recursive: true, force: true }),
|
|
48385
48395
|
move: async (src, dest) => {
|
|
48386
|
-
await
|
|
48396
|
+
await mkdir10(dirname9(dest), { recursive: true });
|
|
48387
48397
|
await rename(src, dest);
|
|
48388
48398
|
},
|
|
48389
48399
|
now: () => Date.now()
|
|
@@ -49017,7 +49027,7 @@ function precomputeBatchPlan(stories, maxBatchSize = DEFAULT_MAX_BATCH_SIZE) {
|
|
|
49017
49027
|
var DEFAULT_MAX_BATCH_SIZE = 4;
|
|
49018
49028
|
|
|
49019
49029
|
// src/pipeline/subscribers/events-writer.ts
|
|
49020
|
-
import { appendFile as
|
|
49030
|
+
import { appendFile as appendFile5, mkdir as mkdir11 } from "fs/promises";
|
|
49021
49031
|
import { homedir as homedir5 } from "os";
|
|
49022
49032
|
import { basename as basename9, join as join61 } from "path";
|
|
49023
49033
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
@@ -49030,10 +49040,10 @@ function wireEventsWriter(bus, feature, runId, workdir) {
|
|
|
49030
49040
|
return (async () => {
|
|
49031
49041
|
try {
|
|
49032
49042
|
if (!dirReady) {
|
|
49033
|
-
await
|
|
49043
|
+
await mkdir11(eventsDir, { recursive: true });
|
|
49034
49044
|
dirReady = true;
|
|
49035
49045
|
}
|
|
49036
|
-
await
|
|
49046
|
+
await appendFile5(eventsFile, `${JSON.stringify(line)}
|
|
49037
49047
|
`);
|
|
49038
49048
|
} catch (err) {
|
|
49039
49049
|
logger?.warn("events-writer", "Failed to write event line (non-fatal)", {
|
|
@@ -49203,7 +49213,7 @@ var init_interaction2 = __esm(() => {
|
|
|
49203
49213
|
});
|
|
49204
49214
|
|
|
49205
49215
|
// src/pipeline/subscribers/registry.ts
|
|
49206
|
-
import { mkdir as
|
|
49216
|
+
import { mkdir as mkdir12, writeFile } from "fs/promises";
|
|
49207
49217
|
import { homedir as homedir6 } from "os";
|
|
49208
49218
|
import { basename as basename10, join as join62 } from "path";
|
|
49209
49219
|
function wireRegistry(bus, feature, runId, workdir) {
|
|
@@ -49214,7 +49224,7 @@ function wireRegistry(bus, feature, runId, workdir) {
|
|
|
49214
49224
|
const unsub = bus.on("run:started", (_ev) => {
|
|
49215
49225
|
return (async () => {
|
|
49216
49226
|
try {
|
|
49217
|
-
await
|
|
49227
|
+
await mkdir12(runDir, { recursive: true });
|
|
49218
49228
|
const meta3 = {
|
|
49219
49229
|
runId,
|
|
49220
49230
|
project,
|
|
@@ -49556,7 +49566,7 @@ __export(exports_manager, {
|
|
|
49556
49566
|
WorktreeManager: () => WorktreeManager
|
|
49557
49567
|
});
|
|
49558
49568
|
import { existsSync as existsSync30, symlinkSync } from "fs";
|
|
49559
|
-
import { mkdir as
|
|
49569
|
+
import { mkdir as mkdir13 } from "fs/promises";
|
|
49560
49570
|
import { join as join64 } from "path";
|
|
49561
49571
|
|
|
49562
49572
|
class WorktreeManager {
|
|
@@ -49565,7 +49575,7 @@ class WorktreeManager {
|
|
|
49565
49575
|
const infoDir = join64(projectRoot, ".git", "info");
|
|
49566
49576
|
const excludePath = join64(infoDir, "exclude");
|
|
49567
49577
|
try {
|
|
49568
|
-
await
|
|
49578
|
+
await mkdir13(infoDir, { recursive: true });
|
|
49569
49579
|
let existing = "";
|
|
49570
49580
|
if (existsSync30(excludePath)) {
|
|
49571
49581
|
existing = await Bun.file(excludePath).text();
|
|
@@ -52006,7 +52016,7 @@ var exports_precheck_runner = {};
|
|
|
52006
52016
|
__export(exports_precheck_runner, {
|
|
52007
52017
|
runPrecheckValidation: () => runPrecheckValidation
|
|
52008
52018
|
});
|
|
52009
|
-
import { mkdirSync as
|
|
52019
|
+
import { mkdirSync as mkdirSync6 } from "fs";
|
|
52010
52020
|
import path18 from "path";
|
|
52011
52021
|
async function runPrecheckValidation(ctx) {
|
|
52012
52022
|
const logger = getSafeLogger();
|
|
@@ -52022,7 +52032,7 @@ async function runPrecheckValidation(ctx) {
|
|
|
52022
52032
|
silent: true
|
|
52023
52033
|
});
|
|
52024
52034
|
if (ctx.logFilePath) {
|
|
52025
|
-
|
|
52035
|
+
mkdirSync6(path18.dirname(ctx.logFilePath), { recursive: true });
|
|
52026
52036
|
const precheckLog = {
|
|
52027
52037
|
type: "precheck",
|
|
52028
52038
|
timestamp: new Date().toISOString(),
|
|
@@ -83483,7 +83493,7 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
83483
83493
|
|
|
83484
83494
|
// bin/nax.ts
|
|
83485
83495
|
init_source();
|
|
83486
|
-
import { existsSync as existsSync33, mkdirSync as
|
|
83496
|
+
import { existsSync as existsSync33, mkdirSync as mkdirSync7 } from "fs";
|
|
83487
83497
|
import { homedir as homedir8 } from "os";
|
|
83488
83498
|
import { join as join72 } from "path";
|
|
83489
83499
|
|
|
@@ -85347,7 +85357,7 @@ async function runsShowCommand(options) {
|
|
|
85347
85357
|
}
|
|
85348
85358
|
// src/cli/prompts-main.ts
|
|
85349
85359
|
init_logger2();
|
|
85350
|
-
import { existsSync as existsSync19, mkdirSync as
|
|
85360
|
+
import { existsSync as existsSync19, mkdirSync as mkdirSync3 } from "fs";
|
|
85351
85361
|
import { join as join40 } from "path";
|
|
85352
85362
|
|
|
85353
85363
|
// src/pipeline/index.ts
|
|
@@ -85487,7 +85497,7 @@ async function promptsCommand(options) {
|
|
|
85487
85497
|
throw new Error(storyId ? `Story "${storyId}" not found in feature "${feature}"` : `No stories found in feature "${feature}"`);
|
|
85488
85498
|
}
|
|
85489
85499
|
if (outputDir) {
|
|
85490
|
-
|
|
85500
|
+
mkdirSync3(outputDir, { recursive: true });
|
|
85491
85501
|
}
|
|
85492
85502
|
logger.info("cli", "Assembling prompts", {
|
|
85493
85503
|
feature,
|
|
@@ -85569,7 +85579,7 @@ ${"=".repeat(80)}`);
|
|
|
85569
85579
|
}
|
|
85570
85580
|
// src/cli/prompts-init.ts
|
|
85571
85581
|
init_role_task();
|
|
85572
|
-
import { existsSync as existsSync20, mkdirSync as
|
|
85582
|
+
import { existsSync as existsSync20, mkdirSync as mkdirSync4 } from "fs";
|
|
85573
85583
|
import { join as join41 } from "path";
|
|
85574
85584
|
var TEMPLATE_ROLES = [
|
|
85575
85585
|
{ file: "test-writer.md", role: "test-writer" },
|
|
@@ -85595,7 +85605,7 @@ var TEMPLATE_HEADER = `<!--
|
|
|
85595
85605
|
async function promptsInitCommand(options) {
|
|
85596
85606
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
85597
85607
|
const templatesDir = join41(workdir, ".nax", "templates");
|
|
85598
|
-
|
|
85608
|
+
mkdirSync4(templatesDir, { recursive: true });
|
|
85599
85609
|
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync20(join41(templatesDir, f)));
|
|
85600
85610
|
if (existingFiles.length > 0 && !force) {
|
|
85601
85611
|
console.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
@@ -86736,7 +86746,7 @@ function formatValueForTable(value) {
|
|
|
86736
86746
|
// src/cli/config-profile.ts
|
|
86737
86747
|
init_paths();
|
|
86738
86748
|
init_profile();
|
|
86739
|
-
import { mkdirSync as
|
|
86749
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
86740
86750
|
import { readdirSync as readdirSync7 } from "fs";
|
|
86741
86751
|
import { join as join49 } from "path";
|
|
86742
86752
|
var _profileCLIDeps = {
|
|
@@ -86830,7 +86840,7 @@ async function profileCreateCommand(profileName, startDir) {
|
|
|
86830
86840
|
if (await profileFile.exists()) {
|
|
86831
86841
|
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
86832
86842
|
}
|
|
86833
|
-
|
|
86843
|
+
mkdirSync5(profilesDir, { recursive: true });
|
|
86834
86844
|
await Bun.write(profilePath, "{}");
|
|
86835
86845
|
return profilePath;
|
|
86836
86846
|
}
|
|
@@ -86946,7 +86956,7 @@ async function contextInspectCommand(options) {
|
|
|
86946
86956
|
// src/cli/rules.ts
|
|
86947
86957
|
init_canonical_loader();
|
|
86948
86958
|
init_errors();
|
|
86949
|
-
import { mkdir as
|
|
86959
|
+
import { mkdir as mkdir9 } from "fs/promises";
|
|
86950
86960
|
import { basename as basename8, join as join50 } from "path";
|
|
86951
86961
|
var _rulesCLIDeps = {
|
|
86952
86962
|
readFile: async (path15) => Bun.file(path15).text(),
|
|
@@ -86962,7 +86972,7 @@ var _rulesCLIDeps = {
|
|
|
86962
86972
|
}
|
|
86963
86973
|
},
|
|
86964
86974
|
mkdir: async (path15) => {
|
|
86965
|
-
await
|
|
86975
|
+
await mkdir9(path15, { recursive: true });
|
|
86966
86976
|
},
|
|
86967
86977
|
globCanonicalRuleFiles: (workdir) => {
|
|
86968
86978
|
try {
|
|
@@ -95647,8 +95657,8 @@ Next: nax generate --package ${options.package}`));
|
|
|
95647
95657
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
95648
95658
|
return;
|
|
95649
95659
|
}
|
|
95650
|
-
|
|
95651
|
-
|
|
95660
|
+
mkdirSync7(join72(naxDir, "features"), { recursive: true });
|
|
95661
|
+
mkdirSync7(join72(naxDir, "hooks"), { recursive: true });
|
|
95652
95662
|
await Bun.write(join72(naxDir, "config.json"), JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
95653
95663
|
await Bun.write(join72(naxDir, "hooks.json"), JSON.stringify({
|
|
95654
95664
|
hooks: {
|
|
@@ -95817,7 +95827,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
95817
95827
|
}
|
|
95818
95828
|
try {
|
|
95819
95829
|
const planLogDir = join72(featureDir, "plan");
|
|
95820
|
-
|
|
95830
|
+
mkdirSync7(planLogDir, { recursive: true });
|
|
95821
95831
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
95822
95832
|
const planLogPath = join72(planLogDir, `${planLogId}.jsonl`);
|
|
95823
95833
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
@@ -95864,7 +95874,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
95864
95874
|
}
|
|
95865
95875
|
resetLogger();
|
|
95866
95876
|
const runsDir = join72(featureDir, "runs");
|
|
95867
|
-
|
|
95877
|
+
mkdirSync7(runsDir, { recursive: true });
|
|
95868
95878
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
95869
95879
|
const logFilePath = join72(runsDir, `${runId}.jsonl`);
|
|
95870
95880
|
const isTTY = process.stdout.isTTY ?? false;
|
|
@@ -95971,7 +95981,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
95971
95981
|
process.exit(1);
|
|
95972
95982
|
}
|
|
95973
95983
|
const featureDir = join72(naxDir, "features", name);
|
|
95974
|
-
|
|
95984
|
+
mkdirSync7(featureDir, { recursive: true });
|
|
95975
95985
|
await Bun.write(join72(featureDir, "spec.md"), `# Feature: ${name}
|
|
95976
95986
|
|
|
95977
95987
|
## Overview
|
|
@@ -96082,7 +96092,7 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
96082
96092
|
}
|
|
96083
96093
|
const config2 = await loadConfig(workdir, cliOverrides);
|
|
96084
96094
|
const featureLogDir = join72(naxDir, "features", options.feature, "plan");
|
|
96085
|
-
|
|
96095
|
+
mkdirSync7(featureLogDir, { recursive: true });
|
|
96086
96096
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
96087
96097
|
const planLogPath = join72(featureLogDir, `${planLogId}.jsonl`);
|
|
96088
96098
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|