@nathapp/nax 0.57.1-canary.1 → 0.57.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nax.js +1509 -917
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -17829,261 +17829,6 @@ var init_zod = __esm(() => {
|
|
|
17829
17829
|
init_external();
|
|
17830
17830
|
});
|
|
17831
17831
|
|
|
17832
|
-
// src/config/defaults.ts
|
|
17833
|
-
var DEFAULT_CONFIG;
|
|
17834
|
-
var init_defaults = __esm(() => {
|
|
17835
|
-
DEFAULT_CONFIG = {
|
|
17836
|
-
version: 1,
|
|
17837
|
-
models: {
|
|
17838
|
-
claude: {
|
|
17839
|
-
fast: "haiku",
|
|
17840
|
-
balanced: "sonnet",
|
|
17841
|
-
powerful: "opus"
|
|
17842
|
-
}
|
|
17843
|
-
},
|
|
17844
|
-
autoMode: {
|
|
17845
|
-
enabled: true,
|
|
17846
|
-
defaultAgent: "claude",
|
|
17847
|
-
fallbackOrder: ["claude"],
|
|
17848
|
-
complexityRouting: {
|
|
17849
|
-
simple: "fast",
|
|
17850
|
-
medium: "balanced",
|
|
17851
|
-
complex: "powerful",
|
|
17852
|
-
expert: "powerful"
|
|
17853
|
-
},
|
|
17854
|
-
escalation: {
|
|
17855
|
-
enabled: true,
|
|
17856
|
-
tierOrder: [
|
|
17857
|
-
{ tier: "fast", attempts: 5 },
|
|
17858
|
-
{ tier: "balanced", attempts: 3 },
|
|
17859
|
-
{ tier: "powerful", attempts: 2 }
|
|
17860
|
-
],
|
|
17861
|
-
escalateEntireBatch: true
|
|
17862
|
-
}
|
|
17863
|
-
},
|
|
17864
|
-
routing: {
|
|
17865
|
-
strategy: "keyword",
|
|
17866
|
-
llm: {
|
|
17867
|
-
model: "fast",
|
|
17868
|
-
fallbackToKeywords: true,
|
|
17869
|
-
cacheDecisions: true,
|
|
17870
|
-
mode: "hybrid",
|
|
17871
|
-
timeoutMs: 30000
|
|
17872
|
-
}
|
|
17873
|
-
},
|
|
17874
|
-
execution: {
|
|
17875
|
-
maxIterations: 10,
|
|
17876
|
-
iterationDelayMs: 2000,
|
|
17877
|
-
costLimit: 30,
|
|
17878
|
-
sessionTimeoutSeconds: 3600,
|
|
17879
|
-
verificationTimeoutSeconds: 600,
|
|
17880
|
-
maxStoriesPerFeature: 500,
|
|
17881
|
-
rectification: {
|
|
17882
|
-
enabled: true,
|
|
17883
|
-
maxRetries: 2,
|
|
17884
|
-
fullSuiteTimeoutSeconds: 300,
|
|
17885
|
-
maxFailureSummaryChars: 2000,
|
|
17886
|
-
abortOnIncreasingFailures: true,
|
|
17887
|
-
escalateOnExhaustion: true,
|
|
17888
|
-
rethinkAtAttempt: 2,
|
|
17889
|
-
urgencyAtAttempt: 3
|
|
17890
|
-
},
|
|
17891
|
-
regressionGate: {
|
|
17892
|
-
enabled: true,
|
|
17893
|
-
timeoutSeconds: 300,
|
|
17894
|
-
acceptOnTimeout: true,
|
|
17895
|
-
maxRectificationAttempts: 2
|
|
17896
|
-
},
|
|
17897
|
-
contextProviderTokenBudget: 2000,
|
|
17898
|
-
smartTestRunner: true
|
|
17899
|
-
},
|
|
17900
|
-
quality: {
|
|
17901
|
-
requireTypecheck: true,
|
|
17902
|
-
requireLint: true,
|
|
17903
|
-
requireTests: true,
|
|
17904
|
-
commands: {},
|
|
17905
|
-
forceExit: false,
|
|
17906
|
-
detectOpenHandles: true,
|
|
17907
|
-
detectOpenHandlesRetries: 1,
|
|
17908
|
-
gracePeriodMs: 5000,
|
|
17909
|
-
drainTimeoutMs: 2000,
|
|
17910
|
-
shell: "/bin/sh",
|
|
17911
|
-
stripEnvVars: [
|
|
17912
|
-
"CLAUDECODE",
|
|
17913
|
-
"REPL_ID",
|
|
17914
|
-
"AGENT",
|
|
17915
|
-
"GITLAB_ACCESS_TOKEN",
|
|
17916
|
-
"GITHUB_TOKEN",
|
|
17917
|
-
"GITHUB_ACCESS_TOKEN",
|
|
17918
|
-
"GH_TOKEN",
|
|
17919
|
-
"CI_GIT_TOKEN",
|
|
17920
|
-
"CI_JOB_TOKEN",
|
|
17921
|
-
"BITBUCKET_ACCESS_TOKEN",
|
|
17922
|
-
"NPM_TOKEN",
|
|
17923
|
-
"NPM_AUTH_TOKEN",
|
|
17924
|
-
"YARN_NPM_AUTH_TOKEN",
|
|
17925
|
-
"ANTHROPIC_API_KEY",
|
|
17926
|
-
"OPENAI_API_KEY",
|
|
17927
|
-
"GEMINI_API_KEY",
|
|
17928
|
-
"COHERE_API_KEY",
|
|
17929
|
-
"AWS_ACCESS_KEY_ID",
|
|
17930
|
-
"AWS_SECRET_ACCESS_KEY",
|
|
17931
|
-
"AWS_SESSION_TOKEN",
|
|
17932
|
-
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
17933
|
-
"GCLOUD_SERVICE_KEY",
|
|
17934
|
-
"AZURE_CLIENT_SECRET",
|
|
17935
|
-
"AZURE_TENANT_ID",
|
|
17936
|
-
"TELEGRAM_BOT_TOKEN",
|
|
17937
|
-
"SLACK_TOKEN",
|
|
17938
|
-
"SLACK_WEBHOOK_URL",
|
|
17939
|
-
"SENTRY_AUTH_TOKEN",
|
|
17940
|
-
"DATADOG_API_KEY"
|
|
17941
|
-
],
|
|
17942
|
-
testing: {
|
|
17943
|
-
hermetic: true
|
|
17944
|
-
}
|
|
17945
|
-
},
|
|
17946
|
-
tdd: {
|
|
17947
|
-
maxRetries: 2,
|
|
17948
|
-
autoVerifyIsolation: true,
|
|
17949
|
-
autoApproveVerifier: true,
|
|
17950
|
-
strategy: "auto",
|
|
17951
|
-
sessionTiers: {
|
|
17952
|
-
testWriter: "balanced",
|
|
17953
|
-
verifier: "fast"
|
|
17954
|
-
},
|
|
17955
|
-
testWriterAllowedPaths: ["src/index.ts", "src/**/index.ts"],
|
|
17956
|
-
rollbackOnFailure: true,
|
|
17957
|
-
greenfieldDetection: true
|
|
17958
|
-
},
|
|
17959
|
-
constitution: {
|
|
17960
|
-
enabled: true,
|
|
17961
|
-
path: "constitution.md",
|
|
17962
|
-
maxTokens: 2000
|
|
17963
|
-
},
|
|
17964
|
-
analyze: {
|
|
17965
|
-
llmEnhanced: true,
|
|
17966
|
-
model: "balanced",
|
|
17967
|
-
fallbackToKeywords: true,
|
|
17968
|
-
maxCodebaseSummaryTokens: 5000
|
|
17969
|
-
},
|
|
17970
|
-
review: {
|
|
17971
|
-
enabled: true,
|
|
17972
|
-
checks: ["typecheck", "lint"],
|
|
17973
|
-
commands: {},
|
|
17974
|
-
pluginMode: "per-story",
|
|
17975
|
-
semantic: {
|
|
17976
|
-
modelTier: "balanced",
|
|
17977
|
-
rules: [],
|
|
17978
|
-
timeoutMs: 600000,
|
|
17979
|
-
excludePatterns: [":!test/", ":!tests/", ":!*_test.go", ":!*.test.ts", ":!*.spec.ts", ":!**/__tests__/"]
|
|
17980
|
-
}
|
|
17981
|
-
},
|
|
17982
|
-
plan: {
|
|
17983
|
-
model: "balanced",
|
|
17984
|
-
outputPath: "spec.md"
|
|
17985
|
-
},
|
|
17986
|
-
acceptance: {
|
|
17987
|
-
enabled: true,
|
|
17988
|
-
maxRetries: 2,
|
|
17989
|
-
generateTests: true,
|
|
17990
|
-
testPath: ".nax-acceptance.test.ts",
|
|
17991
|
-
model: "fast",
|
|
17992
|
-
refinement: true,
|
|
17993
|
-
redGate: true,
|
|
17994
|
-
timeoutMs: 1800000,
|
|
17995
|
-
fix: {
|
|
17996
|
-
diagnoseModel: "fast",
|
|
17997
|
-
fixModel: "balanced",
|
|
17998
|
-
strategy: "diagnose-first",
|
|
17999
|
-
maxRetries: 2
|
|
18000
|
-
}
|
|
18001
|
-
},
|
|
18002
|
-
context: {
|
|
18003
|
-
fileInjection: "disabled",
|
|
18004
|
-
testCoverage: {
|
|
18005
|
-
enabled: true,
|
|
18006
|
-
detail: "names-and-counts",
|
|
18007
|
-
maxTokens: 500,
|
|
18008
|
-
testPattern: "**/*.test.{ts,js,tsx,jsx}",
|
|
18009
|
-
scopeToStory: true
|
|
18010
|
-
},
|
|
18011
|
-
autoDetect: {
|
|
18012
|
-
enabled: true,
|
|
18013
|
-
maxFiles: 5,
|
|
18014
|
-
traceImports: false
|
|
18015
|
-
}
|
|
18016
|
-
},
|
|
18017
|
-
interaction: {
|
|
18018
|
-
plugin: "cli",
|
|
18019
|
-
config: {},
|
|
18020
|
-
defaults: {
|
|
18021
|
-
timeout: 600000,
|
|
18022
|
-
fallback: "escalate"
|
|
18023
|
-
},
|
|
18024
|
-
triggers: {
|
|
18025
|
-
"security-review": true,
|
|
18026
|
-
"cost-warning": true
|
|
18027
|
-
}
|
|
18028
|
-
},
|
|
18029
|
-
precheck: {
|
|
18030
|
-
storySizeGate: {
|
|
18031
|
-
enabled: true,
|
|
18032
|
-
maxAcCount: 10,
|
|
18033
|
-
maxDescriptionLength: 3000,
|
|
18034
|
-
maxBulletPoints: 12,
|
|
18035
|
-
action: "block",
|
|
18036
|
-
maxReplanAttempts: 3
|
|
18037
|
-
}
|
|
18038
|
-
},
|
|
18039
|
-
prompts: {},
|
|
18040
|
-
agent: {
|
|
18041
|
-
protocol: "acp"
|
|
18042
|
-
},
|
|
18043
|
-
debate: {
|
|
18044
|
-
enabled: false,
|
|
18045
|
-
agents: 3,
|
|
18046
|
-
stages: {
|
|
18047
|
-
plan: {
|
|
18048
|
-
enabled: true,
|
|
18049
|
-
resolver: { type: "synthesis" },
|
|
18050
|
-
sessionMode: "stateful",
|
|
18051
|
-
rounds: 3,
|
|
18052
|
-
timeoutSeconds: 600
|
|
18053
|
-
},
|
|
18054
|
-
review: {
|
|
18055
|
-
enabled: true,
|
|
18056
|
-
resolver: { type: "majority-fail-closed" },
|
|
18057
|
-
sessionMode: "one-shot",
|
|
18058
|
-
rounds: 2,
|
|
18059
|
-
timeoutSeconds: 600
|
|
18060
|
-
},
|
|
18061
|
-
acceptance: {
|
|
18062
|
-
enabled: false,
|
|
18063
|
-
resolver: { type: "majority-fail-closed" },
|
|
18064
|
-
sessionMode: "one-shot",
|
|
18065
|
-
rounds: 1,
|
|
18066
|
-
timeoutSeconds: 600
|
|
18067
|
-
},
|
|
18068
|
-
rectification: {
|
|
18069
|
-
enabled: false,
|
|
18070
|
-
resolver: { type: "synthesis" },
|
|
18071
|
-
sessionMode: "one-shot",
|
|
18072
|
-
rounds: 1,
|
|
18073
|
-
timeoutSeconds: 600
|
|
18074
|
-
},
|
|
18075
|
-
escalation: {
|
|
18076
|
-
enabled: false,
|
|
18077
|
-
resolver: { type: "majority-fail-closed" },
|
|
18078
|
-
sessionMode: "one-shot",
|
|
18079
|
-
rounds: 1,
|
|
18080
|
-
timeoutSeconds: 600
|
|
18081
|
-
}
|
|
18082
|
-
}
|
|
18083
|
-
}
|
|
18084
|
-
};
|
|
18085
|
-
});
|
|
18086
|
-
|
|
18087
17832
|
// src/config/schemas.ts
|
|
18088
17833
|
function isLegacyFlatModels(val) {
|
|
18089
17834
|
if (typeof val !== "object" || val === null)
|
|
@@ -18112,7 +17857,6 @@ var TokenPricingSchema, ModelDefSchema, ModelEntrySchema, PerAgentModelMapSchema
|
|
|
18112
17857
|
})), DebateConfigSchema, NaxConfigSchema;
|
|
18113
17858
|
var init_schemas3 = __esm(() => {
|
|
18114
17859
|
init_zod();
|
|
18115
|
-
init_defaults();
|
|
18116
17860
|
TokenPricingSchema = exports_external.object({
|
|
18117
17861
|
inputPer1M: exports_external.number().min(0),
|
|
18118
17862
|
outputPer1M: exports_external.number().min(0)
|
|
@@ -18127,8 +17871,7 @@ var init_schemas3 = __esm(() => {
|
|
|
18127
17871
|
PerAgentModelMapSchema = exports_external.record(exports_external.string().min(1), exports_external.record(exports_external.string().min(1), ModelEntrySchema));
|
|
18128
17872
|
ModelMapSchema = exports_external.preprocess((val) => {
|
|
18129
17873
|
if (isLegacyFlatModels(val)) {
|
|
18130
|
-
|
|
18131
|
-
return { [defaultAgent]: val };
|
|
17874
|
+
return { claude: val };
|
|
18132
17875
|
}
|
|
18133
17876
|
return val;
|
|
18134
17877
|
}, PerAgentModelMapSchema);
|
|
@@ -18220,6 +17963,19 @@ var init_schemas3 = __esm(() => {
|
|
|
18220
17963
|
formatFix: exports_external.string().optional(),
|
|
18221
17964
|
build: exports_external.string().optional()
|
|
18222
17965
|
}),
|
|
17966
|
+
autofix: exports_external.object({
|
|
17967
|
+
enabled: exports_external.boolean().default(true),
|
|
17968
|
+
maxAttempts: exports_external.number().int().min(1).default(3),
|
|
17969
|
+
maxTotalAttempts: exports_external.number().int().min(1).default(12),
|
|
17970
|
+
rethinkAtAttempt: exports_external.number().int().min(1).default(2),
|
|
17971
|
+
urgencyAtAttempt: exports_external.number().int().min(1).default(3)
|
|
17972
|
+
}).default({
|
|
17973
|
+
enabled: true,
|
|
17974
|
+
maxAttempts: 3,
|
|
17975
|
+
maxTotalAttempts: 12,
|
|
17976
|
+
rethinkAtAttempt: 2,
|
|
17977
|
+
urgencyAtAttempt: 3
|
|
17978
|
+
}),
|
|
18223
17979
|
forceExit: exports_external.boolean().default(false),
|
|
18224
17980
|
detectOpenHandles: exports_external.boolean().default(true),
|
|
18225
17981
|
detectOpenHandlesRetries: exports_external.number().int().min(0).max(5).default(1),
|
|
@@ -18313,10 +18069,10 @@ var init_schemas3 = __esm(() => {
|
|
|
18313
18069
|
timeoutSeconds: exports_external.number().int().positive().default(600)
|
|
18314
18070
|
});
|
|
18315
18071
|
AcceptanceFixConfigSchema = exports_external.object({
|
|
18316
|
-
diagnoseModel: exports_external.string().min(1, "acceptance.fix.diagnoseModel must be non-empty"),
|
|
18317
|
-
fixModel: exports_external.string().min(1, "acceptance.fix.fixModel must be non-empty"),
|
|
18318
|
-
strategy: exports_external.enum(["diagnose-first", "implement-only"]),
|
|
18319
|
-
maxRetries: exports_external.number().int().nonnegative()
|
|
18072
|
+
diagnoseModel: exports_external.string().min(1, "acceptance.fix.diagnoseModel must be non-empty").default("fast"),
|
|
18073
|
+
fixModel: exports_external.string().min(1, "acceptance.fix.fixModel must be non-empty").default("balanced"),
|
|
18074
|
+
strategy: exports_external.enum(["diagnose-first", "implement-only"]).default("diagnose-first"),
|
|
18075
|
+
maxRetries: exports_external.number().int().nonnegative().default(2)
|
|
18320
18076
|
});
|
|
18321
18077
|
AcceptanceConfigSchema = exports_external.object({
|
|
18322
18078
|
enabled: exports_external.boolean(),
|
|
@@ -18436,6 +18192,7 @@ var init_schemas3 = __esm(() => {
|
|
|
18436
18192
|
DebateConfigSchema = exports_external.preprocess(toObject, exports_external.object({
|
|
18437
18193
|
enabled: exports_external.boolean().default(false),
|
|
18438
18194
|
agents: exports_external.number().int().min(2).default(3),
|
|
18195
|
+
maxConcurrentDebaters: exports_external.number().int().min(1).max(10).default(2),
|
|
18439
18196
|
stages: exports_external.preprocess(toObject, exports_external.object({
|
|
18440
18197
|
plan: DebateStageConfigSchema({ enabled: true, resolverType: "synthesis", sessionMode: "stateful", rounds: 3 }),
|
|
18441
18198
|
review: DebateStageConfigSchema({
|
|
@@ -18465,36 +18222,289 @@ var init_schemas3 = __esm(() => {
|
|
|
18465
18222
|
}))
|
|
18466
18223
|
}));
|
|
18467
18224
|
NaxConfigSchema = exports_external.object({
|
|
18468
|
-
version: exports_external.number(),
|
|
18469
|
-
models: ModelMapSchema
|
|
18470
|
-
|
|
18471
|
-
|
|
18472
|
-
|
|
18473
|
-
|
|
18474
|
-
|
|
18475
|
-
|
|
18476
|
-
|
|
18477
|
-
|
|
18478
|
-
|
|
18479
|
-
|
|
18480
|
-
|
|
18225
|
+
version: exports_external.number().default(1),
|
|
18226
|
+
models: ModelMapSchema.default({
|
|
18227
|
+
claude: {
|
|
18228
|
+
fast: "haiku",
|
|
18229
|
+
balanced: "sonnet",
|
|
18230
|
+
powerful: "opus"
|
|
18231
|
+
}
|
|
18232
|
+
}),
|
|
18233
|
+
autoMode: AutoModeConfigSchema.default({
|
|
18234
|
+
enabled: true,
|
|
18235
|
+
defaultAgent: "claude",
|
|
18236
|
+
fallbackOrder: ["claude"],
|
|
18237
|
+
complexityRouting: {
|
|
18238
|
+
simple: "fast",
|
|
18239
|
+
medium: "balanced",
|
|
18240
|
+
complex: "powerful",
|
|
18241
|
+
expert: "powerful"
|
|
18242
|
+
},
|
|
18243
|
+
escalation: {
|
|
18244
|
+
enabled: true,
|
|
18245
|
+
tierOrder: [
|
|
18246
|
+
{ tier: "fast", attempts: 5 },
|
|
18247
|
+
{ tier: "balanced", attempts: 3 },
|
|
18248
|
+
{ tier: "powerful", attempts: 2 }
|
|
18249
|
+
],
|
|
18250
|
+
escalateEntireBatch: true
|
|
18251
|
+
}
|
|
18252
|
+
}),
|
|
18253
|
+
routing: RoutingConfigSchema.default({
|
|
18254
|
+
strategy: "keyword",
|
|
18255
|
+
llm: {
|
|
18256
|
+
model: "fast",
|
|
18257
|
+
fallbackToKeywords: true,
|
|
18258
|
+
cacheDecisions: true,
|
|
18259
|
+
mode: "hybrid",
|
|
18260
|
+
timeoutMs: 30000
|
|
18261
|
+
}
|
|
18262
|
+
}),
|
|
18263
|
+
execution: ExecutionConfigSchema.default({
|
|
18264
|
+
maxIterations: 10,
|
|
18265
|
+
iterationDelayMs: 2000,
|
|
18266
|
+
costLimit: 30,
|
|
18267
|
+
sessionTimeoutSeconds: 3600,
|
|
18268
|
+
verificationTimeoutSeconds: 600,
|
|
18269
|
+
maxStoriesPerFeature: 500,
|
|
18270
|
+
rectification: {
|
|
18271
|
+
enabled: true,
|
|
18272
|
+
maxRetries: 2,
|
|
18273
|
+
fullSuiteTimeoutSeconds: 300,
|
|
18274
|
+
maxFailureSummaryChars: 2000,
|
|
18275
|
+
abortOnIncreasingFailures: true,
|
|
18276
|
+
escalateOnExhaustion: true,
|
|
18277
|
+
rethinkAtAttempt: 2,
|
|
18278
|
+
urgencyAtAttempt: 3
|
|
18279
|
+
},
|
|
18280
|
+
regressionGate: {
|
|
18281
|
+
enabled: true,
|
|
18282
|
+
timeoutSeconds: 300,
|
|
18283
|
+
acceptOnTimeout: true,
|
|
18284
|
+
mode: "deferred",
|
|
18285
|
+
maxRectificationAttempts: 3
|
|
18286
|
+
},
|
|
18287
|
+
contextProviderTokenBudget: 2000,
|
|
18288
|
+
lintCommand: null,
|
|
18289
|
+
typecheckCommand: null,
|
|
18290
|
+
dangerouslySkipPermissions: true,
|
|
18291
|
+
permissionProfile: "unrestricted",
|
|
18292
|
+
smartTestRunner: true
|
|
18293
|
+
}),
|
|
18294
|
+
quality: QualityConfigSchema.default({
|
|
18295
|
+
requireTypecheck: true,
|
|
18296
|
+
requireLint: true,
|
|
18297
|
+
requireTests: true,
|
|
18298
|
+
commands: {},
|
|
18299
|
+
autofix: {
|
|
18300
|
+
enabled: true,
|
|
18301
|
+
maxAttempts: 3,
|
|
18302
|
+
maxTotalAttempts: 12,
|
|
18303
|
+
rethinkAtAttempt: 2,
|
|
18304
|
+
urgencyAtAttempt: 3
|
|
18305
|
+
},
|
|
18306
|
+
forceExit: false,
|
|
18307
|
+
detectOpenHandles: true,
|
|
18308
|
+
detectOpenHandlesRetries: 1,
|
|
18309
|
+
gracePeriodMs: 5000,
|
|
18310
|
+
drainTimeoutMs: 2000,
|
|
18311
|
+
shell: "/bin/sh",
|
|
18312
|
+
stripEnvVars: [
|
|
18313
|
+
"CLAUDECODE",
|
|
18314
|
+
"REPL_ID",
|
|
18315
|
+
"AGENT",
|
|
18316
|
+
"GITLAB_ACCESS_TOKEN",
|
|
18317
|
+
"GITHUB_TOKEN",
|
|
18318
|
+
"GITHUB_ACCESS_TOKEN",
|
|
18319
|
+
"GH_TOKEN",
|
|
18320
|
+
"CI_GIT_TOKEN",
|
|
18321
|
+
"CI_JOB_TOKEN",
|
|
18322
|
+
"BITBUCKET_ACCESS_TOKEN",
|
|
18323
|
+
"NPM_TOKEN",
|
|
18324
|
+
"NPM_AUTH_TOKEN",
|
|
18325
|
+
"YARN_NPM_AUTH_TOKEN",
|
|
18326
|
+
"ANTHROPIC_API_KEY",
|
|
18327
|
+
"OPENAI_API_KEY",
|
|
18328
|
+
"GEMINI_API_KEY",
|
|
18329
|
+
"COHERE_API_KEY",
|
|
18330
|
+
"AWS_ACCESS_KEY_ID",
|
|
18331
|
+
"AWS_SECRET_ACCESS_KEY",
|
|
18332
|
+
"AWS_SESSION_TOKEN",
|
|
18333
|
+
"GOOGLE_APPLICATION_CREDENTIALS",
|
|
18334
|
+
"GCLOUD_SERVICE_KEY",
|
|
18335
|
+
"AZURE_CLIENT_SECRET",
|
|
18336
|
+
"AZURE_TENANT_ID",
|
|
18337
|
+
"TELEGRAM_BOT_TOKEN",
|
|
18338
|
+
"SLACK_TOKEN",
|
|
18339
|
+
"SLACK_WEBHOOK_URL",
|
|
18340
|
+
"SENTRY_AUTH_TOKEN",
|
|
18341
|
+
"DATADOG_API_KEY"
|
|
18342
|
+
],
|
|
18343
|
+
testing: {
|
|
18344
|
+
hermetic: true
|
|
18345
|
+
}
|
|
18346
|
+
}),
|
|
18347
|
+
tdd: TddConfigSchema.default({
|
|
18348
|
+
maxRetries: 2,
|
|
18349
|
+
autoVerifyIsolation: true,
|
|
18350
|
+
autoApproveVerifier: true,
|
|
18351
|
+
strategy: "auto",
|
|
18352
|
+
sessionTiers: {
|
|
18353
|
+
testWriter: "balanced",
|
|
18354
|
+
verifier: "fast"
|
|
18355
|
+
},
|
|
18356
|
+
testWriterAllowedPaths: ["src/index.ts", "src/**/index.ts"],
|
|
18357
|
+
rollbackOnFailure: true,
|
|
18358
|
+
greenfieldDetection: true
|
|
18359
|
+
}),
|
|
18360
|
+
constitution: ConstitutionConfigSchema.default({
|
|
18361
|
+
enabled: true,
|
|
18362
|
+
path: "constitution.md",
|
|
18363
|
+
maxTokens: 2000
|
|
18364
|
+
}),
|
|
18365
|
+
analyze: AnalyzeConfigSchema.default({
|
|
18366
|
+
llmEnhanced: true,
|
|
18367
|
+
model: "balanced",
|
|
18368
|
+
fallbackToKeywords: true,
|
|
18369
|
+
maxCodebaseSummaryTokens: 5000
|
|
18370
|
+
}),
|
|
18371
|
+
review: ReviewConfigSchema.default({
|
|
18372
|
+
enabled: true,
|
|
18373
|
+
checks: ["typecheck", "lint"],
|
|
18374
|
+
commands: {},
|
|
18375
|
+
pluginMode: "per-story",
|
|
18376
|
+
semantic: {
|
|
18377
|
+
modelTier: "balanced",
|
|
18378
|
+
rules: [],
|
|
18379
|
+
timeoutMs: 600000,
|
|
18380
|
+
excludePatterns: [":!test/", ":!tests/", ":!*_test.go", ":!*.test.ts", ":!*.spec.ts", ":!**/__tests__/"]
|
|
18381
|
+
}
|
|
18382
|
+
}),
|
|
18383
|
+
plan: PlanConfigSchema.default({
|
|
18384
|
+
model: "balanced",
|
|
18385
|
+
outputPath: "spec.md",
|
|
18386
|
+
timeoutSeconds: 600
|
|
18387
|
+
}),
|
|
18388
|
+
acceptance: AcceptanceConfigSchema.default({
|
|
18389
|
+
enabled: true,
|
|
18390
|
+
maxRetries: 2,
|
|
18391
|
+
generateTests: true,
|
|
18392
|
+
testPath: ".nax-acceptance.test.ts",
|
|
18393
|
+
model: "fast",
|
|
18394
|
+
refinement: true,
|
|
18395
|
+
redGate: true,
|
|
18396
|
+
timeoutMs: 1800000,
|
|
18397
|
+
fix: {
|
|
18398
|
+
diagnoseModel: "fast",
|
|
18399
|
+
fixModel: "balanced",
|
|
18400
|
+
strategy: "diagnose-first",
|
|
18401
|
+
maxRetries: 2
|
|
18402
|
+
}
|
|
18403
|
+
}),
|
|
18404
|
+
context: ContextConfigSchema.default({
|
|
18405
|
+
fileInjection: "disabled",
|
|
18406
|
+
testCoverage: {
|
|
18407
|
+
enabled: true,
|
|
18408
|
+
detail: "names-and-counts",
|
|
18409
|
+
maxTokens: 500,
|
|
18410
|
+
testPattern: "**/*.test.{ts,js,tsx,jsx}",
|
|
18411
|
+
scopeToStory: true
|
|
18412
|
+
},
|
|
18413
|
+
autoDetect: {
|
|
18414
|
+
enabled: true,
|
|
18415
|
+
maxFiles: 5,
|
|
18416
|
+
traceImports: false
|
|
18417
|
+
}
|
|
18418
|
+
}),
|
|
18481
18419
|
optimizer: OptimizerConfigSchema.optional(),
|
|
18482
18420
|
plugins: exports_external.array(PluginConfigEntrySchema).optional(),
|
|
18483
18421
|
disabledPlugins: exports_external.array(exports_external.string()).optional(),
|
|
18484
18422
|
hooks: HooksConfigSchema.optional(),
|
|
18485
|
-
interaction: InteractionConfigSchema.optional()
|
|
18486
|
-
|
|
18487
|
-
|
|
18423
|
+
interaction: InteractionConfigSchema.optional().default({
|
|
18424
|
+
plugin: "cli",
|
|
18425
|
+
config: {},
|
|
18426
|
+
defaults: {
|
|
18427
|
+
timeout: 600000,
|
|
18428
|
+
fallback: "escalate"
|
|
18429
|
+
},
|
|
18430
|
+
triggers: {
|
|
18431
|
+
"security-review": true,
|
|
18432
|
+
"cost-warning": true
|
|
18433
|
+
}
|
|
18434
|
+
}),
|
|
18435
|
+
agent: AgentConfigSchema.optional().default({
|
|
18436
|
+
protocol: "acp",
|
|
18437
|
+
maxInteractionTurns: 10
|
|
18438
|
+
}),
|
|
18439
|
+
precheck: PrecheckConfigSchema.optional().default({
|
|
18440
|
+
storySizeGate: {
|
|
18441
|
+
enabled: true,
|
|
18442
|
+
maxAcCount: 10,
|
|
18443
|
+
maxDescriptionLength: 3000,
|
|
18444
|
+
maxBulletPoints: 12,
|
|
18445
|
+
action: "block",
|
|
18446
|
+
maxReplanAttempts: 3
|
|
18447
|
+
}
|
|
18448
|
+
}),
|
|
18488
18449
|
prompts: PromptsConfigSchema.optional(),
|
|
18489
18450
|
generate: GenerateConfigSchema.optional(),
|
|
18490
18451
|
project: ProjectProfileSchema.optional(),
|
|
18491
|
-
debate: DebateConfigSchema.optional().default(() =>
|
|
18452
|
+
debate: DebateConfigSchema.optional().default(() => ({
|
|
18453
|
+
enabled: false,
|
|
18454
|
+
agents: 3,
|
|
18455
|
+
maxConcurrentDebaters: 2,
|
|
18456
|
+
stages: {
|
|
18457
|
+
plan: {
|
|
18458
|
+
enabled: true,
|
|
18459
|
+
resolver: { type: "synthesis" },
|
|
18460
|
+
sessionMode: "stateful",
|
|
18461
|
+
rounds: 3,
|
|
18462
|
+
timeoutSeconds: 600
|
|
18463
|
+
},
|
|
18464
|
+
review: {
|
|
18465
|
+
enabled: true,
|
|
18466
|
+
resolver: { type: "majority-fail-closed" },
|
|
18467
|
+
sessionMode: "one-shot",
|
|
18468
|
+
rounds: 2,
|
|
18469
|
+
timeoutSeconds: 600
|
|
18470
|
+
},
|
|
18471
|
+
acceptance: {
|
|
18472
|
+
enabled: false,
|
|
18473
|
+
resolver: { type: "majority-fail-closed" },
|
|
18474
|
+
sessionMode: "one-shot",
|
|
18475
|
+
rounds: 1,
|
|
18476
|
+
timeoutSeconds: 600
|
|
18477
|
+
},
|
|
18478
|
+
rectification: {
|
|
18479
|
+
enabled: false,
|
|
18480
|
+
resolver: { type: "synthesis" },
|
|
18481
|
+
sessionMode: "one-shot",
|
|
18482
|
+
rounds: 1,
|
|
18483
|
+
timeoutSeconds: 600
|
|
18484
|
+
},
|
|
18485
|
+
escalation: {
|
|
18486
|
+
enabled: false,
|
|
18487
|
+
resolver: { type: "majority-fail-closed" },
|
|
18488
|
+
sessionMode: "one-shot",
|
|
18489
|
+
rounds: 1,
|
|
18490
|
+
timeoutSeconds: 600
|
|
18491
|
+
}
|
|
18492
|
+
}
|
|
18493
|
+
})),
|
|
18494
|
+
profile: exports_external.string().default("default")
|
|
18492
18495
|
}).refine((data) => data.version === 1, {
|
|
18493
18496
|
message: "Invalid version: expected 1",
|
|
18494
18497
|
path: ["version"]
|
|
18495
18498
|
});
|
|
18496
18499
|
});
|
|
18497
18500
|
|
|
18501
|
+
// src/config/defaults.ts
|
|
18502
|
+
var DEFAULT_CONFIG;
|
|
18503
|
+
var init_defaults = __esm(() => {
|
|
18504
|
+
init_schemas3();
|
|
18505
|
+
DEFAULT_CONFIG = NaxConfigSchema.parse({});
|
|
18506
|
+
});
|
|
18507
|
+
|
|
18498
18508
|
// src/config/schema.ts
|
|
18499
18509
|
var exports_schema = {};
|
|
18500
18510
|
__export(exports_schema, {
|
|
@@ -20687,26 +20697,179 @@ var init_path_security = () => {};
|
|
|
20687
20697
|
import { homedir as homedir2 } from "os";
|
|
20688
20698
|
import { join as join3, resolve as resolve2 } from "path";
|
|
20689
20699
|
function globalConfigDir() {
|
|
20700
|
+
const override = process.env[GLOBAL_CONFIG_DIR_ENV];
|
|
20701
|
+
if (override)
|
|
20702
|
+
return override;
|
|
20690
20703
|
return join3(homedir2(), ".nax");
|
|
20691
20704
|
}
|
|
20692
|
-
|
|
20705
|
+
function projectConfigDir(projectRoot) {
|
|
20706
|
+
return join3(resolve2(projectRoot), PROJECT_NAX_DIR);
|
|
20707
|
+
}
|
|
20708
|
+
var GLOBAL_CONFIG_DIR_ENV = "NAX_GLOBAL_CONFIG_DIR", PROJECT_NAX_DIR = ".nax";
|
|
20693
20709
|
var init_paths = () => {};
|
|
20694
20710
|
|
|
20711
|
+
// src/config/dotenv.ts
|
|
20712
|
+
function parseDotenv(content) {
|
|
20713
|
+
if (!content)
|
|
20714
|
+
return {};
|
|
20715
|
+
const result = {};
|
|
20716
|
+
for (const rawLine of content.split(`
|
|
20717
|
+
`)) {
|
|
20718
|
+
const line = rawLine.trim();
|
|
20719
|
+
if (!line || line.startsWith("#"))
|
|
20720
|
+
continue;
|
|
20721
|
+
const stripped = line.startsWith("export ") ? line.slice(7).trim() : line;
|
|
20722
|
+
const eqIndex = stripped.indexOf("=");
|
|
20723
|
+
if (eqIndex === -1)
|
|
20724
|
+
continue;
|
|
20725
|
+
const key = stripped.slice(0, eqIndex).trim();
|
|
20726
|
+
let value = stripped.slice(eqIndex + 1).trim();
|
|
20727
|
+
if (value.startsWith('"') && value.endsWith('"') || value.startsWith("'") && value.endsWith("'")) {
|
|
20728
|
+
value = value.slice(1, -1);
|
|
20729
|
+
}
|
|
20730
|
+
result[key] = value;
|
|
20731
|
+
}
|
|
20732
|
+
return result;
|
|
20733
|
+
}
|
|
20734
|
+
function resolveEnvVars(config2, env2) {
|
|
20735
|
+
if (typeof config2 === "string") {
|
|
20736
|
+
return resolveString(config2, env2);
|
|
20737
|
+
}
|
|
20738
|
+
if (Array.isArray(config2)) {
|
|
20739
|
+
return config2.map((item) => resolveEnvVars(item, env2));
|
|
20740
|
+
}
|
|
20741
|
+
if (config2 !== null && typeof config2 === "object") {
|
|
20742
|
+
const result = {};
|
|
20743
|
+
for (const [key, value] of Object.entries(config2)) {
|
|
20744
|
+
result[key] = resolveEnvVars(value, env2);
|
|
20745
|
+
}
|
|
20746
|
+
return result;
|
|
20747
|
+
}
|
|
20748
|
+
return config2;
|
|
20749
|
+
}
|
|
20750
|
+
function resolveString(str, env2) {
|
|
20751
|
+
return str.replace(/\$\$([A-Za-z_][A-Za-z0-9_]*)/g, `${DOUBLE_DOLLAR_PLACEHOLDER}$1`).replace(/\$([A-Za-z_][A-Za-z0-9_]*)/g, (_match, varName) => {
|
|
20752
|
+
if (!(varName in env2)) {
|
|
20753
|
+
throw new Error(`Environment variable $${varName} (${varName}) is not defined`);
|
|
20754
|
+
}
|
|
20755
|
+
return env2[varName];
|
|
20756
|
+
}).replace(new RegExp(`${DOUBLE_DOLLAR_PLACEHOLDER}([A-Za-z_][A-Za-z0-9_]*)`, "g"), "$$$1");
|
|
20757
|
+
}
|
|
20758
|
+
var DOUBLE_DOLLAR_PLACEHOLDER = "__DOLLAR_ESCAPE__";
|
|
20759
|
+
|
|
20760
|
+
// src/config/profile.ts
|
|
20761
|
+
import { readdirSync } from "fs";
|
|
20762
|
+
import { join as join4 } from "path";
|
|
20763
|
+
async function loadProfile(profileName, projectRoot) {
|
|
20764
|
+
const globalPath = join4(globalConfigDir(), "profiles", `${profileName}.json`);
|
|
20765
|
+
const projectPath = join4(projectConfigDir(projectRoot), "profiles", `${profileName}.json`);
|
|
20766
|
+
const globalFile = Bun.file(globalPath);
|
|
20767
|
+
const projectFile = Bun.file(projectPath);
|
|
20768
|
+
const [globalExists, projectExists] = await Promise.all([globalFile.exists(), projectFile.exists()]);
|
|
20769
|
+
if (!globalExists && !projectExists) {
|
|
20770
|
+
const available = await listAvailableProfileNames(projectRoot);
|
|
20771
|
+
const availableList = available.length > 0 ? available.join(", ") : "(none)";
|
|
20772
|
+
throw new Error(`Profile "${profileName}" not found. Available: ${availableList}`);
|
|
20773
|
+
}
|
|
20774
|
+
let base = {};
|
|
20775
|
+
if (globalExists) {
|
|
20776
|
+
base = await globalFile.json();
|
|
20777
|
+
}
|
|
20778
|
+
if (projectExists) {
|
|
20779
|
+
const projectData = await projectFile.json();
|
|
20780
|
+
base = deepMergeConfig(base, projectData);
|
|
20781
|
+
}
|
|
20782
|
+
return base;
|
|
20783
|
+
}
|
|
20784
|
+
async function loadProfileEnv(profileName, projectRoot) {
|
|
20785
|
+
const globalPath = join4(globalConfigDir(), "profiles", `${profileName}.env`);
|
|
20786
|
+
const projectPath = join4(projectConfigDir(projectRoot), "profiles", `${profileName}.env`);
|
|
20787
|
+
const globalFile = Bun.file(globalPath);
|
|
20788
|
+
const projectFile = Bun.file(projectPath);
|
|
20789
|
+
const [globalExists, projectExists] = await Promise.all([globalFile.exists(), projectFile.exists()]);
|
|
20790
|
+
if (!globalExists && !projectExists) {
|
|
20791
|
+
return {};
|
|
20792
|
+
}
|
|
20793
|
+
let merged = {};
|
|
20794
|
+
if (globalExists) {
|
|
20795
|
+
const globalContent = await globalFile.text();
|
|
20796
|
+
merged = { ...merged, ...parseDotenv(globalContent) };
|
|
20797
|
+
}
|
|
20798
|
+
if (projectExists) {
|
|
20799
|
+
const projectContent = await projectFile.text();
|
|
20800
|
+
merged = { ...merged, ...parseDotenv(projectContent) };
|
|
20801
|
+
}
|
|
20802
|
+
return merged;
|
|
20803
|
+
}
|
|
20804
|
+
async function resolveProfileName(cliOptions, env2, projectRoot) {
|
|
20805
|
+
if (cliOptions.profile) {
|
|
20806
|
+
return cliOptions.profile;
|
|
20807
|
+
}
|
|
20808
|
+
if (env2.NAX_PROFILE) {
|
|
20809
|
+
return env2.NAX_PROFILE;
|
|
20810
|
+
}
|
|
20811
|
+
const projectConfigPath = join4(projectConfigDir(projectRoot), "config.json");
|
|
20812
|
+
const projectConfigFile = Bun.file(projectConfigPath);
|
|
20813
|
+
if (await projectConfigFile.exists()) {
|
|
20814
|
+
const config2 = await projectConfigFile.json();
|
|
20815
|
+
if (typeof config2.profile === "string" && config2.profile && config2.profile !== "default") {
|
|
20816
|
+
return config2.profile;
|
|
20817
|
+
}
|
|
20818
|
+
}
|
|
20819
|
+
const globalConfigPath = join4(globalConfigDir(), "config.json");
|
|
20820
|
+
const globalConfigFile = Bun.file(globalConfigPath);
|
|
20821
|
+
if (await globalConfigFile.exists()) {
|
|
20822
|
+
const config2 = await globalConfigFile.json();
|
|
20823
|
+
if (typeof config2.profile === "string" && config2.profile && config2.profile !== "default") {
|
|
20824
|
+
return config2.profile;
|
|
20825
|
+
}
|
|
20826
|
+
}
|
|
20827
|
+
return "default";
|
|
20828
|
+
}
|
|
20829
|
+
async function listAvailableProfileNames(projectRoot) {
|
|
20830
|
+
const entries = await listProfiles(projectRoot);
|
|
20831
|
+
const names = [...new Set(entries.map((e) => e.name))].sort();
|
|
20832
|
+
return names;
|
|
20833
|
+
}
|
|
20834
|
+
async function listProfiles(projectRoot) {
|
|
20835
|
+
const globalProfilesDir = join4(globalConfigDir(), "profiles");
|
|
20836
|
+
const projectProfilesDir = join4(projectConfigDir(projectRoot), "profiles");
|
|
20837
|
+
const entries = [];
|
|
20838
|
+
for (const dir of [globalProfilesDir, projectProfilesDir]) {
|
|
20839
|
+
let files;
|
|
20840
|
+
try {
|
|
20841
|
+
files = readdirSync(dir);
|
|
20842
|
+
} catch {
|
|
20843
|
+
continue;
|
|
20844
|
+
}
|
|
20845
|
+
for (const file3 of files) {
|
|
20846
|
+
if (file3.endsWith(".json")) {
|
|
20847
|
+
const name = file3.replace(/\.json$/, "");
|
|
20848
|
+
entries.push({ name, path: join4(dir, file3) });
|
|
20849
|
+
}
|
|
20850
|
+
}
|
|
20851
|
+
}
|
|
20852
|
+
return entries;
|
|
20853
|
+
}
|
|
20854
|
+
var init_profile = __esm(() => {
|
|
20855
|
+
init_paths();
|
|
20856
|
+
});
|
|
20857
|
+
|
|
20695
20858
|
// src/config/loader.ts
|
|
20696
20859
|
import { existsSync as existsSync4 } from "fs";
|
|
20697
|
-
import { basename, dirname, join as
|
|
20860
|
+
import { basename, dirname, join as join5, resolve as resolve3 } from "path";
|
|
20698
20861
|
function globalConfigPath() {
|
|
20699
|
-
return
|
|
20862
|
+
return join5(globalConfigDir(), "config.json");
|
|
20700
20863
|
}
|
|
20701
20864
|
function findProjectDir(startDir = process.cwd()) {
|
|
20702
20865
|
let dir = resolve3(startDir);
|
|
20703
20866
|
let depth = 0;
|
|
20704
20867
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
20705
|
-
const candidate =
|
|
20706
|
-
if (existsSync4(
|
|
20868
|
+
const candidate = join5(dir, PROJECT_NAX_DIR);
|
|
20869
|
+
if (existsSync4(join5(candidate, "config.json"))) {
|
|
20707
20870
|
return candidate;
|
|
20708
20871
|
}
|
|
20709
|
-
const parent =
|
|
20872
|
+
const parent = join5(dir, "..");
|
|
20710
20873
|
if (parent === dir)
|
|
20711
20874
|
break;
|
|
20712
20875
|
dir = parent;
|
|
@@ -20749,22 +20912,36 @@ function applyBatchModeCompat(conf) {
|
|
|
20749
20912
|
}
|
|
20750
20913
|
async function loadConfig(startDir, cliOverrides) {
|
|
20751
20914
|
let rawConfig = structuredClone(DEFAULT_CONFIG);
|
|
20915
|
+
const projDir = startDir ? basename(startDir) === PROJECT_NAX_DIR ? startDir : findProjectDir(startDir) : findProjectDir();
|
|
20916
|
+
const projectRoot = startDir ? basename(startDir) === PROJECT_NAX_DIR ? dirname(startDir) : startDir : process.cwd();
|
|
20917
|
+
const profileName = await resolveProfileName(cliOverrides ?? {}, process.env, projectRoot);
|
|
20752
20918
|
const globalConfRaw = await loadJsonFile(globalConfigPath(), "config");
|
|
20753
20919
|
if (globalConfRaw) {
|
|
20754
|
-
const
|
|
20920
|
+
const { profile: _gProfile, ...globalConfStripped } = globalConfRaw;
|
|
20921
|
+
const globalConf = applyBatchModeCompat(applyRemovedStrategyCompat(globalConfStripped));
|
|
20755
20922
|
rawConfig = deepMergeConfig(rawConfig, globalConf);
|
|
20756
20923
|
}
|
|
20757
|
-
const projDir = startDir ? basename(startDir) === PROJECT_NAX_DIR ? startDir : findProjectDir(startDir) : findProjectDir();
|
|
20758
20924
|
if (projDir) {
|
|
20759
|
-
const projConf = await loadJsonFile(
|
|
20925
|
+
const projConf = await loadJsonFile(join5(projDir, "config.json"), "config");
|
|
20760
20926
|
if (projConf) {
|
|
20761
|
-
const
|
|
20927
|
+
const { profile: _pProfile, ...projConfStripped } = projConf;
|
|
20928
|
+
const resolvedProjConf = applyBatchModeCompat(applyRemovedStrategyCompat(projConfStripped));
|
|
20762
20929
|
rawConfig = deepMergeConfig(rawConfig, resolvedProjConf);
|
|
20763
20930
|
}
|
|
20764
20931
|
}
|
|
20932
|
+
if (profileName !== "default") {
|
|
20933
|
+
const profileData = await loadProfile(profileName, projectRoot);
|
|
20934
|
+
rawConfig = deepMergeConfig(rawConfig, profileData);
|
|
20935
|
+
await loadProfileEnv(profileName, projectRoot);
|
|
20936
|
+
}
|
|
20765
20937
|
if (cliOverrides) {
|
|
20766
20938
|
rawConfig = deepMergeConfig(rawConfig, cliOverrides);
|
|
20767
20939
|
}
|
|
20940
|
+
rawConfig.profile = profileName;
|
|
20941
|
+
const hasMergedConfigs = globalConfRaw || projDir !== null || cliOverrides !== undefined || profileName !== "default";
|
|
20942
|
+
if (!hasMergedConfigs) {
|
|
20943
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
20944
|
+
}
|
|
20768
20945
|
const result = NaxConfigSchema.safeParse(rawConfig);
|
|
20769
20946
|
if (!result.success) {
|
|
20770
20947
|
const errors3 = result.error.issues.map((err) => {
|
|
@@ -20786,7 +20963,7 @@ async function loadConfigForWorkdir(rootConfigPath, packageDir) {
|
|
|
20786
20963
|
return rootConfig;
|
|
20787
20964
|
}
|
|
20788
20965
|
const repoRoot = dirname(rootNaxDir);
|
|
20789
|
-
const packageConfigPath =
|
|
20966
|
+
const packageConfigPath = join5(repoRoot, PROJECT_NAX_DIR, "mono", packageDir, "config.json");
|
|
20790
20967
|
const packageOverride = await loadJsonFile(packageConfigPath, "config");
|
|
20791
20968
|
if (!packageOverride) {
|
|
20792
20969
|
logger.debug("config", "Per-package config not found \u2014 falling back to root config", {
|
|
@@ -20803,6 +20980,7 @@ var init_loader = __esm(() => {
|
|
|
20803
20980
|
init_json_file();
|
|
20804
20981
|
init_path_security();
|
|
20805
20982
|
init_paths();
|
|
20983
|
+
init_profile();
|
|
20806
20984
|
init_schema();
|
|
20807
20985
|
});
|
|
20808
20986
|
// src/config/index.ts
|
|
@@ -20811,6 +20989,7 @@ var init_config = __esm(() => {
|
|
|
20811
20989
|
init_loader();
|
|
20812
20990
|
init_path_security();
|
|
20813
20991
|
init_paths();
|
|
20992
|
+
init_profile();
|
|
20814
20993
|
});
|
|
20815
20994
|
|
|
20816
20995
|
// src/utils/errors.ts
|
|
@@ -20996,7 +21175,7 @@ __export(exports_generator, {
|
|
|
20996
21175
|
acceptanceTestFilename: () => acceptanceTestFilename,
|
|
20997
21176
|
_generatorPRDDeps: () => _generatorPRDDeps
|
|
20998
21177
|
});
|
|
20999
|
-
import { join as
|
|
21178
|
+
import { join as join6 } from "path";
|
|
21000
21179
|
function skeletonImportLine(testFramework) {
|
|
21001
21180
|
if (!testFramework)
|
|
21002
21181
|
return `import { describe, test, expect } from "bun:test";`;
|
|
@@ -21092,7 +21271,7 @@ Rules:
|
|
|
21092
21271
|
- Every test MUST have real assertions that PASS when the feature is correctly implemented and FAIL when it is broken
|
|
21093
21272
|
- **Prefer behavioral tests** \u2014 import functions and call them rather than reading source files. For example, to verify "getPostRunActions() returns empty array", import PluginRegistry and call getPostRunActions(), don't grep the source file for the method name.
|
|
21094
21273
|
- **File output (REQUIRED)**: Write the acceptance test file DIRECTLY to the path shown below. Do NOT output the test code in your response. After writing the file, reply with a brief confirmation.
|
|
21095
|
-
- **Path anchor (CRITICAL)**: Write the test file to this exact path: \`${
|
|
21274
|
+
- **Path anchor (CRITICAL)**: Write the test file to this exact path: \`${join6(options.workdir, ".nax", "features", options.featureName, resolveAcceptanceTestFile(options.language, options.config?.acceptance?.testPath))}\`. Import from package sources using relative paths like \`../../../src/...\` (3 levels up from \`.nax/features/<name>/\` to the package root).`;
|
|
21096
21275
|
const prompt = basePrompt;
|
|
21097
21276
|
logger.info("acceptance", "Generating tests from PRD refined criteria", { count: refinedCriteria.length });
|
|
21098
21277
|
const completeResult = await (options.adapter ?? _generatorPRDDeps.adapter).complete(prompt, {
|
|
@@ -21111,7 +21290,7 @@ Rules:
|
|
|
21111
21290
|
outputPreview: rawOutput.slice(0, 300)
|
|
21112
21291
|
});
|
|
21113
21292
|
if (!testCode) {
|
|
21114
|
-
const targetPath =
|
|
21293
|
+
const targetPath = join6(options.workdir, ".nax", "features", options.featureName, resolveAcceptanceTestFile(options.language, options.config?.acceptance?.testPath));
|
|
21115
21294
|
let recoveryFailed = false;
|
|
21116
21295
|
logger.debug("acceptance", "BUG-076 recovery: checking for agent-written file", { targetPath });
|
|
21117
21296
|
try {
|
|
@@ -21166,7 +21345,7 @@ Rules:
|
|
|
21166
21345
|
testable: c.testable,
|
|
21167
21346
|
storyId: c.storyId
|
|
21168
21347
|
})), null, 2);
|
|
21169
|
-
await _generatorPRDDeps.writeFile(
|
|
21348
|
+
await _generatorPRDDeps.writeFile(join6(options.featureDir, "acceptance-refined.json"), refinedJsonContent);
|
|
21170
21349
|
return { testCode, criteria };
|
|
21171
21350
|
}
|
|
21172
21351
|
function parseAcceptanceCriteria(specContent) {
|
|
@@ -22163,7 +22342,7 @@ var package_default;
|
|
|
22163
22342
|
var init_package = __esm(() => {
|
|
22164
22343
|
package_default = {
|
|
22165
22344
|
name: "@nathapp/nax",
|
|
22166
|
-
version: "0.57.
|
|
22345
|
+
version: "0.57.2",
|
|
22167
22346
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22168
22347
|
type: "module",
|
|
22169
22348
|
bin: {
|
|
@@ -22242,8 +22421,8 @@ var init_version = __esm(() => {
|
|
|
22242
22421
|
NAX_VERSION = package_default.version;
|
|
22243
22422
|
NAX_COMMIT = (() => {
|
|
22244
22423
|
try {
|
|
22245
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22246
|
-
return "
|
|
22424
|
+
if (/^[0-9a-f]{6,10}$/.test("2ffb62ec"))
|
|
22425
|
+
return "2ffb62ec";
|
|
22247
22426
|
} catch {}
|
|
22248
22427
|
try {
|
|
22249
22428
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -22390,6 +22569,27 @@ var init_prd = __esm(() => {
|
|
|
22390
22569
|
PRD_MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
22391
22570
|
});
|
|
22392
22571
|
|
|
22572
|
+
// src/debate/concurrency.ts
|
|
22573
|
+
async function allSettledBounded(tasks, limit) {
|
|
22574
|
+
if (tasks.length === 0)
|
|
22575
|
+
return [];
|
|
22576
|
+
const results = new Array(tasks.length);
|
|
22577
|
+
let nextIndex = 0;
|
|
22578
|
+
async function worker() {
|
|
22579
|
+
while (nextIndex < tasks.length) {
|
|
22580
|
+
const i = nextIndex++;
|
|
22581
|
+
try {
|
|
22582
|
+
results[i] = { status: "fulfilled", value: await tasks[i]() };
|
|
22583
|
+
} catch (reason) {
|
|
22584
|
+
results[i] = { status: "rejected", reason };
|
|
22585
|
+
}
|
|
22586
|
+
}
|
|
22587
|
+
}
|
|
22588
|
+
const concurrency = Math.max(1, Math.min(limit, tasks.length));
|
|
22589
|
+
await Promise.all(Array.from({ length: concurrency }, () => worker()));
|
|
22590
|
+
return results;
|
|
22591
|
+
}
|
|
22592
|
+
|
|
22393
22593
|
// src/debate/prompts.ts
|
|
22394
22594
|
function buildCritiquePrompt(taskPrompt, allProposals, debaterIndex) {
|
|
22395
22595
|
const othersProposals = allProposals.filter((_, i) => i !== debaterIndex);
|
|
@@ -22496,7 +22696,7 @@ var DEFAULT_FALLBACK_AGENT = "claude";
|
|
|
22496
22696
|
var init_resolvers = () => {};
|
|
22497
22697
|
|
|
22498
22698
|
// src/debate/session.ts
|
|
22499
|
-
import { join as
|
|
22699
|
+
import { join as join12 } from "path";
|
|
22500
22700
|
function resolveDebaterModel(debater, config2) {
|
|
22501
22701
|
const tier = debater.model ?? "fast";
|
|
22502
22702
|
if (!config2?.models)
|
|
@@ -22661,7 +22861,10 @@ class DebateSession {
|
|
|
22661
22861
|
stage: this.stage,
|
|
22662
22862
|
debaters: resolved.map((r) => r.debater.agent)
|
|
22663
22863
|
});
|
|
22664
|
-
const
|
|
22864
|
+
const cfg = this.config;
|
|
22865
|
+
const debate = cfg?.debate;
|
|
22866
|
+
const concurrencyLimit = debate?.maxConcurrentDebaters ?? 2;
|
|
22867
|
+
const proposalSettled = await allSettledBounded(resolved.map(({ debater, adapter }, debaterIdx) => () => this.runStatefulTurn(adapter, debater, prompt, `debate-${this.stage}-${debaterIdx}`, config2.rounds > 1)), concurrencyLimit);
|
|
22665
22868
|
const successfulProposals = proposalSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
22666
22869
|
for (const r of proposalSettled) {
|
|
22667
22870
|
if (r.status === "fulfilled") {
|
|
@@ -22741,7 +22944,7 @@ class DebateSession {
|
|
|
22741
22944
|
let critiqueOutputs = [];
|
|
22742
22945
|
if (config2.rounds > 1) {
|
|
22743
22946
|
const proposalOutputs2 = successfulProposals.map((s) => s.output);
|
|
22744
|
-
const critiqueSettled = await
|
|
22947
|
+
const critiqueSettled = await allSettledBounded(successfulProposals.map((proposal, successfulIdx) => () => this.runStatefulTurn(proposal.adapter, proposal.debater, buildCritiquePrompt(prompt, proposalOutputs2, successfulIdx), proposal.roleKey ?? `debate-${this.stage}-${successfulIdx}`, false)), concurrencyLimit);
|
|
22745
22948
|
for (const r of critiqueSettled) {
|
|
22746
22949
|
if (r.status === "fulfilled") {
|
|
22747
22950
|
totalCostUsd += r.value.cost;
|
|
@@ -22791,14 +22994,17 @@ class DebateSession {
|
|
|
22791
22994
|
stage: this.stage,
|
|
22792
22995
|
debaters: resolved.map((r) => r.debater.agent)
|
|
22793
22996
|
});
|
|
22794
|
-
const
|
|
22997
|
+
const cfg = this.config;
|
|
22998
|
+
const debate = cfg?.debate;
|
|
22999
|
+
const concurrencyLimit = debate?.maxConcurrentDebaters ?? 2;
|
|
23000
|
+
const proposalSettled = await allSettledBounded(resolved.map(({ debater, adapter }, i) => () => runComplete(adapter, prompt, {
|
|
22795
23001
|
model: resolveDebaterModel(debater, this.config),
|
|
22796
23002
|
featureName: this.stage,
|
|
22797
23003
|
config: this.config,
|
|
22798
23004
|
storyId: this.storyId,
|
|
22799
|
-
sessionRole:
|
|
23005
|
+
sessionRole: `debate-proposal-${i}`,
|
|
22800
23006
|
timeoutMs: this.timeoutMs
|
|
22801
|
-
}, modelTierFromDebater(debater)).then((result) => ({ debater, adapter, output: result.output, cost: result.costUsd }))));
|
|
23007
|
+
}, modelTierFromDebater(debater)).then((result) => ({ debater, adapter, output: result.output, cost: result.costUsd }))), concurrencyLimit);
|
|
22802
23008
|
const successful = proposalSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
22803
23009
|
for (const r of proposalSettled) {
|
|
22804
23010
|
if (r.status === "fulfilled") {
|
|
@@ -22877,14 +23083,14 @@ class DebateSession {
|
|
|
22877
23083
|
let critiqueOutputs = [];
|
|
22878
23084
|
if (config2.rounds > 1) {
|
|
22879
23085
|
const proposalOutputs2 = successful.map((p) => p.output);
|
|
22880
|
-
const critiqueSettled = await
|
|
23086
|
+
const critiqueSettled = await allSettledBounded(successful.map(({ debater, adapter }, i) => () => runComplete(adapter, buildCritiquePrompt(prompt, proposalOutputs2, i), {
|
|
22881
23087
|
model: resolveDebaterModel(debater, this.config),
|
|
22882
23088
|
featureName: this.stage,
|
|
22883
23089
|
config: this.config,
|
|
22884
23090
|
storyId: this.storyId,
|
|
22885
|
-
sessionRole:
|
|
23091
|
+
sessionRole: `debate-critique-${i}`,
|
|
22886
23092
|
timeoutMs: this.timeoutMs
|
|
22887
|
-
}, modelTierFromDebater(debater))));
|
|
23093
|
+
}, modelTierFromDebater(debater))), concurrencyLimit);
|
|
22888
23094
|
for (const r of critiqueSettled) {
|
|
22889
23095
|
if (r.status === "fulfilled") {
|
|
22890
23096
|
totalCostUsd += r.value.costUsd;
|
|
@@ -22934,8 +23140,11 @@ class DebateSession {
|
|
|
22934
23140
|
stage: this.stage,
|
|
22935
23141
|
debaters: resolved.map((r) => r.debater.agent)
|
|
22936
23142
|
});
|
|
22937
|
-
const
|
|
22938
|
-
|
|
23143
|
+
const cfg = this.config;
|
|
23144
|
+
const debate = cfg?.debate;
|
|
23145
|
+
const concurrencyLimit = debate?.maxConcurrentDebaters ?? 2;
|
|
23146
|
+
const settled = await allSettledBounded(resolved.map(({ debater, adapter }, i) => async () => {
|
|
23147
|
+
const tempOutputPath = join12(opts.outputDir, `prd-debate-${i}.json`);
|
|
22939
23148
|
const debaterPrompt = `${basePrompt}
|
|
22940
23149
|
|
|
22941
23150
|
Write the PRD JSON directly to this file path: ${tempOutputPath}
|
|
@@ -22950,12 +23159,28 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
22950
23159
|
dangerouslySkipPermissions: opts.dangerouslySkipPermissions,
|
|
22951
23160
|
maxInteractionTurns: opts.maxInteractionTurns,
|
|
22952
23161
|
featureName: opts.feature,
|
|
22953
|
-
|
|
23162
|
+
storyId: this.storyId,
|
|
23163
|
+
sessionRole: `plan-${i}`
|
|
22954
23164
|
});
|
|
22955
23165
|
const output = await _debateSessionDeps.readFile(tempOutputPath);
|
|
22956
23166
|
return { debater, adapter, output, cost: 0 };
|
|
22957
|
-
}));
|
|
22958
|
-
const successful =
|
|
23167
|
+
}), concurrencyLimit);
|
|
23168
|
+
const successful = [];
|
|
23169
|
+
for (let i = 0;i < settled.length; i++) {
|
|
23170
|
+
const res = settled[i];
|
|
23171
|
+
if (res.status === "fulfilled") {
|
|
23172
|
+
successful.push(res.value);
|
|
23173
|
+
} else {
|
|
23174
|
+
const { debater } = resolved[i];
|
|
23175
|
+
logger?.warn("debate", "debate:debater-failed", {
|
|
23176
|
+
storyId: this.storyId,
|
|
23177
|
+
stage: this.stage,
|
|
23178
|
+
debaterIndex: i,
|
|
23179
|
+
agent: debater.agent,
|
|
23180
|
+
error: res.reason instanceof Error ? res.reason.message : String(res.reason)
|
|
23181
|
+
});
|
|
23182
|
+
}
|
|
23183
|
+
}
|
|
22959
23184
|
for (let i = 0;i < successful.length; i++) {
|
|
22960
23185
|
logger?.info("debate", "debate:proposal", {
|
|
22961
23186
|
storyId: this.storyId,
|
|
@@ -25881,59 +26106,6 @@ ${stderr}`;
|
|
|
25881
26106
|
};
|
|
25882
26107
|
});
|
|
25883
26108
|
|
|
25884
|
-
// src/agents/claude/index.ts
|
|
25885
|
-
var init_claude = __esm(() => {
|
|
25886
|
-
init_adapter3();
|
|
25887
|
-
init_execution();
|
|
25888
|
-
});
|
|
25889
|
-
|
|
25890
|
-
// src/agents/shared/validation.ts
|
|
25891
|
-
function validateAgentForTier(agent, tier) {
|
|
25892
|
-
return agent.capabilities.supportedTiers.includes(tier);
|
|
25893
|
-
}
|
|
25894
|
-
function validateAgentFeature(agent, feature) {
|
|
25895
|
-
return agent.capabilities.features.has(feature);
|
|
25896
|
-
}
|
|
25897
|
-
function describeAgentCapabilities(agent) {
|
|
25898
|
-
const tiers = agent.capabilities.supportedTiers.join(",");
|
|
25899
|
-
const features = Array.from(agent.capabilities.features).join(",");
|
|
25900
|
-
const maxTokens = agent.capabilities.maxContextTokens;
|
|
25901
|
-
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
25902
|
-
}
|
|
25903
|
-
|
|
25904
|
-
// src/agents/index.ts
|
|
25905
|
-
var exports_agents = {};
|
|
25906
|
-
__export(exports_agents, {
|
|
25907
|
-
validateAgentForTier: () => validateAgentForTier,
|
|
25908
|
-
validateAgentFeature: () => validateAgentFeature,
|
|
25909
|
-
parseTokenUsage: () => parseTokenUsage,
|
|
25910
|
-
getInstalledAgents: () => getInstalledAgents,
|
|
25911
|
-
getAllAgentNames: () => getAllAgentNames,
|
|
25912
|
-
getAgentVersions: () => getAgentVersions,
|
|
25913
|
-
getAgentVersion: () => getAgentVersion,
|
|
25914
|
-
getAgent: () => getAgent,
|
|
25915
|
-
formatCostWithConfidence: () => formatCostWithConfidence,
|
|
25916
|
-
estimateCostFromTokenUsage: () => estimateCostFromTokenUsage,
|
|
25917
|
-
estimateCostFromOutput: () => estimateCostFromOutput,
|
|
25918
|
-
estimateCostByDuration: () => estimateCostByDuration,
|
|
25919
|
-
estimateCost: () => estimateCost,
|
|
25920
|
-
describeAgentCapabilities: () => describeAgentCapabilities,
|
|
25921
|
-
checkAgentHealth: () => checkAgentHealth,
|
|
25922
|
-
MODEL_PRICING: () => MODEL_PRICING,
|
|
25923
|
-
CompleteError: () => CompleteError,
|
|
25924
|
-
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
25925
|
-
COST_RATES: () => COST_RATES,
|
|
25926
|
-
AllAgentsUnavailableError: () => AllAgentsUnavailableError
|
|
25927
|
-
});
|
|
25928
|
-
var init_agents = __esm(() => {
|
|
25929
|
-
init_types2();
|
|
25930
|
-
init_claude();
|
|
25931
|
-
init_registry();
|
|
25932
|
-
init_cost();
|
|
25933
|
-
init_version_detection();
|
|
25934
|
-
init_errors();
|
|
25935
|
-
});
|
|
25936
|
-
|
|
25937
26109
|
// src/pipeline/stages/acceptance-setup.ts
|
|
25938
26110
|
var exports_acceptance_setup = {};
|
|
25939
26111
|
__export(exports_acceptance_setup, {
|
|
@@ -26077,7 +26249,6 @@ ${stderr}` };
|
|
|
26077
26249
|
}
|
|
26078
26250
|
if (shouldGenerate) {
|
|
26079
26251
|
totalCriteria = allCriteria.length;
|
|
26080
|
-
const { getAgent: getAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
26081
26252
|
const agent = (ctx.agentGetFn ?? _acceptanceSetupDeps.getAgent)(ctx.config.autoMode.defaultAgent);
|
|
26082
26253
|
let allRefinedCriteria;
|
|
26083
26254
|
if (ctx.config.acceptance.refinement) {
|
|
@@ -26105,7 +26276,7 @@ ${stderr}` };
|
|
|
26105
26276
|
testableCount = allRefinedCriteria.filter((r) => r.testable).length;
|
|
26106
26277
|
for (const [workdir, group] of workdirGroups) {
|
|
26107
26278
|
const packageDir = workdir ? path5.join(ctx.workdir, workdir) : ctx.workdir;
|
|
26108
|
-
const testPath = path5.join(packageDir, resolveAcceptanceTestFile(language, testPathConfig));
|
|
26279
|
+
const testPath = path5.join(packageDir, ".nax", "features", featureName, resolveAcceptanceTestFile(language, testPathConfig));
|
|
26109
26280
|
const groupStoryIds = new Set(group.stories.map((s) => s.id));
|
|
26110
26281
|
const groupRefined = allRefinedCriteria.filter((r) => groupStoryIds.has(r.storyId));
|
|
26111
26282
|
const result = await _acceptanceSetupDeps.generate(group.stories, groupRefined, {
|
|
@@ -26162,6 +26333,59 @@ ${stderr}` };
|
|
|
26162
26333
|
};
|
|
26163
26334
|
});
|
|
26164
26335
|
|
|
26336
|
+
// src/agents/claude/index.ts
|
|
26337
|
+
var init_claude = __esm(() => {
|
|
26338
|
+
init_adapter3();
|
|
26339
|
+
init_execution();
|
|
26340
|
+
});
|
|
26341
|
+
|
|
26342
|
+
// src/agents/shared/validation.ts
|
|
26343
|
+
function validateAgentForTier(agent, tier) {
|
|
26344
|
+
return agent.capabilities.supportedTiers.includes(tier);
|
|
26345
|
+
}
|
|
26346
|
+
function validateAgentFeature(agent, feature) {
|
|
26347
|
+
return agent.capabilities.features.has(feature);
|
|
26348
|
+
}
|
|
26349
|
+
function describeAgentCapabilities(agent) {
|
|
26350
|
+
const tiers = agent.capabilities.supportedTiers.join(",");
|
|
26351
|
+
const features = Array.from(agent.capabilities.features).join(",");
|
|
26352
|
+
const maxTokens = agent.capabilities.maxContextTokens;
|
|
26353
|
+
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
26354
|
+
}
|
|
26355
|
+
|
|
26356
|
+
// src/agents/index.ts
|
|
26357
|
+
var exports_agents = {};
|
|
26358
|
+
__export(exports_agents, {
|
|
26359
|
+
validateAgentForTier: () => validateAgentForTier,
|
|
26360
|
+
validateAgentFeature: () => validateAgentFeature,
|
|
26361
|
+
parseTokenUsage: () => parseTokenUsage,
|
|
26362
|
+
getInstalledAgents: () => getInstalledAgents,
|
|
26363
|
+
getAllAgentNames: () => getAllAgentNames,
|
|
26364
|
+
getAgentVersions: () => getAgentVersions,
|
|
26365
|
+
getAgentVersion: () => getAgentVersion,
|
|
26366
|
+
getAgent: () => getAgent,
|
|
26367
|
+
formatCostWithConfidence: () => formatCostWithConfidence,
|
|
26368
|
+
estimateCostFromTokenUsage: () => estimateCostFromTokenUsage,
|
|
26369
|
+
estimateCostFromOutput: () => estimateCostFromOutput,
|
|
26370
|
+
estimateCostByDuration: () => estimateCostByDuration,
|
|
26371
|
+
estimateCost: () => estimateCost,
|
|
26372
|
+
describeAgentCapabilities: () => describeAgentCapabilities,
|
|
26373
|
+
checkAgentHealth: () => checkAgentHealth,
|
|
26374
|
+
MODEL_PRICING: () => MODEL_PRICING,
|
|
26375
|
+
CompleteError: () => CompleteError,
|
|
26376
|
+
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
26377
|
+
COST_RATES: () => COST_RATES,
|
|
26378
|
+
AllAgentsUnavailableError: () => AllAgentsUnavailableError
|
|
26379
|
+
});
|
|
26380
|
+
var init_agents = __esm(() => {
|
|
26381
|
+
init_types2();
|
|
26382
|
+
init_claude();
|
|
26383
|
+
init_registry();
|
|
26384
|
+
init_cost();
|
|
26385
|
+
init_version_detection();
|
|
26386
|
+
init_errors();
|
|
26387
|
+
});
|
|
26388
|
+
|
|
26165
26389
|
// src/quality/runner.ts
|
|
26166
26390
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
26167
26391
|
async function runQualityCommand(opts) {
|
|
@@ -26251,6 +26475,59 @@ var init_quality = __esm(() => {
|
|
|
26251
26475
|
init_runner2();
|
|
26252
26476
|
});
|
|
26253
26477
|
|
|
26478
|
+
// src/verification/shared-rectification-loop.ts
|
|
26479
|
+
function resolveLogData(data, state) {
|
|
26480
|
+
if (!data) {
|
|
26481
|
+
return;
|
|
26482
|
+
}
|
|
26483
|
+
return typeof data === "function" ? data(state) : data;
|
|
26484
|
+
}
|
|
26485
|
+
function buildProgressivePromptPreamble(opts) {
|
|
26486
|
+
const rethinkAt = Math.min(opts.rethinkAtAttempt ?? 2, opts.maxAttempts);
|
|
26487
|
+
const urgencyAt = Math.min(opts.urgencyAtAttempt ?? 3, opts.maxAttempts);
|
|
26488
|
+
const shouldRethink = opts.attempt >= rethinkAt;
|
|
26489
|
+
const shouldUrgency = opts.attempt >= urgencyAt;
|
|
26490
|
+
if (!shouldRethink && !shouldUrgency) {
|
|
26491
|
+
return "";
|
|
26492
|
+
}
|
|
26493
|
+
if (shouldUrgency) {
|
|
26494
|
+
opts.logger?.info(opts.stage, "Progressive prompt escalation: urgency + rethink injected", {
|
|
26495
|
+
attempt: opts.attempt,
|
|
26496
|
+
rethinkAtAttempt: rethinkAt,
|
|
26497
|
+
urgencyAtAttempt: urgencyAt,
|
|
26498
|
+
maxAttempts: opts.maxAttempts
|
|
26499
|
+
});
|
|
26500
|
+
} else {
|
|
26501
|
+
opts.logger?.info(opts.stage, "Progressive prompt escalation: rethink injected", {
|
|
26502
|
+
attempt: opts.attempt,
|
|
26503
|
+
rethinkAtAttempt: rethinkAt,
|
|
26504
|
+
maxAttempts: opts.maxAttempts
|
|
26505
|
+
});
|
|
26506
|
+
}
|
|
26507
|
+
const urgencySection = shouldUrgency ? opts.urgencySection : "";
|
|
26508
|
+
const rethinkSection = shouldRethink ? opts.rethinkSection : "";
|
|
26509
|
+
return `${urgencySection}${rethinkSection}`;
|
|
26510
|
+
}
|
|
26511
|
+
async function runSharedRectificationLoop(opts) {
|
|
26512
|
+
opts.logger?.info(opts.stage, opts.startMessage, resolveLogData(opts.startData, opts.state));
|
|
26513
|
+
while (opts.canContinue(opts.state)) {
|
|
26514
|
+
opts.state.attempt++;
|
|
26515
|
+
opts.logger?.info(opts.stage, opts.attemptMessage(opts.state.attempt, opts.maxAttempts, opts.state), resolveLogData(opts.attemptData, opts.state));
|
|
26516
|
+
const prompt = await opts.buildPrompt(opts.state.attempt, opts.state);
|
|
26517
|
+
await opts.runAttempt(opts.state.attempt, prompt, opts.state);
|
|
26518
|
+
const passed = await opts.checkResult(opts.state.attempt, opts.state);
|
|
26519
|
+
if (passed) {
|
|
26520
|
+
return true;
|
|
26521
|
+
}
|
|
26522
|
+
await opts.onAttemptFailure?.(opts.state.attempt, opts.state);
|
|
26523
|
+
}
|
|
26524
|
+
await opts.onLoopEnd?.(opts.state);
|
|
26525
|
+
if (opts.state.attempt >= opts.maxAttempts) {
|
|
26526
|
+
return await opts.onExhausted?.(opts.state) ?? false;
|
|
26527
|
+
}
|
|
26528
|
+
return false;
|
|
26529
|
+
}
|
|
26530
|
+
|
|
26254
26531
|
// src/pipeline/event-bus.ts
|
|
26255
26532
|
class PipelineEventBus {
|
|
26256
26533
|
subscribers = new Map;
|
|
@@ -26702,6 +26979,16 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
26702
26979
|
const needsTruncation = rawDiff.length > DIFF_CAP_BYTES;
|
|
26703
26980
|
const stat = needsTruncation ? await collectDiffStat(workdir, effectiveRef) : undefined;
|
|
26704
26981
|
const diff = truncateDiff(rawDiff, stat);
|
|
26982
|
+
if (!diff) {
|
|
26983
|
+
return {
|
|
26984
|
+
check: "semantic",
|
|
26985
|
+
success: true,
|
|
26986
|
+
command: "",
|
|
26987
|
+
exitCode: 0,
|
|
26988
|
+
output: "skipped: no production code changes",
|
|
26989
|
+
durationMs: Date.now() - startTime
|
|
26990
|
+
};
|
|
26991
|
+
}
|
|
26705
26992
|
const agent = modelResolver(semanticConfig.modelTier);
|
|
26706
26993
|
if (!agent) {
|
|
26707
26994
|
logger?.warn("semantic", "No agent available for semantic review \u2014 skipping", {
|
|
@@ -27196,7 +27483,7 @@ __export(exports_review, {
|
|
|
27196
27483
|
reviewStage: () => reviewStage,
|
|
27197
27484
|
_reviewDeps: () => _reviewDeps
|
|
27198
27485
|
});
|
|
27199
|
-
import { join as
|
|
27486
|
+
import { join as join18 } from "path";
|
|
27200
27487
|
var reviewStage, _reviewDeps;
|
|
27201
27488
|
var init_review = __esm(() => {
|
|
27202
27489
|
init_agents();
|
|
@@ -27210,7 +27497,7 @@ var init_review = __esm(() => {
|
|
|
27210
27497
|
const logger = getLogger();
|
|
27211
27498
|
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
27212
27499
|
logger.info("review", "Running review phase", { storyId: ctx.story.id });
|
|
27213
|
-
const effectiveWorkdir = ctx.story.workdir ?
|
|
27500
|
+
const effectiveWorkdir = ctx.story.workdir ? join18(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
27214
27501
|
const agentResolver = ctx.agentGetFn ?? getAgent;
|
|
27215
27502
|
const agentName = effectiveConfig.autoMode?.defaultAgent;
|
|
27216
27503
|
const modelResolver = (_tier) => agentName ? agentResolver(agentName) ?? null : null;
|
|
@@ -27262,7 +27549,7 @@ var init_review = __esm(() => {
|
|
|
27262
27549
|
});
|
|
27263
27550
|
|
|
27264
27551
|
// src/pipeline/stages/autofix.ts
|
|
27265
|
-
import { join as
|
|
27552
|
+
import { join as join19 } from "path";
|
|
27266
27553
|
async function recheckReview(ctx) {
|
|
27267
27554
|
const { reviewStage: reviewStage2 } = await Promise.resolve().then(() => (init_review(), exports_review));
|
|
27268
27555
|
if (!reviewStage2.enabled(ctx))
|
|
@@ -27295,11 +27582,38 @@ Fix ALL errors listed above. Do NOT change test files or test behavior.
|
|
|
27295
27582
|
Do NOT add new features \u2014 only fix the quality check errors.
|
|
27296
27583
|
Commit your fixes when done.${scopeConstraint}`;
|
|
27297
27584
|
}
|
|
27585
|
+
function buildAutofixEscalationPreamble(attempt, maxAttempts, rethinkAtAttempt, urgencyAtAttempt) {
|
|
27586
|
+
return buildProgressivePromptPreamble({
|
|
27587
|
+
attempt,
|
|
27588
|
+
maxAttempts,
|
|
27589
|
+
rethinkAtAttempt,
|
|
27590
|
+
urgencyAtAttempt,
|
|
27591
|
+
stage: "autofix",
|
|
27592
|
+
logger: getLogger(),
|
|
27593
|
+
urgencySection: `## Final Autofix Attempt Before Escalation
|
|
27594
|
+
|
|
27595
|
+
This is attempt ${attempt}. If the review still fails after this, autofix will escalate instead of retrying.
|
|
27596
|
+
A different approach is required. Do not repeat the same fix.
|
|
27597
|
+
|
|
27598
|
+
`,
|
|
27599
|
+
rethinkSection: `## Previous Attempt Did Not Fix the Failures
|
|
27600
|
+
|
|
27601
|
+
Your previous fix attempt (attempt ${attempt}) did not resolve the quality errors. Rethink your approach.
|
|
27602
|
+
|
|
27603
|
+
- Do not repeat the same edit pattern.
|
|
27604
|
+
- Re-read the failing diagnostics carefully.
|
|
27605
|
+
- Try a fundamentally different fix strategy if the earlier one did not work.
|
|
27606
|
+
|
|
27607
|
+
`
|
|
27608
|
+
});
|
|
27609
|
+
}
|
|
27298
27610
|
async function runAgentRectification(ctx) {
|
|
27299
27611
|
const logger = getLogger();
|
|
27300
27612
|
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
27301
27613
|
const maxPerCycle = effectiveConfig.quality.autofix?.maxAttempts ?? 2;
|
|
27302
27614
|
const maxTotal = effectiveConfig.quality.autofix?.maxTotalAttempts ?? 10;
|
|
27615
|
+
const rethinkAtAttempt = effectiveConfig.quality.autofix?.rethinkAtAttempt ?? 2;
|
|
27616
|
+
const urgencyAtAttempt = effectiveConfig.quality.autofix?.urgencyAtAttempt ?? 3;
|
|
27303
27617
|
const consumed = ctx.autofixAttempt ?? 0;
|
|
27304
27618
|
const failedChecks = collectFailedChecks(ctx);
|
|
27305
27619
|
if (failedChecks.length === 0) {
|
|
@@ -27316,56 +27630,90 @@ async function runAgentRectification(ctx) {
|
|
|
27316
27630
|
}
|
|
27317
27631
|
const remainingBudget = maxTotal - consumed;
|
|
27318
27632
|
const maxAttempts = Math.min(maxPerCycle, remainingBudget);
|
|
27319
|
-
|
|
27633
|
+
const agentGetFn = ctx.agentGetFn ?? _autofixDeps.getAgent;
|
|
27634
|
+
const loopState = {
|
|
27635
|
+
attempt: 0,
|
|
27636
|
+
failedChecks
|
|
27637
|
+
};
|
|
27638
|
+
return runSharedRectificationLoop({
|
|
27639
|
+
stage: "autofix",
|
|
27320
27640
|
storyId: ctx.story.id,
|
|
27321
|
-
failedChecks: failedChecks.map((c) => c.check),
|
|
27322
27641
|
maxAttempts,
|
|
27323
|
-
|
|
27324
|
-
|
|
27325
|
-
|
|
27326
|
-
|
|
27327
|
-
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
27328
|
-
ctx.autofixAttempt = consumed + attempt;
|
|
27329
|
-
logger.info("autofix", `Agent rectification attempt ${ctx.autofixAttempt}/${maxTotal}`, { storyId: ctx.story.id });
|
|
27330
|
-
const agent = agentGetFn(ctx.config.autoMode.defaultAgent);
|
|
27331
|
-
if (!agent) {
|
|
27332
|
-
logger.error("autofix", "Agent not found \u2014 cannot run agent rectification", { storyId: ctx.story.id });
|
|
27333
|
-
return false;
|
|
27334
|
-
}
|
|
27335
|
-
const prompt = buildReviewRectificationPrompt(failedChecks, ctx.story);
|
|
27336
|
-
const modelTier = ctx.story.routing?.modelTier ?? ctx.config.autoMode.escalation.tierOrder[0]?.tier ?? "balanced";
|
|
27337
|
-
const modelDef = resolveModelForAgent(ctx.config.models, ctx.routing.agent ?? ctx.config.autoMode.defaultAgent, modelTier, ctx.config.autoMode.defaultAgent);
|
|
27338
|
-
const rectificationWorkdir = ctx.story.workdir ? join18(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
27339
|
-
await agent.run({
|
|
27340
|
-
prompt,
|
|
27341
|
-
workdir: rectificationWorkdir,
|
|
27342
|
-
modelTier,
|
|
27343
|
-
modelDef,
|
|
27344
|
-
timeoutSeconds: ctx.config.execution.sessionTimeoutSeconds,
|
|
27345
|
-
dangerouslySkipPermissions: resolvePermissions(ctx.config, "rectification").skipPermissions,
|
|
27346
|
-
pipelineStage: "rectification",
|
|
27347
|
-
config: ctx.config,
|
|
27348
|
-
maxInteractionTurns: ctx.config.agent?.maxInteractionTurns,
|
|
27642
|
+
state: loopState,
|
|
27643
|
+
logger,
|
|
27644
|
+
startMessage: "Starting agent rectification for review failures",
|
|
27645
|
+
startData: {
|
|
27349
27646
|
storyId: ctx.story.id,
|
|
27350
|
-
|
|
27351
|
-
|
|
27352
|
-
|
|
27353
|
-
|
|
27354
|
-
|
|
27647
|
+
failedChecks: failedChecks.map((check2) => check2.check),
|
|
27648
|
+
maxAttempts,
|
|
27649
|
+
totalUsed: consumed,
|
|
27650
|
+
maxTotalAttempts: maxTotal
|
|
27651
|
+
},
|
|
27652
|
+
attemptMessage: (attempt) => `Agent rectification attempt ${consumed + attempt}/${maxTotal}`,
|
|
27653
|
+
attemptData: { storyId: ctx.story.id },
|
|
27654
|
+
canContinue: (state) => state.failedChecks.length > 0 && state.attempt < maxAttempts,
|
|
27655
|
+
buildPrompt: (attempt, state) => {
|
|
27656
|
+
let prompt = buildReviewRectificationPrompt(state.failedChecks, ctx.story);
|
|
27657
|
+
const escalationPreamble = buildAutofixEscalationPreamble(attempt, maxAttempts, rethinkAtAttempt, urgencyAtAttempt);
|
|
27658
|
+
if (escalationPreamble) {
|
|
27659
|
+
prompt = `${escalationPreamble}${prompt}`;
|
|
27660
|
+
}
|
|
27661
|
+
return prompt;
|
|
27662
|
+
},
|
|
27663
|
+
runAttempt: async (attempt, prompt) => {
|
|
27664
|
+
ctx.autofixAttempt = consumed + attempt;
|
|
27665
|
+
const agent = agentGetFn(ctx.config.autoMode.defaultAgent);
|
|
27666
|
+
if (!agent) {
|
|
27667
|
+
logger.error("autofix", "Agent not found \u2014 cannot run agent rectification", { storyId: ctx.story.id });
|
|
27668
|
+
throw new Error("AUTOFIX_AGENT_NOT_FOUND");
|
|
27669
|
+
}
|
|
27670
|
+
const modelTier = ctx.story.routing?.modelTier ?? ctx.config.autoMode.escalation.tierOrder[0]?.tier ?? "balanced";
|
|
27671
|
+
const modelDef = resolveModelForAgent(ctx.config.models, ctx.routing.agent ?? ctx.config.autoMode.defaultAgent, modelTier, ctx.config.autoMode.defaultAgent);
|
|
27672
|
+
const rectificationWorkdir = ctx.story.workdir ? join19(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
27673
|
+
await agent.run({
|
|
27674
|
+
prompt,
|
|
27675
|
+
workdir: rectificationWorkdir,
|
|
27676
|
+
modelTier,
|
|
27677
|
+
modelDef,
|
|
27678
|
+
timeoutSeconds: ctx.config.execution.sessionTimeoutSeconds,
|
|
27679
|
+
dangerouslySkipPermissions: resolvePermissions(ctx.config, "rectification").skipPermissions,
|
|
27680
|
+
pipelineStage: "rectification",
|
|
27681
|
+
config: ctx.config,
|
|
27682
|
+
maxInteractionTurns: ctx.config.agent?.maxInteractionTurns,
|
|
27683
|
+
storyId: ctx.story.id,
|
|
27684
|
+
sessionRole: "implementer"
|
|
27685
|
+
});
|
|
27686
|
+
},
|
|
27687
|
+
checkResult: async (attempt, state) => {
|
|
27688
|
+
const passed = await _autofixDeps.recheckReview(ctx);
|
|
27689
|
+
if (passed) {
|
|
27690
|
+
logger.info("autofix", `[OK] Agent rectification succeeded on attempt ${attempt}`, {
|
|
27691
|
+
storyId: ctx.story.id
|
|
27692
|
+
});
|
|
27693
|
+
return true;
|
|
27694
|
+
}
|
|
27695
|
+
const updatedFailed = collectFailedChecks(ctx);
|
|
27696
|
+
if (updatedFailed.length > 0) {
|
|
27697
|
+
state.failedChecks.splice(0, state.failedChecks.length, ...updatedFailed);
|
|
27698
|
+
}
|
|
27699
|
+
return false;
|
|
27700
|
+
},
|
|
27701
|
+
onAttemptFailure: (attempt) => {
|
|
27702
|
+
logger.warn("autofix", `Agent rectification still failing after attempt ${attempt}`, {
|
|
27355
27703
|
storyId: ctx.story.id
|
|
27356
27704
|
});
|
|
27357
|
-
|
|
27705
|
+
},
|
|
27706
|
+
onLoopEnd: (state) => {
|
|
27707
|
+
if (state.attempt >= maxAttempts) {
|
|
27708
|
+
logger.warn("autofix", "Agent rectification exhausted", { storyId: ctx.story.id });
|
|
27709
|
+
}
|
|
27358
27710
|
}
|
|
27359
|
-
|
|
27360
|
-
if (
|
|
27361
|
-
|
|
27711
|
+
}).catch((error48) => {
|
|
27712
|
+
if (error48 instanceof Error && error48.message === "AUTOFIX_AGENT_NOT_FOUND") {
|
|
27713
|
+
return false;
|
|
27362
27714
|
}
|
|
27363
|
-
|
|
27364
|
-
|
|
27365
|
-
});
|
|
27366
|
-
}
|
|
27367
|
-
logger.warn("autofix", "Agent rectification exhausted", { storyId: ctx.story.id });
|
|
27368
|
-
return false;
|
|
27715
|
+
throw error48;
|
|
27716
|
+
});
|
|
27369
27717
|
}
|
|
27370
27718
|
var autofixStage, _autofixDeps;
|
|
27371
27719
|
var init_autofix = __esm(() => {
|
|
@@ -27399,7 +27747,7 @@ var init_autofix = __esm(() => {
|
|
|
27399
27747
|
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
27400
27748
|
const lintFixCmd = effectiveConfig.quality.commands.lintFix;
|
|
27401
27749
|
const formatFixCmd = effectiveConfig.quality.commands.formatFix;
|
|
27402
|
-
const effectiveWorkdir = ctx.story.workdir ?
|
|
27750
|
+
const effectiveWorkdir = ctx.story.workdir ? join19(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
27403
27751
|
const failedCheckNames = new Set((reviewResult.checks ?? []).filter((c) => !c.success).map((c) => c.check));
|
|
27404
27752
|
const hasLintFailure = failedCheckNames.has("lint");
|
|
27405
27753
|
logger.info("autofix", "Starting autofix", {
|
|
@@ -27483,10 +27831,10 @@ var init_autofix = __esm(() => {
|
|
|
27483
27831
|
|
|
27484
27832
|
// src/execution/progress.ts
|
|
27485
27833
|
import { appendFile as appendFile2, mkdir } from "fs/promises";
|
|
27486
|
-
import { join as
|
|
27834
|
+
import { join as join20 } from "path";
|
|
27487
27835
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
27488
27836
|
await mkdir(featureDir, { recursive: true });
|
|
27489
|
-
const progressPath =
|
|
27837
|
+
const progressPath = join20(featureDir, "progress.txt");
|
|
27490
27838
|
const timestamp = new Date().toISOString();
|
|
27491
27839
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
27492
27840
|
`;
|
|
@@ -27569,7 +27917,7 @@ function estimateTokens(text) {
|
|
|
27569
27917
|
|
|
27570
27918
|
// src/constitution/loader.ts
|
|
27571
27919
|
import { existsSync as existsSync19 } from "fs";
|
|
27572
|
-
import { join as
|
|
27920
|
+
import { join as join21 } from "path";
|
|
27573
27921
|
function truncateToTokens(text, maxTokens) {
|
|
27574
27922
|
const maxChars = maxTokens * 3;
|
|
27575
27923
|
if (text.length <= maxChars) {
|
|
@@ -27591,7 +27939,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
27591
27939
|
}
|
|
27592
27940
|
let combinedContent = "";
|
|
27593
27941
|
if (!config2.skipGlobal) {
|
|
27594
|
-
const globalPath =
|
|
27942
|
+
const globalPath = join21(globalConfigDir(), config2.path);
|
|
27595
27943
|
if (existsSync19(globalPath)) {
|
|
27596
27944
|
const validatedPath = validateFilePath(globalPath, globalConfigDir());
|
|
27597
27945
|
const globalFile = Bun.file(validatedPath);
|
|
@@ -27601,7 +27949,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
27601
27949
|
}
|
|
27602
27950
|
}
|
|
27603
27951
|
}
|
|
27604
|
-
const projectPath =
|
|
27952
|
+
const projectPath = join21(projectDir, config2.path);
|
|
27605
27953
|
if (existsSync19(projectPath)) {
|
|
27606
27954
|
const validatedPath = validateFilePath(projectPath, projectDir);
|
|
27607
27955
|
const projectFile = Bun.file(validatedPath);
|
|
@@ -28629,7 +28977,7 @@ var init_helpers = __esm(() => {
|
|
|
28629
28977
|
});
|
|
28630
28978
|
|
|
28631
28979
|
// src/pipeline/stages/context.ts
|
|
28632
|
-
import { join as
|
|
28980
|
+
import { join as join22 } from "path";
|
|
28633
28981
|
var contextStage;
|
|
28634
28982
|
var init_context2 = __esm(() => {
|
|
28635
28983
|
init_helpers();
|
|
@@ -28639,7 +28987,7 @@ var init_context2 = __esm(() => {
|
|
|
28639
28987
|
enabled: () => true,
|
|
28640
28988
|
async execute(ctx) {
|
|
28641
28989
|
const logger = getLogger();
|
|
28642
|
-
const packageWorkdir = ctx.story.workdir ?
|
|
28990
|
+
const packageWorkdir = ctx.story.workdir ? join22(ctx.workdir, ctx.story.workdir) : undefined;
|
|
28643
28991
|
const result = await buildStoryContextFull(ctx.prd, ctx.story, ctx.config, packageWorkdir);
|
|
28644
28992
|
if (result) {
|
|
28645
28993
|
ctx.contextMarkdown = result.markdown;
|
|
@@ -28773,14 +29121,14 @@ var init_isolation = __esm(() => {
|
|
|
28773
29121
|
|
|
28774
29122
|
// src/context/greenfield.ts
|
|
28775
29123
|
import { readdir } from "fs/promises";
|
|
28776
|
-
import { join as
|
|
29124
|
+
import { join as join23 } from "path";
|
|
28777
29125
|
async function scanForTestFiles(dir, testPattern, isRootCall = true) {
|
|
28778
29126
|
const results = [];
|
|
28779
29127
|
const ignoreDirs = new Set(["node_modules", "dist", "build", ".next", ".git"]);
|
|
28780
29128
|
try {
|
|
28781
29129
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
28782
29130
|
for (const entry of entries) {
|
|
28783
|
-
const fullPath =
|
|
29131
|
+
const fullPath = join23(dir, entry.name);
|
|
28784
29132
|
if (entry.isDirectory()) {
|
|
28785
29133
|
if (ignoreDirs.has(entry.name))
|
|
28786
29134
|
continue;
|
|
@@ -29135,13 +29483,13 @@ function parseTestOutput(output, exitCode) {
|
|
|
29135
29483
|
|
|
29136
29484
|
// src/verification/runners.ts
|
|
29137
29485
|
import { existsSync as existsSync20 } from "fs";
|
|
29138
|
-
import { join as
|
|
29486
|
+
import { join as join24 } from "path";
|
|
29139
29487
|
async function verifyAssets(workingDirectory, expectedFiles) {
|
|
29140
29488
|
if (!expectedFiles || expectedFiles.length === 0)
|
|
29141
29489
|
return { success: true, missingFiles: [] };
|
|
29142
29490
|
const missingFiles = [];
|
|
29143
29491
|
for (const file3 of expectedFiles) {
|
|
29144
|
-
if (!existsSync20(
|
|
29492
|
+
if (!existsSync20(join24(workingDirectory, file3)))
|
|
29145
29493
|
missingFiles.push(file3);
|
|
29146
29494
|
}
|
|
29147
29495
|
if (missingFiles.length > 0) {
|
|
@@ -29245,27 +29593,14 @@ function shouldRetryRectification(state, config2) {
|
|
|
29245
29593
|
return true;
|
|
29246
29594
|
}
|
|
29247
29595
|
function buildEscalationPreamble(attempt, config2) {
|
|
29248
|
-
|
|
29249
|
-
|
|
29250
|
-
|
|
29251
|
-
|
|
29252
|
-
|
|
29253
|
-
|
|
29254
|
-
|
|
29255
|
-
|
|
29256
|
-
attempt,
|
|
29257
|
-
urgencyAtAttempt: urgencyAt,
|
|
29258
|
-
rethinkAtAttempt: rethinkAt,
|
|
29259
|
-
maxRetries: config2.maxRetries
|
|
29260
|
-
});
|
|
29261
|
-
} else {
|
|
29262
|
-
logger?.info("rectification", "Progressive prompt escalation: rethink injected", {
|
|
29263
|
-
attempt,
|
|
29264
|
-
rethinkAtAttempt: rethinkAt,
|
|
29265
|
-
maxRetries: config2.maxRetries
|
|
29266
|
-
});
|
|
29267
|
-
}
|
|
29268
|
-
const rethinkSection = `## \u26A0\uFE0F Previous Attempt Did Not Fix the Failures
|
|
29596
|
+
return buildProgressivePromptPreamble({
|
|
29597
|
+
attempt,
|
|
29598
|
+
maxAttempts: config2.maxRetries,
|
|
29599
|
+
rethinkAtAttempt: config2.rethinkAtAttempt,
|
|
29600
|
+
urgencyAtAttempt: config2.urgencyAtAttempt,
|
|
29601
|
+
stage: "rectification",
|
|
29602
|
+
logger: getSafeLogger(),
|
|
29603
|
+
rethinkSection: `## \u26A0\uFE0F Previous Attempt Did Not Fix the Failures
|
|
29269
29604
|
|
|
29270
29605
|
Your previous fix attempt (attempt ${attempt}) did not resolve all failures. **Step back and reconsider your approach.**
|
|
29271
29606
|
|
|
@@ -29274,14 +29609,14 @@ Your previous fix attempt (attempt ${attempt}) did not resolve all failures. **S
|
|
|
29274
29609
|
- Re-read the story context and test failures carefully before making changes.
|
|
29275
29610
|
- Consider: are there missing edge cases, incorrect assumptions, or a design flaw in the implementation?
|
|
29276
29611
|
|
|
29277
|
-
|
|
29278
|
-
|
|
29612
|
+
`,
|
|
29613
|
+
urgencySection: `## \uD83D\uDEA8 Final Rectification Attempt Before Model Escalation
|
|
29279
29614
|
|
|
29280
29615
|
This is attempt ${attempt} \u2014 if the tests still fail after this, the task will escalate to a stronger model tier.
|
|
29281
29616
|
A **completely different approach** is required. Do not repeat what you have already tried.
|
|
29282
29617
|
|
|
29283
|
-
`
|
|
29284
|
-
|
|
29618
|
+
`
|
|
29619
|
+
});
|
|
29285
29620
|
}
|
|
29286
29621
|
function createRectificationPrompt(failures, story, config2, attempt) {
|
|
29287
29622
|
const maxChars = config2?.maxFailureSummaryChars ?? 2000;
|
|
@@ -29541,77 +29876,112 @@ async function runRectificationLoop(story, config2, workdir, agent, implementerT
|
|
|
29541
29876
|
storyId: story.id,
|
|
29542
29877
|
sessionName: rectificationSessionName
|
|
29543
29878
|
});
|
|
29544
|
-
|
|
29545
|
-
rectificationState
|
|
29546
|
-
|
|
29547
|
-
|
|
29548
|
-
|
|
29549
|
-
|
|
29550
|
-
|
|
29551
|
-
|
|
29552
|
-
|
|
29553
|
-
|
|
29554
|
-
|
|
29555
|
-
|
|
29556
|
-
dangerouslySkipPermissions: resolvePermissions(config2, "rectification").skipPermissions,
|
|
29557
|
-
pipelineStage: "rectification",
|
|
29558
|
-
config: config2,
|
|
29559
|
-
maxInteractionTurns: config2.agent?.maxInteractionTurns,
|
|
29560
|
-
featureName,
|
|
29879
|
+
const loopState = {
|
|
29880
|
+
...rectificationState,
|
|
29881
|
+
isolationPassed: true
|
|
29882
|
+
};
|
|
29883
|
+
const fixed = await runSharedRectificationLoop({
|
|
29884
|
+
stage: "tdd",
|
|
29885
|
+
storyId: story.id,
|
|
29886
|
+
maxAttempts: rectificationConfig.maxRetries,
|
|
29887
|
+
state: loopState,
|
|
29888
|
+
logger,
|
|
29889
|
+
startMessage: "Full suite gate detected regressions",
|
|
29890
|
+
startData: {
|
|
29561
29891
|
storyId: story.id,
|
|
29562
|
-
|
|
29563
|
-
|
|
29564
|
-
|
|
29565
|
-
}
|
|
29566
|
-
|
|
29567
|
-
|
|
29568
|
-
|
|
29569
|
-
|
|
29570
|
-
|
|
29571
|
-
|
|
29572
|
-
|
|
29573
|
-
|
|
29574
|
-
|
|
29575
|
-
|
|
29576
|
-
|
|
29892
|
+
failedTests: testSummary.failed,
|
|
29893
|
+
passedTests: testSummary.passed
|
|
29894
|
+
},
|
|
29895
|
+
attemptMessage: (attempt) => `-> Implementer rectification attempt ${attempt}/${rectificationConfig.maxRetries}`,
|
|
29896
|
+
attemptData: (state) => ({
|
|
29897
|
+
storyId: story.id,
|
|
29898
|
+
currentFailures: state.currentFailures
|
|
29899
|
+
}),
|
|
29900
|
+
canContinue: (state) => state.isolationPassed && _rectificationGateDeps.shouldRetryRectification(state, rectificationConfig),
|
|
29901
|
+
buildPrompt: () => buildImplementerRectificationPrompt(testSummary.failures, story, contextMarkdown, rectificationConfig),
|
|
29902
|
+
runAttempt: async (attempt, rectificationPrompt) => {
|
|
29903
|
+
const isLastAttempt = attempt >= rectificationConfig.maxRetries;
|
|
29904
|
+
const rectifyBeforeRef = await captureGitRef(workdir) ?? "HEAD";
|
|
29905
|
+
const rectifyResult = await agent.run({
|
|
29906
|
+
prompt: rectificationPrompt,
|
|
29907
|
+
workdir,
|
|
29908
|
+
modelTier: implementerTier,
|
|
29909
|
+
modelDef: resolveModelForAgent(config2.models, story.routing?.agent ?? config2.autoMode.defaultAgent, implementerTier, config2.autoMode.defaultAgent),
|
|
29910
|
+
timeoutSeconds: config2.execution.sessionTimeoutSeconds,
|
|
29911
|
+
dangerouslySkipPermissions: resolvePermissions(config2, "rectification").skipPermissions,
|
|
29912
|
+
pipelineStage: "rectification",
|
|
29913
|
+
config: config2,
|
|
29914
|
+
maxInteractionTurns: config2.agent?.maxInteractionTurns,
|
|
29915
|
+
featureName,
|
|
29577
29916
|
storyId: story.id,
|
|
29578
|
-
|
|
29579
|
-
|
|
29917
|
+
sessionRole: "implementer",
|
|
29918
|
+
acpSessionName: rectificationSessionName,
|
|
29919
|
+
keepSessionOpen: !isLastAttempt
|
|
29580
29920
|
});
|
|
29581
|
-
|
|
29582
|
-
|
|
29583
|
-
|
|
29584
|
-
|
|
29585
|
-
|
|
29586
|
-
|
|
29587
|
-
|
|
29588
|
-
|
|
29921
|
+
if (!rectifyResult.success && rectifyResult.pid) {
|
|
29922
|
+
await cleanupProcessTree(rectifyResult.pid);
|
|
29923
|
+
}
|
|
29924
|
+
if (rectifyResult.success) {
|
|
29925
|
+
logger.info("tdd", "Rectification agent session complete", {
|
|
29926
|
+
storyId: story.id,
|
|
29927
|
+
attempt,
|
|
29928
|
+
cost: rectifyResult.estimatedCost
|
|
29929
|
+
});
|
|
29930
|
+
} else {
|
|
29931
|
+
logger.warn("tdd", "Rectification agent session failed", {
|
|
29932
|
+
storyId: story.id,
|
|
29933
|
+
attempt,
|
|
29934
|
+
exitCode: rectifyResult.exitCode
|
|
29935
|
+
});
|
|
29936
|
+
}
|
|
29937
|
+
await autoCommitIfDirty(workdir, "tdd", "rectification", story.id);
|
|
29938
|
+
const rectifyIsolation = lite ? undefined : await verifyImplementerIsolation(workdir, rectifyBeforeRef);
|
|
29939
|
+
if (rectifyIsolation && !rectifyIsolation.passed) {
|
|
29940
|
+
loopState.isolationPassed = false;
|
|
29941
|
+
logger.error("tdd", "Rectification violated isolation", {
|
|
29942
|
+
storyId: story.id,
|
|
29943
|
+
attempt,
|
|
29944
|
+
violations: rectifyIsolation.violations
|
|
29945
|
+
});
|
|
29946
|
+
}
|
|
29947
|
+
},
|
|
29948
|
+
checkResult: async (attempt, state) => {
|
|
29949
|
+
if (!state.isolationPassed) {
|
|
29950
|
+
return false;
|
|
29951
|
+
}
|
|
29952
|
+
const retryFullSuite = await _rectificationGateDeps.executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
29953
|
+
cwd: workdir
|
|
29589
29954
|
});
|
|
29590
|
-
|
|
29591
|
-
|
|
29592
|
-
|
|
29593
|
-
|
|
29594
|
-
|
|
29595
|
-
|
|
29596
|
-
|
|
29597
|
-
|
|
29955
|
+
const retrySuitePassed = retryFullSuite.success && retryFullSuite.exitCode === 0;
|
|
29956
|
+
if (retrySuitePassed) {
|
|
29957
|
+
logger.info("tdd", "Full suite gate passed after rectification!", {
|
|
29958
|
+
storyId: story.id,
|
|
29959
|
+
attempt
|
|
29960
|
+
});
|
|
29961
|
+
return true;
|
|
29962
|
+
}
|
|
29963
|
+
if (retryFullSuite.output) {
|
|
29964
|
+
const newTestSummary = _rectificationGateDeps.parseBunTestOutput(retryFullSuite.output);
|
|
29965
|
+
state.currentFailures = newTestSummary.failed;
|
|
29966
|
+
testSummary.failures = newTestSummary.failures;
|
|
29967
|
+
testSummary.failed = newTestSummary.failed;
|
|
29968
|
+
testSummary.passed = newTestSummary.passed;
|
|
29969
|
+
}
|
|
29970
|
+
return false;
|
|
29971
|
+
},
|
|
29972
|
+
onAttemptFailure: (attempt, state) => {
|
|
29973
|
+
if (!state.isolationPassed) {
|
|
29974
|
+
return;
|
|
29975
|
+
}
|
|
29976
|
+
logger.warn("tdd", "Full suite still failing after rectification attempt", {
|
|
29598
29977
|
storyId: story.id,
|
|
29599
|
-
attempt
|
|
29978
|
+
attempt,
|
|
29979
|
+
remainingFailures: state.currentFailures
|
|
29600
29980
|
});
|
|
29601
|
-
return true;
|
|
29602
29981
|
}
|
|
29603
|
-
|
|
29604
|
-
|
|
29605
|
-
|
|
29606
|
-
testSummary.failures = newTestSummary.failures;
|
|
29607
|
-
testSummary.failed = newTestSummary.failed;
|
|
29608
|
-
testSummary.passed = newTestSummary.passed;
|
|
29609
|
-
}
|
|
29610
|
-
logger.warn("tdd", "Full suite still failing after rectification attempt", {
|
|
29611
|
-
storyId: story.id,
|
|
29612
|
-
attempt: rectificationState.attempt,
|
|
29613
|
-
remainingFailures: rectificationState.currentFailures
|
|
29614
|
-
});
|
|
29982
|
+
});
|
|
29983
|
+
if (fixed) {
|
|
29984
|
+
return true;
|
|
29615
29985
|
}
|
|
29616
29986
|
const finalFullSuite = await _rectificationGateDeps.executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
29617
29987
|
cwd: workdir
|
|
@@ -30035,13 +30405,13 @@ var exports_loader = {};
|
|
|
30035
30405
|
__export(exports_loader, {
|
|
30036
30406
|
loadOverride: () => loadOverride
|
|
30037
30407
|
});
|
|
30038
|
-
import { join as
|
|
30408
|
+
import { join as join25 } from "path";
|
|
30039
30409
|
async function loadOverride(role, workdir, config2) {
|
|
30040
30410
|
const overridePath = config2.prompts?.overrides?.[role];
|
|
30041
30411
|
if (!overridePath) {
|
|
30042
30412
|
return null;
|
|
30043
30413
|
}
|
|
30044
|
-
const absolutePath =
|
|
30414
|
+
const absolutePath = join25(workdir, overridePath);
|
|
30045
30415
|
const file3 = Bun.file(absolutePath);
|
|
30046
30416
|
if (!await file3.exists()) {
|
|
30047
30417
|
return null;
|
|
@@ -30900,11 +31270,11 @@ var init_tdd = __esm(() => {
|
|
|
30900
31270
|
|
|
30901
31271
|
// src/pipeline/stages/execution.ts
|
|
30902
31272
|
import { existsSync as existsSync21 } from "fs";
|
|
30903
|
-
import { join as
|
|
31273
|
+
import { join as join26 } from "path";
|
|
30904
31274
|
function resolveStoryWorkdir(repoRoot, storyWorkdir) {
|
|
30905
31275
|
if (!storyWorkdir)
|
|
30906
31276
|
return repoRoot;
|
|
30907
|
-
const resolved =
|
|
31277
|
+
const resolved = join26(repoRoot, storyWorkdir);
|
|
30908
31278
|
if (!existsSync21(resolved)) {
|
|
30909
31279
|
throw new Error(`[execution] story.workdir "${storyWorkdir}" does not exist at "${resolved}"`);
|
|
30910
31280
|
}
|
|
@@ -31609,167 +31979,202 @@ async function runRectificationLoop2(opts) {
|
|
|
31609
31979
|
currentFailures: testSummary.failed,
|
|
31610
31980
|
lastExitCode: 1
|
|
31611
31981
|
};
|
|
31612
|
-
|
|
31982
|
+
return runSharedRectificationLoop({
|
|
31983
|
+
stage: "rectification",
|
|
31613
31984
|
storyId: story.id,
|
|
31614
|
-
|
|
31615
|
-
|
|
31616
|
-
|
|
31617
|
-
|
|
31618
|
-
|
|
31619
|
-
|
|
31985
|
+
maxAttempts: rectificationConfig.maxRetries,
|
|
31986
|
+
state: rectificationState,
|
|
31987
|
+
logger,
|
|
31988
|
+
startMessage: `Starting ${label} loop`,
|
|
31989
|
+
startData: {
|
|
31990
|
+
storyId: story.id,
|
|
31991
|
+
initialFailures: rectificationState.initialFailures,
|
|
31992
|
+
maxRetries: rectificationConfig.maxRetries
|
|
31993
|
+
},
|
|
31994
|
+
attemptMessage: (attempt) => `${label} attempt ${attempt}/${rectificationConfig.maxRetries}`,
|
|
31995
|
+
attemptData: () => ({
|
|
31620
31996
|
storyId: story.id,
|
|
31621
31997
|
currentFailures: rectificationState.currentFailures
|
|
31622
|
-
})
|
|
31623
|
-
|
|
31624
|
-
|
|
31625
|
-
|
|
31626
|
-
const
|
|
31627
|
-
|
|
31998
|
+
}),
|
|
31999
|
+
canContinue: (state) => shouldRetryRectification(state, rectificationConfig),
|
|
32000
|
+
buildPrompt: async (attempt) => {
|
|
32001
|
+
let diagnosisPrefix = null;
|
|
32002
|
+
const debateStageConfig = config2.debate?.stages?.rectification;
|
|
32003
|
+
if (debateStageConfig?.enabled) {
|
|
32004
|
+
const failureSummary = formatFailureSummary(testSummary.failures);
|
|
32005
|
+
const diagnosisPrompt = `Analyze the following test failures and identify the root cause:
|
|
31628
32006
|
|
|
31629
32007
|
${failureSummary}`;
|
|
31630
|
-
|
|
31631
|
-
|
|
31632
|
-
|
|
31633
|
-
|
|
31634
|
-
|
|
31635
|
-
|
|
31636
|
-
|
|
32008
|
+
try {
|
|
32009
|
+
const debateResult = await _rectificationDeps.runDebate(story.id, debateStageConfig, diagnosisPrompt, config2);
|
|
32010
|
+
if (debateResult.totalCostUsd > 0 && story.routing) {
|
|
32011
|
+
story.routing.estimatedCost = (story.routing.estimatedCost ?? 0) + debateResult.totalCostUsd;
|
|
32012
|
+
}
|
|
32013
|
+
if (debateResult.output !== null) {
|
|
32014
|
+
diagnosisPrefix = `## Root Cause Analysis
|
|
31637
32015
|
|
|
31638
32016
|
${debateResult.output}`;
|
|
31639
|
-
|
|
31640
|
-
|
|
32017
|
+
} else {
|
|
32018
|
+
logger?.info("rectification", "debate diagnosis fallback \u2014 all debaters failed", {
|
|
32019
|
+
storyId: story.id,
|
|
32020
|
+
attempt,
|
|
32021
|
+
event: "fallback"
|
|
32022
|
+
});
|
|
32023
|
+
}
|
|
32024
|
+
} catch (_error) {
|
|
32025
|
+
logger?.info("rectification", "debate diagnosis fallback \u2014 debate threw error", {
|
|
31641
32026
|
storyId: story.id,
|
|
31642
|
-
attempt
|
|
32027
|
+
attempt,
|
|
31643
32028
|
event: "fallback"
|
|
31644
32029
|
});
|
|
31645
32030
|
}
|
|
31646
|
-
} catch (err) {
|
|
31647
|
-
logger?.info("rectification", "debate diagnosis fallback \u2014 debate threw error", {
|
|
31648
|
-
storyId: story.id,
|
|
31649
|
-
attempt: rectificationState.attempt,
|
|
31650
|
-
event: "fallback"
|
|
31651
|
-
});
|
|
31652
32031
|
}
|
|
31653
|
-
|
|
31654
|
-
|
|
31655
|
-
|
|
31656
|
-
rectificationPrompt = `${diagnosisPrefix}
|
|
32032
|
+
let rectificationPrompt = createRectificationPrompt(testSummary.failures, story, rectificationConfig, attempt);
|
|
32033
|
+
if (diagnosisPrefix) {
|
|
32034
|
+
rectificationPrompt = `${diagnosisPrefix}
|
|
31657
32035
|
|
|
31658
32036
|
${rectificationPrompt}`;
|
|
31659
|
-
|
|
31660
|
-
|
|
32037
|
+
}
|
|
32038
|
+
if (promptPrefix) {
|
|
32039
|
+
rectificationPrompt = `${promptPrefix}
|
|
31661
32040
|
|
|
31662
32041
|
${rectificationPrompt}`;
|
|
31663
|
-
|
|
31664
|
-
|
|
31665
|
-
|
|
31666
|
-
|
|
31667
|
-
|
|
31668
|
-
|
|
31669
|
-
|
|
31670
|
-
|
|
31671
|
-
|
|
31672
|
-
|
|
31673
|
-
|
|
31674
|
-
modelTier,
|
|
31675
|
-
|
|
31676
|
-
|
|
31677
|
-
|
|
31678
|
-
|
|
31679
|
-
|
|
31680
|
-
|
|
31681
|
-
|
|
31682
|
-
|
|
31683
|
-
|
|
31684
|
-
|
|
31685
|
-
|
|
31686
|
-
logger?.info("rectification", `Agent ${label} session complete`, {
|
|
32042
|
+
}
|
|
32043
|
+
return rectificationPrompt;
|
|
32044
|
+
},
|
|
32045
|
+
runAttempt: async (attempt, rectificationPrompt) => {
|
|
32046
|
+
const agent = agentGetFn ? agentGetFn(config2.autoMode.defaultAgent) : _rectificationDeps.getAgent(config2.autoMode.defaultAgent, config2);
|
|
32047
|
+
if (!agent) {
|
|
32048
|
+
logger?.error("rectification", "Agent not found, cannot retry");
|
|
32049
|
+
throw new Error("RECTIFICATION_AGENT_NOT_FOUND");
|
|
32050
|
+
}
|
|
32051
|
+
const complexity = story.routing?.complexity ?? "medium";
|
|
32052
|
+
const modelTier = config2.autoMode.complexityRouting?.[complexity] || config2.autoMode.escalation.tierOrder[0]?.tier || "balanced";
|
|
32053
|
+
const modelDef = resolveModelForAgent(config2.models, story.routing?.agent ?? config2.autoMode.defaultAgent, modelTier, config2.autoMode.defaultAgent);
|
|
32054
|
+
const agentResult = await agent.run({
|
|
32055
|
+
prompt: rectificationPrompt,
|
|
32056
|
+
workdir,
|
|
32057
|
+
modelTier,
|
|
32058
|
+
modelDef,
|
|
32059
|
+
timeoutSeconds: config2.execution.sessionTimeoutSeconds,
|
|
32060
|
+
dangerouslySkipPermissions: resolvePermissions(config2, "rectification").skipPermissions,
|
|
32061
|
+
pipelineStage: "rectification",
|
|
32062
|
+
config: config2,
|
|
32063
|
+
maxInteractionTurns: config2.agent?.maxInteractionTurns,
|
|
32064
|
+
featureName,
|
|
31687
32065
|
storyId: story.id,
|
|
31688
|
-
|
|
31689
|
-
cost: agentResult.estimatedCost
|
|
32066
|
+
sessionRole: "implementer"
|
|
31690
32067
|
});
|
|
31691
|
-
|
|
31692
|
-
|
|
31693
|
-
|
|
31694
|
-
|
|
31695
|
-
|
|
32068
|
+
if (agentResult.success) {
|
|
32069
|
+
logger?.info("rectification", `Agent ${label} session complete`, {
|
|
32070
|
+
storyId: story.id,
|
|
32071
|
+
attempt,
|
|
32072
|
+
cost: agentResult.estimatedCost
|
|
32073
|
+
});
|
|
32074
|
+
} else {
|
|
32075
|
+
logger?.warn("rectification", `Agent ${label} session failed`, {
|
|
32076
|
+
storyId: story.id,
|
|
32077
|
+
attempt,
|
|
32078
|
+
exitCode: agentResult.exitCode
|
|
32079
|
+
});
|
|
32080
|
+
}
|
|
32081
|
+
},
|
|
32082
|
+
checkResult: async (attempt, state) => {
|
|
32083
|
+
const retryVerification = await _rectificationDeps.runVerification({
|
|
32084
|
+
workdir,
|
|
32085
|
+
expectedFiles: getExpectedFiles(story),
|
|
32086
|
+
command: testCommand,
|
|
32087
|
+
timeoutSeconds,
|
|
32088
|
+
forceExit: config2.quality.forceExit,
|
|
32089
|
+
detectOpenHandles: config2.quality.detectOpenHandles,
|
|
32090
|
+
detectOpenHandlesRetries: config2.quality.detectOpenHandlesRetries,
|
|
32091
|
+
timeoutRetryCount: 0,
|
|
32092
|
+
gracePeriodMs: config2.quality.gracePeriodMs,
|
|
32093
|
+
drainTimeoutMs: config2.quality.drainTimeoutMs,
|
|
32094
|
+
shell: config2.quality.shell,
|
|
32095
|
+
stripEnvVars: config2.quality.stripEnvVars
|
|
31696
32096
|
});
|
|
31697
|
-
|
|
31698
|
-
|
|
31699
|
-
|
|
31700
|
-
|
|
31701
|
-
|
|
31702
|
-
|
|
31703
|
-
|
|
31704
|
-
|
|
31705
|
-
|
|
31706
|
-
|
|
31707
|
-
|
|
31708
|
-
|
|
31709
|
-
|
|
31710
|
-
|
|
31711
|
-
|
|
31712
|
-
|
|
31713
|
-
|
|
32097
|
+
if (retryVerification.success) {
|
|
32098
|
+
logger?.info("rectification", `[OK] ${label} succeeded!`, {
|
|
32099
|
+
storyId: story.id,
|
|
32100
|
+
attempt,
|
|
32101
|
+
initialFailures: state.initialFailures
|
|
32102
|
+
});
|
|
32103
|
+
return true;
|
|
32104
|
+
}
|
|
32105
|
+
if (retryVerification.output) {
|
|
32106
|
+
const newTestSummary = parseBunTestOutput(retryVerification.output);
|
|
32107
|
+
state.currentFailures = newTestSummary.failed;
|
|
32108
|
+
state.lastExitCode = retryVerification.status === "SUCCESS" ? 0 : 1;
|
|
32109
|
+
testSummary.failures = newTestSummary.failures;
|
|
32110
|
+
testSummary.failed = newTestSummary.failed;
|
|
32111
|
+
testSummary.passed = newTestSummary.passed;
|
|
32112
|
+
if (newTestSummary.failed === 0) {
|
|
32113
|
+
state.lastExitCode = 0;
|
|
32114
|
+
logger?.info("rectification", `[OK] ${label} succeeded after parsing retry output`, {
|
|
32115
|
+
storyId: story.id,
|
|
32116
|
+
attempt,
|
|
32117
|
+
initialFailures: state.initialFailures
|
|
32118
|
+
});
|
|
32119
|
+
return true;
|
|
32120
|
+
}
|
|
32121
|
+
}
|
|
32122
|
+
return false;
|
|
32123
|
+
},
|
|
32124
|
+
onAttemptFailure: (attempt, state) => {
|
|
32125
|
+
const failingTests = testSummary.failures.slice(0, 10).map((failure) => failure.testName);
|
|
32126
|
+
const logData = {
|
|
31714
32127
|
storyId: story.id,
|
|
31715
|
-
attempt
|
|
31716
|
-
|
|
31717
|
-
|
|
31718
|
-
|
|
31719
|
-
|
|
31720
|
-
|
|
31721
|
-
|
|
31722
|
-
|
|
31723
|
-
|
|
31724
|
-
|
|
31725
|
-
|
|
31726
|
-
|
|
31727
|
-
|
|
31728
|
-
|
|
31729
|
-
|
|
31730
|
-
|
|
31731
|
-
|
|
31732
|
-
|
|
31733
|
-
|
|
31734
|
-
|
|
31735
|
-
|
|
31736
|
-
|
|
31737
|
-
|
|
31738
|
-
|
|
31739
|
-
|
|
31740
|
-
|
|
31741
|
-
|
|
31742
|
-
|
|
31743
|
-
|
|
31744
|
-
|
|
31745
|
-
|
|
31746
|
-
|
|
31747
|
-
|
|
31748
|
-
|
|
31749
|
-
|
|
31750
|
-
|
|
31751
|
-
|
|
31752
|
-
|
|
31753
|
-
const shouldEscalate = rectificationConfig.escalateOnExhaustion !== false && config2.autoMode?.escalation?.enabled === true && rectificationState.attempt >= rectificationConfig.maxRetries && rectificationState.currentFailures > 0;
|
|
31754
|
-
if (shouldEscalate) {
|
|
31755
|
-
const complexity = story.routing?.complexity ?? "medium";
|
|
31756
|
-
const currentTier = config2.autoMode.complexityRouting?.[complexity] || config2.autoMode.escalation.tierOrder[0]?.tier || "balanced";
|
|
31757
|
-
const tierOrder = config2.autoMode.escalation.tierOrder;
|
|
31758
|
-
const escalationResult = _rectificationDeps.escalateTier(currentTier, tierOrder);
|
|
31759
|
-
const escalatedTier = escalationResult?.tier ?? null;
|
|
31760
|
-
const escalatedAgent = escalationResult?.agent;
|
|
31761
|
-
if (escalatedTier !== null) {
|
|
32128
|
+
attempt,
|
|
32129
|
+
remainingFailures: state.currentFailures,
|
|
32130
|
+
failingTests
|
|
32131
|
+
};
|
|
32132
|
+
if (testSummary.failures.length > 10 || testSummary.failures.length === 0 && testSummary.failed > 0) {
|
|
32133
|
+
logData.totalFailingTests = testSummary.failed;
|
|
32134
|
+
}
|
|
32135
|
+
logger?.warn("rectification", `${label} still failing after attempt`, logData);
|
|
32136
|
+
},
|
|
32137
|
+
onLoopEnd: (state) => {
|
|
32138
|
+
if (state.attempt >= rectificationConfig.maxRetries) {
|
|
32139
|
+
logger?.warn("rectification", `${label} exhausted max retries`, {
|
|
32140
|
+
storyId: story.id,
|
|
32141
|
+
attempts: state.attempt,
|
|
32142
|
+
remainingFailures: state.currentFailures
|
|
32143
|
+
});
|
|
32144
|
+
} else if (state.currentFailures > state.initialFailures) {
|
|
32145
|
+
logger?.warn("rectification", `${label} aborted due to further regression`, {
|
|
32146
|
+
storyId: story.id,
|
|
32147
|
+
initialFailures: state.initialFailures,
|
|
32148
|
+
currentFailures: state.currentFailures
|
|
32149
|
+
});
|
|
32150
|
+
}
|
|
32151
|
+
},
|
|
32152
|
+
onExhausted: async (state) => {
|
|
32153
|
+
const shouldEscalate = rectificationConfig.escalateOnExhaustion !== false && config2.autoMode?.escalation?.enabled === true && state.currentFailures > 0;
|
|
32154
|
+
if (!shouldEscalate) {
|
|
32155
|
+
return false;
|
|
32156
|
+
}
|
|
32157
|
+
const complexity = story.routing?.complexity ?? "medium";
|
|
32158
|
+
const currentTier = config2.autoMode.complexityRouting?.[complexity] || config2.autoMode.escalation.tierOrder[0]?.tier || "balanced";
|
|
32159
|
+
const tierOrder = config2.autoMode.escalation.tierOrder;
|
|
32160
|
+
const escalationResult = _rectificationDeps.escalateTier(currentTier, tierOrder);
|
|
32161
|
+
const escalatedTier = escalationResult?.tier ?? null;
|
|
32162
|
+
const escalatedAgent = escalationResult?.agent;
|
|
32163
|
+
if (escalatedTier === null) {
|
|
32164
|
+
return false;
|
|
32165
|
+
}
|
|
31762
32166
|
const agentName = escalatedAgent ?? story.routing?.agent ?? config2.autoMode.defaultAgent;
|
|
31763
32167
|
const agent = agentGetFn ? agentGetFn(agentName) : _rectificationDeps.getAgent(agentName, config2);
|
|
31764
32168
|
if (!agent) {
|
|
31765
32169
|
return false;
|
|
31766
32170
|
}
|
|
31767
32171
|
const escalatedModelDef = resolveModelForAgent(config2.models, agentName, escalatedTier, config2.autoMode.defaultAgent);
|
|
31768
|
-
let escalationPrompt = createEscalatedRectificationPrompt(testSummary.failures, story,
|
|
31769
|
-
if (promptPrefix)
|
|
32172
|
+
let escalationPrompt = createEscalatedRectificationPrompt(testSummary.failures, story, state.attempt, currentTier, escalatedTier, rectificationConfig);
|
|
32173
|
+
if (promptPrefix) {
|
|
31770
32174
|
escalationPrompt = `${promptPrefix}
|
|
31771
32175
|
|
|
31772
32176
|
${escalationPrompt}`;
|
|
32177
|
+
}
|
|
31773
32178
|
const escalationRunResult = await agent.run({
|
|
31774
32179
|
prompt: escalationPrompt,
|
|
31775
32180
|
workdir,
|
|
@@ -31812,9 +32217,14 @@ ${escalationPrompt}`;
|
|
|
31812
32217
|
return true;
|
|
31813
32218
|
}
|
|
31814
32219
|
logger?.warn("rectification", "escalated rectification also failed", { storyId: story.id, escalatedTier });
|
|
32220
|
+
return false;
|
|
31815
32221
|
}
|
|
31816
|
-
}
|
|
31817
|
-
|
|
32222
|
+
}).catch((error48) => {
|
|
32223
|
+
if (error48 instanceof Error && error48.message === "RECTIFICATION_AGENT_NOT_FOUND") {
|
|
32224
|
+
return false;
|
|
32225
|
+
}
|
|
32226
|
+
throw error48;
|
|
32227
|
+
});
|
|
31818
32228
|
}
|
|
31819
32229
|
var _rectificationDeps;
|
|
31820
32230
|
var init_rectification_loop = __esm(() => {
|
|
@@ -32475,7 +32885,7 @@ var init_regression2 = __esm(() => {
|
|
|
32475
32885
|
});
|
|
32476
32886
|
|
|
32477
32887
|
// src/pipeline/stages/routing.ts
|
|
32478
|
-
import { join as
|
|
32888
|
+
import { join as join27 } from "path";
|
|
32479
32889
|
var routingStage, _routingDeps;
|
|
32480
32890
|
var init_routing2 = __esm(() => {
|
|
32481
32891
|
init_registry();
|
|
@@ -32512,7 +32922,7 @@ var init_routing2 = __esm(() => {
|
|
|
32512
32922
|
}
|
|
32513
32923
|
const greenfieldDetectionEnabled = effectiveConfig.tdd.greenfieldDetection ?? true;
|
|
32514
32924
|
if (greenfieldDetectionEnabled && routing.testStrategy.startsWith("three-session-tdd")) {
|
|
32515
|
-
const greenfieldScanDir = ctx.story.workdir ?
|
|
32925
|
+
const greenfieldScanDir = ctx.story.workdir ? join27(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
32516
32926
|
const isGreenfield = await _routingDeps.isGreenfieldStory(ctx.story, greenfieldScanDir);
|
|
32517
32927
|
if (isGreenfield) {
|
|
32518
32928
|
logger.info("routing", "Greenfield detected \u2014 forcing test-after strategy", {
|
|
@@ -32564,7 +32974,7 @@ var init_crash_detector = __esm(() => {
|
|
|
32564
32974
|
});
|
|
32565
32975
|
|
|
32566
32976
|
// src/pipeline/stages/verify.ts
|
|
32567
|
-
import { join as
|
|
32977
|
+
import { join as join28 } from "path";
|
|
32568
32978
|
function coerceSmartTestRunner(val) {
|
|
32569
32979
|
if (val === undefined || val === true)
|
|
32570
32980
|
return DEFAULT_SMART_RUNNER_CONFIG2;
|
|
@@ -32580,7 +32990,7 @@ function buildScopedCommand2(testFiles, baseCommand, testScopedTemplate) {
|
|
|
32580
32990
|
}
|
|
32581
32991
|
async function readPackageName(dir) {
|
|
32582
32992
|
try {
|
|
32583
|
-
const content = await Bun.file(
|
|
32993
|
+
const content = await Bun.file(join28(dir, "package.json")).json();
|
|
32584
32994
|
return typeof content.name === "string" ? content.name : null;
|
|
32585
32995
|
} catch {
|
|
32586
32996
|
return null;
|
|
@@ -32625,7 +33035,7 @@ var init_verify = __esm(() => {
|
|
|
32625
33035
|
return { action: "continue" };
|
|
32626
33036
|
}
|
|
32627
33037
|
logger.info("verify", "Running verification", { storyId: ctx.story.id });
|
|
32628
|
-
const effectiveWorkdir = ctx.story.workdir ?
|
|
33038
|
+
const effectiveWorkdir = ctx.story.workdir ? join28(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
32629
33039
|
let effectiveCommand = testCommand;
|
|
32630
33040
|
let isFullSuite = true;
|
|
32631
33041
|
const smartRunnerConfig = coerceSmartTestRunner(effectiveConfig.execution.smartTestRunner);
|
|
@@ -32843,7 +33253,7 @@ __export(exports_init_context, {
|
|
|
32843
33253
|
});
|
|
32844
33254
|
import { existsSync as existsSync24 } from "fs";
|
|
32845
33255
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
32846
|
-
import { basename as basename3, join as
|
|
33256
|
+
import { basename as basename3, join as join32 } from "path";
|
|
32847
33257
|
async function findFiles(dir, maxFiles = 200) {
|
|
32848
33258
|
try {
|
|
32849
33259
|
const proc = Bun.spawnSync([
|
|
@@ -32871,7 +33281,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
32871
33281
|
return [];
|
|
32872
33282
|
}
|
|
32873
33283
|
async function readPackageManifest(projectRoot) {
|
|
32874
|
-
const packageJsonPath =
|
|
33284
|
+
const packageJsonPath = join32(projectRoot, "package.json");
|
|
32875
33285
|
if (!existsSync24(packageJsonPath)) {
|
|
32876
33286
|
return null;
|
|
32877
33287
|
}
|
|
@@ -32889,7 +33299,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
32889
33299
|
}
|
|
32890
33300
|
}
|
|
32891
33301
|
async function readReadmeSnippet(projectRoot) {
|
|
32892
|
-
const readmePath =
|
|
33302
|
+
const readmePath = join32(projectRoot, "README.md");
|
|
32893
33303
|
if (!existsSync24(readmePath)) {
|
|
32894
33304
|
return null;
|
|
32895
33305
|
}
|
|
@@ -32907,7 +33317,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
32907
33317
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
32908
33318
|
const found = [];
|
|
32909
33319
|
for (const candidate of candidates) {
|
|
32910
|
-
const path12 =
|
|
33320
|
+
const path12 = join32(projectRoot, candidate);
|
|
32911
33321
|
if (existsSync24(path12)) {
|
|
32912
33322
|
found.push(candidate);
|
|
32913
33323
|
}
|
|
@@ -32918,7 +33328,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
32918
33328
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
32919
33329
|
const found = [];
|
|
32920
33330
|
for (const candidate of candidates) {
|
|
32921
|
-
const path12 =
|
|
33331
|
+
const path12 = join32(projectRoot, candidate);
|
|
32922
33332
|
if (existsSync24(path12)) {
|
|
32923
33333
|
found.push(candidate);
|
|
32924
33334
|
}
|
|
@@ -33079,8 +33489,8 @@ function generatePackageContextTemplate(packagePath) {
|
|
|
33079
33489
|
}
|
|
33080
33490
|
async function initPackage(repoRoot, packagePath, force = false) {
|
|
33081
33491
|
const logger = getLogger();
|
|
33082
|
-
const naxDir =
|
|
33083
|
-
const contextPath =
|
|
33492
|
+
const naxDir = join32(repoRoot, ".nax", "mono", packagePath);
|
|
33493
|
+
const contextPath = join32(naxDir, "context.md");
|
|
33084
33494
|
if (existsSync24(contextPath) && !force) {
|
|
33085
33495
|
logger.info("init", "Package context.md already exists (use --force to overwrite)", { path: contextPath });
|
|
33086
33496
|
return;
|
|
@@ -33094,8 +33504,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
33094
33504
|
}
|
|
33095
33505
|
async function initContext(projectRoot, options = {}) {
|
|
33096
33506
|
const logger = getLogger();
|
|
33097
|
-
const naxDir =
|
|
33098
|
-
const contextPath =
|
|
33507
|
+
const naxDir = join32(projectRoot, ".nax");
|
|
33508
|
+
const contextPath = join32(naxDir, "context.md");
|
|
33099
33509
|
if (existsSync24(contextPath) && !options.force) {
|
|
33100
33510
|
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", { path: contextPath });
|
|
33101
33511
|
return;
|
|
@@ -33125,7 +33535,7 @@ var init_init_context = __esm(() => {
|
|
|
33125
33535
|
|
|
33126
33536
|
// src/utils/path-security.ts
|
|
33127
33537
|
import { realpathSync as realpathSync3 } from "fs";
|
|
33128
|
-
import { dirname as dirname4, isAbsolute as isAbsolute4, join as
|
|
33538
|
+
import { dirname as dirname4, isAbsolute as isAbsolute4, join as join33, normalize as normalize2, resolve as resolve5 } from "path";
|
|
33129
33539
|
function safeRealpathForComparison(p) {
|
|
33130
33540
|
try {
|
|
33131
33541
|
return realpathSync3(p);
|
|
@@ -33134,7 +33544,7 @@ function safeRealpathForComparison(p) {
|
|
|
33134
33544
|
if (parent === p)
|
|
33135
33545
|
return normalize2(p);
|
|
33136
33546
|
const resolvedParent = safeRealpathForComparison(parent);
|
|
33137
|
-
return
|
|
33547
|
+
return join33(resolvedParent, p.split("/").pop() ?? "");
|
|
33138
33548
|
}
|
|
33139
33549
|
}
|
|
33140
33550
|
function validateModulePath(modulePath, allowedRoots) {
|
|
@@ -33152,7 +33562,7 @@ function validateModulePath(modulePath, allowedRoots) {
|
|
|
33152
33562
|
} else {
|
|
33153
33563
|
for (let i = 0;i < allowedRoots.length; i++) {
|
|
33154
33564
|
const originalRoot = resolve5(allowedRoots[i]);
|
|
33155
|
-
const absoluteInput = resolve5(
|
|
33565
|
+
const absoluteInput = resolve5(join33(originalRoot, modulePath));
|
|
33156
33566
|
const resolved = safeRealpathForComparison(absoluteInput);
|
|
33157
33567
|
const resolvedRoot = resolvedRoots[i];
|
|
33158
33568
|
if (resolved.startsWith(`${resolvedRoot}/`) || resolved === resolvedRoot) {
|
|
@@ -33688,19 +34098,19 @@ var init_loader4 = __esm(() => {
|
|
|
33688
34098
|
});
|
|
33689
34099
|
|
|
33690
34100
|
// src/hooks/runner.ts
|
|
33691
|
-
import { join as
|
|
34101
|
+
import { join as join48 } from "path";
|
|
33692
34102
|
async function loadHooksConfig(projectDir, globalDir) {
|
|
33693
34103
|
let globalHooks = { hooks: {} };
|
|
33694
34104
|
let projectHooks = { hooks: {} };
|
|
33695
34105
|
let skipGlobal = false;
|
|
33696
|
-
const projectPath =
|
|
34106
|
+
const projectPath = join48(projectDir, "hooks.json");
|
|
33697
34107
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
33698
34108
|
if (projectData) {
|
|
33699
34109
|
projectHooks = projectData;
|
|
33700
34110
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
33701
34111
|
}
|
|
33702
34112
|
if (!skipGlobal && globalDir) {
|
|
33703
|
-
const globalPath =
|
|
34113
|
+
const globalPath = join48(globalDir, "hooks.json");
|
|
33704
34114
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
33705
34115
|
if (globalData) {
|
|
33706
34116
|
globalHooks = globalData;
|
|
@@ -34239,7 +34649,7 @@ async function diagnoseAcceptanceFailure(agent, options) {
|
|
|
34239
34649
|
reasoning: "diagnosis failed \u2014 falling back to source fix",
|
|
34240
34650
|
confidence: 0
|
|
34241
34651
|
};
|
|
34242
|
-
} catch
|
|
34652
|
+
} catch {
|
|
34243
34653
|
return {
|
|
34244
34654
|
verdict: "source_bug",
|
|
34245
34655
|
reasoning: "diagnosis failed \u2014 falling back to source fix",
|
|
@@ -34349,11 +34759,12 @@ var init_fix_executor = __esm(() => {
|
|
|
34349
34759
|
var exports_acceptance_loop = {};
|
|
34350
34760
|
__export(exports_acceptance_loop, {
|
|
34351
34761
|
runAcceptanceLoop: () => runAcceptanceLoop,
|
|
34762
|
+
regenerateAcceptanceTest: () => regenerateAcceptanceTest,
|
|
34352
34763
|
isTestLevelFailure: () => isTestLevelFailure,
|
|
34353
34764
|
isStubTestFile: () => isStubTestFile,
|
|
34354
34765
|
_acceptanceLoopDeps: () => _acceptanceLoopDeps
|
|
34355
34766
|
});
|
|
34356
|
-
import path14, { join as
|
|
34767
|
+
import path14, { join as join49 } from "path";
|
|
34357
34768
|
function isStubTestFile(content) {
|
|
34358
34769
|
return /expect\s*\(\s*true\s*\)\s*\.\s*toBe\s*\(\s*(?:false|true)\s*\)/.test(content);
|
|
34359
34770
|
}
|
|
@@ -34424,7 +34835,7 @@ async function executeFixStory(ctx, story, prd, iterations) {
|
|
|
34424
34835
|
agent: ctx.config.autoMode.defaultAgent,
|
|
34425
34836
|
iteration: iterations
|
|
34426
34837
|
}), ctx.workdir);
|
|
34427
|
-
const fixEffectiveConfig = story.workdir ? await loadConfigForWorkdir(
|
|
34838
|
+
const fixEffectiveConfig = story.workdir ? await loadConfigForWorkdir(join49(ctx.workdir, ".nax", "config.json"), story.workdir) : ctx.config;
|
|
34428
34839
|
const fixContext = {
|
|
34429
34840
|
config: ctx.config,
|
|
34430
34841
|
effectiveConfig: fixEffectiveConfig,
|
|
@@ -35111,6 +35522,8 @@ async function handleRunCompletion(options) {
|
|
|
35111
35522
|
totalStories: finalCounts.total,
|
|
35112
35523
|
passedStories: finalCounts.passed,
|
|
35113
35524
|
failedStories: finalCounts.failed,
|
|
35525
|
+
skippedStories: finalCounts.skipped,
|
|
35526
|
+
pausedStories: finalCounts.paused,
|
|
35114
35527
|
durationMs,
|
|
35115
35528
|
totalCost
|
|
35116
35529
|
});
|
|
@@ -35237,12 +35650,12 @@ var init_headless_formatter = __esm(() => {
|
|
|
35237
35650
|
// src/pipeline/subscribers/events-writer.ts
|
|
35238
35651
|
import { appendFile as appendFile3, mkdir as mkdir3 } from "fs/promises";
|
|
35239
35652
|
import { homedir as homedir5 } from "os";
|
|
35240
|
-
import { basename as basename6, join as
|
|
35653
|
+
import { basename as basename6, join as join50 } from "path";
|
|
35241
35654
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
35242
35655
|
const logger = getSafeLogger();
|
|
35243
35656
|
const project = basename6(workdir);
|
|
35244
|
-
const eventsDir =
|
|
35245
|
-
const eventsFile =
|
|
35657
|
+
const eventsDir = join50(homedir5(), ".nax", "events", project);
|
|
35658
|
+
const eventsFile = join50(eventsDir, "events.jsonl");
|
|
35246
35659
|
let dirReady = false;
|
|
35247
35660
|
const write = (line) => {
|
|
35248
35661
|
return (async () => {
|
|
@@ -35423,12 +35836,12 @@ var init_interaction2 = __esm(() => {
|
|
|
35423
35836
|
// src/pipeline/subscribers/registry.ts
|
|
35424
35837
|
import { mkdir as mkdir4, writeFile } from "fs/promises";
|
|
35425
35838
|
import { homedir as homedir6 } from "os";
|
|
35426
|
-
import { basename as basename7, join as
|
|
35839
|
+
import { basename as basename7, join as join51 } from "path";
|
|
35427
35840
|
function wireRegistry(bus, feature, runId, workdir) {
|
|
35428
35841
|
const logger = getSafeLogger();
|
|
35429
35842
|
const project = basename7(workdir);
|
|
35430
|
-
const runDir =
|
|
35431
|
-
const metaFile =
|
|
35843
|
+
const runDir = join51(homedir6(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
35844
|
+
const metaFile = join51(runDir, "meta.json");
|
|
35432
35845
|
const unsub = bus.on("run:started", (_ev) => {
|
|
35433
35846
|
return (async () => {
|
|
35434
35847
|
try {
|
|
@@ -35438,8 +35851,8 @@ function wireRegistry(bus, feature, runId, workdir) {
|
|
|
35438
35851
|
project,
|
|
35439
35852
|
feature,
|
|
35440
35853
|
workdir,
|
|
35441
|
-
statusPath:
|
|
35442
|
-
eventsDir:
|
|
35854
|
+
statusPath: join51(workdir, ".nax", "features", feature, "status.json"),
|
|
35855
|
+
eventsDir: join51(workdir, ".nax", "features", feature, "runs"),
|
|
35443
35856
|
registeredAt: new Date().toISOString()
|
|
35444
35857
|
};
|
|
35445
35858
|
await writeFile(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -35562,8 +35975,8 @@ function wireReporters(bus, pluginRegistry, runId, startTime) {
|
|
|
35562
35975
|
storySummary: {
|
|
35563
35976
|
completed: ev.passedStories,
|
|
35564
35977
|
failed: ev.failedStories,
|
|
35565
|
-
skipped:
|
|
35566
|
-
paused:
|
|
35978
|
+
skipped: ev.skippedStories,
|
|
35979
|
+
paused: ev.pausedStories
|
|
35567
35980
|
}
|
|
35568
35981
|
});
|
|
35569
35982
|
} catch (err) {
|
|
@@ -36091,7 +36504,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
36091
36504
|
});
|
|
36092
36505
|
|
|
36093
36506
|
// src/execution/iteration-runner.ts
|
|
36094
|
-
import { join as
|
|
36507
|
+
import { join as join52 } from "path";
|
|
36095
36508
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
36096
36509
|
const logger = getSafeLogger();
|
|
36097
36510
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
@@ -36126,7 +36539,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
36126
36539
|
}
|
|
36127
36540
|
}
|
|
36128
36541
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
36129
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
36542
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join52(ctx.workdir, ".nax", "config.json"), story.workdir) : ctx.config;
|
|
36130
36543
|
const pipelineContext = {
|
|
36131
36544
|
config: ctx.config,
|
|
36132
36545
|
effectiveConfig,
|
|
@@ -36385,13 +36798,13 @@ __export(exports_manager, {
|
|
|
36385
36798
|
});
|
|
36386
36799
|
import { existsSync as existsSync32, symlinkSync } from "fs";
|
|
36387
36800
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
36388
|
-
import { join as
|
|
36801
|
+
import { join as join53 } from "path";
|
|
36389
36802
|
|
|
36390
36803
|
class WorktreeManager {
|
|
36391
36804
|
async ensureGitExcludes(projectRoot) {
|
|
36392
36805
|
const logger = getSafeLogger();
|
|
36393
|
-
const infoDir =
|
|
36394
|
-
const excludePath =
|
|
36806
|
+
const infoDir = join53(projectRoot, ".git", "info");
|
|
36807
|
+
const excludePath = join53(infoDir, "exclude");
|
|
36395
36808
|
try {
|
|
36396
36809
|
await mkdir5(infoDir, { recursive: true });
|
|
36397
36810
|
let existing = "";
|
|
@@ -36418,7 +36831,7 @@ ${missing.join(`
|
|
|
36418
36831
|
}
|
|
36419
36832
|
async create(projectRoot, storyId) {
|
|
36420
36833
|
validateStoryId(storyId);
|
|
36421
|
-
const worktreePath =
|
|
36834
|
+
const worktreePath = join53(projectRoot, ".nax-wt", storyId);
|
|
36422
36835
|
const branchName = `nax/${storyId}`;
|
|
36423
36836
|
try {
|
|
36424
36837
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -36459,9 +36872,9 @@ ${missing.join(`
|
|
|
36459
36872
|
}
|
|
36460
36873
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
36461
36874
|
}
|
|
36462
|
-
const nodeModulesSource =
|
|
36875
|
+
const nodeModulesSource = join53(projectRoot, "node_modules");
|
|
36463
36876
|
if (existsSync32(nodeModulesSource)) {
|
|
36464
|
-
const nodeModulesTarget =
|
|
36877
|
+
const nodeModulesTarget = join53(worktreePath, "node_modules");
|
|
36465
36878
|
try {
|
|
36466
36879
|
symlinkSync(nodeModulesSource, nodeModulesTarget, "dir");
|
|
36467
36880
|
} catch (error48) {
|
|
@@ -36469,9 +36882,9 @@ ${missing.join(`
|
|
|
36469
36882
|
throw new Error(`Failed to symlink node_modules: ${errorMessage(error48)}`);
|
|
36470
36883
|
}
|
|
36471
36884
|
}
|
|
36472
|
-
const envSource =
|
|
36885
|
+
const envSource = join53(projectRoot, ".env");
|
|
36473
36886
|
if (existsSync32(envSource)) {
|
|
36474
|
-
const envTarget =
|
|
36887
|
+
const envTarget = join53(worktreePath, ".env");
|
|
36475
36888
|
try {
|
|
36476
36889
|
symlinkSync(envSource, envTarget, "file");
|
|
36477
36890
|
} catch (error48) {
|
|
@@ -36482,7 +36895,7 @@ ${missing.join(`
|
|
|
36482
36895
|
}
|
|
36483
36896
|
async remove(projectRoot, storyId) {
|
|
36484
36897
|
validateStoryId(storyId);
|
|
36485
|
-
const worktreePath =
|
|
36898
|
+
const worktreePath = join53(projectRoot, ".nax-wt", storyId);
|
|
36486
36899
|
const branchName = `nax/${storyId}`;
|
|
36487
36900
|
try {
|
|
36488
36901
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -37057,6 +37470,12 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
37057
37470
|
wireInteraction(pipelineEventBus, ctx.interactionChain, ctx.config);
|
|
37058
37471
|
wireEventsWriter(pipelineEventBus, ctx.feature, ctx.runId, ctx.workdir);
|
|
37059
37472
|
wireRegistry(pipelineEventBus, ctx.feature, ctx.runId, ctx.workdir);
|
|
37473
|
+
pipelineEventBus.emit({
|
|
37474
|
+
type: "run:started",
|
|
37475
|
+
feature: ctx.feature,
|
|
37476
|
+
totalStories: initialPrd.userStories.length,
|
|
37477
|
+
workdir: ctx.workdir
|
|
37478
|
+
});
|
|
37060
37479
|
const buildResult2 = (exitReason) => ({
|
|
37061
37480
|
prd,
|
|
37062
37481
|
iterations,
|
|
@@ -37401,16 +37820,16 @@ var init_unified_executor = __esm(() => {
|
|
|
37401
37820
|
});
|
|
37402
37821
|
|
|
37403
37822
|
// src/project/detector.ts
|
|
37404
|
-
import { join as
|
|
37823
|
+
import { join as join54 } from "path";
|
|
37405
37824
|
async function detectLanguage(workdir, pkg) {
|
|
37406
37825
|
const deps = _detectorDeps;
|
|
37407
|
-
if (await deps.fileExists(
|
|
37826
|
+
if (await deps.fileExists(join54(workdir, "go.mod")))
|
|
37408
37827
|
return "go";
|
|
37409
|
-
if (await deps.fileExists(
|
|
37828
|
+
if (await deps.fileExists(join54(workdir, "Cargo.toml")))
|
|
37410
37829
|
return "rust";
|
|
37411
|
-
if (await deps.fileExists(
|
|
37830
|
+
if (await deps.fileExists(join54(workdir, "pyproject.toml")))
|
|
37412
37831
|
return "python";
|
|
37413
|
-
if (await deps.fileExists(
|
|
37832
|
+
if (await deps.fileExists(join54(workdir, "requirements.txt")))
|
|
37414
37833
|
return "python";
|
|
37415
37834
|
if (pkg != null) {
|
|
37416
37835
|
const allDeps = {
|
|
@@ -37470,18 +37889,18 @@ async function detectLintTool(workdir, language) {
|
|
|
37470
37889
|
if (language === "python")
|
|
37471
37890
|
return "ruff";
|
|
37472
37891
|
const deps = _detectorDeps;
|
|
37473
|
-
if (await deps.fileExists(
|
|
37892
|
+
if (await deps.fileExists(join54(workdir, "biome.json")))
|
|
37474
37893
|
return "biome";
|
|
37475
|
-
if (await deps.fileExists(
|
|
37894
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc")))
|
|
37476
37895
|
return "eslint";
|
|
37477
|
-
if (await deps.fileExists(
|
|
37896
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc.js")))
|
|
37478
37897
|
return "eslint";
|
|
37479
|
-
if (await deps.fileExists(
|
|
37898
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc.json")))
|
|
37480
37899
|
return "eslint";
|
|
37481
37900
|
return;
|
|
37482
37901
|
}
|
|
37483
37902
|
async function detectProjectProfile(workdir, existing) {
|
|
37484
|
-
const pkg = await _detectorDeps.readJson(
|
|
37903
|
+
const pkg = await _detectorDeps.readJson(join54(workdir, "package.json"));
|
|
37485
37904
|
const language = existing.language !== undefined ? existing.language : await detectLanguage(workdir, pkg);
|
|
37486
37905
|
const type = existing.type !== undefined ? existing.type : detectType(pkg);
|
|
37487
37906
|
const testFramework = existing.testFramework !== undefined ? existing.testFramework : await detectTestFramework(workdir, language, pkg);
|
|
@@ -37574,7 +37993,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
37574
37993
|
var init_status_file = () => {};
|
|
37575
37994
|
|
|
37576
37995
|
// src/execution/status-writer.ts
|
|
37577
|
-
import { join as
|
|
37996
|
+
import { join as join55 } from "path";
|
|
37578
37997
|
|
|
37579
37998
|
class StatusWriter {
|
|
37580
37999
|
statusFile;
|
|
@@ -37648,7 +38067,7 @@ class StatusWriter {
|
|
|
37648
38067
|
if (!this._prd)
|
|
37649
38068
|
return;
|
|
37650
38069
|
const safeLogger = getSafeLogger();
|
|
37651
|
-
const featureStatusPath =
|
|
38070
|
+
const featureStatusPath = join55(featureDir, "status.json");
|
|
37652
38071
|
const write = async () => {
|
|
37653
38072
|
try {
|
|
37654
38073
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -37764,7 +38183,7 @@ var exports_precheck_runner = {};
|
|
|
37764
38183
|
__export(exports_precheck_runner, {
|
|
37765
38184
|
runPrecheckValidation: () => runPrecheckValidation
|
|
37766
38185
|
});
|
|
37767
|
-
import { mkdirSync as
|
|
38186
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
37768
38187
|
import path17 from "path";
|
|
37769
38188
|
async function runPrecheckValidation(ctx) {
|
|
37770
38189
|
const logger = getSafeLogger();
|
|
@@ -37779,7 +38198,7 @@ async function runPrecheckValidation(ctx) {
|
|
|
37779
38198
|
format: "human"
|
|
37780
38199
|
});
|
|
37781
38200
|
if (ctx.logFilePath) {
|
|
37782
|
-
|
|
38201
|
+
mkdirSync5(path17.dirname(ctx.logFilePath), { recursive: true });
|
|
37783
38202
|
const precheckLog = {
|
|
37784
38203
|
type: "precheck",
|
|
37785
38204
|
timestamp: new Date().toISOString(),
|
|
@@ -37859,7 +38278,7 @@ __export(exports_run_initialization, {
|
|
|
37859
38278
|
initializeRun: () => initializeRun,
|
|
37860
38279
|
_reconcileDeps: () => _reconcileDeps
|
|
37861
38280
|
});
|
|
37862
|
-
import { join as
|
|
38281
|
+
import { join as join56 } from "path";
|
|
37863
38282
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
37864
38283
|
const logger = getSafeLogger();
|
|
37865
38284
|
let reconciledCount = 0;
|
|
@@ -37877,7 +38296,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
37877
38296
|
});
|
|
37878
38297
|
continue;
|
|
37879
38298
|
}
|
|
37880
|
-
const effectiveWorkdir = story.workdir ?
|
|
38299
|
+
const effectiveWorkdir = story.workdir ? join56(workdir, story.workdir) : workdir;
|
|
37881
38300
|
try {
|
|
37882
38301
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
37883
38302
|
if (!reviewResult.success) {
|
|
@@ -37987,7 +38406,6 @@ async function setupRun(options) {
|
|
|
37987
38406
|
prdPath,
|
|
37988
38407
|
workdir,
|
|
37989
38408
|
config: config2,
|
|
37990
|
-
hooks,
|
|
37991
38409
|
feature,
|
|
37992
38410
|
dryRun,
|
|
37993
38411
|
statusFile,
|
|
@@ -38093,7 +38511,6 @@ async function setupRun(options) {
|
|
|
38093
38511
|
naxVersion: NAX_VERSION,
|
|
38094
38512
|
naxCommit: NAX_COMMIT
|
|
38095
38513
|
});
|
|
38096
|
-
await fireHook(hooks, "on-start", hookCtx(feature), workdir);
|
|
38097
38514
|
const { initializeRun: initializeRun2 } = await Promise.resolve().then(() => (init_run_initialization(), exports_run_initialization));
|
|
38098
38515
|
const initResult = await initializeRun2({
|
|
38099
38516
|
config: config2,
|
|
@@ -38121,7 +38538,6 @@ async function setupRun(options) {
|
|
|
38121
38538
|
var _runSetupDeps;
|
|
38122
38539
|
var init_run_setup = __esm(() => {
|
|
38123
38540
|
init_errors();
|
|
38124
|
-
init_hooks();
|
|
38125
38541
|
init_interaction();
|
|
38126
38542
|
init_logger2();
|
|
38127
38543
|
init_event_bus();
|
|
@@ -38171,24 +38587,26 @@ async function cleanupRun(options) {
|
|
|
38171
38587
|
const logger = getSafeLogger();
|
|
38172
38588
|
const { runId, startTime, totalCost, storiesCompleted, prd, pluginRegistry, workdir, interactionChain } = options;
|
|
38173
38589
|
const durationMs = Date.now() - startTime;
|
|
38174
|
-
|
|
38175
|
-
|
|
38176
|
-
|
|
38177
|
-
|
|
38178
|
-
|
|
38179
|
-
|
|
38180
|
-
|
|
38181
|
-
|
|
38182
|
-
|
|
38183
|
-
|
|
38184
|
-
|
|
38185
|
-
|
|
38186
|
-
|
|
38187
|
-
|
|
38188
|
-
|
|
38189
|
-
|
|
38190
|
-
|
|
38191
|
-
|
|
38590
|
+
if (!options.runCompleted) {
|
|
38591
|
+
const finalCounts = countStories(prd);
|
|
38592
|
+
const reporters = pluginRegistry.getReporters();
|
|
38593
|
+
for (const reporter of reporters) {
|
|
38594
|
+
if (reporter.onRunEnd) {
|
|
38595
|
+
try {
|
|
38596
|
+
await reporter.onRunEnd({
|
|
38597
|
+
runId,
|
|
38598
|
+
totalDurationMs: durationMs,
|
|
38599
|
+
totalCost,
|
|
38600
|
+
storySummary: {
|
|
38601
|
+
completed: storiesCompleted,
|
|
38602
|
+
failed: finalCounts.failed,
|
|
38603
|
+
skipped: finalCounts.skipped,
|
|
38604
|
+
paused: finalCounts.paused
|
|
38605
|
+
}
|
|
38606
|
+
});
|
|
38607
|
+
} catch (error48) {
|
|
38608
|
+
logger?.warn("plugins", `Reporter '${reporter.name}' onRunEnd failed`, { error: error48 });
|
|
38609
|
+
}
|
|
38192
38610
|
}
|
|
38193
38611
|
}
|
|
38194
38612
|
}
|
|
@@ -69085,9 +69503,9 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
69085
69503
|
|
|
69086
69504
|
// bin/nax.ts
|
|
69087
69505
|
init_source();
|
|
69088
|
-
import { existsSync as existsSync34, mkdirSync as
|
|
69506
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync6 } from "fs";
|
|
69089
69507
|
import { homedir as homedir8 } from "os";
|
|
69090
|
-
import { join as
|
|
69508
|
+
import { join as join58 } from "path";
|
|
69091
69509
|
|
|
69092
69510
|
// node_modules/commander/esm.mjs
|
|
69093
69511
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -69109,14 +69527,14 @@ var {
|
|
|
69109
69527
|
init_acceptance();
|
|
69110
69528
|
init_registry();
|
|
69111
69529
|
import { existsSync as existsSync8 } from "fs";
|
|
69112
|
-
import { join as
|
|
69530
|
+
import { join as join9 } from "path";
|
|
69113
69531
|
|
|
69114
69532
|
// src/analyze/scanner.ts
|
|
69115
|
-
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
69116
|
-
import { join as
|
|
69533
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
69534
|
+
import { join as join7 } from "path";
|
|
69117
69535
|
async function scanCodebase(workdir) {
|
|
69118
|
-
const srcPath =
|
|
69119
|
-
const packageJsonPath =
|
|
69536
|
+
const srcPath = join7(workdir, "src");
|
|
69537
|
+
const packageJsonPath = join7(workdir, "package.json");
|
|
69120
69538
|
const fileTree = existsSync5(srcPath) ? await generateFileTree(srcPath, 3) : "No src/ directory";
|
|
69121
69539
|
let dependencies = {};
|
|
69122
69540
|
let devDependencies = {};
|
|
@@ -69141,7 +69559,7 @@ async function generateFileTree(dir, maxDepth, currentDepth = 0, prefix = "") {
|
|
|
69141
69559
|
}
|
|
69142
69560
|
const entries = [];
|
|
69143
69561
|
try {
|
|
69144
|
-
const dirEntries =
|
|
69562
|
+
const dirEntries = readdirSync2(dir, { withFileTypes: true });
|
|
69145
69563
|
dirEntries.sort((a, b) => {
|
|
69146
69564
|
if (a.isDirectory() && !b.isDirectory())
|
|
69147
69565
|
return -1;
|
|
@@ -69157,7 +69575,7 @@ async function generateFileTree(dir, maxDepth, currentDepth = 0, prefix = "") {
|
|
|
69157
69575
|
const isDir = dirent.isDirectory();
|
|
69158
69576
|
entries.push(`${prefix}${connector}${dirent.name}${isDir ? "/" : ""}`);
|
|
69159
69577
|
if (isDir) {
|
|
69160
|
-
const subtree = await generateFileTree(
|
|
69578
|
+
const subtree = await generateFileTree(join7(dir, dirent.name), maxDepth, currentDepth + 1, prefix + childPrefix);
|
|
69161
69579
|
if (subtree) {
|
|
69162
69580
|
entries.push(subtree);
|
|
69163
69581
|
}
|
|
@@ -69181,16 +69599,16 @@ function detectTestPatterns(workdir, dependencies, devDependencies) {
|
|
|
69181
69599
|
} else {
|
|
69182
69600
|
patterns.push("Test framework: likely bun:test (no framework dependency)");
|
|
69183
69601
|
}
|
|
69184
|
-
if (existsSync5(
|
|
69602
|
+
if (existsSync5(join7(workdir, "test"))) {
|
|
69185
69603
|
patterns.push("Test directory: test/");
|
|
69186
69604
|
}
|
|
69187
|
-
if (existsSync5(
|
|
69605
|
+
if (existsSync5(join7(workdir, "__tests__"))) {
|
|
69188
69606
|
patterns.push("Test directory: __tests__/");
|
|
69189
69607
|
}
|
|
69190
|
-
if (existsSync5(
|
|
69608
|
+
if (existsSync5(join7(workdir, "tests"))) {
|
|
69191
69609
|
patterns.push("Test directory: tests/");
|
|
69192
69610
|
}
|
|
69193
|
-
const hasTestFiles = existsSync5(
|
|
69611
|
+
const hasTestFiles = existsSync5(join7(workdir, "test")) || existsSync5(join7(workdir, "src"));
|
|
69194
69612
|
if (hasTestFiles) {
|
|
69195
69613
|
patterns.push("Test files: *.test.ts, *.spec.ts");
|
|
69196
69614
|
}
|
|
@@ -69206,7 +69624,7 @@ init_version();
|
|
|
69206
69624
|
// src/cli/analyze-parser.ts
|
|
69207
69625
|
init_registry();
|
|
69208
69626
|
import { existsSync as existsSync7 } from "fs";
|
|
69209
|
-
import { join as
|
|
69627
|
+
import { join as join8 } from "path";
|
|
69210
69628
|
init_config();
|
|
69211
69629
|
init_logger2();
|
|
69212
69630
|
init_prd();
|
|
@@ -69336,7 +69754,7 @@ function estimateLOCFromComplexity(complexity) {
|
|
|
69336
69754
|
}
|
|
69337
69755
|
}
|
|
69338
69756
|
async function reclassifyExistingPRD(featureDir, featureName, branchName, workdir, config2) {
|
|
69339
|
-
const prdPath =
|
|
69757
|
+
const prdPath = join8(featureDir, "prd.json");
|
|
69340
69758
|
if (!existsSync7(prdPath)) {
|
|
69341
69759
|
throw new Error(`prd.json not found at ${prdPath}. Run analyze without --reclassify first.`);
|
|
69342
69760
|
}
|
|
@@ -69437,11 +69855,11 @@ function reclassifyWithKeywords(story, config2) {
|
|
|
69437
69855
|
// src/cli/analyze.ts
|
|
69438
69856
|
async function analyzeFeature(options) {
|
|
69439
69857
|
const { featureDir, featureName, branchName, config: config2, specPath: explicitSpecPath, reclassify = false } = options;
|
|
69440
|
-
const workdir =
|
|
69858
|
+
const workdir = join9(featureDir, "../..");
|
|
69441
69859
|
if (reclassify) {
|
|
69442
69860
|
return await reclassifyExistingPRD(featureDir, featureName, branchName, workdir, config2);
|
|
69443
69861
|
}
|
|
69444
|
-
const specPath = explicitSpecPath ||
|
|
69862
|
+
const specPath = explicitSpecPath || join9(featureDir, "spec.md");
|
|
69445
69863
|
if (!existsSync8(specPath))
|
|
69446
69864
|
throw new Error(`spec.md not found at ${specPath}`);
|
|
69447
69865
|
const specContent = await Bun.file(specPath).text();
|
|
@@ -69558,7 +69976,7 @@ async function generateAcceptanceTestsForFeature(specContent, featureName, featu
|
|
|
69558
69976
|
modelDef,
|
|
69559
69977
|
config: config2
|
|
69560
69978
|
});
|
|
69561
|
-
const acceptanceTestPath =
|
|
69979
|
+
const acceptanceTestPath = join9(featureDir, config2.acceptance.testPath);
|
|
69562
69980
|
await Bun.write(acceptanceTestPath, result.testCode);
|
|
69563
69981
|
logger.info("cli", "[OK] Acceptance tests generated", {
|
|
69564
69982
|
criteriaCount: result.criteria.length,
|
|
@@ -69571,18 +69989,18 @@ async function generateAcceptanceTestsForFeature(specContent, featureName, featu
|
|
|
69571
69989
|
// src/cli/plan.ts
|
|
69572
69990
|
init_registry();
|
|
69573
69991
|
import { existsSync as existsSync15 } from "fs";
|
|
69574
|
-
import { join as
|
|
69992
|
+
import { join as join13 } from "path";
|
|
69575
69993
|
import { createInterface as createInterface2 } from "readline";
|
|
69576
69994
|
init_test_strategy();
|
|
69577
69995
|
|
|
69578
69996
|
// src/context/generator.ts
|
|
69579
69997
|
init_path_security();
|
|
69580
69998
|
import { existsSync as existsSync10, readFileSync } from "fs";
|
|
69581
|
-
import { join as
|
|
69999
|
+
import { join as join11, relative } from "path";
|
|
69582
70000
|
|
|
69583
70001
|
// src/context/injector.ts
|
|
69584
70002
|
import { existsSync as existsSync9 } from "fs";
|
|
69585
|
-
import { join as
|
|
70003
|
+
import { join as join10 } from "path";
|
|
69586
70004
|
var NOTABLE_NODE_DEPS = [
|
|
69587
70005
|
"@nestjs",
|
|
69588
70006
|
"express",
|
|
@@ -69612,7 +70030,7 @@ var NOTABLE_NODE_DEPS = [
|
|
|
69612
70030
|
"ioredis"
|
|
69613
70031
|
];
|
|
69614
70032
|
async function detectNode(workdir) {
|
|
69615
|
-
const pkgPath =
|
|
70033
|
+
const pkgPath = join10(workdir, "package.json");
|
|
69616
70034
|
if (!existsSync9(pkgPath))
|
|
69617
70035
|
return null;
|
|
69618
70036
|
try {
|
|
@@ -69629,7 +70047,7 @@ async function detectNode(workdir) {
|
|
|
69629
70047
|
}
|
|
69630
70048
|
}
|
|
69631
70049
|
async function detectGo(workdir) {
|
|
69632
|
-
const goMod =
|
|
70050
|
+
const goMod = join10(workdir, "go.mod");
|
|
69633
70051
|
if (!existsSync9(goMod))
|
|
69634
70052
|
return null;
|
|
69635
70053
|
try {
|
|
@@ -69653,7 +70071,7 @@ async function detectGo(workdir) {
|
|
|
69653
70071
|
}
|
|
69654
70072
|
}
|
|
69655
70073
|
async function detectRust(workdir) {
|
|
69656
|
-
const cargoPath =
|
|
70074
|
+
const cargoPath = join10(workdir, "Cargo.toml");
|
|
69657
70075
|
if (!existsSync9(cargoPath))
|
|
69658
70076
|
return null;
|
|
69659
70077
|
try {
|
|
@@ -69669,8 +70087,8 @@ async function detectRust(workdir) {
|
|
|
69669
70087
|
}
|
|
69670
70088
|
}
|
|
69671
70089
|
async function detectPython(workdir) {
|
|
69672
|
-
const pyproject =
|
|
69673
|
-
const requirements =
|
|
70090
|
+
const pyproject = join10(workdir, "pyproject.toml");
|
|
70091
|
+
const requirements = join10(workdir, "requirements.txt");
|
|
69674
70092
|
if (!existsSync9(pyproject) && !existsSync9(requirements))
|
|
69675
70093
|
return null;
|
|
69676
70094
|
try {
|
|
@@ -69689,7 +70107,7 @@ async function detectPython(workdir) {
|
|
|
69689
70107
|
}
|
|
69690
70108
|
}
|
|
69691
70109
|
async function detectPhp(workdir) {
|
|
69692
|
-
const composerPath =
|
|
70110
|
+
const composerPath = join10(workdir, "composer.json");
|
|
69693
70111
|
if (!existsSync9(composerPath))
|
|
69694
70112
|
return null;
|
|
69695
70113
|
try {
|
|
@@ -69702,7 +70120,7 @@ async function detectPhp(workdir) {
|
|
|
69702
70120
|
}
|
|
69703
70121
|
}
|
|
69704
70122
|
async function detectRuby(workdir) {
|
|
69705
|
-
const gemfile =
|
|
70123
|
+
const gemfile = join10(workdir, "Gemfile");
|
|
69706
70124
|
if (!existsSync9(gemfile))
|
|
69707
70125
|
return null;
|
|
69708
70126
|
try {
|
|
@@ -69714,9 +70132,9 @@ async function detectRuby(workdir) {
|
|
|
69714
70132
|
}
|
|
69715
70133
|
}
|
|
69716
70134
|
async function detectJvm(workdir) {
|
|
69717
|
-
const pom =
|
|
69718
|
-
const gradle =
|
|
69719
|
-
const gradleKts =
|
|
70135
|
+
const pom = join10(workdir, "pom.xml");
|
|
70136
|
+
const gradle = join10(workdir, "build.gradle");
|
|
70137
|
+
const gradleKts = join10(workdir, "build.gradle.kts");
|
|
69720
70138
|
if (!existsSync9(pom) && !existsSync9(gradle) && !existsSync9(gradleKts))
|
|
69721
70139
|
return null;
|
|
69722
70140
|
try {
|
|
@@ -69724,7 +70142,7 @@ async function detectJvm(workdir) {
|
|
|
69724
70142
|
const content2 = await Bun.file(pom).text();
|
|
69725
70143
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
69726
70144
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
69727
|
-
const lang2 = existsSync9(
|
|
70145
|
+
const lang2 = existsSync9(join10(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
69728
70146
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
69729
70147
|
}
|
|
69730
70148
|
const gradleFile = existsSync9(gradleKts) ? gradleKts : gradle;
|
|
@@ -69951,7 +70369,7 @@ async function generateFor(agent, options, config2) {
|
|
|
69951
70369
|
try {
|
|
69952
70370
|
const context = await loadContextContent(options, config2);
|
|
69953
70371
|
const content = generator.generate(context);
|
|
69954
|
-
const outputPath =
|
|
70372
|
+
const outputPath = join11(options.outputDir, generator.outputFile);
|
|
69955
70373
|
validateFilePath(outputPath, options.outputDir);
|
|
69956
70374
|
if (!options.dryRun) {
|
|
69957
70375
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -69969,7 +70387,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
69969
70387
|
for (const [agentKey, generator] of entries) {
|
|
69970
70388
|
try {
|
|
69971
70389
|
const content = generator.generate(context);
|
|
69972
|
-
const outputPath =
|
|
70390
|
+
const outputPath = join11(options.outputDir, generator.outputFile);
|
|
69973
70391
|
validateFilePath(outputPath, options.outputDir);
|
|
69974
70392
|
if (!options.dryRun) {
|
|
69975
70393
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -69989,7 +70407,7 @@ async function discoverPackages(repoRoot) {
|
|
|
69989
70407
|
const glob = new Bun.Glob(pattern);
|
|
69990
70408
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
69991
70409
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
69992
|
-
const pkgAbsolute =
|
|
70410
|
+
const pkgAbsolute = join11(repoRoot, pkgRelative);
|
|
69993
70411
|
if (!seen.has(pkgAbsolute)) {
|
|
69994
70412
|
seen.add(pkgAbsolute);
|
|
69995
70413
|
packages.push(pkgAbsolute);
|
|
@@ -70021,7 +70439,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70021
70439
|
}
|
|
70022
70440
|
}
|
|
70023
70441
|
}
|
|
70024
|
-
const turboPath =
|
|
70442
|
+
const turboPath = join11(repoRoot, "turbo.json");
|
|
70025
70443
|
if (_generatorDeps.existsSync(turboPath)) {
|
|
70026
70444
|
try {
|
|
70027
70445
|
const turbo = JSON.parse(_generatorDeps.readFileSync(turboPath, "utf-8"));
|
|
@@ -70030,7 +70448,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70030
70448
|
}
|
|
70031
70449
|
} catch {}
|
|
70032
70450
|
}
|
|
70033
|
-
const pkgPath =
|
|
70451
|
+
const pkgPath = join11(repoRoot, "package.json");
|
|
70034
70452
|
if (_generatorDeps.existsSync(pkgPath)) {
|
|
70035
70453
|
try {
|
|
70036
70454
|
const pkg = JSON.parse(_generatorDeps.readFileSync(pkgPath, "utf-8"));
|
|
@@ -70040,7 +70458,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70040
70458
|
await resolveGlobs(patterns);
|
|
70041
70459
|
} catch {}
|
|
70042
70460
|
}
|
|
70043
|
-
const pnpmPath =
|
|
70461
|
+
const pnpmPath = join11(repoRoot, "pnpm-workspace.yaml");
|
|
70044
70462
|
if (_generatorDeps.existsSync(pnpmPath)) {
|
|
70045
70463
|
try {
|
|
70046
70464
|
const raw = _generatorDeps.readFileSync(pnpmPath, "utf-8");
|
|
@@ -70068,7 +70486,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70068
70486
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
70069
70487
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
70070
70488
|
const relativePkgPath = relative(resolvedRepoRoot, packageDir);
|
|
70071
|
-
const contextPath =
|
|
70489
|
+
const contextPath = join11(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
70072
70490
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
70073
70491
|
return [
|
|
70074
70492
|
{
|
|
@@ -70300,7 +70718,7 @@ var _planDeps = {
|
|
|
70300
70718
|
writeFile: (path, content) => Bun.write(path, content).then(() => {}),
|
|
70301
70719
|
scanCodebase: (workdir) => scanCodebase(workdir),
|
|
70302
70720
|
getAgent: (name, cfg) => cfg ? createAgentRegistry(cfg).getAgent(name) : getAgent(name),
|
|
70303
|
-
readPackageJson: (workdir) => Bun.file(
|
|
70721
|
+
readPackageJson: (workdir) => Bun.file(join13(workdir, "package.json")).json().catch(() => null),
|
|
70304
70722
|
spawnSync: (cmd, opts) => {
|
|
70305
70723
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
70306
70724
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
@@ -70320,7 +70738,7 @@ var _planDeps = {
|
|
|
70320
70738
|
planDecompose: (workdir, config2, opts) => planDecomposeCommand(workdir, config2, opts)
|
|
70321
70739
|
};
|
|
70322
70740
|
async function planCommand(workdir, config2, options) {
|
|
70323
|
-
const naxDir =
|
|
70741
|
+
const naxDir = join13(workdir, ".nax");
|
|
70324
70742
|
if (!existsSync15(naxDir)) {
|
|
70325
70743
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
70326
70744
|
}
|
|
@@ -70336,13 +70754,13 @@ async function planCommand(workdir, config2, options) {
|
|
|
70336
70754
|
const codebaseContext = buildCodebaseContext2(scan);
|
|
70337
70755
|
const relativePackages = discoveredPackages.map((p) => p.startsWith("/") ? p.replace(`${workdir}/`, "") : p);
|
|
70338
70756
|
const packageDetails = relativePackages.length > 0 ? await Promise.all(relativePackages.map(async (rel) => {
|
|
70339
|
-
const pkgJson = await _planDeps.readPackageJsonAt(
|
|
70757
|
+
const pkgJson = await _planDeps.readPackageJsonAt(join13(workdir, rel, "package.json"));
|
|
70340
70758
|
return buildPackageSummary(rel, pkgJson);
|
|
70341
70759
|
})) : [];
|
|
70342
70760
|
const projectName = detectProjectName(workdir, pkg);
|
|
70343
70761
|
const branchName = options.branch ?? `feat/${options.feature}`;
|
|
70344
|
-
const outputDir =
|
|
70345
|
-
const outputPath =
|
|
70762
|
+
const outputDir = join13(naxDir, "features", options.feature);
|
|
70763
|
+
const outputPath = join13(outputDir, "prd.json");
|
|
70346
70764
|
await _planDeps.mkdirp(outputDir);
|
|
70347
70765
|
const agentName = config2?.autoMode?.defaultAgent ?? "claude";
|
|
70348
70766
|
const timeoutSeconds = config2?.plan?.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS2;
|
|
@@ -70777,7 +71195,7 @@ Return JSON with this exact structure (no markdown, no explanation \u2014 JSON o
|
|
|
70777
71195
|
}`;
|
|
70778
71196
|
}
|
|
70779
71197
|
async function planDecomposeCommand(workdir, config2, options) {
|
|
70780
|
-
const prdPath =
|
|
71198
|
+
const prdPath = join13(workdir, ".nax", "features", options.feature, "prd.json");
|
|
70781
71199
|
if (!_planDeps.existsSync(prdPath)) {
|
|
70782
71200
|
throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
|
|
70783
71201
|
stage: "decompose",
|
|
@@ -70807,6 +71225,15 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
70807
71225
|
const adapter = _planDeps.getAgent(agentName, config2);
|
|
70808
71226
|
if (!adapter)
|
|
70809
71227
|
throw new Error(`[decompose] No agent adapter found for '${agentName}'`);
|
|
71228
|
+
let decomposeModel;
|
|
71229
|
+
try {
|
|
71230
|
+
const planTier = config2?.plan?.model ?? "balanced";
|
|
71231
|
+
const { resolveModelForAgent: resolveModelForAgent2 } = await Promise.resolve().then(() => (init_schema(), exports_schema));
|
|
71232
|
+
if (config2?.models) {
|
|
71233
|
+
const defaultAgent = config2.autoMode?.defaultAgent ?? "claude";
|
|
71234
|
+
decomposeModel = resolveModelForAgent2(config2.models, defaultAgent, planTier, defaultAgent).model;
|
|
71235
|
+
}
|
|
71236
|
+
} catch {}
|
|
70810
71237
|
const stages = config2?.debate?.stages;
|
|
70811
71238
|
const debateEnabled = config2?.debate?.enabled && stages?.decompose?.enabled;
|
|
70812
71239
|
let rawResponse;
|
|
@@ -70828,6 +71255,7 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
70828
71255
|
rawResponse = debateResult.output;
|
|
70829
71256
|
} else {
|
|
70830
71257
|
const completeResult = await adapter.complete(prompt, {
|
|
71258
|
+
model: decomposeModel,
|
|
70831
71259
|
jsonMode: true,
|
|
70832
71260
|
workdir,
|
|
70833
71261
|
sessionRole: "decompose",
|
|
@@ -70839,6 +71267,7 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
70839
71267
|
}
|
|
70840
71268
|
} else {
|
|
70841
71269
|
const completeResult = await adapter.complete(prompt, {
|
|
71270
|
+
model: decomposeModel,
|
|
70842
71271
|
jsonMode: true,
|
|
70843
71272
|
workdir,
|
|
70844
71273
|
sessionRole: "decompose",
|
|
@@ -71068,14 +71497,14 @@ async function displayModelEfficiency(workdir) {
|
|
|
71068
71497
|
}
|
|
71069
71498
|
// src/cli/status-features.ts
|
|
71070
71499
|
init_source();
|
|
71071
|
-
import { existsSync as existsSync17, readdirSync as
|
|
71072
|
-
import { join as
|
|
71500
|
+
import { existsSync as existsSync17, readdirSync as readdirSync4 } from "fs";
|
|
71501
|
+
import { join as join16 } from "path";
|
|
71073
71502
|
|
|
71074
71503
|
// src/commands/common.ts
|
|
71075
71504
|
init_path_security();
|
|
71076
71505
|
init_errors();
|
|
71077
|
-
import { existsSync as existsSync16, readdirSync as
|
|
71078
|
-
import { join as
|
|
71506
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
|
|
71507
|
+
import { join as join14, resolve as resolve4 } from "path";
|
|
71079
71508
|
function resolveProject(options = {}) {
|
|
71080
71509
|
const { dir, feature } = options;
|
|
71081
71510
|
let projectRoot;
|
|
@@ -71083,12 +71512,12 @@ function resolveProject(options = {}) {
|
|
|
71083
71512
|
let configPath;
|
|
71084
71513
|
if (dir) {
|
|
71085
71514
|
projectRoot = realpathSync2(resolve4(dir));
|
|
71086
|
-
naxDir =
|
|
71515
|
+
naxDir = join14(projectRoot, ".nax");
|
|
71087
71516
|
if (!existsSync16(naxDir)) {
|
|
71088
71517
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
71089
71518
|
Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
|
|
71090
71519
|
}
|
|
71091
|
-
configPath =
|
|
71520
|
+
configPath = join14(naxDir, "config.json");
|
|
71092
71521
|
if (!existsSync16(configPath)) {
|
|
71093
71522
|
throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
|
|
71094
71523
|
Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
@@ -71096,24 +71525,24 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
|
71096
71525
|
} else {
|
|
71097
71526
|
const found = findProjectRoot(process.cwd());
|
|
71098
71527
|
if (!found) {
|
|
71099
|
-
const cwdNaxDir =
|
|
71528
|
+
const cwdNaxDir = join14(process.cwd(), ".nax");
|
|
71100
71529
|
if (existsSync16(cwdNaxDir)) {
|
|
71101
|
-
const cwdConfigPath =
|
|
71530
|
+
const cwdConfigPath = join14(cwdNaxDir, "config.json");
|
|
71102
71531
|
throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
|
|
71103
71532
|
Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
|
|
71104
71533
|
}
|
|
71105
71534
|
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() });
|
|
71106
71535
|
}
|
|
71107
71536
|
projectRoot = found;
|
|
71108
|
-
naxDir =
|
|
71109
|
-
configPath =
|
|
71537
|
+
naxDir = join14(projectRoot, ".nax");
|
|
71538
|
+
configPath = join14(naxDir, "config.json");
|
|
71110
71539
|
}
|
|
71111
71540
|
let featureDir;
|
|
71112
71541
|
if (feature) {
|
|
71113
|
-
const featuresDir =
|
|
71114
|
-
featureDir =
|
|
71542
|
+
const featuresDir = join14(naxDir, "features");
|
|
71543
|
+
featureDir = join14(featuresDir, feature);
|
|
71115
71544
|
if (!existsSync16(featureDir)) {
|
|
71116
|
-
const availableFeatures = existsSync16(featuresDir) ?
|
|
71545
|
+
const availableFeatures = existsSync16(featuresDir) ? readdirSync3(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
|
|
71117
71546
|
const availableMsg = availableFeatures.length > 0 ? `
|
|
71118
71547
|
|
|
71119
71548
|
Available features:
|
|
@@ -71138,12 +71567,12 @@ function findProjectRoot(startDir) {
|
|
|
71138
71567
|
let current = resolve4(startDir);
|
|
71139
71568
|
let depth = 0;
|
|
71140
71569
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
71141
|
-
const naxDir =
|
|
71142
|
-
const configPath =
|
|
71570
|
+
const naxDir = join14(current, ".nax");
|
|
71571
|
+
const configPath = join14(naxDir, "config.json");
|
|
71143
71572
|
if (existsSync16(configPath)) {
|
|
71144
71573
|
return realpathSync2(current);
|
|
71145
71574
|
}
|
|
71146
|
-
const parent =
|
|
71575
|
+
const parent = join14(current, "..");
|
|
71147
71576
|
if (parent === current) {
|
|
71148
71577
|
break;
|
|
71149
71578
|
}
|
|
@@ -71165,7 +71594,7 @@ function isPidAlive(pid) {
|
|
|
71165
71594
|
}
|
|
71166
71595
|
}
|
|
71167
71596
|
async function loadStatusFile(featureDir) {
|
|
71168
|
-
const statusPath =
|
|
71597
|
+
const statusPath = join16(featureDir, "status.json");
|
|
71169
71598
|
if (!existsSync17(statusPath)) {
|
|
71170
71599
|
return null;
|
|
71171
71600
|
}
|
|
@@ -71177,7 +71606,7 @@ async function loadStatusFile(featureDir) {
|
|
|
71177
71606
|
}
|
|
71178
71607
|
}
|
|
71179
71608
|
async function loadProjectStatusFile(projectDir) {
|
|
71180
|
-
const statusPath =
|
|
71609
|
+
const statusPath = join16(projectDir, ".nax", "status.json");
|
|
71181
71610
|
if (!existsSync17(statusPath)) {
|
|
71182
71611
|
return null;
|
|
71183
71612
|
}
|
|
@@ -71189,7 +71618,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
71189
71618
|
}
|
|
71190
71619
|
}
|
|
71191
71620
|
async function getFeatureSummary(featureName, featureDir) {
|
|
71192
|
-
const prdPath =
|
|
71621
|
+
const prdPath = join16(featureDir, "prd.json");
|
|
71193
71622
|
if (!existsSync17(prdPath)) {
|
|
71194
71623
|
return {
|
|
71195
71624
|
name: featureName,
|
|
@@ -71232,9 +71661,9 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
71232
71661
|
};
|
|
71233
71662
|
}
|
|
71234
71663
|
}
|
|
71235
|
-
const runsDir =
|
|
71664
|
+
const runsDir = join16(featureDir, "runs");
|
|
71236
71665
|
if (existsSync17(runsDir)) {
|
|
71237
|
-
const runs =
|
|
71666
|
+
const runs = readdirSync4(runsDir, { withFileTypes: true }).filter((e) => e.isFile() && e.name.endsWith(".jsonl") && e.name !== "latest.jsonl").map((e) => e.name).sort().reverse();
|
|
71238
71667
|
if (runs.length > 0) {
|
|
71239
71668
|
const latestRun = runs[0].replace(".jsonl", "");
|
|
71240
71669
|
summary.lastRun = latestRun;
|
|
@@ -71243,12 +71672,12 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
71243
71672
|
return summary;
|
|
71244
71673
|
}
|
|
71245
71674
|
async function displayAllFeatures(projectDir) {
|
|
71246
|
-
const featuresDir =
|
|
71675
|
+
const featuresDir = join16(projectDir, ".nax", "features");
|
|
71247
71676
|
if (!existsSync17(featuresDir)) {
|
|
71248
71677
|
console.log(source_default.dim("No features found."));
|
|
71249
71678
|
return;
|
|
71250
71679
|
}
|
|
71251
|
-
const features =
|
|
71680
|
+
const features = readdirSync4(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
71252
71681
|
if (features.length === 0) {
|
|
71253
71682
|
console.log(source_default.dim("No features found."));
|
|
71254
71683
|
return;
|
|
@@ -71284,7 +71713,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
71284
71713
|
console.log();
|
|
71285
71714
|
}
|
|
71286
71715
|
}
|
|
71287
|
-
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name,
|
|
71716
|
+
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join16(featuresDir, name))));
|
|
71288
71717
|
console.log(source_default.bold(`\uD83D\uDCCA Features
|
|
71289
71718
|
`));
|
|
71290
71719
|
const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
|
|
@@ -71310,7 +71739,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
71310
71739
|
console.log();
|
|
71311
71740
|
}
|
|
71312
71741
|
async function displayFeatureDetails(featureName, featureDir) {
|
|
71313
|
-
const prdPath =
|
|
71742
|
+
const prdPath = join16(featureDir, "prd.json");
|
|
71314
71743
|
if (!existsSync17(prdPath)) {
|
|
71315
71744
|
console.log(source_default.bold(`
|
|
71316
71745
|
\uD83D\uDCCA ${featureName}
|
|
@@ -71431,8 +71860,8 @@ async function displayFeatureStatus(options = {}) {
|
|
|
71431
71860
|
// src/cli/runs.ts
|
|
71432
71861
|
init_errors();
|
|
71433
71862
|
init_logger2();
|
|
71434
|
-
import { existsSync as existsSync18, readdirSync as
|
|
71435
|
-
import { join as
|
|
71863
|
+
import { existsSync as existsSync18, readdirSync as readdirSync5 } from "fs";
|
|
71864
|
+
import { join as join17 } from "path";
|
|
71436
71865
|
async function parseRunLog(logPath) {
|
|
71437
71866
|
const logger = getLogger();
|
|
71438
71867
|
try {
|
|
@@ -71448,19 +71877,19 @@ async function parseRunLog(logPath) {
|
|
|
71448
71877
|
async function runsListCommand(options) {
|
|
71449
71878
|
const logger = getLogger();
|
|
71450
71879
|
const { feature, workdir } = options;
|
|
71451
|
-
const runsDir =
|
|
71880
|
+
const runsDir = join17(workdir, ".nax", "features", feature, "runs");
|
|
71452
71881
|
if (!existsSync18(runsDir)) {
|
|
71453
71882
|
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
71454
71883
|
return;
|
|
71455
71884
|
}
|
|
71456
|
-
const files =
|
|
71885
|
+
const files = readdirSync5(runsDir).filter((f) => f.endsWith(".jsonl"));
|
|
71457
71886
|
if (files.length === 0) {
|
|
71458
71887
|
logger.info("cli", "No runs found for feature", { feature });
|
|
71459
71888
|
return;
|
|
71460
71889
|
}
|
|
71461
71890
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
71462
71891
|
for (const file3 of files.sort().reverse()) {
|
|
71463
|
-
const logPath =
|
|
71892
|
+
const logPath = join17(runsDir, file3);
|
|
71464
71893
|
const entries = await parseRunLog(logPath);
|
|
71465
71894
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
71466
71895
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
@@ -71486,7 +71915,7 @@ async function runsListCommand(options) {
|
|
|
71486
71915
|
async function runsShowCommand(options) {
|
|
71487
71916
|
const logger = getLogger();
|
|
71488
71917
|
const { runId, feature, workdir } = options;
|
|
71489
|
-
const logPath =
|
|
71918
|
+
const logPath = join17(workdir, ".nax", "features", feature, "runs", `${runId}.jsonl`);
|
|
71490
71919
|
if (!existsSync18(logPath)) {
|
|
71491
71920
|
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
71492
71921
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
@@ -71525,7 +71954,7 @@ async function runsShowCommand(options) {
|
|
|
71525
71954
|
// src/cli/prompts-main.ts
|
|
71526
71955
|
init_logger2();
|
|
71527
71956
|
import { existsSync as existsSync22, mkdirSync as mkdirSync2 } from "fs";
|
|
71528
|
-
import { join as
|
|
71957
|
+
import { join as join30 } from "path";
|
|
71529
71958
|
|
|
71530
71959
|
// src/pipeline/index.ts
|
|
71531
71960
|
init_runner();
|
|
@@ -71600,7 +72029,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
71600
72029
|
|
|
71601
72030
|
// src/cli/prompts-tdd.ts
|
|
71602
72031
|
init_prompts2();
|
|
71603
|
-
import { join as
|
|
72032
|
+
import { join as join29 } from "path";
|
|
71604
72033
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
71605
72034
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
71606
72035
|
PromptBuilder.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(),
|
|
@@ -71619,7 +72048,7 @@ ${frontmatter}---
|
|
|
71619
72048
|
|
|
71620
72049
|
${session.prompt}`;
|
|
71621
72050
|
if (outputDir) {
|
|
71622
|
-
const promptFile =
|
|
72051
|
+
const promptFile = join29(outputDir, `${story.id}.${session.role}.md`);
|
|
71623
72052
|
await Bun.write(promptFile, fullOutput);
|
|
71624
72053
|
logger.info("cli", "Written TDD prompt file", {
|
|
71625
72054
|
storyId: story.id,
|
|
@@ -71635,7 +72064,7 @@ ${"=".repeat(80)}`);
|
|
|
71635
72064
|
}
|
|
71636
72065
|
}
|
|
71637
72066
|
if (outputDir && ctx.contextMarkdown) {
|
|
71638
|
-
const contextFile =
|
|
72067
|
+
const contextFile = join29(outputDir, `${story.id}.context.md`);
|
|
71639
72068
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
71640
72069
|
const contextOutput = `---
|
|
71641
72070
|
${frontmatter}---
|
|
@@ -71649,12 +72078,12 @@ ${ctx.contextMarkdown}`;
|
|
|
71649
72078
|
async function promptsCommand(options) {
|
|
71650
72079
|
const logger = getLogger();
|
|
71651
72080
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
71652
|
-
const naxDir =
|
|
72081
|
+
const naxDir = join30(workdir, ".nax");
|
|
71653
72082
|
if (!existsSync22(naxDir)) {
|
|
71654
72083
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
71655
72084
|
}
|
|
71656
|
-
const featureDir =
|
|
71657
|
-
const prdPath =
|
|
72085
|
+
const featureDir = join30(naxDir, "features", feature);
|
|
72086
|
+
const prdPath = join30(featureDir, "prd.json");
|
|
71658
72087
|
if (!existsSync22(prdPath)) {
|
|
71659
72088
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
71660
72089
|
}
|
|
@@ -71715,10 +72144,10 @@ ${frontmatter}---
|
|
|
71715
72144
|
|
|
71716
72145
|
${ctx.prompt}`;
|
|
71717
72146
|
if (outputDir) {
|
|
71718
|
-
const promptFile =
|
|
72147
|
+
const promptFile = join30(outputDir, `${story.id}.prompt.md`);
|
|
71719
72148
|
await Bun.write(promptFile, fullOutput);
|
|
71720
72149
|
if (ctx.contextMarkdown) {
|
|
71721
|
-
const contextFile =
|
|
72150
|
+
const contextFile = join30(outputDir, `${story.id}.context.md`);
|
|
71722
72151
|
const contextOutput = `---
|
|
71723
72152
|
${frontmatter}---
|
|
71724
72153
|
|
|
@@ -71745,7 +72174,7 @@ ${"=".repeat(80)}`);
|
|
|
71745
72174
|
}
|
|
71746
72175
|
// src/cli/prompts-init.ts
|
|
71747
72176
|
import { existsSync as existsSync23, mkdirSync as mkdirSync3 } from "fs";
|
|
71748
|
-
import { join as
|
|
72177
|
+
import { join as join31 } from "path";
|
|
71749
72178
|
var TEMPLATE_ROLES = [
|
|
71750
72179
|
{ file: "test-writer.md", role: "test-writer" },
|
|
71751
72180
|
{ file: "implementer.md", role: "implementer", variant: "standard" },
|
|
@@ -71769,9 +72198,9 @@ var TEMPLATE_HEADER = `<!--
|
|
|
71769
72198
|
`;
|
|
71770
72199
|
async function promptsInitCommand(options) {
|
|
71771
72200
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
71772
|
-
const templatesDir =
|
|
72201
|
+
const templatesDir = join31(workdir, ".nax", "templates");
|
|
71773
72202
|
mkdirSync3(templatesDir, { recursive: true });
|
|
71774
|
-
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync23(
|
|
72203
|
+
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync23(join31(templatesDir, f)));
|
|
71775
72204
|
if (existingFiles.length > 0 && !force) {
|
|
71776
72205
|
console.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
71777
72206
|
Pass --force to overwrite existing templates.`);
|
|
@@ -71779,7 +72208,7 @@ async function promptsInitCommand(options) {
|
|
|
71779
72208
|
}
|
|
71780
72209
|
const written = [];
|
|
71781
72210
|
for (const template of TEMPLATE_ROLES) {
|
|
71782
|
-
const filePath =
|
|
72211
|
+
const filePath = join31(templatesDir, template.file);
|
|
71783
72212
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
71784
72213
|
const content = TEMPLATE_HEADER + roleBody;
|
|
71785
72214
|
await Bun.write(filePath, content);
|
|
@@ -71795,7 +72224,7 @@ async function promptsInitCommand(options) {
|
|
|
71795
72224
|
return written;
|
|
71796
72225
|
}
|
|
71797
72226
|
async function autoWirePromptsConfig(workdir) {
|
|
71798
|
-
const configPath =
|
|
72227
|
+
const configPath = join31(workdir, "nax.config.json");
|
|
71799
72228
|
if (!existsSync23(configPath)) {
|
|
71800
72229
|
const exampleConfig = JSON.stringify({
|
|
71801
72230
|
prompts: {
|
|
@@ -71960,8 +72389,8 @@ function pad(str, width) {
|
|
|
71960
72389
|
init_config();
|
|
71961
72390
|
init_logger2();
|
|
71962
72391
|
init_prd();
|
|
71963
|
-
import { existsSync as existsSync25, readdirSync as
|
|
71964
|
-
import { join as
|
|
72392
|
+
import { existsSync as existsSync25, readdirSync as readdirSync6 } from "fs";
|
|
72393
|
+
import { join as join36 } from "path";
|
|
71965
72394
|
|
|
71966
72395
|
// src/cli/diagnose-analysis.ts
|
|
71967
72396
|
function detectFailurePattern(story, prd, status) {
|
|
@@ -72160,7 +72589,7 @@ function isProcessAlive2(pid) {
|
|
|
72160
72589
|
}
|
|
72161
72590
|
}
|
|
72162
72591
|
async function loadStatusFile2(workdir) {
|
|
72163
|
-
const statusPath =
|
|
72592
|
+
const statusPath = join36(workdir, ".nax", "status.json");
|
|
72164
72593
|
if (!existsSync25(statusPath))
|
|
72165
72594
|
return null;
|
|
72166
72595
|
try {
|
|
@@ -72188,7 +72617,7 @@ async function countCommitsSince(workdir, since) {
|
|
|
72188
72617
|
}
|
|
72189
72618
|
}
|
|
72190
72619
|
async function checkLock(workdir) {
|
|
72191
|
-
const lockFile = Bun.file(
|
|
72620
|
+
const lockFile = Bun.file(join36(workdir, "nax.lock"));
|
|
72192
72621
|
if (!await lockFile.exists())
|
|
72193
72622
|
return { lockPresent: false };
|
|
72194
72623
|
try {
|
|
@@ -72206,8 +72635,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
72206
72635
|
const logger = getLogger();
|
|
72207
72636
|
const workdir = options.workdir ?? process.cwd();
|
|
72208
72637
|
const naxSubdir = findProjectDir(workdir);
|
|
72209
|
-
let projectDir = naxSubdir ?
|
|
72210
|
-
if (!projectDir && existsSync25(
|
|
72638
|
+
let projectDir = naxSubdir ? join36(naxSubdir, "..") : null;
|
|
72639
|
+
if (!projectDir && existsSync25(join36(workdir, ".nax"))) {
|
|
72211
72640
|
projectDir = workdir;
|
|
72212
72641
|
}
|
|
72213
72642
|
if (!projectDir)
|
|
@@ -72218,18 +72647,18 @@ async function diagnoseCommand(options = {}) {
|
|
|
72218
72647
|
if (status2) {
|
|
72219
72648
|
feature = status2.run.feature;
|
|
72220
72649
|
} else {
|
|
72221
|
-
const featuresDir =
|
|
72650
|
+
const featuresDir = join36(projectDir, ".nax", "features");
|
|
72222
72651
|
if (!existsSync25(featuresDir))
|
|
72223
72652
|
throw new Error("No features found in project");
|
|
72224
|
-
const features =
|
|
72653
|
+
const features = readdirSync6(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
72225
72654
|
if (features.length === 0)
|
|
72226
72655
|
throw new Error("No features found");
|
|
72227
72656
|
feature = features[0];
|
|
72228
72657
|
logger.info("diagnose", "No feature specified, using first found", { feature });
|
|
72229
72658
|
}
|
|
72230
72659
|
}
|
|
72231
|
-
const featureDir =
|
|
72232
|
-
const prdPath =
|
|
72660
|
+
const featureDir = join36(projectDir, ".nax", "features", feature);
|
|
72661
|
+
const prdPath = join36(featureDir, "prd.json");
|
|
72233
72662
|
if (!existsSync25(prdPath))
|
|
72234
72663
|
throw new Error(`Feature not found: ${feature}`);
|
|
72235
72664
|
const prd = await loadPRD(prdPath);
|
|
@@ -72272,7 +72701,7 @@ init_interaction();
|
|
|
72272
72701
|
init_source();
|
|
72273
72702
|
init_loader();
|
|
72274
72703
|
import { existsSync as existsSync26 } from "fs";
|
|
72275
|
-
import { join as
|
|
72704
|
+
import { join as join37 } from "path";
|
|
72276
72705
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
72277
72706
|
async function generateCommand(options) {
|
|
72278
72707
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -72315,7 +72744,7 @@ async function generateCommand(options) {
|
|
|
72315
72744
|
return;
|
|
72316
72745
|
}
|
|
72317
72746
|
if (options.package) {
|
|
72318
|
-
const packageDir =
|
|
72747
|
+
const packageDir = join37(workdir, options.package);
|
|
72319
72748
|
if (dryRun) {
|
|
72320
72749
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
72321
72750
|
}
|
|
@@ -72335,8 +72764,8 @@ async function generateCommand(options) {
|
|
|
72335
72764
|
process.exit(1);
|
|
72336
72765
|
return;
|
|
72337
72766
|
}
|
|
72338
|
-
const contextPath = options.context ?
|
|
72339
|
-
const outputDir = options.output ?
|
|
72767
|
+
const contextPath = options.context ? join37(workdir, options.context) : join37(workdir, ".nax/context.md");
|
|
72768
|
+
const outputDir = options.output ? join37(workdir, options.output) : workdir;
|
|
72340
72769
|
const autoInject = !options.noAutoInject;
|
|
72341
72770
|
if (!existsSync26(contextPath)) {
|
|
72342
72771
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -72441,7 +72870,7 @@ async function generateCommand(options) {
|
|
|
72441
72870
|
// src/cli/config-display.ts
|
|
72442
72871
|
init_loader();
|
|
72443
72872
|
import { existsSync as existsSync28 } from "fs";
|
|
72444
|
-
import { join as
|
|
72873
|
+
import { join as join39 } from "path";
|
|
72445
72874
|
|
|
72446
72875
|
// src/cli/config-descriptions.ts
|
|
72447
72876
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -72679,7 +73108,7 @@ function deepEqual(a, b) {
|
|
|
72679
73108
|
init_defaults();
|
|
72680
73109
|
init_loader();
|
|
72681
73110
|
import { existsSync as existsSync27 } from "fs";
|
|
72682
|
-
import { join as
|
|
73111
|
+
import { join as join38 } from "path";
|
|
72683
73112
|
async function loadConfigFile(path14) {
|
|
72684
73113
|
if (!existsSync27(path14))
|
|
72685
73114
|
return null;
|
|
@@ -72701,7 +73130,7 @@ async function loadProjectConfig() {
|
|
|
72701
73130
|
const projectDir = findProjectDir();
|
|
72702
73131
|
if (!projectDir)
|
|
72703
73132
|
return null;
|
|
72704
|
-
const projectPath =
|
|
73133
|
+
const projectPath = join38(projectDir, "config.json");
|
|
72705
73134
|
return await loadConfigFile(projectPath);
|
|
72706
73135
|
}
|
|
72707
73136
|
|
|
@@ -72761,7 +73190,7 @@ async function configCommand(config2, options = {}) {
|
|
|
72761
73190
|
function determineConfigSources() {
|
|
72762
73191
|
const globalPath = globalConfigPath();
|
|
72763
73192
|
const projectDir = findProjectDir();
|
|
72764
|
-
const projectPath = projectDir ?
|
|
73193
|
+
const projectPath = projectDir ? join39(projectDir, "config.json") : null;
|
|
72765
73194
|
return {
|
|
72766
73195
|
global: fileExists(globalPath) ? globalPath : null,
|
|
72767
73196
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -72831,6 +73260,26 @@ function displayConfigWithDescriptions(obj, path14, sources, indent = 0) {
|
|
|
72831
73260
|
}
|
|
72832
73261
|
}
|
|
72833
73262
|
}
|
|
73263
|
+
if (indent === 0 && !entries.find(([k]) => k === "prompts")) {
|
|
73264
|
+
console.log("# prompts: Prompt template overrides (PB-003: PromptBuilder)");
|
|
73265
|
+
const description = FIELD_DESCRIPTIONS["prompts.overrides"];
|
|
73266
|
+
if (description) {
|
|
73267
|
+
console.log(`# prompts.overrides: ${description}`);
|
|
73268
|
+
}
|
|
73269
|
+
const roles = ["test-writer", "implementer", "verifier", "single-session"];
|
|
73270
|
+
console.log("overrides:");
|
|
73271
|
+
for (const role of roles) {
|
|
73272
|
+
const roleDesc = FIELD_DESCRIPTIONS[`prompts.overrides.${role}`];
|
|
73273
|
+
if (roleDesc) {
|
|
73274
|
+
console.log(` # ${roleDesc}`);
|
|
73275
|
+
const match = roleDesc.match(/e\.g\., "([^"]+)"/);
|
|
73276
|
+
if (match) {
|
|
73277
|
+
console.log(` # ${role}: "${match[1]}"`);
|
|
73278
|
+
}
|
|
73279
|
+
}
|
|
73280
|
+
}
|
|
73281
|
+
console.log();
|
|
73282
|
+
}
|
|
72834
73283
|
}
|
|
72835
73284
|
function formatValue(value) {
|
|
72836
73285
|
if (value === null)
|
|
@@ -72885,6 +73334,107 @@ function formatValueForTable(value) {
|
|
|
72885
73334
|
}
|
|
72886
73335
|
return String(value);
|
|
72887
73336
|
}
|
|
73337
|
+
// src/cli/config-profile.ts
|
|
73338
|
+
init_paths();
|
|
73339
|
+
init_profile();
|
|
73340
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
73341
|
+
import { readdirSync as readdirSync7 } from "fs";
|
|
73342
|
+
import { join as join40 } from "path";
|
|
73343
|
+
var _profileCLIDeps = {
|
|
73344
|
+
env: process.env
|
|
73345
|
+
};
|
|
73346
|
+
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
73347
|
+
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
73348
|
+
async function profileListCommand(startDir) {
|
|
73349
|
+
const globalProfilesDir = join40(globalConfigDir(), "profiles");
|
|
73350
|
+
const projectProfilesDir = join40(projectConfigDir(startDir), "profiles");
|
|
73351
|
+
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
73352
|
+
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
73353
|
+
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
73354
|
+
const lines = [];
|
|
73355
|
+
lines.push("global:");
|
|
73356
|
+
if (globalProfiles.length === 0) {
|
|
73357
|
+
lines.push(" (none)");
|
|
73358
|
+
} else {
|
|
73359
|
+
for (const name of globalProfiles) {
|
|
73360
|
+
const marker = name === activeProfile ? "* " : " ";
|
|
73361
|
+
lines.push(`${marker}${name}`);
|
|
73362
|
+
}
|
|
73363
|
+
}
|
|
73364
|
+
if (projectProfiles.length > 0) {
|
|
73365
|
+
lines.push("project:");
|
|
73366
|
+
for (const name of projectProfiles) {
|
|
73367
|
+
const marker = name === activeProfile ? "* " : " ";
|
|
73368
|
+
lines.push(`${marker}${name}`);
|
|
73369
|
+
}
|
|
73370
|
+
}
|
|
73371
|
+
return lines.join(`
|
|
73372
|
+
`);
|
|
73373
|
+
}
|
|
73374
|
+
function scanProfileDir(dir) {
|
|
73375
|
+
try {
|
|
73376
|
+
return readdirSync7(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
73377
|
+
} catch {
|
|
73378
|
+
return [];
|
|
73379
|
+
}
|
|
73380
|
+
}
|
|
73381
|
+
async function profileShowCommand(profileName, startDir, opts) {
|
|
73382
|
+
const rawProfile = await loadProfile(profileName, startDir);
|
|
73383
|
+
const envVars = await loadProfileEnv(profileName, startDir);
|
|
73384
|
+
if (opts.unmask) {
|
|
73385
|
+
const resolved = resolveEnvVars(rawProfile, envVars);
|
|
73386
|
+
const warning = "WARNING: Sensitive values are displayed in plaintext.";
|
|
73387
|
+
return `${warning}
|
|
73388
|
+
${JSON.stringify(resolved, null, 2)}`;
|
|
73389
|
+
}
|
|
73390
|
+
const masked = maskProfileValues(rawProfile);
|
|
73391
|
+
return JSON.stringify(masked, null, 2);
|
|
73392
|
+
}
|
|
73393
|
+
function maskProfileValues(obj) {
|
|
73394
|
+
const result = {};
|
|
73395
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
73396
|
+
if (SENSITIVE_KEY_PATTERN.test(key)) {
|
|
73397
|
+
result[key] = "***";
|
|
73398
|
+
} else if (typeof value === "string" && VAR_PATTERN.test(value)) {
|
|
73399
|
+
result[key] = "***";
|
|
73400
|
+
} else if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
73401
|
+
result[key] = maskProfileValues(value);
|
|
73402
|
+
} else {
|
|
73403
|
+
result[key] = value;
|
|
73404
|
+
}
|
|
73405
|
+
}
|
|
73406
|
+
return result;
|
|
73407
|
+
}
|
|
73408
|
+
async function profileUseCommand(profileName, startDir) {
|
|
73409
|
+
const configPath = join40(projectConfigDir(startDir), "config.json");
|
|
73410
|
+
const configFile = Bun.file(configPath);
|
|
73411
|
+
let existing = {};
|
|
73412
|
+
if (await configFile.exists()) {
|
|
73413
|
+
existing = await configFile.json();
|
|
73414
|
+
}
|
|
73415
|
+
if (profileName === "default") {
|
|
73416
|
+
const { profile: _removed, ...rest } = existing;
|
|
73417
|
+
await Bun.write(configPath, JSON.stringify(rest, null, 2));
|
|
73418
|
+
return "Profile reset to default.";
|
|
73419
|
+
}
|
|
73420
|
+
const updated = { ...existing, profile: profileName };
|
|
73421
|
+
await Bun.write(configPath, JSON.stringify(updated, null, 2));
|
|
73422
|
+
return `Now using profile: ${profileName}`;
|
|
73423
|
+
}
|
|
73424
|
+
async function profileCurrentCommand(startDir) {
|
|
73425
|
+
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
73426
|
+
}
|
|
73427
|
+
async function profileCreateCommand(profileName, startDir) {
|
|
73428
|
+
const profilesDir = join40(projectConfigDir(startDir), "profiles");
|
|
73429
|
+
const profilePath = join40(profilesDir, `${profileName}.json`);
|
|
73430
|
+
const profileFile = Bun.file(profilePath);
|
|
73431
|
+
if (await profileFile.exists()) {
|
|
73432
|
+
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
73433
|
+
}
|
|
73434
|
+
mkdirSync4(profilesDir, { recursive: true });
|
|
73435
|
+
await Bun.write(profilePath, "{}");
|
|
73436
|
+
return profilePath;
|
|
73437
|
+
}
|
|
72888
73438
|
// src/cli/agents.ts
|
|
72889
73439
|
init_registry();
|
|
72890
73440
|
init_version_detection();
|
|
@@ -72941,24 +73491,24 @@ async function diagnose(options) {
|
|
|
72941
73491
|
|
|
72942
73492
|
// src/commands/logs.ts
|
|
72943
73493
|
import { existsSync as existsSync30 } from "fs";
|
|
72944
|
-
import { join as
|
|
73494
|
+
import { join as join44 } from "path";
|
|
72945
73495
|
|
|
72946
73496
|
// src/commands/logs-formatter.ts
|
|
72947
73497
|
init_source();
|
|
72948
73498
|
init_formatter();
|
|
72949
|
-
import { readdirSync as
|
|
72950
|
-
import { join as
|
|
73499
|
+
import { readdirSync as readdirSync9 } from "fs";
|
|
73500
|
+
import { join as join43 } from "path";
|
|
72951
73501
|
|
|
72952
73502
|
// src/commands/logs-reader.ts
|
|
72953
|
-
import { existsSync as existsSync29, readdirSync as
|
|
73503
|
+
import { existsSync as existsSync29, readdirSync as readdirSync8 } from "fs";
|
|
72954
73504
|
import { readdir as readdir3 } from "fs/promises";
|
|
72955
|
-
import { join as
|
|
73505
|
+
import { join as join42 } from "path";
|
|
72956
73506
|
|
|
72957
73507
|
// src/utils/paths.ts
|
|
72958
73508
|
import { homedir as homedir4 } from "os";
|
|
72959
|
-
import { join as
|
|
73509
|
+
import { join as join41 } from "path";
|
|
72960
73510
|
function getRunsDir() {
|
|
72961
|
-
return process.env.NAX_RUNS_DIR ??
|
|
73511
|
+
return process.env.NAX_RUNS_DIR ?? join41(homedir4(), ".nax", "runs");
|
|
72962
73512
|
}
|
|
72963
73513
|
|
|
72964
73514
|
// src/commands/logs-reader.ts
|
|
@@ -72975,7 +73525,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
72975
73525
|
}
|
|
72976
73526
|
let matched = null;
|
|
72977
73527
|
for (const entry of entries) {
|
|
72978
|
-
const metaPath =
|
|
73528
|
+
const metaPath = join42(runsDir, entry, "meta.json");
|
|
72979
73529
|
try {
|
|
72980
73530
|
const meta3 = await Bun.file(metaPath).json();
|
|
72981
73531
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -72991,20 +73541,20 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
72991
73541
|
console.log(`Log directory unavailable for run: ${runId}`);
|
|
72992
73542
|
return null;
|
|
72993
73543
|
}
|
|
72994
|
-
const files =
|
|
73544
|
+
const files = readdirSync8(matched.eventsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
72995
73545
|
if (files.length === 0) {
|
|
72996
73546
|
console.log(`No log files found for run: ${runId}`);
|
|
72997
73547
|
return null;
|
|
72998
73548
|
}
|
|
72999
73549
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
73000
|
-
return
|
|
73550
|
+
return join42(matched.eventsDir, specificFile ?? files[0]);
|
|
73001
73551
|
}
|
|
73002
73552
|
async function selectRunFile(runsDir) {
|
|
73003
|
-
const files =
|
|
73553
|
+
const files = readdirSync8(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
73004
73554
|
if (files.length === 0) {
|
|
73005
73555
|
return null;
|
|
73006
73556
|
}
|
|
73007
|
-
return
|
|
73557
|
+
return join42(runsDir, files[0]);
|
|
73008
73558
|
}
|
|
73009
73559
|
async function extractRunSummary(filePath) {
|
|
73010
73560
|
const file3 = Bun.file(filePath);
|
|
@@ -73078,7 +73628,7 @@ var LOG_LEVEL_PRIORITY2 = {
|
|
|
73078
73628
|
error: 3
|
|
73079
73629
|
};
|
|
73080
73630
|
async function displayRunsList(runsDir) {
|
|
73081
|
-
const files =
|
|
73631
|
+
const files = readdirSync9(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
73082
73632
|
if (files.length === 0) {
|
|
73083
73633
|
console.log(source_default.dim("No runs found"));
|
|
73084
73634
|
return;
|
|
@@ -73089,7 +73639,7 @@ Runs:
|
|
|
73089
73639
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
73090
73640
|
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"));
|
|
73091
73641
|
for (const file3 of files) {
|
|
73092
|
-
const filePath =
|
|
73642
|
+
const filePath = join43(runsDir, file3);
|
|
73093
73643
|
const summary = await extractRunSummary(filePath);
|
|
73094
73644
|
const timestamp = file3.replace(".jsonl", "");
|
|
73095
73645
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -73203,7 +73753,7 @@ async function logsCommand(options) {
|
|
|
73203
73753
|
return;
|
|
73204
73754
|
}
|
|
73205
73755
|
const resolved = resolveProject({ dir: options.dir });
|
|
73206
|
-
const naxDir =
|
|
73756
|
+
const naxDir = join44(resolved.projectDir, ".nax");
|
|
73207
73757
|
const configPath = resolved.configPath;
|
|
73208
73758
|
const configFile = Bun.file(configPath);
|
|
73209
73759
|
const config2 = await configFile.json();
|
|
@@ -73211,8 +73761,8 @@ async function logsCommand(options) {
|
|
|
73211
73761
|
if (!featureName) {
|
|
73212
73762
|
throw new Error("No feature specified in config.json");
|
|
73213
73763
|
}
|
|
73214
|
-
const featureDir =
|
|
73215
|
-
const runsDir =
|
|
73764
|
+
const featureDir = join44(naxDir, "features", featureName);
|
|
73765
|
+
const runsDir = join44(featureDir, "runs");
|
|
73216
73766
|
if (!existsSync30(runsDir)) {
|
|
73217
73767
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
73218
73768
|
}
|
|
@@ -73237,7 +73787,7 @@ init_config();
|
|
|
73237
73787
|
init_prd();
|
|
73238
73788
|
init_precheck();
|
|
73239
73789
|
import { existsSync as existsSync31 } from "fs";
|
|
73240
|
-
import { join as
|
|
73790
|
+
import { join as join45 } from "path";
|
|
73241
73791
|
async function precheckCommand(options) {
|
|
73242
73792
|
const resolved = resolveProject({
|
|
73243
73793
|
dir: options.dir,
|
|
@@ -73259,9 +73809,9 @@ async function precheckCommand(options) {
|
|
|
73259
73809
|
process.exit(1);
|
|
73260
73810
|
}
|
|
73261
73811
|
}
|
|
73262
|
-
const naxDir =
|
|
73263
|
-
const featureDir =
|
|
73264
|
-
const prdPath =
|
|
73812
|
+
const naxDir = join45(resolved.projectDir, ".nax");
|
|
73813
|
+
const featureDir = join45(naxDir, "features", featureName);
|
|
73814
|
+
const prdPath = join45(featureDir, "prd.json");
|
|
73265
73815
|
if (!existsSync31(featureDir)) {
|
|
73266
73816
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
73267
73817
|
process.exit(1);
|
|
@@ -73283,7 +73833,7 @@ async function precheckCommand(options) {
|
|
|
73283
73833
|
// src/commands/runs.ts
|
|
73284
73834
|
init_source();
|
|
73285
73835
|
import { readdir as readdir4 } from "fs/promises";
|
|
73286
|
-
import { join as
|
|
73836
|
+
import { join as join46 } from "path";
|
|
73287
73837
|
var DEFAULT_LIMIT = 20;
|
|
73288
73838
|
var _runsCmdDeps = {
|
|
73289
73839
|
getRunsDir
|
|
@@ -73338,7 +73888,7 @@ async function runsCommand(options = {}) {
|
|
|
73338
73888
|
}
|
|
73339
73889
|
const rows = [];
|
|
73340
73890
|
for (const entry of entries) {
|
|
73341
|
-
const metaPath =
|
|
73891
|
+
const metaPath = join46(runsDir, entry, "meta.json");
|
|
73342
73892
|
let meta3;
|
|
73343
73893
|
try {
|
|
73344
73894
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -73415,7 +73965,7 @@ async function runsCommand(options = {}) {
|
|
|
73415
73965
|
|
|
73416
73966
|
// src/commands/unlock.ts
|
|
73417
73967
|
init_source();
|
|
73418
|
-
import { join as
|
|
73968
|
+
import { join as join47 } from "path";
|
|
73419
73969
|
function isProcessAlive3(pid) {
|
|
73420
73970
|
try {
|
|
73421
73971
|
process.kill(pid, 0);
|
|
@@ -73430,7 +73980,7 @@ function formatLockAge(ageMs) {
|
|
|
73430
73980
|
}
|
|
73431
73981
|
async function unlockCommand(options) {
|
|
73432
73982
|
const workdir = options.dir ?? process.cwd();
|
|
73433
|
-
const lockPath =
|
|
73983
|
+
const lockPath = join47(workdir, "nax.lock");
|
|
73434
73984
|
const lockFile = Bun.file(lockPath);
|
|
73435
73985
|
const exists = await lockFile.exists();
|
|
73436
73986
|
if (!exists) {
|
|
@@ -73637,21 +74187,6 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
|
|
|
73637
74187
|
options.statusWriter.setRunStatus("running");
|
|
73638
74188
|
options.statusWriter.setCurrentStory(null);
|
|
73639
74189
|
await options.statusWriter.update(totalCost, iterations);
|
|
73640
|
-
const reporters = pluginRegistry.getReporters();
|
|
73641
|
-
for (const reporter of reporters) {
|
|
73642
|
-
if (reporter.onRunStart) {
|
|
73643
|
-
try {
|
|
73644
|
-
await reporter.onRunStart({
|
|
73645
|
-
runId: options.runId,
|
|
73646
|
-
feature: options.feature,
|
|
73647
|
-
totalStories: prd.userStories.length,
|
|
73648
|
-
startTime: options.startedAt
|
|
73649
|
-
});
|
|
73650
|
-
} catch (error48) {
|
|
73651
|
-
logger?.warn("plugins", `Reporter '${reporter.name}' onRunStart failed`, { error: error48 });
|
|
73652
|
-
}
|
|
73653
|
-
}
|
|
73654
|
-
}
|
|
73655
74190
|
logger?.info("execution", `Starting ${options.feature}`, {
|
|
73656
74191
|
totalStories: prd.userStories.length,
|
|
73657
74192
|
doneStories: prd.userStories.filter((s) => s.status === "passed").length,
|
|
@@ -73765,6 +74300,7 @@ async function run(options) {
|
|
|
73765
74300
|
let iterations = 0;
|
|
73766
74301
|
let storiesCompleted = 0;
|
|
73767
74302
|
let totalCost = 0;
|
|
74303
|
+
let runCompleted = false;
|
|
73768
74304
|
const allStoryMetrics = [];
|
|
73769
74305
|
const logger = getSafeLogger();
|
|
73770
74306
|
const registry2 = createAgentRegistry(config2);
|
|
@@ -73857,6 +74393,7 @@ async function run(options) {
|
|
|
73857
74393
|
agentGetFn
|
|
73858
74394
|
});
|
|
73859
74395
|
const { durationMs } = completionResult;
|
|
74396
|
+
runCompleted = true;
|
|
73860
74397
|
return {
|
|
73861
74398
|
success: isComplete(prd),
|
|
73862
74399
|
iterations,
|
|
@@ -73892,7 +74429,8 @@ async function run(options) {
|
|
|
73892
74429
|
feature,
|
|
73893
74430
|
prdPath,
|
|
73894
74431
|
branch,
|
|
73895
|
-
version: NAX_VERSION
|
|
74432
|
+
version: NAX_VERSION,
|
|
74433
|
+
runCompleted
|
|
73896
74434
|
});
|
|
73897
74435
|
logger2?.debug("execution", "Runner finally \u2014 cleanupRun done, run() returning");
|
|
73898
74436
|
}
|
|
@@ -81253,15 +81791,15 @@ Next: nax generate --package ${options.package}`));
|
|
|
81253
81791
|
}
|
|
81254
81792
|
return;
|
|
81255
81793
|
}
|
|
81256
|
-
const naxDir =
|
|
81794
|
+
const naxDir = join58(workdir, ".nax");
|
|
81257
81795
|
if (existsSync34(naxDir) && !options.force) {
|
|
81258
81796
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
81259
81797
|
return;
|
|
81260
81798
|
}
|
|
81261
|
-
|
|
81262
|
-
|
|
81263
|
-
await Bun.write(
|
|
81264
|
-
await Bun.write(
|
|
81799
|
+
mkdirSync6(join58(naxDir, "features"), { recursive: true });
|
|
81800
|
+
mkdirSync6(join58(naxDir, "hooks"), { recursive: true });
|
|
81801
|
+
await Bun.write(join58(naxDir, "config.json"), JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
81802
|
+
await Bun.write(join58(naxDir, "hooks.json"), JSON.stringify({
|
|
81265
81803
|
hooks: {
|
|
81266
81804
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
81267
81805
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -81269,12 +81807,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
81269
81807
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
81270
81808
|
}
|
|
81271
81809
|
}, null, 2));
|
|
81272
|
-
await Bun.write(
|
|
81810
|
+
await Bun.write(join58(naxDir, ".gitignore"), `# nax temp files
|
|
81273
81811
|
*.tmp
|
|
81274
81812
|
.paused.json
|
|
81275
81813
|
.nax-verifier-verdict.json
|
|
81276
81814
|
`);
|
|
81277
|
-
await Bun.write(
|
|
81815
|
+
await Bun.write(join58(naxDir, "context.md"), `# Project Context
|
|
81278
81816
|
|
|
81279
81817
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
81280
81818
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -81359,7 +81897,7 @@ Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cu
|
|
|
81359
81897
|
console.log(source_default.dim(`
|
|
81360
81898
|
Next: nax features create <name>`));
|
|
81361
81899
|
});
|
|
81362
|
-
program2.command("run").description("Run the orchestration loop for a feature").requiredOption("-f, --feature <name>", "Feature name").option("-a, --agent <name>", "Force a specific agent").option("-m, --max-iterations <n>", "Max iterations", "20").option("--dry-run", "Show plan without executing", false).option("--no-context", "Disable context builder (skip file context in prompts)").option("--no-batch", "Disable story batching (execute all stories individually)").option("--parallel <n>", "Max parallel sessions (0=auto, omit=sequential)").option("--plan", "Run plan phase first before execution", false).option("--from <spec-path>", "Path to spec file (required when --plan is used)").option("--one-shot", "Skip interactive planning Q&A, use single LLM call (ACP only)", false).option("--force", "Force overwrite existing prd.json when using --plan", false).option("--headless", "Force headless mode (disable TUI, use pipe mode)", false).option("--verbose", "Enable verbose logging (debug level)", false).option("--quiet", "Quiet mode (warnings and errors only)", false).option("--silent", "Silent mode (errors only)", false).option("--json", "JSON mode (raw JSONL output to stdout)", false).option("-d, --dir <path>", "Working directory", process.cwd()).option("--skip-precheck", "Skip precheck validations (advanced users only)", false).action(async (options) => {
|
|
81900
|
+
program2.command("run").description("Run the orchestration loop for a feature").requiredOption("-f, --feature <name>", "Feature name").option("-a, --agent <name>", "Force a specific agent").option("-m, --max-iterations <n>", "Max iterations", "20").option("--dry-run", "Show plan without executing", false).option("--no-context", "Disable context builder (skip file context in prompts)").option("--no-batch", "Disable story batching (execute all stories individually)").option("--parallel <n>", "Max parallel sessions (0=auto, omit=sequential)").option("--plan", "Run plan phase first before execution", false).option("--from <spec-path>", "Path to spec file (required when --plan is used)").option("--one-shot", "Skip interactive planning Q&A, use single LLM call (ACP only)", false).option("--force", "Force overwrite existing prd.json when using --plan", false).option("--headless", "Force headless mode (disable TUI, use pipe mode)", false).option("--verbose", "Enable verbose logging (debug level)", false).option("--quiet", "Quiet mode (warnings and errors only)", false).option("--silent", "Silent mode (errors only)", false).option("--json", "JSON mode (raw JSONL output to stdout)", false).option("-d, --dir <path>", "Working directory", process.cwd()).option("--skip-precheck", "Skip precheck validations (advanced users only)", false).option("--profile <name>", "Profile to use (overrides config.json profile)").action(async (options) => {
|
|
81363
81901
|
let workdir;
|
|
81364
81902
|
try {
|
|
81365
81903
|
workdir = validateDirectory(options.dir);
|
|
@@ -81395,13 +81933,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81395
81933
|
formatterMode = "quiet";
|
|
81396
81934
|
}
|
|
81397
81935
|
const naxDir = findProjectDir(workdir);
|
|
81398
|
-
const
|
|
81936
|
+
const cliOverrides = {};
|
|
81937
|
+
if (options.profile) {
|
|
81938
|
+
cliOverrides.profile = options.profile;
|
|
81939
|
+
}
|
|
81940
|
+
const config2 = await loadConfig(naxDir ?? undefined, cliOverrides);
|
|
81399
81941
|
if (!naxDir) {
|
|
81400
81942
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81401
81943
|
process.exit(1);
|
|
81402
81944
|
}
|
|
81403
|
-
const featureDir =
|
|
81404
|
-
const prdPath =
|
|
81945
|
+
const featureDir = join58(naxDir, "features", options.feature);
|
|
81946
|
+
const prdPath = join58(featureDir, "prd.json");
|
|
81405
81947
|
if (options.plan && options.from) {
|
|
81406
81948
|
if (existsSync34(prdPath) && !options.force) {
|
|
81407
81949
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
@@ -81423,10 +81965,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81423
81965
|
}
|
|
81424
81966
|
}
|
|
81425
81967
|
try {
|
|
81426
|
-
const planLogDir =
|
|
81427
|
-
|
|
81968
|
+
const planLogDir = join58(featureDir, "plan");
|
|
81969
|
+
mkdirSync6(planLogDir, { recursive: true });
|
|
81428
81970
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81429
|
-
const planLogPath =
|
|
81971
|
+
const planLogPath = join58(planLogDir, `${planLogId}.jsonl`);
|
|
81430
81972
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
81431
81973
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
81432
81974
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -81470,10 +82012,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81470
82012
|
process.exit(1);
|
|
81471
82013
|
}
|
|
81472
82014
|
resetLogger();
|
|
81473
|
-
const runsDir =
|
|
81474
|
-
|
|
82015
|
+
const runsDir = join58(featureDir, "runs");
|
|
82016
|
+
mkdirSync6(runsDir, { recursive: true });
|
|
81475
82017
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81476
|
-
const logFilePath =
|
|
82018
|
+
const logFilePath = join58(runsDir, `${runId}.jsonl`);
|
|
81477
82019
|
const isTTY = process.stdout.isTTY ?? false;
|
|
81478
82020
|
const headlessFlag = options.headless ?? false;
|
|
81479
82021
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -81489,7 +82031,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81489
82031
|
config2.autoMode.defaultAgent = options.agent;
|
|
81490
82032
|
}
|
|
81491
82033
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
81492
|
-
const globalNaxDir =
|
|
82034
|
+
const globalNaxDir = join58(homedir8(), ".nax");
|
|
81493
82035
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
81494
82036
|
const eventEmitter = new PipelineEventEmitter;
|
|
81495
82037
|
let tuiInstance;
|
|
@@ -81512,7 +82054,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81512
82054
|
} else {
|
|
81513
82055
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
81514
82056
|
}
|
|
81515
|
-
const statusFilePath =
|
|
82057
|
+
const statusFilePath = join58(workdir, ".nax", "status.json");
|
|
81516
82058
|
let parallel;
|
|
81517
82059
|
if (options.parallel !== undefined) {
|
|
81518
82060
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -81538,7 +82080,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81538
82080
|
headless: useHeadless,
|
|
81539
82081
|
skipPrecheck: options.skipPrecheck ?? false
|
|
81540
82082
|
});
|
|
81541
|
-
const latestSymlink =
|
|
82083
|
+
const latestSymlink = join58(runsDir, "latest.jsonl");
|
|
81542
82084
|
try {
|
|
81543
82085
|
if (existsSync34(latestSymlink)) {
|
|
81544
82086
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
@@ -81576,9 +82118,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
81576
82118
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81577
82119
|
process.exit(1);
|
|
81578
82120
|
}
|
|
81579
|
-
const featureDir =
|
|
81580
|
-
|
|
81581
|
-
await Bun.write(
|
|
82121
|
+
const featureDir = join58(naxDir, "features", name);
|
|
82122
|
+
mkdirSync6(featureDir, { recursive: true });
|
|
82123
|
+
await Bun.write(join58(featureDir, "spec.md"), `# Feature: ${name}
|
|
81582
82124
|
|
|
81583
82125
|
## Overview
|
|
81584
82126
|
|
|
@@ -81611,7 +82153,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
81611
82153
|
|
|
81612
82154
|
<!-- What this feature explicitly does NOT cover. -->
|
|
81613
82155
|
`);
|
|
81614
|
-
await Bun.write(
|
|
82156
|
+
await Bun.write(join58(featureDir, "progress.txt"), `# Progress: ${name}
|
|
81615
82157
|
|
|
81616
82158
|
Created: ${new Date().toISOString()}
|
|
81617
82159
|
|
|
@@ -81637,13 +82179,13 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
81637
82179
|
console.error(source_default.red("nax not initialized."));
|
|
81638
82180
|
process.exit(1);
|
|
81639
82181
|
}
|
|
81640
|
-
const featuresDir =
|
|
82182
|
+
const featuresDir = join58(naxDir, "features");
|
|
81641
82183
|
if (!existsSync34(featuresDir)) {
|
|
81642
82184
|
console.log(source_default.dim("No features yet."));
|
|
81643
82185
|
return;
|
|
81644
82186
|
}
|
|
81645
|
-
const { readdirSync:
|
|
81646
|
-
const entries =
|
|
82187
|
+
const { readdirSync: readdirSync10 } = await import("fs");
|
|
82188
|
+
const entries = readdirSync10(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
81647
82189
|
if (entries.length === 0) {
|
|
81648
82190
|
console.log(source_default.dim("No features yet."));
|
|
81649
82191
|
return;
|
|
@@ -81652,7 +82194,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
81652
82194
|
Features:
|
|
81653
82195
|
`));
|
|
81654
82196
|
for (const name of entries) {
|
|
81655
|
-
const prdPath =
|
|
82197
|
+
const prdPath = join58(featuresDir, name, "prd.json");
|
|
81656
82198
|
if (existsSync34(prdPath)) {
|
|
81657
82199
|
const prd = await loadPRD(prdPath);
|
|
81658
82200
|
const c = countStories(prd);
|
|
@@ -81663,7 +82205,7 @@ Features:
|
|
|
81663
82205
|
}
|
|
81664
82206
|
console.log();
|
|
81665
82207
|
});
|
|
81666
|
-
program2.command("plan [description]").description("Generate prd.json from a spec file via LLM one-shot call (replaces deprecated 'nax analyze')").option("--from <spec-path>", "Path to spec file (required unless --decompose is used)").requiredOption("-f, --feature <name>", "Feature name (required)").option("--auto", "Run in one-shot LLM mode (alias: --one-shot)", false).option("--one-shot", "Run in one-shot LLM mode (alias: --auto)", false).option("-b, --branch <branch>", "Override default branch name").option("-d, --dir <path>", "Project directory", process.cwd()).option("--decompose <storyId>", "Decompose an existing story into sub-stories").action(async (description, options) => {
|
|
82208
|
+
program2.command("plan [description]").description("Generate prd.json from a spec file via LLM one-shot call (replaces deprecated 'nax analyze')").option("--from <spec-path>", "Path to spec file (required unless --decompose is used)").requiredOption("-f, --feature <name>", "Feature name (required)").option("--auto", "Run in one-shot LLM mode (alias: --one-shot)", false).option("--one-shot", "Run in one-shot LLM mode (alias: --auto)", false).option("-b, --branch <branch>", "Override default branch name").option("-d, --dir <path>", "Project directory", process.cwd()).option("--decompose <storyId>", "Decompose an existing story into sub-stories").option("--profile <name>", "Profile to use (overrides config.json profile)").action(async (description, options) => {
|
|
81667
82209
|
if (description) {
|
|
81668
82210
|
console.error(source_default.red(`Error: Positional args removed in plan v2.
|
|
81669
82211
|
|
|
@@ -81682,11 +82224,15 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
81682
82224
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81683
82225
|
process.exit(1);
|
|
81684
82226
|
}
|
|
81685
|
-
const
|
|
81686
|
-
|
|
81687
|
-
|
|
82227
|
+
const cliOverrides = {};
|
|
82228
|
+
if (options.profile) {
|
|
82229
|
+
cliOverrides.profile = options.profile;
|
|
82230
|
+
}
|
|
82231
|
+
const config2 = await loadConfig(workdir, cliOverrides);
|
|
82232
|
+
const featureLogDir = join58(naxDir, "features", options.feature, "plan");
|
|
82233
|
+
mkdirSync6(featureLogDir, { recursive: true });
|
|
81688
82234
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81689
|
-
const planLogPath =
|
|
82235
|
+
const planLogPath = join58(featureLogDir, `${planLogId}.jsonl`);
|
|
81690
82236
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
81691
82237
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
81692
82238
|
try {
|
|
@@ -81737,7 +82283,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
81737
82283
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81738
82284
|
process.exit(1);
|
|
81739
82285
|
}
|
|
81740
|
-
const featureDir =
|
|
82286
|
+
const featureDir = join58(naxDir, "features", options.feature);
|
|
81741
82287
|
if (!existsSync34(featureDir)) {
|
|
81742
82288
|
console.error(source_default.red(`Feature "${options.feature}" not found.`));
|
|
81743
82289
|
process.exit(1);
|
|
@@ -81753,7 +82299,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
81753
82299
|
specPath: options.from,
|
|
81754
82300
|
reclassify: options.reclassify
|
|
81755
82301
|
});
|
|
81756
|
-
const prdPath =
|
|
82302
|
+
const prdPath = join58(featureDir, "prd.json");
|
|
81757
82303
|
await Bun.write(prdPath, JSON.stringify(prd, null, 2));
|
|
81758
82304
|
const c = countStories(prd);
|
|
81759
82305
|
console.log(source_default.green(`
|
|
@@ -81786,7 +82332,7 @@ program2.command("agents").description("List available coding agents with status
|
|
|
81786
82332
|
process.exit(1);
|
|
81787
82333
|
}
|
|
81788
82334
|
});
|
|
81789
|
-
program2.command("config").description("Display effective merged configuration").option("-d, --dir <path>", "Project directory", process.cwd()).option("--explain", "Show detailed field descriptions", false).option("--diff", "Show only fields where project overrides global", false).action(async (options) => {
|
|
82335
|
+
var configCmd = program2.command("config").description("Display effective merged configuration").option("-d, --dir <path>", "Project directory", process.cwd()).option("--explain", "Show detailed field descriptions", false).option("--diff", "Show only fields where project overrides global", false).action(async (options) => {
|
|
81790
82336
|
let workdir;
|
|
81791
82337
|
try {
|
|
81792
82338
|
workdir = validateDirectory(options.dir);
|
|
@@ -81803,6 +82349,52 @@ program2.command("config").description("Display effective merged configuration")
|
|
|
81803
82349
|
process.exit(1);
|
|
81804
82350
|
}
|
|
81805
82351
|
});
|
|
82352
|
+
var configProfileCmd = configCmd.command("profile").description("Manage config profiles");
|
|
82353
|
+
configProfileCmd.command("list").description("List all available profiles grouped by scope").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (options) => {
|
|
82354
|
+
try {
|
|
82355
|
+
const output = await profileListCommand(options.dir);
|
|
82356
|
+
console.log(output);
|
|
82357
|
+
} catch (err) {
|
|
82358
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82359
|
+
process.exit(1);
|
|
82360
|
+
}
|
|
82361
|
+
});
|
|
82362
|
+
configProfileCmd.command("show <name>").description("Show resolved profile JSON").option("-d, --dir <path>", "Project directory", process.cwd()).option("--unmask", "Show raw values including secrets", false).action(async (name, options) => {
|
|
82363
|
+
try {
|
|
82364
|
+
const output = await profileShowCommand(name, options.dir, { unmask: options.unmask });
|
|
82365
|
+
console.log(output);
|
|
82366
|
+
} catch (err) {
|
|
82367
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82368
|
+
process.exit(1);
|
|
82369
|
+
}
|
|
82370
|
+
});
|
|
82371
|
+
configProfileCmd.command("use <name>").description("Set the active profile (use 'default' to clear)").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (name, options) => {
|
|
82372
|
+
try {
|
|
82373
|
+
const msg = await profileUseCommand(name, options.dir);
|
|
82374
|
+
console.log(msg);
|
|
82375
|
+
} catch (err) {
|
|
82376
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82377
|
+
process.exit(1);
|
|
82378
|
+
}
|
|
82379
|
+
});
|
|
82380
|
+
configProfileCmd.command("current").description("Show the currently active profile name").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (options) => {
|
|
82381
|
+
try {
|
|
82382
|
+
const name = await profileCurrentCommand(options.dir);
|
|
82383
|
+
console.log(name);
|
|
82384
|
+
} catch (err) {
|
|
82385
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82386
|
+
process.exit(1);
|
|
82387
|
+
}
|
|
82388
|
+
});
|
|
82389
|
+
configProfileCmd.command("create <name>").description("Create a new empty profile").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (name, options) => {
|
|
82390
|
+
try {
|
|
82391
|
+
const path19 = await profileCreateCommand(name, options.dir);
|
|
82392
|
+
console.log(`Created profile at: ${path19}`);
|
|
82393
|
+
} catch (err) {
|
|
82394
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82395
|
+
process.exit(1);
|
|
82396
|
+
}
|
|
82397
|
+
});
|
|
81806
82398
|
program2.command("status").description("Show current run status").option("-f, --feature <name>", "Feature name").option("-d, --dir <path>", "Project directory", process.cwd()).option("--cost", "Show cost metrics across all runs", false).option("--last", "Show last run metrics (requires --cost)", false).option("--model", "Show per-model efficiency (requires --cost)", false).action(async (options) => {
|
|
81807
82399
|
let workdir;
|
|
81808
82400
|
try {
|