@nathapp/nax 0.57.0 → 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 +1561 -896
- package/package.json +1 -1
package/dist/nax.js
CHANGED
|
@@ -17829,256 +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
|
-
},
|
|
18053
|
-
review: {
|
|
18054
|
-
enabled: true,
|
|
18055
|
-
resolver: { type: "majority-fail-closed" },
|
|
18056
|
-
sessionMode: "one-shot",
|
|
18057
|
-
rounds: 2
|
|
18058
|
-
},
|
|
18059
|
-
acceptance: {
|
|
18060
|
-
enabled: false,
|
|
18061
|
-
resolver: { type: "majority-fail-closed" },
|
|
18062
|
-
sessionMode: "one-shot",
|
|
18063
|
-
rounds: 1
|
|
18064
|
-
},
|
|
18065
|
-
rectification: {
|
|
18066
|
-
enabled: false,
|
|
18067
|
-
resolver: { type: "synthesis" },
|
|
18068
|
-
sessionMode: "one-shot",
|
|
18069
|
-
rounds: 1
|
|
18070
|
-
},
|
|
18071
|
-
escalation: {
|
|
18072
|
-
enabled: false,
|
|
18073
|
-
resolver: { type: "majority-fail-closed" },
|
|
18074
|
-
sessionMode: "one-shot",
|
|
18075
|
-
rounds: 1
|
|
18076
|
-
}
|
|
18077
|
-
}
|
|
18078
|
-
}
|
|
18079
|
-
};
|
|
18080
|
-
});
|
|
18081
|
-
|
|
18082
17832
|
// src/config/schemas.ts
|
|
18083
17833
|
function isLegacyFlatModels(val) {
|
|
18084
17834
|
if (typeof val !== "object" || val === null)
|
|
@@ -18102,11 +17852,11 @@ var TokenPricingSchema, ModelDefSchema, ModelEntrySchema, PerAgentModelMapSchema
|
|
|
18102
17852
|
resolver: makeResolverSchema(defaults.resolverType),
|
|
18103
17853
|
sessionMode: exports_external.enum(["one-shot", "stateful"]).default(defaults.sessionMode),
|
|
18104
17854
|
rounds: exports_external.number().int().min(1).default(defaults.rounds),
|
|
18105
|
-
debaters: exports_external.array(DebaterSchema).min(2, "debaters must have at least 2 entries").optional()
|
|
17855
|
+
debaters: exports_external.array(DebaterSchema).min(2, "debaters must have at least 2 entries").optional(),
|
|
17856
|
+
timeoutSeconds: exports_external.number().int().positive().default(600)
|
|
18106
17857
|
})), DebateConfigSchema, NaxConfigSchema;
|
|
18107
17858
|
var init_schemas3 = __esm(() => {
|
|
18108
17859
|
init_zod();
|
|
18109
|
-
init_defaults();
|
|
18110
17860
|
TokenPricingSchema = exports_external.object({
|
|
18111
17861
|
inputPer1M: exports_external.number().min(0),
|
|
18112
17862
|
outputPer1M: exports_external.number().min(0)
|
|
@@ -18121,8 +17871,7 @@ var init_schemas3 = __esm(() => {
|
|
|
18121
17871
|
PerAgentModelMapSchema = exports_external.record(exports_external.string().min(1), exports_external.record(exports_external.string().min(1), ModelEntrySchema));
|
|
18122
17872
|
ModelMapSchema = exports_external.preprocess((val) => {
|
|
18123
17873
|
if (isLegacyFlatModels(val)) {
|
|
18124
|
-
|
|
18125
|
-
return { [defaultAgent]: val };
|
|
17874
|
+
return { claude: val };
|
|
18126
17875
|
}
|
|
18127
17876
|
return val;
|
|
18128
17877
|
}, PerAgentModelMapSchema);
|
|
@@ -18214,6 +17963,19 @@ var init_schemas3 = __esm(() => {
|
|
|
18214
17963
|
formatFix: exports_external.string().optional(),
|
|
18215
17964
|
build: exports_external.string().optional()
|
|
18216
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
|
+
}),
|
|
18217
17979
|
forceExit: exports_external.boolean().default(false),
|
|
18218
17980
|
detectOpenHandles: exports_external.boolean().default(true),
|
|
18219
17981
|
detectOpenHandlesRetries: exports_external.number().int().min(0).max(5).default(1),
|
|
@@ -18303,13 +18065,14 @@ var init_schemas3 = __esm(() => {
|
|
|
18303
18065
|
});
|
|
18304
18066
|
PlanConfigSchema = exports_external.object({
|
|
18305
18067
|
model: ModelTierSchema,
|
|
18306
|
-
outputPath: exports_external.string().min(1, "plan.outputPath must be non-empty")
|
|
18068
|
+
outputPath: exports_external.string().min(1, "plan.outputPath must be non-empty"),
|
|
18069
|
+
timeoutSeconds: exports_external.number().int().positive().default(600)
|
|
18307
18070
|
});
|
|
18308
18071
|
AcceptanceFixConfigSchema = exports_external.object({
|
|
18309
|
-
diagnoseModel: exports_external.string().min(1, "acceptance.fix.diagnoseModel must be non-empty"),
|
|
18310
|
-
fixModel: exports_external.string().min(1, "acceptance.fix.fixModel must be non-empty"),
|
|
18311
|
-
strategy: exports_external.enum(["diagnose-first", "implement-only"]),
|
|
18312
|
-
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)
|
|
18313
18076
|
});
|
|
18314
18077
|
AcceptanceConfigSchema = exports_external.object({
|
|
18315
18078
|
enabled: exports_external.boolean(),
|
|
@@ -18429,6 +18192,7 @@ var init_schemas3 = __esm(() => {
|
|
|
18429
18192
|
DebateConfigSchema = exports_external.preprocess(toObject, exports_external.object({
|
|
18430
18193
|
enabled: exports_external.boolean().default(false),
|
|
18431
18194
|
agents: exports_external.number().int().min(2).default(3),
|
|
18195
|
+
maxConcurrentDebaters: exports_external.number().int().min(1).max(10).default(2),
|
|
18432
18196
|
stages: exports_external.preprocess(toObject, exports_external.object({
|
|
18433
18197
|
plan: DebateStageConfigSchema({ enabled: true, resolverType: "synthesis", sessionMode: "stateful", rounds: 3 }),
|
|
18434
18198
|
review: DebateStageConfigSchema({
|
|
@@ -18458,36 +18222,289 @@ var init_schemas3 = __esm(() => {
|
|
|
18458
18222
|
}))
|
|
18459
18223
|
}));
|
|
18460
18224
|
NaxConfigSchema = exports_external.object({
|
|
18461
|
-
version: exports_external.number(),
|
|
18462
|
-
models: ModelMapSchema
|
|
18463
|
-
|
|
18464
|
-
|
|
18465
|
-
|
|
18466
|
-
|
|
18467
|
-
|
|
18468
|
-
|
|
18469
|
-
|
|
18470
|
-
|
|
18471
|
-
|
|
18472
|
-
|
|
18473
|
-
|
|
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
|
+
}),
|
|
18474
18419
|
optimizer: OptimizerConfigSchema.optional(),
|
|
18475
18420
|
plugins: exports_external.array(PluginConfigEntrySchema).optional(),
|
|
18476
18421
|
disabledPlugins: exports_external.array(exports_external.string()).optional(),
|
|
18477
18422
|
hooks: HooksConfigSchema.optional(),
|
|
18478
|
-
interaction: InteractionConfigSchema.optional()
|
|
18479
|
-
|
|
18480
|
-
|
|
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
|
+
}),
|
|
18481
18449
|
prompts: PromptsConfigSchema.optional(),
|
|
18482
18450
|
generate: GenerateConfigSchema.optional(),
|
|
18483
18451
|
project: ProjectProfileSchema.optional(),
|
|
18484
|
-
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")
|
|
18485
18495
|
}).refine((data) => data.version === 1, {
|
|
18486
18496
|
message: "Invalid version: expected 1",
|
|
18487
18497
|
path: ["version"]
|
|
18488
18498
|
});
|
|
18489
18499
|
});
|
|
18490
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
|
+
|
|
18491
18508
|
// src/config/schema.ts
|
|
18492
18509
|
var exports_schema = {};
|
|
18493
18510
|
__export(exports_schema, {
|
|
@@ -19846,9 +19863,9 @@ async function runPlan(binary, options, pidRegistry, buildAllowedEnv2) {
|
|
|
19846
19863
|
modelDef,
|
|
19847
19864
|
prompt: "",
|
|
19848
19865
|
modelTier: options.modelTier || "balanced",
|
|
19849
|
-
timeoutSeconds: 600
|
|
19866
|
+
timeoutSeconds: options.timeoutSeconds ?? 600
|
|
19850
19867
|
};
|
|
19851
|
-
const
|
|
19868
|
+
const planTimeoutMs = (options.timeoutSeconds ?? 600) * 1000;
|
|
19852
19869
|
if (options.interactive) {
|
|
19853
19870
|
const proc = Bun.spawn(cmd, {
|
|
19854
19871
|
cwd: options.workdir,
|
|
@@ -19860,7 +19877,7 @@ async function runPlan(binary, options, pidRegistry, buildAllowedEnv2) {
|
|
|
19860
19877
|
await pidRegistry.register(proc.pid);
|
|
19861
19878
|
let exitCode;
|
|
19862
19879
|
try {
|
|
19863
|
-
const timeoutResult = await withProcessTimeout(proc,
|
|
19880
|
+
const timeoutResult = await withProcessTimeout(proc, planTimeoutMs, {
|
|
19864
19881
|
graceMs: 5000
|
|
19865
19882
|
});
|
|
19866
19883
|
exitCode = timeoutResult.exitCode;
|
|
@@ -19886,7 +19903,7 @@ async function runPlan(binary, options, pidRegistry, buildAllowedEnv2) {
|
|
|
19886
19903
|
await pidRegistry.register(proc.pid);
|
|
19887
19904
|
let exitCode;
|
|
19888
19905
|
try {
|
|
19889
|
-
const timeoutResult = await withProcessTimeout(proc,
|
|
19906
|
+
const timeoutResult = await withProcessTimeout(proc, planTimeoutMs, {
|
|
19890
19907
|
graceMs: 5000
|
|
19891
19908
|
});
|
|
19892
19909
|
exitCode = timeoutResult.exitCode;
|
|
@@ -20680,26 +20697,179 @@ var init_path_security = () => {};
|
|
|
20680
20697
|
import { homedir as homedir2 } from "os";
|
|
20681
20698
|
import { join as join3, resolve as resolve2 } from "path";
|
|
20682
20699
|
function globalConfigDir() {
|
|
20700
|
+
const override = process.env[GLOBAL_CONFIG_DIR_ENV];
|
|
20701
|
+
if (override)
|
|
20702
|
+
return override;
|
|
20683
20703
|
return join3(homedir2(), ".nax");
|
|
20684
20704
|
}
|
|
20685
|
-
|
|
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";
|
|
20686
20709
|
var init_paths = () => {};
|
|
20687
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
|
+
|
|
20688
20858
|
// src/config/loader.ts
|
|
20689
20859
|
import { existsSync as existsSync4 } from "fs";
|
|
20690
|
-
import { basename, dirname, join as
|
|
20860
|
+
import { basename, dirname, join as join5, resolve as resolve3 } from "path";
|
|
20691
20861
|
function globalConfigPath() {
|
|
20692
|
-
return
|
|
20862
|
+
return join5(globalConfigDir(), "config.json");
|
|
20693
20863
|
}
|
|
20694
20864
|
function findProjectDir(startDir = process.cwd()) {
|
|
20695
20865
|
let dir = resolve3(startDir);
|
|
20696
20866
|
let depth = 0;
|
|
20697
20867
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
20698
|
-
const candidate =
|
|
20699
|
-
if (existsSync4(
|
|
20868
|
+
const candidate = join5(dir, PROJECT_NAX_DIR);
|
|
20869
|
+
if (existsSync4(join5(candidate, "config.json"))) {
|
|
20700
20870
|
return candidate;
|
|
20701
20871
|
}
|
|
20702
|
-
const parent =
|
|
20872
|
+
const parent = join5(dir, "..");
|
|
20703
20873
|
if (parent === dir)
|
|
20704
20874
|
break;
|
|
20705
20875
|
dir = parent;
|
|
@@ -20742,22 +20912,36 @@ function applyBatchModeCompat(conf) {
|
|
|
20742
20912
|
}
|
|
20743
20913
|
async function loadConfig(startDir, cliOverrides) {
|
|
20744
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
|
+
}
|
|
20745
20923
|
const globalConfRaw = await loadJsonFile(globalConfigPath(), "config");
|
|
20746
20924
|
if (globalConfRaw) {
|
|
20747
|
-
const
|
|
20925
|
+
const { profile: _gProfile, ...globalConfStripped } = globalConfRaw;
|
|
20926
|
+
const globalConf = applyBatchModeCompat(applyRemovedStrategyCompat(globalConfStripped));
|
|
20748
20927
|
rawConfig = deepMergeConfig(rawConfig, globalConf);
|
|
20749
20928
|
}
|
|
20750
|
-
const projDir = startDir ? basename(startDir) === PROJECT_NAX_DIR ? startDir : findProjectDir(startDir) : findProjectDir();
|
|
20751
20929
|
if (projDir) {
|
|
20752
|
-
const projConf = await loadJsonFile(
|
|
20930
|
+
const projConf = await loadJsonFile(join5(projDir, "config.json"), "config");
|
|
20753
20931
|
if (projConf) {
|
|
20754
|
-
const
|
|
20932
|
+
const { profile: _pProfile, ...projConfStripped } = projConf;
|
|
20933
|
+
const resolvedProjConf = applyBatchModeCompat(applyRemovedStrategyCompat(projConfStripped));
|
|
20755
20934
|
rawConfig = deepMergeConfig(rawConfig, resolvedProjConf);
|
|
20756
20935
|
}
|
|
20757
20936
|
}
|
|
20758
20937
|
if (cliOverrides) {
|
|
20759
20938
|
rawConfig = deepMergeConfig(rawConfig, cliOverrides);
|
|
20760
20939
|
}
|
|
20940
|
+
rawConfig.profile = profileName;
|
|
20941
|
+
const hasMergedConfigs = globalConfRaw || projDir !== null || cliOverrides !== undefined || profileName !== "default";
|
|
20942
|
+
if (!hasMergedConfigs) {
|
|
20943
|
+
return structuredClone(DEFAULT_CONFIG);
|
|
20944
|
+
}
|
|
20761
20945
|
const result = NaxConfigSchema.safeParse(rawConfig);
|
|
20762
20946
|
if (!result.success) {
|
|
20763
20947
|
const errors3 = result.error.issues.map((err) => {
|
|
@@ -20779,7 +20963,7 @@ async function loadConfigForWorkdir(rootConfigPath, packageDir) {
|
|
|
20779
20963
|
return rootConfig;
|
|
20780
20964
|
}
|
|
20781
20965
|
const repoRoot = dirname(rootNaxDir);
|
|
20782
|
-
const packageConfigPath =
|
|
20966
|
+
const packageConfigPath = join5(repoRoot, PROJECT_NAX_DIR, "mono", packageDir, "config.json");
|
|
20783
20967
|
const packageOverride = await loadJsonFile(packageConfigPath, "config");
|
|
20784
20968
|
if (!packageOverride) {
|
|
20785
20969
|
logger.debug("config", "Per-package config not found \u2014 falling back to root config", {
|
|
@@ -20796,6 +20980,7 @@ var init_loader = __esm(() => {
|
|
|
20796
20980
|
init_json_file();
|
|
20797
20981
|
init_path_security();
|
|
20798
20982
|
init_paths();
|
|
20983
|
+
init_profile();
|
|
20799
20984
|
init_schema();
|
|
20800
20985
|
});
|
|
20801
20986
|
// src/config/index.ts
|
|
@@ -20804,6 +20989,7 @@ var init_config = __esm(() => {
|
|
|
20804
20989
|
init_loader();
|
|
20805
20990
|
init_path_security();
|
|
20806
20991
|
init_paths();
|
|
20992
|
+
init_profile();
|
|
20807
20993
|
});
|
|
20808
20994
|
|
|
20809
20995
|
// src/utils/errors.ts
|
|
@@ -20989,7 +21175,7 @@ __export(exports_generator, {
|
|
|
20989
21175
|
acceptanceTestFilename: () => acceptanceTestFilename,
|
|
20990
21176
|
_generatorPRDDeps: () => _generatorPRDDeps
|
|
20991
21177
|
});
|
|
20992
|
-
import { join as
|
|
21178
|
+
import { join as join6 } from "path";
|
|
20993
21179
|
function skeletonImportLine(testFramework) {
|
|
20994
21180
|
if (!testFramework)
|
|
20995
21181
|
return `import { describe, test, expect } from "bun:test";`;
|
|
@@ -21085,7 +21271,7 @@ Rules:
|
|
|
21085
21271
|
- Every test MUST have real assertions that PASS when the feature is correctly implemented and FAIL when it is broken
|
|
21086
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.
|
|
21087
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.
|
|
21088
|
-
- **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).`;
|
|
21089
21275
|
const prompt = basePrompt;
|
|
21090
21276
|
logger.info("acceptance", "Generating tests from PRD refined criteria", { count: refinedCriteria.length });
|
|
21091
21277
|
const completeResult = await (options.adapter ?? _generatorPRDDeps.adapter).complete(prompt, {
|
|
@@ -21104,7 +21290,7 @@ Rules:
|
|
|
21104
21290
|
outputPreview: rawOutput.slice(0, 300)
|
|
21105
21291
|
});
|
|
21106
21292
|
if (!testCode) {
|
|
21107
|
-
const targetPath =
|
|
21293
|
+
const targetPath = join6(options.workdir, ".nax", "features", options.featureName, resolveAcceptanceTestFile(options.language, options.config?.acceptance?.testPath));
|
|
21108
21294
|
let recoveryFailed = false;
|
|
21109
21295
|
logger.debug("acceptance", "BUG-076 recovery: checking for agent-written file", { targetPath });
|
|
21110
21296
|
try {
|
|
@@ -21159,7 +21345,7 @@ Rules:
|
|
|
21159
21345
|
testable: c.testable,
|
|
21160
21346
|
storyId: c.storyId
|
|
21161
21347
|
})), null, 2);
|
|
21162
|
-
await _generatorPRDDeps.writeFile(
|
|
21348
|
+
await _generatorPRDDeps.writeFile(join6(options.featureDir, "acceptance-refined.json"), refinedJsonContent);
|
|
21163
21349
|
return { testCode, criteria };
|
|
21164
21350
|
}
|
|
21165
21351
|
function parseAcceptanceCriteria(specContent) {
|
|
@@ -22156,7 +22342,7 @@ var package_default;
|
|
|
22156
22342
|
var init_package = __esm(() => {
|
|
22157
22343
|
package_default = {
|
|
22158
22344
|
name: "@nathapp/nax",
|
|
22159
|
-
version: "0.57.
|
|
22345
|
+
version: "0.57.1",
|
|
22160
22346
|
description: "AI Coding Agent Orchestrator \u2014 loops until done",
|
|
22161
22347
|
type: "module",
|
|
22162
22348
|
bin: {
|
|
@@ -22235,8 +22421,8 @@ var init_version = __esm(() => {
|
|
|
22235
22421
|
NAX_VERSION = package_default.version;
|
|
22236
22422
|
NAX_COMMIT = (() => {
|
|
22237
22423
|
try {
|
|
22238
|
-
if (/^[0-9a-f]{6,10}$/.test("
|
|
22239
|
-
return "
|
|
22424
|
+
if (/^[0-9a-f]{6,10}$/.test("39861723"))
|
|
22425
|
+
return "39861723";
|
|
22240
22426
|
} catch {}
|
|
22241
22427
|
try {
|
|
22242
22428
|
const result = Bun.spawnSync(["git", "rev-parse", "--short", "HEAD"], {
|
|
@@ -22383,6 +22569,27 @@ var init_prd = __esm(() => {
|
|
|
22383
22569
|
PRD_MAX_FILE_SIZE = 5 * 1024 * 1024;
|
|
22384
22570
|
});
|
|
22385
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
|
+
|
|
22386
22593
|
// src/debate/prompts.ts
|
|
22387
22594
|
function buildCritiquePrompt(taskPrompt, allProposals, debaterIndex) {
|
|
22388
22595
|
const othersProposals = allProposals.filter((_, i) => i !== debaterIndex);
|
|
@@ -22489,7 +22696,7 @@ var DEFAULT_FALLBACK_AGENT = "claude";
|
|
|
22489
22696
|
var init_resolvers = () => {};
|
|
22490
22697
|
|
|
22491
22698
|
// src/debate/session.ts
|
|
22492
|
-
import { join as
|
|
22699
|
+
import { join as join12 } from "path";
|
|
22493
22700
|
function resolveDebaterModel(debater, config2) {
|
|
22494
22701
|
const tier = debater.model ?? "fast";
|
|
22495
22702
|
if (!config2?.models)
|
|
@@ -22523,10 +22730,11 @@ function modelTierFromDebater(debater) {
|
|
|
22523
22730
|
function isTierLabel(value) {
|
|
22524
22731
|
return value === "fast" || value === "balanced" || value === "powerful";
|
|
22525
22732
|
}
|
|
22526
|
-
async function runComplete(adapter, prompt, options, modelTier) {
|
|
22733
|
+
async function runComplete(adapter, prompt, options, modelTier, timeoutMs) {
|
|
22527
22734
|
return adapter.complete(prompt, {
|
|
22528
22735
|
...options,
|
|
22529
|
-
modelTier
|
|
22736
|
+
modelTier,
|
|
22737
|
+
...timeoutMs !== undefined && { timeoutMs }
|
|
22530
22738
|
});
|
|
22531
22739
|
}
|
|
22532
22740
|
|
|
@@ -22538,6 +22746,9 @@ class DebateSession {
|
|
|
22538
22746
|
workdir;
|
|
22539
22747
|
featureName;
|
|
22540
22748
|
timeoutSeconds;
|
|
22749
|
+
get timeoutMs() {
|
|
22750
|
+
return this.timeoutSeconds * 1000;
|
|
22751
|
+
}
|
|
22541
22752
|
constructor(opts) {
|
|
22542
22753
|
this.storyId = opts.storyId;
|
|
22543
22754
|
this.stage = opts.stage;
|
|
@@ -22545,7 +22756,7 @@ class DebateSession {
|
|
|
22545
22756
|
this.config = opts.config;
|
|
22546
22757
|
this.workdir = opts.workdir ?? process.cwd();
|
|
22547
22758
|
this.featureName = opts.featureName ?? opts.stage;
|
|
22548
|
-
this.timeoutSeconds = opts.timeoutSeconds ?? opts.
|
|
22759
|
+
this.timeoutSeconds = opts.timeoutSeconds ?? opts.stageConfig.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS;
|
|
22549
22760
|
}
|
|
22550
22761
|
pipelineStageForDebate() {
|
|
22551
22762
|
switch (this.stage) {
|
|
@@ -22650,7 +22861,10 @@ class DebateSession {
|
|
|
22650
22861
|
stage: this.stage,
|
|
22651
22862
|
debaters: resolved.map((r) => r.debater.agent)
|
|
22652
22863
|
});
|
|
22653
|
-
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);
|
|
22654
22868
|
const successfulProposals = proposalSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
22655
22869
|
for (const r of proposalSettled) {
|
|
22656
22870
|
if (r.status === "fulfilled") {
|
|
@@ -22730,7 +22944,7 @@ class DebateSession {
|
|
|
22730
22944
|
let critiqueOutputs = [];
|
|
22731
22945
|
if (config2.rounds > 1) {
|
|
22732
22946
|
const proposalOutputs2 = successfulProposals.map((s) => s.output);
|
|
22733
|
-
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);
|
|
22734
22948
|
for (const r of critiqueSettled) {
|
|
22735
22949
|
if (r.status === "fulfilled") {
|
|
22736
22950
|
totalCostUsd += r.value.cost;
|
|
@@ -22780,13 +22994,17 @@ class DebateSession {
|
|
|
22780
22994
|
stage: this.stage,
|
|
22781
22995
|
debaters: resolved.map((r) => r.debater.agent)
|
|
22782
22996
|
});
|
|
22783
|
-
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, {
|
|
22784
23001
|
model: resolveDebaterModel(debater, this.config),
|
|
22785
23002
|
featureName: this.stage,
|
|
22786
23003
|
config: this.config,
|
|
22787
23004
|
storyId: this.storyId,
|
|
22788
|
-
sessionRole:
|
|
22789
|
-
|
|
23005
|
+
sessionRole: `debate-proposal-${i}`,
|
|
23006
|
+
timeoutMs: this.timeoutMs
|
|
23007
|
+
}, modelTierFromDebater(debater)).then((result) => ({ debater, adapter, output: result.output, cost: result.costUsd }))), concurrencyLimit);
|
|
22790
23008
|
const successful = proposalSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
22791
23009
|
for (const r of proposalSettled) {
|
|
22792
23010
|
if (r.status === "fulfilled") {
|
|
@@ -22839,7 +23057,8 @@ class DebateSession {
|
|
|
22839
23057
|
featureName: this.stage,
|
|
22840
23058
|
config: this.config,
|
|
22841
23059
|
storyId: this.storyId,
|
|
22842
|
-
sessionRole: "debate-fallback"
|
|
23060
|
+
sessionRole: "debate-fallback",
|
|
23061
|
+
timeoutMs: this.timeoutMs
|
|
22843
23062
|
}, modelTierFromDebater(fallbackDebater));
|
|
22844
23063
|
totalCostUsd += fallbackResult.costUsd;
|
|
22845
23064
|
logger?.info("debate", "debate:result", {
|
|
@@ -22864,13 +23083,14 @@ class DebateSession {
|
|
|
22864
23083
|
let critiqueOutputs = [];
|
|
22865
23084
|
if (config2.rounds > 1) {
|
|
22866
23085
|
const proposalOutputs2 = successful.map((p) => p.output);
|
|
22867
|
-
const critiqueSettled = await
|
|
23086
|
+
const critiqueSettled = await allSettledBounded(successful.map(({ debater, adapter }, i) => () => runComplete(adapter, buildCritiquePrompt(prompt, proposalOutputs2, i), {
|
|
22868
23087
|
model: resolveDebaterModel(debater, this.config),
|
|
22869
23088
|
featureName: this.stage,
|
|
22870
23089
|
config: this.config,
|
|
22871
23090
|
storyId: this.storyId,
|
|
22872
|
-
sessionRole:
|
|
22873
|
-
|
|
23091
|
+
sessionRole: `debate-critique-${i}`,
|
|
23092
|
+
timeoutMs: this.timeoutMs
|
|
23093
|
+
}, modelTierFromDebater(debater))), concurrencyLimit);
|
|
22874
23094
|
for (const r of critiqueSettled) {
|
|
22875
23095
|
if (r.status === "fulfilled") {
|
|
22876
23096
|
totalCostUsd += r.value.costUsd;
|
|
@@ -22920,8 +23140,11 @@ class DebateSession {
|
|
|
22920
23140
|
stage: this.stage,
|
|
22921
23141
|
debaters: resolved.map((r) => r.debater.agent)
|
|
22922
23142
|
});
|
|
22923
|
-
const
|
|
22924
|
-
|
|
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`);
|
|
22925
23148
|
const debaterPrompt = `${basePrompt}
|
|
22926
23149
|
|
|
22927
23150
|
Write the PRD JSON directly to this file path: ${tempOutputPath}
|
|
@@ -22936,12 +23159,28 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
22936
23159
|
dangerouslySkipPermissions: opts.dangerouslySkipPermissions,
|
|
22937
23160
|
maxInteractionTurns: opts.maxInteractionTurns,
|
|
22938
23161
|
featureName: opts.feature,
|
|
22939
|
-
|
|
23162
|
+
storyId: this.storyId,
|
|
23163
|
+
sessionRole: `plan-${i}`
|
|
22940
23164
|
});
|
|
22941
23165
|
const output = await _debateSessionDeps.readFile(tempOutputPath);
|
|
22942
23166
|
return { debater, adapter, output, cost: 0 };
|
|
22943
|
-
}));
|
|
22944
|
-
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
|
+
}
|
|
22945
23184
|
for (let i = 0;i < successful.length; i++) {
|
|
22946
23185
|
logger?.info("debate", "debate:proposal", {
|
|
22947
23186
|
storyId: this.storyId,
|
|
@@ -23012,7 +23251,8 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
23012
23251
|
model: resolveDebaterModel({ agent: agentName }, this.config),
|
|
23013
23252
|
config: this.config,
|
|
23014
23253
|
storyId: this.storyId,
|
|
23015
|
-
sessionRole: "synthesis"
|
|
23254
|
+
sessionRole: "synthesis",
|
|
23255
|
+
timeoutMs: this.timeoutMs
|
|
23016
23256
|
}
|
|
23017
23257
|
});
|
|
23018
23258
|
return {
|
|
@@ -23034,7 +23274,8 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
23034
23274
|
model: resolveDebaterModel({ agent: agentName }, this.config),
|
|
23035
23275
|
config: this.config,
|
|
23036
23276
|
storyId: this.storyId,
|
|
23037
|
-
sessionRole: "judge"
|
|
23277
|
+
sessionRole: "judge",
|
|
23278
|
+
timeoutMs: this.timeoutMs
|
|
23038
23279
|
}
|
|
23039
23280
|
});
|
|
23040
23281
|
return {
|
|
@@ -23048,7 +23289,7 @@ Do NOT output the JSON to the conversation. Write the file, then reply with a br
|
|
|
23048
23289
|
};
|
|
23049
23290
|
}
|
|
23050
23291
|
}
|
|
23051
|
-
var RESOLVER_FALLBACK_AGENT = "synthesis", _debateSessionDeps;
|
|
23292
|
+
var RESOLVER_FALLBACK_AGENT = "synthesis", _debateSessionDeps, DEFAULT_TIMEOUT_SECONDS = 600;
|
|
23052
23293
|
var init_session = __esm(() => {
|
|
23053
23294
|
init_registry();
|
|
23054
23295
|
init_config();
|
|
@@ -23252,13 +23493,15 @@ class AutoInteractionPlugin {
|
|
|
23252
23493
|
const modelDef = resolveModelForAgent(naxConfig.models, naxConfig.autoMode.defaultAgent, modelTier, naxConfig.autoMode.defaultAgent);
|
|
23253
23494
|
modelArg = modelDef.model;
|
|
23254
23495
|
}
|
|
23496
|
+
const timeoutMs = this.config.naxConfig ? (this.config.naxConfig.execution?.sessionTimeoutSeconds ?? 600) * 1000 : undefined;
|
|
23255
23497
|
const result = await adapter.complete(prompt, {
|
|
23256
23498
|
...modelArg && { model: modelArg },
|
|
23257
23499
|
jsonMode: true,
|
|
23258
23500
|
...this.config.naxConfig && { config: this.config.naxConfig },
|
|
23259
23501
|
featureName: request.featureName,
|
|
23260
23502
|
storyId: request.storyId,
|
|
23261
|
-
sessionRole: "auto"
|
|
23503
|
+
sessionRole: "auto",
|
|
23504
|
+
...timeoutMs !== undefined && { timeoutMs }
|
|
23262
23505
|
});
|
|
23263
23506
|
const output = typeof result === "string" ? result : result.output;
|
|
23264
23507
|
return this.parseResponse(output);
|
|
@@ -25863,59 +26106,6 @@ ${stderr}`;
|
|
|
25863
26106
|
};
|
|
25864
26107
|
});
|
|
25865
26108
|
|
|
25866
|
-
// src/agents/claude/index.ts
|
|
25867
|
-
var init_claude = __esm(() => {
|
|
25868
|
-
init_adapter3();
|
|
25869
|
-
init_execution();
|
|
25870
|
-
});
|
|
25871
|
-
|
|
25872
|
-
// src/agents/shared/validation.ts
|
|
25873
|
-
function validateAgentForTier(agent, tier) {
|
|
25874
|
-
return agent.capabilities.supportedTiers.includes(tier);
|
|
25875
|
-
}
|
|
25876
|
-
function validateAgentFeature(agent, feature) {
|
|
25877
|
-
return agent.capabilities.features.has(feature);
|
|
25878
|
-
}
|
|
25879
|
-
function describeAgentCapabilities(agent) {
|
|
25880
|
-
const tiers = agent.capabilities.supportedTiers.join(",");
|
|
25881
|
-
const features = Array.from(agent.capabilities.features).join(",");
|
|
25882
|
-
const maxTokens = agent.capabilities.maxContextTokens;
|
|
25883
|
-
return `${agent.name}: tiers=[${tiers}], maxTokens=${maxTokens}, features=[${features}]`;
|
|
25884
|
-
}
|
|
25885
|
-
|
|
25886
|
-
// src/agents/index.ts
|
|
25887
|
-
var exports_agents = {};
|
|
25888
|
-
__export(exports_agents, {
|
|
25889
|
-
validateAgentForTier: () => validateAgentForTier,
|
|
25890
|
-
validateAgentFeature: () => validateAgentFeature,
|
|
25891
|
-
parseTokenUsage: () => parseTokenUsage,
|
|
25892
|
-
getInstalledAgents: () => getInstalledAgents,
|
|
25893
|
-
getAllAgentNames: () => getAllAgentNames,
|
|
25894
|
-
getAgentVersions: () => getAgentVersions,
|
|
25895
|
-
getAgentVersion: () => getAgentVersion,
|
|
25896
|
-
getAgent: () => getAgent,
|
|
25897
|
-
formatCostWithConfidence: () => formatCostWithConfidence,
|
|
25898
|
-
estimateCostFromTokenUsage: () => estimateCostFromTokenUsage,
|
|
25899
|
-
estimateCostFromOutput: () => estimateCostFromOutput,
|
|
25900
|
-
estimateCostByDuration: () => estimateCostByDuration,
|
|
25901
|
-
estimateCost: () => estimateCost,
|
|
25902
|
-
describeAgentCapabilities: () => describeAgentCapabilities,
|
|
25903
|
-
checkAgentHealth: () => checkAgentHealth,
|
|
25904
|
-
MODEL_PRICING: () => MODEL_PRICING,
|
|
25905
|
-
CompleteError: () => CompleteError,
|
|
25906
|
-
ClaudeCodeAdapter: () => ClaudeCodeAdapter,
|
|
25907
|
-
COST_RATES: () => COST_RATES,
|
|
25908
|
-
AllAgentsUnavailableError: () => AllAgentsUnavailableError
|
|
25909
|
-
});
|
|
25910
|
-
var init_agents = __esm(() => {
|
|
25911
|
-
init_types2();
|
|
25912
|
-
init_claude();
|
|
25913
|
-
init_registry();
|
|
25914
|
-
init_cost();
|
|
25915
|
-
init_version_detection();
|
|
25916
|
-
init_errors();
|
|
25917
|
-
});
|
|
25918
|
-
|
|
25919
26109
|
// src/pipeline/stages/acceptance-setup.ts
|
|
25920
26110
|
var exports_acceptance_setup = {};
|
|
25921
26111
|
__export(exports_acceptance_setup, {
|
|
@@ -26059,7 +26249,6 @@ ${stderr}` };
|
|
|
26059
26249
|
}
|
|
26060
26250
|
if (shouldGenerate) {
|
|
26061
26251
|
totalCriteria = allCriteria.length;
|
|
26062
|
-
const { getAgent: getAgent2 } = await Promise.resolve().then(() => (init_agents(), exports_agents));
|
|
26063
26252
|
const agent = (ctx.agentGetFn ?? _acceptanceSetupDeps.getAgent)(ctx.config.autoMode.defaultAgent);
|
|
26064
26253
|
let allRefinedCriteria;
|
|
26065
26254
|
if (ctx.config.acceptance.refinement) {
|
|
@@ -26087,7 +26276,7 @@ ${stderr}` };
|
|
|
26087
26276
|
testableCount = allRefinedCriteria.filter((r) => r.testable).length;
|
|
26088
26277
|
for (const [workdir, group] of workdirGroups) {
|
|
26089
26278
|
const packageDir = workdir ? path5.join(ctx.workdir, workdir) : ctx.workdir;
|
|
26090
|
-
const testPath = path5.join(packageDir, resolveAcceptanceTestFile(language, testPathConfig));
|
|
26279
|
+
const testPath = path5.join(packageDir, ".nax", "features", featureName, resolveAcceptanceTestFile(language, testPathConfig));
|
|
26091
26280
|
const groupStoryIds = new Set(group.stories.map((s) => s.id));
|
|
26092
26281
|
const groupRefined = allRefinedCriteria.filter((r) => groupStoryIds.has(r.storyId));
|
|
26093
26282
|
const result = await _acceptanceSetupDeps.generate(group.stories, groupRefined, {
|
|
@@ -26144,6 +26333,59 @@ ${stderr}` };
|
|
|
26144
26333
|
};
|
|
26145
26334
|
});
|
|
26146
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
|
+
|
|
26147
26389
|
// src/quality/runner.ts
|
|
26148
26390
|
var {spawn: spawn2 } = globalThis.Bun;
|
|
26149
26391
|
async function runQualityCommand(opts) {
|
|
@@ -26233,6 +26475,59 @@ var init_quality = __esm(() => {
|
|
|
26233
26475
|
init_runner2();
|
|
26234
26476
|
});
|
|
26235
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
|
+
|
|
26236
26531
|
// src/pipeline/event-bus.ts
|
|
26237
26532
|
class PipelineEventBus {
|
|
26238
26533
|
subscribers = new Map;
|
|
@@ -26684,6 +26979,16 @@ async function runSemanticReview(workdir, storyGitRef, story, semanticConfig, mo
|
|
|
26684
26979
|
const needsTruncation = rawDiff.length > DIFF_CAP_BYTES;
|
|
26685
26980
|
const stat = needsTruncation ? await collectDiffStat(workdir, effectiveRef) : undefined;
|
|
26686
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
|
+
}
|
|
26687
26992
|
const agent = modelResolver(semanticConfig.modelTier);
|
|
26688
26993
|
if (!agent) {
|
|
26689
26994
|
logger?.warn("semantic", "No agent available for semantic review \u2014 skipping", {
|
|
@@ -27178,7 +27483,7 @@ __export(exports_review, {
|
|
|
27178
27483
|
reviewStage: () => reviewStage,
|
|
27179
27484
|
_reviewDeps: () => _reviewDeps
|
|
27180
27485
|
});
|
|
27181
|
-
import { join as
|
|
27486
|
+
import { join as join18 } from "path";
|
|
27182
27487
|
var reviewStage, _reviewDeps;
|
|
27183
27488
|
var init_review = __esm(() => {
|
|
27184
27489
|
init_agents();
|
|
@@ -27192,7 +27497,7 @@ var init_review = __esm(() => {
|
|
|
27192
27497
|
const logger = getLogger();
|
|
27193
27498
|
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
27194
27499
|
logger.info("review", "Running review phase", { storyId: ctx.story.id });
|
|
27195
|
-
const effectiveWorkdir = ctx.story.workdir ?
|
|
27500
|
+
const effectiveWorkdir = ctx.story.workdir ? join18(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
27196
27501
|
const agentResolver = ctx.agentGetFn ?? getAgent;
|
|
27197
27502
|
const agentName = effectiveConfig.autoMode?.defaultAgent;
|
|
27198
27503
|
const modelResolver = (_tier) => agentName ? agentResolver(agentName) ?? null : null;
|
|
@@ -27244,7 +27549,7 @@ var init_review = __esm(() => {
|
|
|
27244
27549
|
});
|
|
27245
27550
|
|
|
27246
27551
|
// src/pipeline/stages/autofix.ts
|
|
27247
|
-
import { join as
|
|
27552
|
+
import { join as join19 } from "path";
|
|
27248
27553
|
async function recheckReview(ctx) {
|
|
27249
27554
|
const { reviewStage: reviewStage2 } = await Promise.resolve().then(() => (init_review(), exports_review));
|
|
27250
27555
|
if (!reviewStage2.enabled(ctx))
|
|
@@ -27277,11 +27582,38 @@ Fix ALL errors listed above. Do NOT change test files or test behavior.
|
|
|
27277
27582
|
Do NOT add new features \u2014 only fix the quality check errors.
|
|
27278
27583
|
Commit your fixes when done.${scopeConstraint}`;
|
|
27279
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
|
+
}
|
|
27280
27610
|
async function runAgentRectification(ctx) {
|
|
27281
27611
|
const logger = getLogger();
|
|
27282
27612
|
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
27283
27613
|
const maxPerCycle = effectiveConfig.quality.autofix?.maxAttempts ?? 2;
|
|
27284
27614
|
const maxTotal = effectiveConfig.quality.autofix?.maxTotalAttempts ?? 10;
|
|
27615
|
+
const rethinkAtAttempt = effectiveConfig.quality.autofix?.rethinkAtAttempt ?? 2;
|
|
27616
|
+
const urgencyAtAttempt = effectiveConfig.quality.autofix?.urgencyAtAttempt ?? 3;
|
|
27285
27617
|
const consumed = ctx.autofixAttempt ?? 0;
|
|
27286
27618
|
const failedChecks = collectFailedChecks(ctx);
|
|
27287
27619
|
if (failedChecks.length === 0) {
|
|
@@ -27298,56 +27630,90 @@ async function runAgentRectification(ctx) {
|
|
|
27298
27630
|
}
|
|
27299
27631
|
const remainingBudget = maxTotal - consumed;
|
|
27300
27632
|
const maxAttempts = Math.min(maxPerCycle, remainingBudget);
|
|
27301
|
-
|
|
27633
|
+
const agentGetFn = ctx.agentGetFn ?? _autofixDeps.getAgent;
|
|
27634
|
+
const loopState = {
|
|
27635
|
+
attempt: 0,
|
|
27636
|
+
failedChecks
|
|
27637
|
+
};
|
|
27638
|
+
return runSharedRectificationLoop({
|
|
27639
|
+
stage: "autofix",
|
|
27302
27640
|
storyId: ctx.story.id,
|
|
27303
|
-
failedChecks: failedChecks.map((c) => c.check),
|
|
27304
27641
|
maxAttempts,
|
|
27305
|
-
|
|
27306
|
-
|
|
27307
|
-
|
|
27308
|
-
|
|
27309
|
-
for (let attempt = 1;attempt <= maxAttempts; attempt++) {
|
|
27310
|
-
ctx.autofixAttempt = consumed + attempt;
|
|
27311
|
-
logger.info("autofix", `Agent rectification attempt ${ctx.autofixAttempt}/${maxTotal}`, { storyId: ctx.story.id });
|
|
27312
|
-
const agent = agentGetFn(ctx.config.autoMode.defaultAgent);
|
|
27313
|
-
if (!agent) {
|
|
27314
|
-
logger.error("autofix", "Agent not found \u2014 cannot run agent rectification", { storyId: ctx.story.id });
|
|
27315
|
-
return false;
|
|
27316
|
-
}
|
|
27317
|
-
const prompt = buildReviewRectificationPrompt(failedChecks, ctx.story);
|
|
27318
|
-
const modelTier = ctx.story.routing?.modelTier ?? ctx.config.autoMode.escalation.tierOrder[0]?.tier ?? "balanced";
|
|
27319
|
-
const modelDef = resolveModelForAgent(ctx.config.models, ctx.routing.agent ?? ctx.config.autoMode.defaultAgent, modelTier, ctx.config.autoMode.defaultAgent);
|
|
27320
|
-
const rectificationWorkdir = ctx.story.workdir ? join18(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
27321
|
-
await agent.run({
|
|
27322
|
-
prompt,
|
|
27323
|
-
workdir: rectificationWorkdir,
|
|
27324
|
-
modelTier,
|
|
27325
|
-
modelDef,
|
|
27326
|
-
timeoutSeconds: ctx.config.execution.sessionTimeoutSeconds,
|
|
27327
|
-
dangerouslySkipPermissions: resolvePermissions(ctx.config, "rectification").skipPermissions,
|
|
27328
|
-
pipelineStage: "rectification",
|
|
27329
|
-
config: ctx.config,
|
|
27330
|
-
maxInteractionTurns: ctx.config.agent?.maxInteractionTurns,
|
|
27642
|
+
state: loopState,
|
|
27643
|
+
logger,
|
|
27644
|
+
startMessage: "Starting agent rectification for review failures",
|
|
27645
|
+
startData: {
|
|
27331
27646
|
storyId: ctx.story.id,
|
|
27332
|
-
|
|
27333
|
-
|
|
27334
|
-
|
|
27335
|
-
|
|
27336
|
-
|
|
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}`, {
|
|
27337
27703
|
storyId: ctx.story.id
|
|
27338
27704
|
});
|
|
27339
|
-
|
|
27705
|
+
},
|
|
27706
|
+
onLoopEnd: (state) => {
|
|
27707
|
+
if (state.attempt >= maxAttempts) {
|
|
27708
|
+
logger.warn("autofix", "Agent rectification exhausted", { storyId: ctx.story.id });
|
|
27709
|
+
}
|
|
27340
27710
|
}
|
|
27341
|
-
|
|
27342
|
-
if (
|
|
27343
|
-
|
|
27711
|
+
}).catch((error48) => {
|
|
27712
|
+
if (error48 instanceof Error && error48.message === "AUTOFIX_AGENT_NOT_FOUND") {
|
|
27713
|
+
return false;
|
|
27344
27714
|
}
|
|
27345
|
-
|
|
27346
|
-
|
|
27347
|
-
});
|
|
27348
|
-
}
|
|
27349
|
-
logger.warn("autofix", "Agent rectification exhausted", { storyId: ctx.story.id });
|
|
27350
|
-
return false;
|
|
27715
|
+
throw error48;
|
|
27716
|
+
});
|
|
27351
27717
|
}
|
|
27352
27718
|
var autofixStage, _autofixDeps;
|
|
27353
27719
|
var init_autofix = __esm(() => {
|
|
@@ -27381,7 +27747,7 @@ var init_autofix = __esm(() => {
|
|
|
27381
27747
|
const effectiveConfig = ctx.effectiveConfig ?? ctx.config;
|
|
27382
27748
|
const lintFixCmd = effectiveConfig.quality.commands.lintFix;
|
|
27383
27749
|
const formatFixCmd = effectiveConfig.quality.commands.formatFix;
|
|
27384
|
-
const effectiveWorkdir = ctx.story.workdir ?
|
|
27750
|
+
const effectiveWorkdir = ctx.story.workdir ? join19(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
27385
27751
|
const failedCheckNames = new Set((reviewResult.checks ?? []).filter((c) => !c.success).map((c) => c.check));
|
|
27386
27752
|
const hasLintFailure = failedCheckNames.has("lint");
|
|
27387
27753
|
logger.info("autofix", "Starting autofix", {
|
|
@@ -27465,10 +27831,10 @@ var init_autofix = __esm(() => {
|
|
|
27465
27831
|
|
|
27466
27832
|
// src/execution/progress.ts
|
|
27467
27833
|
import { appendFile as appendFile2, mkdir } from "fs/promises";
|
|
27468
|
-
import { join as
|
|
27834
|
+
import { join as join20 } from "path";
|
|
27469
27835
|
async function appendProgress(featureDir, storyId, status, message) {
|
|
27470
27836
|
await mkdir(featureDir, { recursive: true });
|
|
27471
|
-
const progressPath =
|
|
27837
|
+
const progressPath = join20(featureDir, "progress.txt");
|
|
27472
27838
|
const timestamp = new Date().toISOString();
|
|
27473
27839
|
const entry = `[${timestamp}] ${storyId} \u2014 ${status.toUpperCase()} \u2014 ${message}
|
|
27474
27840
|
`;
|
|
@@ -27551,7 +27917,7 @@ function estimateTokens(text) {
|
|
|
27551
27917
|
|
|
27552
27918
|
// src/constitution/loader.ts
|
|
27553
27919
|
import { existsSync as existsSync19 } from "fs";
|
|
27554
|
-
import { join as
|
|
27920
|
+
import { join as join21 } from "path";
|
|
27555
27921
|
function truncateToTokens(text, maxTokens) {
|
|
27556
27922
|
const maxChars = maxTokens * 3;
|
|
27557
27923
|
if (text.length <= maxChars) {
|
|
@@ -27573,7 +27939,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
27573
27939
|
}
|
|
27574
27940
|
let combinedContent = "";
|
|
27575
27941
|
if (!config2.skipGlobal) {
|
|
27576
|
-
const globalPath =
|
|
27942
|
+
const globalPath = join21(globalConfigDir(), config2.path);
|
|
27577
27943
|
if (existsSync19(globalPath)) {
|
|
27578
27944
|
const validatedPath = validateFilePath(globalPath, globalConfigDir());
|
|
27579
27945
|
const globalFile = Bun.file(validatedPath);
|
|
@@ -27583,7 +27949,7 @@ async function loadConstitution(projectDir, config2) {
|
|
|
27583
27949
|
}
|
|
27584
27950
|
}
|
|
27585
27951
|
}
|
|
27586
|
-
const projectPath =
|
|
27952
|
+
const projectPath = join21(projectDir, config2.path);
|
|
27587
27953
|
if (existsSync19(projectPath)) {
|
|
27588
27954
|
const validatedPath = validateFilePath(projectPath, projectDir);
|
|
27589
27955
|
const projectFile = Bun.file(validatedPath);
|
|
@@ -28611,7 +28977,7 @@ var init_helpers = __esm(() => {
|
|
|
28611
28977
|
});
|
|
28612
28978
|
|
|
28613
28979
|
// src/pipeline/stages/context.ts
|
|
28614
|
-
import { join as
|
|
28980
|
+
import { join as join22 } from "path";
|
|
28615
28981
|
var contextStage;
|
|
28616
28982
|
var init_context2 = __esm(() => {
|
|
28617
28983
|
init_helpers();
|
|
@@ -28621,7 +28987,7 @@ var init_context2 = __esm(() => {
|
|
|
28621
28987
|
enabled: () => true,
|
|
28622
28988
|
async execute(ctx) {
|
|
28623
28989
|
const logger = getLogger();
|
|
28624
|
-
const packageWorkdir = ctx.story.workdir ?
|
|
28990
|
+
const packageWorkdir = ctx.story.workdir ? join22(ctx.workdir, ctx.story.workdir) : undefined;
|
|
28625
28991
|
const result = await buildStoryContextFull(ctx.prd, ctx.story, ctx.config, packageWorkdir);
|
|
28626
28992
|
if (result) {
|
|
28627
28993
|
ctx.contextMarkdown = result.markdown;
|
|
@@ -28755,14 +29121,14 @@ var init_isolation = __esm(() => {
|
|
|
28755
29121
|
|
|
28756
29122
|
// src/context/greenfield.ts
|
|
28757
29123
|
import { readdir } from "fs/promises";
|
|
28758
|
-
import { join as
|
|
29124
|
+
import { join as join23 } from "path";
|
|
28759
29125
|
async function scanForTestFiles(dir, testPattern, isRootCall = true) {
|
|
28760
29126
|
const results = [];
|
|
28761
29127
|
const ignoreDirs = new Set(["node_modules", "dist", "build", ".next", ".git"]);
|
|
28762
29128
|
try {
|
|
28763
29129
|
const entries = await readdir(dir, { withFileTypes: true });
|
|
28764
29130
|
for (const entry of entries) {
|
|
28765
|
-
const fullPath =
|
|
29131
|
+
const fullPath = join23(dir, entry.name);
|
|
28766
29132
|
if (entry.isDirectory()) {
|
|
28767
29133
|
if (ignoreDirs.has(entry.name))
|
|
28768
29134
|
continue;
|
|
@@ -29117,13 +29483,13 @@ function parseTestOutput(output, exitCode) {
|
|
|
29117
29483
|
|
|
29118
29484
|
// src/verification/runners.ts
|
|
29119
29485
|
import { existsSync as existsSync20 } from "fs";
|
|
29120
|
-
import { join as
|
|
29486
|
+
import { join as join24 } from "path";
|
|
29121
29487
|
async function verifyAssets(workingDirectory, expectedFiles) {
|
|
29122
29488
|
if (!expectedFiles || expectedFiles.length === 0)
|
|
29123
29489
|
return { success: true, missingFiles: [] };
|
|
29124
29490
|
const missingFiles = [];
|
|
29125
29491
|
for (const file3 of expectedFiles) {
|
|
29126
|
-
if (!existsSync20(
|
|
29492
|
+
if (!existsSync20(join24(workingDirectory, file3)))
|
|
29127
29493
|
missingFiles.push(file3);
|
|
29128
29494
|
}
|
|
29129
29495
|
if (missingFiles.length > 0) {
|
|
@@ -29227,27 +29593,14 @@ function shouldRetryRectification(state, config2) {
|
|
|
29227
29593
|
return true;
|
|
29228
29594
|
}
|
|
29229
29595
|
function buildEscalationPreamble(attempt, config2) {
|
|
29230
|
-
|
|
29231
|
-
|
|
29232
|
-
|
|
29233
|
-
|
|
29234
|
-
|
|
29235
|
-
|
|
29236
|
-
|
|
29237
|
-
|
|
29238
|
-
attempt,
|
|
29239
|
-
urgencyAtAttempt: urgencyAt,
|
|
29240
|
-
rethinkAtAttempt: rethinkAt,
|
|
29241
|
-
maxRetries: config2.maxRetries
|
|
29242
|
-
});
|
|
29243
|
-
} else {
|
|
29244
|
-
logger?.info("rectification", "Progressive prompt escalation: rethink injected", {
|
|
29245
|
-
attempt,
|
|
29246
|
-
rethinkAtAttempt: rethinkAt,
|
|
29247
|
-
maxRetries: config2.maxRetries
|
|
29248
|
-
});
|
|
29249
|
-
}
|
|
29250
|
-
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
|
|
29251
29604
|
|
|
29252
29605
|
Your previous fix attempt (attempt ${attempt}) did not resolve all failures. **Step back and reconsider your approach.**
|
|
29253
29606
|
|
|
@@ -29256,14 +29609,14 @@ Your previous fix attempt (attempt ${attempt}) did not resolve all failures. **S
|
|
|
29256
29609
|
- Re-read the story context and test failures carefully before making changes.
|
|
29257
29610
|
- Consider: are there missing edge cases, incorrect assumptions, or a design flaw in the implementation?
|
|
29258
29611
|
|
|
29259
|
-
|
|
29260
|
-
|
|
29612
|
+
`,
|
|
29613
|
+
urgencySection: `## \uD83D\uDEA8 Final Rectification Attempt Before Model Escalation
|
|
29261
29614
|
|
|
29262
29615
|
This is attempt ${attempt} \u2014 if the tests still fail after this, the task will escalate to a stronger model tier.
|
|
29263
29616
|
A **completely different approach** is required. Do not repeat what you have already tried.
|
|
29264
29617
|
|
|
29265
|
-
`
|
|
29266
|
-
|
|
29618
|
+
`
|
|
29619
|
+
});
|
|
29267
29620
|
}
|
|
29268
29621
|
function createRectificationPrompt(failures, story, config2, attempt) {
|
|
29269
29622
|
const maxChars = config2?.maxFailureSummaryChars ?? 2000;
|
|
@@ -29523,77 +29876,112 @@ async function runRectificationLoop(story, config2, workdir, agent, implementerT
|
|
|
29523
29876
|
storyId: story.id,
|
|
29524
29877
|
sessionName: rectificationSessionName
|
|
29525
29878
|
});
|
|
29526
|
-
|
|
29527
|
-
rectificationState
|
|
29528
|
-
|
|
29529
|
-
|
|
29530
|
-
|
|
29531
|
-
|
|
29532
|
-
|
|
29533
|
-
|
|
29534
|
-
|
|
29535
|
-
|
|
29536
|
-
|
|
29537
|
-
|
|
29538
|
-
dangerouslySkipPermissions: resolvePermissions(config2, "rectification").skipPermissions,
|
|
29539
|
-
pipelineStage: "rectification",
|
|
29540
|
-
config: config2,
|
|
29541
|
-
maxInteractionTurns: config2.agent?.maxInteractionTurns,
|
|
29542
|
-
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: {
|
|
29543
29891
|
storyId: story.id,
|
|
29544
|
-
|
|
29545
|
-
|
|
29546
|
-
|
|
29547
|
-
}
|
|
29548
|
-
|
|
29549
|
-
|
|
29550
|
-
|
|
29551
|
-
|
|
29552
|
-
|
|
29553
|
-
|
|
29554
|
-
|
|
29555
|
-
|
|
29556
|
-
|
|
29557
|
-
|
|
29558
|
-
|
|
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,
|
|
29559
29916
|
storyId: story.id,
|
|
29560
|
-
|
|
29561
|
-
|
|
29917
|
+
sessionRole: "implementer",
|
|
29918
|
+
acpSessionName: rectificationSessionName,
|
|
29919
|
+
keepSessionOpen: !isLastAttempt
|
|
29562
29920
|
});
|
|
29563
|
-
|
|
29564
|
-
|
|
29565
|
-
|
|
29566
|
-
|
|
29567
|
-
|
|
29568
|
-
|
|
29569
|
-
|
|
29570
|
-
|
|
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
|
|
29571
29954
|
});
|
|
29572
|
-
|
|
29573
|
-
|
|
29574
|
-
|
|
29575
|
-
|
|
29576
|
-
|
|
29577
|
-
|
|
29578
|
-
|
|
29579
|
-
|
|
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", {
|
|
29580
29977
|
storyId: story.id,
|
|
29581
|
-
attempt
|
|
29978
|
+
attempt,
|
|
29979
|
+
remainingFailures: state.currentFailures
|
|
29582
29980
|
});
|
|
29583
|
-
return true;
|
|
29584
29981
|
}
|
|
29585
|
-
|
|
29586
|
-
|
|
29587
|
-
|
|
29588
|
-
testSummary.failures = newTestSummary.failures;
|
|
29589
|
-
testSummary.failed = newTestSummary.failed;
|
|
29590
|
-
testSummary.passed = newTestSummary.passed;
|
|
29591
|
-
}
|
|
29592
|
-
logger.warn("tdd", "Full suite still failing after rectification attempt", {
|
|
29593
|
-
storyId: story.id,
|
|
29594
|
-
attempt: rectificationState.attempt,
|
|
29595
|
-
remainingFailures: rectificationState.currentFailures
|
|
29596
|
-
});
|
|
29982
|
+
});
|
|
29983
|
+
if (fixed) {
|
|
29984
|
+
return true;
|
|
29597
29985
|
}
|
|
29598
29986
|
const finalFullSuite = await _rectificationGateDeps.executeWithTimeout(testCmd, fullSuiteTimeout, undefined, {
|
|
29599
29987
|
cwd: workdir
|
|
@@ -30017,13 +30405,13 @@ var exports_loader = {};
|
|
|
30017
30405
|
__export(exports_loader, {
|
|
30018
30406
|
loadOverride: () => loadOverride
|
|
30019
30407
|
});
|
|
30020
|
-
import { join as
|
|
30408
|
+
import { join as join25 } from "path";
|
|
30021
30409
|
async function loadOverride(role, workdir, config2) {
|
|
30022
30410
|
const overridePath = config2.prompts?.overrides?.[role];
|
|
30023
30411
|
if (!overridePath) {
|
|
30024
30412
|
return null;
|
|
30025
30413
|
}
|
|
30026
|
-
const absolutePath =
|
|
30414
|
+
const absolutePath = join25(workdir, overridePath);
|
|
30027
30415
|
const file3 = Bun.file(absolutePath);
|
|
30028
30416
|
if (!await file3.exists()) {
|
|
30029
30417
|
return null;
|
|
@@ -30882,11 +31270,11 @@ var init_tdd = __esm(() => {
|
|
|
30882
31270
|
|
|
30883
31271
|
// src/pipeline/stages/execution.ts
|
|
30884
31272
|
import { existsSync as existsSync21 } from "fs";
|
|
30885
|
-
import { join as
|
|
31273
|
+
import { join as join26 } from "path";
|
|
30886
31274
|
function resolveStoryWorkdir(repoRoot, storyWorkdir) {
|
|
30887
31275
|
if (!storyWorkdir)
|
|
30888
31276
|
return repoRoot;
|
|
30889
|
-
const resolved =
|
|
31277
|
+
const resolved = join26(repoRoot, storyWorkdir);
|
|
30890
31278
|
if (!existsSync21(resolved)) {
|
|
30891
31279
|
throw new Error(`[execution] story.workdir "${storyWorkdir}" does not exist at "${resolved}"`);
|
|
30892
31280
|
}
|
|
@@ -31556,8 +31944,15 @@ async function _defaultRunDebate(storyId, stageConfig, prompt, config2) {
|
|
|
31556
31944
|
if (resolved.length === 0) {
|
|
31557
31945
|
return { output: null, totalCostUsd: 0 };
|
|
31558
31946
|
}
|
|
31947
|
+
const timeoutMs = (config2?.execution?.sessionTimeoutSeconds ?? 600) * 1000;
|
|
31559
31948
|
const startMs = Date.now();
|
|
31560
|
-
const proposalSettled = await Promise.allSettled(resolved.map(({ debater, adapter }) => adapter.complete(prompt, {
|
|
31949
|
+
const proposalSettled = await Promise.allSettled(resolved.map(({ debater, adapter }) => adapter.complete(prompt, {
|
|
31950
|
+
model: debater.model,
|
|
31951
|
+
config: config2,
|
|
31952
|
+
storyId,
|
|
31953
|
+
sessionRole: "debate-proposal",
|
|
31954
|
+
timeoutMs
|
|
31955
|
+
}).then((out) => typeof out === "string" ? out : out.output)));
|
|
31561
31956
|
const durationMs = Date.now() - startMs;
|
|
31562
31957
|
const successful = proposalSettled.filter((r) => r.status === "fulfilled").map((r) => r.value);
|
|
31563
31958
|
if (successful.length === 0) {
|
|
@@ -31584,167 +31979,202 @@ async function runRectificationLoop2(opts) {
|
|
|
31584
31979
|
currentFailures: testSummary.failed,
|
|
31585
31980
|
lastExitCode: 1
|
|
31586
31981
|
};
|
|
31587
|
-
|
|
31982
|
+
return runSharedRectificationLoop({
|
|
31983
|
+
stage: "rectification",
|
|
31588
31984
|
storyId: story.id,
|
|
31589
|
-
|
|
31590
|
-
|
|
31591
|
-
|
|
31592
|
-
|
|
31593
|
-
|
|
31594
|
-
|
|
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: () => ({
|
|
31595
31996
|
storyId: story.id,
|
|
31596
31997
|
currentFailures: rectificationState.currentFailures
|
|
31597
|
-
})
|
|
31598
|
-
|
|
31599
|
-
|
|
31600
|
-
|
|
31601
|
-
const
|
|
31602
|
-
|
|
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:
|
|
31603
32006
|
|
|
31604
32007
|
${failureSummary}`;
|
|
31605
|
-
|
|
31606
|
-
|
|
31607
|
-
|
|
31608
|
-
|
|
31609
|
-
|
|
31610
|
-
|
|
31611
|
-
|
|
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
|
|
31612
32015
|
|
|
31613
32016
|
${debateResult.output}`;
|
|
31614
|
-
|
|
31615
|
-
|
|
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", {
|
|
31616
32026
|
storyId: story.id,
|
|
31617
|
-
attempt
|
|
32027
|
+
attempt,
|
|
31618
32028
|
event: "fallback"
|
|
31619
32029
|
});
|
|
31620
32030
|
}
|
|
31621
|
-
} catch (err) {
|
|
31622
|
-
logger?.info("rectification", "debate diagnosis fallback \u2014 debate threw error", {
|
|
31623
|
-
storyId: story.id,
|
|
31624
|
-
attempt: rectificationState.attempt,
|
|
31625
|
-
event: "fallback"
|
|
31626
|
-
});
|
|
31627
32031
|
}
|
|
31628
|
-
|
|
31629
|
-
|
|
31630
|
-
|
|
31631
|
-
rectificationPrompt = `${diagnosisPrefix}
|
|
32032
|
+
let rectificationPrompt = createRectificationPrompt(testSummary.failures, story, rectificationConfig, attempt);
|
|
32033
|
+
if (diagnosisPrefix) {
|
|
32034
|
+
rectificationPrompt = `${diagnosisPrefix}
|
|
31632
32035
|
|
|
31633
32036
|
${rectificationPrompt}`;
|
|
31634
|
-
|
|
31635
|
-
|
|
32037
|
+
}
|
|
32038
|
+
if (promptPrefix) {
|
|
32039
|
+
rectificationPrompt = `${promptPrefix}
|
|
31636
32040
|
|
|
31637
32041
|
${rectificationPrompt}`;
|
|
31638
|
-
|
|
31639
|
-
|
|
31640
|
-
|
|
31641
|
-
|
|
31642
|
-
|
|
31643
|
-
|
|
31644
|
-
|
|
31645
|
-
|
|
31646
|
-
|
|
31647
|
-
|
|
31648
|
-
|
|
31649
|
-
modelTier,
|
|
31650
|
-
|
|
31651
|
-
|
|
31652
|
-
|
|
31653
|
-
|
|
31654
|
-
|
|
31655
|
-
|
|
31656
|
-
|
|
31657
|
-
|
|
31658
|
-
|
|
31659
|
-
|
|
31660
|
-
|
|
31661
|
-
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,
|
|
31662
32065
|
storyId: story.id,
|
|
31663
|
-
|
|
31664
|
-
cost: agentResult.estimatedCost
|
|
32066
|
+
sessionRole: "implementer"
|
|
31665
32067
|
});
|
|
31666
|
-
|
|
31667
|
-
|
|
31668
|
-
|
|
31669
|
-
|
|
31670
|
-
|
|
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
|
|
31671
32096
|
});
|
|
31672
|
-
|
|
31673
|
-
|
|
31674
|
-
|
|
31675
|
-
|
|
31676
|
-
|
|
31677
|
-
|
|
31678
|
-
|
|
31679
|
-
|
|
31680
|
-
|
|
31681
|
-
|
|
31682
|
-
|
|
31683
|
-
|
|
31684
|
-
|
|
31685
|
-
|
|
31686
|
-
|
|
31687
|
-
|
|
31688
|
-
|
|
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 = {
|
|
31689
32127
|
storyId: story.id,
|
|
31690
|
-
attempt
|
|
31691
|
-
|
|
31692
|
-
|
|
31693
|
-
|
|
31694
|
-
|
|
31695
|
-
|
|
31696
|
-
|
|
31697
|
-
|
|
31698
|
-
|
|
31699
|
-
|
|
31700
|
-
|
|
31701
|
-
|
|
31702
|
-
|
|
31703
|
-
|
|
31704
|
-
|
|
31705
|
-
|
|
31706
|
-
|
|
31707
|
-
|
|
31708
|
-
|
|
31709
|
-
|
|
31710
|
-
|
|
31711
|
-
|
|
31712
|
-
|
|
31713
|
-
|
|
31714
|
-
|
|
31715
|
-
|
|
31716
|
-
|
|
31717
|
-
|
|
31718
|
-
|
|
31719
|
-
|
|
31720
|
-
|
|
31721
|
-
|
|
31722
|
-
|
|
31723
|
-
|
|
31724
|
-
|
|
31725
|
-
|
|
31726
|
-
|
|
31727
|
-
|
|
31728
|
-
const shouldEscalate = rectificationConfig.escalateOnExhaustion !== false && config2.autoMode?.escalation?.enabled === true && rectificationState.attempt >= rectificationConfig.maxRetries && rectificationState.currentFailures > 0;
|
|
31729
|
-
if (shouldEscalate) {
|
|
31730
|
-
const complexity = story.routing?.complexity ?? "medium";
|
|
31731
|
-
const currentTier = config2.autoMode.complexityRouting?.[complexity] || config2.autoMode.escalation.tierOrder[0]?.tier || "balanced";
|
|
31732
|
-
const tierOrder = config2.autoMode.escalation.tierOrder;
|
|
31733
|
-
const escalationResult = _rectificationDeps.escalateTier(currentTier, tierOrder);
|
|
31734
|
-
const escalatedTier = escalationResult?.tier ?? null;
|
|
31735
|
-
const escalatedAgent = escalationResult?.agent;
|
|
31736
|
-
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
|
+
}
|
|
31737
32166
|
const agentName = escalatedAgent ?? story.routing?.agent ?? config2.autoMode.defaultAgent;
|
|
31738
32167
|
const agent = agentGetFn ? agentGetFn(agentName) : _rectificationDeps.getAgent(agentName, config2);
|
|
31739
32168
|
if (!agent) {
|
|
31740
32169
|
return false;
|
|
31741
32170
|
}
|
|
31742
32171
|
const escalatedModelDef = resolveModelForAgent(config2.models, agentName, escalatedTier, config2.autoMode.defaultAgent);
|
|
31743
|
-
let escalationPrompt = createEscalatedRectificationPrompt(testSummary.failures, story,
|
|
31744
|
-
if (promptPrefix)
|
|
32172
|
+
let escalationPrompt = createEscalatedRectificationPrompt(testSummary.failures, story, state.attempt, currentTier, escalatedTier, rectificationConfig);
|
|
32173
|
+
if (promptPrefix) {
|
|
31745
32174
|
escalationPrompt = `${promptPrefix}
|
|
31746
32175
|
|
|
31747
32176
|
${escalationPrompt}`;
|
|
32177
|
+
}
|
|
31748
32178
|
const escalationRunResult = await agent.run({
|
|
31749
32179
|
prompt: escalationPrompt,
|
|
31750
32180
|
workdir,
|
|
@@ -31787,9 +32217,14 @@ ${escalationPrompt}`;
|
|
|
31787
32217
|
return true;
|
|
31788
32218
|
}
|
|
31789
32219
|
logger?.warn("rectification", "escalated rectification also failed", { storyId: story.id, escalatedTier });
|
|
32220
|
+
return false;
|
|
31790
32221
|
}
|
|
31791
|
-
}
|
|
31792
|
-
|
|
32222
|
+
}).catch((error48) => {
|
|
32223
|
+
if (error48 instanceof Error && error48.message === "RECTIFICATION_AGENT_NOT_FOUND") {
|
|
32224
|
+
return false;
|
|
32225
|
+
}
|
|
32226
|
+
throw error48;
|
|
32227
|
+
});
|
|
31793
32228
|
}
|
|
31794
32229
|
var _rectificationDeps;
|
|
31795
32230
|
var init_rectification_loop = __esm(() => {
|
|
@@ -32450,7 +32885,7 @@ var init_regression2 = __esm(() => {
|
|
|
32450
32885
|
});
|
|
32451
32886
|
|
|
32452
32887
|
// src/pipeline/stages/routing.ts
|
|
32453
|
-
import { join as
|
|
32888
|
+
import { join as join27 } from "path";
|
|
32454
32889
|
var routingStage, _routingDeps;
|
|
32455
32890
|
var init_routing2 = __esm(() => {
|
|
32456
32891
|
init_registry();
|
|
@@ -32487,7 +32922,7 @@ var init_routing2 = __esm(() => {
|
|
|
32487
32922
|
}
|
|
32488
32923
|
const greenfieldDetectionEnabled = effectiveConfig.tdd.greenfieldDetection ?? true;
|
|
32489
32924
|
if (greenfieldDetectionEnabled && routing.testStrategy.startsWith("three-session-tdd")) {
|
|
32490
|
-
const greenfieldScanDir = ctx.story.workdir ?
|
|
32925
|
+
const greenfieldScanDir = ctx.story.workdir ? join27(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
32491
32926
|
const isGreenfield = await _routingDeps.isGreenfieldStory(ctx.story, greenfieldScanDir);
|
|
32492
32927
|
if (isGreenfield) {
|
|
32493
32928
|
logger.info("routing", "Greenfield detected \u2014 forcing test-after strategy", {
|
|
@@ -32539,7 +32974,7 @@ var init_crash_detector = __esm(() => {
|
|
|
32539
32974
|
});
|
|
32540
32975
|
|
|
32541
32976
|
// src/pipeline/stages/verify.ts
|
|
32542
|
-
import { join as
|
|
32977
|
+
import { join as join28 } from "path";
|
|
32543
32978
|
function coerceSmartTestRunner(val) {
|
|
32544
32979
|
if (val === undefined || val === true)
|
|
32545
32980
|
return DEFAULT_SMART_RUNNER_CONFIG2;
|
|
@@ -32555,7 +32990,7 @@ function buildScopedCommand2(testFiles, baseCommand, testScopedTemplate) {
|
|
|
32555
32990
|
}
|
|
32556
32991
|
async function readPackageName(dir) {
|
|
32557
32992
|
try {
|
|
32558
|
-
const content = await Bun.file(
|
|
32993
|
+
const content = await Bun.file(join28(dir, "package.json")).json();
|
|
32559
32994
|
return typeof content.name === "string" ? content.name : null;
|
|
32560
32995
|
} catch {
|
|
32561
32996
|
return null;
|
|
@@ -32600,7 +33035,7 @@ var init_verify = __esm(() => {
|
|
|
32600
33035
|
return { action: "continue" };
|
|
32601
33036
|
}
|
|
32602
33037
|
logger.info("verify", "Running verification", { storyId: ctx.story.id });
|
|
32603
|
-
const effectiveWorkdir = ctx.story.workdir ?
|
|
33038
|
+
const effectiveWorkdir = ctx.story.workdir ? join28(ctx.workdir, ctx.story.workdir) : ctx.workdir;
|
|
32604
33039
|
let effectiveCommand = testCommand;
|
|
32605
33040
|
let isFullSuite = true;
|
|
32606
33041
|
const smartRunnerConfig = coerceSmartTestRunner(effectiveConfig.execution.smartTestRunner);
|
|
@@ -32818,7 +33253,7 @@ __export(exports_init_context, {
|
|
|
32818
33253
|
});
|
|
32819
33254
|
import { existsSync as existsSync24 } from "fs";
|
|
32820
33255
|
import { mkdir as mkdir2 } from "fs/promises";
|
|
32821
|
-
import { basename as basename3, join as
|
|
33256
|
+
import { basename as basename3, join as join32 } from "path";
|
|
32822
33257
|
async function findFiles(dir, maxFiles = 200) {
|
|
32823
33258
|
try {
|
|
32824
33259
|
const proc = Bun.spawnSync([
|
|
@@ -32846,7 +33281,7 @@ async function findFiles(dir, maxFiles = 200) {
|
|
|
32846
33281
|
return [];
|
|
32847
33282
|
}
|
|
32848
33283
|
async function readPackageManifest(projectRoot) {
|
|
32849
|
-
const packageJsonPath =
|
|
33284
|
+
const packageJsonPath = join32(projectRoot, "package.json");
|
|
32850
33285
|
if (!existsSync24(packageJsonPath)) {
|
|
32851
33286
|
return null;
|
|
32852
33287
|
}
|
|
@@ -32864,7 +33299,7 @@ async function readPackageManifest(projectRoot) {
|
|
|
32864
33299
|
}
|
|
32865
33300
|
}
|
|
32866
33301
|
async function readReadmeSnippet(projectRoot) {
|
|
32867
|
-
const readmePath =
|
|
33302
|
+
const readmePath = join32(projectRoot, "README.md");
|
|
32868
33303
|
if (!existsSync24(readmePath)) {
|
|
32869
33304
|
return null;
|
|
32870
33305
|
}
|
|
@@ -32882,7 +33317,7 @@ async function detectEntryPoints(projectRoot) {
|
|
|
32882
33317
|
const candidates = ["src/index.ts", "src/main.ts", "main.go", "src/lib.rs"];
|
|
32883
33318
|
const found = [];
|
|
32884
33319
|
for (const candidate of candidates) {
|
|
32885
|
-
const path12 =
|
|
33320
|
+
const path12 = join32(projectRoot, candidate);
|
|
32886
33321
|
if (existsSync24(path12)) {
|
|
32887
33322
|
found.push(candidate);
|
|
32888
33323
|
}
|
|
@@ -32893,7 +33328,7 @@ async function detectConfigFiles(projectRoot) {
|
|
|
32893
33328
|
const candidates = ["tsconfig.json", "biome.json", "turbo.json", ".env.example"];
|
|
32894
33329
|
const found = [];
|
|
32895
33330
|
for (const candidate of candidates) {
|
|
32896
|
-
const path12 =
|
|
33331
|
+
const path12 = join32(projectRoot, candidate);
|
|
32897
33332
|
if (existsSync24(path12)) {
|
|
32898
33333
|
found.push(candidate);
|
|
32899
33334
|
}
|
|
@@ -33054,8 +33489,8 @@ function generatePackageContextTemplate(packagePath) {
|
|
|
33054
33489
|
}
|
|
33055
33490
|
async function initPackage(repoRoot, packagePath, force = false) {
|
|
33056
33491
|
const logger = getLogger();
|
|
33057
|
-
const naxDir =
|
|
33058
|
-
const contextPath =
|
|
33492
|
+
const naxDir = join32(repoRoot, ".nax", "mono", packagePath);
|
|
33493
|
+
const contextPath = join32(naxDir, "context.md");
|
|
33059
33494
|
if (existsSync24(contextPath) && !force) {
|
|
33060
33495
|
logger.info("init", "Package context.md already exists (use --force to overwrite)", { path: contextPath });
|
|
33061
33496
|
return;
|
|
@@ -33069,8 +33504,8 @@ async function initPackage(repoRoot, packagePath, force = false) {
|
|
|
33069
33504
|
}
|
|
33070
33505
|
async function initContext(projectRoot, options = {}) {
|
|
33071
33506
|
const logger = getLogger();
|
|
33072
|
-
const naxDir =
|
|
33073
|
-
const contextPath =
|
|
33507
|
+
const naxDir = join32(projectRoot, ".nax");
|
|
33508
|
+
const contextPath = join32(naxDir, "context.md");
|
|
33074
33509
|
if (existsSync24(contextPath) && !options.force) {
|
|
33075
33510
|
logger.info("init", "context.md already exists, skipping (use --force to overwrite)", { path: contextPath });
|
|
33076
33511
|
return;
|
|
@@ -33100,7 +33535,7 @@ var init_init_context = __esm(() => {
|
|
|
33100
33535
|
|
|
33101
33536
|
// src/utils/path-security.ts
|
|
33102
33537
|
import { realpathSync as realpathSync3 } from "fs";
|
|
33103
|
-
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";
|
|
33104
33539
|
function safeRealpathForComparison(p) {
|
|
33105
33540
|
try {
|
|
33106
33541
|
return realpathSync3(p);
|
|
@@ -33109,7 +33544,7 @@ function safeRealpathForComparison(p) {
|
|
|
33109
33544
|
if (parent === p)
|
|
33110
33545
|
return normalize2(p);
|
|
33111
33546
|
const resolvedParent = safeRealpathForComparison(parent);
|
|
33112
|
-
return
|
|
33547
|
+
return join33(resolvedParent, p.split("/").pop() ?? "");
|
|
33113
33548
|
}
|
|
33114
33549
|
}
|
|
33115
33550
|
function validateModulePath(modulePath, allowedRoots) {
|
|
@@ -33127,7 +33562,7 @@ function validateModulePath(modulePath, allowedRoots) {
|
|
|
33127
33562
|
} else {
|
|
33128
33563
|
for (let i = 0;i < allowedRoots.length; i++) {
|
|
33129
33564
|
const originalRoot = resolve5(allowedRoots[i]);
|
|
33130
|
-
const absoluteInput = resolve5(
|
|
33565
|
+
const absoluteInput = resolve5(join33(originalRoot, modulePath));
|
|
33131
33566
|
const resolved = safeRealpathForComparison(absoluteInput);
|
|
33132
33567
|
const resolvedRoot = resolvedRoots[i];
|
|
33133
33568
|
if (resolved.startsWith(`${resolvedRoot}/`) || resolved === resolvedRoot) {
|
|
@@ -33663,19 +34098,19 @@ var init_loader4 = __esm(() => {
|
|
|
33663
34098
|
});
|
|
33664
34099
|
|
|
33665
34100
|
// src/hooks/runner.ts
|
|
33666
|
-
import { join as
|
|
34101
|
+
import { join as join48 } from "path";
|
|
33667
34102
|
async function loadHooksConfig(projectDir, globalDir) {
|
|
33668
34103
|
let globalHooks = { hooks: {} };
|
|
33669
34104
|
let projectHooks = { hooks: {} };
|
|
33670
34105
|
let skipGlobal = false;
|
|
33671
|
-
const projectPath =
|
|
34106
|
+
const projectPath = join48(projectDir, "hooks.json");
|
|
33672
34107
|
const projectData = await loadJsonFile(projectPath, "hooks");
|
|
33673
34108
|
if (projectData) {
|
|
33674
34109
|
projectHooks = projectData;
|
|
33675
34110
|
skipGlobal = projectData.skipGlobal ?? false;
|
|
33676
34111
|
}
|
|
33677
34112
|
if (!skipGlobal && globalDir) {
|
|
33678
|
-
const globalPath =
|
|
34113
|
+
const globalPath = join48(globalDir, "hooks.json");
|
|
33679
34114
|
const globalData = await loadJsonFile(globalPath, "hooks");
|
|
33680
34115
|
if (globalData) {
|
|
33681
34116
|
globalHooks = globalData;
|
|
@@ -34214,7 +34649,7 @@ async function diagnoseAcceptanceFailure(agent, options) {
|
|
|
34214
34649
|
reasoning: "diagnosis failed \u2014 falling back to source fix",
|
|
34215
34650
|
confidence: 0
|
|
34216
34651
|
};
|
|
34217
|
-
} catch
|
|
34652
|
+
} catch {
|
|
34218
34653
|
return {
|
|
34219
34654
|
verdict: "source_bug",
|
|
34220
34655
|
reasoning: "diagnosis failed \u2014 falling back to source fix",
|
|
@@ -34324,11 +34759,12 @@ var init_fix_executor = __esm(() => {
|
|
|
34324
34759
|
var exports_acceptance_loop = {};
|
|
34325
34760
|
__export(exports_acceptance_loop, {
|
|
34326
34761
|
runAcceptanceLoop: () => runAcceptanceLoop,
|
|
34762
|
+
regenerateAcceptanceTest: () => regenerateAcceptanceTest,
|
|
34327
34763
|
isTestLevelFailure: () => isTestLevelFailure,
|
|
34328
34764
|
isStubTestFile: () => isStubTestFile,
|
|
34329
34765
|
_acceptanceLoopDeps: () => _acceptanceLoopDeps
|
|
34330
34766
|
});
|
|
34331
|
-
import path14, { join as
|
|
34767
|
+
import path14, { join as join49 } from "path";
|
|
34332
34768
|
function isStubTestFile(content) {
|
|
34333
34769
|
return /expect\s*\(\s*true\s*\)\s*\.\s*toBe\s*\(\s*(?:false|true)\s*\)/.test(content);
|
|
34334
34770
|
}
|
|
@@ -34399,7 +34835,7 @@ async function executeFixStory(ctx, story, prd, iterations) {
|
|
|
34399
34835
|
agent: ctx.config.autoMode.defaultAgent,
|
|
34400
34836
|
iteration: iterations
|
|
34401
34837
|
}), ctx.workdir);
|
|
34402
|
-
const fixEffectiveConfig = story.workdir ? await loadConfigForWorkdir(
|
|
34838
|
+
const fixEffectiveConfig = story.workdir ? await loadConfigForWorkdir(join49(ctx.workdir, ".nax", "config.json"), story.workdir) : ctx.config;
|
|
34403
34839
|
const fixContext = {
|
|
34404
34840
|
config: ctx.config,
|
|
34405
34841
|
effectiveConfig: fixEffectiveConfig,
|
|
@@ -34882,6 +35318,16 @@ async function runDeferredRegression(options) {
|
|
|
34882
35318
|
};
|
|
34883
35319
|
}
|
|
34884
35320
|
const testSummary = _regressionDeps.parseBunTestOutput(fullSuiteResult.output);
|
|
35321
|
+
if (testSummary.failed === 0 && testSummary.passed === 0) {
|
|
35322
|
+
logger?.warn("regression", "No test results parsed from output \u2014 test runner likely crashed or errored (not a regression, accepting as pass)", { output: fullSuiteResult.output.slice(0, 500) });
|
|
35323
|
+
return {
|
|
35324
|
+
success: true,
|
|
35325
|
+
failedTests: 0,
|
|
35326
|
+
passedTests: 0,
|
|
35327
|
+
rectificationAttempts: 0,
|
|
35328
|
+
affectedStories: []
|
|
35329
|
+
};
|
|
35330
|
+
}
|
|
34885
35331
|
const affectedStories = new Set;
|
|
34886
35332
|
const affectedStoriesObjs = new Map;
|
|
34887
35333
|
logger?.warn("regression", "Regression detected", {
|
|
@@ -35202,12 +35648,12 @@ var init_headless_formatter = __esm(() => {
|
|
|
35202
35648
|
// src/pipeline/subscribers/events-writer.ts
|
|
35203
35649
|
import { appendFile as appendFile3, mkdir as mkdir3 } from "fs/promises";
|
|
35204
35650
|
import { homedir as homedir5 } from "os";
|
|
35205
|
-
import { basename as basename6, join as
|
|
35651
|
+
import { basename as basename6, join as join50 } from "path";
|
|
35206
35652
|
function wireEventsWriter(bus, feature, runId, workdir) {
|
|
35207
35653
|
const logger = getSafeLogger();
|
|
35208
35654
|
const project = basename6(workdir);
|
|
35209
|
-
const eventsDir =
|
|
35210
|
-
const eventsFile =
|
|
35655
|
+
const eventsDir = join50(homedir5(), ".nax", "events", project);
|
|
35656
|
+
const eventsFile = join50(eventsDir, "events.jsonl");
|
|
35211
35657
|
let dirReady = false;
|
|
35212
35658
|
const write = (line) => {
|
|
35213
35659
|
return (async () => {
|
|
@@ -35388,12 +35834,12 @@ var init_interaction2 = __esm(() => {
|
|
|
35388
35834
|
// src/pipeline/subscribers/registry.ts
|
|
35389
35835
|
import { mkdir as mkdir4, writeFile } from "fs/promises";
|
|
35390
35836
|
import { homedir as homedir6 } from "os";
|
|
35391
|
-
import { basename as basename7, join as
|
|
35837
|
+
import { basename as basename7, join as join51 } from "path";
|
|
35392
35838
|
function wireRegistry(bus, feature, runId, workdir) {
|
|
35393
35839
|
const logger = getSafeLogger();
|
|
35394
35840
|
const project = basename7(workdir);
|
|
35395
|
-
const runDir =
|
|
35396
|
-
const metaFile =
|
|
35841
|
+
const runDir = join51(homedir6(), ".nax", "runs", `${project}-${feature}-${runId}`);
|
|
35842
|
+
const metaFile = join51(runDir, "meta.json");
|
|
35397
35843
|
const unsub = bus.on("run:started", (_ev) => {
|
|
35398
35844
|
return (async () => {
|
|
35399
35845
|
try {
|
|
@@ -35403,8 +35849,8 @@ function wireRegistry(bus, feature, runId, workdir) {
|
|
|
35403
35849
|
project,
|
|
35404
35850
|
feature,
|
|
35405
35851
|
workdir,
|
|
35406
|
-
statusPath:
|
|
35407
|
-
eventsDir:
|
|
35852
|
+
statusPath: join51(workdir, ".nax", "features", feature, "status.json"),
|
|
35853
|
+
eventsDir: join51(workdir, ".nax", "features", feature, "runs"),
|
|
35408
35854
|
registeredAt: new Date().toISOString()
|
|
35409
35855
|
};
|
|
35410
35856
|
await writeFile(metaFile, JSON.stringify(meta3, null, 2));
|
|
@@ -36056,7 +36502,7 @@ var init_pipeline_result_handler = __esm(() => {
|
|
|
36056
36502
|
});
|
|
36057
36503
|
|
|
36058
36504
|
// src/execution/iteration-runner.ts
|
|
36059
|
-
import { join as
|
|
36505
|
+
import { join as join52 } from "path";
|
|
36060
36506
|
async function runIteration(ctx, prd, selection, iterations, totalCost, allStoryMetrics) {
|
|
36061
36507
|
const logger = getSafeLogger();
|
|
36062
36508
|
const { story, storiesToExecute, routing, isBatchExecution } = selection;
|
|
@@ -36091,7 +36537,7 @@ async function runIteration(ctx, prd, selection, iterations, totalCost, allStory
|
|
|
36091
36537
|
}
|
|
36092
36538
|
}
|
|
36093
36539
|
const accumulatedAttemptCost = (story.priorFailures || []).reduce((sum, f) => sum + (f.cost || 0), 0);
|
|
36094
|
-
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;
|
|
36095
36541
|
const pipelineContext = {
|
|
36096
36542
|
config: ctx.config,
|
|
36097
36543
|
effectiveConfig,
|
|
@@ -36350,13 +36796,13 @@ __export(exports_manager, {
|
|
|
36350
36796
|
});
|
|
36351
36797
|
import { existsSync as existsSync32, symlinkSync } from "fs";
|
|
36352
36798
|
import { mkdir as mkdir5 } from "fs/promises";
|
|
36353
|
-
import { join as
|
|
36799
|
+
import { join as join53 } from "path";
|
|
36354
36800
|
|
|
36355
36801
|
class WorktreeManager {
|
|
36356
36802
|
async ensureGitExcludes(projectRoot) {
|
|
36357
36803
|
const logger = getSafeLogger();
|
|
36358
|
-
const infoDir =
|
|
36359
|
-
const excludePath =
|
|
36804
|
+
const infoDir = join53(projectRoot, ".git", "info");
|
|
36805
|
+
const excludePath = join53(infoDir, "exclude");
|
|
36360
36806
|
try {
|
|
36361
36807
|
await mkdir5(infoDir, { recursive: true });
|
|
36362
36808
|
let existing = "";
|
|
@@ -36383,7 +36829,7 @@ ${missing.join(`
|
|
|
36383
36829
|
}
|
|
36384
36830
|
async create(projectRoot, storyId) {
|
|
36385
36831
|
validateStoryId(storyId);
|
|
36386
|
-
const worktreePath =
|
|
36832
|
+
const worktreePath = join53(projectRoot, ".nax-wt", storyId);
|
|
36387
36833
|
const branchName = `nax/${storyId}`;
|
|
36388
36834
|
try {
|
|
36389
36835
|
const pruneProc = _managerDeps.spawn(["git", "worktree", "prune"], {
|
|
@@ -36424,9 +36870,9 @@ ${missing.join(`
|
|
|
36424
36870
|
}
|
|
36425
36871
|
throw new Error(`Failed to create worktree: ${String(error48)}`);
|
|
36426
36872
|
}
|
|
36427
|
-
const nodeModulesSource =
|
|
36873
|
+
const nodeModulesSource = join53(projectRoot, "node_modules");
|
|
36428
36874
|
if (existsSync32(nodeModulesSource)) {
|
|
36429
|
-
const nodeModulesTarget =
|
|
36875
|
+
const nodeModulesTarget = join53(worktreePath, "node_modules");
|
|
36430
36876
|
try {
|
|
36431
36877
|
symlinkSync(nodeModulesSource, nodeModulesTarget, "dir");
|
|
36432
36878
|
} catch (error48) {
|
|
@@ -36434,9 +36880,9 @@ ${missing.join(`
|
|
|
36434
36880
|
throw new Error(`Failed to symlink node_modules: ${errorMessage(error48)}`);
|
|
36435
36881
|
}
|
|
36436
36882
|
}
|
|
36437
|
-
const envSource =
|
|
36883
|
+
const envSource = join53(projectRoot, ".env");
|
|
36438
36884
|
if (existsSync32(envSource)) {
|
|
36439
|
-
const envTarget =
|
|
36885
|
+
const envTarget = join53(worktreePath, ".env");
|
|
36440
36886
|
try {
|
|
36441
36887
|
symlinkSync(envSource, envTarget, "file");
|
|
36442
36888
|
} catch (error48) {
|
|
@@ -36447,7 +36893,7 @@ ${missing.join(`
|
|
|
36447
36893
|
}
|
|
36448
36894
|
async remove(projectRoot, storyId) {
|
|
36449
36895
|
validateStoryId(storyId);
|
|
36450
|
-
const worktreePath =
|
|
36896
|
+
const worktreePath = join53(projectRoot, ".nax-wt", storyId);
|
|
36451
36897
|
const branchName = `nax/${storyId}`;
|
|
36452
36898
|
try {
|
|
36453
36899
|
const proc = _managerDeps.spawn(["git", "worktree", "remove", worktreePath, "--force"], {
|
|
@@ -37063,13 +37509,25 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
37063
37509
|
prd = await loadPRD(ctx.prdPath);
|
|
37064
37510
|
prdDirty = false;
|
|
37065
37511
|
}
|
|
37512
|
+
const storyCounts = countStories(prd);
|
|
37513
|
+
logger?.debug("execution", "Loop iteration", {
|
|
37514
|
+
iteration: iterations,
|
|
37515
|
+
isComplete: isComplete(prd),
|
|
37516
|
+
passed: storyCounts.passed,
|
|
37517
|
+
pending: storyCounts.pending,
|
|
37518
|
+
failed: storyCounts.failed,
|
|
37519
|
+
total: storyCounts.total
|
|
37520
|
+
});
|
|
37066
37521
|
if (isComplete(prd)) {
|
|
37522
|
+
logger?.debug("execution", "All stories complete \u2014 entering completion path");
|
|
37067
37523
|
if (ctx.interactionChain && isTriggerEnabled("pre-merge", ctx.config)) {
|
|
37068
37524
|
const shouldProceed = await checkPreMerge({ featureName: ctx.feature, totalStories: prd.userStories.length, cost: totalCost }, ctx.config, ctx.interactionChain);
|
|
37069
37525
|
if (!shouldProceed)
|
|
37070
37526
|
return buildResult2("pre-merge-aborted");
|
|
37071
37527
|
}
|
|
37528
|
+
logger?.debug("execution", "Running deferred review");
|
|
37072
37529
|
deferredReview = await runDeferredReview(ctx.workdir, ctx.config.review, ctx.pluginRegistry, runStartRef);
|
|
37530
|
+
logger?.debug("execution", "Deferred review done \u2014 returning completed");
|
|
37073
37531
|
return buildResult2("completed");
|
|
37074
37532
|
}
|
|
37075
37533
|
const costLimit = ctx.config.execution.costLimit;
|
|
@@ -37322,9 +37780,7 @@ async function executeUnified(ctx, initialPrd) {
|
|
|
37322
37780
|
}, ctx.eventEmitter);
|
|
37323
37781
|
}
|
|
37324
37782
|
return buildResult2("max-iterations");
|
|
37325
|
-
} finally {
|
|
37326
|
-
stopHeartbeat();
|
|
37327
|
-
}
|
|
37783
|
+
} finally {}
|
|
37328
37784
|
}
|
|
37329
37785
|
var _unifiedExecutorDeps;
|
|
37330
37786
|
var init_unified_executor = __esm(() => {
|
|
@@ -37356,16 +37812,16 @@ var init_unified_executor = __esm(() => {
|
|
|
37356
37812
|
});
|
|
37357
37813
|
|
|
37358
37814
|
// src/project/detector.ts
|
|
37359
|
-
import { join as
|
|
37815
|
+
import { join as join54 } from "path";
|
|
37360
37816
|
async function detectLanguage(workdir, pkg) {
|
|
37361
37817
|
const deps = _detectorDeps;
|
|
37362
|
-
if (await deps.fileExists(
|
|
37818
|
+
if (await deps.fileExists(join54(workdir, "go.mod")))
|
|
37363
37819
|
return "go";
|
|
37364
|
-
if (await deps.fileExists(
|
|
37820
|
+
if (await deps.fileExists(join54(workdir, "Cargo.toml")))
|
|
37365
37821
|
return "rust";
|
|
37366
|
-
if (await deps.fileExists(
|
|
37822
|
+
if (await deps.fileExists(join54(workdir, "pyproject.toml")))
|
|
37367
37823
|
return "python";
|
|
37368
|
-
if (await deps.fileExists(
|
|
37824
|
+
if (await deps.fileExists(join54(workdir, "requirements.txt")))
|
|
37369
37825
|
return "python";
|
|
37370
37826
|
if (pkg != null) {
|
|
37371
37827
|
const allDeps = {
|
|
@@ -37425,18 +37881,18 @@ async function detectLintTool(workdir, language) {
|
|
|
37425
37881
|
if (language === "python")
|
|
37426
37882
|
return "ruff";
|
|
37427
37883
|
const deps = _detectorDeps;
|
|
37428
|
-
if (await deps.fileExists(
|
|
37884
|
+
if (await deps.fileExists(join54(workdir, "biome.json")))
|
|
37429
37885
|
return "biome";
|
|
37430
|
-
if (await deps.fileExists(
|
|
37886
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc")))
|
|
37431
37887
|
return "eslint";
|
|
37432
|
-
if (await deps.fileExists(
|
|
37888
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc.js")))
|
|
37433
37889
|
return "eslint";
|
|
37434
|
-
if (await deps.fileExists(
|
|
37890
|
+
if (await deps.fileExists(join54(workdir, ".eslintrc.json")))
|
|
37435
37891
|
return "eslint";
|
|
37436
37892
|
return;
|
|
37437
37893
|
}
|
|
37438
37894
|
async function detectProjectProfile(workdir, existing) {
|
|
37439
|
-
const pkg = await _detectorDeps.readJson(
|
|
37895
|
+
const pkg = await _detectorDeps.readJson(join54(workdir, "package.json"));
|
|
37440
37896
|
const language = existing.language !== undefined ? existing.language : await detectLanguage(workdir, pkg);
|
|
37441
37897
|
const type = existing.type !== undefined ? existing.type : detectType(pkg);
|
|
37442
37898
|
const testFramework = existing.testFramework !== undefined ? existing.testFramework : await detectTestFramework(workdir, language, pkg);
|
|
@@ -37529,7 +37985,7 @@ async function writeStatusFile(filePath, status) {
|
|
|
37529
37985
|
var init_status_file = () => {};
|
|
37530
37986
|
|
|
37531
37987
|
// src/execution/status-writer.ts
|
|
37532
|
-
import { join as
|
|
37988
|
+
import { join as join55 } from "path";
|
|
37533
37989
|
|
|
37534
37990
|
class StatusWriter {
|
|
37535
37991
|
statusFile;
|
|
@@ -37603,7 +38059,7 @@ class StatusWriter {
|
|
|
37603
38059
|
if (!this._prd)
|
|
37604
38060
|
return;
|
|
37605
38061
|
const safeLogger = getSafeLogger();
|
|
37606
|
-
const featureStatusPath =
|
|
38062
|
+
const featureStatusPath = join55(featureDir, "status.json");
|
|
37607
38063
|
const write = async () => {
|
|
37608
38064
|
try {
|
|
37609
38065
|
const base = this.getSnapshot(totalCost, iterations);
|
|
@@ -37719,7 +38175,7 @@ var exports_precheck_runner = {};
|
|
|
37719
38175
|
__export(exports_precheck_runner, {
|
|
37720
38176
|
runPrecheckValidation: () => runPrecheckValidation
|
|
37721
38177
|
});
|
|
37722
|
-
import { mkdirSync as
|
|
38178
|
+
import { mkdirSync as mkdirSync5 } from "fs";
|
|
37723
38179
|
import path17 from "path";
|
|
37724
38180
|
async function runPrecheckValidation(ctx) {
|
|
37725
38181
|
const logger = getSafeLogger();
|
|
@@ -37734,7 +38190,7 @@ async function runPrecheckValidation(ctx) {
|
|
|
37734
38190
|
format: "human"
|
|
37735
38191
|
});
|
|
37736
38192
|
if (ctx.logFilePath) {
|
|
37737
|
-
|
|
38193
|
+
mkdirSync5(path17.dirname(ctx.logFilePath), { recursive: true });
|
|
37738
38194
|
const precheckLog = {
|
|
37739
38195
|
type: "precheck",
|
|
37740
38196
|
timestamp: new Date().toISOString(),
|
|
@@ -37814,7 +38270,7 @@ __export(exports_run_initialization, {
|
|
|
37814
38270
|
initializeRun: () => initializeRun,
|
|
37815
38271
|
_reconcileDeps: () => _reconcileDeps
|
|
37816
38272
|
});
|
|
37817
|
-
import { join as
|
|
38273
|
+
import { join as join56 } from "path";
|
|
37818
38274
|
async function reconcileState(prd, prdPath, workdir, config2) {
|
|
37819
38275
|
const logger = getSafeLogger();
|
|
37820
38276
|
let reconciledCount = 0;
|
|
@@ -37832,7 +38288,7 @@ async function reconcileState(prd, prdPath, workdir, config2) {
|
|
|
37832
38288
|
});
|
|
37833
38289
|
continue;
|
|
37834
38290
|
}
|
|
37835
|
-
const effectiveWorkdir = story.workdir ?
|
|
38291
|
+
const effectiveWorkdir = story.workdir ? join56(workdir, story.workdir) : workdir;
|
|
37836
38292
|
try {
|
|
37837
38293
|
const reviewResult = await _reconcileDeps.runReview(config2.review, effectiveWorkdir, config2.execution);
|
|
37838
38294
|
if (!reviewResult.success) {
|
|
@@ -69040,9 +69496,9 @@ var require_jsx_dev_runtime = __commonJS((exports, module) => {
|
|
|
69040
69496
|
|
|
69041
69497
|
// bin/nax.ts
|
|
69042
69498
|
init_source();
|
|
69043
|
-
import { existsSync as existsSync34, mkdirSync as
|
|
69499
|
+
import { existsSync as existsSync34, mkdirSync as mkdirSync6 } from "fs";
|
|
69044
69500
|
import { homedir as homedir8 } from "os";
|
|
69045
|
-
import { join as
|
|
69501
|
+
import { join as join58 } from "path";
|
|
69046
69502
|
|
|
69047
69503
|
// node_modules/commander/esm.mjs
|
|
69048
69504
|
var import__ = __toESM(require_commander(), 1);
|
|
@@ -69064,14 +69520,14 @@ var {
|
|
|
69064
69520
|
init_acceptance();
|
|
69065
69521
|
init_registry();
|
|
69066
69522
|
import { existsSync as existsSync8 } from "fs";
|
|
69067
|
-
import { join as
|
|
69523
|
+
import { join as join9 } from "path";
|
|
69068
69524
|
|
|
69069
69525
|
// src/analyze/scanner.ts
|
|
69070
|
-
import { existsSync as existsSync5, readdirSync } from "fs";
|
|
69071
|
-
import { join as
|
|
69526
|
+
import { existsSync as existsSync5, readdirSync as readdirSync2 } from "fs";
|
|
69527
|
+
import { join as join7 } from "path";
|
|
69072
69528
|
async function scanCodebase(workdir) {
|
|
69073
|
-
const srcPath =
|
|
69074
|
-
const packageJsonPath =
|
|
69529
|
+
const srcPath = join7(workdir, "src");
|
|
69530
|
+
const packageJsonPath = join7(workdir, "package.json");
|
|
69075
69531
|
const fileTree = existsSync5(srcPath) ? await generateFileTree(srcPath, 3) : "No src/ directory";
|
|
69076
69532
|
let dependencies = {};
|
|
69077
69533
|
let devDependencies = {};
|
|
@@ -69096,7 +69552,7 @@ async function generateFileTree(dir, maxDepth, currentDepth = 0, prefix = "") {
|
|
|
69096
69552
|
}
|
|
69097
69553
|
const entries = [];
|
|
69098
69554
|
try {
|
|
69099
|
-
const dirEntries =
|
|
69555
|
+
const dirEntries = readdirSync2(dir, { withFileTypes: true });
|
|
69100
69556
|
dirEntries.sort((a, b) => {
|
|
69101
69557
|
if (a.isDirectory() && !b.isDirectory())
|
|
69102
69558
|
return -1;
|
|
@@ -69112,7 +69568,7 @@ async function generateFileTree(dir, maxDepth, currentDepth = 0, prefix = "") {
|
|
|
69112
69568
|
const isDir = dirent.isDirectory();
|
|
69113
69569
|
entries.push(`${prefix}${connector}${dirent.name}${isDir ? "/" : ""}`);
|
|
69114
69570
|
if (isDir) {
|
|
69115
|
-
const subtree = await generateFileTree(
|
|
69571
|
+
const subtree = await generateFileTree(join7(dir, dirent.name), maxDepth, currentDepth + 1, prefix + childPrefix);
|
|
69116
69572
|
if (subtree) {
|
|
69117
69573
|
entries.push(subtree);
|
|
69118
69574
|
}
|
|
@@ -69136,16 +69592,16 @@ function detectTestPatterns(workdir, dependencies, devDependencies) {
|
|
|
69136
69592
|
} else {
|
|
69137
69593
|
patterns.push("Test framework: likely bun:test (no framework dependency)");
|
|
69138
69594
|
}
|
|
69139
|
-
if (existsSync5(
|
|
69595
|
+
if (existsSync5(join7(workdir, "test"))) {
|
|
69140
69596
|
patterns.push("Test directory: test/");
|
|
69141
69597
|
}
|
|
69142
|
-
if (existsSync5(
|
|
69598
|
+
if (existsSync5(join7(workdir, "__tests__"))) {
|
|
69143
69599
|
patterns.push("Test directory: __tests__/");
|
|
69144
69600
|
}
|
|
69145
|
-
if (existsSync5(
|
|
69601
|
+
if (existsSync5(join7(workdir, "tests"))) {
|
|
69146
69602
|
patterns.push("Test directory: tests/");
|
|
69147
69603
|
}
|
|
69148
|
-
const hasTestFiles = existsSync5(
|
|
69604
|
+
const hasTestFiles = existsSync5(join7(workdir, "test")) || existsSync5(join7(workdir, "src"));
|
|
69149
69605
|
if (hasTestFiles) {
|
|
69150
69606
|
patterns.push("Test files: *.test.ts, *.spec.ts");
|
|
69151
69607
|
}
|
|
@@ -69161,7 +69617,7 @@ init_version();
|
|
|
69161
69617
|
// src/cli/analyze-parser.ts
|
|
69162
69618
|
init_registry();
|
|
69163
69619
|
import { existsSync as existsSync7 } from "fs";
|
|
69164
|
-
import { join as
|
|
69620
|
+
import { join as join8 } from "path";
|
|
69165
69621
|
init_config();
|
|
69166
69622
|
init_logger2();
|
|
69167
69623
|
init_prd();
|
|
@@ -69291,7 +69747,7 @@ function estimateLOCFromComplexity(complexity) {
|
|
|
69291
69747
|
}
|
|
69292
69748
|
}
|
|
69293
69749
|
async function reclassifyExistingPRD(featureDir, featureName, branchName, workdir, config2) {
|
|
69294
|
-
const prdPath =
|
|
69750
|
+
const prdPath = join8(featureDir, "prd.json");
|
|
69295
69751
|
if (!existsSync7(prdPath)) {
|
|
69296
69752
|
throw new Error(`prd.json not found at ${prdPath}. Run analyze without --reclassify first.`);
|
|
69297
69753
|
}
|
|
@@ -69392,11 +69848,11 @@ function reclassifyWithKeywords(story, config2) {
|
|
|
69392
69848
|
// src/cli/analyze.ts
|
|
69393
69849
|
async function analyzeFeature(options) {
|
|
69394
69850
|
const { featureDir, featureName, branchName, config: config2, specPath: explicitSpecPath, reclassify = false } = options;
|
|
69395
|
-
const workdir =
|
|
69851
|
+
const workdir = join9(featureDir, "../..");
|
|
69396
69852
|
if (reclassify) {
|
|
69397
69853
|
return await reclassifyExistingPRD(featureDir, featureName, branchName, workdir, config2);
|
|
69398
69854
|
}
|
|
69399
|
-
const specPath = explicitSpecPath ||
|
|
69855
|
+
const specPath = explicitSpecPath || join9(featureDir, "spec.md");
|
|
69400
69856
|
if (!existsSync8(specPath))
|
|
69401
69857
|
throw new Error(`spec.md not found at ${specPath}`);
|
|
69402
69858
|
const specContent = await Bun.file(specPath).text();
|
|
@@ -69513,7 +69969,7 @@ async function generateAcceptanceTestsForFeature(specContent, featureName, featu
|
|
|
69513
69969
|
modelDef,
|
|
69514
69970
|
config: config2
|
|
69515
69971
|
});
|
|
69516
|
-
const acceptanceTestPath =
|
|
69972
|
+
const acceptanceTestPath = join9(featureDir, config2.acceptance.testPath);
|
|
69517
69973
|
await Bun.write(acceptanceTestPath, result.testCode);
|
|
69518
69974
|
logger.info("cli", "[OK] Acceptance tests generated", {
|
|
69519
69975
|
criteriaCount: result.criteria.length,
|
|
@@ -69526,18 +69982,18 @@ async function generateAcceptanceTestsForFeature(specContent, featureName, featu
|
|
|
69526
69982
|
// src/cli/plan.ts
|
|
69527
69983
|
init_registry();
|
|
69528
69984
|
import { existsSync as existsSync15 } from "fs";
|
|
69529
|
-
import { join as
|
|
69985
|
+
import { join as join13 } from "path";
|
|
69530
69986
|
import { createInterface as createInterface2 } from "readline";
|
|
69531
69987
|
init_test_strategy();
|
|
69532
69988
|
|
|
69533
69989
|
// src/context/generator.ts
|
|
69534
69990
|
init_path_security();
|
|
69535
69991
|
import { existsSync as existsSync10, readFileSync } from "fs";
|
|
69536
|
-
import { join as
|
|
69992
|
+
import { join as join11, relative } from "path";
|
|
69537
69993
|
|
|
69538
69994
|
// src/context/injector.ts
|
|
69539
69995
|
import { existsSync as existsSync9 } from "fs";
|
|
69540
|
-
import { join as
|
|
69996
|
+
import { join as join10 } from "path";
|
|
69541
69997
|
var NOTABLE_NODE_DEPS = [
|
|
69542
69998
|
"@nestjs",
|
|
69543
69999
|
"express",
|
|
@@ -69567,7 +70023,7 @@ var NOTABLE_NODE_DEPS = [
|
|
|
69567
70023
|
"ioredis"
|
|
69568
70024
|
];
|
|
69569
70025
|
async function detectNode(workdir) {
|
|
69570
|
-
const pkgPath =
|
|
70026
|
+
const pkgPath = join10(workdir, "package.json");
|
|
69571
70027
|
if (!existsSync9(pkgPath))
|
|
69572
70028
|
return null;
|
|
69573
70029
|
try {
|
|
@@ -69584,7 +70040,7 @@ async function detectNode(workdir) {
|
|
|
69584
70040
|
}
|
|
69585
70041
|
}
|
|
69586
70042
|
async function detectGo(workdir) {
|
|
69587
|
-
const goMod =
|
|
70043
|
+
const goMod = join10(workdir, "go.mod");
|
|
69588
70044
|
if (!existsSync9(goMod))
|
|
69589
70045
|
return null;
|
|
69590
70046
|
try {
|
|
@@ -69608,7 +70064,7 @@ async function detectGo(workdir) {
|
|
|
69608
70064
|
}
|
|
69609
70065
|
}
|
|
69610
70066
|
async function detectRust(workdir) {
|
|
69611
|
-
const cargoPath =
|
|
70067
|
+
const cargoPath = join10(workdir, "Cargo.toml");
|
|
69612
70068
|
if (!existsSync9(cargoPath))
|
|
69613
70069
|
return null;
|
|
69614
70070
|
try {
|
|
@@ -69624,8 +70080,8 @@ async function detectRust(workdir) {
|
|
|
69624
70080
|
}
|
|
69625
70081
|
}
|
|
69626
70082
|
async function detectPython(workdir) {
|
|
69627
|
-
const pyproject =
|
|
69628
|
-
const requirements =
|
|
70083
|
+
const pyproject = join10(workdir, "pyproject.toml");
|
|
70084
|
+
const requirements = join10(workdir, "requirements.txt");
|
|
69629
70085
|
if (!existsSync9(pyproject) && !existsSync9(requirements))
|
|
69630
70086
|
return null;
|
|
69631
70087
|
try {
|
|
@@ -69644,7 +70100,7 @@ async function detectPython(workdir) {
|
|
|
69644
70100
|
}
|
|
69645
70101
|
}
|
|
69646
70102
|
async function detectPhp(workdir) {
|
|
69647
|
-
const composerPath =
|
|
70103
|
+
const composerPath = join10(workdir, "composer.json");
|
|
69648
70104
|
if (!existsSync9(composerPath))
|
|
69649
70105
|
return null;
|
|
69650
70106
|
try {
|
|
@@ -69657,7 +70113,7 @@ async function detectPhp(workdir) {
|
|
|
69657
70113
|
}
|
|
69658
70114
|
}
|
|
69659
70115
|
async function detectRuby(workdir) {
|
|
69660
|
-
const gemfile =
|
|
70116
|
+
const gemfile = join10(workdir, "Gemfile");
|
|
69661
70117
|
if (!existsSync9(gemfile))
|
|
69662
70118
|
return null;
|
|
69663
70119
|
try {
|
|
@@ -69669,9 +70125,9 @@ async function detectRuby(workdir) {
|
|
|
69669
70125
|
}
|
|
69670
70126
|
}
|
|
69671
70127
|
async function detectJvm(workdir) {
|
|
69672
|
-
const pom =
|
|
69673
|
-
const gradle =
|
|
69674
|
-
const gradleKts =
|
|
70128
|
+
const pom = join10(workdir, "pom.xml");
|
|
70129
|
+
const gradle = join10(workdir, "build.gradle");
|
|
70130
|
+
const gradleKts = join10(workdir, "build.gradle.kts");
|
|
69675
70131
|
if (!existsSync9(pom) && !existsSync9(gradle) && !existsSync9(gradleKts))
|
|
69676
70132
|
return null;
|
|
69677
70133
|
try {
|
|
@@ -69679,7 +70135,7 @@ async function detectJvm(workdir) {
|
|
|
69679
70135
|
const content2 = await Bun.file(pom).text();
|
|
69680
70136
|
const nameMatch = content2.match(/<artifactId>([^<]+)<\/artifactId>/);
|
|
69681
70137
|
const deps2 = [...content2.matchAll(/<artifactId>([^<]+)<\/artifactId>/g)].map((m) => m[1]).filter((d) => d !== nameMatch?.[1]).slice(0, 10);
|
|
69682
|
-
const lang2 = existsSync9(
|
|
70138
|
+
const lang2 = existsSync9(join10(workdir, "src/main/kotlin")) ? "Kotlin" : "Java";
|
|
69683
70139
|
return { name: nameMatch?.[1], lang: lang2, dependencies: deps2 };
|
|
69684
70140
|
}
|
|
69685
70141
|
const gradleFile = existsSync9(gradleKts) ? gradleKts : gradle;
|
|
@@ -69906,7 +70362,7 @@ async function generateFor(agent, options, config2) {
|
|
|
69906
70362
|
try {
|
|
69907
70363
|
const context = await loadContextContent(options, config2);
|
|
69908
70364
|
const content = generator.generate(context);
|
|
69909
|
-
const outputPath =
|
|
70365
|
+
const outputPath = join11(options.outputDir, generator.outputFile);
|
|
69910
70366
|
validateFilePath(outputPath, options.outputDir);
|
|
69911
70367
|
if (!options.dryRun) {
|
|
69912
70368
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -69924,7 +70380,7 @@ async function generateAll(options, config2, agentFilter) {
|
|
|
69924
70380
|
for (const [agentKey, generator] of entries) {
|
|
69925
70381
|
try {
|
|
69926
70382
|
const content = generator.generate(context);
|
|
69927
|
-
const outputPath =
|
|
70383
|
+
const outputPath = join11(options.outputDir, generator.outputFile);
|
|
69928
70384
|
validateFilePath(outputPath, options.outputDir);
|
|
69929
70385
|
if (!options.dryRun) {
|
|
69930
70386
|
await _generatorDeps.writeFile(outputPath, content);
|
|
@@ -69944,7 +70400,7 @@ async function discoverPackages(repoRoot) {
|
|
|
69944
70400
|
const glob = new Bun.Glob(pattern);
|
|
69945
70401
|
for await (const match of glob.scan({ cwd: repoRoot, dot: true })) {
|
|
69946
70402
|
const pkgRelative = match.replace(/^\.nax\/mono\//, "").replace(/\/context\.md$/, "");
|
|
69947
|
-
const pkgAbsolute =
|
|
70403
|
+
const pkgAbsolute = join11(repoRoot, pkgRelative);
|
|
69948
70404
|
if (!seen.has(pkgAbsolute)) {
|
|
69949
70405
|
seen.add(pkgAbsolute);
|
|
69950
70406
|
packages.push(pkgAbsolute);
|
|
@@ -69976,7 +70432,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
69976
70432
|
}
|
|
69977
70433
|
}
|
|
69978
70434
|
}
|
|
69979
|
-
const turboPath =
|
|
70435
|
+
const turboPath = join11(repoRoot, "turbo.json");
|
|
69980
70436
|
if (_generatorDeps.existsSync(turboPath)) {
|
|
69981
70437
|
try {
|
|
69982
70438
|
const turbo = JSON.parse(_generatorDeps.readFileSync(turboPath, "utf-8"));
|
|
@@ -69985,7 +70441,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
69985
70441
|
}
|
|
69986
70442
|
} catch {}
|
|
69987
70443
|
}
|
|
69988
|
-
const pkgPath =
|
|
70444
|
+
const pkgPath = join11(repoRoot, "package.json");
|
|
69989
70445
|
if (_generatorDeps.existsSync(pkgPath)) {
|
|
69990
70446
|
try {
|
|
69991
70447
|
const pkg = JSON.parse(_generatorDeps.readFileSync(pkgPath, "utf-8"));
|
|
@@ -69995,7 +70451,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
69995
70451
|
await resolveGlobs(patterns);
|
|
69996
70452
|
} catch {}
|
|
69997
70453
|
}
|
|
69998
|
-
const pnpmPath =
|
|
70454
|
+
const pnpmPath = join11(repoRoot, "pnpm-workspace.yaml");
|
|
69999
70455
|
if (_generatorDeps.existsSync(pnpmPath)) {
|
|
70000
70456
|
try {
|
|
70001
70457
|
const raw = _generatorDeps.readFileSync(pnpmPath, "utf-8");
|
|
@@ -70023,7 +70479,7 @@ async function discoverWorkspacePackages(repoRoot) {
|
|
|
70023
70479
|
async function generateForPackage(packageDir, config2, dryRun = false, repoRoot) {
|
|
70024
70480
|
const resolvedRepoRoot = repoRoot ?? packageDir;
|
|
70025
70481
|
const relativePkgPath = relative(resolvedRepoRoot, packageDir);
|
|
70026
|
-
const contextPath =
|
|
70482
|
+
const contextPath = join11(resolvedRepoRoot, ".nax", "mono", relativePkgPath, "context.md");
|
|
70027
70483
|
if (!_generatorDeps.existsSync(contextPath)) {
|
|
70028
70484
|
return [
|
|
70029
70485
|
{
|
|
@@ -70249,12 +70705,13 @@ function validatePlanOutput(raw, feature, branch) {
|
|
|
70249
70705
|
}
|
|
70250
70706
|
|
|
70251
70707
|
// src/cli/plan.ts
|
|
70708
|
+
var DEFAULT_TIMEOUT_SECONDS2 = 600;
|
|
70252
70709
|
var _planDeps = {
|
|
70253
70710
|
readFile: (path) => Bun.file(path).text(),
|
|
70254
70711
|
writeFile: (path, content) => Bun.write(path, content).then(() => {}),
|
|
70255
70712
|
scanCodebase: (workdir) => scanCodebase(workdir),
|
|
70256
70713
|
getAgent: (name, cfg) => cfg ? createAgentRegistry(cfg).getAgent(name) : getAgent(name),
|
|
70257
|
-
readPackageJson: (workdir) => Bun.file(
|
|
70714
|
+
readPackageJson: (workdir) => Bun.file(join13(workdir, "package.json")).json().catch(() => null),
|
|
70258
70715
|
spawnSync: (cmd, opts) => {
|
|
70259
70716
|
const result = Bun.spawnSync(cmd, opts ? { cwd: opts.cwd } : {});
|
|
70260
70717
|
return { stdout: result.stdout, exitCode: result.exitCode };
|
|
@@ -70274,7 +70731,7 @@ var _planDeps = {
|
|
|
70274
70731
|
planDecompose: (workdir, config2, opts) => planDecomposeCommand(workdir, config2, opts)
|
|
70275
70732
|
};
|
|
70276
70733
|
async function planCommand(workdir, config2, options) {
|
|
70277
|
-
const naxDir =
|
|
70734
|
+
const naxDir = join13(workdir, ".nax");
|
|
70278
70735
|
if (!existsSync15(naxDir)) {
|
|
70279
70736
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
70280
70737
|
}
|
|
@@ -70290,16 +70747,16 @@ async function planCommand(workdir, config2, options) {
|
|
|
70290
70747
|
const codebaseContext = buildCodebaseContext2(scan);
|
|
70291
70748
|
const relativePackages = discoveredPackages.map((p) => p.startsWith("/") ? p.replace(`${workdir}/`, "") : p);
|
|
70292
70749
|
const packageDetails = relativePackages.length > 0 ? await Promise.all(relativePackages.map(async (rel) => {
|
|
70293
|
-
const pkgJson = await _planDeps.readPackageJsonAt(
|
|
70750
|
+
const pkgJson = await _planDeps.readPackageJsonAt(join13(workdir, rel, "package.json"));
|
|
70294
70751
|
return buildPackageSummary(rel, pkgJson);
|
|
70295
70752
|
})) : [];
|
|
70296
70753
|
const projectName = detectProjectName(workdir, pkg);
|
|
70297
70754
|
const branchName = options.branch ?? `feat/${options.feature}`;
|
|
70298
|
-
const outputDir =
|
|
70299
|
-
const outputPath =
|
|
70755
|
+
const outputDir = join13(naxDir, "features", options.feature);
|
|
70756
|
+
const outputPath = join13(outputDir, "prd.json");
|
|
70300
70757
|
await _planDeps.mkdirp(outputDir);
|
|
70301
70758
|
const agentName = config2?.autoMode?.defaultAgent ?? "claude";
|
|
70302
|
-
const timeoutSeconds = config2?.
|
|
70759
|
+
const timeoutSeconds = config2?.plan?.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS2;
|
|
70303
70760
|
let rawResponse;
|
|
70304
70761
|
const debateEnabled = config2?.debate?.enabled && config2?.debate?.stages?.plan?.enabled;
|
|
70305
70762
|
if (debateEnabled) {
|
|
@@ -70383,6 +70840,7 @@ async function planCommand(workdir, config2, options) {
|
|
|
70383
70840
|
}
|
|
70384
70841
|
rawResponse = await _planDeps.readFile(outputPath);
|
|
70385
70842
|
} else {
|
|
70843
|
+
const timeoutMs = (config2?.plan?.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS2) * 1000;
|
|
70386
70844
|
const completeResult = await adapter.complete(prompt, {
|
|
70387
70845
|
model: autoModel,
|
|
70388
70846
|
jsonMode: true,
|
|
@@ -70390,7 +70848,7 @@ async function planCommand(workdir, config2, options) {
|
|
|
70390
70848
|
config: config2,
|
|
70391
70849
|
featureName: options.feature,
|
|
70392
70850
|
sessionRole: "plan",
|
|
70393
|
-
timeoutMs
|
|
70851
|
+
timeoutMs
|
|
70394
70852
|
});
|
|
70395
70853
|
let result = typeof completeResult === "string" ? completeResult : completeResult.output;
|
|
70396
70854
|
try {
|
|
@@ -70730,7 +71188,7 @@ Return JSON with this exact structure (no markdown, no explanation \u2014 JSON o
|
|
|
70730
71188
|
}`;
|
|
70731
71189
|
}
|
|
70732
71190
|
async function planDecomposeCommand(workdir, config2, options) {
|
|
70733
|
-
const prdPath =
|
|
71191
|
+
const prdPath = join13(workdir, ".nax", "features", options.feature, "prd.json");
|
|
70734
71192
|
if (!_planDeps.existsSync(prdPath)) {
|
|
70735
71193
|
throw new NaxError(`PRD not found: ${prdPath}`, "PRD_NOT_FOUND", {
|
|
70736
71194
|
stage: "decompose",
|
|
@@ -70760,9 +71218,20 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
70760
71218
|
const adapter = _planDeps.getAgent(agentName, config2);
|
|
70761
71219
|
if (!adapter)
|
|
70762
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 {}
|
|
70763
71230
|
const stages = config2?.debate?.stages;
|
|
70764
71231
|
const debateEnabled = config2?.debate?.enabled && stages?.decompose?.enabled;
|
|
70765
71232
|
let rawResponse;
|
|
71233
|
+
const timeoutSeconds = config2?.plan?.timeoutSeconds ?? DEFAULT_TIMEOUT_SECONDS2;
|
|
71234
|
+
const timeoutMs = timeoutSeconds * 1000;
|
|
70766
71235
|
if (debateEnabled) {
|
|
70767
71236
|
const stageConfig = stages?.decompose;
|
|
70768
71237
|
const debateSession = _planDeps.createDebateSession({
|
|
@@ -70772,30 +71241,32 @@ async function planDecomposeCommand(workdir, config2, options) {
|
|
|
70772
71241
|
config: config2,
|
|
70773
71242
|
workdir,
|
|
70774
71243
|
featureName: options.feature,
|
|
70775
|
-
timeoutSeconds
|
|
71244
|
+
timeoutSeconds
|
|
70776
71245
|
});
|
|
70777
71246
|
const debateResult = await debateSession.run(prompt);
|
|
70778
71247
|
if (debateResult.outcome !== "failed" && debateResult.output) {
|
|
70779
71248
|
rawResponse = debateResult.output;
|
|
70780
71249
|
} else {
|
|
70781
71250
|
const completeResult = await adapter.complete(prompt, {
|
|
71251
|
+
model: decomposeModel,
|
|
70782
71252
|
jsonMode: true,
|
|
70783
71253
|
workdir,
|
|
70784
71254
|
sessionRole: "decompose",
|
|
70785
71255
|
featureName: options.feature,
|
|
70786
71256
|
storyId: options.storyId,
|
|
70787
|
-
timeoutMs
|
|
71257
|
+
timeoutMs
|
|
70788
71258
|
});
|
|
70789
71259
|
rawResponse = typeof completeResult === "string" ? completeResult : completeResult.output;
|
|
70790
71260
|
}
|
|
70791
71261
|
} else {
|
|
70792
71262
|
const completeResult = await adapter.complete(prompt, {
|
|
71263
|
+
model: decomposeModel,
|
|
70793
71264
|
jsonMode: true,
|
|
70794
71265
|
workdir,
|
|
70795
71266
|
sessionRole: "decompose",
|
|
70796
71267
|
featureName: options.feature,
|
|
70797
71268
|
storyId: options.storyId,
|
|
70798
|
-
timeoutMs
|
|
71269
|
+
timeoutMs
|
|
70799
71270
|
});
|
|
70800
71271
|
rawResponse = typeof completeResult === "string" ? completeResult : completeResult.output;
|
|
70801
71272
|
}
|
|
@@ -71019,14 +71490,14 @@ async function displayModelEfficiency(workdir) {
|
|
|
71019
71490
|
}
|
|
71020
71491
|
// src/cli/status-features.ts
|
|
71021
71492
|
init_source();
|
|
71022
|
-
import { existsSync as existsSync17, readdirSync as
|
|
71023
|
-
import { join as
|
|
71493
|
+
import { existsSync as existsSync17, readdirSync as readdirSync4 } from "fs";
|
|
71494
|
+
import { join as join16 } from "path";
|
|
71024
71495
|
|
|
71025
71496
|
// src/commands/common.ts
|
|
71026
71497
|
init_path_security();
|
|
71027
71498
|
init_errors();
|
|
71028
|
-
import { existsSync as existsSync16, readdirSync as
|
|
71029
|
-
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";
|
|
71030
71501
|
function resolveProject(options = {}) {
|
|
71031
71502
|
const { dir, feature } = options;
|
|
71032
71503
|
let projectRoot;
|
|
@@ -71034,12 +71505,12 @@ function resolveProject(options = {}) {
|
|
|
71034
71505
|
let configPath;
|
|
71035
71506
|
if (dir) {
|
|
71036
71507
|
projectRoot = realpathSync2(resolve4(dir));
|
|
71037
|
-
naxDir =
|
|
71508
|
+
naxDir = join14(projectRoot, ".nax");
|
|
71038
71509
|
if (!existsSync16(naxDir)) {
|
|
71039
71510
|
throw new NaxError(`Directory does not contain a nax project: ${projectRoot}
|
|
71040
71511
|
Expected to find: ${naxDir}`, "NAX_DIR_NOT_FOUND", { projectRoot, naxDir });
|
|
71041
71512
|
}
|
|
71042
|
-
configPath =
|
|
71513
|
+
configPath = join14(naxDir, "config.json");
|
|
71043
71514
|
if (!existsSync16(configPath)) {
|
|
71044
71515
|
throw new NaxError(`.nax directory found but config.json is missing: ${naxDir}
|
|
71045
71516
|
Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
@@ -71047,24 +71518,24 @@ Expected to find: ${configPath}`, "CONFIG_NOT_FOUND", { naxDir, configPath });
|
|
|
71047
71518
|
} else {
|
|
71048
71519
|
const found = findProjectRoot(process.cwd());
|
|
71049
71520
|
if (!found) {
|
|
71050
|
-
const cwdNaxDir =
|
|
71521
|
+
const cwdNaxDir = join14(process.cwd(), ".nax");
|
|
71051
71522
|
if (existsSync16(cwdNaxDir)) {
|
|
71052
|
-
const cwdConfigPath =
|
|
71523
|
+
const cwdConfigPath = join14(cwdNaxDir, "config.json");
|
|
71053
71524
|
throw new NaxError(`.nax directory found but config.json is missing: ${cwdNaxDir}
|
|
71054
71525
|
Expected to find: ${cwdConfigPath}`, "CONFIG_NOT_FOUND", { naxDir: cwdNaxDir, configPath: cwdConfigPath });
|
|
71055
71526
|
}
|
|
71056
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() });
|
|
71057
71528
|
}
|
|
71058
71529
|
projectRoot = found;
|
|
71059
|
-
naxDir =
|
|
71060
|
-
configPath =
|
|
71530
|
+
naxDir = join14(projectRoot, ".nax");
|
|
71531
|
+
configPath = join14(naxDir, "config.json");
|
|
71061
71532
|
}
|
|
71062
71533
|
let featureDir;
|
|
71063
71534
|
if (feature) {
|
|
71064
|
-
const featuresDir =
|
|
71065
|
-
featureDir =
|
|
71535
|
+
const featuresDir = join14(naxDir, "features");
|
|
71536
|
+
featureDir = join14(featuresDir, feature);
|
|
71066
71537
|
if (!existsSync16(featureDir)) {
|
|
71067
|
-
const availableFeatures = existsSync16(featuresDir) ?
|
|
71538
|
+
const availableFeatures = existsSync16(featuresDir) ? readdirSync3(featuresDir, { withFileTypes: true }).filter((entry) => entry.isDirectory()).map((entry) => entry.name) : [];
|
|
71068
71539
|
const availableMsg = availableFeatures.length > 0 ? `
|
|
71069
71540
|
|
|
71070
71541
|
Available features:
|
|
@@ -71089,12 +71560,12 @@ function findProjectRoot(startDir) {
|
|
|
71089
71560
|
let current = resolve4(startDir);
|
|
71090
71561
|
let depth = 0;
|
|
71091
71562
|
while (depth < MAX_DIRECTORY_DEPTH) {
|
|
71092
|
-
const naxDir =
|
|
71093
|
-
const configPath =
|
|
71563
|
+
const naxDir = join14(current, ".nax");
|
|
71564
|
+
const configPath = join14(naxDir, "config.json");
|
|
71094
71565
|
if (existsSync16(configPath)) {
|
|
71095
71566
|
return realpathSync2(current);
|
|
71096
71567
|
}
|
|
71097
|
-
const parent =
|
|
71568
|
+
const parent = join14(current, "..");
|
|
71098
71569
|
if (parent === current) {
|
|
71099
71570
|
break;
|
|
71100
71571
|
}
|
|
@@ -71116,7 +71587,7 @@ function isPidAlive(pid) {
|
|
|
71116
71587
|
}
|
|
71117
71588
|
}
|
|
71118
71589
|
async function loadStatusFile(featureDir) {
|
|
71119
|
-
const statusPath =
|
|
71590
|
+
const statusPath = join16(featureDir, "status.json");
|
|
71120
71591
|
if (!existsSync17(statusPath)) {
|
|
71121
71592
|
return null;
|
|
71122
71593
|
}
|
|
@@ -71128,7 +71599,7 @@ async function loadStatusFile(featureDir) {
|
|
|
71128
71599
|
}
|
|
71129
71600
|
}
|
|
71130
71601
|
async function loadProjectStatusFile(projectDir) {
|
|
71131
|
-
const statusPath =
|
|
71602
|
+
const statusPath = join16(projectDir, ".nax", "status.json");
|
|
71132
71603
|
if (!existsSync17(statusPath)) {
|
|
71133
71604
|
return null;
|
|
71134
71605
|
}
|
|
@@ -71140,7 +71611,7 @@ async function loadProjectStatusFile(projectDir) {
|
|
|
71140
71611
|
}
|
|
71141
71612
|
}
|
|
71142
71613
|
async function getFeatureSummary(featureName, featureDir) {
|
|
71143
|
-
const prdPath =
|
|
71614
|
+
const prdPath = join16(featureDir, "prd.json");
|
|
71144
71615
|
if (!existsSync17(prdPath)) {
|
|
71145
71616
|
return {
|
|
71146
71617
|
name: featureName,
|
|
@@ -71183,9 +71654,9 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
71183
71654
|
};
|
|
71184
71655
|
}
|
|
71185
71656
|
}
|
|
71186
|
-
const runsDir =
|
|
71657
|
+
const runsDir = join16(featureDir, "runs");
|
|
71187
71658
|
if (existsSync17(runsDir)) {
|
|
71188
|
-
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();
|
|
71189
71660
|
if (runs.length > 0) {
|
|
71190
71661
|
const latestRun = runs[0].replace(".jsonl", "");
|
|
71191
71662
|
summary.lastRun = latestRun;
|
|
@@ -71194,12 +71665,12 @@ async function getFeatureSummary(featureName, featureDir) {
|
|
|
71194
71665
|
return summary;
|
|
71195
71666
|
}
|
|
71196
71667
|
async function displayAllFeatures(projectDir) {
|
|
71197
|
-
const featuresDir =
|
|
71668
|
+
const featuresDir = join16(projectDir, ".nax", "features");
|
|
71198
71669
|
if (!existsSync17(featuresDir)) {
|
|
71199
71670
|
console.log(source_default.dim("No features found."));
|
|
71200
71671
|
return;
|
|
71201
71672
|
}
|
|
71202
|
-
const features =
|
|
71673
|
+
const features = readdirSync4(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name).sort();
|
|
71203
71674
|
if (features.length === 0) {
|
|
71204
71675
|
console.log(source_default.dim("No features found."));
|
|
71205
71676
|
return;
|
|
@@ -71235,7 +71706,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
71235
71706
|
console.log();
|
|
71236
71707
|
}
|
|
71237
71708
|
}
|
|
71238
|
-
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name,
|
|
71709
|
+
const summaries = await Promise.all(features.map((name) => getFeatureSummary(name, join16(featuresDir, name))));
|
|
71239
71710
|
console.log(source_default.bold(`\uD83D\uDCCA Features
|
|
71240
71711
|
`));
|
|
71241
71712
|
const header = ` ${"Feature".padEnd(25)} ${"Done".padEnd(6)} ${"Failed".padEnd(8)} ${"Pending".padEnd(9)} ${"Last Run".padEnd(22)} ${"Cost".padEnd(10)} Status`;
|
|
@@ -71261,7 +71732,7 @@ async function displayAllFeatures(projectDir) {
|
|
|
71261
71732
|
console.log();
|
|
71262
71733
|
}
|
|
71263
71734
|
async function displayFeatureDetails(featureName, featureDir) {
|
|
71264
|
-
const prdPath =
|
|
71735
|
+
const prdPath = join16(featureDir, "prd.json");
|
|
71265
71736
|
if (!existsSync17(prdPath)) {
|
|
71266
71737
|
console.log(source_default.bold(`
|
|
71267
71738
|
\uD83D\uDCCA ${featureName}
|
|
@@ -71382,8 +71853,8 @@ async function displayFeatureStatus(options = {}) {
|
|
|
71382
71853
|
// src/cli/runs.ts
|
|
71383
71854
|
init_errors();
|
|
71384
71855
|
init_logger2();
|
|
71385
|
-
import { existsSync as existsSync18, readdirSync as
|
|
71386
|
-
import { join as
|
|
71856
|
+
import { existsSync as existsSync18, readdirSync as readdirSync5 } from "fs";
|
|
71857
|
+
import { join as join17 } from "path";
|
|
71387
71858
|
async function parseRunLog(logPath) {
|
|
71388
71859
|
const logger = getLogger();
|
|
71389
71860
|
try {
|
|
@@ -71399,19 +71870,19 @@ async function parseRunLog(logPath) {
|
|
|
71399
71870
|
async function runsListCommand(options) {
|
|
71400
71871
|
const logger = getLogger();
|
|
71401
71872
|
const { feature, workdir } = options;
|
|
71402
|
-
const runsDir =
|
|
71873
|
+
const runsDir = join17(workdir, ".nax", "features", feature, "runs");
|
|
71403
71874
|
if (!existsSync18(runsDir)) {
|
|
71404
71875
|
logger.info("cli", "No runs found for feature", { feature, hint: `Directory not found: ${runsDir}` });
|
|
71405
71876
|
return;
|
|
71406
71877
|
}
|
|
71407
|
-
const files =
|
|
71878
|
+
const files = readdirSync5(runsDir).filter((f) => f.endsWith(".jsonl"));
|
|
71408
71879
|
if (files.length === 0) {
|
|
71409
71880
|
logger.info("cli", "No runs found for feature", { feature });
|
|
71410
71881
|
return;
|
|
71411
71882
|
}
|
|
71412
71883
|
logger.info("cli", `Runs for ${feature}`, { count: files.length });
|
|
71413
71884
|
for (const file3 of files.sort().reverse()) {
|
|
71414
|
-
const logPath =
|
|
71885
|
+
const logPath = join17(runsDir, file3);
|
|
71415
71886
|
const entries = await parseRunLog(logPath);
|
|
71416
71887
|
const startEvent = entries.find((e) => e.message === "run.start");
|
|
71417
71888
|
const completeEvent = entries.find((e) => e.message === "run.complete");
|
|
@@ -71437,7 +71908,7 @@ async function runsListCommand(options) {
|
|
|
71437
71908
|
async function runsShowCommand(options) {
|
|
71438
71909
|
const logger = getLogger();
|
|
71439
71910
|
const { runId, feature, workdir } = options;
|
|
71440
|
-
const logPath =
|
|
71911
|
+
const logPath = join17(workdir, ".nax", "features", feature, "runs", `${runId}.jsonl`);
|
|
71441
71912
|
if (!existsSync18(logPath)) {
|
|
71442
71913
|
logger.error("cli", "Run not found", { runId, feature, logPath });
|
|
71443
71914
|
throw new NaxError("Run not found", "RUN_NOT_FOUND", { runId, feature, logPath });
|
|
@@ -71476,7 +71947,7 @@ async function runsShowCommand(options) {
|
|
|
71476
71947
|
// src/cli/prompts-main.ts
|
|
71477
71948
|
init_logger2();
|
|
71478
71949
|
import { existsSync as existsSync22, mkdirSync as mkdirSync2 } from "fs";
|
|
71479
|
-
import { join as
|
|
71950
|
+
import { join as join30 } from "path";
|
|
71480
71951
|
|
|
71481
71952
|
// src/pipeline/index.ts
|
|
71482
71953
|
init_runner();
|
|
@@ -71551,7 +72022,7 @@ function buildFrontmatter(story, ctx, role) {
|
|
|
71551
72022
|
|
|
71552
72023
|
// src/cli/prompts-tdd.ts
|
|
71553
72024
|
init_prompts2();
|
|
71554
|
-
import { join as
|
|
72025
|
+
import { join as join29 } from "path";
|
|
71555
72026
|
async function handleThreeSessionTddPrompts(story, ctx, outputDir, logger) {
|
|
71556
72027
|
const [testWriterPrompt, implementerPrompt, verifierPrompt] = await Promise.all([
|
|
71557
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(),
|
|
@@ -71570,7 +72041,7 @@ ${frontmatter}---
|
|
|
71570
72041
|
|
|
71571
72042
|
${session.prompt}`;
|
|
71572
72043
|
if (outputDir) {
|
|
71573
|
-
const promptFile =
|
|
72044
|
+
const promptFile = join29(outputDir, `${story.id}.${session.role}.md`);
|
|
71574
72045
|
await Bun.write(promptFile, fullOutput);
|
|
71575
72046
|
logger.info("cli", "Written TDD prompt file", {
|
|
71576
72047
|
storyId: story.id,
|
|
@@ -71586,7 +72057,7 @@ ${"=".repeat(80)}`);
|
|
|
71586
72057
|
}
|
|
71587
72058
|
}
|
|
71588
72059
|
if (outputDir && ctx.contextMarkdown) {
|
|
71589
|
-
const contextFile =
|
|
72060
|
+
const contextFile = join29(outputDir, `${story.id}.context.md`);
|
|
71590
72061
|
const frontmatter = buildFrontmatter(story, ctx);
|
|
71591
72062
|
const contextOutput = `---
|
|
71592
72063
|
${frontmatter}---
|
|
@@ -71600,12 +72071,12 @@ ${ctx.contextMarkdown}`;
|
|
|
71600
72071
|
async function promptsCommand(options) {
|
|
71601
72072
|
const logger = getLogger();
|
|
71602
72073
|
const { feature, workdir, config: config2, storyId, outputDir } = options;
|
|
71603
|
-
const naxDir =
|
|
72074
|
+
const naxDir = join30(workdir, ".nax");
|
|
71604
72075
|
if (!existsSync22(naxDir)) {
|
|
71605
72076
|
throw new Error(`.nax directory not found. Run 'nax init' first in ${workdir}`);
|
|
71606
72077
|
}
|
|
71607
|
-
const featureDir =
|
|
71608
|
-
const prdPath =
|
|
72078
|
+
const featureDir = join30(naxDir, "features", feature);
|
|
72079
|
+
const prdPath = join30(featureDir, "prd.json");
|
|
71609
72080
|
if (!existsSync22(prdPath)) {
|
|
71610
72081
|
throw new Error(`Feature "${feature}" not found or missing prd.json`);
|
|
71611
72082
|
}
|
|
@@ -71666,10 +72137,10 @@ ${frontmatter}---
|
|
|
71666
72137
|
|
|
71667
72138
|
${ctx.prompt}`;
|
|
71668
72139
|
if (outputDir) {
|
|
71669
|
-
const promptFile =
|
|
72140
|
+
const promptFile = join30(outputDir, `${story.id}.prompt.md`);
|
|
71670
72141
|
await Bun.write(promptFile, fullOutput);
|
|
71671
72142
|
if (ctx.contextMarkdown) {
|
|
71672
|
-
const contextFile =
|
|
72143
|
+
const contextFile = join30(outputDir, `${story.id}.context.md`);
|
|
71673
72144
|
const contextOutput = `---
|
|
71674
72145
|
${frontmatter}---
|
|
71675
72146
|
|
|
@@ -71696,7 +72167,7 @@ ${"=".repeat(80)}`);
|
|
|
71696
72167
|
}
|
|
71697
72168
|
// src/cli/prompts-init.ts
|
|
71698
72169
|
import { existsSync as existsSync23, mkdirSync as mkdirSync3 } from "fs";
|
|
71699
|
-
import { join as
|
|
72170
|
+
import { join as join31 } from "path";
|
|
71700
72171
|
var TEMPLATE_ROLES = [
|
|
71701
72172
|
{ file: "test-writer.md", role: "test-writer" },
|
|
71702
72173
|
{ file: "implementer.md", role: "implementer", variant: "standard" },
|
|
@@ -71720,9 +72191,9 @@ var TEMPLATE_HEADER = `<!--
|
|
|
71720
72191
|
`;
|
|
71721
72192
|
async function promptsInitCommand(options) {
|
|
71722
72193
|
const { workdir, force = false, autoWireConfig = true } = options;
|
|
71723
|
-
const templatesDir =
|
|
72194
|
+
const templatesDir = join31(workdir, ".nax", "templates");
|
|
71724
72195
|
mkdirSync3(templatesDir, { recursive: true });
|
|
71725
|
-
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)));
|
|
71726
72197
|
if (existingFiles.length > 0 && !force) {
|
|
71727
72198
|
console.warn(`[WARN] nax/templates/ already contains files: ${existingFiles.join(", ")}. No files overwritten.
|
|
71728
72199
|
Pass --force to overwrite existing templates.`);
|
|
@@ -71730,7 +72201,7 @@ async function promptsInitCommand(options) {
|
|
|
71730
72201
|
}
|
|
71731
72202
|
const written = [];
|
|
71732
72203
|
for (const template of TEMPLATE_ROLES) {
|
|
71733
|
-
const filePath =
|
|
72204
|
+
const filePath = join31(templatesDir, template.file);
|
|
71734
72205
|
const roleBody = template.role === "implementer" ? buildRoleTaskSection(template.role, template.variant) : buildRoleTaskSection(template.role);
|
|
71735
72206
|
const content = TEMPLATE_HEADER + roleBody;
|
|
71736
72207
|
await Bun.write(filePath, content);
|
|
@@ -71746,7 +72217,7 @@ async function promptsInitCommand(options) {
|
|
|
71746
72217
|
return written;
|
|
71747
72218
|
}
|
|
71748
72219
|
async function autoWirePromptsConfig(workdir) {
|
|
71749
|
-
const configPath =
|
|
72220
|
+
const configPath = join31(workdir, "nax.config.json");
|
|
71750
72221
|
if (!existsSync23(configPath)) {
|
|
71751
72222
|
const exampleConfig = JSON.stringify({
|
|
71752
72223
|
prompts: {
|
|
@@ -71911,8 +72382,8 @@ function pad(str, width) {
|
|
|
71911
72382
|
init_config();
|
|
71912
72383
|
init_logger2();
|
|
71913
72384
|
init_prd();
|
|
71914
|
-
import { existsSync as existsSync25, readdirSync as
|
|
71915
|
-
import { join as
|
|
72385
|
+
import { existsSync as existsSync25, readdirSync as readdirSync6 } from "fs";
|
|
72386
|
+
import { join as join36 } from "path";
|
|
71916
72387
|
|
|
71917
72388
|
// src/cli/diagnose-analysis.ts
|
|
71918
72389
|
function detectFailurePattern(story, prd, status) {
|
|
@@ -72111,7 +72582,7 @@ function isProcessAlive2(pid) {
|
|
|
72111
72582
|
}
|
|
72112
72583
|
}
|
|
72113
72584
|
async function loadStatusFile2(workdir) {
|
|
72114
|
-
const statusPath =
|
|
72585
|
+
const statusPath = join36(workdir, ".nax", "status.json");
|
|
72115
72586
|
if (!existsSync25(statusPath))
|
|
72116
72587
|
return null;
|
|
72117
72588
|
try {
|
|
@@ -72139,7 +72610,7 @@ async function countCommitsSince(workdir, since) {
|
|
|
72139
72610
|
}
|
|
72140
72611
|
}
|
|
72141
72612
|
async function checkLock(workdir) {
|
|
72142
|
-
const lockFile = Bun.file(
|
|
72613
|
+
const lockFile = Bun.file(join36(workdir, "nax.lock"));
|
|
72143
72614
|
if (!await lockFile.exists())
|
|
72144
72615
|
return { lockPresent: false };
|
|
72145
72616
|
try {
|
|
@@ -72157,8 +72628,8 @@ async function diagnoseCommand(options = {}) {
|
|
|
72157
72628
|
const logger = getLogger();
|
|
72158
72629
|
const workdir = options.workdir ?? process.cwd();
|
|
72159
72630
|
const naxSubdir = findProjectDir(workdir);
|
|
72160
|
-
let projectDir = naxSubdir ?
|
|
72161
|
-
if (!projectDir && existsSync25(
|
|
72631
|
+
let projectDir = naxSubdir ? join36(naxSubdir, "..") : null;
|
|
72632
|
+
if (!projectDir && existsSync25(join36(workdir, ".nax"))) {
|
|
72162
72633
|
projectDir = workdir;
|
|
72163
72634
|
}
|
|
72164
72635
|
if (!projectDir)
|
|
@@ -72169,18 +72640,18 @@ async function diagnoseCommand(options = {}) {
|
|
|
72169
72640
|
if (status2) {
|
|
72170
72641
|
feature = status2.run.feature;
|
|
72171
72642
|
} else {
|
|
72172
|
-
const featuresDir =
|
|
72643
|
+
const featuresDir = join36(projectDir, ".nax", "features");
|
|
72173
72644
|
if (!existsSync25(featuresDir))
|
|
72174
72645
|
throw new Error("No features found in project");
|
|
72175
|
-
const features =
|
|
72646
|
+
const features = readdirSync6(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
72176
72647
|
if (features.length === 0)
|
|
72177
72648
|
throw new Error("No features found");
|
|
72178
72649
|
feature = features[0];
|
|
72179
72650
|
logger.info("diagnose", "No feature specified, using first found", { feature });
|
|
72180
72651
|
}
|
|
72181
72652
|
}
|
|
72182
|
-
const featureDir =
|
|
72183
|
-
const prdPath =
|
|
72653
|
+
const featureDir = join36(projectDir, ".nax", "features", feature);
|
|
72654
|
+
const prdPath = join36(featureDir, "prd.json");
|
|
72184
72655
|
if (!existsSync25(prdPath))
|
|
72185
72656
|
throw new Error(`Feature not found: ${feature}`);
|
|
72186
72657
|
const prd = await loadPRD(prdPath);
|
|
@@ -72223,7 +72694,7 @@ init_interaction();
|
|
|
72223
72694
|
init_source();
|
|
72224
72695
|
init_loader();
|
|
72225
72696
|
import { existsSync as existsSync26 } from "fs";
|
|
72226
|
-
import { join as
|
|
72697
|
+
import { join as join37 } from "path";
|
|
72227
72698
|
var VALID_AGENTS = ["claude", "codex", "opencode", "cursor", "windsurf", "aider", "gemini"];
|
|
72228
72699
|
async function generateCommand(options) {
|
|
72229
72700
|
const workdir = options.dir ?? process.cwd();
|
|
@@ -72266,7 +72737,7 @@ async function generateCommand(options) {
|
|
|
72266
72737
|
return;
|
|
72267
72738
|
}
|
|
72268
72739
|
if (options.package) {
|
|
72269
|
-
const packageDir =
|
|
72740
|
+
const packageDir = join37(workdir, options.package);
|
|
72270
72741
|
if (dryRun) {
|
|
72271
72742
|
console.log(source_default.yellow("\u26A0 Dry run \u2014 no files will be written"));
|
|
72272
72743
|
}
|
|
@@ -72286,8 +72757,8 @@ async function generateCommand(options) {
|
|
|
72286
72757
|
process.exit(1);
|
|
72287
72758
|
return;
|
|
72288
72759
|
}
|
|
72289
|
-
const contextPath = options.context ?
|
|
72290
|
-
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;
|
|
72291
72762
|
const autoInject = !options.noAutoInject;
|
|
72292
72763
|
if (!existsSync26(contextPath)) {
|
|
72293
72764
|
console.error(source_default.red(`\u2717 Context file not found: ${contextPath}`));
|
|
@@ -72392,7 +72863,7 @@ async function generateCommand(options) {
|
|
|
72392
72863
|
// src/cli/config-display.ts
|
|
72393
72864
|
init_loader();
|
|
72394
72865
|
import { existsSync as existsSync28 } from "fs";
|
|
72395
|
-
import { join as
|
|
72866
|
+
import { join as join39 } from "path";
|
|
72396
72867
|
|
|
72397
72868
|
// src/cli/config-descriptions.ts
|
|
72398
72869
|
var FIELD_DESCRIPTIONS = {
|
|
@@ -72630,7 +73101,7 @@ function deepEqual(a, b) {
|
|
|
72630
73101
|
init_defaults();
|
|
72631
73102
|
init_loader();
|
|
72632
73103
|
import { existsSync as existsSync27 } from "fs";
|
|
72633
|
-
import { join as
|
|
73104
|
+
import { join as join38 } from "path";
|
|
72634
73105
|
async function loadConfigFile(path14) {
|
|
72635
73106
|
if (!existsSync27(path14))
|
|
72636
73107
|
return null;
|
|
@@ -72652,7 +73123,7 @@ async function loadProjectConfig() {
|
|
|
72652
73123
|
const projectDir = findProjectDir();
|
|
72653
73124
|
if (!projectDir)
|
|
72654
73125
|
return null;
|
|
72655
|
-
const projectPath =
|
|
73126
|
+
const projectPath = join38(projectDir, "config.json");
|
|
72656
73127
|
return await loadConfigFile(projectPath);
|
|
72657
73128
|
}
|
|
72658
73129
|
|
|
@@ -72712,7 +73183,7 @@ async function configCommand(config2, options = {}) {
|
|
|
72712
73183
|
function determineConfigSources() {
|
|
72713
73184
|
const globalPath = globalConfigPath();
|
|
72714
73185
|
const projectDir = findProjectDir();
|
|
72715
|
-
const projectPath = projectDir ?
|
|
73186
|
+
const projectPath = projectDir ? join39(projectDir, "config.json") : null;
|
|
72716
73187
|
return {
|
|
72717
73188
|
global: fileExists(globalPath) ? globalPath : null,
|
|
72718
73189
|
project: projectPath && fileExists(projectPath) ? projectPath : null
|
|
@@ -72782,6 +73253,26 @@ function displayConfigWithDescriptions(obj, path14, sources, indent = 0) {
|
|
|
72782
73253
|
}
|
|
72783
73254
|
}
|
|
72784
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
|
+
}
|
|
72785
73276
|
}
|
|
72786
73277
|
function formatValue(value) {
|
|
72787
73278
|
if (value === null)
|
|
@@ -72836,6 +73327,107 @@ function formatValueForTable(value) {
|
|
|
72836
73327
|
}
|
|
72837
73328
|
return String(value);
|
|
72838
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
|
+
}
|
|
72839
73431
|
// src/cli/agents.ts
|
|
72840
73432
|
init_registry();
|
|
72841
73433
|
init_version_detection();
|
|
@@ -72892,24 +73484,24 @@ async function diagnose(options) {
|
|
|
72892
73484
|
|
|
72893
73485
|
// src/commands/logs.ts
|
|
72894
73486
|
import { existsSync as existsSync30 } from "fs";
|
|
72895
|
-
import { join as
|
|
73487
|
+
import { join as join44 } from "path";
|
|
72896
73488
|
|
|
72897
73489
|
// src/commands/logs-formatter.ts
|
|
72898
73490
|
init_source();
|
|
72899
73491
|
init_formatter();
|
|
72900
|
-
import { readdirSync as
|
|
72901
|
-
import { join as
|
|
73492
|
+
import { readdirSync as readdirSync9 } from "fs";
|
|
73493
|
+
import { join as join43 } from "path";
|
|
72902
73494
|
|
|
72903
73495
|
// src/commands/logs-reader.ts
|
|
72904
|
-
import { existsSync as existsSync29, readdirSync as
|
|
73496
|
+
import { existsSync as existsSync29, readdirSync as readdirSync8 } from "fs";
|
|
72905
73497
|
import { readdir as readdir3 } from "fs/promises";
|
|
72906
|
-
import { join as
|
|
73498
|
+
import { join as join42 } from "path";
|
|
72907
73499
|
|
|
72908
73500
|
// src/utils/paths.ts
|
|
72909
73501
|
import { homedir as homedir4 } from "os";
|
|
72910
|
-
import { join as
|
|
73502
|
+
import { join as join41 } from "path";
|
|
72911
73503
|
function getRunsDir() {
|
|
72912
|
-
return process.env.NAX_RUNS_DIR ??
|
|
73504
|
+
return process.env.NAX_RUNS_DIR ?? join41(homedir4(), ".nax", "runs");
|
|
72913
73505
|
}
|
|
72914
73506
|
|
|
72915
73507
|
// src/commands/logs-reader.ts
|
|
@@ -72926,7 +73518,7 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
72926
73518
|
}
|
|
72927
73519
|
let matched = null;
|
|
72928
73520
|
for (const entry of entries) {
|
|
72929
|
-
const metaPath =
|
|
73521
|
+
const metaPath = join42(runsDir, entry, "meta.json");
|
|
72930
73522
|
try {
|
|
72931
73523
|
const meta3 = await Bun.file(metaPath).json();
|
|
72932
73524
|
if (meta3.runId === runId || meta3.runId.startsWith(runId)) {
|
|
@@ -72942,20 +73534,20 @@ async function resolveRunFileFromRegistry(runId) {
|
|
|
72942
73534
|
console.log(`Log directory unavailable for run: ${runId}`);
|
|
72943
73535
|
return null;
|
|
72944
73536
|
}
|
|
72945
|
-
const files =
|
|
73537
|
+
const files = readdirSync8(matched.eventsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
72946
73538
|
if (files.length === 0) {
|
|
72947
73539
|
console.log(`No log files found for run: ${runId}`);
|
|
72948
73540
|
return null;
|
|
72949
73541
|
}
|
|
72950
73542
|
const specificFile = files.find((f) => f === `${matched.runId}.jsonl`);
|
|
72951
|
-
return
|
|
73543
|
+
return join42(matched.eventsDir, specificFile ?? files[0]);
|
|
72952
73544
|
}
|
|
72953
73545
|
async function selectRunFile(runsDir) {
|
|
72954
|
-
const files =
|
|
73546
|
+
const files = readdirSync8(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
72955
73547
|
if (files.length === 0) {
|
|
72956
73548
|
return null;
|
|
72957
73549
|
}
|
|
72958
|
-
return
|
|
73550
|
+
return join42(runsDir, files[0]);
|
|
72959
73551
|
}
|
|
72960
73552
|
async function extractRunSummary(filePath) {
|
|
72961
73553
|
const file3 = Bun.file(filePath);
|
|
@@ -73029,7 +73621,7 @@ var LOG_LEVEL_PRIORITY2 = {
|
|
|
73029
73621
|
error: 3
|
|
73030
73622
|
};
|
|
73031
73623
|
async function displayRunsList(runsDir) {
|
|
73032
|
-
const files =
|
|
73624
|
+
const files = readdirSync9(runsDir).filter((f) => f.endsWith(".jsonl") && f !== "latest.jsonl").sort().reverse();
|
|
73033
73625
|
if (files.length === 0) {
|
|
73034
73626
|
console.log(source_default.dim("No runs found"));
|
|
73035
73627
|
return;
|
|
@@ -73040,7 +73632,7 @@ Runs:
|
|
|
73040
73632
|
console.log(source_default.gray(" Timestamp Stories Duration Cost Status"));
|
|
73041
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"));
|
|
73042
73634
|
for (const file3 of files) {
|
|
73043
|
-
const filePath =
|
|
73635
|
+
const filePath = join43(runsDir, file3);
|
|
73044
73636
|
const summary = await extractRunSummary(filePath);
|
|
73045
73637
|
const timestamp = file3.replace(".jsonl", "");
|
|
73046
73638
|
const stories = summary ? `${summary.passed}/${summary.total}` : "?/?";
|
|
@@ -73154,7 +73746,7 @@ async function logsCommand(options) {
|
|
|
73154
73746
|
return;
|
|
73155
73747
|
}
|
|
73156
73748
|
const resolved = resolveProject({ dir: options.dir });
|
|
73157
|
-
const naxDir =
|
|
73749
|
+
const naxDir = join44(resolved.projectDir, ".nax");
|
|
73158
73750
|
const configPath = resolved.configPath;
|
|
73159
73751
|
const configFile = Bun.file(configPath);
|
|
73160
73752
|
const config2 = await configFile.json();
|
|
@@ -73162,8 +73754,8 @@ async function logsCommand(options) {
|
|
|
73162
73754
|
if (!featureName) {
|
|
73163
73755
|
throw new Error("No feature specified in config.json");
|
|
73164
73756
|
}
|
|
73165
|
-
const featureDir =
|
|
73166
|
-
const runsDir =
|
|
73757
|
+
const featureDir = join44(naxDir, "features", featureName);
|
|
73758
|
+
const runsDir = join44(featureDir, "runs");
|
|
73167
73759
|
if (!existsSync30(runsDir)) {
|
|
73168
73760
|
throw new Error(`No runs directory found for feature: ${featureName}`);
|
|
73169
73761
|
}
|
|
@@ -73188,7 +73780,7 @@ init_config();
|
|
|
73188
73780
|
init_prd();
|
|
73189
73781
|
init_precheck();
|
|
73190
73782
|
import { existsSync as existsSync31 } from "fs";
|
|
73191
|
-
import { join as
|
|
73783
|
+
import { join as join45 } from "path";
|
|
73192
73784
|
async function precheckCommand(options) {
|
|
73193
73785
|
const resolved = resolveProject({
|
|
73194
73786
|
dir: options.dir,
|
|
@@ -73210,9 +73802,9 @@ async function precheckCommand(options) {
|
|
|
73210
73802
|
process.exit(1);
|
|
73211
73803
|
}
|
|
73212
73804
|
}
|
|
73213
|
-
const naxDir =
|
|
73214
|
-
const featureDir =
|
|
73215
|
-
const prdPath =
|
|
73805
|
+
const naxDir = join45(resolved.projectDir, ".nax");
|
|
73806
|
+
const featureDir = join45(naxDir, "features", featureName);
|
|
73807
|
+
const prdPath = join45(featureDir, "prd.json");
|
|
73216
73808
|
if (!existsSync31(featureDir)) {
|
|
73217
73809
|
console.error(source_default.red(`Feature not found: ${featureName}`));
|
|
73218
73810
|
process.exit(1);
|
|
@@ -73234,7 +73826,7 @@ async function precheckCommand(options) {
|
|
|
73234
73826
|
// src/commands/runs.ts
|
|
73235
73827
|
init_source();
|
|
73236
73828
|
import { readdir as readdir4 } from "fs/promises";
|
|
73237
|
-
import { join as
|
|
73829
|
+
import { join as join46 } from "path";
|
|
73238
73830
|
var DEFAULT_LIMIT = 20;
|
|
73239
73831
|
var _runsCmdDeps = {
|
|
73240
73832
|
getRunsDir
|
|
@@ -73289,7 +73881,7 @@ async function runsCommand(options = {}) {
|
|
|
73289
73881
|
}
|
|
73290
73882
|
const rows = [];
|
|
73291
73883
|
for (const entry of entries) {
|
|
73292
|
-
const metaPath =
|
|
73884
|
+
const metaPath = join46(runsDir, entry, "meta.json");
|
|
73293
73885
|
let meta3;
|
|
73294
73886
|
try {
|
|
73295
73887
|
meta3 = await Bun.file(metaPath).json();
|
|
@@ -73366,7 +73958,7 @@ async function runsCommand(options = {}) {
|
|
|
73366
73958
|
|
|
73367
73959
|
// src/commands/unlock.ts
|
|
73368
73960
|
init_source();
|
|
73369
|
-
import { join as
|
|
73961
|
+
import { join as join47 } from "path";
|
|
73370
73962
|
function isProcessAlive3(pid) {
|
|
73371
73963
|
try {
|
|
73372
73964
|
process.kill(pid, 0);
|
|
@@ -73381,7 +73973,7 @@ function formatLockAge(ageMs) {
|
|
|
73381
73973
|
}
|
|
73382
73974
|
async function unlockCommand(options) {
|
|
73383
73975
|
const workdir = options.dir ?? process.cwd();
|
|
73384
|
-
const lockPath =
|
|
73976
|
+
const lockPath = join47(workdir, "nax.lock");
|
|
73385
73977
|
const lockFile = Bun.file(lockPath);
|
|
73386
73978
|
const exists = await lockFile.exists();
|
|
73387
73979
|
if (!exists) {
|
|
@@ -73438,6 +74030,10 @@ init_crash_recovery();
|
|
|
73438
74030
|
init_story_context();
|
|
73439
74031
|
async function runCompletionPhase(options) {
|
|
73440
74032
|
const logger = getSafeLogger();
|
|
74033
|
+
logger?.debug("execution", "Completion phase started", {
|
|
74034
|
+
acceptanceEnabled: options.config.acceptance?.enabled,
|
|
74035
|
+
isComplete: isComplete(options.prd)
|
|
74036
|
+
});
|
|
73441
74037
|
if (options.config.acceptance.enabled && isComplete(options.prd)) {
|
|
73442
74038
|
const { runAcceptanceLoop: runAcceptanceLoop2 } = await Promise.resolve().then(() => (init_acceptance_loop(), exports_acceptance_loop));
|
|
73443
74039
|
const acceptanceResult = await runAcceptanceLoop2({
|
|
@@ -73505,9 +74101,12 @@ async function runCompletionPhase(options) {
|
|
|
73505
74101
|
formatterMode: options.formatterMode
|
|
73506
74102
|
});
|
|
73507
74103
|
}
|
|
74104
|
+
logger?.debug("execution", "Completion phase \u2014 stopping heartbeat and writing exit summary");
|
|
73508
74105
|
stopHeartbeat();
|
|
73509
74106
|
await writeExitSummary(options.logFilePath, options.totalCost, options.iterations, options.storiesCompleted, durationMs);
|
|
74107
|
+
logger?.debug("execution", "Completion phase \u2014 auto-committing dirty files");
|
|
73510
74108
|
await autoCommitIfDirty(options.workdir, "run.complete", "run-summary", options.feature);
|
|
74109
|
+
logger?.debug("execution", "Completion phase done \u2014 returning to runner");
|
|
73511
74110
|
return {
|
|
73512
74111
|
durationMs,
|
|
73513
74112
|
runCompletedAt
|
|
@@ -73645,6 +74244,12 @@ async function runExecutionPhase(options, prd, pluginRegistry) {
|
|
|
73645
74244
|
storiesCompleted = unifiedResult.storiesCompleted;
|
|
73646
74245
|
totalCost = unifiedResult.totalCost;
|
|
73647
74246
|
allStoryMetrics.push(...unifiedResult.allStoryMetrics);
|
|
74247
|
+
logger?.debug("execution", "Execution phase complete \u2014 handing off to completion phase", {
|
|
74248
|
+
exitReason: unifiedResult.exitReason,
|
|
74249
|
+
iterations,
|
|
74250
|
+
storiesCompleted,
|
|
74251
|
+
totalCost
|
|
74252
|
+
});
|
|
73648
74253
|
return { prd, iterations, storiesCompleted, totalCost, allStoryMetrics };
|
|
73649
74254
|
}
|
|
73650
74255
|
|
|
@@ -73803,15 +74408,20 @@ async function run(options) {
|
|
|
73803
74408
|
durationMs
|
|
73804
74409
|
};
|
|
73805
74410
|
} finally {
|
|
74411
|
+
const logger2 = getSafeLogger();
|
|
74412
|
+
logger2?.debug("execution", "Runner finally block \u2014 starting cleanup");
|
|
73806
74413
|
stopHeartbeat();
|
|
73807
74414
|
cleanupCrashHandlers();
|
|
74415
|
+
logger2?.debug("execution", "Runner finally \u2014 sweeping ACP sessions");
|
|
73808
74416
|
await sweepFeatureSessions(workdir, feature).catch(() => {});
|
|
74417
|
+
logger2?.debug("execution", "Runner finally \u2014 ACP sweep done");
|
|
73809
74418
|
let branch = "";
|
|
73810
74419
|
try {
|
|
73811
74420
|
const { stdout, exitCode } = await gitWithTimeout(["branch", "--show-current"], workdir);
|
|
73812
74421
|
if (exitCode === 0)
|
|
73813
74422
|
branch = stdout.trim();
|
|
73814
74423
|
} catch {}
|
|
74424
|
+
logger2?.debug("execution", "Runner finally \u2014 running cleanupRun");
|
|
73815
74425
|
const { cleanupRun: cleanupRun2 } = await Promise.resolve().then(() => (init_run_cleanup(), exports_run_cleanup));
|
|
73816
74426
|
await cleanupRun2({
|
|
73817
74427
|
runId,
|
|
@@ -73827,6 +74437,7 @@ async function run(options) {
|
|
|
73827
74437
|
branch,
|
|
73828
74438
|
version: NAX_VERSION
|
|
73829
74439
|
});
|
|
74440
|
+
logger2?.debug("execution", "Runner finally \u2014 cleanupRun done, run() returning");
|
|
73830
74441
|
}
|
|
73831
74442
|
}
|
|
73832
74443
|
|
|
@@ -81185,15 +81796,15 @@ Next: nax generate --package ${options.package}`));
|
|
|
81185
81796
|
}
|
|
81186
81797
|
return;
|
|
81187
81798
|
}
|
|
81188
|
-
const naxDir =
|
|
81799
|
+
const naxDir = join58(workdir, ".nax");
|
|
81189
81800
|
if (existsSync34(naxDir) && !options.force) {
|
|
81190
81801
|
console.log(source_default.yellow("nax already initialized. Use --force to overwrite."));
|
|
81191
81802
|
return;
|
|
81192
81803
|
}
|
|
81193
|
-
|
|
81194
|
-
|
|
81195
|
-
await Bun.write(
|
|
81196
|
-
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({
|
|
81197
81808
|
hooks: {
|
|
81198
81809
|
"on-start": { command: 'echo "nax started: $NAX_FEATURE"', enabled: false },
|
|
81199
81810
|
"on-complete": { command: 'echo "nax complete: $NAX_FEATURE"', enabled: false },
|
|
@@ -81201,12 +81812,12 @@ Next: nax generate --package ${options.package}`));
|
|
|
81201
81812
|
"on-error": { command: 'echo "nax error: $NAX_REASON"', enabled: false }
|
|
81202
81813
|
}
|
|
81203
81814
|
}, null, 2));
|
|
81204
|
-
await Bun.write(
|
|
81815
|
+
await Bun.write(join58(naxDir, ".gitignore"), `# nax temp files
|
|
81205
81816
|
*.tmp
|
|
81206
81817
|
.paused.json
|
|
81207
81818
|
.nax-verifier-verdict.json
|
|
81208
81819
|
`);
|
|
81209
|
-
await Bun.write(
|
|
81820
|
+
await Bun.write(join58(naxDir, "context.md"), `# Project Context
|
|
81210
81821
|
|
|
81211
81822
|
This document defines coding standards, architectural decisions, and forbidden patterns for this project.
|
|
81212
81823
|
Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cursorrules, etc.) from this file.
|
|
@@ -81291,7 +81902,7 @@ Run \`nax generate\` to regenerate agent config files (CLAUDE.md, AGENTS.md, .cu
|
|
|
81291
81902
|
console.log(source_default.dim(`
|
|
81292
81903
|
Next: nax features create <name>`));
|
|
81293
81904
|
});
|
|
81294
|
-
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) => {
|
|
81295
81906
|
let workdir;
|
|
81296
81907
|
try {
|
|
81297
81908
|
workdir = validateDirectory(options.dir);
|
|
@@ -81327,13 +81938,17 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81327
81938
|
formatterMode = "quiet";
|
|
81328
81939
|
}
|
|
81329
81940
|
const naxDir = findProjectDir(workdir);
|
|
81330
|
-
const
|
|
81941
|
+
const cliOverrides = {};
|
|
81942
|
+
if (options.profile) {
|
|
81943
|
+
cliOverrides.profile = options.profile;
|
|
81944
|
+
}
|
|
81945
|
+
const config2 = await loadConfig(naxDir ?? undefined, cliOverrides);
|
|
81331
81946
|
if (!naxDir) {
|
|
81332
81947
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81333
81948
|
process.exit(1);
|
|
81334
81949
|
}
|
|
81335
|
-
const featureDir =
|
|
81336
|
-
const prdPath =
|
|
81950
|
+
const featureDir = join58(naxDir, "features", options.feature);
|
|
81951
|
+
const prdPath = join58(featureDir, "prd.json");
|
|
81337
81952
|
if (options.plan && options.from) {
|
|
81338
81953
|
if (existsSync34(prdPath) && !options.force) {
|
|
81339
81954
|
console.error(source_default.red(`Error: prd.json already exists for feature "${options.feature}".`));
|
|
@@ -81355,10 +81970,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81355
81970
|
}
|
|
81356
81971
|
}
|
|
81357
81972
|
try {
|
|
81358
|
-
const planLogDir =
|
|
81359
|
-
|
|
81973
|
+
const planLogDir = join58(featureDir, "plan");
|
|
81974
|
+
mkdirSync6(planLogDir, { recursive: true });
|
|
81360
81975
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81361
|
-
const planLogPath =
|
|
81976
|
+
const planLogPath = join58(planLogDir, `${planLogId}.jsonl`);
|
|
81362
81977
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
81363
81978
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
81364
81979
|
console.log(source_default.dim(" [Planning phase: generating PRD from spec]"));
|
|
@@ -81402,10 +82017,10 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81402
82017
|
process.exit(1);
|
|
81403
82018
|
}
|
|
81404
82019
|
resetLogger();
|
|
81405
|
-
const runsDir =
|
|
81406
|
-
|
|
82020
|
+
const runsDir = join58(featureDir, "runs");
|
|
82021
|
+
mkdirSync6(runsDir, { recursive: true });
|
|
81407
82022
|
const runId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81408
|
-
const logFilePath =
|
|
82023
|
+
const logFilePath = join58(runsDir, `${runId}.jsonl`);
|
|
81409
82024
|
const isTTY = process.stdout.isTTY ?? false;
|
|
81410
82025
|
const headlessFlag = options.headless ?? false;
|
|
81411
82026
|
const headlessEnv = process.env.NAX_HEADLESS === "1";
|
|
@@ -81421,7 +82036,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81421
82036
|
config2.autoMode.defaultAgent = options.agent;
|
|
81422
82037
|
}
|
|
81423
82038
|
config2.execution.maxIterations = Number.parseInt(options.maxIterations, 10);
|
|
81424
|
-
const globalNaxDir =
|
|
82039
|
+
const globalNaxDir = join58(homedir8(), ".nax");
|
|
81425
82040
|
const hooks = await loadHooksConfig(naxDir, globalNaxDir);
|
|
81426
82041
|
const eventEmitter = new PipelineEventEmitter;
|
|
81427
82042
|
let tuiInstance;
|
|
@@ -81444,7 +82059,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81444
82059
|
} else {
|
|
81445
82060
|
console.log(source_default.dim(" [Headless mode \u2014 pipe output]"));
|
|
81446
82061
|
}
|
|
81447
|
-
const statusFilePath =
|
|
82062
|
+
const statusFilePath = join58(workdir, ".nax", "status.json");
|
|
81448
82063
|
let parallel;
|
|
81449
82064
|
if (options.parallel !== undefined) {
|
|
81450
82065
|
parallel = Number.parseInt(options.parallel, 10);
|
|
@@ -81470,7 +82085,7 @@ program2.command("run").description("Run the orchestration loop for a feature").
|
|
|
81470
82085
|
headless: useHeadless,
|
|
81471
82086
|
skipPrecheck: options.skipPrecheck ?? false
|
|
81472
82087
|
});
|
|
81473
|
-
const latestSymlink =
|
|
82088
|
+
const latestSymlink = join58(runsDir, "latest.jsonl");
|
|
81474
82089
|
try {
|
|
81475
82090
|
if (existsSync34(latestSymlink)) {
|
|
81476
82091
|
Bun.spawnSync(["rm", latestSymlink]);
|
|
@@ -81508,9 +82123,9 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
81508
82123
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81509
82124
|
process.exit(1);
|
|
81510
82125
|
}
|
|
81511
|
-
const featureDir =
|
|
81512
|
-
|
|
81513
|
-
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}
|
|
81514
82129
|
|
|
81515
82130
|
## Overview
|
|
81516
82131
|
|
|
@@ -81543,7 +82158,7 @@ features.command("create <name>").description("Create a new feature").option("-d
|
|
|
81543
82158
|
|
|
81544
82159
|
<!-- What this feature explicitly does NOT cover. -->
|
|
81545
82160
|
`);
|
|
81546
|
-
await Bun.write(
|
|
82161
|
+
await Bun.write(join58(featureDir, "progress.txt"), `# Progress: ${name}
|
|
81547
82162
|
|
|
81548
82163
|
Created: ${new Date().toISOString()}
|
|
81549
82164
|
|
|
@@ -81569,13 +82184,13 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
81569
82184
|
console.error(source_default.red("nax not initialized."));
|
|
81570
82185
|
process.exit(1);
|
|
81571
82186
|
}
|
|
81572
|
-
const featuresDir =
|
|
82187
|
+
const featuresDir = join58(naxDir, "features");
|
|
81573
82188
|
if (!existsSync34(featuresDir)) {
|
|
81574
82189
|
console.log(source_default.dim("No features yet."));
|
|
81575
82190
|
return;
|
|
81576
82191
|
}
|
|
81577
|
-
const { readdirSync:
|
|
81578
|
-
const entries =
|
|
82192
|
+
const { readdirSync: readdirSync10 } = await import("fs");
|
|
82193
|
+
const entries = readdirSync10(featuresDir, { withFileTypes: true }).filter((e) => e.isDirectory()).map((e) => e.name);
|
|
81579
82194
|
if (entries.length === 0) {
|
|
81580
82195
|
console.log(source_default.dim("No features yet."));
|
|
81581
82196
|
return;
|
|
@@ -81584,7 +82199,7 @@ features.command("list").description("List all features").option("-d, --dir <pat
|
|
|
81584
82199
|
Features:
|
|
81585
82200
|
`));
|
|
81586
82201
|
for (const name of entries) {
|
|
81587
|
-
const prdPath =
|
|
82202
|
+
const prdPath = join58(featuresDir, name, "prd.json");
|
|
81588
82203
|
if (existsSync34(prdPath)) {
|
|
81589
82204
|
const prd = await loadPRD(prdPath);
|
|
81590
82205
|
const c = countStories(prd);
|
|
@@ -81595,7 +82210,7 @@ Features:
|
|
|
81595
82210
|
}
|
|
81596
82211
|
console.log();
|
|
81597
82212
|
});
|
|
81598
|
-
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) => {
|
|
81599
82214
|
if (description) {
|
|
81600
82215
|
console.error(source_default.red(`Error: Positional args removed in plan v2.
|
|
81601
82216
|
|
|
@@ -81614,11 +82229,15 @@ Use: nax plan -f <feature> --from <spec>`));
|
|
|
81614
82229
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81615
82230
|
process.exit(1);
|
|
81616
82231
|
}
|
|
81617
|
-
const
|
|
81618
|
-
|
|
81619
|
-
|
|
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 });
|
|
81620
82239
|
const planLogId = new Date().toISOString().replace(/:/g, "-").replace(/\..+/, "");
|
|
81621
|
-
const planLogPath =
|
|
82240
|
+
const planLogPath = join58(featureLogDir, `${planLogId}.jsonl`);
|
|
81622
82241
|
initLogger({ level: "info", filePath: planLogPath, useChalk: false, headless: true });
|
|
81623
82242
|
console.log(source_default.dim(` [Plan log: ${planLogPath}]`));
|
|
81624
82243
|
try {
|
|
@@ -81669,7 +82288,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
81669
82288
|
console.error(source_default.red("nax not initialized. Run: nax init"));
|
|
81670
82289
|
process.exit(1);
|
|
81671
82290
|
}
|
|
81672
|
-
const featureDir =
|
|
82291
|
+
const featureDir = join58(naxDir, "features", options.feature);
|
|
81673
82292
|
if (!existsSync34(featureDir)) {
|
|
81674
82293
|
console.error(source_default.red(`Feature "${options.feature}" not found.`));
|
|
81675
82294
|
process.exit(1);
|
|
@@ -81685,7 +82304,7 @@ program2.command("analyze").description("(deprecated) Parse spec.md into prd.jso
|
|
|
81685
82304
|
specPath: options.from,
|
|
81686
82305
|
reclassify: options.reclassify
|
|
81687
82306
|
});
|
|
81688
|
-
const prdPath =
|
|
82307
|
+
const prdPath = join58(featureDir, "prd.json");
|
|
81689
82308
|
await Bun.write(prdPath, JSON.stringify(prd, null, 2));
|
|
81690
82309
|
const c = countStories(prd);
|
|
81691
82310
|
console.log(source_default.green(`
|
|
@@ -81718,7 +82337,7 @@ program2.command("agents").description("List available coding agents with status
|
|
|
81718
82337
|
process.exit(1);
|
|
81719
82338
|
}
|
|
81720
82339
|
});
|
|
81721
|
-
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) => {
|
|
81722
82341
|
let workdir;
|
|
81723
82342
|
try {
|
|
81724
82343
|
workdir = validateDirectory(options.dir);
|
|
@@ -81735,6 +82354,52 @@ program2.command("config").description("Display effective merged configuration")
|
|
|
81735
82354
|
process.exit(1);
|
|
81736
82355
|
}
|
|
81737
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
|
+
});
|
|
81738
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) => {
|
|
81739
82404
|
let workdir;
|
|
81740
82405
|
try {
|