@nathapp/nax 0.57.1-canary.1 → 0.57.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/nax.js +1475 -878
- 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);
|
|
20918
|
+
if (profileName !== "default") {
|
|
20919
|
+
const profileData = await loadProfile(profileName, projectRoot);
|
|
20920
|
+
rawConfig = deepMergeConfig(rawConfig, profileData);
|
|
20921
|
+
await loadProfileEnv(profileName, projectRoot);
|
|
20922
|
+
}
|
|
20752
20923
|
const globalConfRaw = await loadJsonFile(globalConfigPath(), "config");
|
|
20753
20924
|
if (globalConfRaw) {
|
|
20754
|
-
const
|
|
20925
|
+
const { profile: _gProfile, ...globalConfStripped } = globalConfRaw;
|
|
20926
|
+
const globalConf = applyBatchModeCompat(applyRemovedStrategyCompat(globalConfStripped));
|
|
20755
20927
|
rawConfig = deepMergeConfig(rawConfig, globalConf);
|
|
20756
20928
|
}
|
|
20757
|
-
const projDir = startDir ? basename(startDir) === PROJECT_NAX_DIR ? startDir : findProjectDir(startDir) : findProjectDir();
|
|
20758
20929
|
if (projDir) {
|
|
20759
|
-
const projConf = await loadJsonFile(
|
|
20930
|
+
const projConf = await loadJsonFile(join5(projDir, "config.json"), "config");
|
|
20760
20931
|
if (projConf) {
|
|
20761
|
-
const
|
|
20932
|
+
const { profile: _pProfile, ...projConfStripped } = projConf;
|
|
20933
|
+
const resolvedProjConf = applyBatchModeCompat(applyRemovedStrategyCompat(projConfStripped));
|
|
20762
20934
|
rawConfig = deepMergeConfig(rawConfig, resolvedProjConf);
|
|
20763
20935
|
}
|
|
20764
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.1
|
|
22345
|
+
version: "0.57.1",
|
|
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("39861723"))
|
|
22425
|
+
return "39861723";
|
|
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,
|
|
@@ -35237,12 +35648,12 @@ var init_headless_formatter = __esm(() => {
|
|
|
35237
35648
|
// src/pipeline/subscribers/events-writer.ts
|
|
35238
35649
|
import { appendFile as appendFile3, mkdir as mkdir3 } from "fs/promises";
|
|
35239
35650
|
import { homedir as homedir5 } from "os";
|
|
35240
|
-
import { basename as basename6, join as
|
|
35651
|
+
import { basename as basename6, join as join50 } from "path";
|
|
35241
35652
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
35242
35653
|
const logger = getSafeLogger();
|
|
35243
35654
|
const project = basename6(workdir);
|
|
35244
|
-
const eventsDir =
|
|
35245
|
-
const eventsFile =
|
|
35655
|
+
const eventsDir = join50(homedir5(), ".nax", "events", project);
|
|
35656
|
+
const eventsFile = join50(eventsDir, "events.jsonl");
|
|
35246
35657
|
let dirReady = false;
|
|
35247
35658
|
const write = (line) => {
|
|
35248
35659
|
return (async () => {
|
|
@@ -35423,12 +35834,12 @@ var init_interaction2 = __esm(() => {
|
|
|
35423
35834
|
// src/pipeline/subscribers/registry.ts
|
|
35424
35835
|
import { mkdir as mkdir4, writeFile } from "fs/promises";
|
|
35425
35836
|
import { homedir as homedir6 } from "os";
|
|
35426
|
-
import { basename as basename7, join as
|
|
35837
|
+
import { basename as basename7, join as join51 } from "path";
|
|
35427
35838
|
function wireRegistry(bus, feature, runId, workdir) {
|
|
35428
35839
|
const logger = getSafeLogger();
|
|
35429
35840
|
const project = basename7(workdir);
|
|
35430
|
-
const runDir =
|
|
35431
|
-
const metaFile =
|
|
35841
|
+
const runDir = join51(homedir6(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
35842
|
+
const metaFile = join51(runDir, "meta.json");
|
|
35432
35843
|
const unsub = bus.on("run:started", (_ev) => {
|
|
35433
35844
|
return (async () => {
|
|
35434
35845
|
try {
|
|
@@ -35438,8 +35849,8 @@ function wireRegistry(bus, feature, runId, workdir) {
|
|
|
35438
35849
|
project,
|
|
35439
35850
|
feature,
|
|
35440
35851
|
workdir,
|
|
35441
|
-
statusPath:
|
|
35442
|
-
eventsDir:
|
|
35852
|
+
statusPath: join51(workdir, ".nax", "features", feature, "status.json"),
|
|
35853
|
+
eventsDir: join51(workdir, ".nax", "features", feature, "runs"),
|
|
35443
35854
|
registeredAt: new Date().toISOString()
|
|
35444
35855
|
};
|
|
35445
35856
|
await writeFile(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -36091,7 +36502,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
36091
36502
|
});
|
|
36092
36503
|
|
|
36093
36504
|
// src/execution/iteration-runner.ts
|
|
36094
|
-
import { join as
|
|
36505
|
+
import { join as join52 } from "path";
|
|
36095
36506
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
36096
36507
|
const logger = getSafeLogger();
|
|
36097
36508
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
@@ -36126,7 +36537,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
36126
36537
|
}
|
|
36127
36538
|
}
|
|
36128
36539
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
36129
|
-
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(
|
|
36540
|
+
const effectiveConfig = story.workdir ? await _iterationRunnerDeps.loadConfigForWorkdir(join52(ctx.workdir, ".nax", "config.json"), story.workdir) : ctx.config;
|
|
36130
36541
|
const pipelineContext = {
|
|
36131
36542
|
config: ctx.config,
|
|
36132
36543
|
effectiveConfig,
|
|
@@ -36385,13 +36796,13 @@ __export(exports_manager, {
|
|
|
36385
36796
|
});
|
|
36386
36797
|
import { existsSync as existsSync32, symlinkSync } from "fs";
|
|
36387
36798
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
36388
|
-
import { join as
|
|
36799
|
+
import { join as join53 } from "path";
|
|
36389
36800
|
|
|
36390
36801
|
class WorktreeManager {
|
|
36391
36802
|
async ensureGitExcludes(projectRoot) {
|
|
36392
36803
|
const logger = getSafeLogger();
|
|
36393
|
-
const infoDir =
|
|
36394
|
-
const excludePath =
|
|
36804
|
+
const infoDir = join53(projectRoot, ".git", "info");
|
|
36805
|
+
const excludePath = join53(infoDir, "exclude");
|
|
36395
36806
|
try {
|
|
36396
36807
|
await mkdir5(infoDir, { recursive: true });
|
|
36397
36808
|
let existing = "";
|
|
@@ -36418,7 +36829,7 @@ ${missing.join(`
|
|
|
36418
36829
|
}
|
|
36419
36830
|
async create(projectRoot, storyId) {
|
|
36420
36831
|
validateStoryId(storyId);
|
|
36421
|
-
const worktreePath =
|
|
36832
|
+
const worktreePath = join53(projectRoot, ".nax-wt", storyId);
|
|
36422
36833
|
const branchName = `nax/${storyId}`;
|
|
36423
36834
|
try {
|
|
36424
36835
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -36459,9 +36870,9 @@ ${missing.join(`
|
|
|
36459
36870
|
}
|
|
36460
36871
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
36461
36872
|
}
|
|
36462
|
-
const nodeModulesSource =
|
|
36873
|
+
const nodeModulesSource = join53(projectRoot, "node_modules");
|
|
36463
36874
|
if (existsSync32(nodeModulesSource)) {
|
|
36464
|
-
const nodeModulesTarget =
|
|
36875
|
+
const nodeModulesTarget = join53(worktreePath, "node_modules");
|
|
36465
36876
|
try {
|
|
36466
36877
|
symlinkSync(nodeModulesSource, nodeModulesTarget, "dir");
|
|
36467
36878
|
} catch (error48) {
|
|
@@ -36469,9 +36880,9 @@ ${missing.join(`
|
|
|
36469
36880
|
throw new Error(`Failed to symlink node_modules: ${errorMessage(error48)}`);
|
|
36470
36881
|
}
|
|
36471
36882
|
}
|
|
36472
|
-
const envSource =
|
|
36883
|
+
const envSource = join53(projectRoot, ".env");
|
|
36473
36884
|
if (existsSync32(envSource)) {
|
|
36474
|
-
const envTarget =
|
|
36885
|
+
const envTarget = join53(worktreePath, ".env");
|
|
36475
36886
|
try {
|
|
36476
36887
|
symlinkSync(envSource, envTarget, "file");
|
|
36477
36888
|
} catch (error48) {
|
|
@@ -36482,7 +36893,7 @@ ${missing.join(`
|
|
|
36482
36893
|
}
|
|
36483
36894
|
async remove(projectRoot, storyId) {
|
|
36484
36895
|
validateStoryId(storyId);
|
|
36485
|
-
const worktreePath =
|
|
36896
|
+
const worktreePath = join53(projectRoot, ".nax-wt", storyId);
|
|
36486
36897
|
const branchName = `nax/${storyId}`;
|
|
36487
36898
|
try {
|
|
36488
36899
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -37401,16 +37812,16 @@ var init_unified_executor = __esm(() => {
|
|
|
37401
37812
|
});
|
|
37402
37813
|
|
|
37403
37814
|
// src/project/detector.ts
|
|
37404
|
-
import { join as
|
|
37815
|
+
import { join as join54 } from "path";
|
|
37405
37816
|
async function detectLanguage(workdir, pkg) {
|
|
37406
37817
|
const deps = _detectorDeps;
|
|
37407
|
-
if (await deps.fileExists(
|
|
37818
|
+
if (await deps.fileExists(join54(workdir, "go.mod")))
|
|
37408
37819
|
return "go";
|
|
37409
|
-
if (await deps.fileExists(
|
|
37820
|
+
if (await deps.fileExists(join54(workdir, "Cargo.toml")))
|
|
37410
37821
|
return "rust";
|
|
37411
|
-
if (await deps.fileExists(
|
|
37822
|
+
if (await deps.fileExists(join54(workdir, "pyproject.toml")))
|
|
37412
37823
|
return "python";
|
|
37413
|
-
if (await deps.fileExists(
|
|
37824
|
+
if (await deps.fileExists(join54(workdir, "requirements.txt")))
|
|
37414
37825
|
return "python";
|
|
37415
37826
|
if (pkg != null) {
|
|
37416
37827
|
const allDeps = {
|
|
@@ -37470,18 +37881,18 @@ async function detectLintTool(workdir, language) {
|
|
|
37470
37881
|
if (language === "python")
|
|
37471
37882
|
return "ruff";
|
|
37472
37883
|
const deps = _detectorDeps;
|
|
37473
|
-
if (await deps.fileExists(
|
|
37884
|
+
if (await deps.fileExists(join54(workdir, "biome.json")))
|
|
37474
37885
|
return "biome";
|
|
37475
|
-
if (await deps.fileExists(
|
|
37886
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc")))
|
|
37476
37887
|
return "eslint";
|
|
37477
|
-
if (await deps.fileExists(
|
|
37888
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc.js")))
|
|
37478
37889
|
return "eslint";
|
|
37479
|
-
if (await deps.fileExists(
|
|
37890
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc.json")))
|
|
37480
37891
|
return "eslint";
|
|
37481
37892
|
return;
|
|
37482
37893
|
}
|
|
37483
37894
|
async function detectProjectProfile(workdir, existing) {
|
|
37484
|
-
const pkg = await _detectorDeps.readJson(
|
|
37895
|
+
const pkg = await _detectorDeps.readJson(join54(workdir, "package.json"));
|
|
37485
37896
|
const language = existing.language !== undefined ? existing.language : await detectLanguage(workdir, pkg);
|
|
37486
37897
|
const type = existing.type !== undefined ? existing.type : detectType(pkg);
|
|
37487
37898
|
const testFramework = existing.testFramework !== undefined ? existing.testFramework : await detectTestFramework(workdir, language, pkg);
|
|
@@ -37574,7 +37985,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
37574
37985
|
var init_status_file = () => {};
|
|
37575
37986
|
|
|
37576
37987
|
// src/execution/status-writer.ts
|
|
37577
|
-
import { join as
|
|
37988
|
+
import { join as join55 } from "path";
|
|
37578
37989
|
|
|
37579
37990
|
class StatusWriter {
|
|
37580
37991
|
statusFile;
|
|
@@ -37648,7 +38059,7 @@ class StatusWriter {
|
|
|
37648
38059
|
if (!this._prd)
|
|
37649
38060
|
return;
|
|
37650
38061
|
const safeLogger = getSafeLogger();
|
|
37651
|
-
const featureStatusPath =
|
|
38062
|
+
const featureStatusPath = join55(featureDir, "status.json");
|
|
37652
38063
|
const write = async () => {
|
|
37653
38064
|
try {
|
|
37654
38065
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -37764,7 +38175,7 @@ var exports_precheck_runner = {};
|
|
|
37764
38175
|
__export(exports_precheck_runner, {
|
|
37765
38176
|
runPrecheckValidation: () => runPrecheckValidation
|
|
37766
38177
|
});
|
|
37767
|
-
import { mkdirSync as
|
|
38178
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
37768
38179
|
import path17 from "path";
|
|
37769
38180
|
async function runPrecheckValidation(ctx) {
|
|
37770
38181
|
const logger = getSafeLogger();
|
|
@@ -37779,7 +38190,7 @@ async function runPrecheckValidation(ctx) {
|
|
|
37779
38190
|
format: "human"
|
|
37780
38191
|
});
|
|
37781
38192
|
if (ctx.logFilePath) {
|
|
37782
|
-
|
|
38193
|
+
mkdirSync5(path17.dirname(ctx.logFilePath), { recursive: true });
|
|
37783
38194
|
const precheckLog = {
|
|
37784
38195
|
type: "precheck",
|
|
37785
38196
|
timestamp: new Date().toISOString(),
|
|
@@ -37859,7 +38270,7 @@ __export(exports_run_initialization, {
|
|
|
37859
38270
|
initializeRun: () => initializeRun,
|
|
37860
38271
|
_reconcileDeps: () => _reconcileDeps
|
|
37861
38272
|
});
|
|
37862
|
-
import { join as
|
|
38273
|
+
import { join as join56 } from "path";
|
|
37863
38274
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
37864
38275
|
const logger = getSafeLogger();
|
|
37865
38276
|
let reconciledCount = 0;
|
|
@@ -37877,7 +38288,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
37877
38288
|
});
|
|
37878
38289
|
continue;
|
|
37879
38290
|
}
|
|
37880
|
-
const effectiveWorkdir = story.workdir ?
|
|
38291
|
+
const effectiveWorkdir = story.workdir ? join56(workdir, story.workdir) : workdir;
|
|
37881
38292
|
try {
|
|
37882
38293
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
37883
38294
|
if (!reviewResult.success) {
|
|
@@ -69085,9 +69496,9 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
69085
69496
|
|
|
69086
69497
|
// bin/nax.ts
|
|
69087
69498
|
init_source();
|
|
69088
|
-
import { existsSync as existsSync34, mkdirSync as
|
|
69499
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync6 } from "fs";
|
|
69089
69500
|
import { homedir as homedir8 } from "os";
|
|
69090
|
-
import { join as
|
|
69501
|
+
import { join as join58 } from "path";
|
|
69091
69502
|
|
|
69092
69503
|
// node_modules/commander/esm.mjs
|
|
69093
69504
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -69109,14 +69520,14 @@ var {
|
|
|
69109
69520
|
init_acceptance();
|
|
69110
69521
|
init_registry();
|
|
69111
69522
|
import { existsSync as existsSync8 } from "fs";
|
|
69112
|
-
import { join as
|
|
69523
|
+
import { join as join9 } from "path";
|
|
69113
69524
|
|
|
69114
69525
|
// src/analyze/scanner.ts
|
|
69115
|
-
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
69116
|
-
import { join as
|
|
69526
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
69527
|
+
import { join as join7 } from "path";
|
|
69117
69528
|
async function scanCodebase(workdir) {
|
|
69118
|
-
const srcPath =
|
|
69119
|
-
const packageJsonPath =
|
|
69529
|
+
const srcPath = join7(workdir, "src");
|
|
69530
|
+
const packageJsonPath = join7(workdir, "package.json");
|
|
69120
69531
|
const fileTree = existsSync5(srcPath) ? await generateFileTree(srcPath, 3) : "No src/ directory";
|
|
69121
69532
|
let dependencies = {};
|
|
69122
69533
|
let devDependencies = {};
|
|
@@ -69141,7 +69552,7 @@ async function generateFileTree(dir, maxDepth, currentDepth = 0, prefix = "") {
|
|
|
69141
69552
|
}
|
|
69142
69553
|
const entries = [];
|
|
69143
69554
|
try {
|
|
69144
|
-
const dirEntries =
|
|
69555
|
+
const dirEntries = readdirSync2(dir, { withFileTypes: true });
|
|
69145
69556
|
dirEntries.sort((a, b) => {
|
|
69146
69557
|
if (a.isDirectory() && !b.isDirectory())
|
|
69147
69558
|
return -1;
|
|
@@ -69157,7 +69568,7 @@ async function generateFileTree(dir, maxDepth, currentDepth = 0, prefix = "") {
|
|
|
69157
69568
|
const isDir = dirent.isDirectory();
|
|
69158
69569
|
entries.push(`${prefix}${connector}${dirent.name}${isDir ? "/" : ""}`);
|
|
69159
69570
|
if (isDir) {
|
|
69160
|
-
const subtree = await generateFileTree(
|
|
69571
|
+
const subtree = await generateFileTree(join7(dir, dirent.name), maxDepth, currentDepth + 1, prefix + childPrefix);
|
|
69161
69572
|
if (subtree) {
|
|
69162
69573
|
entries.push(subtree);
|
|
69163
69574
|
}
|
|
@@ -69181,16 +69592,16 @@ function detectTestPatterns(workdir, dependencies, devDependencies) {
|
|
|
69181
69592
|
} else {
|
|
69182
69593
|
patterns.push("Test framework: likely bun:test (no framework dependency)");
|
|
69183
69594
|
}
|
|
69184
|
-
if (existsSync5(
|
|
69595
|
+
if (existsSync5(join7(workdir, "test"))) {
|
|
69185
69596
|
patterns.push("Test directory: test/");
|
|
69186
69597
|
}
|
|
69187
|
-
if (existsSync5(
|
|
69598
|
+
if (existsSync5(join7(workdir, "__tests__"))) {
|
|
69188
69599
|
patterns.push("Test directory: __tests__/");
|
|
69189
69600
|
}
|
|
69190
|
-
if (existsSync5(
|
|
69601
|
+
if (existsSync5(join7(workdir, "tests"))) {
|
|
69191
69602
|
patterns.push("Test directory: tests/");
|
|
69192
69603
|
}
|
|
69193
|
-
const hasTestFiles = existsSync5(
|
|
69604
|
+
const hasTestFiles = existsSync5(join7(workdir, "test")) || existsSync5(join7(workdir, "src"));
|
|
69194
69605
|
if (hasTestFiles) {
|
|
69195
69606
|
patterns.push("Test files: *.test.ts, *.spec.ts");
|
|
69196
69607
|
}
|
|
@@ -69206,7 +69617,7 @@ init_version();
|
|
|
69206
69617
|
// src/cli/analyze-parser.ts
|
|
69207
69618
|
init_registry();
|
|
69208
69619
|
import { existsSync as existsSync7 } from "fs";
|
|
69209
|
-
import { join as
|
|
69620
|
+
import { join as join8 } from "path";
|
|
69210
69621
|
init_config();
|
|
69211
69622
|
init_logger2();
|
|
69212
69623
|
init_prd();
|
|
@@ -69336,7 +69747,7 @@ function estimateLOCFromComplexity(complexity) {
|
|
|
69336
69747
|
}
|
|
69337
69748
|
}
|
|
69338
69749
|
async function reclassifyExistingPRD(featureDir, featureName, branchName, workdir, config2) {
|
|
69339
|
-
const prdPath =
|
|
69750
|
+
const prdPath = join8(featureDir, "prd.json");
|
|
69340
69751
|
if (!existsSync7(prdPath)) {
|
|
69341
69752
|
throw new Error(`prd.json not found at ${prdPath}. Run analyze without --reclassify first.`);
|
|
69342
69753
|
}
|
|
@@ -69437,11 +69848,11 @@ function reclassifyWithKeywords(story, config2) {
|
|
|
69437
69848
|
// src/cli/analyze.ts
|
|
69438
69849
|
async function analyzeFeature(options) {
|
|
69439
69850
|
const { featureDir, featureName, branchName, config: config2, specPath: explicitSpecPath, reclassify = false } = options;
|
|
69440
|
-
const workdir =
|
|
69851
|
+
const workdir = join9(featureDir, "../..");
|
|
69441
69852
|
if (reclassify) {
|
|
69442
69853
|
return await reclassifyExistingPRD(featureDir, featureName, branchName, workdir, config2);
|
|
69443
69854
|
}
|
|
69444
|
-
const specPath = explicitSpecPath ||
|
|
69855
|
+
const specPath = explicitSpecPath || join9(featureDir, "spec.md");
|
|
69445
69856
|
if (!existsSync8(specPath))
|
|
69446
69857
|
throw new Error(`spec.md not found at ${specPath}`);
|
|
69447
69858
|
const specContent = await Bun.file(specPath).text();
|
|
@@ -69558,7 +69969,7 @@ async function generateAcceptanceTestsForFeature(specContent, featureName, featu
|
|
|
69558
69969
|
modelDef,
|
|
69559
69970
|
config: config2
|
|
69560
69971
|
});
|
|
69561
|
-
const acceptanceTestPath =
|
|
69972
|
+
const acceptanceTestPath = join9(featureDir, config2.acceptance.testPath);
|
|
69562
69973
|
await Bun.write(acceptanceTestPath, result.testCode);
|
|
69563
69974
|
logger.info("cli", "[OK] Acceptance tests generated", {
|
|
69564
69975
|
criteriaCount: result.criteria.length,
|
|
@@ -69571,18 +69982,18 @@ async function generateAcceptanceTestsForFeature(specContent, featureName, featu
|
|
|
69571
69982
|
// src/cli/plan.ts
|
|
69572
69983
|
init_registry();
|
|
69573
69984
|
import { existsSync as existsSync15 } from "fs";
|
|
69574
|
-
import { join as
|
|
69985
|
+
import { join as join13 } from "path";
|
|
69575
69986
|
import { createInterface as createInterface2 } from "readline";
|
|
69576
69987
|
init_test_strategy();
|
|
69577
69988
|
|
|
69578
69989
|
// src/context/generator.ts
|
|
69579
69990
|
init_path_security();
|
|
69580
69991
|
import { existsSync as existsSync10, readFileSync } from "fs";
|
|
69581
|
-
import { join as
|
|
69992
|
+
import { join as join11, relative } from "path";
|
|
69582
69993
|
|
|
69583
69994
|
// src/context/injector.ts
|
|
69584
69995
|
import { existsSync as existsSync9 } from "fs";
|
|
69585
|
-
import { join as
|
|
69996
|
+
import { join as join10 } from "path";
|
|
69586
69997
|
var NOTABLE_NODE_DEPS = [
|
|
69587
69998
|
"@nestjs",
|
|
69588
69999
|
"express",
|
|
@@ -69612,7 +70023,7 @@ var NOTABLE_NODE_DEPS = [
|
|
|
69612
70023
|
"ioredis"
|
|
69613
70024
|
];
|
|
69614
70025
|
async function detectNode(workdir) {
|
|
69615
|
-
const pkgPath =
|
|
70026
|
+
const pkgPath = join10(workdir, "package.json");
|
|
69616
70027
|
if (!existsSync9(pkgPath))
|
|
69617
70028
|
return null;
|
|
69618
70029
|
try {
|
|
@@ -69629,7 +70040,7 @@ async function detectNode(workdir) {
|
|
|
69629
70040
|
}
|
|
69630
70041
|
}
|
|
69631
70042
|
async function detectGo(workdir) {
|
|
69632
|
-
const goMod =
|
|
70043
|
+
const goMod = join10(workdir, "go.mod");
|
|
69633
70044
|
if (!existsSync9(goMod))
|
|
69634
70045
|
return null;
|
|
69635
70046
|
try {
|
|
@@ -69653,7 +70064,7 @@ async function detectGo(workdir) {
|
|
|
69653
70064
|
}
|
|
69654
70065
|
}
|
|
69655
70066
|
async function detectRust(workdir) {
|
|
69656
|
-
const cargoPath =
|
|
70067
|
+
const cargoPath = join10(workdir, "Cargo.toml");
|
|
69657
70068
|
if (!existsSync9(cargoPath))
|
|
69658
70069
|
return null;
|
|
69659
70070
|
try {
|
|
@@ -69669,8 +70080,8 @@ async function detectRust(workdir) {
|
|
|
69669
70080
|
}
|
|
69670
70081
|
}
|
|
69671
70082
|
async function detectPython(workdir) {
|
|
69672
|
-
const pyproject =
|
|
69673
|
-
const requirements =
|
|
70083
|
+
const pyproject = join10(workdir, "pyproject.toml");
|
|
70084
|
+
const requirements = join10(workdir, "requirements.txt");
|
|
69674
70085
|
if (!existsSync9(pyproject) && !existsSync9(requirements))
|
|
69675
70086
|
return null;
|
|
69676
70087
|
try {
|
|
@@ -69689,7 +70100,7 @@ async function detectPython(workdir) {
|
|
|
69689
70100
|
}
|
|
69690
70101
|
}
|
|
69691
70102
|
async function detectPhp(workdir) {
|
|
69692
|
-
const composerPath =
|
|
70103
|
+
const composerPath = join10(workdir, "composer.json");
|
|
69693
70104
|
if (!existsSync9(composerPath))
|
|
69694
70105
|
return null;
|
|
69695
70106
|
try {
|
|
@@ -69702,7 +70113,7 @@ async function detectPhp(workdir) {
|
|
|
69702
70113
|
}
|
|
69703
70114
|
}
|
|
69704
70115
|
async function detectRuby(workdir) {
|
|
69705
|
-
const gemfile =
|
|
70116
|
+
const gemfile = join10(workdir, "Gemfile");
|
|
69706
70117
|
if (!existsSync9(gemfile))
|
|
69707
70118
|
return null;
|
|
69708
70119
|
try {
|
|
@@ -69714,9 +70125,9 @@ async function detectRuby(workdir) {
|
|
|
69714
70125
|
}
|
|
69715
70126
|
}
|
|
69716
70127
|
async function detectJvm(workdir) {
|
|
69717
|
-
const pom =
|
|
69718
|
-
const gradle =
|
|
69719
|
-
const gradleKts =
|
|
70128
|
+
const pom = join10(workdir, "pom.xml");
|
|
70129
|
+
const gradle = join10(workdir, "build.gradle");
|
|
70130
|
+
const gradleKts = join10(workdir, "build.gradle.kts");
|
|
69720
70131
|
if (!existsSync9(pom) && !existsSync9(gradle) && !existsSync9(gradleKts))
|
|
69721
70132
|
return null;
|
|
69722
70133
|
try {
|
|
@@ -69724,7 +70135,7 @@ async function detectJvm(workdir) {
|
|
|
69724
70135
|
const content2 = await Bun.file(pom).text();
|
|
69725
70136
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
69726
70137
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
69727
|
-
const lang2 = existsSync9(
|
|
70138
|
+
const lang2 = existsSync9(join10(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
69728
70139
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
69729
70140
|
}
|
|
69730
70141
|
const gradleFile = existsSync9(gradleKts) ? gradleKts : gradle;
|
|
@@ -69951,7 +70362,7 @@ async function generateFor(agent, options, config2) {
|
|
|
69951
70362
|
try {
|
|
69952
70363
|
const context = await loadContextContent(options, config2);
|
|
69953
70364
|
const content = generator.generate(context);
|
|
69954
|
-
const outputPath =
|
|
70365
|
+
const outputPath = join11(options.outputDir, generator.outputFile);
|
|
69955
70366
|
validateFilePath(outputPath, options.outputDir);
|
|
69956
70367
|
if (!options.dryRun) {
|
|
69957
70368
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -69969,7 +70380,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
69969
70380
|
for (const [agentKey, generator] of entries) {
|
|
69970
70381
|
try {
|
|
69971
70382
|
const content = generator.generate(context);
|
|
69972
|
-
const outputPath =
|
|
70383
|
+
const outputPath = join11(options.outputDir, generator.outputFile);
|
|
69973
70384
|
validateFilePath(outputPath, options.outputDir);
|
|
69974
70385
|
if (!options.dryRun) {
|
|
69975
70386
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -69989,7 +70400,7 @@ async function discoverPackages(repoRoot) {
|
|
|
69989
70400
|
const glob = new Bun.Glob(pattern);
|
|
69990
70401
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
69991
70402
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
69992
|
-
const pkgAbsolute =
|
|
70403
|
+
const pkgAbsolute = join11(repoRoot, pkgRelative);
|
|
69993
70404
|
if (!seen.has(pkgAbsolute)) {
|
|
69994
70405
|
seen.add(pkgAbsolute);
|
|
69995
70406
|
packages.push(pkgAbsolute);
|
|
@@ -70021,7 +70432,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70021
70432
|
}
|
|
70022
70433
|
}
|
|
70023
70434
|
}
|
|
70024
|
-
const turboPath =
|
|
70435
|
+
const turboPath = join11(repoRoot, "turbo.json");
|
|
70025
70436
|
if (_generatorDeps.existsSync(turboPath)) {
|
|
70026
70437
|
try {
|
|
70027
70438
|
const turbo = JSON.parse(_generatorDeps.readFileSync(turboPath, "utf-8"));
|
|
@@ -70030,7 +70441,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70030
70441
|
}
|
|
70031
70442
|
} catch {}
|
|
70032
70443
|
}
|
|
70033
|
-
const pkgPath =
|
|
70444
|
+
const pkgPath = join11(repoRoot, "package.json");
|
|
70034
70445
|
if (_generatorDeps.existsSync(pkgPath)) {
|
|
70035
70446
|
try {
|
|
70036
70447
|
const pkg = JSON.parse(_generatorDeps.readFileSync(pkgPath, "utf-8"));
|
|
@@ -70040,7 +70451,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70040
70451
|
await resolveGlobs(patterns);
|
|
70041
70452
|
} catch {}
|
|
70042
70453
|
}
|
|
70043
|
-
const pnpmPath =
|
|
70454
|
+
const pnpmPath = join11(repoRoot, "pnpm-workspace.yaml");
|
|
70044
70455
|
if (_generatorDeps.existsSync(pnpmPath)) {
|
|
70045
70456
|
try {
|
|
70046
70457
|
const raw = _generatorDeps.readFileSync(pnpmPath, "utf-8");
|
|
@@ -70068,7 +70479,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70068
70479
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
70069
70480
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
70070
70481
|
const relativePkgPath = relative(resolvedRepoRoot, packageDir);
|
|
70071
|
-
const contextPath =
|
|
70482
|
+
const contextPath = join11(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
70072
70483
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
70073
70484
|
return [
|
|
70074
70485
|
{
|
|
@@ -70300,7 +70711,7 @@ var _planDeps = {
|
|
|
70300
70711
|
writeFile: (path, content) => Bun.write(path, content).then(() => {}),
|
|
70301
70712
|
scanCodebase: (workdir) => scanCodebase(workdir),
|
|
70302
70713
|
getAgent: (name, cfg) => cfg ? createAgentRegistry(cfg).getAgent(name) : getAgent(name),
|
|
70303
|
-
readPackageJson: (workdir) => Bun.file(
|
|
70714
|
+
readPackageJson: (workdir) => Bun.file(join13(workdir, "package.json")).json().catch(() => null),
|
|
70304
70715
|
spawnSync: (cmd, opts) => {
|
|
70305
70716
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
70306
70717
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
@@ -70320,7 +70731,7 @@ var _planDeps = {
|
|
|
70320
70731
|
planDecompose: (workdir, config2, opts) => planDecomposeCommand(workdir, config2, opts)
|
|
70321
70732
|
};
|
|
70322
70733
|
async function planCommand(workdir, config2, options) {
|
|
70323
|
-
const naxDir =
|
|
70734
|
+
const naxDir = join13(workdir, ".nax");
|
|
70324
70735
|
if (!existsSync15(naxDir)) {
|
|
70325
70736
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
70326
70737
|
}
|
|
@@ -70336,13 +70747,13 @@ async function planCommand(workdir, config2, options) {
|
|
|
70336
70747
|
const codebaseContext = buildCodebaseContext2(scan);
|
|
70337
70748
|
const relativePackages = discoveredPackages.map((p) => p.startsWith("/") ? p.replace(`${workdir}/`, "") : p);
|
|
70338
70749
|
const packageDetails = relativePackages.length > 0 ? await Promise.all(relativePackages.map(async (rel) => {
|
|
70339
|
-
const pkgJson = await _planDeps.readPackageJsonAt(
|
|
70750
|
+
const pkgJson = await _planDeps.readPackageJsonAt(join13(workdir, rel, "package.json"));
|
|
70340
70751
|
return buildPackageSummary(rel, pkgJson);
|
|
70341
70752
|
})) : [];
|
|
70342
70753
|
const projectName = detectProjectName(workdir, pkg);
|
|
70343
70754
|
const branchName = options.branch ?? `feat/${options.feature}`;
|
|
70344
|
-
const outputDir =
|
|
70345
|
-
const outputPath =
|
|
70755
|
+
const outputDir = join13(naxDir, "features", options.feature);
|
|
70756
|
+
const outputPath = join13(outputDir, "prd.json");
|
|
70346
70757
|
await _planDeps.mkdirp(outputDir);
|
|
70347
70758
|
const agentName = config2?.autoMode?.defaultAgent ?? "claude";
|
|
70348
70759
|
const timeoutSeconds = config2?.plan?.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS2;
|
|
@@ -70777,7 +71188,7 @@ Return JSON with this exact structure (no markdown, no explanation \u2014 JSON o
|
|
|
70777
71188
|
}`;
|
|
70778
71189
|
}
|
|
70779
71190
|
async function planDecomposeCommand(workdir, config2, options) {
|
|
70780
|
-
const prdPath =
|
|
71191
|
+
const prdPath = join13(workdir, ".nax", "features", options.feature, "prd.json");
|
|
70781
71192
|
if (!_planDeps.existsSync(prdPath)) {
|
|
70782
71193
|
throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
|
|
70783
71194
|
stage: "decompose",
|
|
@@ -70807,6 +71218,15 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
70807
71218
|
const adapter = _planDeps.getAgent(agentName, config2);
|
|
70808
71219
|
if (!adapter)
|
|
70809
71220
|
throw new Error(`[decompose] No agent adapter found for '${agentName}'`);
|
|
71221
|
+
let decomposeModel;
|
|
71222
|
+
try {
|
|
71223
|
+
const planTier = config2?.plan?.model ?? "balanced";
|
|
71224
|
+
const { resolveModelForAgent: resolveModelForAgent2 } = await Promise.resolve().then(() => (init_schema(), exports_schema));
|
|
71225
|
+
if (config2?.models) {
|
|
71226
|
+
const defaultAgent = config2.autoMode?.defaultAgent ?? "claude";
|
|
71227
|
+
decomposeModel = resolveModelForAgent2(config2.models, defaultAgent, planTier, defaultAgent).model;
|
|
71228
|
+
}
|
|
71229
|
+
} catch {}
|
|
70810
71230
|
const stages = config2?.debate?.stages;
|
|
70811
71231
|
const debateEnabled = config2?.debate?.enabled && stages?.decompose?.enabled;
|
|
70812
71232
|
let rawResponse;
|
|
@@ -70828,6 +71248,7 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
70828
71248
|
rawResponse = debateResult.output;
|
|
70829
71249
|
} else {
|
|
70830
71250
|
const completeResult = await adapter.complete(prompt, {
|
|
71251
|
+
model: decomposeModel,
|
|
70831
71252
|
jsonMode: true,
|
|
70832
71253
|
workdir,
|
|
70833
71254
|
sessionRole: "decompose",
|
|
@@ -70839,6 +71260,7 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
70839
71260
|
}
|
|
70840
71261
|
} else {
|
|
70841
71262
|
const completeResult = await adapter.complete(prompt, {
|
|
71263
|
+
model: decomposeModel,
|
|
70842
71264
|
jsonMode: true,
|
|
70843
71265
|
workdir,
|
|
70844
71266
|
sessionRole: "decompose",
|
|
@@ -71068,14 +71490,14 @@ async function displayModelEfficiency(workdir) {
|
|
|
71068
71490
|
}
|
|
71069
71491
|
// src/cli/status-features.ts
|
|
71070
71492
|
init_source();
|
|
71071
|
-
import { existsSync as existsSync17, readdirSync as
|
|
71072
|
-
import { join as
|
|
71493
|
+
import { existsSync as existsSync17, readdirSync as readdirSync4 } from "fs";
|
|
71494
|
+
import { join as join16 } from "path";
|
|
71073
71495
|
|
|
71074
71496
|
// src/commands/common.ts
|
|
71075
71497
|
init_path_security();
|
|
71076
71498
|
init_errors();
|
|
71077
|
-
import { existsSync as existsSync16, readdirSync as
|
|
71078
|
-
import { join as
|
|
71499
|
+
import { existsSync as existsSync16, readdirSync as readdirSync3, realpathSync as realpathSync2 } from "fs";
|
|
71500
|
+
import { join as join14, resolve as resolve4 } from "path";
|
|
71079
71501
|
function resolveProject(options = {}) {
|
|
71080
71502
|
const { dir, feature } = options;
|
|
71081
71503
|
let projectRoot;
|
|
@@ -71083,12 +71505,12 @@ function resolveProject(options = {}) {
|
|
|
71083
71505
|
let configPath;
|
|
71084
71506
|
if (dir) {
|
|
71085
71507
|
projectRoot = realpathSync2(resolve4(dir));
|
|
71086
|
-
naxDir =
|
|
71508
|
+
naxDir = join14(projectRoot, ".nax");
|
|
71087
71509
|
if (!existsSync16(naxDir)) {
|
|
71088
71510
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
71089
71511
|
Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
|
|
71090
71512
|
}
|
|
71091
|
-
configPath =
|
|
71513
|
+
configPath = join14(naxDir, "config.json");
|
|
71092
71514
|
if (!existsSync16(configPath)) {
|
|
71093
71515
|
throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
|
|
71094
71516
|
Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
@@ -71096,24 +71518,24 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
|
71096
71518
|
} else {
|
|
71097
71519
|
const found = findProjectRoot(process.cwd());
|
|
71098
71520
|
if (!found) {
|
|
71099
|
-
const cwdNaxDir =
|
|
71521
|
+
const cwdNaxDir = join14(process.cwd(), ".nax");
|
|
71100
71522
|
if (existsSync16(cwdNaxDir)) {
|
|
71101
|
-
const cwdConfigPath =
|
|
71523
|
+
const cwdConfigPath = join14(cwdNaxDir, "config.json");
|
|
71102
71524
|
throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
|
|
71103
71525
|
Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
|
|
71104
71526
|
}
|
|
71105
71527
|
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
71528
|
}
|
|
71107
71529
|
projectRoot = found;
|
|
71108
|
-
naxDir =
|
|
71109
|
-
configPath =
|
|
71530
|
+
naxDir = join14(projectRoot, ".nax");
|
|
71531
|
+
configPath = join14(naxDir, "config.json");
|
|
71110
71532
|
}
|
|
71111
71533
|
let featureDir;
|
|
71112
71534
|
if (feature) {
|
|
71113
|
-
const featuresDir =
|
|
71114
|
-
featureDir =
|
|
71535
|
+
const featuresDir = join14(naxDir, "features");
|
|
71536
|
+
featureDir = join14(featuresDir, feature);
|
|
71115
71537
|
if (!existsSync16(featureDir)) {
|
|
71116
|
-
const availableFeatures = existsSync16(featuresDir) ?
|
|
71538
|
+
const availableFeatures = existsSync16(featuresDir) ? readdirSync3(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
|
|
71117
71539
|
const availableMsg = availableFeatures.length > 0 ? `
|
|
71118
71540
|
|
|
71119
71541
|
Available features:
|
|
@@ -71138,12 +71560,12 @@ function findProjectRoot(startDir) {
|
|
|
71138
71560
|
let current = resolve4(startDir);
|
|
71139
71561
|
let depth = 0;
|
|
71140
71562
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
71141
|
-
const naxDir =
|
|
71142
|
-
const configPath =
|
|
71563
|
+
const naxDir = join14(current, ".nax");
|
|
71564
|
+
const configPath = join14(naxDir, "config.json");
|
|
71143
71565
|
if (existsSync16(configPath)) {
|
|
71144
71566
|
return realpathSync2(current);
|
|
71145
71567
|
}
|
|
71146
|
-
const parent =
|
|
71568
|
+
const parent = join14(current, "..");
|
|
71147
71569
|
if (parent === current) {
|
|
71148
71570
|
break;
|
|
71149
71571
|
}
|
|
@@ -71165,7 +71587,7 @@ function isPidAlive(pid) {
|
|
|
71165
71587
|
}
|
|
71166
71588
|
}
|
|
71167
71589
|
async function loadStatusFile(featureDir) {
|
|
71168
|
-
const statusPath =
|
|
71590
|
+
const statusPath = join16(featureDir, "status.json");
|
|
71169
71591
|
if (!existsSync17(statusPath)) {
|
|
71170
71592
|
return null;
|
|
71171
71593
|
}
|
|
@@ -71177,7 +71599,7 @@ async function loadStatusFile(featureDir) {
|
|
|
71177
71599
|
}
|
|
71178
71600
|
}
|
|
71179
71601
|
async function loadProjectStatusFile(projectDir) {
|
|
71180
|
-
const statusPath =
|
|
71602
|
+
const statusPath = join16(projectDir, ".nax", "status.json");
|
|
71181
71603
|
if (!existsSync17(statusPath)) {
|
|
71182
71604
|
return null;
|
|
71183
71605
|
}
|
|
@@ -71189,7 +71611,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
71189
71611
|
}
|
|
71190
71612
|
}
|
|
71191
71613
|
async function getFeatureSummary(featureName, featureDir) {
|
|
71192
|
-
const prdPath =
|
|
71614
|
+
const prdPath = join16(featureDir, "prd.json");
|
|
71193
71615
|
if (!existsSync17(prdPath)) {
|
|
71194
71616
|
return {
|
|
71195
71617
|
name: featureName,
|
|
@@ -71232,9 +71654,9 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
71232
71654
|
};
|
|
71233
71655
|
}
|
|
71234
71656
|
}
|
|
71235
|
-
const runsDir =
|
|
71657
|
+
const runsDir = join16(featureDir, "runs");
|
|
71236
71658
|
if (existsSync17(runsDir)) {
|
|
71237
|
-
const runs =
|
|
71659
|
+
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
71660
|
if (runs.length > 0) {
|
|
71239
71661
|
const latestRun = runs[0].replace(".jsonl", "");
|
|
71240
71662
|
summary.lastRun = latestRun;
|
|
@@ -71243,12 +71665,12 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
71243
71665
|
return summary;
|
|
71244
71666
|
}
|
|
71245
71667
|
async function displayAllFeatures(projectDir) {
|
|
71246
|
-
const featuresDir =
|
|
71668
|
+
const featuresDir = join16(projectDir, ".nax", "features");
|
|
71247
71669
|
if (!existsSync17(featuresDir)) {
|
|
71248
71670
|
console.log(source_default.dim("No features found."));
|
|
71249
71671
|
return;
|
|
71250
71672
|
}
|
|
71251
|
-
const features =
|
|
71673
|
+
const features = readdirSync4(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
71252
71674
|
if (features.length === 0) {
|
|
71253
71675
|
console.log(source_default.dim("No features found."));
|
|
71254
71676
|
return;
|
|
@@ -71284,7 +71706,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
71284
71706
|
console.log();
|
|
71285
71707
|
}
|
|
71286
71708
|
}
|
|
71287
|
-
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name,
|
|
71709
|
+
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join16(featuresDir, name))));
|
|
71288
71710
|
console.log(source_default.bold(`\uD83D\uDCCA Features
|
|
71289
71711
|
`));
|
|
71290
71712
|
const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
|
|
@@ -71310,7 +71732,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
71310
71732
|
console.log();
|
|
71311
71733
|
}
|
|
71312
71734
|
async function displayFeatureDetails(featureName, featureDir) {
|
|
71313
|
-
const prdPath =
|
|
71735
|
+
const prdPath = join16(featureDir, "prd.json");
|
|
71314
71736
|
if (!existsSync17(prdPath)) {
|
|
71315
71737
|
console.log(source_default.bold(`
|
|
71316
71738
|
\uD83D\uDCCA ${featureName}
|
|
@@ -71431,8 +71853,8 @@ async function displayFeatureStatus(options = {}) {
|
|
|
71431
71853
|
// src/cli/runs.ts
|
|
71432
71854
|
init_errors();
|
|
71433
71855
|
init_logger2();
|
|
71434
|
-
import { existsSync as existsSync18, readdirSync as
|
|
71435
|
-
import { join as
|
|
71856
|
+
import { existsSync as existsSync18, readdirSync as readdirSync5 } from "fs";
|
|
71857
|
+
import { join as join17 } from "path";
|
|
71436
71858
|
async function parseRunLog(logPath) {
|
|
71437
71859
|
const logger = getLogger();
|
|
71438
71860
|
try {
|
|
@@ -71448,19 +71870,19 @@ async function parseRunLog(logPath) {
|
|
|
71448
71870
|
async function runsListCommand(options) {
|
|
71449
71871
|
const logger = getLogger();
|
|
71450
71872
|
const { feature, workdir } = options;
|
|
71451
|
-
const runsDir =
|
|
71873
|
+
const runsDir = join17(workdir, ".nax", "features", feature, "runs");
|
|
71452
71874
|
if (!existsSync18(runsDir)) {
|
|
71453
71875
|
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
71454
71876
|
return;
|
|
71455
71877
|
}
|
|
71456
|
-
const files =
|
|
71878
|
+
const files = readdirSync5(runsDir).filter((f) => f.endsWith(".jsonl"));
|
|
71457
71879
|
if (files.length === 0) {
|
|
71458
71880
|
logger.info("cli", "No runs found for feature", { feature });
|
|
71459
71881
|
return;
|
|
71460
71882
|
}
|
|
71461
71883
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
71462
71884
|
for (const file3 of files.sort().reverse()) {
|
|
71463
|
-
const logPath =
|
|
71885
|
+
const logPath = join17(runsDir, file3);
|
|
71464
71886
|
const entries = await parseRunLog(logPath);
|
|
71465
71887
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
71466
71888
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
@@ -71486,7 +71908,7 @@ async function runsListCommand(options) {
|
|
|
71486
71908
|
async function runsShowCommand(options) {
|
|
71487
71909
|
const logger = getLogger();
|
|
71488
71910
|
const { runId, feature, workdir } = options;
|
|
71489
|
-
const logPath =
|
|
71911
|
+
const logPath = join17(workdir, ".nax", "features", feature, "runs", `${runId}.jsonl`);
|
|
71490
71912
|
if (!existsSync18(logPath)) {
|
|
71491
71913
|
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
71492
71914
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
@@ -71525,7 +71947,7 @@ async function runsShowCommand(options) {
|
|
|
71525
71947
|
// src/cli/prompts-main.ts
|
|
71526
71948
|
init_logger2();
|
|
71527
71949
|
import { existsSync as existsSync22, mkdirSync as mkdirSync2 } from "fs";
|
|
71528
|
-
import { join as
|
|
71950
|
+
import { join as join30 } from "path";
|
|
71529
71951
|
|
|
71530
71952
|
// src/pipeline/index.ts
|
|
71531
71953
|
init_runner();
|
|
@@ -71600,7 +72022,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
71600
72022
|
|
|
71601
72023
|
// src/cli/prompts-tdd.ts
|
|
71602
72024
|
init_prompts2();
|
|
71603
|
-
import { join as
|
|
72025
|
+
import { join as join29 } from "path";
|
|
71604
72026
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
71605
72027
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
71606
72028
|
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 +72041,7 @@ ${frontmatter}---
|
|
|
71619
72041
|
|
|
71620
72042
|
${session.prompt}`;
|
|
71621
72043
|
if (outputDir) {
|
|
71622
|
-
const promptFile =
|
|
72044
|
+
const promptFile = join29(outputDir, `${story.id}.${session.role}.md`);
|
|
71623
72045
|
await Bun.write(promptFile, fullOutput);
|
|
71624
72046
|
logger.info("cli", "Written TDD prompt file", {
|
|
71625
72047
|
storyId: story.id,
|
|
@@ -71635,7 +72057,7 @@ ${"=".repeat(80)}`);
|
|
|
71635
72057
|
}
|
|
71636
72058
|
}
|
|
71637
72059
|
if (outputDir && ctx.contextMarkdown) {
|
|
71638
|
-
const contextFile =
|
|
72060
|
+
const contextFile = join29(outputDir, `${story.id}.context.md`);
|
|
71639
72061
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
71640
72062
|
const contextOutput = `---
|
|
71641
72063
|
${frontmatter}---
|
|
@@ -71649,12 +72071,12 @@ ${ctx.contextMarkdown}`;
|
|
|
71649
72071
|
async function promptsCommand(options) {
|
|
71650
72072
|
const logger = getLogger();
|
|
71651
72073
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
71652
|
-
const naxDir =
|
|
72074
|
+
const naxDir = join30(workdir, ".nax");
|
|
71653
72075
|
if (!existsSync22(naxDir)) {
|
|
71654
72076
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
71655
72077
|
}
|
|
71656
|
-
const featureDir =
|
|
71657
|
-
const prdPath =
|
|
72078
|
+
const featureDir = join30(naxDir, "features", feature);
|
|
72079
|
+
const prdPath = join30(featureDir, "prd.json");
|
|
71658
72080
|
if (!existsSync22(prdPath)) {
|
|
71659
72081
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
71660
72082
|
}
|
|
@@ -71715,10 +72137,10 @@ ${frontmatter}---
|
|
|
71715
72137
|
|
|
71716
72138
|
${ctx.prompt}`;
|
|
71717
72139
|
if (outputDir) {
|
|
71718
|
-
const promptFile =
|
|
72140
|
+
const promptFile = join30(outputDir, `${story.id}.prompt.md`);
|
|
71719
72141
|
await Bun.write(promptFile, fullOutput);
|
|
71720
72142
|
if (ctx.contextMarkdown) {
|
|
71721
|
-
const contextFile =
|
|
72143
|
+
const contextFile = join30(outputDir, `${story.id}.context.md`);
|
|
71722
72144
|
const contextOutput = `---
|
|
71723
72145
|
${frontmatter}---
|
|
71724
72146
|
|
|
@@ -71745,7 +72167,7 @@ ${"=".repeat(80)}`);
|
|
|
71745
72167
|
}
|
|
71746
72168
|
// src/cli/prompts-init.ts
|
|
71747
72169
|
import { existsSync as existsSync23, mkdirSync as mkdirSync3 } from "fs";
|
|
71748
|
-
import { join as
|
|
72170
|
+
import { join as join31 } from "path";
|
|
71749
72171
|
var TEMPLATE_ROLES = [
|
|
71750
72172
|
{ file: "test-writer.md", role: "test-writer" },
|
|
71751
72173
|
{ file: "implementer.md", role: "implementer", variant: "standard" },
|
|
@@ -71769,9 +72191,9 @@ var TEMPLATE_HEADER = `<!--
|
|
|
71769
72191
|
`;
|
|
71770
72192
|
async function promptsInitCommand(options) {
|
|
71771
72193
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
71772
|
-
const templatesDir =
|
|
72194
|
+
const templatesDir = join31(workdir, ".nax", "templates");
|
|
71773
72195
|
mkdirSync3(templatesDir, { recursive: true });
|
|
71774
|
-
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync23(
|
|
72196
|
+
const existingFiles = TEMPLATE_ROLES.map((t) => t.file).filter((f) => existsSync23(join31(templatesDir, f)));
|
|
71775
72197
|
if (existingFiles.length > 0 && !force) {
|
|
71776
72198
|
console.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
71777
72199
|
Pass --force to overwrite existing templates.`);
|
|
@@ -71779,7 +72201,7 @@ async function promptsInitCommand(options) {
|
|
|
71779
72201
|
}
|
|
71780
72202
|
const written = [];
|
|
71781
72203
|
for (const template of TEMPLATE_ROLES) {
|
|
71782
|
-
const filePath =
|
|
72204
|
+
const filePath = join31(templatesDir, template.file);
|
|
71783
72205
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
71784
72206
|
const content = TEMPLATE_HEADER + roleBody;
|
|
71785
72207
|
await Bun.write(filePath, content);
|
|
@@ -71795,7 +72217,7 @@ async function promptsInitCommand(options) {
|
|
|
71795
72217
|
return written;
|
|
71796
72218
|
}
|
|
71797
72219
|
async function autoWirePromptsConfig(workdir) {
|
|
71798
|
-
const configPath =
|
|
72220
|
+
const configPath = join31(workdir, "nax.config.json");
|
|
71799
72221
|
if (!existsSync23(configPath)) {
|
|
71800
72222
|
const exampleConfig = JSON.stringify({
|
|
71801
72223
|
prompts: {
|
|
@@ -71960,8 +72382,8 @@ function pad(str, width) {
|
|
|
71960
72382
|
init_config();
|
|
71961
72383
|
init_logger2();
|
|
71962
72384
|
init_prd();
|
|
71963
|
-
import { existsSync as existsSync25, readdirSync as
|
|
71964
|
-
import { join as
|
|
72385
|
+
import { existsSync as existsSync25, readdirSync as readdirSync6 } from "fs";
|
|
72386
|
+
import { join as join36 } from "path";
|
|
71965
72387
|
|
|
71966
72388
|
// src/cli/diagnose-analysis.ts
|
|
71967
72389
|
function detectFailurePattern(story, prd, status) {
|
|
@@ -72160,7 +72582,7 @@ function isProcessAlive2(pid) {
|
|
|
72160
72582
|
}
|
|
72161
72583
|
}
|
|
72162
72584
|
async function loadStatusFile2(workdir) {
|
|
72163
|
-
const statusPath =
|
|
72585
|
+
const statusPath = join36(workdir, ".nax", "status.json");
|
|
72164
72586
|
if (!existsSync25(statusPath))
|
|
72165
72587
|
return null;
|
|
72166
72588
|
try {
|
|
@@ -72188,7 +72610,7 @@ async function countCommitsSince(workdir, since) {
|
|
|
72188
72610
|
}
|
|
72189
72611
|
}
|
|
72190
72612
|
async function checkLock(workdir) {
|
|
72191
|
-
const lockFile = Bun.file(
|
|
72613
|
+
const lockFile = Bun.file(join36(workdir, "nax.lock"));
|
|
72192
72614
|
if (!await lockFile.exists())
|
|
72193
72615
|
return { lockPresent: false };
|
|
72194
72616
|
try {
|
|
@@ -72206,8 +72628,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
72206
72628
|
const logger = getLogger();
|
|
72207
72629
|
const workdir = options.workdir ?? process.cwd();
|
|
72208
72630
|
const naxSubdir = findProjectDir(workdir);
|
|
72209
|
-
let projectDir = naxSubdir ?
|
|
72210
|
-
if (!projectDir && existsSync25(
|
|
72631
|
+
let projectDir = naxSubdir ? join36(naxSubdir, "..") : null;
|
|
72632
|
+
if (!projectDir && existsSync25(join36(workdir, ".nax"))) {
|
|
72211
72633
|
projectDir = workdir;
|
|
72212
72634
|
}
|
|
72213
72635
|
if (!projectDir)
|
|
@@ -72218,18 +72640,18 @@ async function diagnoseCommand(options = {}) {
|
|
|
72218
72640
|
if (status2) {
|
|
72219
72641
|
feature = status2.run.feature;
|
|
72220
72642
|
} else {
|
|
72221
|
-
const featuresDir =
|
|
72643
|
+
const featuresDir = join36(projectDir, ".nax", "features");
|
|
72222
72644
|
if (!existsSync25(featuresDir))
|
|
72223
72645
|
throw new Error("No features found in project");
|
|
72224
|
-
const features =
|
|
72646
|
+
const features = readdirSync6(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
72225
72647
|
if (features.length === 0)
|
|
72226
72648
|
throw new Error("No features found");
|
|
72227
72649
|
feature = features[0];
|
|
72228
72650
|
logger.info("diagnose", "No feature specified, using first found", { feature });
|
|
72229
72651
|
}
|
|
72230
72652
|
}
|
|
72231
|
-
const featureDir =
|
|
72232
|
-
const prdPath =
|
|
72653
|
+
const featureDir = join36(projectDir, ".nax", "features", feature);
|
|
72654
|
+
const prdPath = join36(featureDir, "prd.json");
|
|
72233
72655
|
if (!existsSync25(prdPath))
|
|
72234
72656
|
throw new Error(`Feature not found: ${feature}`);
|
|
72235
72657
|
const prd = await loadPRD(prdPath);
|
|
@@ -72272,7 +72694,7 @@ init_interaction();
|
|
|
72272
72694
|
init_source();
|
|
72273
72695
|
init_loader();
|
|
72274
72696
|
import { existsSync as existsSync26 } from "fs";
|
|
72275
|
-
import { join as
|
|
72697
|
+
import { join as join37 } from "path";
|
|
72276
72698
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
72277
72699
|
async function generateCommand(options) {
|
|
72278
72700
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -72315,7 +72737,7 @@ async function generateCommand(options) {
|
|
|
72315
72737
|
return;
|
|
72316
72738
|
}
|
|
72317
72739
|
if (options.package) {
|
|
72318
|
-
const packageDir =
|
|
72740
|
+
const packageDir = join37(workdir, options.package);
|
|
72319
72741
|
if (dryRun) {
|
|
72320
72742
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
72321
72743
|
}
|
|
@@ -72335,8 +72757,8 @@ async function generateCommand(options) {
|
|
|
72335
72757
|
process.exit(1);
|
|
72336
72758
|
return;
|
|
72337
72759
|
}
|
|
72338
|
-
const contextPath = options.context ?
|
|
72339
|
-
const outputDir = options.output ?
|
|
72760
|
+
const contextPath = options.context ? join37(workdir, options.context) : join37(workdir, ".nax/context.md");
|
|
72761
|
+
const outputDir = options.output ? join37(workdir, options.output) : workdir;
|
|
72340
72762
|
const autoInject = !options.noAutoInject;
|
|
72341
72763
|
if (!existsSync26(contextPath)) {
|
|
72342
72764
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -72441,7 +72863,7 @@ async function generateCommand(options) {
|
|
|
72441
72863
|
// src/cli/config-display.ts
|
|
72442
72864
|
init_loader();
|
|
72443
72865
|
import { existsSync as existsSync28 } from "fs";
|
|
72444
|
-
import { join as
|
|
72866
|
+
import { join as join39 } from "path";
|
|
72445
72867
|
|
|
72446
72868
|
// src/cli/config-descriptions.ts
|
|
72447
72869
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -72679,7 +73101,7 @@ function deepEqual(a, b) {
|
|
|
72679
73101
|
init_defaults();
|
|
72680
73102
|
init_loader();
|
|
72681
73103
|
import { existsSync as existsSync27 } from "fs";
|
|
72682
|
-
import { join as
|
|
73104
|
+
import { join as join38 } from "path";
|
|
72683
73105
|
async function loadConfigFile(path14) {
|
|
72684
73106
|
if (!existsSync27(path14))
|
|
72685
73107
|
return null;
|
|
@@ -72701,7 +73123,7 @@ async function loadProjectConfig() {
|
|
|
72701
73123
|
const projectDir = findProjectDir();
|
|
72702
73124
|
if (!projectDir)
|
|
72703
73125
|
return null;
|
|
72704
|
-
const projectPath =
|
|
73126
|
+
const projectPath = join38(projectDir, "config.json");
|
|
72705
73127
|
return await loadConfigFile(projectPath);
|
|
72706
73128
|
}
|
|
72707
73129
|
|
|
@@ -72761,7 +73183,7 @@ async function configCommand(config2, options = {}) {
|
|
|
72761
73183
|
function determineConfigSources() {
|
|
72762
73184
|
const globalPath = globalConfigPath();
|
|
72763
73185
|
const projectDir = findProjectDir();
|
|
72764
|
-
const projectPath = projectDir ?
|
|
73186
|
+
const projectPath = projectDir ? join39(projectDir, "config.json") : null;
|
|
72765
73187
|
return {
|
|
72766
73188
|
global: fileExists(globalPath) ? globalPath : null,
|
|
72767
73189
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -72831,6 +73253,26 @@ function displayConfigWithDescriptions(obj, path14, sources, indent = 0) {
|
|
|
72831
73253
|
}
|
|
72832
73254
|
}
|
|
72833
73255
|
}
|
|
73256
|
+
if (indent === 0 && !entries.find(([k]) => k === "prompts")) {
|
|
73257
|
+
console.log("# prompts: Prompt template overrides (PB-003: PromptBuilder)");
|
|
73258
|
+
const description = FIELD_DESCRIPTIONS["prompts.overrides"];
|
|
73259
|
+
if (description) {
|
|
73260
|
+
console.log(`# prompts.overrides: ${description}`);
|
|
73261
|
+
}
|
|
73262
|
+
const roles = ["test-writer", "implementer", "verifier", "single-session"];
|
|
73263
|
+
console.log("overrides:");
|
|
73264
|
+
for (const role of roles) {
|
|
73265
|
+
const roleDesc = FIELD_DESCRIPTIONS[`prompts.overrides.${role}`];
|
|
73266
|
+
if (roleDesc) {
|
|
73267
|
+
console.log(` # ${roleDesc}`);
|
|
73268
|
+
const match = roleDesc.match(/e\.g\., "([^"]+)"/);
|
|
73269
|
+
if (match) {
|
|
73270
|
+
console.log(` # ${role}: "${match[1]}"`);
|
|
73271
|
+
}
|
|
73272
|
+
}
|
|
73273
|
+
}
|
|
73274
|
+
console.log();
|
|
73275
|
+
}
|
|
72834
73276
|
}
|
|
72835
73277
|
function formatValue(value) {
|
|
72836
73278
|
if (value === null)
|
|
@@ -72885,6 +73327,107 @@ function formatValueForTable(value) {
|
|
|
72885
73327
|
}
|
|
72886
73328
|
return String(value);
|
|
72887
73329
|
}
|
|
73330
|
+
// src/cli/config-profile.ts
|
|
73331
|
+
init_paths();
|
|
73332
|
+
init_profile();
|
|
73333
|
+
import { mkdirSync as mkdirSync4 } from "fs";
|
|
73334
|
+
import { readdirSync as readdirSync7 } from "fs";
|
|
73335
|
+
import { join as join40 } from "path";
|
|
73336
|
+
var _profileCLIDeps = {
|
|
73337
|
+
env: process.env
|
|
73338
|
+
};
|
|
73339
|
+
var SENSITIVE_KEY_PATTERN = /key|token|secret|password|credential/i;
|
|
73340
|
+
var VAR_PATTERN = /\$[A-Za-z_][A-Za-z0-9_]*/;
|
|
73341
|
+
async function profileListCommand(startDir) {
|
|
73342
|
+
const globalProfilesDir = join40(globalConfigDir(), "profiles");
|
|
73343
|
+
const projectProfilesDir = join40(projectConfigDir(startDir), "profiles");
|
|
73344
|
+
const globalProfiles = scanProfileDir(globalProfilesDir);
|
|
73345
|
+
const projectProfiles = scanProfileDir(projectProfilesDir);
|
|
73346
|
+
const activeProfile = await resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
73347
|
+
const lines = [];
|
|
73348
|
+
lines.push("global:");
|
|
73349
|
+
if (globalProfiles.length === 0) {
|
|
73350
|
+
lines.push(" (none)");
|
|
73351
|
+
} else {
|
|
73352
|
+
for (const name of globalProfiles) {
|
|
73353
|
+
const marker = name === activeProfile ? "* " : " ";
|
|
73354
|
+
lines.push(`${marker}${name}`);
|
|
73355
|
+
}
|
|
73356
|
+
}
|
|
73357
|
+
if (projectProfiles.length > 0) {
|
|
73358
|
+
lines.push("project:");
|
|
73359
|
+
for (const name of projectProfiles) {
|
|
73360
|
+
const marker = name === activeProfile ? "* " : " ";
|
|
73361
|
+
lines.push(`${marker}${name}`);
|
|
73362
|
+
}
|
|
73363
|
+
}
|
|
73364
|
+
return lines.join(`
|
|
73365
|
+
`);
|
|
73366
|
+
}
|
|
73367
|
+
function scanProfileDir(dir) {
|
|
73368
|
+
try {
|
|
73369
|
+
return readdirSync7(dir).filter((f) => f.endsWith(".json")).map((f) => f.replace(/\.json$/, ""));
|
|
73370
|
+
} catch {
|
|
73371
|
+
return [];
|
|
73372
|
+
}
|
|
73373
|
+
}
|
|
73374
|
+
async function profileShowCommand(profileName, startDir, opts) {
|
|
73375
|
+
const rawProfile = await loadProfile(profileName, startDir);
|
|
73376
|
+
const envVars = await loadProfileEnv(profileName, startDir);
|
|
73377
|
+
if (opts.unmask) {
|
|
73378
|
+
const resolved = resolveEnvVars(rawProfile, envVars);
|
|
73379
|
+
const warning = "WARNING: Sensitive values are displayed in plaintext.";
|
|
73380
|
+
return `${warning}
|
|
73381
|
+
${JSON.stringify(resolved, null, 2)}`;
|
|
73382
|
+
}
|
|
73383
|
+
const masked = maskProfileValues(rawProfile);
|
|
73384
|
+
return JSON.stringify(masked, null, 2);
|
|
73385
|
+
}
|
|
73386
|
+
function maskProfileValues(obj) {
|
|
73387
|
+
const result = {};
|
|
73388
|
+
for (const [key, value] of Object.entries(obj)) {
|
|
73389
|
+
if (SENSITIVE_KEY_PATTERN.test(key)) {
|
|
73390
|
+
result[key] = "***";
|
|
73391
|
+
} else if (typeof value === "string" && VAR_PATTERN.test(value)) {
|
|
73392
|
+
result[key] = "***";
|
|
73393
|
+
} else if (value !== null && typeof value === "object" && !Array.isArray(value)) {
|
|
73394
|
+
result[key] = maskProfileValues(value);
|
|
73395
|
+
} else {
|
|
73396
|
+
result[key] = value;
|
|
73397
|
+
}
|
|
73398
|
+
}
|
|
73399
|
+
return result;
|
|
73400
|
+
}
|
|
73401
|
+
async function profileUseCommand(profileName, startDir) {
|
|
73402
|
+
const configPath = join40(projectConfigDir(startDir), "config.json");
|
|
73403
|
+
const configFile = Bun.file(configPath);
|
|
73404
|
+
let existing = {};
|
|
73405
|
+
if (await configFile.exists()) {
|
|
73406
|
+
existing = await configFile.json();
|
|
73407
|
+
}
|
|
73408
|
+
if (profileName === "default") {
|
|
73409
|
+
const { profile: _removed, ...rest } = existing;
|
|
73410
|
+
await Bun.write(configPath, JSON.stringify(rest, null, 2));
|
|
73411
|
+
return "Profile reset to default.";
|
|
73412
|
+
}
|
|
73413
|
+
const updated = { ...existing, profile: profileName };
|
|
73414
|
+
await Bun.write(configPath, JSON.stringify(updated, null, 2));
|
|
73415
|
+
return `Now using profile: ${profileName}`;
|
|
73416
|
+
}
|
|
73417
|
+
async function profileCurrentCommand(startDir) {
|
|
73418
|
+
return resolveProfileName({}, _profileCLIDeps.env, startDir);
|
|
73419
|
+
}
|
|
73420
|
+
async function profileCreateCommand(profileName, startDir) {
|
|
73421
|
+
const profilesDir = join40(projectConfigDir(startDir), "profiles");
|
|
73422
|
+
const profilePath = join40(profilesDir, `${profileName}.json`);
|
|
73423
|
+
const profileFile = Bun.file(profilePath);
|
|
73424
|
+
if (await profileFile.exists()) {
|
|
73425
|
+
throw new Error(`Profile "${profileName}" already exists at ${profilePath}`);
|
|
73426
|
+
}
|
|
73427
|
+
mkdirSync4(profilesDir, { recursive: true });
|
|
73428
|
+
await Bun.write(profilePath, "{}");
|
|
73429
|
+
return profilePath;
|
|
73430
|
+
}
|
|
72888
73431
|
// src/cli/agents.ts
|
|
72889
73432
|
init_registry();
|
|
72890
73433
|
init_version_detection();
|
|
@@ -72941,24 +73484,24 @@ async function diagnose(options) {
|
|
|
72941
73484
|
|
|
72942
73485
|
// src/commands/logs.ts
|
|
72943
73486
|
import { existsSync as existsSync30 } from "fs";
|
|
72944
|
-
import { join as
|
|
73487
|
+
import { join as join44 } from "path";
|
|
72945
73488
|
|
|
72946
73489
|
// src/commands/logs-formatter.ts
|
|
72947
73490
|
init_source();
|
|
72948
73491
|
init_formatter();
|
|
72949
|
-
import { readdirSync as
|
|
72950
|
-
import { join as
|
|
73492
|
+
import { readdirSync as readdirSync9 } from "fs";
|
|
73493
|
+
import { join as join43 } from "path";
|
|
72951
73494
|
|
|
72952
73495
|
// src/commands/logs-reader.ts
|
|
72953
|
-
import { existsSync as existsSync29, readdirSync as
|
|
73496
|
+
import { existsSync as existsSync29, readdirSync as readdirSync8 } from "fs";
|
|
72954
73497
|
import { readdir as readdir3 } from "fs/promises";
|
|
72955
|
-
import { join as
|
|
73498
|
+
import { join as join42 } from "path";
|
|
72956
73499
|
|
|
72957
73500
|
// src/utils/paths.ts
|
|
72958
73501
|
import { homedir as homedir4 } from "os";
|
|
72959
|
-
import { join as
|
|
73502
|
+
import { join as join41 } from "path";
|
|
72960
73503
|
function getRunsDir() {
|
|
72961
|
-
return process.env.NAX_RUNS_DIR ??
|
|
73504
|
+
return process.env.NAX_RUNS_DIR ?? join41(homedir4(), ".nax", "runs");
|
|
72962
73505
|
}
|
|
72963
73506
|
|
|
72964
73507
|
// src/commands/logs-reader.ts
|
|
@@ -72975,7 +73518,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
72975
73518
|
}
|
|
72976
73519
|
let matched = null;
|
|
72977
73520
|
for (const entry of entries) {
|
|
72978
|
-
const metaPath =
|
|
73521
|
+
const metaPath = join42(runsDir, entry, "meta.json");
|
|
72979
73522
|
try {
|
|
72980
73523
|
const meta3 = await Bun.file(metaPath).json();
|
|
72981
73524
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -72991,20 +73534,20 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
72991
73534
|
console.log(`Log directory unavailable for run: ${runId}`);
|
|
72992
73535
|
return null;
|
|
72993
73536
|
}
|
|
72994
|
-
const files =
|
|
73537
|
+
const files = readdirSync8(matched.eventsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
72995
73538
|
if (files.length === 0) {
|
|
72996
73539
|
console.log(`No log files found for run: ${runId}`);
|
|
72997
73540
|
return null;
|
|
72998
73541
|
}
|
|
72999
73542
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
73000
|
-
return
|
|
73543
|
+
return join42(matched.eventsDir, specificFile ?? files[0]);
|
|
73001
73544
|
}
|
|
73002
73545
|
async function selectRunFile(runsDir) {
|
|
73003
|
-
const files =
|
|
73546
|
+
const files = readdirSync8(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
73004
73547
|
if (files.length === 0) {
|
|
73005
73548
|
return null;
|
|
73006
73549
|
}
|
|
73007
|
-
return
|
|
73550
|
+
return join42(runsDir, files[0]);
|
|
73008
73551
|
}
|
|
73009
73552
|
async function extractRunSummary(filePath) {
|
|
73010
73553
|
const file3 = Bun.file(filePath);
|
|
@@ -73078,7 +73621,7 @@ var LOG_LEVEL_PRIORITY2 = {
|
|
|
73078
73621
|
error: 3
|
|
73079
73622
|
};
|
|
73080
73623
|
async function displayRunsList(runsDir) {
|
|
73081
|
-
const files =
|
|
73624
|
+
const files = readdirSync9(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
73082
73625
|
if (files.length === 0) {
|
|
73083
73626
|
console.log(source_default.dim("No runs found"));
|
|
73084
73627
|
return;
|
|
@@ -73089,7 +73632,7 @@ Runs:
|
|
|
73089
73632
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
73090
73633
|
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
73634
|
for (const file3 of files) {
|
|
73092
|
-
const filePath =
|
|
73635
|
+
const filePath = join43(runsDir, file3);
|
|
73093
73636
|
const summary = await extractRunSummary(filePath);
|
|
73094
73637
|
const timestamp = file3.replace(".jsonl", "");
|
|
73095
73638
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -73203,7 +73746,7 @@ async function logsCommand(options) {
|
|
|
73203
73746
|
return;
|
|
73204
73747
|
}
|
|
73205
73748
|
const resolved = resolveProject({ dir: options.dir });
|
|
73206
|
-
const naxDir =
|
|
73749
|
+
const naxDir = join44(resolved.projectDir, ".nax");
|
|
73207
73750
|
const configPath = resolved.configPath;
|
|
73208
73751
|
const configFile = Bun.file(configPath);
|
|
73209
73752
|
const config2 = await configFile.json();
|
|
@@ -73211,8 +73754,8 @@ async function logsCommand(options) {
|
|
|
73211
73754
|
if (!featureName) {
|
|
73212
73755
|
throw new Error("No feature specified in config.json");
|
|
73213
73756
|
}
|
|
73214
|
-
const featureDir =
|
|
73215
|
-
const runsDir =
|
|
73757
|
+
const featureDir = join44(naxDir, "features", featureName);
|
|
73758
|
+
const runsDir = join44(featureDir, "runs");
|
|
73216
73759
|
if (!existsSync30(runsDir)) {
|
|
73217
73760
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
73218
73761
|
}
|
|
@@ -73237,7 +73780,7 @@ init_config();
|
|
|
73237
73780
|
init_prd();
|
|
73238
73781
|
init_precheck();
|
|
73239
73782
|
import { existsSync as existsSync31 } from "fs";
|
|
73240
|
-
import { join as
|
|
73783
|
+
import { join as join45 } from "path";
|
|
73241
73784
|
async function precheckCommand(options) {
|
|
73242
73785
|
const resolved = resolveProject({
|
|
73243
73786
|
dir: options.dir,
|
|
@@ -73259,9 +73802,9 @@ async function precheckCommand(options) {
|
|
|
73259
73802
|
process.exit(1);
|
|
73260
73803
|
}
|
|
73261
73804
|
}
|
|
73262
|
-
const naxDir =
|
|
73263
|
-
const featureDir =
|
|
73264
|
-
const prdPath =
|
|
73805
|
+
const naxDir = join45(resolved.projectDir, ".nax");
|
|
73806
|
+
const featureDir = join45(naxDir, "features", featureName);
|
|
73807
|
+
const prdPath = join45(featureDir, "prd.json");
|
|
73265
73808
|
if (!existsSync31(featureDir)) {
|
|
73266
73809
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
73267
73810
|
process.exit(1);
|
|
@@ -73283,7 +73826,7 @@ async function precheckCommand(options) {
|
|
|
73283
73826
|
// src/commands/runs.ts
|
|
73284
73827
|
init_source();
|
|
73285
73828
|
import { readdir as readdir4 } from "fs/promises";
|
|
73286
|
-
import { join as
|
|
73829
|
+
import { join as join46 } from "path";
|
|
73287
73830
|
var DEFAULT_LIMIT = 20;
|
|
73288
73831
|
var _runsCmdDeps = {
|
|
73289
73832
|
getRunsDir
|
|
@@ -73338,7 +73881,7 @@ async function runsCommand(options = {}) {
|
|
|
73338
73881
|
}
|
|
73339
73882
|
const rows = [];
|
|
73340
73883
|
for (const entry of entries) {
|
|
73341
|
-
const metaPath =
|
|
73884
|
+
const metaPath = join46(runsDir, entry, "meta.json");
|
|
73342
73885
|
let meta3;
|
|
73343
73886
|
try {
|
|
73344
73887
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -73415,7 +73958,7 @@ async function runsCommand(options = {}) {
|
|
|
73415
73958
|
|
|
73416
73959
|
// src/commands/unlock.ts
|
|
73417
73960
|
init_source();
|
|
73418
|
-
import { join as
|
|
73961
|
+
import { join as join47 } from "path";
|
|
73419
73962
|
function isProcessAlive3(pid) {
|
|
73420
73963
|
try {
|
|
73421
73964
|
process.kill(pid, 0);
|
|
@@ -73430,7 +73973,7 @@ function formatLockAge(ageMs) {
|
|
|
73430
73973
|
}
|
|
73431
73974
|
async function unlockCommand(options) {
|
|
73432
73975
|
const workdir = options.dir ?? process.cwd();
|
|
73433
|
-
const lockPath =
|
|
73976
|
+
const lockPath = join47(workdir, "nax.lock");
|
|
73434
73977
|
const lockFile = Bun.file(lockPath);
|
|
73435
73978
|
const exists = await lockFile.exists();
|
|
73436
73979
|
if (!exists) {
|
|
@@ -81253,15 +81796,15 @@ Next: nax generate --package ${options.package}`));
|
|
|
81253
81796
|
}
|
|
81254
81797
|
return;
|
|
81255
81798
|
}
|
|
81256
|
-
const naxDir =
|
|
81799
|
+
const naxDir = join58(workdir, ".nax");
|
|
81257
81800
|
if (existsSync34(naxDir) && !options.force) {
|
|
81258
81801
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
81259
81802
|
return;
|
|
81260
81803
|
}
|
|
81261
|
-
|
|
81262
|
-
|
|
81263
|
-
await Bun.write(
|
|
81264
|
-
await Bun.write(
|
|
81804
|
+
mkdirSync6(join58(naxDir, "features"), { recursive: true });
|
|
81805
|
+
mkdirSync6(join58(naxDir, "hooks"), { recursive: true });
|
|
81806
|
+
await Bun.write(join58(naxDir, "config.json"), JSON.stringify(DEFAULT_CONFIG, null, 2));
|
|
81807
|
+
await Bun.write(join58(naxDir, "hooks.json"), JSON.stringify({
|
|
81265
81808
|
hooks: {
|
|
81266
81809
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
81267
81810
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -81269,12 +81812,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
81269
81812
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
81270
81813
|
}
|
|
81271
81814
|
}, null, 2));
|
|
81272
|
-
await Bun.write(
|
|
81815
|
+
await Bun.write(join58(naxDir, ".gitignore"), `# nax temp files
|
|
81273
81816
|
*.tmp
|
|
81274
81817
|
.paused.json
|
|
81275
81818
|
.nax-verifier-verdict.json
|
|
81276
81819
|
`);
|
|
81277
|
-
await Bun.write(
|
|
81820
|
+
await Bun.write(join58(naxDir, "context.md"), `# Project Context
|
|
81278
81821
|
|
|
81279
81822
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
81280
81823
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -81359,7 +81902,7 @@ Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cu
|
|
|
81359
81902
|
console.log(source_default.dim(`
|
|
81360
81903
|
Next: nax features create <name>`));
|
|
81361
81904
|
});
|
|
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) => {
|
|
81905
|
+
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
81906
|
let workdir;
|
|
81364
81907
|
try {
|
|
81365
81908
|
workdir = validateDirectory(options.dir);
|
|
@@ -81395,13 +81938,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81395
81938
|
formatterMode = "quiet";
|
|
81396
81939
|
}
|
|
81397
81940
|
const naxDir = findProjectDir(workdir);
|
|
81398
|
-
const
|
|
81941
|
+
const cliOverrides = {};
|
|
81942
|
+
if (options.profile) {
|
|
81943
|
+
cliOverrides.profile = options.profile;
|
|
81944
|
+
}
|
|
81945
|
+
const config2 = await loadConfig(naxDir ?? undefined, cliOverrides);
|
|
81399
81946
|
if (!naxDir) {
|
|
81400
81947
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81401
81948
|
process.exit(1);
|
|
81402
81949
|
}
|
|
81403
|
-
const featureDir =
|
|
81404
|
-
const prdPath =
|
|
81950
|
+
const featureDir = join58(naxDir, "features", options.feature);
|
|
81951
|
+
const prdPath = join58(featureDir, "prd.json");
|
|
81405
81952
|
if (options.plan && options.from) {
|
|
81406
81953
|
if (existsSync34(prdPath) && !options.force) {
|
|
81407
81954
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
@@ -81423,10 +81970,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81423
81970
|
}
|
|
81424
81971
|
}
|
|
81425
81972
|
try {
|
|
81426
|
-
const planLogDir =
|
|
81427
|
-
|
|
81973
|
+
const planLogDir = join58(featureDir, "plan");
|
|
81974
|
+
mkdirSync6(planLogDir, { recursive: true });
|
|
81428
81975
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81429
|
-
const planLogPath =
|
|
81976
|
+
const planLogPath = join58(planLogDir, `${planLogId}.jsonl`);
|
|
81430
81977
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
81431
81978
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
81432
81979
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -81470,10 +82017,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81470
82017
|
process.exit(1);
|
|
81471
82018
|
}
|
|
81472
82019
|
resetLogger();
|
|
81473
|
-
const runsDir =
|
|
81474
|
-
|
|
82020
|
+
const runsDir = join58(featureDir, "runs");
|
|
82021
|
+
mkdirSync6(runsDir, { recursive: true });
|
|
81475
82022
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81476
|
-
const logFilePath =
|
|
82023
|
+
const logFilePath = join58(runsDir, `${runId}.jsonl`);
|
|
81477
82024
|
const isTTY = process.stdout.isTTY ?? false;
|
|
81478
82025
|
const headlessFlag = options.headless ?? false;
|
|
81479
82026
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -81489,7 +82036,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81489
82036
|
config2.autoMode.defaultAgent = options.agent;
|
|
81490
82037
|
}
|
|
81491
82038
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
81492
|
-
const globalNaxDir =
|
|
82039
|
+
const globalNaxDir = join58(homedir8(), ".nax");
|
|
81493
82040
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
81494
82041
|
const eventEmitter = new PipelineEventEmitter;
|
|
81495
82042
|
let tuiInstance;
|
|
@@ -81512,7 +82059,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81512
82059
|
} else {
|
|
81513
82060
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
81514
82061
|
}
|
|
81515
|
-
const statusFilePath =
|
|
82062
|
+
const statusFilePath = join58(workdir, ".nax", "status.json");
|
|
81516
82063
|
let parallel;
|
|
81517
82064
|
if (options.parallel !== undefined) {
|
|
81518
82065
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -81538,7 +82085,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81538
82085
|
headless: useHeadless,
|
|
81539
82086
|
skipPrecheck: options.skipPrecheck ?? false
|
|
81540
82087
|
});
|
|
81541
|
-
const latestSymlink =
|
|
82088
|
+
const latestSymlink = join58(runsDir, "latest.jsonl");
|
|
81542
82089
|
try {
|
|
81543
82090
|
if (existsSync34(latestSymlink)) {
|
|
81544
82091
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
@@ -81576,9 +82123,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
81576
82123
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81577
82124
|
process.exit(1);
|
|
81578
82125
|
}
|
|
81579
|
-
const featureDir =
|
|
81580
|
-
|
|
81581
|
-
await Bun.write(
|
|
82126
|
+
const featureDir = join58(naxDir, "features", name);
|
|
82127
|
+
mkdirSync6(featureDir, { recursive: true });
|
|
82128
|
+
await Bun.write(join58(featureDir, "spec.md"), `# Feature: ${name}
|
|
81582
82129
|
|
|
81583
82130
|
## Overview
|
|
81584
82131
|
|
|
@@ -81611,7 +82158,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
81611
82158
|
|
|
81612
82159
|
<!-- What this feature explicitly does NOT cover. -->
|
|
81613
82160
|
`);
|
|
81614
|
-
await Bun.write(
|
|
82161
|
+
await Bun.write(join58(featureDir, "progress.txt"), `# Progress: ${name}
|
|
81615
82162
|
|
|
81616
82163
|
Created: ${new Date().toISOString()}
|
|
81617
82164
|
|
|
@@ -81637,13 +82184,13 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
81637
82184
|
console.error(source_default.red("nax not initialized."));
|
|
81638
82185
|
process.exit(1);
|
|
81639
82186
|
}
|
|
81640
|
-
const featuresDir =
|
|
82187
|
+
const featuresDir = join58(naxDir, "features");
|
|
81641
82188
|
if (!existsSync34(featuresDir)) {
|
|
81642
82189
|
console.log(source_default.dim("No features yet."));
|
|
81643
82190
|
return;
|
|
81644
82191
|
}
|
|
81645
|
-
const { readdirSync:
|
|
81646
|
-
const entries =
|
|
82192
|
+
const { readdirSync: readdirSync10 } = await import("fs");
|
|
82193
|
+
const entries = readdirSync10(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
81647
82194
|
if (entries.length === 0) {
|
|
81648
82195
|
console.log(source_default.dim("No features yet."));
|
|
81649
82196
|
return;
|
|
@@ -81652,7 +82199,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
81652
82199
|
Features:
|
|
81653
82200
|
`));
|
|
81654
82201
|
for (const name of entries) {
|
|
81655
|
-
const prdPath =
|
|
82202
|
+
const prdPath = join58(featuresDir, name, "prd.json");
|
|
81656
82203
|
if (existsSync34(prdPath)) {
|
|
81657
82204
|
const prd = await loadPRD(prdPath);
|
|
81658
82205
|
const c = countStories(prd);
|
|
@@ -81663,7 +82210,7 @@ Features:
|
|
|
81663
82210
|
}
|
|
81664
82211
|
console.log();
|
|
81665
82212
|
});
|
|
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) => {
|
|
82213
|
+
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
82214
|
if (description) {
|
|
81668
82215
|
console.error(source_default.red(`Error: Positional args removed in plan v2.
|
|
81669
82216
|
|
|
@@ -81682,11 +82229,15 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
81682
82229
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81683
82230
|
process.exit(1);
|
|
81684
82231
|
}
|
|
81685
|
-
const
|
|
81686
|
-
|
|
81687
|
-
|
|
82232
|
+
const cliOverrides = {};
|
|
82233
|
+
if (options.profile) {
|
|
82234
|
+
cliOverrides.profile = options.profile;
|
|
82235
|
+
}
|
|
82236
|
+
const config2 = await loadConfig(workdir, cliOverrides);
|
|
82237
|
+
const featureLogDir = join58(naxDir, "features", options.feature, "plan");
|
|
82238
|
+
mkdirSync6(featureLogDir, { recursive: true });
|
|
81688
82239
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81689
|
-
const planLogPath =
|
|
82240
|
+
const planLogPath = join58(featureLogDir, `${planLogId}.jsonl`);
|
|
81690
82241
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
81691
82242
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
81692
82243
|
try {
|
|
@@ -81737,7 +82288,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
81737
82288
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81738
82289
|
process.exit(1);
|
|
81739
82290
|
}
|
|
81740
|
-
const featureDir =
|
|
82291
|
+
const featureDir = join58(naxDir, "features", options.feature);
|
|
81741
82292
|
if (!existsSync34(featureDir)) {
|
|
81742
82293
|
console.error(source_default.red(`Feature "${options.feature}" not found.`));
|
|
81743
82294
|
process.exit(1);
|
|
@@ -81753,7 +82304,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
81753
82304
|
specPath: options.from,
|
|
81754
82305
|
reclassify: options.reclassify
|
|
81755
82306
|
});
|
|
81756
|
-
const prdPath =
|
|
82307
|
+
const prdPath = join58(featureDir, "prd.json");
|
|
81757
82308
|
await Bun.write(prdPath, JSON.stringify(prd, null, 2));
|
|
81758
82309
|
const c = countStories(prd);
|
|
81759
82310
|
console.log(source_default.green(`
|
|
@@ -81786,7 +82337,7 @@ program2.command("agents").description("List available coding agents with status
|
|
|
81786
82337
|
process.exit(1);
|
|
81787
82338
|
}
|
|
81788
82339
|
});
|
|
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) => {
|
|
82340
|
+
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
82341
|
let workdir;
|
|
81791
82342
|
try {
|
|
81792
82343
|
workdir = validateDirectory(options.dir);
|
|
@@ -81803,6 +82354,52 @@ program2.command("config").description("Display effective merged configuration")
|
|
|
81803
82354
|
process.exit(1);
|
|
81804
82355
|
}
|
|
81805
82356
|
});
|
|
82357
|
+
var configProfileCmd = configCmd.command("profile").description("Manage config profiles");
|
|
82358
|
+
configProfileCmd.command("list").description("List all available profiles grouped by scope").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (options) => {
|
|
82359
|
+
try {
|
|
82360
|
+
const output = await profileListCommand(options.dir);
|
|
82361
|
+
console.log(output);
|
|
82362
|
+
} catch (err) {
|
|
82363
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82364
|
+
process.exit(1);
|
|
82365
|
+
}
|
|
82366
|
+
});
|
|
82367
|
+
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) => {
|
|
82368
|
+
try {
|
|
82369
|
+
const output = await profileShowCommand(name, options.dir, { unmask: options.unmask });
|
|
82370
|
+
console.log(output);
|
|
82371
|
+
} catch (err) {
|
|
82372
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82373
|
+
process.exit(1);
|
|
82374
|
+
}
|
|
82375
|
+
});
|
|
82376
|
+
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) => {
|
|
82377
|
+
try {
|
|
82378
|
+
const msg = await profileUseCommand(name, options.dir);
|
|
82379
|
+
console.log(msg);
|
|
82380
|
+
} catch (err) {
|
|
82381
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82382
|
+
process.exit(1);
|
|
82383
|
+
}
|
|
82384
|
+
});
|
|
82385
|
+
configProfileCmd.command("current").description("Show the currently active profile name").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (options) => {
|
|
82386
|
+
try {
|
|
82387
|
+
const name = await profileCurrentCommand(options.dir);
|
|
82388
|
+
console.log(name);
|
|
82389
|
+
} catch (err) {
|
|
82390
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82391
|
+
process.exit(1);
|
|
82392
|
+
}
|
|
82393
|
+
});
|
|
82394
|
+
configProfileCmd.command("create <name>").description("Create a new empty profile").option("-d, --dir <path>", "Project directory", process.cwd()).action(async (name, options) => {
|
|
82395
|
+
try {
|
|
82396
|
+
const path19 = await profileCreateCommand(name, options.dir);
|
|
82397
|
+
console.log(`Created profile at: ${path19}`);
|
|
82398
|
+
} catch (err) {
|
|
82399
|
+
console.error(source_default.red(`Error: ${err.message}`));
|
|
82400
|
+
process.exit(1);
|
|
82401
|
+
}
|
|
82402
|
+
});
|
|
81806
82403
|
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
82404
|
let workdir;
|
|
81808
82405
|
try {
|