nexus-agents 2.28.0 → 2.29.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/README.md +11 -10
- package/dist/adaptive-memory-RST6DZYR.js +15 -0
- package/dist/chunk-2UR7YN6T.js +700 -0
- package/dist/chunk-2UR7YN6T.js.map +1 -0
- package/dist/chunk-2UUUKVNR.js +61 -0
- package/dist/chunk-2UUUKVNR.js.map +1 -0
- package/dist/chunk-3EVVQ32X.js +838 -0
- package/dist/chunk-3EVVQ32X.js.map +1 -0
- package/dist/{chunk-UGNLR4NZ.js → chunk-3GXDN4AX.js} +2 -2
- package/dist/{chunk-LKSTILEE.js → chunk-4AGPR6XZ.js} +194 -1461
- package/dist/chunk-4AGPR6XZ.js.map +1 -0
- package/dist/chunk-4HA5PAL7.js +246 -0
- package/dist/chunk-4HA5PAL7.js.map +1 -0
- package/dist/chunk-633WH2ML.js +127 -0
- package/dist/chunk-633WH2ML.js.map +1 -0
- package/dist/chunk-7SKAKA4I.js +90 -0
- package/dist/chunk-7SKAKA4I.js.map +1 -0
- package/dist/chunk-AP2FD37C.js +127 -0
- package/dist/chunk-AP2FD37C.js.map +1 -0
- package/dist/chunk-BC3M4VLP.js +359 -0
- package/dist/chunk-BC3M4VLP.js.map +1 -0
- package/dist/chunk-BQ4YXGGQ.js +127 -0
- package/dist/chunk-BQ4YXGGQ.js.map +1 -0
- package/dist/chunk-C2C5ONFR.js +195 -0
- package/dist/chunk-C2C5ONFR.js.map +1 -0
- package/dist/chunk-CGWRJ4EM.js +12661 -0
- package/dist/chunk-CGWRJ4EM.js.map +1 -0
- package/dist/chunk-CLYZ7FWP.js +30 -0
- package/dist/chunk-CLYZ7FWP.js.map +1 -0
- package/dist/chunk-ED6VQWNG.js +63 -0
- package/dist/chunk-ED6VQWNG.js.map +1 -0
- package/dist/{chunk-L2SHSW4T.js → chunk-ELIFTCYM.js} +2705 -2473
- package/dist/chunk-ELIFTCYM.js.map +1 -0
- package/dist/chunk-FYJVXQHX.js +944 -0
- package/dist/chunk-FYJVXQHX.js.map +1 -0
- package/dist/{chunk-QZEAD6AG.js → chunk-FZFZ77UJ.js} +19434 -38182
- package/dist/chunk-FZFZ77UJ.js.map +1 -0
- package/dist/chunk-IECE6DBS.js +1585 -0
- package/dist/chunk-IECE6DBS.js.map +1 -0
- package/dist/chunk-LLGUX44Z.js +356 -0
- package/dist/chunk-LLGUX44Z.js.map +1 -0
- package/dist/chunk-MRU6L7YJ.js +931 -0
- package/dist/chunk-MRU6L7YJ.js.map +1 -0
- package/dist/chunk-N5SY7V45.js +44 -0
- package/dist/chunk-N5SY7V45.js.map +1 -0
- package/dist/chunk-OOIPRRPX.js +3790 -0
- package/dist/chunk-OOIPRRPX.js.map +1 -0
- package/dist/chunk-SRECH7OQ.js +354 -0
- package/dist/chunk-SRECH7OQ.js.map +1 -0
- package/dist/{chunk-YSDUVCCZ.js → chunk-VKRMXD62.js} +6 -6
- package/dist/{chunk-E7EX2KQJ.js → chunk-XU3NADFE.js} +2 -2
- package/dist/chunk-YSTJEMQX.js +122 -0
- package/dist/chunk-YSTJEMQX.js.map +1 -0
- package/dist/cli-circuit-breaker-5FAODXVY.js +13 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +1485 -683
- package/dist/cli.js.map +1 -1
- package/dist/codebase-search-CZUA37RU.js +9 -0
- package/dist/composite-router-FNW7ZWL7.js +17 -0
- package/dist/composite-router-FNW7ZWL7.js.map +1 -0
- package/dist/consensus-vote-757YULIP.js +25 -0
- package/dist/consensus-vote-757YULIP.js.map +1 -0
- package/dist/{dist-H5XNXVAV.js → dist-CV74KUT7.js} +1521 -972
- package/dist/dist-CV74KUT7.js.map +1 -0
- package/dist/{doctor-deep-BDE2PHVX.js → doctor-deep-LMCEAFU4.js} +4 -3
- package/dist/doctor-deep-LMCEAFU4.js.map +1 -0
- package/dist/expert-bridge-L2D4OXOR.js +10 -0
- package/dist/expert-bridge-L2D4OXOR.js.map +1 -0
- package/dist/expert-config-A5CHKUGI.js +22 -0
- package/dist/expert-config-A5CHKUGI.js.map +1 -0
- package/dist/factory-ELEDP2WD.js +17 -0
- package/dist/factory-ELEDP2WD.js.map +1 -0
- package/dist/factory-IDTIBX6B.js +14 -0
- package/dist/factory-IDTIBX6B.js.map +1 -0
- package/dist/index.d.ts +1814 -777
- package/dist/index.js +785 -355
- package/dist/index.js.map +1 -1
- package/dist/issue-triage-SJPKJLXH.js +15 -0
- package/dist/issue-triage-SJPKJLXH.js.map +1 -0
- package/dist/learning-persistence-WMWZJZ35.js +16 -0
- package/dist/learning-persistence-WMWZJZ35.js.map +1 -0
- package/dist/mcp-config-2OXIOMJ6.js +12 -0
- package/dist/mcp-config-2OXIOMJ6.js.map +1 -0
- package/dist/mobimem-5S3VLNSU.js +13 -0
- package/dist/mobimem-5S3VLNSU.js.map +1 -0
- package/dist/{model-capabilities-types-CSWO27YN.d.ts → model-capabilities-types-B57GZryc.d.ts} +1 -1
- package/dist/repo-analyze-HWMXSK5C.js +24 -0
- package/dist/repo-analyze-HWMXSK5C.js.map +1 -0
- package/dist/repo-security-plan-MUFDGWSQ.js +17 -0
- package/dist/repo-security-plan-MUFDGWSQ.js.map +1 -0
- package/dist/research-helpers-synthesize-OBQJ5BGX.js +10 -0
- package/dist/research-helpers-synthesize-OBQJ5BGX.js.map +1 -0
- package/dist/routing-memory-3QBQTS4A.js +13 -0
- package/dist/routing-memory-3QBQTS4A.js.map +1 -0
- package/dist/session-memory-VXWLOFRC.js +22 -0
- package/dist/session-memory-VXWLOFRC.js.map +1 -0
- package/dist/{setup-command-SS7LMN7Y.js → setup-command-E6MXO5RZ.js} +9 -5
- package/dist/setup-command-E6MXO5RZ.js.map +1 -0
- package/dist/setup-config-O5F3AZBL.js +10 -0
- package/dist/setup-config-O5F3AZBL.js.map +1 -0
- package/dist/shared-memory-AEO2HJLC.js +8 -0
- package/dist/shared-memory-AEO2HJLC.js.map +1 -0
- package/dist/symbol-extractor-UEBANFSN.js +10 -0
- package/dist/symbol-extractor-UEBANFSN.js.map +1 -0
- package/dist/weather-report-MUGSIOU5.js +15 -0
- package/dist/weather-report-MUGSIOU5.js.map +1 -0
- package/package.json +18 -19
- package/dist/chunk-L2SHSW4T.js.map +0 -1
- package/dist/chunk-LKSTILEE.js.map +0 -1
- package/dist/chunk-QZEAD6AG.js.map +0 -1
- package/dist/dist-H5XNXVAV.js.map +0 -1
- package/dist/setup-config-DSMOOLVW.js +0 -9
- /package/dist/{doctor-deep-BDE2PHVX.js.map → adaptive-memory-RST6DZYR.js.map} +0 -0
- /package/dist/{chunk-UGNLR4NZ.js.map → chunk-3GXDN4AX.js.map} +0 -0
- /package/dist/{chunk-YSDUVCCZ.js.map → chunk-VKRMXD62.js.map} +0 -0
- /package/dist/{chunk-E7EX2KQJ.js.map → chunk-XU3NADFE.js.map} +0 -0
- /package/dist/{setup-command-SS7LMN7Y.js.map → cli-circuit-breaker-5FAODXVY.js.map} +0 -0
- /package/dist/{setup-config-DSMOOLVW.js.map → codebase-search-CZUA37RU.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,44 +1,52 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import {
|
|
3
|
+
calculateConsensus,
|
|
4
|
+
countByCategory,
|
|
5
|
+
countBySeverity,
|
|
6
|
+
createFailedReview,
|
|
7
|
+
determineApproval,
|
|
8
|
+
determineDecision,
|
|
9
|
+
extractSummary,
|
|
10
|
+
formatReviewComment,
|
|
11
|
+
generateSummary,
|
|
12
|
+
parseFindings,
|
|
13
|
+
sumFindings
|
|
14
|
+
} from "./chunk-6E3NMMEY.js";
|
|
15
|
+
import "./chunk-XU3NADFE.js";
|
|
16
|
+
import {
|
|
17
|
+
setupCommandAsync
|
|
18
|
+
} from "./chunk-VKRMXD62.js";
|
|
19
|
+
import "./chunk-3GXDN4AX.js";
|
|
2
20
|
import {
|
|
3
21
|
AuthHandler,
|
|
4
|
-
BUILT_IN_EXPERTS,
|
|
5
22
|
CustomExpertDefinitionSchema,
|
|
6
23
|
DEFAULTS,
|
|
7
24
|
DEFAULT_EXPERTS,
|
|
8
25
|
DEFAULT_SWE_BENCH_CONFIG,
|
|
9
|
-
DEFAULT_VOTE_TIMEOUT_MS,
|
|
10
26
|
DEVELOPMENT_POLICY,
|
|
11
27
|
ExpertFactory,
|
|
12
28
|
FeedbackIntegration,
|
|
13
29
|
MAX_SYSTEM_PROMPT_LENGTH,
|
|
14
|
-
MemoryError,
|
|
15
30
|
Orchestrator,
|
|
16
31
|
PuppeteerOrchestrator,
|
|
17
32
|
READONLY_POLICY,
|
|
18
|
-
|
|
19
|
-
SwarmObserver2 as SwarmObserver,
|
|
20
|
-
THRESHOLD_MAP,
|
|
33
|
+
SwarmObserver,
|
|
21
34
|
VALID_EXPERT_DOMAINS,
|
|
22
35
|
VALID_EXPERT_TIERS,
|
|
23
|
-
VOTER_ROLES,
|
|
24
36
|
ValidationDashboard,
|
|
25
37
|
WorkflowDefinitionSchema,
|
|
26
38
|
adaptRoutingConfig,
|
|
27
39
|
addResearchPaper,
|
|
28
40
|
analyzeTools,
|
|
29
|
-
|
|
30
|
-
collectRealVotes,
|
|
41
|
+
createAgentStages,
|
|
31
42
|
createAnnotationsProxy,
|
|
32
43
|
createAuditLogger,
|
|
33
44
|
createCodeExpert,
|
|
34
|
-
createConsensusEngine,
|
|
35
45
|
createDefaultDeps,
|
|
36
46
|
createDefaultPolicyEngine,
|
|
37
|
-
createDefaultPolicyFirewall,
|
|
38
47
|
createEventBusBridge,
|
|
39
48
|
createExecutor,
|
|
40
49
|
createFeedbackIntegration,
|
|
41
|
-
createFullGitHubProvider,
|
|
42
50
|
createGatewayServerProxy,
|
|
43
51
|
createHarnessExecutor,
|
|
44
52
|
createLearnablePolicy,
|
|
@@ -48,12 +56,13 @@ import {
|
|
|
48
56
|
createRuleBasedPolicy,
|
|
49
57
|
createSecurityExpert,
|
|
50
58
|
createSkillLibrary,
|
|
59
|
+
createTaskTracker,
|
|
51
60
|
createTemplateRegistry,
|
|
52
61
|
createTestingExpert,
|
|
53
62
|
createToolObservabilityProxy,
|
|
54
63
|
createToolRateLimiterFactory,
|
|
55
|
-
createUnifiedRegistry,
|
|
56
64
|
defaultConfig,
|
|
65
|
+
detectBackend,
|
|
57
66
|
discoverDeepMind,
|
|
58
67
|
discoverGitHubRepos,
|
|
59
68
|
discoverGoogleAI,
|
|
@@ -62,8 +71,8 @@ import {
|
|
|
62
71
|
discoverOpenAlex,
|
|
63
72
|
discoverPapersWithCode,
|
|
64
73
|
discoverSemanticScholar,
|
|
65
|
-
evaluatePolicy3 as evaluatePolicy,
|
|
66
74
|
findOverlaps,
|
|
75
|
+
flushPipelineMemory,
|
|
67
76
|
formatOverlapResult,
|
|
68
77
|
formatResearchIssueBody,
|
|
69
78
|
formatStatusResult,
|
|
@@ -80,7 +89,6 @@ import {
|
|
|
80
89
|
getDefaultTokenPath,
|
|
81
90
|
getEventBusStats,
|
|
82
91
|
getExpertRegistry,
|
|
83
|
-
getPipelineEventBus,
|
|
84
92
|
getPipelinePluginRegistry,
|
|
85
93
|
getResearchStatus,
|
|
86
94
|
getSandboxMode,
|
|
@@ -97,16 +105,15 @@ import {
|
|
|
97
105
|
loadAllExternalPacks,
|
|
98
106
|
loadConfig,
|
|
99
107
|
loadDataset,
|
|
100
|
-
|
|
108
|
+
loadSourcesRegistry,
|
|
101
109
|
loadWorkflowFile,
|
|
102
|
-
parsePRUrl,
|
|
103
110
|
rankDiscoveredItems,
|
|
104
111
|
readStoredToken,
|
|
105
|
-
registerConsensusVoteTool,
|
|
106
112
|
registerCreateExpertTool,
|
|
107
113
|
registerDelegateToModelTool,
|
|
108
114
|
registerExecuteExpertTool,
|
|
109
115
|
registerExecuteSpecTool,
|
|
116
|
+
registerExtractSymbolsTool,
|
|
110
117
|
registerIssueTriageTool,
|
|
111
118
|
registerListExpertsTool,
|
|
112
119
|
registerListWorkflowsTool,
|
|
@@ -114,11 +121,13 @@ import {
|
|
|
114
121
|
registerMemoryStatsTool,
|
|
115
122
|
registerMemoryWriteTool,
|
|
116
123
|
registerOrchestrateTool,
|
|
124
|
+
registerPipelineTool,
|
|
117
125
|
registerPrompts,
|
|
118
126
|
registerQueryTraceTool,
|
|
119
127
|
registerRegistryImportTool,
|
|
120
128
|
registerRepoAnalyzeTool,
|
|
121
129
|
registerRepoSecurityPlanTool,
|
|
130
|
+
registerResearchAddSourceTool,
|
|
122
131
|
registerResearchAddTool,
|
|
123
132
|
registerResearchAnalyzeTool,
|
|
124
133
|
registerResearchCatalogReviewTool,
|
|
@@ -128,58 +137,88 @@ import {
|
|
|
128
137
|
registerResources,
|
|
129
138
|
registerRunGraphWorkflowTool,
|
|
130
139
|
registerRunWorkflowTool,
|
|
140
|
+
registerSearchCodebaseTool,
|
|
131
141
|
registerTools,
|
|
132
142
|
registerWeatherReportTool,
|
|
133
|
-
resolveToken,
|
|
134
143
|
resolveV2Config,
|
|
135
144
|
runBenchmarkInstances,
|
|
136
|
-
|
|
145
|
+
runDevPipeline,
|
|
146
|
+
saveSourcesRegistry,
|
|
137
147
|
setGlobalToolRateLimiterFactory,
|
|
138
|
-
shutdownToolMemory,
|
|
139
|
-
synthesizeResearch,
|
|
140
148
|
validateArgs,
|
|
141
149
|
validateCommand,
|
|
142
150
|
validateNexusEnv,
|
|
143
|
-
validateTimeout,
|
|
144
151
|
validateWorkflow,
|
|
145
152
|
wrapInMarkdownFence
|
|
146
|
-
} from "./chunk-
|
|
153
|
+
} from "./chunk-FZFZ77UJ.js";
|
|
147
154
|
import {
|
|
148
|
-
|
|
149
|
-
} from "./chunk-
|
|
155
|
+
resolveToken
|
|
156
|
+
} from "./chunk-7SKAKA4I.js";
|
|
157
|
+
import {
|
|
158
|
+
CATEGORY_DISPLAY_NAMES,
|
|
159
|
+
DEFAULT_PR_REVIEW_CONFIG
|
|
160
|
+
} from "./chunk-X2M7OF27.js";
|
|
161
|
+
import {
|
|
162
|
+
DEFAULT_VOTE_TIMEOUT_MS,
|
|
163
|
+
THRESHOLD_MAP,
|
|
164
|
+
VOTER_ROLES,
|
|
165
|
+
collectRealVotes,
|
|
166
|
+
createConsensusEngine,
|
|
167
|
+
createDefaultPolicyFirewall,
|
|
168
|
+
createUnifiedRegistry,
|
|
169
|
+
registerConsensusVoteTool,
|
|
170
|
+
shutdownToolMemory,
|
|
171
|
+
validateTimeout
|
|
172
|
+
} from "./chunk-CGWRJ4EM.js";
|
|
173
|
+
import {
|
|
174
|
+
loadPapersRegistry,
|
|
175
|
+
loadTechniquesRegistry,
|
|
176
|
+
savePapersRegistry,
|
|
177
|
+
synthesizeResearch
|
|
178
|
+
} from "./chunk-MRU6L7YJ.js";
|
|
179
|
+
import {
|
|
180
|
+
classifyTrust,
|
|
181
|
+
createFullGitHubProvider,
|
|
182
|
+
evaluatePolicy,
|
|
183
|
+
parsePRUrl,
|
|
184
|
+
sanitizeInput
|
|
185
|
+
} from "./chunk-IECE6DBS.js";
|
|
186
|
+
import "./chunk-4HA5PAL7.js";
|
|
187
|
+
import "./chunk-2UR7YN6T.js";
|
|
188
|
+
import "./chunk-BC3M4VLP.js";
|
|
189
|
+
import "./chunk-AP2FD37C.js";
|
|
190
|
+
import "./chunk-BQ4YXGGQ.js";
|
|
191
|
+
import "./chunk-ED6VQWNG.js";
|
|
192
|
+
import {
|
|
193
|
+
BUILT_IN_EXPERTS
|
|
194
|
+
} from "./chunk-OOIPRRPX.js";
|
|
150
195
|
import {
|
|
151
196
|
VERSION,
|
|
152
|
-
capitalize,
|
|
153
|
-
capitalizeKebab,
|
|
154
197
|
closeServer,
|
|
155
198
|
connectTransport,
|
|
156
|
-
createAllAdapters,
|
|
157
199
|
createServer,
|
|
158
200
|
doctorCommand,
|
|
159
|
-
getAvailableClis,
|
|
160
201
|
initDataDirectories,
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
import "./chunk-UGNLR4NZ.js";
|
|
202
|
+
runDoctor
|
|
203
|
+
} from "./chunk-3EVVQ32X.js";
|
|
204
|
+
import "./chunk-SRECH7OQ.js";
|
|
165
205
|
import {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
countBySeverity,
|
|
169
|
-
createFailedReview,
|
|
170
|
-
determineApproval,
|
|
171
|
-
determineDecision,
|
|
172
|
-
extractSummary,
|
|
173
|
-
formatReviewComment,
|
|
174
|
-
generateSummary,
|
|
175
|
-
parseFindings,
|
|
176
|
-
sumFindings
|
|
177
|
-
} from "./chunk-6E3NMMEY.js";
|
|
206
|
+
MemoryError
|
|
207
|
+
} from "./chunk-FYJVXQHX.js";
|
|
178
208
|
import {
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
209
|
+
capitalize,
|
|
210
|
+
capitalizeKebab,
|
|
211
|
+
truncateSentence
|
|
212
|
+
} from "./chunk-633WH2ML.js";
|
|
213
|
+
import "./chunk-2UUUKVNR.js";
|
|
214
|
+
import "./chunk-C2C5ONFR.js";
|
|
215
|
+
import "./chunk-YSTJEMQX.js";
|
|
216
|
+
import {
|
|
217
|
+
createAllAdapters,
|
|
218
|
+
getAvailableClis,
|
|
219
|
+
isRecord
|
|
220
|
+
} from "./chunk-4AGPR6XZ.js";
|
|
221
|
+
import "./chunk-LLGUX44Z.js";
|
|
183
222
|
import {
|
|
184
223
|
API_TIMEOUTS,
|
|
185
224
|
AgentCapability,
|
|
@@ -224,6 +263,7 @@ import {
|
|
|
224
263
|
getErrorMessage,
|
|
225
264
|
getModelCapabilities,
|
|
226
265
|
getOutcomeStore,
|
|
266
|
+
getPipelineEventBus,
|
|
227
267
|
getRandomProvider,
|
|
228
268
|
getTimeProvider,
|
|
229
269
|
logger,
|
|
@@ -239,7 +279,8 @@ import {
|
|
|
239
279
|
toError,
|
|
240
280
|
writeEmptyLine,
|
|
241
281
|
writeLine
|
|
242
|
-
} from "./chunk-
|
|
282
|
+
} from "./chunk-ELIFTCYM.js";
|
|
283
|
+
import "./chunk-CLYZ7FWP.js";
|
|
243
284
|
import "./chunk-UP2VWCW5.js";
|
|
244
285
|
|
|
245
286
|
// src/cli.ts
|
|
@@ -423,8 +464,8 @@ function resolveOutputPath(output2) {
|
|
|
423
464
|
}
|
|
424
465
|
return resolve(process.cwd(), DEFAULT_CONFIG_FILE);
|
|
425
466
|
}
|
|
426
|
-
function fileExists(
|
|
427
|
-
return existsSync(
|
|
467
|
+
function fileExists(path23) {
|
|
468
|
+
return existsSync(path23);
|
|
428
469
|
}
|
|
429
470
|
async function ensureDirectory(filePath) {
|
|
430
471
|
const dir = dirname(filePath);
|
|
@@ -432,9 +473,9 @@ async function ensureDirectory(filePath) {
|
|
|
432
473
|
await mkdir(dir, { recursive: true });
|
|
433
474
|
}
|
|
434
475
|
}
|
|
435
|
-
async function writeConfigFile(
|
|
436
|
-
await ensureDirectory(
|
|
437
|
-
await writeFile(
|
|
476
|
+
async function writeConfigFile(path23) {
|
|
477
|
+
await ensureDirectory(path23);
|
|
478
|
+
await writeFile(path23, CONFIG_TEMPLATE, "utf-8");
|
|
438
479
|
}
|
|
439
480
|
async function runConfigInit(options = {}) {
|
|
440
481
|
const outputPath = resolveOutputPath(options.output);
|
|
@@ -993,14 +1034,14 @@ Hint: Ensure the file exists and the path is correct relative to ${cwd}`
|
|
|
993
1034
|
try {
|
|
994
1035
|
return JSON.parse(content);
|
|
995
1036
|
} catch (e) {
|
|
996
|
-
const msg =
|
|
1037
|
+
const msg = getErrorMessage(e);
|
|
997
1038
|
throw new Error(`Invalid JSON in input file "${inputArg}": ${msg}`);
|
|
998
1039
|
}
|
|
999
1040
|
}
|
|
1000
1041
|
try {
|
|
1001
1042
|
return JSON.parse(inputArg);
|
|
1002
1043
|
} catch (e) {
|
|
1003
|
-
const msg =
|
|
1044
|
+
const msg = getErrorMessage(e);
|
|
1004
1045
|
throw new Error(
|
|
1005
1046
|
`Invalid JSON input: ${msg}
|
|
1006
1047
|
Hint: Provide a valid JSON string or path to a .json file.`
|
|
@@ -1403,14 +1444,14 @@ ${colors.dim}Session ended.${colors.reset}
|
|
|
1403
1444
|
});
|
|
1404
1445
|
});
|
|
1405
1446
|
rl.prompt();
|
|
1406
|
-
return new Promise((
|
|
1447
|
+
return new Promise((resolve14) => {
|
|
1407
1448
|
rl.on("line", (line) => {
|
|
1408
1449
|
void (async () => {
|
|
1409
1450
|
try {
|
|
1410
1451
|
const shouldExit = await processLine(line, session, logger17);
|
|
1411
1452
|
if (shouldExit) {
|
|
1412
1453
|
rl.close();
|
|
1413
|
-
|
|
1454
|
+
resolve14();
|
|
1414
1455
|
return;
|
|
1415
1456
|
}
|
|
1416
1457
|
} catch (error) {
|
|
@@ -1422,7 +1463,7 @@ ${colors.dim}Session ended.${colors.reset}
|
|
|
1422
1463
|
})();
|
|
1423
1464
|
});
|
|
1424
1465
|
rl.on("close", () => {
|
|
1425
|
-
|
|
1466
|
+
resolve14();
|
|
1426
1467
|
});
|
|
1427
1468
|
});
|
|
1428
1469
|
}
|
|
@@ -2416,9 +2457,9 @@ async function evaluateDirectory(target, timeoutMs) {
|
|
|
2416
2457
|
}
|
|
2417
2458
|
const aggregator = createAggregator();
|
|
2418
2459
|
const results = [];
|
|
2419
|
-
for (const [
|
|
2460
|
+
for (const [path23, evaluations] of evaluationsByComponent) {
|
|
2420
2461
|
if (evaluations.length > 0) {
|
|
2421
|
-
results.push(aggregator.aggregate(
|
|
2462
|
+
results.push(aggregator.aggregate(path23, evaluations));
|
|
2422
2463
|
}
|
|
2423
2464
|
}
|
|
2424
2465
|
const priority = { deprecate: 0, refactor: 1, review: 2, retain: 3 };
|
|
@@ -3930,27 +3971,27 @@ var CliAdapterAgent = class {
|
|
|
3930
3971
|
};
|
|
3931
3972
|
|
|
3932
3973
|
// src/cli/orchestrate-puppeteer.ts
|
|
3933
|
-
function loadPolicyParameters(
|
|
3974
|
+
function loadPolicyParameters(path23, logger17) {
|
|
3934
3975
|
try {
|
|
3935
|
-
if (fs2.existsSync(
|
|
3936
|
-
const content = fs2.readFileSync(
|
|
3976
|
+
if (fs2.existsSync(path23)) {
|
|
3977
|
+
const content = fs2.readFileSync(path23, "utf-8");
|
|
3937
3978
|
const params = JSON.parse(content);
|
|
3938
|
-
logger17.info("Loaded policy parameters", { path:
|
|
3979
|
+
logger17.info("Loaded policy parameters", { path: path23 });
|
|
3939
3980
|
return params;
|
|
3940
3981
|
}
|
|
3941
3982
|
} catch (error) {
|
|
3942
3983
|
const message = getErrorMessage(error);
|
|
3943
|
-
logger17.warn("Failed to load policy parameters", { path:
|
|
3984
|
+
logger17.warn("Failed to load policy parameters", { path: path23, error: message });
|
|
3944
3985
|
}
|
|
3945
3986
|
return void 0;
|
|
3946
3987
|
}
|
|
3947
|
-
function savePolicyParameters(
|
|
3988
|
+
function savePolicyParameters(path23, params, logger17) {
|
|
3948
3989
|
try {
|
|
3949
|
-
fs2.writeFileSync(
|
|
3950
|
-
logger17.info("Saved policy parameters", { path:
|
|
3990
|
+
fs2.writeFileSync(path23, JSON.stringify(params, null, 2));
|
|
3991
|
+
logger17.info("Saved policy parameters", { path: path23 });
|
|
3951
3992
|
} catch (error) {
|
|
3952
3993
|
const message = getErrorMessage(error);
|
|
3953
|
-
logger17.warn("Failed to save policy parameters", { path:
|
|
3994
|
+
logger17.warn("Failed to save policy parameters", { path: path23, error: message });
|
|
3954
3995
|
}
|
|
3955
3996
|
}
|
|
3956
3997
|
function createAgentsFromAdapters(adapters) {
|
|
@@ -6754,15 +6795,7 @@ import * as yaml3 from "yaml";
|
|
|
6754
6795
|
|
|
6755
6796
|
// src/research/research-schemas.ts
|
|
6756
6797
|
import { z as z2 } from "zod";
|
|
6757
|
-
var ResearchTopicSchema = z2.
|
|
6758
|
-
"consensus",
|
|
6759
|
-
"routing",
|
|
6760
|
-
"memory",
|
|
6761
|
-
"code-generation",
|
|
6762
|
-
"cli-tools",
|
|
6763
|
-
"orchestration",
|
|
6764
|
-
"security"
|
|
6765
|
-
]);
|
|
6798
|
+
var ResearchTopicSchema = z2.string().min(1);
|
|
6766
6799
|
var RESEARCH_TOPICS = [
|
|
6767
6800
|
"consensus",
|
|
6768
6801
|
"routing",
|
|
@@ -6770,16 +6803,26 @@ var RESEARCH_TOPICS = [
|
|
|
6770
6803
|
"code-generation",
|
|
6771
6804
|
"cli-tools",
|
|
6772
6805
|
"orchestration",
|
|
6773
|
-
"security"
|
|
6806
|
+
"security",
|
|
6807
|
+
"evaluation",
|
|
6808
|
+
"safety",
|
|
6809
|
+
"planning",
|
|
6810
|
+
"tool-use",
|
|
6811
|
+
"reasoning"
|
|
6774
6812
|
];
|
|
6775
6813
|
var TOPIC_DESCRIPTIONS = {
|
|
6776
|
-
consensus: "Multi-agent decision protocols",
|
|
6777
|
-
routing: "Cost-efficient model routing",
|
|
6778
|
-
memory: "Context
|
|
6779
|
-
"code-generation": "
|
|
6780
|
-
"cli-tools": "External CLI integration",
|
|
6781
|
-
orchestration: "Multi-agent coordination",
|
|
6782
|
-
security: "
|
|
6814
|
+
consensus: "Multi-agent decision protocols and voting",
|
|
6815
|
+
routing: "Cost-efficient model routing and selection",
|
|
6816
|
+
memory: "Context, long-term memory, and compression",
|
|
6817
|
+
"code-generation": "Code generation, repair, and self-improvement",
|
|
6818
|
+
"cli-tools": "External CLI integration and protocols",
|
|
6819
|
+
orchestration: "Multi-agent coordination and workflows",
|
|
6820
|
+
security: "Security analysis, prompt injection defense",
|
|
6821
|
+
evaluation: "Benchmarks, metrics, and testing methodologies",
|
|
6822
|
+
safety: "AI safety, alignment, and reward hacking",
|
|
6823
|
+
planning: "Task planning, decomposition, and reasoning chains",
|
|
6824
|
+
"tool-use": "Tool augmentation, function calling, and MCP",
|
|
6825
|
+
reasoning: "Reasoning, self-reflection, and search strategies"
|
|
6783
6826
|
};
|
|
6784
6827
|
var TechniqueStatusSchema = z2.enum([
|
|
6785
6828
|
"implemented",
|
|
@@ -6814,7 +6857,8 @@ var ResearchPaperSchema = z2.object({
|
|
|
6814
6857
|
/** arXiv ID (e.g., '2501.06322') */
|
|
6815
6858
|
arxiv_id: z2.string().optional(),
|
|
6816
6859
|
/** URL to the paper */
|
|
6817
|
-
|
|
6860
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- z.url() fails at runtime despite being recommended
|
|
6861
|
+
url: z2.string().url().optional(),
|
|
6818
6862
|
/** Publication date (YYYY-MM format) */
|
|
6819
6863
|
publication_date: z2.string().optional(),
|
|
6820
6864
|
/** Publication venue */
|
|
@@ -6823,7 +6867,7 @@ var ResearchPaperSchema = z2.object({
|
|
|
6823
6867
|
* Research topics (array).
|
|
6824
6868
|
* First topic is considered primary for counting purposes.
|
|
6825
6869
|
*/
|
|
6826
|
-
topics: z2.array(
|
|
6870
|
+
topics: z2.array(z2.string()).optional().default([]),
|
|
6827
6871
|
/** Tags for searching */
|
|
6828
6872
|
tags: z2.array(z2.string()).optional().default([]),
|
|
6829
6873
|
/** Date when the paper was reviewed (YYYY-MM-DD) */
|
|
@@ -6841,7 +6885,28 @@ var ResearchPaperSchema = z2.object({
|
|
|
6841
6885
|
/** Related GitHub issues */
|
|
6842
6886
|
related_issues: z2.array(z2.number().int().positive()).optional().default([]),
|
|
6843
6887
|
/** Implementation status */
|
|
6844
|
-
implementation_status: PaperStatusSchema.optional().default("not-started")
|
|
6888
|
+
implementation_status: PaperStatusSchema.optional().default("not-started"),
|
|
6889
|
+
// ── Quality Assessment (Issue #1571) ──────────────────────────
|
|
6890
|
+
/** Citation count from Semantic Scholar (auto-fetched) */
|
|
6891
|
+
citation_count: z2.number().nonnegative().optional(),
|
|
6892
|
+
/** Venue quality tier: 3=top (NeurIPS/ICML/ICLR), 2=good, 1=workshop, 0=preprint */
|
|
6893
|
+
venue_tier: z2.number().min(0).max(3).optional(),
|
|
6894
|
+
/** Whether the paper links to a code repository */
|
|
6895
|
+
has_code: z2.boolean().optional(),
|
|
6896
|
+
/** Code repository URL (from Papers With Code or manual) */
|
|
6897
|
+
code_url: z2.string().optional(),
|
|
6898
|
+
/** Rigor assessment tags (factual, verifiable signals) */
|
|
6899
|
+
rigor_tags: z2.array(
|
|
6900
|
+
z2.enum(["has-code", "has-dataset", "has-baselines", "peer-reviewed", "single-model-eval"])
|
|
6901
|
+
).optional().default([]),
|
|
6902
|
+
/** Composite quality score (0-10, auto-computed from signals) */
|
|
6903
|
+
quality_score: z2.number().min(0).max(10).optional(),
|
|
6904
|
+
/** Evidence confidence tier for extracted techniques */
|
|
6905
|
+
evidence_tier: z2.enum(["high", "medium", "low"]).optional(),
|
|
6906
|
+
/** Why this paper scored low — enables future re-review when conditions change */
|
|
6907
|
+
quality_notes: z2.string().optional(),
|
|
6908
|
+
/** When quality was last assessed (ISO date) — enables periodic re-review */
|
|
6909
|
+
last_quality_check: z2.string().optional()
|
|
6845
6910
|
});
|
|
6846
6911
|
var IntegrationFileObjectSchema = z2.object({
|
|
6847
6912
|
path: z2.string(),
|
|
@@ -6879,7 +6944,17 @@ var ResearchTechniqueSchema = z2.object({
|
|
|
6879
6944
|
/** Dependent techniques */
|
|
6880
6945
|
dependencies: z2.array(z2.string()).optional().default([]),
|
|
6881
6946
|
/** Decision history */
|
|
6882
|
-
decision_history: z2.array(DecisionHistoryEntrySchema).optional().default([])
|
|
6947
|
+
decision_history: z2.array(DecisionHistoryEntrySchema).optional().default([]),
|
|
6948
|
+
// ── Evaluation Plan (Issue #1576 Wave 6) ──────────────────────
|
|
6949
|
+
/** Structured evaluation plan for keeping or removing the technique */
|
|
6950
|
+
evaluation_plan: z2.object({
|
|
6951
|
+
success_criteria: z2.string().optional(),
|
|
6952
|
+
evaluation_method: z2.string().optional(),
|
|
6953
|
+
evaluation_deadline: z2.string().optional(),
|
|
6954
|
+
removal_criteria: z2.string().optional()
|
|
6955
|
+
}).optional(),
|
|
6956
|
+
/** Evaluation status: pending (not yet evaluated), passed, failed, deferred */
|
|
6957
|
+
evaluation_status: z2.enum(["pending", "passed", "failed", "deferred"]).optional()
|
|
6883
6958
|
});
|
|
6884
6959
|
var PapersRegistrySchema = z2.object({
|
|
6885
6960
|
schema_version: z2.string(),
|
|
@@ -6976,7 +7051,7 @@ function generateTopicsTable(stats) {
|
|
|
6976
7051
|
const topicDisplay = capitalizeKebab(ts.topic);
|
|
6977
7052
|
const topicLink = `[${topicDisplay}](topics/${ts.topic}/README.md)`;
|
|
6978
7053
|
const desc = TOPIC_DESCRIPTIONS[ts.topic];
|
|
6979
|
-
return `| ${topicLink} | ${String(ts.papers)} | ${String(ts.techniques)} | ${desc} |`;
|
|
7054
|
+
return `| ${topicLink} | ${String(ts.papers)} | ${String(ts.techniques)} | ${desc ?? ""} |`;
|
|
6980
7055
|
});
|
|
6981
7056
|
return `## Topics
|
|
6982
7057
|
|
|
@@ -7810,6 +7885,267 @@ async function researchIndexCommand(options) {
|
|
|
7810
7885
|
}
|
|
7811
7886
|
}
|
|
7812
7887
|
|
|
7888
|
+
// src/cli/research-import-command.ts
|
|
7889
|
+
import * as fs9 from "fs/promises";
|
|
7890
|
+
import { resolve as resolve5 } from "path";
|
|
7891
|
+
var VALID_TYPES = ["paper", "repo", "tool", "blog"];
|
|
7892
|
+
function parseCsvLine(line) {
|
|
7893
|
+
const fields = [];
|
|
7894
|
+
let current = "";
|
|
7895
|
+
let inQuotes = false;
|
|
7896
|
+
for (let i = 0; i < line.length; i++) {
|
|
7897
|
+
const ch = line[i];
|
|
7898
|
+
if (inQuotes) {
|
|
7899
|
+
if (ch === '"') {
|
|
7900
|
+
const next = line[i + 1];
|
|
7901
|
+
if (next === '"') {
|
|
7902
|
+
current += '"';
|
|
7903
|
+
i++;
|
|
7904
|
+
} else {
|
|
7905
|
+
inQuotes = false;
|
|
7906
|
+
}
|
|
7907
|
+
} else {
|
|
7908
|
+
current += ch;
|
|
7909
|
+
}
|
|
7910
|
+
} else if (ch === '"') {
|
|
7911
|
+
inQuotes = true;
|
|
7912
|
+
} else if (ch === ",") {
|
|
7913
|
+
fields.push(current.trim());
|
|
7914
|
+
current = "";
|
|
7915
|
+
} else {
|
|
7916
|
+
current += ch;
|
|
7917
|
+
}
|
|
7918
|
+
}
|
|
7919
|
+
fields.push(current.trim());
|
|
7920
|
+
return fields;
|
|
7921
|
+
}
|
|
7922
|
+
function isValidUrl(value) {
|
|
7923
|
+
return /^https?:\/\/.+/.test(value);
|
|
7924
|
+
}
|
|
7925
|
+
function isValidType(value) {
|
|
7926
|
+
return VALID_TYPES.includes(value);
|
|
7927
|
+
}
|
|
7928
|
+
function validateRow(fields, colIndex, rowNum) {
|
|
7929
|
+
const title = fields[colIndex["title"]] ?? "";
|
|
7930
|
+
const url = fields[colIndex["url"]] ?? "";
|
|
7931
|
+
const rawType = (fields[colIndex["type"]] ?? "").toLowerCase();
|
|
7932
|
+
const topic = fields[colIndex["topic"]] ?? "";
|
|
7933
|
+
const description = fields[colIndex["description"]] ?? "";
|
|
7934
|
+
if (title === "") return `Row ${String(rowNum)}: missing title`;
|
|
7935
|
+
if (!isValidUrl(url)) return `Row ${String(rowNum)}: invalid URL "${url}"`;
|
|
7936
|
+
if (!isValidType(rawType)) {
|
|
7937
|
+
return `Row ${String(rowNum)}: invalid type "${rawType}" (expected: ${VALID_TYPES.join(", ")})`;
|
|
7938
|
+
}
|
|
7939
|
+
if (topic === "") return `Row ${String(rowNum)}: missing topic`;
|
|
7940
|
+
return { title, url, type: rawType, topic, description };
|
|
7941
|
+
}
|
|
7942
|
+
function parseHeader(headerLine) {
|
|
7943
|
+
const header = parseCsvLine(headerLine).map((h) => h.toLowerCase());
|
|
7944
|
+
const required = ["title", "url", "type", "topic", "description"];
|
|
7945
|
+
const missing = required.filter((col) => !header.includes(col));
|
|
7946
|
+
if (missing.length > 0) {
|
|
7947
|
+
return { ok: false, error: { message: `Missing CSV columns: ${missing.join(", ")}` } };
|
|
7948
|
+
}
|
|
7949
|
+
const colIndex = Object.fromEntries(required.map((col) => [col, header.indexOf(col)]));
|
|
7950
|
+
return { ok: true, value: colIndex };
|
|
7951
|
+
}
|
|
7952
|
+
function parseCsvContent(content) {
|
|
7953
|
+
const lines = content.split(/\r?\n/).filter((l) => l.trim() !== "");
|
|
7954
|
+
if (lines.length < 2) {
|
|
7955
|
+
return {
|
|
7956
|
+
ok: false,
|
|
7957
|
+
error: { message: "CSV must have a header row and at least one data row" }
|
|
7958
|
+
};
|
|
7959
|
+
}
|
|
7960
|
+
const headerResult = parseHeader(lines[0]);
|
|
7961
|
+
if (!headerResult.ok) return headerResult;
|
|
7962
|
+
const rows = [];
|
|
7963
|
+
const errors = [];
|
|
7964
|
+
for (let i = 1; i < lines.length; i++) {
|
|
7965
|
+
const fields = parseCsvLine(lines[i]);
|
|
7966
|
+
const result = validateRow(fields, headerResult.value, i + 1);
|
|
7967
|
+
if (typeof result === "string") {
|
|
7968
|
+
errors.push(result);
|
|
7969
|
+
} else {
|
|
7970
|
+
rows.push(result);
|
|
7971
|
+
}
|
|
7972
|
+
}
|
|
7973
|
+
if (errors.length > 0 && rows.length === 0) {
|
|
7974
|
+
return { ok: false, error: { message: errors.join("\n") } };
|
|
7975
|
+
}
|
|
7976
|
+
return { ok: true, value: rows };
|
|
7977
|
+
}
|
|
7978
|
+
function generateIdFromTitle(title) {
|
|
7979
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
7980
|
+
}
|
|
7981
|
+
function mapToSourceType(csvType) {
|
|
7982
|
+
switch (csvType) {
|
|
7983
|
+
case "repo":
|
|
7984
|
+
return "open_source_repo";
|
|
7985
|
+
case "tool":
|
|
7986
|
+
return "code_analysis";
|
|
7987
|
+
case "blog":
|
|
7988
|
+
return "research_blog";
|
|
7989
|
+
default:
|
|
7990
|
+
return "code_analysis";
|
|
7991
|
+
}
|
|
7992
|
+
}
|
|
7993
|
+
async function executeImport(options) {
|
|
7994
|
+
const csvPath = resolve5(options.csvPath);
|
|
7995
|
+
let content;
|
|
7996
|
+
try {
|
|
7997
|
+
content = await fs9.readFile(csvPath, "utf-8");
|
|
7998
|
+
} catch {
|
|
7999
|
+
return buildResult(
|
|
8000
|
+
[],
|
|
8001
|
+
[{ row: 0, title: "", status: "error", reason: `Cannot read file: ${csvPath}` }]
|
|
8002
|
+
);
|
|
8003
|
+
}
|
|
8004
|
+
const parseResult = parseCsvContent(content);
|
|
8005
|
+
if (!parseResult.ok) {
|
|
8006
|
+
return buildResult(
|
|
8007
|
+
[],
|
|
8008
|
+
[{ row: 0, title: "", status: "error", reason: parseResult.error.message }]
|
|
8009
|
+
);
|
|
8010
|
+
}
|
|
8011
|
+
const rows = parseResult.value;
|
|
8012
|
+
const outcomes = [];
|
|
8013
|
+
const rootDir = options.rootDir;
|
|
8014
|
+
const paperRows = rows.filter((r) => r.type === "paper");
|
|
8015
|
+
const sourceRows = rows.filter((r) => r.type !== "paper");
|
|
8016
|
+
if (paperRows.length > 0) {
|
|
8017
|
+
await processPaperRows(paperRows, outcomes, options.dryRun === true, rootDir);
|
|
8018
|
+
}
|
|
8019
|
+
if (sourceRows.length > 0) {
|
|
8020
|
+
await processSourceRows(sourceRows, outcomes, options.dryRun === true, rootDir);
|
|
8021
|
+
}
|
|
8022
|
+
return buildResult(rows, outcomes);
|
|
8023
|
+
}
|
|
8024
|
+
function buildPaperEntry(row) {
|
|
8025
|
+
return {
|
|
8026
|
+
title: row.title,
|
|
8027
|
+
url: row.url,
|
|
8028
|
+
source: "preprint",
|
|
8029
|
+
arxiv_id: "",
|
|
8030
|
+
publication_date: "",
|
|
8031
|
+
venue: null,
|
|
8032
|
+
reviewed_date: "",
|
|
8033
|
+
reviewed_in: "",
|
|
8034
|
+
topics: [row.topic],
|
|
8035
|
+
summary: row.description,
|
|
8036
|
+
relevance: "medium",
|
|
8037
|
+
implementation_status: "not-started",
|
|
8038
|
+
tags: [],
|
|
8039
|
+
authors: [],
|
|
8040
|
+
key_findings: [],
|
|
8041
|
+
techniques_extracted: [],
|
|
8042
|
+
related_issues: []
|
|
8043
|
+
};
|
|
8044
|
+
}
|
|
8045
|
+
function collectNewPapers(rows, existingUrls, existingTitles, outcomes) {
|
|
8046
|
+
const newPapers = {};
|
|
8047
|
+
for (const row of rows) {
|
|
8048
|
+
if (existingUrls.has(row.url) || existingTitles.has(row.title.toLowerCase())) {
|
|
8049
|
+
outcomes.push({ row: 0, title: row.title, status: "skipped", reason: "Already exists" });
|
|
8050
|
+
continue;
|
|
8051
|
+
}
|
|
8052
|
+
newPapers[generateIdFromTitle(row.title)] = buildPaperEntry(row);
|
|
8053
|
+
existingTitles.add(row.title.toLowerCase());
|
|
8054
|
+
outcomes.push({ row: 0, title: row.title, status: "added" });
|
|
8055
|
+
}
|
|
8056
|
+
return newPapers;
|
|
8057
|
+
}
|
|
8058
|
+
async function processPaperRows(rows, outcomes, dryRun, rootDir) {
|
|
8059
|
+
const loadResult = await loadPapersRegistry(rootDir);
|
|
8060
|
+
if (!loadResult.ok) {
|
|
8061
|
+
for (const row of rows) {
|
|
8062
|
+
outcomes.push({
|
|
8063
|
+
row: 0,
|
|
8064
|
+
title: row.title,
|
|
8065
|
+
status: "error",
|
|
8066
|
+
reason: "Failed to load papers registry"
|
|
8067
|
+
});
|
|
8068
|
+
}
|
|
8069
|
+
return;
|
|
8070
|
+
}
|
|
8071
|
+
const registry = loadResult.value;
|
|
8072
|
+
const existingUrls = new Set(
|
|
8073
|
+
Object.values(registry.papers).map((p) => p.url).filter(Boolean)
|
|
8074
|
+
);
|
|
8075
|
+
const existingTitles = new Set(Object.values(registry.papers).map((p) => p.title.toLowerCase()));
|
|
8076
|
+
const newPapers = collectNewPapers(rows, existingUrls, existingTitles, outcomes);
|
|
8077
|
+
if (!dryRun && Object.keys(newPapers).length > 0) {
|
|
8078
|
+
const updated = { ...registry, papers: { ...registry.papers, ...newPapers } };
|
|
8079
|
+
await savePapersRegistry(updated, rootDir);
|
|
8080
|
+
}
|
|
8081
|
+
}
|
|
8082
|
+
async function processSourceRows(rows, outcomes, dryRun, rootDir) {
|
|
8083
|
+
const loadResult = await loadSourcesRegistry(rootDir);
|
|
8084
|
+
if (!loadResult.ok) {
|
|
8085
|
+
for (const row of rows) {
|
|
8086
|
+
outcomes.push({
|
|
8087
|
+
row: 0,
|
|
8088
|
+
title: row.title,
|
|
8089
|
+
status: "error",
|
|
8090
|
+
reason: "Failed to load sources registry"
|
|
8091
|
+
});
|
|
8092
|
+
}
|
|
8093
|
+
return;
|
|
8094
|
+
}
|
|
8095
|
+
const registry = loadResult.value;
|
|
8096
|
+
const existingUrls = new Set(Object.values(registry.sources).map((s) => s.url));
|
|
8097
|
+
const newSources = {};
|
|
8098
|
+
for (const row of rows) {
|
|
8099
|
+
if (existingUrls.has(row.url)) {
|
|
8100
|
+
outcomes.push({ row: 0, title: row.title, status: "skipped", reason: "Already exists" });
|
|
8101
|
+
continue;
|
|
8102
|
+
}
|
|
8103
|
+
const sourceId = generateIdFromTitle(row.title);
|
|
8104
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8105
|
+
newSources[sourceId] = {
|
|
8106
|
+
name: row.title,
|
|
8107
|
+
type: mapToSourceType(row.type),
|
|
8108
|
+
url: row.url,
|
|
8109
|
+
topics: [row.topic],
|
|
8110
|
+
reviewed_date: today
|
|
8111
|
+
};
|
|
8112
|
+
existingUrls.add(row.url);
|
|
8113
|
+
outcomes.push({ row: 0, title: row.title, status: "added" });
|
|
8114
|
+
}
|
|
8115
|
+
if (!dryRun && Object.keys(newSources).length > 0) {
|
|
8116
|
+
const updated = {
|
|
8117
|
+
...registry,
|
|
8118
|
+
sources: { ...registry.sources, ...newSources }
|
|
8119
|
+
};
|
|
8120
|
+
await saveSourcesRegistry(updated, rootDir);
|
|
8121
|
+
}
|
|
8122
|
+
}
|
|
8123
|
+
function buildResult(_rows, outcomes) {
|
|
8124
|
+
const added = outcomes.filter((o) => o.status === "added").length;
|
|
8125
|
+
const skipped = outcomes.filter((o) => o.status === "skipped").length;
|
|
8126
|
+
const errors = outcomes.filter((o) => o.status === "error").length;
|
|
8127
|
+
const lines = [
|
|
8128
|
+
`Import complete: ${String(added)} added, ${String(skipped)} skipped, ${String(errors)} errors`
|
|
8129
|
+
];
|
|
8130
|
+
for (const o of outcomes) {
|
|
8131
|
+
const suffix = o.reason !== void 0 ? ` (${o.reason})` : "";
|
|
8132
|
+
lines.push(` [${o.status}] ${o.title}${suffix}`);
|
|
8133
|
+
}
|
|
8134
|
+
return { added, skipped, errors, outcomes, message: lines.join("\n") };
|
|
8135
|
+
}
|
|
8136
|
+
async function handleImportCommand(args, options) {
|
|
8137
|
+
const csvPath = args[0];
|
|
8138
|
+
if (csvPath === void 0 || csvPath === "") {
|
|
8139
|
+
return "Error: CSV file path is required. Usage: nexus-agents research import <csv-file> [--dryRun]";
|
|
8140
|
+
}
|
|
8141
|
+
const dryRun = options["dryRun"] === true;
|
|
8142
|
+
const result = await executeImport({ csvPath, dryRun });
|
|
8143
|
+
if (dryRun) {
|
|
8144
|
+
return `[DRY RUN] ${result.message}`;
|
|
8145
|
+
}
|
|
8146
|
+
return result.message;
|
|
8147
|
+
}
|
|
8148
|
+
|
|
7813
8149
|
// src/cli/research-command.ts
|
|
7814
8150
|
function optString(options, key) {
|
|
7815
8151
|
const val = options[key];
|
|
@@ -8023,7 +8359,8 @@ var VALID_SUBCOMMANDS = [
|
|
|
8023
8359
|
"discover",
|
|
8024
8360
|
"review",
|
|
8025
8361
|
"prioritize",
|
|
8026
|
-
"synthesize"
|
|
8362
|
+
"synthesize",
|
|
8363
|
+
"import"
|
|
8027
8364
|
];
|
|
8028
8365
|
function isValidResearchSubcommand(value) {
|
|
8029
8366
|
return value !== void 0 && VALID_SUBCOMMANDS.includes(value);
|
|
@@ -8044,7 +8381,8 @@ var SUBCOMMAND_HANDLERS = {
|
|
|
8044
8381
|
discover: handleDiscoverCommand,
|
|
8045
8382
|
review: handleReviewCommand,
|
|
8046
8383
|
prioritize: handlePrioritizeCommand,
|
|
8047
|
-
synthesize: handleSynthesizeCommand
|
|
8384
|
+
synthesize: handleSynthesizeCommand,
|
|
8385
|
+
import: handleImportCommand
|
|
8048
8386
|
};
|
|
8049
8387
|
async function researchCommand(subcommand, args, options) {
|
|
8050
8388
|
const handler = SUBCOMMAND_HANDLERS[subcommand];
|
|
@@ -8055,8 +8393,8 @@ async function researchCommand(subcommand, args, options) {
|
|
|
8055
8393
|
}
|
|
8056
8394
|
|
|
8057
8395
|
// src/cli/index-command.ts
|
|
8058
|
-
import * as
|
|
8059
|
-
import * as
|
|
8396
|
+
import * as fs13 from "fs/promises";
|
|
8397
|
+
import * as path17 from "path";
|
|
8060
8398
|
import * as yaml6 from "yaml";
|
|
8061
8399
|
|
|
8062
8400
|
// src/indexer/types.ts
|
|
@@ -8558,7 +8896,7 @@ function buildIndex(files) {
|
|
|
8558
8896
|
}
|
|
8559
8897
|
|
|
8560
8898
|
// src/indexer/generator.ts
|
|
8561
|
-
import * as
|
|
8899
|
+
import * as fs10 from "fs/promises";
|
|
8562
8900
|
import * as path11 from "path";
|
|
8563
8901
|
import * as yaml4 from "yaml";
|
|
8564
8902
|
function indexToYaml(index) {
|
|
@@ -8721,33 +9059,22 @@ var McpToolSpecSchema = z4.object({
|
|
|
8721
9059
|
source_file: z4.string(),
|
|
8722
9060
|
source_line: z4.number().int().positive()
|
|
8723
9061
|
});
|
|
8724
|
-
var RestEndpointSpecSchema = z4.object({
|
|
8725
|
-
method: z4.string(),
|
|
8726
|
-
path: z4.string(),
|
|
8727
|
-
description: z4.string(),
|
|
8728
|
-
body_params: z4.array(ParameterSpecSchema).readonly().optional(),
|
|
8729
|
-
query_params: z4.array(ParameterSpecSchema).readonly().optional(),
|
|
8730
|
-
source_file: z4.string(),
|
|
8731
|
-
source_line: z4.number().int().positive()
|
|
8732
|
-
});
|
|
8733
9062
|
var EntrypointManifestSchema = z4.object({
|
|
8734
9063
|
schema_version: z4.literal(ENTRYPOINT_SCHEMA_VERSION),
|
|
8735
9064
|
generated_at: z4.string(),
|
|
8736
9065
|
cli_commands: z4.array(CliCommandSpecSchema).readonly(),
|
|
8737
|
-
mcp_tools: z4.array(McpToolSpecSchema).readonly()
|
|
8738
|
-
rest_endpoints: z4.array(RestEndpointSpecSchema).readonly()
|
|
9066
|
+
mcp_tools: z4.array(McpToolSpecSchema).readonly()
|
|
8739
9067
|
});
|
|
8740
9068
|
var DEFAULT_ENTRYPOINT_EXTRACTOR_OPTIONS = {
|
|
8741
9069
|
packageRoot: "packages/nexus-agents",
|
|
8742
9070
|
cliCommandsPath: "src/cli-commands.ts",
|
|
8743
9071
|
mcpToolsPath: "src/mcp/tools",
|
|
8744
|
-
restRoutesPath: "src/api/routes",
|
|
8745
9072
|
sanitize: true
|
|
8746
9073
|
};
|
|
8747
9074
|
|
|
8748
9075
|
// src/indexer/entrypoint-extractor.ts
|
|
8749
9076
|
import { Project as Project2 } from "ts-morph";
|
|
8750
|
-
import * as
|
|
9077
|
+
import * as path14 from "path";
|
|
8751
9078
|
import * as yaml5 from "yaml";
|
|
8752
9079
|
|
|
8753
9080
|
// src/indexer/entrypoint-sanitizer.ts
|
|
@@ -8841,22 +9168,6 @@ function sanitizeTool(tool) {
|
|
|
8841
9168
|
source_line: tool.source_line
|
|
8842
9169
|
};
|
|
8843
9170
|
}
|
|
8844
|
-
function sanitizeEndpoint(endpoint) {
|
|
8845
|
-
const result = {
|
|
8846
|
-
method: endpoint.method,
|
|
8847
|
-
path: endpoint.path,
|
|
8848
|
-
description: sanitizeValue(endpoint.description),
|
|
8849
|
-
source_file: endpoint.source_file,
|
|
8850
|
-
source_line: endpoint.source_line
|
|
8851
|
-
};
|
|
8852
|
-
if (endpoint.body_params !== void 0) {
|
|
8853
|
-
result.body_params = endpoint.body_params.map(sanitizeParameter);
|
|
8854
|
-
}
|
|
8855
|
-
if (endpoint.query_params !== void 0) {
|
|
8856
|
-
result.query_params = endpoint.query_params.map(sanitizeParameter);
|
|
8857
|
-
}
|
|
8858
|
-
return result;
|
|
8859
|
-
}
|
|
8860
9171
|
|
|
8861
9172
|
// src/indexer/entrypoint-cli-extractor.ts
|
|
8862
9173
|
import { SyntaxKind } from "ts-morph";
|
|
@@ -9173,159 +9484,6 @@ function extractParametersFromSchema(schemaArg, sourceFile) {
|
|
|
9173
9484
|
return [];
|
|
9174
9485
|
}
|
|
9175
9486
|
|
|
9176
|
-
// src/indexer/entrypoint-rest-extractor.ts
|
|
9177
|
-
import { SyntaxKind as SyntaxKind3 } from "ts-morph";
|
|
9178
|
-
import * as path14 from "path";
|
|
9179
|
-
function extractJsonSchemaParams(schemaObj) {
|
|
9180
|
-
const params = [];
|
|
9181
|
-
const objLiteral = schemaObj.asKind(SyntaxKind3.ObjectLiteralExpression);
|
|
9182
|
-
if (objLiteral === void 0) return params;
|
|
9183
|
-
const propertiesProp = objLiteral.getProperty("properties");
|
|
9184
|
-
if (propertiesProp === void 0) return params;
|
|
9185
|
-
const propsObj = propertiesProp.asKind(SyntaxKind3.PropertyAssignment)?.getInitializer()?.asKind(SyntaxKind3.ObjectLiteralExpression);
|
|
9186
|
-
if (propsObj === void 0) return params;
|
|
9187
|
-
const requiredFields = extractRequiredFields(objLiteral);
|
|
9188
|
-
for (const prop of propsObj.getProperties()) {
|
|
9189
|
-
const param = extractParameterFromProperty(prop, requiredFields);
|
|
9190
|
-
if (param !== void 0) {
|
|
9191
|
-
params.push(param);
|
|
9192
|
-
}
|
|
9193
|
-
}
|
|
9194
|
-
return params;
|
|
9195
|
-
}
|
|
9196
|
-
function extractRequiredFields(objLiteral) {
|
|
9197
|
-
const requiredFields = /* @__PURE__ */ new Set();
|
|
9198
|
-
const obj = objLiteral.asKind(SyntaxKind3.ObjectLiteralExpression);
|
|
9199
|
-
if (obj === void 0) return requiredFields;
|
|
9200
|
-
const requiredProp = obj.getProperty("required");
|
|
9201
|
-
if (requiredProp === void 0) return requiredFields;
|
|
9202
|
-
const requiredArr = requiredProp.asKind(SyntaxKind3.PropertyAssignment)?.getInitializer()?.asKind(SyntaxKind3.ArrayLiteralExpression);
|
|
9203
|
-
if (requiredArr !== void 0) {
|
|
9204
|
-
for (const elem of requiredArr.getElements()) {
|
|
9205
|
-
requiredFields.add(elem.getText().replace(/['"]/g, ""));
|
|
9206
|
-
}
|
|
9207
|
-
}
|
|
9208
|
-
return requiredFields;
|
|
9209
|
-
}
|
|
9210
|
-
function extractParameterFromProperty(prop, requiredFields) {
|
|
9211
|
-
const propAssign = prop.asKind(SyntaxKind3.PropertyAssignment);
|
|
9212
|
-
if (propAssign === void 0) return void 0;
|
|
9213
|
-
const paramName = propAssign.getName().replace(/['"]/g, "");
|
|
9214
|
-
const propValue = propAssign.getInitializer()?.asKind(SyntaxKind3.ObjectLiteralExpression);
|
|
9215
|
-
if (propValue === void 0) return void 0;
|
|
9216
|
-
const spec = {
|
|
9217
|
-
name: paramName,
|
|
9218
|
-
type: "string",
|
|
9219
|
-
required: requiredFields.has(paramName)
|
|
9220
|
-
};
|
|
9221
|
-
const typeProp = propValue.getProperty("type");
|
|
9222
|
-
if (typeProp !== void 0) {
|
|
9223
|
-
const typeValue = typeProp.asKind(SyntaxKind3.PropertyAssignment)?.getInitializer()?.getText().replace(/['"]/g, "");
|
|
9224
|
-
if (typeValue !== void 0 && typeValue !== "") {
|
|
9225
|
-
spec.type = typeValue;
|
|
9226
|
-
}
|
|
9227
|
-
}
|
|
9228
|
-
const descProp = propValue.getProperty("description");
|
|
9229
|
-
if (descProp !== void 0) {
|
|
9230
|
-
const descValue = descProp.asKind(SyntaxKind3.PropertyAssignment)?.getInitializer()?.getText().replace(/^['"]|['"]$/g, "");
|
|
9231
|
-
if (descValue !== void 0 && descValue !== "") {
|
|
9232
|
-
spec.description = descValue;
|
|
9233
|
-
}
|
|
9234
|
-
}
|
|
9235
|
-
return spec;
|
|
9236
|
-
}
|
|
9237
|
-
function extractRestEndpoints(project, packageRoot, restRoutesPath) {
|
|
9238
|
-
const endpoints = [];
|
|
9239
|
-
const routesDir = path14.join(packageRoot, restRoutesPath);
|
|
9240
|
-
const routeFiles = project.getSourceFiles(`${routesDir}/*.ts`);
|
|
9241
|
-
for (const sourceFile of routeFiles) {
|
|
9242
|
-
const filePath = sourceFile.getFilePath();
|
|
9243
|
-
const fileName = path14.basename(filePath);
|
|
9244
|
-
if (fileName.endsWith(".test.ts") || fileName === "index.ts") continue;
|
|
9245
|
-
const relativePath = path14.relative(process.cwd(), filePath);
|
|
9246
|
-
const extractedEndpoints = extractEndpointsFromFile(sourceFile, relativePath);
|
|
9247
|
-
endpoints.push(...extractedEndpoints);
|
|
9248
|
-
}
|
|
9249
|
-
return endpoints;
|
|
9250
|
-
}
|
|
9251
|
-
function extractEndpointsFromFile(sourceFile, relativePath) {
|
|
9252
|
-
const endpoints = [];
|
|
9253
|
-
if (sourceFile === void 0) return endpoints;
|
|
9254
|
-
const callExpressions = sourceFile.getDescendantsOfKind(SyntaxKind3.CallExpression);
|
|
9255
|
-
for (const callExpr of callExpressions) {
|
|
9256
|
-
const callText = callExpr.getExpression().getText();
|
|
9257
|
-
const methodMatch = callText.match(/\.(get|post|put|delete|patch)$/i);
|
|
9258
|
-
if (methodMatch === null) continue;
|
|
9259
|
-
const method = methodMatch[1]?.toUpperCase();
|
|
9260
|
-
if (method === void 0) continue;
|
|
9261
|
-
const endpoint = extractEndpointFromCall(callExpr, method, relativePath);
|
|
9262
|
-
if (endpoint !== void 0) {
|
|
9263
|
-
endpoints.push(endpoint);
|
|
9264
|
-
}
|
|
9265
|
-
}
|
|
9266
|
-
return endpoints;
|
|
9267
|
-
}
|
|
9268
|
-
function extractEndpointFromCall(callExpr, method, relativePath) {
|
|
9269
|
-
const typedCall = callExpr;
|
|
9270
|
-
const args = typedCall.getArguments();
|
|
9271
|
-
if (args.length < 2) return void 0;
|
|
9272
|
-
const pathArgNode = args[0];
|
|
9273
|
-
if (pathArgNode === void 0) return void 0;
|
|
9274
|
-
const pathArg = pathArgNode.getText().replace(/['"]/g, "");
|
|
9275
|
-
const { description, bodyParams } = extractEndpointMeta(args, method, pathArg);
|
|
9276
|
-
const endpointSpec = {
|
|
9277
|
-
method,
|
|
9278
|
-
path: pathArg,
|
|
9279
|
-
description,
|
|
9280
|
-
source_file: relativePath,
|
|
9281
|
-
source_line: typedCall.getStartLineNumber()
|
|
9282
|
-
};
|
|
9283
|
-
if (bodyParams.length > 0) {
|
|
9284
|
-
endpointSpec.body_params = bodyParams;
|
|
9285
|
-
}
|
|
9286
|
-
return endpointSpec;
|
|
9287
|
-
}
|
|
9288
|
-
function extractEndpointMeta(args, method, pathArg) {
|
|
9289
|
-
let description = `${method} ${pathArg}`;
|
|
9290
|
-
let bodyParams = [];
|
|
9291
|
-
const schemaArg = args[1];
|
|
9292
|
-
if (schemaArg === void 0) {
|
|
9293
|
-
return { description, bodyParams };
|
|
9294
|
-
}
|
|
9295
|
-
if (schemaArg.getKind() !== SyntaxKind3.ObjectLiteralExpression) {
|
|
9296
|
-
return { description, bodyParams };
|
|
9297
|
-
}
|
|
9298
|
-
const schemaObj = schemaArg.asKind(SyntaxKind3.ObjectLiteralExpression);
|
|
9299
|
-
if (schemaObj === void 0) {
|
|
9300
|
-
return { description, bodyParams };
|
|
9301
|
-
}
|
|
9302
|
-
const schemaProp = schemaObj.getProperty("schema");
|
|
9303
|
-
if (schemaProp === void 0) {
|
|
9304
|
-
return { description, bodyParams };
|
|
9305
|
-
}
|
|
9306
|
-
const schemaValue = schemaProp.asKind(SyntaxKind3.PropertyAssignment)?.getInitializer()?.asKind(SyntaxKind3.ObjectLiteralExpression);
|
|
9307
|
-
if (schemaValue === void 0) {
|
|
9308
|
-
return { description, bodyParams };
|
|
9309
|
-
}
|
|
9310
|
-
const descProp = schemaValue.getProperty("description");
|
|
9311
|
-
if (descProp !== void 0) {
|
|
9312
|
-
const descValue = descProp.asKind(SyntaxKind3.PropertyAssignment)?.getInitializer()?.getText().replace(/^['"]|['"]$/g, "");
|
|
9313
|
-
if (descValue !== void 0 && descValue !== "") {
|
|
9314
|
-
description = descValue;
|
|
9315
|
-
}
|
|
9316
|
-
}
|
|
9317
|
-
if (["POST", "PUT", "PATCH"].includes(method)) {
|
|
9318
|
-
const bodyProp = schemaValue.getProperty("body");
|
|
9319
|
-
if (bodyProp !== void 0) {
|
|
9320
|
-
const bodyObj = bodyProp.asKind(SyntaxKind3.PropertyAssignment)?.getInitializer()?.asKind(SyntaxKind3.ObjectLiteralExpression);
|
|
9321
|
-
if (bodyObj !== void 0) {
|
|
9322
|
-
bodyParams = extractJsonSchemaParams(bodyObj);
|
|
9323
|
-
}
|
|
9324
|
-
}
|
|
9325
|
-
}
|
|
9326
|
-
return { description, bodyParams };
|
|
9327
|
-
}
|
|
9328
|
-
|
|
9329
9487
|
// src/indexer/entrypoint-extractor.ts
|
|
9330
9488
|
function generateTimestamp() {
|
|
9331
9489
|
return new Date(getTimeProvider().now()).toLocaleString("en-US", {
|
|
@@ -9341,14 +9499,13 @@ function generateTimestamp() {
|
|
|
9341
9499
|
}
|
|
9342
9500
|
function createProject(opts) {
|
|
9343
9501
|
const project = new Project2({
|
|
9344
|
-
tsConfigFilePath:
|
|
9502
|
+
tsConfigFilePath: path14.join(opts.packageRoot, "tsconfig.json"),
|
|
9345
9503
|
skipAddingFilesFromTsConfig: true
|
|
9346
9504
|
});
|
|
9347
9505
|
project.addSourceFilesAtPaths([
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
path15.join(opts.packageRoot, opts.restRoutesPath, "*.ts")
|
|
9506
|
+
path14.join(opts.packageRoot, opts.cliCommandsPath),
|
|
9507
|
+
path14.join(opts.packageRoot, "src/cli-types.ts"),
|
|
9508
|
+
path14.join(opts.packageRoot, opts.mcpToolsPath, "*.ts")
|
|
9352
9509
|
]);
|
|
9353
9510
|
return project;
|
|
9354
9511
|
}
|
|
@@ -9365,16 +9522,13 @@ function extractEntrypoints(options = {}) {
|
|
|
9365
9522
|
"src/cli-types.ts"
|
|
9366
9523
|
);
|
|
9367
9524
|
const mcpTools = extractMcpTools(project, opts.packageRoot, opts.mcpToolsPath);
|
|
9368
|
-
const restEndpoints = extractRestEndpoints(project, opts.packageRoot, opts.restRoutesPath);
|
|
9369
9525
|
const finalCommands = opts.sanitize ? cliCommands.map(sanitizeCommand) : cliCommands;
|
|
9370
9526
|
const finalTools = opts.sanitize ? mcpTools.map(sanitizeTool) : mcpTools;
|
|
9371
|
-
const finalEndpoints = opts.sanitize ? restEndpoints.map(sanitizeEndpoint) : restEndpoints;
|
|
9372
9527
|
const manifest = {
|
|
9373
9528
|
schema_version: ENTRYPOINT_SCHEMA_VERSION,
|
|
9374
9529
|
generated_at: generateTimestamp(),
|
|
9375
9530
|
cli_commands: finalCommands,
|
|
9376
|
-
mcp_tools: finalTools
|
|
9377
|
-
rest_endpoints: finalEndpoints
|
|
9531
|
+
mcp_tools: finalTools
|
|
9378
9532
|
};
|
|
9379
9533
|
return { success: true, manifest, errors, warnings };
|
|
9380
9534
|
} catch (error) {
|
|
@@ -9396,20 +9550,20 @@ function manifestToJson(manifest) {
|
|
|
9396
9550
|
}
|
|
9397
9551
|
|
|
9398
9552
|
// src/cli/index-command-link-validator.ts
|
|
9399
|
-
import * as
|
|
9400
|
-
import * as
|
|
9553
|
+
import * as fs12 from "fs/promises";
|
|
9554
|
+
import * as path16 from "path";
|
|
9401
9555
|
|
|
9402
9556
|
// src/cli/index-command-link-validation-helpers.ts
|
|
9403
|
-
import * as
|
|
9404
|
-
import * as
|
|
9557
|
+
import * as fs11 from "fs/promises";
|
|
9558
|
+
import * as path15 from "path";
|
|
9405
9559
|
async function validateInternalLink(url, sourceFile) {
|
|
9406
9560
|
const [filePath, anchor] = url.split("#");
|
|
9407
9561
|
if (filePath === void 0 || filePath === "") {
|
|
9408
9562
|
return { valid: true };
|
|
9409
9563
|
}
|
|
9410
|
-
const resolvedPath =
|
|
9564
|
+
const resolvedPath = path15.resolve(path15.dirname(sourceFile), filePath);
|
|
9411
9565
|
try {
|
|
9412
|
-
await
|
|
9566
|
+
await fs11.access(resolvedPath);
|
|
9413
9567
|
if (anchor !== void 0 && anchor !== "") {
|
|
9414
9568
|
const anchorResult = await validateAnchorInFile(anchor, resolvedPath);
|
|
9415
9569
|
if (!anchorResult.valid) {
|
|
@@ -9483,7 +9637,7 @@ function headingToAnchor(heading) {
|
|
|
9483
9637
|
}
|
|
9484
9638
|
async function validateAnchorInFile(anchor, targetFile) {
|
|
9485
9639
|
try {
|
|
9486
|
-
const content = await
|
|
9640
|
+
const content = await fs11.readFile(targetFile, "utf-8");
|
|
9487
9641
|
const headingRegex = /^#{1,6}\s+(.+)$/gm;
|
|
9488
9642
|
let match;
|
|
9489
9643
|
while ((match = headingRegex.exec(content)) !== null) {
|
|
@@ -9492,9 +9646,9 @@ async function validateAnchorInFile(anchor, targetFile) {
|
|
|
9492
9646
|
return { valid: true };
|
|
9493
9647
|
}
|
|
9494
9648
|
}
|
|
9495
|
-
return { valid: false, error: `Anchor '#${anchor}' not found in ${
|
|
9649
|
+
return { valid: false, error: `Anchor '#${anchor}' not found in ${path15.basename(targetFile)}` };
|
|
9496
9650
|
} catch {
|
|
9497
|
-
return { valid: false, error: `Failed to read ${
|
|
9651
|
+
return { valid: false, error: `Failed to read ${path15.basename(targetFile)} for anchor check` };
|
|
9498
9652
|
}
|
|
9499
9653
|
}
|
|
9500
9654
|
async function validateAnchorLink(anchor, sourceFile) {
|
|
@@ -9584,9 +9738,9 @@ var REFERENCE_DEF_REGEX = /^\[([^\]]+)\]:\s*(.+)$/gm;
|
|
|
9584
9738
|
async function findMarkdownFiles(dir) {
|
|
9585
9739
|
const files = [];
|
|
9586
9740
|
try {
|
|
9587
|
-
const entries = await
|
|
9741
|
+
const entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
9588
9742
|
for (const entry of entries) {
|
|
9589
|
-
const fullPath =
|
|
9743
|
+
const fullPath = path16.join(dir, entry.name);
|
|
9590
9744
|
if (entry.name === "node_modules" || entry.name === "dist") {
|
|
9591
9745
|
continue;
|
|
9592
9746
|
}
|
|
@@ -9680,7 +9834,7 @@ function extractLinks(content) {
|
|
|
9680
9834
|
}
|
|
9681
9835
|
async function validateFileLinks(filePath, checkExternal, counters) {
|
|
9682
9836
|
try {
|
|
9683
|
-
const content = await
|
|
9837
|
+
const content = await fs12.readFile(filePath, "utf-8");
|
|
9684
9838
|
const links = extractLinks(content);
|
|
9685
9839
|
const brokenLinksInFile = [];
|
|
9686
9840
|
for (const link of links) {
|
|
@@ -9781,10 +9935,10 @@ async function generateIndex(options) {
|
|
|
9781
9935
|
`Extracted ${String(extraction.files.length)} files in ${String(extraction.durationMs)}ms`
|
|
9782
9936
|
);
|
|
9783
9937
|
const index = buildIndex(extraction.files);
|
|
9784
|
-
const outputDir =
|
|
9785
|
-
await
|
|
9938
|
+
const outputDir = path17.dirname(outputPath);
|
|
9939
|
+
await fs13.mkdir(outputDir, { recursive: true });
|
|
9786
9940
|
const content = format === "yaml" ? indexToYaml(index) : indexToJson(index);
|
|
9787
|
-
await
|
|
9941
|
+
await fs13.writeFile(outputPath, content, "utf-8");
|
|
9788
9942
|
logger9.info(`Wrote index to ${outputPath}`);
|
|
9789
9943
|
return {
|
|
9790
9944
|
success: true,
|
|
@@ -9799,14 +9953,14 @@ async function generateIndex(options) {
|
|
|
9799
9953
|
async function checkIndex(options) {
|
|
9800
9954
|
const indexPath = options.output ?? "docs/codebase-index.yaml";
|
|
9801
9955
|
try {
|
|
9802
|
-
await
|
|
9956
|
+
await fs13.access(indexPath);
|
|
9803
9957
|
} catch {
|
|
9804
9958
|
return {
|
|
9805
9959
|
success: false,
|
|
9806
9960
|
message: `Index file not found: ${indexPath}. Run 'nexus-agents index generate' first.`
|
|
9807
9961
|
};
|
|
9808
9962
|
}
|
|
9809
|
-
const content = await
|
|
9963
|
+
const content = await fs13.readFile(indexPath, "utf-8");
|
|
9810
9964
|
let existingIndex;
|
|
9811
9965
|
try {
|
|
9812
9966
|
const parsed = indexPath.endsWith(".json") ? JSON.parse(content) : yaml6.parse(content);
|
|
@@ -9842,18 +9996,18 @@ async function generateDiagram(options) {
|
|
|
9842
9996
|
const indexPath = options.output ?? "docs/codebase-index.yaml";
|
|
9843
9997
|
const diagramPath = "docs/dependency-graph.md";
|
|
9844
9998
|
try {
|
|
9845
|
-
await
|
|
9999
|
+
await fs13.access(indexPath);
|
|
9846
10000
|
} catch {
|
|
9847
10001
|
logger9.info("Index not found, generating...");
|
|
9848
10002
|
await generateIndex({ ...options, subcommand: "generate" });
|
|
9849
10003
|
}
|
|
9850
|
-
const content = await
|
|
10004
|
+
const content = await fs13.readFile(indexPath, "utf-8");
|
|
9851
10005
|
const parsed = indexPath.endsWith(".json") ? JSON.parse(content) : yaml6.parse(content);
|
|
9852
10006
|
CodebaseIndexSchema.parse(parsed);
|
|
9853
10007
|
const index = parsed;
|
|
9854
10008
|
const diagram = generateDiagramMarkdown(index);
|
|
9855
|
-
await
|
|
9856
|
-
await
|
|
10009
|
+
await fs13.mkdir(path17.dirname(diagramPath), { recursive: true });
|
|
10010
|
+
await fs13.writeFile(diagramPath, diagram, "utf-8");
|
|
9857
10011
|
logger9.info(`Wrote diagram to ${diagramPath}`);
|
|
9858
10012
|
return {
|
|
9859
10013
|
success: true,
|
|
@@ -9882,7 +10036,7 @@ function extractDocumentedModules(architectureContent) {
|
|
|
9882
10036
|
}
|
|
9883
10037
|
async function getActualModules(srcPath) {
|
|
9884
10038
|
try {
|
|
9885
|
-
const entries = await
|
|
10039
|
+
const entries = await fs13.readdir(srcPath, { withFileTypes: true });
|
|
9886
10040
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !e.name.startsWith("__")).map((e) => e.name);
|
|
9887
10041
|
} catch (error) {
|
|
9888
10042
|
logger9.debug("Failed to read source modules", { srcPath, error: getErrorMessage(error) });
|
|
@@ -9918,12 +10072,12 @@ async function validateArchitecture(options) {
|
|
|
9918
10072
|
const srcPath = "packages/nexus-agents/src";
|
|
9919
10073
|
let architectureContent;
|
|
9920
10074
|
try {
|
|
9921
|
-
architectureContent = await
|
|
10075
|
+
architectureContent = await fs13.readFile(architecturePath, "utf-8");
|
|
9922
10076
|
} catch {
|
|
9923
10077
|
return { success: false, message: `ARCHITECTURE.md not found at ${architecturePath}` };
|
|
9924
10078
|
}
|
|
9925
10079
|
try {
|
|
9926
|
-
await
|
|
10080
|
+
await fs13.access(srcPath);
|
|
9927
10081
|
} catch {
|
|
9928
10082
|
return { success: false, message: `Source directory not found: ${srcPath}` };
|
|
9929
10083
|
}
|
|
@@ -9945,7 +10099,6 @@ async function entrypointsCommand(options) {
|
|
|
9945
10099
|
packageRoot: "packages/nexus-agents",
|
|
9946
10100
|
cliCommandsPath: "src/cli-commands.ts",
|
|
9947
10101
|
mcpToolsPath: "src/mcp/tools",
|
|
9948
|
-
restRoutesPath: "src/api/routes",
|
|
9949
10102
|
sanitize: true
|
|
9950
10103
|
});
|
|
9951
10104
|
if (!result.success || result.manifest === void 0) {
|
|
@@ -9959,15 +10112,15 @@ async function entrypointsCommand(options) {
|
|
|
9959
10112
|
for (const warning of result.warnings) {
|
|
9960
10113
|
logger9.warn(warning);
|
|
9961
10114
|
}
|
|
9962
|
-
const outputDir =
|
|
9963
|
-
await
|
|
10115
|
+
const outputDir = path17.dirname(outputPath);
|
|
10116
|
+
await fs13.mkdir(outputDir, { recursive: true });
|
|
9964
10117
|
const content = format === "yaml" ? manifestToYaml(manifest) : manifestToJson(manifest);
|
|
9965
|
-
await
|
|
10118
|
+
await fs13.writeFile(outputPath, content, "utf-8");
|
|
9966
10119
|
logger9.info(`Wrote entrypoints manifest to ${outputPath}`);
|
|
9967
|
-
const total = manifest.cli_commands.length + manifest.mcp_tools.length
|
|
10120
|
+
const total = manifest.cli_commands.length + manifest.mcp_tools.length;
|
|
9968
10121
|
return {
|
|
9969
10122
|
success: true,
|
|
9970
|
-
message: `Extracted ${String(total)} entrypoints (${String(manifest.cli_commands.length)} CLI, ${String(manifest.mcp_tools.length)} MCP
|
|
10123
|
+
message: `Extracted ${String(total)} entrypoints (${String(manifest.cli_commands.length)} CLI, ${String(manifest.mcp_tools.length)} MCP)`,
|
|
9971
10124
|
data: {
|
|
9972
10125
|
outputPath
|
|
9973
10126
|
}
|
|
@@ -9980,9 +10133,9 @@ async function freshnessCommand(options) {
|
|
|
9980
10133
|
const hasIssues = summary.stale > 0 || summary.warning > 0;
|
|
9981
10134
|
const output2 = options.format === "json" ? formatFreshnessJson(result) : formatFreshnessTable(result);
|
|
9982
10135
|
if (options.output !== void 0) {
|
|
9983
|
-
const outputDir =
|
|
9984
|
-
await
|
|
9985
|
-
await
|
|
10136
|
+
const outputDir = path17.dirname(options.output);
|
|
10137
|
+
await fs13.mkdir(outputDir, { recursive: true });
|
|
10138
|
+
await fs13.writeFile(options.output, output2, "utf-8");
|
|
9986
10139
|
logger9.info(`Wrote freshness report to ${options.output}`);
|
|
9987
10140
|
} else {
|
|
9988
10141
|
process.stdout.write(output2 + "\n");
|
|
@@ -10005,9 +10158,9 @@ async function linksCommand(options) {
|
|
|
10005
10158
|
const hasBrokenLinks = summary.brokenLinks > 0;
|
|
10006
10159
|
const output2 = options.format === "json" ? formatLinkValidationJson(result) : formatLinkValidationTable(result);
|
|
10007
10160
|
if (options.output !== void 0) {
|
|
10008
|
-
const outputDir =
|
|
10009
|
-
await
|
|
10010
|
-
await
|
|
10161
|
+
const outputDir = path17.dirname(options.output);
|
|
10162
|
+
await fs13.mkdir(outputDir, { recursive: true });
|
|
10163
|
+
await fs13.writeFile(options.output, output2, "utf-8");
|
|
10011
10164
|
logger9.info(`Wrote link validation report to ${options.output}`);
|
|
10012
10165
|
} else {
|
|
10013
10166
|
process.stdout.write(output2 + "\n");
|
|
@@ -10348,7 +10501,7 @@ async function verifyCommand(options) {
|
|
|
10348
10501
|
|
|
10349
10502
|
// src/cli/swe-bench-command.ts
|
|
10350
10503
|
import * as os2 from "os";
|
|
10351
|
-
import * as
|
|
10504
|
+
import * as path18 from "path";
|
|
10352
10505
|
function runInfo(options) {
|
|
10353
10506
|
console.log(`
|
|
10354
10507
|
SWE-bench Dataset: ${options.variant}`);
|
|
@@ -10415,7 +10568,8 @@ Error loading dataset: ${error.message}`);
|
|
|
10415
10568
|
}
|
|
10416
10569
|
async function loadAndSelectInstances(options) {
|
|
10417
10570
|
console.log("Loading dataset...");
|
|
10418
|
-
const
|
|
10571
|
+
const hasInstanceFilter = options.instances.length > 0;
|
|
10572
|
+
const loadOptions = !hasInstanceFilter && options.limit !== void 0 ? { limit: options.limit } : {};
|
|
10419
10573
|
const loadResult = await loadDataset(options.variant, loadOptions);
|
|
10420
10574
|
if (!loadResult.ok) {
|
|
10421
10575
|
logDatasetError(loadResult.error);
|
|
@@ -10464,8 +10618,8 @@ function isValidRunId(runId) {
|
|
|
10464
10618
|
return /^[a-zA-Z0-9_-]{1,64}$/.test(runId);
|
|
10465
10619
|
}
|
|
10466
10620
|
function isValidOutputDir(dir) {
|
|
10467
|
-
const resolved =
|
|
10468
|
-
return !resolved.includes("..") && resolved ===
|
|
10621
|
+
const resolved = path18.resolve(dir);
|
|
10622
|
+
return !resolved.includes("..") && resolved === path18.normalize(resolved);
|
|
10469
10623
|
}
|
|
10470
10624
|
function formatProgress(progress) {
|
|
10471
10625
|
const pct = progress.totalCount > 0 ? Math.round(progress.completedCount / progress.totalCount * 100) : 0;
|
|
@@ -10479,12 +10633,12 @@ function buildHarnessConfig(options) {
|
|
|
10479
10633
|
const predictionsPath = options.predictions ?? options.output;
|
|
10480
10634
|
const runId = options.runId ?? `eval-${String(Date.now())}`;
|
|
10481
10635
|
return {
|
|
10482
|
-
predictionsPath:
|
|
10636
|
+
predictionsPath: path18.resolve(predictionsPath),
|
|
10483
10637
|
datasetName: options.variant,
|
|
10484
10638
|
maxWorkers: options.maxWorkers,
|
|
10485
10639
|
runId,
|
|
10486
10640
|
timeoutSeconds: 1800,
|
|
10487
|
-
outputDir:
|
|
10641
|
+
outputDir: path18.resolve(options.outputDir),
|
|
10488
10642
|
useDocker: true,
|
|
10489
10643
|
cacheLevel: options.cacheLevel
|
|
10490
10644
|
};
|
|
@@ -11066,8 +11220,8 @@ function isValidConfigAction(value) {
|
|
|
11066
11220
|
}
|
|
11067
11221
|
|
|
11068
11222
|
// src/cli/config-command-helpers.ts
|
|
11069
|
-
import * as
|
|
11070
|
-
import * as
|
|
11223
|
+
import * as fs14 from "fs/promises";
|
|
11224
|
+
import * as path19 from "path";
|
|
11071
11225
|
import { existsSync as existsSync10 } from "fs";
|
|
11072
11226
|
|
|
11073
11227
|
// src/cli/config-command-formatting.ts
|
|
@@ -11335,10 +11489,10 @@ function parseValueFromString(stringValue, defaultValue) {
|
|
|
11335
11489
|
}
|
|
11336
11490
|
function resolveFilePath(filePath, allowedBase) {
|
|
11337
11491
|
const base = allowedBase ?? process.cwd();
|
|
11338
|
-
const resolved =
|
|
11339
|
-
const normalizedResolved =
|
|
11340
|
-
const normalizedBase =
|
|
11341
|
-
const isWithinBase = normalizedResolved === normalizedBase || normalizedResolved.startsWith(normalizedBase +
|
|
11492
|
+
const resolved = path19.isAbsolute(filePath) ? filePath : path19.resolve(base, filePath);
|
|
11493
|
+
const normalizedResolved = path19.normalize(resolved);
|
|
11494
|
+
const normalizedBase = path19.normalize(base);
|
|
11495
|
+
const isWithinBase = normalizedResolved === normalizedBase || normalizedResolved.startsWith(normalizedBase + path19.sep);
|
|
11342
11496
|
if (!isWithinBase) {
|
|
11343
11497
|
throw new ConfigCommandError(
|
|
11344
11498
|
"PATH_TRAVERSAL",
|
|
@@ -11349,11 +11503,11 @@ function resolveFilePath(filePath, allowedBase) {
|
|
|
11349
11503
|
}
|
|
11350
11504
|
function getDefaultExportPath(format) {
|
|
11351
11505
|
const extension = format === "json" ? "json" : "yaml";
|
|
11352
|
-
return
|
|
11506
|
+
return path19.resolve(process.cwd(), `nexus-config.${extension}`);
|
|
11353
11507
|
}
|
|
11354
11508
|
|
|
11355
11509
|
// src/cli/config-command-handlers.ts
|
|
11356
|
-
import * as
|
|
11510
|
+
import * as fs15 from "fs/promises";
|
|
11357
11511
|
import { existsSync as existsSync11 } from "fs";
|
|
11358
11512
|
async function handleGet(key) {
|
|
11359
11513
|
const parsed = parseConfigKey(key);
|
|
@@ -11455,7 +11609,7 @@ function buildConfigEntries() {
|
|
|
11455
11609
|
}
|
|
11456
11610
|
async function writeConfigFile2(filePath, content) {
|
|
11457
11611
|
try {
|
|
11458
|
-
await
|
|
11612
|
+
await fs15.writeFile(filePath, content, "utf-8");
|
|
11459
11613
|
} catch (error) {
|
|
11460
11614
|
const message = getErrorMessage(error);
|
|
11461
11615
|
throw new ConfigCommandError("WRITE_ERROR", `Failed to write file: ${message}`);
|
|
@@ -11481,7 +11635,7 @@ async function readImportFile(filePath) {
|
|
|
11481
11635
|
}
|
|
11482
11636
|
const format = filePath.endsWith(".yaml") || filePath.endsWith(".yml") ? "yaml" : "json";
|
|
11483
11637
|
try {
|
|
11484
|
-
const content = await
|
|
11638
|
+
const content = await fs15.readFile(filePath, "utf-8");
|
|
11485
11639
|
return { content, format };
|
|
11486
11640
|
} catch (error) {
|
|
11487
11641
|
const message = getErrorMessage(error);
|
|
@@ -11979,7 +12133,7 @@ function writeResultAndExit(result) {
|
|
|
11979
12133
|
|
|
11980
12134
|
// src/cli/hooks/hook-router.ts
|
|
11981
12135
|
async function readStdin() {
|
|
11982
|
-
return new Promise((
|
|
12136
|
+
return new Promise((resolve14, reject) => {
|
|
11983
12137
|
let data = "";
|
|
11984
12138
|
process.stdin.setEncoding("utf8");
|
|
11985
12139
|
process.stdin.on("readable", () => {
|
|
@@ -11990,24 +12144,24 @@ async function readStdin() {
|
|
|
11990
12144
|
}
|
|
11991
12145
|
});
|
|
11992
12146
|
process.stdin.on("end", () => {
|
|
11993
|
-
|
|
12147
|
+
resolve14(data);
|
|
11994
12148
|
});
|
|
11995
12149
|
process.stdin.on("error", (err2) => {
|
|
11996
12150
|
reject(err2);
|
|
11997
12151
|
});
|
|
11998
12152
|
if (process.stdin.isTTY) {
|
|
11999
|
-
|
|
12153
|
+
resolve14("");
|
|
12000
12154
|
}
|
|
12001
12155
|
});
|
|
12002
12156
|
}
|
|
12003
12157
|
async function readStdinWithTimeout(timeoutMs = 5e3) {
|
|
12004
|
-
return new Promise((
|
|
12158
|
+
return new Promise((resolve14, reject) => {
|
|
12005
12159
|
const timeout2 = setTimeout(() => {
|
|
12006
12160
|
reject(new Error(`Stdin read timeout after ${String(timeoutMs)}ms`));
|
|
12007
12161
|
}, timeoutMs);
|
|
12008
12162
|
readStdin().then((data) => {
|
|
12009
12163
|
clearTimeout(timeout2);
|
|
12010
|
-
|
|
12164
|
+
resolve14(data);
|
|
12011
12165
|
}).catch((err2) => {
|
|
12012
12166
|
clearTimeout(timeout2);
|
|
12013
12167
|
reject(err2 instanceof Error ? err2 : new Error(String(err2)));
|
|
@@ -12139,9 +12293,9 @@ function setStringOption(options, key, value) {
|
|
|
12139
12293
|
|
|
12140
12294
|
// src/cli/hooks/handlers/handler-utils.ts
|
|
12141
12295
|
import { homedir as homedir2 } from "os";
|
|
12142
|
-
import { join as
|
|
12296
|
+
import { join as join13 } from "path";
|
|
12143
12297
|
function getDefaultDbPath2() {
|
|
12144
|
-
return
|
|
12298
|
+
return join13(homedir2(), ".nexus-agents", "sessions.db");
|
|
12145
12299
|
}
|
|
12146
12300
|
var HookEnvVars = {
|
|
12147
12301
|
/** Override the default database path */
|
|
@@ -13324,6 +13478,72 @@ async function demoCommand(subcommand, args, options) {
|
|
|
13324
13478
|
}
|
|
13325
13479
|
|
|
13326
13480
|
// src/governance/fitness-score.ts
|
|
13481
|
+
import { existsSync as existsSync12, readFileSync as readFileSync6, readdirSync, statSync } from "fs";
|
|
13482
|
+
import { join as join14 } from "path";
|
|
13483
|
+
function findPkgRoot() {
|
|
13484
|
+
let dir = import.meta.dirname;
|
|
13485
|
+
for (let i = 0; i < 10; i++) {
|
|
13486
|
+
const pkgPath = join14(dir, "package.json");
|
|
13487
|
+
if (existsSync12(pkgPath)) {
|
|
13488
|
+
const content = readFileSync6(pkgPath, "utf-8");
|
|
13489
|
+
if (content.includes('"nexus-agents"')) return dir;
|
|
13490
|
+
}
|
|
13491
|
+
dir = join14(dir, "..");
|
|
13492
|
+
}
|
|
13493
|
+
return join14(import.meta.dirname, "../..");
|
|
13494
|
+
}
|
|
13495
|
+
var PKG_ROOT = findPkgRoot();
|
|
13496
|
+
var SRC_ROOT = join14(PKG_ROOT, "src");
|
|
13497
|
+
var REPO_ROOT = join14(PKG_ROOT, "../..");
|
|
13498
|
+
var DOCS_ROOT = join14(REPO_ROOT, "docs");
|
|
13499
|
+
var DETERMINISM_EXCLUDES = [
|
|
13500
|
+
/\.test\.ts$/,
|
|
13501
|
+
/\.spec\.ts$/,
|
|
13502
|
+
/random-provider\.ts$/,
|
|
13503
|
+
/time-provider\.ts$/
|
|
13504
|
+
];
|
|
13505
|
+
function countFiles(dir, pattern) {
|
|
13506
|
+
if (!existsSync12(dir)) return 0;
|
|
13507
|
+
let count = 0;
|
|
13508
|
+
for (const entry of readdirSync(dir)) {
|
|
13509
|
+
const fullPath = join14(dir, entry);
|
|
13510
|
+
const stat2 = statSync(fullPath);
|
|
13511
|
+
if (stat2.isDirectory() && !entry.startsWith(".")) {
|
|
13512
|
+
count += countFiles(fullPath, pattern);
|
|
13513
|
+
} else if (pattern.test(entry)) {
|
|
13514
|
+
count++;
|
|
13515
|
+
}
|
|
13516
|
+
}
|
|
13517
|
+
return count;
|
|
13518
|
+
}
|
|
13519
|
+
function fileContains(filePath, pattern) {
|
|
13520
|
+
if (!existsSync12(filePath)) return false;
|
|
13521
|
+
return pattern.test(readFileSync6(filePath, "utf-8"));
|
|
13522
|
+
}
|
|
13523
|
+
function isExcluded(entry, excludePatterns) {
|
|
13524
|
+
return excludePatterns?.some((p) => p.test(entry)) ?? false;
|
|
13525
|
+
}
|
|
13526
|
+
function countMatchesInFile(fullPath, contentPattern) {
|
|
13527
|
+
const matches = readFileSync6(fullPath, "utf-8").match(contentPattern);
|
|
13528
|
+
return matches?.length ?? 0;
|
|
13529
|
+
}
|
|
13530
|
+
function countPatternInDir(dir, filePattern, contentPattern, excludePatterns) {
|
|
13531
|
+
if (!existsSync12(dir)) return 0;
|
|
13532
|
+
let count = 0;
|
|
13533
|
+
for (const entry of readdirSync(dir)) {
|
|
13534
|
+
const fullPath = join14(dir, entry);
|
|
13535
|
+
const stat2 = statSync(fullPath);
|
|
13536
|
+
if (stat2.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
|
|
13537
|
+
count += countPatternInDir(fullPath, filePattern, contentPattern, excludePatterns);
|
|
13538
|
+
} else if (filePattern.test(entry) && !isExcluded(entry, excludePatterns)) {
|
|
13539
|
+
count += countMatchesInFile(fullPath, contentPattern);
|
|
13540
|
+
}
|
|
13541
|
+
}
|
|
13542
|
+
return count;
|
|
13543
|
+
}
|
|
13544
|
+
function clamp2(score, max) {
|
|
13545
|
+
return Math.max(0, Math.min(max, score));
|
|
13546
|
+
}
|
|
13327
13547
|
var FitnessScoreCalculator = class {
|
|
13328
13548
|
logger;
|
|
13329
13549
|
checks = [];
|
|
@@ -13331,62 +13551,26 @@ var FitnessScoreCalculator = class {
|
|
|
13331
13551
|
this.logger = logger17 ?? createLogger({ component: "FitnessScoreCalculator" });
|
|
13332
13552
|
this.registerDefaultChecks();
|
|
13333
13553
|
}
|
|
13334
|
-
/**
|
|
13335
|
-
* Register default fitness checks.
|
|
13336
|
-
*/
|
|
13554
|
+
/** Register default fitness checks. */
|
|
13337
13555
|
registerDefaultChecks() {
|
|
13338
|
-
|
|
13339
|
-
dimension
|
|
13340
|
-
|
|
13341
|
-
|
|
13342
|
-
|
|
13343
|
-
|
|
13344
|
-
this.
|
|
13345
|
-
|
|
13346
|
-
|
|
13347
|
-
|
|
13348
|
-
|
|
13349
|
-
|
|
13350
|
-
|
|
13351
|
-
|
|
13352
|
-
|
|
13353
|
-
|
|
13354
|
-
check: () => this.checkDeterminism()
|
|
13355
|
-
});
|
|
13356
|
-
this.checks.push({
|
|
13357
|
-
dimension: "observability",
|
|
13358
|
-
maxPoints: 15,
|
|
13359
|
-
name: "Observability Analysis",
|
|
13360
|
-
check: () => this.checkObservability()
|
|
13361
|
-
});
|
|
13362
|
-
this.checks.push({
|
|
13363
|
-
dimension: "configSimplicity",
|
|
13364
|
-
maxPoints: 10,
|
|
13365
|
-
name: "Config Simplicity Analysis",
|
|
13366
|
-
check: () => this.checkConfigSimplicity()
|
|
13367
|
-
});
|
|
13368
|
-
this.checks.push({
|
|
13369
|
-
dimension: "layerSeparation",
|
|
13370
|
-
maxPoints: 10,
|
|
13371
|
-
name: "Layer Separation Analysis",
|
|
13372
|
-
check: () => this.checkLayerSeparation()
|
|
13373
|
-
});
|
|
13374
|
-
this.checks.push({
|
|
13375
|
-
dimension: "operatorErgonomics",
|
|
13376
|
-
maxPoints: 10,
|
|
13377
|
-
name: "Operator Ergonomics Analysis",
|
|
13378
|
-
check: () => this.checkOperatorErgonomics()
|
|
13379
|
-
});
|
|
13380
|
-
this.checks.push({
|
|
13381
|
-
dimension: "governanceIntegration",
|
|
13382
|
-
maxPoints: 5,
|
|
13383
|
-
name: "Governance Integration Analysis",
|
|
13384
|
-
check: () => this.checkGovernanceIntegration()
|
|
13385
|
-
});
|
|
13556
|
+
const reg = (dimension, maxPoints, name, check) => {
|
|
13557
|
+
this.checks.push({ dimension, maxPoints, name, check });
|
|
13558
|
+
};
|
|
13559
|
+
reg("canonicalPaths", 20, "Canonical Paths", () => this.checkCanonicalPaths());
|
|
13560
|
+
reg("explicitBehavior", 15, "Explicit Behavior", () => this.checkExplicitBehavior());
|
|
13561
|
+
reg("determinism", 15, "Determinism", () => this.checkDeterminism());
|
|
13562
|
+
reg("observability", 15, "Observability", () => this.checkObservability());
|
|
13563
|
+
reg("configSimplicity", 10, "Config Simplicity", () => this.checkConfigSimplicity());
|
|
13564
|
+
reg("layerSeparation", 10, "Layer Separation", () => this.checkLayerSeparation());
|
|
13565
|
+
reg("operatorErgonomics", 10, "Operator Ergonomics", () => this.checkOperatorErgonomics());
|
|
13566
|
+
reg(
|
|
13567
|
+
"governanceIntegration",
|
|
13568
|
+
5,
|
|
13569
|
+
"Governance Integration",
|
|
13570
|
+
() => this.checkGovernanceIntegration()
|
|
13571
|
+
);
|
|
13386
13572
|
}
|
|
13387
|
-
/**
|
|
13388
|
-
* Run full fitness audit.
|
|
13389
|
-
*/
|
|
13573
|
+
/** Run full fitness audit. */
|
|
13390
13574
|
audit(version) {
|
|
13391
13575
|
const findings = [];
|
|
13392
13576
|
const dimensions = {};
|
|
@@ -13407,80 +13591,391 @@ var FitnessScoreCalculator = class {
|
|
|
13407
13591
|
};
|
|
13408
13592
|
}
|
|
13409
13593
|
// =========================================================================
|
|
13410
|
-
// Individual Checks
|
|
13594
|
+
// Individual Checks — real filesystem analysis
|
|
13411
13595
|
// =========================================================================
|
|
13412
|
-
/**
|
|
13413
|
-
* Check canonical paths - penalize duplicate paths to same workflow.
|
|
13414
|
-
* Target: 18/20 after consolidation (per Issue #574)
|
|
13415
|
-
*/
|
|
13596
|
+
/** Check canonical paths: penalize duplicate router implementations. */
|
|
13416
13597
|
checkCanonicalPaths() {
|
|
13417
13598
|
const findings = [];
|
|
13418
|
-
|
|
13419
|
-
|
|
13599
|
+
let score = 20;
|
|
13600
|
+
const routerCount = countFiles(join14(SRC_ROOT, "cli-adapters"), /router\.ts$/);
|
|
13601
|
+
if (routerCount > 5) {
|
|
13602
|
+
const excess = routerCount - 5;
|
|
13603
|
+
const deduction = Math.min(5, excess);
|
|
13604
|
+
score -= deduction;
|
|
13605
|
+
findings.push(
|
|
13606
|
+
this.finding(
|
|
13607
|
+
"canonicalPaths",
|
|
13608
|
+
"warning",
|
|
13609
|
+
`${String(routerCount)} router implementations found (target: <=5)`,
|
|
13610
|
+
deduction,
|
|
13611
|
+
"Consolidate duplicate routers into CompositeRouter"
|
|
13612
|
+
)
|
|
13613
|
+
);
|
|
13614
|
+
}
|
|
13615
|
+
if (existsSync12(join14(SRC_ROOT, "cli-adapters/composite-router.ts"))) {
|
|
13616
|
+
score += 2;
|
|
13617
|
+
} else {
|
|
13618
|
+
score -= 3;
|
|
13619
|
+
findings.push(
|
|
13620
|
+
this.finding(
|
|
13621
|
+
"canonicalPaths",
|
|
13622
|
+
"critical",
|
|
13623
|
+
"CompositeRouter missing \u2014 no unified routing entry point",
|
|
13624
|
+
3
|
|
13625
|
+
)
|
|
13626
|
+
);
|
|
13627
|
+
}
|
|
13628
|
+
score = this.checkOrchestratorInterface(score, findings);
|
|
13629
|
+
return { score: clamp2(score, 20), findings };
|
|
13630
|
+
}
|
|
13631
|
+
/** Sub-check for IOrchestrator interface and adapter wiring. */
|
|
13632
|
+
checkOrchestratorInterface(score, findings) {
|
|
13633
|
+
const orchPath = join14(SRC_ROOT, "core/types/orchestrator.ts");
|
|
13634
|
+
if (existsSync12(orchPath) && fileContains(orchPath, /interface IOrchestrator/)) {
|
|
13635
|
+
score += 3;
|
|
13636
|
+
} else {
|
|
13637
|
+
score -= 2;
|
|
13638
|
+
findings.push(
|
|
13639
|
+
this.finding("canonicalPaths", "warning", "No IOrchestrator interface in core/types", 2)
|
|
13640
|
+
);
|
|
13641
|
+
}
|
|
13642
|
+
const adapterPath = join14(SRC_ROOT, "orchestration/orchestrator-adapters.ts");
|
|
13643
|
+
if (existsSync12(adapterPath) && fileContains(adapterPath, /TechLeadAdapter|PuppeteerAdapter/)) {
|
|
13644
|
+
score += 2;
|
|
13645
|
+
}
|
|
13646
|
+
return score;
|
|
13420
13647
|
}
|
|
13421
13648
|
/**
|
|
13422
|
-
* Check explicit behavior
|
|
13649
|
+
* Check explicit behavior: penalize hidden/magic behavior.
|
|
13650
|
+
* TODO: This dimension lacks strong filesystem signals. Currently checks
|
|
13651
|
+
* for NEXUS_ALLOW_MOCK_ORCHESTRATION guard and magic routing patterns.
|
|
13652
|
+
* Future: add AST-based detection of implicit fallbacks.
|
|
13423
13653
|
*/
|
|
13424
13654
|
checkExplicitBehavior() {
|
|
13425
13655
|
const findings = [];
|
|
13426
|
-
|
|
13427
|
-
|
|
13656
|
+
let score = 15;
|
|
13657
|
+
const mockGuardCount = countPatternInDir(
|
|
13658
|
+
SRC_ROOT,
|
|
13659
|
+
/\.ts$/,
|
|
13660
|
+
/NEXUS_ALLOW_MOCK_ORCHESTRATION/g,
|
|
13661
|
+
DETERMINISM_EXCLUDES
|
|
13662
|
+
);
|
|
13663
|
+
if (mockGuardCount === 0) {
|
|
13664
|
+
score -= 3;
|
|
13665
|
+
findings.push(
|
|
13666
|
+
this.finding(
|
|
13667
|
+
"explicitBehavior",
|
|
13668
|
+
"warning",
|
|
13669
|
+
"No NEXUS_ALLOW_MOCK_ORCHESTRATION guard found \u2014 mock fallback may be implicit",
|
|
13670
|
+
3,
|
|
13671
|
+
"Require explicit env var for mock orchestration"
|
|
13672
|
+
)
|
|
13673
|
+
);
|
|
13674
|
+
}
|
|
13675
|
+
const magicRouting = countPatternInDir(
|
|
13676
|
+
SRC_ROOT,
|
|
13677
|
+
/\.ts$/,
|
|
13678
|
+
/fallback.*=.*true|implicitRoute/g,
|
|
13679
|
+
DETERMINISM_EXCLUDES
|
|
13680
|
+
);
|
|
13681
|
+
if (magicRouting > 5) {
|
|
13682
|
+
const deduction = Math.min(3, Math.floor(magicRouting / 3));
|
|
13683
|
+
score -= deduction;
|
|
13684
|
+
findings.push(
|
|
13685
|
+
this.finding(
|
|
13686
|
+
"explicitBehavior",
|
|
13687
|
+
"info",
|
|
13688
|
+
`${String(magicRouting)} implicit fallback/routing patterns detected`,
|
|
13689
|
+
deduction
|
|
13690
|
+
)
|
|
13691
|
+
);
|
|
13692
|
+
}
|
|
13693
|
+
return { score: clamp2(score, 15), findings };
|
|
13428
13694
|
}
|
|
13429
|
-
/**
|
|
13430
|
-
* Check determinism - reward deterministic execution.
|
|
13431
|
-
*/
|
|
13695
|
+
/** Check determinism: penalize unseeded random and raw Date.now(). */
|
|
13432
13696
|
checkDeterminism() {
|
|
13433
13697
|
const findings = [];
|
|
13434
13698
|
let score = 15;
|
|
13435
|
-
score
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
|
|
13699
|
+
score = this.checkRandomDeterminism(score, findings);
|
|
13700
|
+
score = this.checkTimeDeterminism(score, findings);
|
|
13701
|
+
return { score: clamp2(score, 15), findings };
|
|
13702
|
+
}
|
|
13703
|
+
/** Sub-check for Math.random() and injectable random provider. */
|
|
13704
|
+
checkRandomDeterminism(score, findings) {
|
|
13705
|
+
const randomCount = countPatternInDir(
|
|
13706
|
+
SRC_ROOT,
|
|
13707
|
+
/\.ts$/,
|
|
13708
|
+
/Math\.random\(\)/g,
|
|
13709
|
+
DETERMINISM_EXCLUDES
|
|
13710
|
+
);
|
|
13711
|
+
if (randomCount > 10) {
|
|
13712
|
+
const deduction = Math.min(5, Math.floor(randomCount / 5));
|
|
13713
|
+
score -= deduction;
|
|
13714
|
+
findings.push(
|
|
13715
|
+
this.finding(
|
|
13716
|
+
"determinism",
|
|
13717
|
+
"warning",
|
|
13718
|
+
`${String(randomCount)} unseeded Math.random() calls in production code`,
|
|
13719
|
+
deduction,
|
|
13720
|
+
"Use getRandomProvider() for injectable randomness"
|
|
13721
|
+
)
|
|
13722
|
+
);
|
|
13723
|
+
} else if (randomCount === 0) {
|
|
13724
|
+
score += 2;
|
|
13725
|
+
}
|
|
13726
|
+
if (existsSync12(join14(SRC_ROOT, "core/random-provider.ts"))) {
|
|
13727
|
+
score += 1;
|
|
13728
|
+
}
|
|
13729
|
+
const randomUsage = countPatternInDir(
|
|
13730
|
+
SRC_ROOT,
|
|
13731
|
+
/\.ts$/,
|
|
13732
|
+
/getRandomProvider\(\)/g,
|
|
13733
|
+
DETERMINISM_EXCLUDES
|
|
13734
|
+
);
|
|
13735
|
+
if (randomUsage > 5) {
|
|
13736
|
+
score += 1;
|
|
13737
|
+
}
|
|
13738
|
+
return score;
|
|
13444
13739
|
}
|
|
13445
|
-
/**
|
|
13446
|
-
|
|
13447
|
-
|
|
13740
|
+
/** Sub-check for Date.now() and injectable time provider. */
|
|
13741
|
+
checkTimeDeterminism(score, findings) {
|
|
13742
|
+
const dateNowCount = countPatternInDir(
|
|
13743
|
+
SRC_ROOT,
|
|
13744
|
+
/\.ts$/,
|
|
13745
|
+
/Date\.now\(\)/g,
|
|
13746
|
+
DETERMINISM_EXCLUDES
|
|
13747
|
+
);
|
|
13748
|
+
if (dateNowCount > 50) {
|
|
13749
|
+
score -= 2;
|
|
13750
|
+
findings.push(
|
|
13751
|
+
this.finding(
|
|
13752
|
+
"determinism",
|
|
13753
|
+
"info",
|
|
13754
|
+
`${String(dateNowCount)} Date.now() calls in production code`,
|
|
13755
|
+
2,
|
|
13756
|
+
"Use getTimeProvider() for injectable time"
|
|
13757
|
+
)
|
|
13758
|
+
);
|
|
13759
|
+
}
|
|
13760
|
+
if (existsSync12(join14(SRC_ROOT, "core/time-provider.ts"))) {
|
|
13761
|
+
score += 1;
|
|
13762
|
+
}
|
|
13763
|
+
const timeUsage = countPatternInDir(
|
|
13764
|
+
SRC_ROOT,
|
|
13765
|
+
/\.ts$/,
|
|
13766
|
+
/getTimeProvider\(\)/g,
|
|
13767
|
+
DETERMINISM_EXCLUDES
|
|
13768
|
+
);
|
|
13769
|
+
if (timeUsage > 10) {
|
|
13770
|
+
score += 1;
|
|
13771
|
+
}
|
|
13772
|
+
return score;
|
|
13773
|
+
}
|
|
13774
|
+
/** Check observability: reward tracing, logging, and audit coverage. */
|
|
13448
13775
|
checkObservability() {
|
|
13449
13776
|
const findings = [];
|
|
13450
|
-
|
|
13451
|
-
|
|
13777
|
+
let score = 15;
|
|
13778
|
+
if (existsSync12(join14(SRC_ROOT, "observability/swarm-observer.ts"))) {
|
|
13779
|
+
score += 3;
|
|
13780
|
+
} else {
|
|
13781
|
+
score -= 3;
|
|
13782
|
+
findings.push(
|
|
13783
|
+
this.finding(
|
|
13784
|
+
"observability",
|
|
13785
|
+
"warning",
|
|
13786
|
+
"No SwarmObserver found",
|
|
13787
|
+
3,
|
|
13788
|
+
"Add observability/swarm-observer.ts"
|
|
13789
|
+
)
|
|
13790
|
+
);
|
|
13791
|
+
}
|
|
13792
|
+
if (existsSync12(join14(SRC_ROOT, "core/trace.ts"))) {
|
|
13793
|
+
score += 2;
|
|
13794
|
+
}
|
|
13795
|
+
const loggerCount = countPatternInDir(SRC_ROOT, /\.ts$/, /createLogger\(/g);
|
|
13796
|
+
if (loggerCount > 50) {
|
|
13797
|
+
score += 2;
|
|
13798
|
+
} else {
|
|
13799
|
+
findings.push(
|
|
13800
|
+
this.finding(
|
|
13801
|
+
"observability",
|
|
13802
|
+
"info",
|
|
13803
|
+
`Only ${String(loggerCount)} createLogger() calls (target: >50)`,
|
|
13804
|
+
0
|
|
13805
|
+
)
|
|
13806
|
+
);
|
|
13807
|
+
}
|
|
13808
|
+
if (existsSync12(join14(SRC_ROOT, "audit"))) {
|
|
13809
|
+
score += 2;
|
|
13810
|
+
}
|
|
13811
|
+
return { score: clamp2(score, 15), findings };
|
|
13452
13812
|
}
|
|
13453
|
-
/**
|
|
13454
|
-
* Check config simplicity - penalize too many config surfaces.
|
|
13455
|
-
*/
|
|
13813
|
+
/** Check config simplicity: penalize excessive schema sprawl. */
|
|
13456
13814
|
checkConfigSimplicity() {
|
|
13457
13815
|
const findings = [];
|
|
13458
|
-
|
|
13459
|
-
|
|
13816
|
+
let score = 10;
|
|
13817
|
+
const schemaCount = countFiles(join14(SRC_ROOT, "config"), /schema.*\.ts$/);
|
|
13818
|
+
if (schemaCount > 10) {
|
|
13819
|
+
score -= 2;
|
|
13820
|
+
findings.push(
|
|
13821
|
+
this.finding(
|
|
13822
|
+
"configSimplicity",
|
|
13823
|
+
"info",
|
|
13824
|
+
`${String(schemaCount)} config schemas (target: <=10)`,
|
|
13825
|
+
2,
|
|
13826
|
+
"Consolidate related schemas"
|
|
13827
|
+
)
|
|
13828
|
+
);
|
|
13829
|
+
} else {
|
|
13830
|
+
score += 1;
|
|
13831
|
+
}
|
|
13832
|
+
if (existsSync12(join14(SRC_ROOT, "config/config-loader.ts"))) {
|
|
13833
|
+
score += 2;
|
|
13834
|
+
}
|
|
13835
|
+
if (existsSync12(join14(SRC_ROOT, "config/config-manager.ts"))) {
|
|
13836
|
+
score += 1;
|
|
13837
|
+
}
|
|
13838
|
+
return { score: clamp2(score, 10), findings };
|
|
13460
13839
|
}
|
|
13461
|
-
/**
|
|
13462
|
-
* Check layer separation - penalize cross-layer coupling.
|
|
13463
|
-
*/
|
|
13840
|
+
/** Check layer separation: penalize cross-layer imports. */
|
|
13464
13841
|
checkLayerSeparation() {
|
|
13465
13842
|
const findings = [];
|
|
13466
|
-
|
|
13467
|
-
|
|
13843
|
+
let score = 10;
|
|
13844
|
+
const adapterAgentImports = countPatternInDir(
|
|
13845
|
+
join14(SRC_ROOT, "adapters"),
|
|
13846
|
+
/\.ts$/,
|
|
13847
|
+
/from ['"]\.\.\/agents\//g
|
|
13848
|
+
);
|
|
13849
|
+
if (adapterAgentImports > 0) {
|
|
13850
|
+
const deduction = Math.min(5, adapterAgentImports);
|
|
13851
|
+
score -= deduction;
|
|
13852
|
+
findings.push(
|
|
13853
|
+
this.finding(
|
|
13854
|
+
"layerSeparation",
|
|
13855
|
+
"warning",
|
|
13856
|
+
`${String(adapterAgentImports)} adapter->agent import violations`,
|
|
13857
|
+
deduction,
|
|
13858
|
+
"Adapters should not import from agents layer"
|
|
13859
|
+
)
|
|
13860
|
+
);
|
|
13861
|
+
} else {
|
|
13862
|
+
score += 2;
|
|
13863
|
+
}
|
|
13864
|
+
const coreMcpImports = countPatternInDir(
|
|
13865
|
+
join14(SRC_ROOT, "core"),
|
|
13866
|
+
/\.ts$/,
|
|
13867
|
+
/from ['"]\.\.\/mcp\//g
|
|
13868
|
+
);
|
|
13869
|
+
if (coreMcpImports > 0) {
|
|
13870
|
+
const deduction = Math.min(3, coreMcpImports);
|
|
13871
|
+
score -= deduction;
|
|
13872
|
+
findings.push(
|
|
13873
|
+
this.finding(
|
|
13874
|
+
"layerSeparation",
|
|
13875
|
+
"critical",
|
|
13876
|
+
`${String(coreMcpImports)} core->MCP import violations`,
|
|
13877
|
+
deduction,
|
|
13878
|
+
"Core must not depend on MCP layer"
|
|
13879
|
+
)
|
|
13880
|
+
);
|
|
13881
|
+
} else {
|
|
13882
|
+
score += 1;
|
|
13883
|
+
}
|
|
13884
|
+
return { score: clamp2(score, 10), findings };
|
|
13468
13885
|
}
|
|
13469
|
-
/**
|
|
13470
|
-
* Check operator ergonomics - reward minimal CLI steps.
|
|
13471
|
-
*/
|
|
13886
|
+
/** Check operator ergonomics: reward rich CLI commands. */
|
|
13472
13887
|
checkOperatorErgonomics() {
|
|
13473
13888
|
const findings = [];
|
|
13474
|
-
|
|
13475
|
-
|
|
13889
|
+
let score = 10;
|
|
13890
|
+
const commandCount = countFiles(join14(SRC_ROOT, "cli"), /\.ts$/);
|
|
13891
|
+
if (commandCount >= 20) {
|
|
13892
|
+
score += 3;
|
|
13893
|
+
} else {
|
|
13894
|
+
findings.push(
|
|
13895
|
+
this.finding(
|
|
13896
|
+
"operatorErgonomics",
|
|
13897
|
+
"info",
|
|
13898
|
+
`${String(commandCount)} CLI commands (target: >=20)`,
|
|
13899
|
+
0
|
|
13900
|
+
)
|
|
13901
|
+
);
|
|
13902
|
+
}
|
|
13903
|
+
score = this.checkCliCommands(score, findings);
|
|
13904
|
+
return { score: clamp2(score, 10), findings };
|
|
13476
13905
|
}
|
|
13477
|
-
/**
|
|
13478
|
-
|
|
13479
|
-
|
|
13906
|
+
/** Sub-check for essential CLI commands (doctor, setup, demo, config). */
|
|
13907
|
+
checkCliCommands(score, findings) {
|
|
13908
|
+
const commands = [
|
|
13909
|
+
["cli/doctor.ts", "Doctor command", 2],
|
|
13910
|
+
["cli/setup-command.ts", "Setup wizard", 2],
|
|
13911
|
+
["cli/demo-command.ts", "Demo command", 1],
|
|
13912
|
+
["cli/config-command.ts", "Config command", 1]
|
|
13913
|
+
];
|
|
13914
|
+
for (const [path23, name, bonus] of commands) {
|
|
13915
|
+
if (existsSync12(join14(SRC_ROOT, path23))) {
|
|
13916
|
+
score += bonus;
|
|
13917
|
+
} else {
|
|
13918
|
+
findings.push(this.finding("operatorErgonomics", "info", `Missing ${name} (${path23})`, 0));
|
|
13919
|
+
}
|
|
13920
|
+
}
|
|
13921
|
+
return score;
|
|
13922
|
+
}
|
|
13923
|
+
/** Check governance integration: policy firewall, rate limiter, docs. */
|
|
13480
13924
|
checkGovernanceIntegration() {
|
|
13481
13925
|
const findings = [];
|
|
13482
|
-
|
|
13483
|
-
|
|
13926
|
+
let score = 5;
|
|
13927
|
+
score = this.checkGovernanceDocs(score, findings);
|
|
13928
|
+
score = this.checkGovernanceInfra(score, findings);
|
|
13929
|
+
return { score: clamp2(score, 5), findings };
|
|
13930
|
+
}
|
|
13931
|
+
/** Sub-check for governance documentation artifacts. */
|
|
13932
|
+
checkGovernanceDocs(score, findings) {
|
|
13933
|
+
if (!existsSync12(join14(REPO_ROOT, "CLAUDE.md"))) {
|
|
13934
|
+
score -= 3;
|
|
13935
|
+
findings.push(
|
|
13936
|
+
this.finding("governanceIntegration", "critical", "No CLAUDE.md governance document", 3)
|
|
13937
|
+
);
|
|
13938
|
+
} else {
|
|
13939
|
+
score += 1;
|
|
13940
|
+
}
|
|
13941
|
+
if (existsSync12(join14(DOCS_ROOT, "architecture/wiring-graph.json"))) {
|
|
13942
|
+
score += 1;
|
|
13943
|
+
}
|
|
13944
|
+
if (existsSync12(join14(DOCS_ROOT, "adr"))) {
|
|
13945
|
+
score += 1;
|
|
13946
|
+
} else {
|
|
13947
|
+
findings.push(this.finding("governanceIntegration", "info", "No ADR directory", 0));
|
|
13948
|
+
}
|
|
13949
|
+
return score;
|
|
13950
|
+
}
|
|
13951
|
+
/** Sub-check for governance runtime infrastructure. */
|
|
13952
|
+
checkGovernanceInfra(score, findings) {
|
|
13953
|
+
const hasPolicyFirewall = countPatternInDir(join14(SRC_ROOT, "security"), /\.ts$/, /PolicyGate|policyFirewall/g) > 0;
|
|
13954
|
+
if (hasPolicyFirewall) {
|
|
13955
|
+
score += 1;
|
|
13956
|
+
} else {
|
|
13957
|
+
findings.push(
|
|
13958
|
+
this.finding(
|
|
13959
|
+
"governanceIntegration",
|
|
13960
|
+
"warning",
|
|
13961
|
+
"No policy firewall detected in security layer",
|
|
13962
|
+
0
|
|
13963
|
+
)
|
|
13964
|
+
);
|
|
13965
|
+
}
|
|
13966
|
+
const hasRateLimiter = countPatternInDir(SRC_ROOT, /\.ts$/, /RateLimiter|rateLimiter/g) > 0;
|
|
13967
|
+
if (hasRateLimiter) {
|
|
13968
|
+
score += 1;
|
|
13969
|
+
}
|
|
13970
|
+
return score;
|
|
13971
|
+
}
|
|
13972
|
+
/** Helper to create a FitnessFinding with defaults. */
|
|
13973
|
+
finding(dimension, severity, description, pointsDeducted, suggestion) {
|
|
13974
|
+
const base = { dimension, severity, description, pointsDeducted };
|
|
13975
|
+
if (suggestion !== void 0) {
|
|
13976
|
+
return { ...base, suggestion };
|
|
13977
|
+
}
|
|
13978
|
+
return base;
|
|
13484
13979
|
}
|
|
13485
13980
|
};
|
|
13486
13981
|
function createFitnessScoreCalculator(logger17) {
|
|
@@ -13658,10 +14153,10 @@ var CATEGORY_ORDER = [
|
|
|
13658
14153
|
];
|
|
13659
14154
|
|
|
13660
14155
|
// src/cli/release-notes-helpers.ts
|
|
13661
|
-
import {
|
|
14156
|
+
import { execFileSync } from "child_process";
|
|
13662
14157
|
function getLatestTag() {
|
|
13663
14158
|
try {
|
|
13664
|
-
const result =
|
|
14159
|
+
const result = execFileSync("git", ["describe", "--tags", "--abbrev=0"], {
|
|
13665
14160
|
encoding: "utf-8",
|
|
13666
14161
|
stdio: ["pipe", "pipe", "pipe"],
|
|
13667
14162
|
timeout: CLI_SUBPROCESS_TIMEOUTS.ghCommandMs
|
|
@@ -13672,8 +14167,12 @@ function getLatestTag() {
|
|
|
13672
14167
|
}
|
|
13673
14168
|
}
|
|
13674
14169
|
function getCommitsBetween(from, to = "HEAD") {
|
|
14170
|
+
const SAFE_REF = /^[a-zA-Z0-9._\-/~^]+$/;
|
|
14171
|
+
if (!SAFE_REF.test(from) || !SAFE_REF.test(to)) {
|
|
14172
|
+
return [];
|
|
14173
|
+
}
|
|
13675
14174
|
try {
|
|
13676
|
-
const result =
|
|
14175
|
+
const result = execFileSync("git", ["log", `${from}..${to}`, "--oneline", "--format=%h %s"], {
|
|
13677
14176
|
encoding: "utf-8",
|
|
13678
14177
|
stdio: ["pipe", "pipe", "pipe"],
|
|
13679
14178
|
timeout: CLI_SUBPROCESS_TIMEOUTS.ghCommandMs
|
|
@@ -13920,16 +14419,16 @@ async function releaseNotesCommand(args) {
|
|
|
13920
14419
|
}
|
|
13921
14420
|
|
|
13922
14421
|
// src/cli/release-validate-command.ts
|
|
13923
|
-
import { readFileSync as
|
|
14422
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
13924
14423
|
|
|
13925
14424
|
// src/cli/release-validate-helpers.ts
|
|
13926
|
-
import { execSync as
|
|
13927
|
-
import { existsSync as
|
|
14425
|
+
import { execSync as execSync2 } from "child_process";
|
|
14426
|
+
import { existsSync as existsSync13, readFileSync as readFileSync7 } from "fs";
|
|
13928
14427
|
async function validateSecurity(options) {
|
|
13929
14428
|
const startTime = Date.now();
|
|
13930
14429
|
const findings = [];
|
|
13931
14430
|
try {
|
|
13932
|
-
|
|
14431
|
+
execSync2("npm audit --audit-level=high 2>/dev/null", {
|
|
13933
14432
|
encoding: "utf-8",
|
|
13934
14433
|
stdio: ["pipe", "pipe", "pipe"],
|
|
13935
14434
|
timeout: CLI_SUBPROCESS_TIMEOUTS.ghCommandMs
|
|
@@ -13943,7 +14442,7 @@ async function validateSecurity(options) {
|
|
|
13943
14442
|
remediation: "Run npm audit fix or review and update vulnerable dependencies."
|
|
13944
14443
|
});
|
|
13945
14444
|
}
|
|
13946
|
-
if (
|
|
14445
|
+
if (existsSync13(".env")) {
|
|
13947
14446
|
findings.push({
|
|
13948
14447
|
severity: "error",
|
|
13949
14448
|
category: "security",
|
|
@@ -13953,7 +14452,7 @@ async function validateSecurity(options) {
|
|
|
13953
14452
|
});
|
|
13954
14453
|
}
|
|
13955
14454
|
try {
|
|
13956
|
-
const result =
|
|
14455
|
+
const result = execSync2(
|
|
13957
14456
|
'git diff HEAD~10..HEAD -- "*.ts" "*.js" | grep -iE "(api[_-]?key|secret|password|token)" | head -5',
|
|
13958
14457
|
{
|
|
13959
14458
|
encoding: "utf-8",
|
|
@@ -13984,7 +14483,7 @@ async function validateArchitecture2(options) {
|
|
|
13984
14483
|
const startTime = Date.now();
|
|
13985
14484
|
const findings = [];
|
|
13986
14485
|
try {
|
|
13987
|
-
const result =
|
|
14486
|
+
const result = execSync2("npx nexus-agents fitness-audit --format=json 2>/dev/null", {
|
|
13988
14487
|
encoding: "utf-8",
|
|
13989
14488
|
stdio: ["pipe", "pipe", "pipe"],
|
|
13990
14489
|
timeout: CLI_SUBPROCESS_TIMEOUTS.releaseValidateMs
|
|
@@ -14039,7 +14538,7 @@ async function validateArchitecture2(options) {
|
|
|
14039
14538
|
async function validateDocumentation(options) {
|
|
14040
14539
|
const startTime = Date.now();
|
|
14041
14540
|
const findings = [];
|
|
14042
|
-
if (!
|
|
14541
|
+
if (!existsSync13("CHANGELOG.md")) {
|
|
14043
14542
|
findings.push({
|
|
14044
14543
|
severity: "error",
|
|
14045
14544
|
category: "docs",
|
|
@@ -14047,7 +14546,7 @@ async function validateDocumentation(options) {
|
|
|
14047
14546
|
description: "No CHANGELOG.md found in repository root.",
|
|
14048
14547
|
remediation: "Create CHANGELOG.md following Keep a Changelog format."
|
|
14049
14548
|
});
|
|
14050
|
-
} else if (!
|
|
14549
|
+
} else if (!readFileSync7("CHANGELOG.md", "utf-8").includes(options.version)) {
|
|
14051
14550
|
findings.push({
|
|
14052
14551
|
severity: "warning",
|
|
14053
14552
|
category: "docs",
|
|
@@ -14057,7 +14556,7 @@ async function validateDocumentation(options) {
|
|
|
14057
14556
|
});
|
|
14058
14557
|
}
|
|
14059
14558
|
const pkgCl = "packages/nexus-agents/CHANGELOG.md";
|
|
14060
|
-
if (
|
|
14559
|
+
if (existsSync13(pkgCl) && !readFileSync7(pkgCl, "utf-8").includes(options.version)) {
|
|
14061
14560
|
findings.push({
|
|
14062
14561
|
severity: "warning",
|
|
14063
14562
|
category: "docs",
|
|
@@ -14066,7 +14565,7 @@ async function validateDocumentation(options) {
|
|
|
14066
14565
|
remediation: "Run pnpm changeset:version to generate changelog from pending changesets."
|
|
14067
14566
|
});
|
|
14068
14567
|
}
|
|
14069
|
-
if (!
|
|
14568
|
+
if (!existsSync13("README.md")) {
|
|
14070
14569
|
findings.push({
|
|
14071
14570
|
severity: "error",
|
|
14072
14571
|
category: "docs",
|
|
@@ -14075,8 +14574,8 @@ async function validateDocumentation(options) {
|
|
|
14075
14574
|
remediation: "Create README.md with project overview and usage instructions."
|
|
14076
14575
|
});
|
|
14077
14576
|
}
|
|
14078
|
-
if (
|
|
14079
|
-
const claudeMd =
|
|
14577
|
+
if (existsSync13("CLAUDE.md")) {
|
|
14578
|
+
const claudeMd = readFileSync7("CLAUDE.md", "utf-8");
|
|
14080
14579
|
const match = claudeMd.match(/Governance Version: (\d{4}-\d{2}-\d{2})/);
|
|
14081
14580
|
if (match?.[1]) {
|
|
14082
14581
|
const governanceDate = new Date(match[1]);
|
|
@@ -14105,7 +14604,7 @@ async function validateDevOps(options) {
|
|
|
14105
14604
|
const startTime = Date.now();
|
|
14106
14605
|
const findings = [];
|
|
14107
14606
|
try {
|
|
14108
|
-
|
|
14607
|
+
execSync2("pnpm build 2>/dev/null", {
|
|
14109
14608
|
encoding: "utf-8",
|
|
14110
14609
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14111
14610
|
timeout: CLI_SUBPROCESS_TIMEOUTS.releaseValidateMs
|
|
@@ -14126,7 +14625,7 @@ async function validateDevOps(options) {
|
|
|
14126
14625
|
});
|
|
14127
14626
|
}
|
|
14128
14627
|
try {
|
|
14129
|
-
|
|
14628
|
+
execSync2("pnpm lint 2>/dev/null", {
|
|
14130
14629
|
encoding: "utf-8",
|
|
14131
14630
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14132
14631
|
timeout: CLI_SUBPROCESS_TIMEOUTS.releaseBuildMs
|
|
@@ -14147,7 +14646,7 @@ async function validateDevOps(options) {
|
|
|
14147
14646
|
});
|
|
14148
14647
|
}
|
|
14149
14648
|
try {
|
|
14150
|
-
|
|
14649
|
+
execSync2("pnpm typecheck 2>/dev/null", {
|
|
14151
14650
|
encoding: "utf-8",
|
|
14152
14651
|
stdio: ["pipe", "pipe", "pipe"],
|
|
14153
14652
|
timeout: CLI_SUBPROCESS_TIMEOUTS.releaseValidateMs
|
|
@@ -14184,7 +14683,7 @@ var DEFAULT_OPTIONS2 = {
|
|
|
14184
14683
|
};
|
|
14185
14684
|
function readVersionFromPackage() {
|
|
14186
14685
|
try {
|
|
14187
|
-
const raw = JSON.parse(
|
|
14686
|
+
const raw = JSON.parse(readFileSync8("package.json", "utf-8"));
|
|
14188
14687
|
if (typeof raw === "object" && raw !== null && "version" in raw) {
|
|
14189
14688
|
const pkg = raw;
|
|
14190
14689
|
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
@@ -14298,7 +14797,7 @@ async function releaseValidateCommand(args) {
|
|
|
14298
14797
|
}
|
|
14299
14798
|
|
|
14300
14799
|
// src/cli/release-announce-command.ts
|
|
14301
|
-
import { readFileSync as
|
|
14800
|
+
import { readFileSync as readFileSync9, existsSync as existsSync14 } from "fs";
|
|
14302
14801
|
|
|
14303
14802
|
// src/cli/release-announce-types.ts
|
|
14304
14803
|
var BLUESKY_LIMITS = {
|
|
@@ -14323,7 +14822,7 @@ function getBlueskyConfig() {
|
|
|
14323
14822
|
}
|
|
14324
14823
|
async function loadAtproto() {
|
|
14325
14824
|
try {
|
|
14326
|
-
const mod = await import("./dist-
|
|
14825
|
+
const mod = await import("./dist-CV74KUT7.js");
|
|
14327
14826
|
return { AtpAgent: mod.AtpAgent, RichText: mod.RichText };
|
|
14328
14827
|
} catch {
|
|
14329
14828
|
return void 0;
|
|
@@ -14365,8 +14864,8 @@ var DEFAULT_OPTIONS3 = {
|
|
|
14365
14864
|
verbose: false
|
|
14366
14865
|
};
|
|
14367
14866
|
function extractHighlightsFromChangelog(version) {
|
|
14368
|
-
if (!
|
|
14369
|
-
const changelog =
|
|
14867
|
+
if (!existsSync14("CHANGELOG.md")) return [];
|
|
14868
|
+
const changelog = readFileSync9("CHANGELOG.md", "utf-8");
|
|
14370
14869
|
const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14371
14870
|
const versionRegex = new RegExp(`## \\[${escaped}\\][^#]*`, "s");
|
|
14372
14871
|
const match = changelog.match(versionRegex);
|
|
@@ -14628,7 +15127,7 @@ async function releaseAnnounceCommand(args) {
|
|
|
14628
15127
|
version = args.options.version;
|
|
14629
15128
|
} else {
|
|
14630
15129
|
try {
|
|
14631
|
-
const pkg = JSON.parse(
|
|
15130
|
+
const pkg = JSON.parse(readFileSync9("package.json", "utf-8"));
|
|
14632
15131
|
if (!pkg.version) {
|
|
14633
15132
|
console.error(`${colors.red}Error: Could not determine version${colors.reset}`);
|
|
14634
15133
|
return 1;
|
|
@@ -14655,8 +15154,8 @@ async function releaseAnnounceCommand(args) {
|
|
|
14655
15154
|
}
|
|
14656
15155
|
|
|
14657
15156
|
// src/cli/scaffold.ts
|
|
14658
|
-
import * as
|
|
14659
|
-
import * as
|
|
15157
|
+
import * as fs16 from "fs";
|
|
15158
|
+
import * as path20 from "path";
|
|
14660
15159
|
|
|
14661
15160
|
// src/cli/scaffold-templates.ts
|
|
14662
15161
|
function toolImportsAndSchema(name, pascal) {
|
|
@@ -14992,17 +15491,17 @@ ${lines}`;
|
|
|
14992
15491
|
function writeFiles(files) {
|
|
14993
15492
|
const createdPaths = [];
|
|
14994
15493
|
for (const file of files) {
|
|
14995
|
-
const fullPath =
|
|
14996
|
-
const dir =
|
|
14997
|
-
|
|
14998
|
-
if (
|
|
15494
|
+
const fullPath = path20.resolve(process.cwd(), file.path);
|
|
15495
|
+
const dir = path20.dirname(fullPath);
|
|
15496
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
15497
|
+
if (fs16.existsSync(fullPath)) {
|
|
14999
15498
|
return {
|
|
15000
15499
|
success: false,
|
|
15001
15500
|
filesCreated: createdPaths,
|
|
15002
15501
|
message: `File already exists: ${file.path}. Use a different name.`
|
|
15003
15502
|
};
|
|
15004
15503
|
}
|
|
15005
|
-
|
|
15504
|
+
fs16.writeFileSync(fullPath, file.content, "utf-8");
|
|
15006
15505
|
createdPaths.push(file.path);
|
|
15007
15506
|
}
|
|
15008
15507
|
const lines = createdPaths.map((p) => ` ${p}`).join("\n");
|
|
@@ -15079,8 +15578,18 @@ var LATENCY_RANGES = {
|
|
|
15079
15578
|
secondary: { min: 3e3, max: 8e3 },
|
|
15080
15579
|
other: { min: 5e3, max: 15e3 }
|
|
15081
15580
|
};
|
|
15082
|
-
function
|
|
15083
|
-
|
|
15581
|
+
function createRng(seed) {
|
|
15582
|
+
if (seed === void 0) return Math.random;
|
|
15583
|
+
let s = seed | 0;
|
|
15584
|
+
return () => {
|
|
15585
|
+
s = s + 1831565813 | 0;
|
|
15586
|
+
let t = Math.imul(s ^ s >>> 15, 1 | s);
|
|
15587
|
+
t = t + Math.imul(t ^ t >>> 7, 61 | t) ^ t;
|
|
15588
|
+
return ((t ^ t >>> 14) >>> 0) / 4294967296;
|
|
15589
|
+
};
|
|
15590
|
+
}
|
|
15591
|
+
function pickCategory(rng) {
|
|
15592
|
+
const roll = rng();
|
|
15084
15593
|
let cumulative = 0;
|
|
15085
15594
|
for (const [category, weight] of DEFAULT_TASK_DISTRIBUTION) {
|
|
15086
15595
|
cumulative += weight;
|
|
@@ -15088,12 +15597,12 @@ function pickCategory() {
|
|
|
15088
15597
|
}
|
|
15089
15598
|
return "code_generation";
|
|
15090
15599
|
}
|
|
15091
|
-
function pickCli(category) {
|
|
15600
|
+
function pickCli(category, rng) {
|
|
15092
15601
|
const primary = PRIMARY_CLI.get(category) ?? "claude";
|
|
15093
|
-
const roll =
|
|
15602
|
+
const roll = rng();
|
|
15094
15603
|
if (roll < 0.5) return primary;
|
|
15095
15604
|
if (roll < 0.75) return CLI_NAMES3.find((c) => c !== primary) ?? "gemini";
|
|
15096
|
-
return CLI_NAMES3[Math.floor(
|
|
15605
|
+
return CLI_NAMES3[Math.floor(rng() * CLI_NAMES3.length)] ?? "claude";
|
|
15097
15606
|
}
|
|
15098
15607
|
function getCliRole(cli, category) {
|
|
15099
15608
|
const primary = PRIMARY_CLI.get(category);
|
|
@@ -15112,12 +15621,12 @@ function getCliRole(cli, category) {
|
|
|
15112
15621
|
]);
|
|
15113
15622
|
return cli === secondaryMap.get(category) ? "secondary" : "other";
|
|
15114
15623
|
}
|
|
15115
|
-
function simulateTask(taskIndex, category, cli) {
|
|
15624
|
+
function simulateTask(taskIndex, category, cli, rng) {
|
|
15116
15625
|
const role = getCliRole(cli, category);
|
|
15117
15626
|
const successRate = SUCCESS_RATES[role];
|
|
15118
15627
|
const latency = LATENCY_RANGES[role];
|
|
15119
|
-
const success =
|
|
15120
|
-
const durationMs = latency.min +
|
|
15628
|
+
const success = rng() < successRate;
|
|
15629
|
+
const durationMs = latency.min + rng() * (latency.max - latency.min);
|
|
15121
15630
|
return {
|
|
15122
15631
|
id: `e2e-${String(taskIndex)}-${cli}-${category}`,
|
|
15123
15632
|
cli,
|
|
@@ -15130,7 +15639,7 @@ function simulateTask(taskIndex, category, cli) {
|
|
|
15130
15639
|
source: "manual"
|
|
15131
15640
|
};
|
|
15132
15641
|
}
|
|
15133
|
-
function generateOutcomes(taskCount) {
|
|
15642
|
+
function generateOutcomes(taskCount, rng) {
|
|
15134
15643
|
const store = getOutcomeStore();
|
|
15135
15644
|
const success = /* @__PURE__ */ new Map();
|
|
15136
15645
|
const total = /* @__PURE__ */ new Map();
|
|
@@ -15139,9 +15648,9 @@ function generateOutcomes(taskCount) {
|
|
|
15139
15648
|
total.set(cli, 0);
|
|
15140
15649
|
}
|
|
15141
15650
|
for (let i = 0; i < taskCount; i++) {
|
|
15142
|
-
const category = pickCategory();
|
|
15143
|
-
const cli = pickCli(category);
|
|
15144
|
-
const outcome = simulateTask(i, category, cli);
|
|
15651
|
+
const category = pickCategory(rng);
|
|
15652
|
+
const cli = pickCli(category, rng);
|
|
15653
|
+
const outcome = simulateTask(i, category, cli, rng);
|
|
15145
15654
|
store.append(outcome);
|
|
15146
15655
|
total.set(cli, (total.get(cli) ?? 0) + 1);
|
|
15147
15656
|
if (outcome.success) success.set(cli, (success.get(cli) ?? 0) + 1);
|
|
@@ -15192,7 +15701,8 @@ function runE2EEval(config, logger17) {
|
|
|
15192
15701
|
resetOutcomeStore();
|
|
15193
15702
|
setOutcomeStore(new OutcomeStore());
|
|
15194
15703
|
}
|
|
15195
|
-
const
|
|
15704
|
+
const rng = createRng(config?.seed);
|
|
15705
|
+
const counts = generateOutcomes(taskCount, rng);
|
|
15196
15706
|
log.info("E2E eval: outcomes recorded", { taskCount });
|
|
15197
15707
|
const { bonuses, nonZeroCount } = checkAdaptiveBonuses();
|
|
15198
15708
|
const convergenceScore = computeConvergence(counts, nonZeroCount);
|
|
@@ -15549,13 +16059,13 @@ function formatEvalDetails(baseline, reflective, improvement, datasetSize) {
|
|
|
15549
16059
|
}
|
|
15550
16060
|
|
|
15551
16061
|
// src/cli/auth-command.ts
|
|
15552
|
-
import { existsSync as
|
|
16062
|
+
import { existsSync as existsSync16, statSync as statSync2, chmodSync } from "fs";
|
|
15553
16063
|
function isValidAuthSubcommand(value) {
|
|
15554
16064
|
return value === "init" || value === "show" || value === "rotate" || value === "help";
|
|
15555
16065
|
}
|
|
15556
16066
|
function getFilePermissions(filePath) {
|
|
15557
16067
|
try {
|
|
15558
|
-
const stats =
|
|
16068
|
+
const stats = statSync2(filePath);
|
|
15559
16069
|
return (stats.mode & 511).toString(8);
|
|
15560
16070
|
} catch {
|
|
15561
16071
|
return void 0;
|
|
@@ -15569,7 +16079,7 @@ function fixPermissions(filePath) {
|
|
|
15569
16079
|
}
|
|
15570
16080
|
function runAuthInit(options) {
|
|
15571
16081
|
const tokenFile = options.tokenFile ?? getDefaultTokenPath();
|
|
15572
|
-
const tokenExists =
|
|
16082
|
+
const tokenExists = existsSync16(tokenFile);
|
|
15573
16083
|
if (tokenExists && options.force !== true) {
|
|
15574
16084
|
return {
|
|
15575
16085
|
success: false,
|
|
@@ -15593,7 +16103,7 @@ function runAuthInit(options) {
|
|
|
15593
16103
|
}
|
|
15594
16104
|
function runAuthShow(options) {
|
|
15595
16105
|
const tokenFile = options.tokenFile ?? getDefaultTokenPath();
|
|
15596
|
-
const tokenExists =
|
|
16106
|
+
const tokenExists = existsSync16(tokenFile);
|
|
15597
16107
|
const token = readStoredToken(tokenFile);
|
|
15598
16108
|
if (!tokenExists || token === void 0) {
|
|
15599
16109
|
return {
|
|
@@ -15614,7 +16124,7 @@ function runAuthShow(options) {
|
|
|
15614
16124
|
}
|
|
15615
16125
|
function runAuthRotate(options) {
|
|
15616
16126
|
const tokenFile = options.tokenFile ?? getDefaultTokenPath();
|
|
15617
|
-
const tokenExists =
|
|
16127
|
+
const tokenExists = existsSync16(tokenFile);
|
|
15618
16128
|
if (!tokenExists) {
|
|
15619
16129
|
return {
|
|
15620
16130
|
success: false,
|
|
@@ -15650,14 +16160,14 @@ function runAuthCommand(options) {
|
|
|
15650
16160
|
success: true,
|
|
15651
16161
|
operation: "help",
|
|
15652
16162
|
tokenFile: getDefaultTokenPath(),
|
|
15653
|
-
tokenExists:
|
|
16163
|
+
tokenExists: existsSync16(getDefaultTokenPath())
|
|
15654
16164
|
};
|
|
15655
16165
|
default:
|
|
15656
16166
|
return {
|
|
15657
16167
|
success: false,
|
|
15658
16168
|
operation: "help",
|
|
15659
16169
|
tokenFile: getDefaultTokenPath(),
|
|
15660
|
-
tokenExists:
|
|
16170
|
+
tokenExists: existsSync16(getDefaultTokenPath()),
|
|
15661
16171
|
error: `Unknown subcommand: ${String(subcommand)}`
|
|
15662
16172
|
};
|
|
15663
16173
|
}
|
|
@@ -15773,7 +16283,7 @@ function authCommand(subcommand, options) {
|
|
|
15773
16283
|
|
|
15774
16284
|
// src/cli-help-text.ts
|
|
15775
16285
|
var HELP_TEXT = `
|
|
15776
|
-
nexus-agents -
|
|
16286
|
+
nexus-agents - Intelligent orchestration platform for AI coding tools
|
|
15777
16287
|
|
|
15778
16288
|
USAGE:
|
|
15779
16289
|
nexus-agents [OPTIONS]
|
|
@@ -15814,6 +16324,7 @@ COMMANDS:
|
|
|
15814
16324
|
capabilities Show model capabilities matrix (modalities, tools, features)
|
|
15815
16325
|
status At-a-glance project health dashboard (fitness, adapters, version)
|
|
15816
16326
|
health Swarm health metrics dashboard (utilization, routing accuracy, failures)
|
|
16327
|
+
validate Run unified validation (doctor + fitness + config)
|
|
15817
16328
|
auth Manage MCP authentication tokens (init, show, rotate)
|
|
15818
16329
|
|
|
15819
16330
|
OPTIONS:
|
|
@@ -16415,15 +16926,142 @@ function isValidCommand(value) {
|
|
|
16415
16926
|
"e2e-eval",
|
|
16416
16927
|
"routing-ab",
|
|
16417
16928
|
"memory-eval",
|
|
16418
|
-
"health"
|
|
16929
|
+
"health",
|
|
16930
|
+
"validate"
|
|
16419
16931
|
];
|
|
16420
16932
|
return validCommands.includes(value);
|
|
16421
16933
|
}
|
|
16422
16934
|
|
|
16423
16935
|
// src/cli-commands-handlers.ts
|
|
16424
|
-
import { existsSync as
|
|
16425
|
-
import { join as
|
|
16426
|
-
import { homedir as
|
|
16936
|
+
import { existsSync as existsSync18 } from "fs";
|
|
16937
|
+
import { join as join16 } from "path";
|
|
16938
|
+
import { homedir as homedir4 } from "os";
|
|
16939
|
+
|
|
16940
|
+
// src/mcp/tools/dev-pipeline-tool.ts
|
|
16941
|
+
import { z as z7 } from "zod";
|
|
16942
|
+
import * as fs17 from "fs";
|
|
16943
|
+
import * as path21 from "path";
|
|
16944
|
+
var DevPipelineInputSchema = z7.object({
|
|
16945
|
+
/** Direct task instructions. */
|
|
16946
|
+
task: z7.string().max(1e4).optional().describe("Direct task instructions (what to build)"),
|
|
16947
|
+
/** Path to a plan file (.md, .yaml, .txt) to use as input. */
|
|
16948
|
+
planFile: z7.string().max(500).optional().describe("Path to a plan/spec file to use as input"),
|
|
16949
|
+
/** Whether to run in dry-run mode (plan+vote only, no implementation). */
|
|
16950
|
+
dryRun: z7.boolean().default(false).describe("If true, stop after plan+vote (no implementation)"),
|
|
16951
|
+
/** Maximum vote iterations before proceeding (default: 3). */
|
|
16952
|
+
maxVoteIterations: z7.number().int().min(1).max(5).default(3).describe("Max plan\u2192vote iterations"),
|
|
16953
|
+
/** Maximum QA iterations per task (default: 3). */
|
|
16954
|
+
maxQaIterations: z7.number().int().min(1).max(5).default(3).describe("Max QA review iterations per task"),
|
|
16955
|
+
/** Working directory for the pipeline (default: cwd). Used for security scan and context. */
|
|
16956
|
+
workingDir: z7.string().max(500).optional().describe("Working directory (default: cwd)"),
|
|
16957
|
+
/** GitHub issue number to track progress on. Updates posted as comments. */
|
|
16958
|
+
issueNumber: z7.number().int().positive().optional().describe("GitHub issue to post progress to"),
|
|
16959
|
+
/** GitHub repo (owner/name) for issue tracking. */
|
|
16960
|
+
repo: z7.string().max(200).optional().describe("GitHub repo for issue tracking (e.g., owner/repo)"),
|
|
16961
|
+
/** Task tracking backend: github, gitlab, or json (default: json). */
|
|
16962
|
+
trackerBackend: z7.enum(["github", "gitlab", "json"]).default("json").describe("Task tracking backend for issue creation"),
|
|
16963
|
+
/** Labels to apply to created issues. */
|
|
16964
|
+
labels: z7.array(z7.string()).optional().describe("Labels for created issues"),
|
|
16965
|
+
/** Session ID for checkpoint/resume. Enables crash recovery. */
|
|
16966
|
+
sessionId: z7.string().max(128).regex(/^[a-zA-Z0-9_-]+$/).optional().describe("Session ID for checkpoint/resume (crash recovery)"),
|
|
16967
|
+
/** When true, use simulated votes instead of real CLI consensus (for testing). */
|
|
16968
|
+
simulateVotes: z7.boolean().default(false).describe("Use simulated votes (for testing without real CLIs)"),
|
|
16969
|
+
/** Voting strategy for consensus stages. */
|
|
16970
|
+
votingStrategy: z7.enum([
|
|
16971
|
+
"simple_majority",
|
|
16972
|
+
"supermajority",
|
|
16973
|
+
"unanimous",
|
|
16974
|
+
"higher_order",
|
|
16975
|
+
"proof_of_learning",
|
|
16976
|
+
"opinion_wise"
|
|
16977
|
+
]).optional().describe("Voting strategy for plan approval (default: higher_order)"),
|
|
16978
|
+
/** Use 3 agents instead of 6 for faster voting. */
|
|
16979
|
+
quickMode: z7.boolean().default(false).describe("Use 3 agents instead of 6 for faster consensus voting"),
|
|
16980
|
+
/** Maximum execution time per stage in milliseconds (min 30s, max 600s). */
|
|
16981
|
+
timeoutMs: z7.number().int().min(3e4).max(6e5).optional().describe("Max time per stage in ms (30000-600000). Default: varies by stage complexity"),
|
|
16982
|
+
/** Pipeline execution mode. */
|
|
16983
|
+
mode: z7.enum(["autonomous", "harness"]).default("autonomous").describe(
|
|
16984
|
+
"'autonomous': full pipeline. 'harness': stops after decompose, returns tasks for caller to implement."
|
|
16985
|
+
)
|
|
16986
|
+
});
|
|
16987
|
+
function resolveTaskInput(input) {
|
|
16988
|
+
if (input.task !== void 0 && input.task.trim() !== "") {
|
|
16989
|
+
return input.task;
|
|
16990
|
+
}
|
|
16991
|
+
if (input.planFile !== void 0) {
|
|
16992
|
+
const resolved = path21.resolve(input.planFile);
|
|
16993
|
+
const cwdRoot = path21.resolve(".");
|
|
16994
|
+
if (!resolved.startsWith(cwdRoot)) {
|
|
16995
|
+
throw new Error(`Path traversal denied: planFile must be within ${cwdRoot}`);
|
|
16996
|
+
}
|
|
16997
|
+
if (!fs17.existsSync(resolved)) {
|
|
16998
|
+
throw new Error(`Plan file not found: ${resolved}`);
|
|
16999
|
+
}
|
|
17000
|
+
return fs17.readFileSync(resolved, "utf-8");
|
|
17001
|
+
}
|
|
17002
|
+
throw new Error("Either task or planFile must be provided");
|
|
17003
|
+
}
|
|
17004
|
+
async function createStages(input) {
|
|
17005
|
+
const backendChoice = input.trackerBackend;
|
|
17006
|
+
const backend = backendChoice === "json" && input.repo !== void 0 ? await detectBackend() : backendChoice;
|
|
17007
|
+
const tracker = input.repo !== void 0 ? createTaskTracker({ backend, repo: input.repo, labels: input.labels }) : void 0;
|
|
17008
|
+
return createAgentStages({
|
|
17009
|
+
scanTarget: input.workingDir,
|
|
17010
|
+
simulateVotes: input.simulateVotes,
|
|
17011
|
+
votingStrategy: input.votingStrategy,
|
|
17012
|
+
quickMode: input.quickMode,
|
|
17013
|
+
issueNumber: input.issueNumber,
|
|
17014
|
+
repo: input.repo,
|
|
17015
|
+
tracker
|
|
17016
|
+
});
|
|
17017
|
+
}
|
|
17018
|
+
function buildStructuredOutput(result) {
|
|
17019
|
+
return {
|
|
17020
|
+
completed: result.completed,
|
|
17021
|
+
securityPassed: result.securityPassed,
|
|
17022
|
+
voteIterations: result.voteIterations,
|
|
17023
|
+
qaIterations: result.qaIterations,
|
|
17024
|
+
plan: result.plan,
|
|
17025
|
+
tasks: result.tasks.map((t) => ({
|
|
17026
|
+
id: t.id,
|
|
17027
|
+
title: t.title,
|
|
17028
|
+
status: t.status,
|
|
17029
|
+
implementation: t.implementation ?? null,
|
|
17030
|
+
feedback: t.feedback ?? null
|
|
17031
|
+
}))
|
|
17032
|
+
};
|
|
17033
|
+
}
|
|
17034
|
+
function registerDevPipelineTool(server, _deps) {
|
|
17035
|
+
server.tool("run_dev_pipeline", DevPipelineInputSchema.shape, async (args) => {
|
|
17036
|
+
const input = DevPipelineInputSchema.parse(args);
|
|
17037
|
+
try {
|
|
17038
|
+
const taskText = resolveTaskInput(input);
|
|
17039
|
+
const stages = await createStages(input);
|
|
17040
|
+
const pipelineOptions = {
|
|
17041
|
+
...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {},
|
|
17042
|
+
...input.dryRun ? { dryRun: true } : {},
|
|
17043
|
+
...input.mode === "harness" ? { mode: "harness" } : {}
|
|
17044
|
+
};
|
|
17045
|
+
const hasOptions = Object.keys(pipelineOptions).length > 0;
|
|
17046
|
+
const result = await runDevPipeline(
|
|
17047
|
+
taskText,
|
|
17048
|
+
stages,
|
|
17049
|
+
hasOptions ? pipelineOptions : void 0
|
|
17050
|
+
);
|
|
17051
|
+
flushPipelineMemory();
|
|
17052
|
+
const structured = buildStructuredOutput(result);
|
|
17053
|
+
return {
|
|
17054
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
17055
|
+
structuredContent: structured
|
|
17056
|
+
};
|
|
17057
|
+
} catch (error) {
|
|
17058
|
+
return {
|
|
17059
|
+
content: [{ type: "text", text: `Pipeline error: ${getErrorMessage(error)}` }],
|
|
17060
|
+
isError: true
|
|
17061
|
+
};
|
|
17062
|
+
}
|
|
17063
|
+
});
|
|
17064
|
+
}
|
|
16427
17065
|
|
|
16428
17066
|
// src/cli-server-stpa.ts
|
|
16429
17067
|
var TOOL_DEFINITIONS = [
|
|
@@ -16605,6 +17243,206 @@ function runStpaSafetyAnalysis(logger17, failOnHighSeverity) {
|
|
|
16605
17243
|
}
|
|
16606
17244
|
}
|
|
16607
17245
|
|
|
17246
|
+
// src/mcp/gateway/upstream-client.ts
|
|
17247
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
17248
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
17249
|
+
function resolveEnv(env) {
|
|
17250
|
+
if (env === void 0) return {};
|
|
17251
|
+
const resolved = {};
|
|
17252
|
+
for (const [key, val] of Object.entries(env)) {
|
|
17253
|
+
const match = /^\{env:(\w+)\}$/.exec(val);
|
|
17254
|
+
if (match?.[1] !== void 0) {
|
|
17255
|
+
const envKey = match[1];
|
|
17256
|
+
const envVal = process.env[envKey];
|
|
17257
|
+
if (envVal !== void 0) resolved[key] = envVal;
|
|
17258
|
+
} else {
|
|
17259
|
+
resolved[key] = val;
|
|
17260
|
+
}
|
|
17261
|
+
}
|
|
17262
|
+
return resolved;
|
|
17263
|
+
}
|
|
17264
|
+
var UpstreamClient = class {
|
|
17265
|
+
name;
|
|
17266
|
+
config;
|
|
17267
|
+
log;
|
|
17268
|
+
client = null;
|
|
17269
|
+
transport = null;
|
|
17270
|
+
state = "disconnected";
|
|
17271
|
+
tools = [];
|
|
17272
|
+
constructor(config, logger17) {
|
|
17273
|
+
this.name = config.name;
|
|
17274
|
+
this.config = config;
|
|
17275
|
+
this.log = logger17 ?? createLogger({ component: `upstream:${config.name}` });
|
|
17276
|
+
}
|
|
17277
|
+
/** Connect to the upstream server. Idempotent. */
|
|
17278
|
+
async connect() {
|
|
17279
|
+
if (this.state === "connected") return;
|
|
17280
|
+
if (this.state === "connecting") return;
|
|
17281
|
+
this.state = "connecting";
|
|
17282
|
+
try {
|
|
17283
|
+
const resolvedEnv = resolveEnv(this.config.env);
|
|
17284
|
+
const baseEnv = {};
|
|
17285
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
17286
|
+
if (v !== void 0) baseEnv[k] = v;
|
|
17287
|
+
}
|
|
17288
|
+
this.transport = new StdioClientTransport({
|
|
17289
|
+
command: this.config.command,
|
|
17290
|
+
args: [...this.config.args],
|
|
17291
|
+
env: { ...baseEnv, ...resolvedEnv }
|
|
17292
|
+
});
|
|
17293
|
+
this.client = new Client(
|
|
17294
|
+
{ name: `nexus-upstream-${this.name}`, version: "1.0.0" },
|
|
17295
|
+
{ capabilities: {} }
|
|
17296
|
+
);
|
|
17297
|
+
await this.client.connect(this.transport);
|
|
17298
|
+
const result = await this.client.listTools();
|
|
17299
|
+
this.tools = result.tools;
|
|
17300
|
+
this.state = "connected";
|
|
17301
|
+
this.log.info("Upstream server connected", {
|
|
17302
|
+
name: this.name,
|
|
17303
|
+
toolCount: this.tools.length,
|
|
17304
|
+
tools: this.tools.map((t) => t.name)
|
|
17305
|
+
});
|
|
17306
|
+
} catch (error) {
|
|
17307
|
+
this.state = "failed";
|
|
17308
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
17309
|
+
this.log.warn("Upstream server connection failed", {
|
|
17310
|
+
name: this.name,
|
|
17311
|
+
error: msg
|
|
17312
|
+
});
|
|
17313
|
+
throw error;
|
|
17314
|
+
}
|
|
17315
|
+
}
|
|
17316
|
+
/** Get available tools (prefixed with server name). */
|
|
17317
|
+
getTools() {
|
|
17318
|
+
return this.tools.map((t) => ({
|
|
17319
|
+
...t,
|
|
17320
|
+
name: `${this.name}.${t.name}`,
|
|
17321
|
+
description: `[${this.name}] ${t.description ?? ""}`
|
|
17322
|
+
}));
|
|
17323
|
+
}
|
|
17324
|
+
/** Call a tool on the upstream server. Returns the raw SDK result. */
|
|
17325
|
+
async callTool(toolName, args) {
|
|
17326
|
+
if (this.client === null || this.state !== "connected") {
|
|
17327
|
+
if (this.config.lazy) {
|
|
17328
|
+
await this.connect();
|
|
17329
|
+
} else {
|
|
17330
|
+
throw new Error(`Upstream server ${this.name} not connected`);
|
|
17331
|
+
}
|
|
17332
|
+
}
|
|
17333
|
+
const activeClient = this.client;
|
|
17334
|
+
if (activeClient === null) throw new Error(`Upstream server ${this.name} not connected`);
|
|
17335
|
+
return activeClient.callTool({ name: toolName, arguments: args });
|
|
17336
|
+
}
|
|
17337
|
+
/** Check if the server is healthy. */
|
|
17338
|
+
async ping() {
|
|
17339
|
+
if (this.client === null || this.state !== "connected") return false;
|
|
17340
|
+
try {
|
|
17341
|
+
await this.client.ping();
|
|
17342
|
+
return true;
|
|
17343
|
+
} catch {
|
|
17344
|
+
return false;
|
|
17345
|
+
}
|
|
17346
|
+
}
|
|
17347
|
+
/** Gracefully shut down the connection. */
|
|
17348
|
+
async shutdown() {
|
|
17349
|
+
if (this.client !== null) {
|
|
17350
|
+
try {
|
|
17351
|
+
await this.client.close();
|
|
17352
|
+
} catch {
|
|
17353
|
+
}
|
|
17354
|
+
this.client = null;
|
|
17355
|
+
}
|
|
17356
|
+
if (this.transport !== null) {
|
|
17357
|
+
try {
|
|
17358
|
+
await this.transport.close();
|
|
17359
|
+
} catch {
|
|
17360
|
+
}
|
|
17361
|
+
this.transport = null;
|
|
17362
|
+
}
|
|
17363
|
+
this.state = "disconnected";
|
|
17364
|
+
this.tools = [];
|
|
17365
|
+
this.log.info("Upstream server disconnected", { name: this.name });
|
|
17366
|
+
}
|
|
17367
|
+
/** Get current connection state. */
|
|
17368
|
+
getState() {
|
|
17369
|
+
return this.state;
|
|
17370
|
+
}
|
|
17371
|
+
};
|
|
17372
|
+
var UpstreamClientManager = class {
|
|
17373
|
+
clients = /* @__PURE__ */ new Map();
|
|
17374
|
+
log;
|
|
17375
|
+
constructor(logger17) {
|
|
17376
|
+
this.log = logger17 ?? createLogger({ component: "UpstreamClientManager" });
|
|
17377
|
+
}
|
|
17378
|
+
/** Register upstream servers from config. */
|
|
17379
|
+
registerServers(configs) {
|
|
17380
|
+
for (const config of configs) {
|
|
17381
|
+
if (this.clients.has(config.name)) {
|
|
17382
|
+
this.log.warn("Duplicate upstream server name", { name: config.name });
|
|
17383
|
+
continue;
|
|
17384
|
+
}
|
|
17385
|
+
this.clients.set(config.name, new UpstreamClient(config, this.log));
|
|
17386
|
+
this.log.info("Registered upstream server", {
|
|
17387
|
+
name: config.name,
|
|
17388
|
+
command: config.command,
|
|
17389
|
+
lazy: config.lazy
|
|
17390
|
+
});
|
|
17391
|
+
}
|
|
17392
|
+
}
|
|
17393
|
+
/** Connect all non-lazy servers. */
|
|
17394
|
+
async connectEager() {
|
|
17395
|
+
for (const client of this.clients.values()) {
|
|
17396
|
+
if (client.getState() === "disconnected") {
|
|
17397
|
+
try {
|
|
17398
|
+
await client.connect();
|
|
17399
|
+
} catch {
|
|
17400
|
+
}
|
|
17401
|
+
}
|
|
17402
|
+
}
|
|
17403
|
+
}
|
|
17404
|
+
/** Get all available tools across all connected upstream servers. */
|
|
17405
|
+
getAllTools() {
|
|
17406
|
+
const tools = [];
|
|
17407
|
+
for (const client of this.clients.values()) {
|
|
17408
|
+
if (client.getState() === "connected") {
|
|
17409
|
+
tools.push(...client.getTools());
|
|
17410
|
+
}
|
|
17411
|
+
}
|
|
17412
|
+
return tools;
|
|
17413
|
+
}
|
|
17414
|
+
/** Route a prefixed tool call to the appropriate upstream server. */
|
|
17415
|
+
async callTool(prefixedName, args) {
|
|
17416
|
+
const dotIndex = prefixedName.indexOf(".");
|
|
17417
|
+
if (dotIndex === -1) return null;
|
|
17418
|
+
const serverName = prefixedName.slice(0, dotIndex);
|
|
17419
|
+
const toolName = prefixedName.slice(dotIndex + 1);
|
|
17420
|
+
const client = this.clients.get(serverName);
|
|
17421
|
+
if (client === void 0) return null;
|
|
17422
|
+
return client.callTool(toolName, args);
|
|
17423
|
+
}
|
|
17424
|
+
/** Check if a tool name is an upstream prefixed tool. */
|
|
17425
|
+
isUpstreamTool(name) {
|
|
17426
|
+
const dotIndex = name.indexOf(".");
|
|
17427
|
+
if (dotIndex === -1) return false;
|
|
17428
|
+
return this.clients.has(name.slice(0, dotIndex));
|
|
17429
|
+
}
|
|
17430
|
+
/** Shut down all upstream connections. */
|
|
17431
|
+
async shutdownAll() {
|
|
17432
|
+
const promises = [...this.clients.values()].map((c) => c.shutdown());
|
|
17433
|
+
await Promise.allSettled(promises);
|
|
17434
|
+
this.clients.clear();
|
|
17435
|
+
this.log.info("All upstream servers shut down");
|
|
17436
|
+
}
|
|
17437
|
+
/** Get health status of all upstream servers. */
|
|
17438
|
+
getStatus() {
|
|
17439
|
+
return [...this.clients.entries()].map(([name, client]) => ({
|
|
17440
|
+
name,
|
|
17441
|
+
state: client.getState()
|
|
17442
|
+
}));
|
|
17443
|
+
}
|
|
17444
|
+
};
|
|
17445
|
+
|
|
16608
17446
|
// src/cli-server-tools.ts
|
|
16609
17447
|
var OrchestratorUnavailableError = class extends NexusError {
|
|
16610
17448
|
constructor(message) {
|
|
@@ -16718,6 +17556,10 @@ function registerResearchTools(ctx) {
|
|
|
16718
17556
|
...researchDeps,
|
|
16719
17557
|
rateLimiter: ctx.rateLimiterFactory.getForTool("research_add")
|
|
16720
17558
|
});
|
|
17559
|
+
registerResearchAddSourceTool(ctx.server, {
|
|
17560
|
+
...researchDeps,
|
|
17561
|
+
rateLimiter: ctx.rateLimiterFactory.getForTool("research_add_source")
|
|
17562
|
+
});
|
|
16721
17563
|
registerResearchDiscoverTool(ctx.server, {
|
|
16722
17564
|
...researchDeps,
|
|
16723
17565
|
rateLimiter: ctx.rateLimiterFactory.getForTool("research_discover")
|
|
@@ -16785,6 +17627,33 @@ function registerOrchestrateToolSafe(ctx) {
|
|
|
16785
17627
|
});
|
|
16786
17628
|
}
|
|
16787
17629
|
}
|
|
17630
|
+
async function initUpstreamServers(gatewayConfig, server, logger17) {
|
|
17631
|
+
const upstreamServers = gatewayConfig?.["upstreamServers"];
|
|
17632
|
+
if (upstreamServers === void 0 || upstreamServers.length === 0) return;
|
|
17633
|
+
const manager = new UpstreamClientManager(logger17);
|
|
17634
|
+
manager.registerServers(upstreamServers);
|
|
17635
|
+
await manager.connectEager();
|
|
17636
|
+
const tools = manager.getAllTools();
|
|
17637
|
+
logger17.info("Upstream MCP servers initialized", {
|
|
17638
|
+
servers: upstreamServers.length,
|
|
17639
|
+
tools: tools.length
|
|
17640
|
+
});
|
|
17641
|
+
const { z: z8 } = await import("zod");
|
|
17642
|
+
const passthroughSchema = z8.looseObject({});
|
|
17643
|
+
for (const tool of tools) {
|
|
17644
|
+
const toolName = tool.name;
|
|
17645
|
+
const desc = tool.description ?? `Upstream tool: ${toolName}`;
|
|
17646
|
+
server.registerTool(
|
|
17647
|
+
toolName,
|
|
17648
|
+
{ description: desc, inputSchema: passthroughSchema },
|
|
17649
|
+
async (args) => {
|
|
17650
|
+
const upstreamResult = await manager.callTool(toolName, args);
|
|
17651
|
+
const text = upstreamResult !== null ? JSON.stringify(upstreamResult) : "Upstream tool not found";
|
|
17652
|
+
return { content: [{ type: "text", text }] };
|
|
17653
|
+
}
|
|
17654
|
+
);
|
|
17655
|
+
}
|
|
17656
|
+
}
|
|
16788
17657
|
function maybeRunStpaAnalysis(options, logger17) {
|
|
16789
17658
|
const enableStpa = options.enableStpaSafetyAnalysis ?? false;
|
|
16790
17659
|
if (!enableStpa) return;
|
|
@@ -16859,7 +17728,11 @@ var STANDALONE_TOOLS = [
|
|
|
16859
17728
|
{ name: "run_graph_workflow", register: registerRunGraphWorkflowTool },
|
|
16860
17729
|
{ name: "execute_spec", register: registerExecuteSpecTool },
|
|
16861
17730
|
{ name: "list_experts", register: registerListExpertsTool },
|
|
16862
|
-
{ name: "query_trace", register: registerQueryTraceTool }
|
|
17731
|
+
{ name: "query_trace", register: registerQueryTraceTool },
|
|
17732
|
+
{ name: "extract_symbols", register: registerExtractSymbolsTool },
|
|
17733
|
+
{ name: "search_codebase", register: registerSearchCodebaseTool },
|
|
17734
|
+
{ name: "run_dev_pipeline", register: registerDevPipelineTool },
|
|
17735
|
+
{ name: "run_pipeline", register: registerPipelineTool }
|
|
16863
17736
|
];
|
|
16864
17737
|
function registerToolCategories(ctx) {
|
|
16865
17738
|
const allowlist = ctx.toolAllowlist;
|
|
@@ -16914,6 +17787,7 @@ function registerMcpTools(options) {
|
|
|
16914
17787
|
const gatewayOptions = { ...options, server: observableServer };
|
|
16915
17788
|
const ctx = createToolContext(gatewayOptions, toolInfra, rateLimiterFactory);
|
|
16916
17789
|
registerToolCategories(ctx);
|
|
17790
|
+
void initUpstreamServers(gatewayConfig, observableServer, logger17);
|
|
16917
17791
|
registerPrompts(observableServer, logger17);
|
|
16918
17792
|
registerResources(observableServer, logger17);
|
|
16919
17793
|
logToolRegistration(logger17, ctx.toolAllowlist, {
|
|
@@ -17017,7 +17891,7 @@ function logFinalEventBusStats(logger17) {
|
|
|
17017
17891
|
// src/cli-orchestrator.ts
|
|
17018
17892
|
import * as readline2 from "readline";
|
|
17019
17893
|
function runOrchestratorRepl(options, logger17) {
|
|
17020
|
-
return new Promise((
|
|
17894
|
+
return new Promise((resolve14) => {
|
|
17021
17895
|
const rl = readline2.createInterface({
|
|
17022
17896
|
input: process.stdin,
|
|
17023
17897
|
output: process.stdout,
|
|
@@ -17053,7 +17927,7 @@ function runOrchestratorRepl(options, logger17) {
|
|
|
17053
17927
|
});
|
|
17054
17928
|
rl.on("close", () => {
|
|
17055
17929
|
logger17.info("Orchestrator REPL closed");
|
|
17056
|
-
|
|
17930
|
+
resolve14();
|
|
17057
17931
|
});
|
|
17058
17932
|
});
|
|
17059
17933
|
}
|
|
@@ -18198,127 +19072,6 @@ function initializeFeedbackIntegration(options) {
|
|
|
18198
19072
|
}
|
|
18199
19073
|
}
|
|
18200
19074
|
|
|
18201
|
-
// src/cli-server-rest.ts
|
|
18202
|
-
import * as fs16 from "fs";
|
|
18203
|
-
import * as path22 from "path";
|
|
18204
|
-
import * as crypto3 from "crypto";
|
|
18205
|
-
import { homedir as homedir3 } from "os";
|
|
18206
|
-
var DEFAULT_REST_CONFIG = {
|
|
18207
|
-
enabled: false,
|
|
18208
|
-
port: 3e3,
|
|
18209
|
-
host: "0.0.0.0",
|
|
18210
|
-
cors: true,
|
|
18211
|
-
swagger: true
|
|
18212
|
-
};
|
|
18213
|
-
var configLogger = createLogger({ component: "RestApiConfig" });
|
|
18214
|
-
function parseValidPort(value) {
|
|
18215
|
-
const port = parseInt(value, 10);
|
|
18216
|
-
if (isNaN(port) || port < 1 || port > 65535) {
|
|
18217
|
-
configLogger.warn("Invalid NEXUS_REST_PORT value, using default", {
|
|
18218
|
-
value,
|
|
18219
|
-
default: DEFAULT_REST_CONFIG.port
|
|
18220
|
-
});
|
|
18221
|
-
return DEFAULT_REST_CONFIG.port;
|
|
18222
|
-
}
|
|
18223
|
-
return port;
|
|
18224
|
-
}
|
|
18225
|
-
function extractRestConfig(_config) {
|
|
18226
|
-
const portEnv = process.env["NEXUS_REST_PORT"];
|
|
18227
|
-
const hostEnv = process.env["NEXUS_REST_HOST"];
|
|
18228
|
-
return {
|
|
18229
|
-
enabled: process.env["NEXUS_REST_ENABLED"] === "true",
|
|
18230
|
-
port: portEnv !== void 0 ? parseValidPort(portEnv) : DEFAULT_REST_CONFIG.port,
|
|
18231
|
-
host: hostEnv ?? DEFAULT_REST_CONFIG.host,
|
|
18232
|
-
cors: DEFAULT_REST_CONFIG.cors,
|
|
18233
|
-
swagger: DEFAULT_REST_CONFIG.swagger
|
|
18234
|
-
};
|
|
18235
|
-
}
|
|
18236
|
-
var AUTH_DIR = "auth";
|
|
18237
|
-
var API_KEY_FILE = "rest-api-key";
|
|
18238
|
-
function getAuthDir(baseDir) {
|
|
18239
|
-
const home = baseDir ?? path22.join(homedir3(), ".nexus-agents");
|
|
18240
|
-
return path22.join(home, AUTH_DIR);
|
|
18241
|
-
}
|
|
18242
|
-
function loadOrGenerateApiKey(logger17, baseDir) {
|
|
18243
|
-
const authDir = getAuthDir(baseDir);
|
|
18244
|
-
const keyPath = path22.join(authDir, API_KEY_FILE);
|
|
18245
|
-
if (fs16.existsSync(keyPath)) {
|
|
18246
|
-
const key2 = fs16.readFileSync(keyPath, "utf-8").trim();
|
|
18247
|
-
if (key2.length > 0) {
|
|
18248
|
-
logger17.info("Loaded REST API key from disk", { keyFile: keyPath });
|
|
18249
|
-
return { key: key2, name: "auto-generated" };
|
|
18250
|
-
}
|
|
18251
|
-
}
|
|
18252
|
-
const key = "nxa-" + crypto3.randomBytes(24).toString("hex");
|
|
18253
|
-
if (!fs16.existsSync(authDir)) {
|
|
18254
|
-
fs16.mkdirSync(authDir, { recursive: true, mode: 448 });
|
|
18255
|
-
}
|
|
18256
|
-
fs16.writeFileSync(keyPath, key + "\n", { mode: 384 });
|
|
18257
|
-
logger17.info("Generated new REST API key", { keyFile: keyPath });
|
|
18258
|
-
logger17.warn("REST API key stored at: " + keyPath + " \u2014 keep this file secure");
|
|
18259
|
-
return { key, name: "auto-generated" };
|
|
18260
|
-
}
|
|
18261
|
-
function buildApiKeys(logger17, authHandler) {
|
|
18262
|
-
const keys = [loadOrGenerateApiKey(logger17)];
|
|
18263
|
-
if (authHandler?.isEnabled() === true && authHandler.hasToken()) {
|
|
18264
|
-
const token = authHandler.getStoredTokenForIntegration();
|
|
18265
|
-
if (token !== void 0) {
|
|
18266
|
-
keys.push({ key: token, name: "mcp-bearer-token" });
|
|
18267
|
-
logger17.info("REST API also accepts MCP bearer token for authentication");
|
|
18268
|
-
}
|
|
18269
|
-
}
|
|
18270
|
-
return keys;
|
|
18271
|
-
}
|
|
18272
|
-
async function startRestApiServer(config, logger17, authHandler) {
|
|
18273
|
-
if (!config.enabled) {
|
|
18274
|
-
logger17.debug("REST API disabled (set NEXUS_REST_ENABLED=true to enable)");
|
|
18275
|
-
return null;
|
|
18276
|
-
}
|
|
18277
|
-
const restLogger = createLogger({ component: "RestApiServer" });
|
|
18278
|
-
const apiKeys = buildApiKeys(logger17, authHandler);
|
|
18279
|
-
const serverOptions = {
|
|
18280
|
-
config: {
|
|
18281
|
-
port: config.port,
|
|
18282
|
-
host: config.host,
|
|
18283
|
-
enableCors: config.cors,
|
|
18284
|
-
enableSwagger: config.swagger
|
|
18285
|
-
},
|
|
18286
|
-
logger: restLogger,
|
|
18287
|
-
apiKeys
|
|
18288
|
-
};
|
|
18289
|
-
const server = new RestApiServer(serverOptions);
|
|
18290
|
-
await server.start();
|
|
18291
|
-
logger17.info("REST API server started", {
|
|
18292
|
-
address: `http://${config.host}:${String(config.port)}`,
|
|
18293
|
-
swagger: config.swagger ? `http://${config.host}:${String(config.port)}/docs` : "disabled"
|
|
18294
|
-
});
|
|
18295
|
-
return server;
|
|
18296
|
-
}
|
|
18297
|
-
async function stopRestApiServer(server, logger17) {
|
|
18298
|
-
if (server === null) {
|
|
18299
|
-
return;
|
|
18300
|
-
}
|
|
18301
|
-
try {
|
|
18302
|
-
await server.stop();
|
|
18303
|
-
logger17.info("REST API server stopped");
|
|
18304
|
-
} catch (error) {
|
|
18305
|
-
logger17.error(
|
|
18306
|
-
"Error stopping REST API server",
|
|
18307
|
-
error instanceof Error ? error : new Error(String(error))
|
|
18308
|
-
);
|
|
18309
|
-
}
|
|
18310
|
-
}
|
|
18311
|
-
function logRestApiConfig(config, logger17) {
|
|
18312
|
-
if (config.enabled) {
|
|
18313
|
-
logger17.info("REST API configuration", {
|
|
18314
|
-
port: config.port,
|
|
18315
|
-
host: config.host,
|
|
18316
|
-
cors: config.cors,
|
|
18317
|
-
swagger: config.swagger
|
|
18318
|
-
});
|
|
18319
|
-
}
|
|
18320
|
-
}
|
|
18321
|
-
|
|
18322
19075
|
// src/cli-server-auth.ts
|
|
18323
19076
|
function resolveAuthEnabled(config) {
|
|
18324
19077
|
const envValue = process.env["NEXUS_AUTH_ENABLED"];
|
|
@@ -18363,9 +19116,9 @@ function initializeAuth(config, logger17) {
|
|
|
18363
19116
|
}
|
|
18364
19117
|
|
|
18365
19118
|
// src/cli-server-audit.ts
|
|
18366
|
-
import { join as
|
|
18367
|
-
import { homedir as
|
|
18368
|
-
var DEFAULT_AUDIT_DIR =
|
|
19119
|
+
import { join as join15 } from "path";
|
|
19120
|
+
import { homedir as homedir3 } from "os";
|
|
19121
|
+
var DEFAULT_AUDIT_DIR = join15(homedir3(), ".nexus-agents", "audit");
|
|
18369
19122
|
function initializeAuditLogger(securityConfig, logger17) {
|
|
18370
19123
|
if (securityConfig?.audit?.enabled !== true) {
|
|
18371
19124
|
logger17.debug("Audit logging disabled (set security.audit.enabled: true to enable)");
|
|
@@ -18535,18 +19288,8 @@ function loadAndLogConfig(logger17) {
|
|
|
18535
19288
|
return configResult;
|
|
18536
19289
|
}
|
|
18537
19290
|
function createShutdownCleanup(options) {
|
|
18538
|
-
const {
|
|
18539
|
-
eventBusBridge,
|
|
18540
|
-
observer,
|
|
18541
|
-
eventContext,
|
|
18542
|
-
server,
|
|
18543
|
-
serverLogger,
|
|
18544
|
-
logger: logger17,
|
|
18545
|
-
restServer,
|
|
18546
|
-
auditLogger
|
|
18547
|
-
} = options;
|
|
19291
|
+
const { eventBusBridge, observer, eventContext, server, serverLogger, logger: logger17, auditLogger } = options;
|
|
18548
19292
|
return async () => {
|
|
18549
|
-
await stopRestApiServer(restServer, logger17);
|
|
18550
19293
|
await shutdownAuditLogger(auditLogger, logger17);
|
|
18551
19294
|
if (eventBusBridge.initialized) {
|
|
18552
19295
|
logFinalEventBusStats(logger17);
|
|
@@ -18703,11 +19446,8 @@ async function startServer(verbose, mode, modeWasExplicit = false, orchestratorO
|
|
|
18703
19446
|
const configResult = loadAndLogConfig(logger17);
|
|
18704
19447
|
applyLoggingConfig(logger17, verbose, configResult.config);
|
|
18705
19448
|
validateNexusEnv(logger17);
|
|
18706
|
-
const { server, serverLogger, observer, eventBusBridge, auditLogger
|
|
19449
|
+
const { server, serverLogger, observer, eventBusBridge, auditLogger } = await initializeSubsystems(configResult.config, logger17);
|
|
18707
19450
|
await connectToStdioTransport(server, logger17, serverLogger);
|
|
18708
|
-
const restConfig = extractRestConfig(configResult.config);
|
|
18709
|
-
logRestApiConfig(restConfig, logger17);
|
|
18710
|
-
const restServer = await startRestApiServer(restConfig, logger17, authInit.handler);
|
|
18711
19451
|
const eventContext = recordServerStartup(observer);
|
|
18712
19452
|
const stdinMonitor = getStdinLifecycleMonitor();
|
|
18713
19453
|
stdinMonitor.start();
|
|
@@ -18722,7 +19462,6 @@ async function startServer(verbose, mode, modeWasExplicit = false, orchestratorO
|
|
|
18722
19462
|
server,
|
|
18723
19463
|
serverLogger,
|
|
18724
19464
|
logger: logger17,
|
|
18725
|
-
restServer,
|
|
18726
19465
|
auditLogger
|
|
18727
19466
|
});
|
|
18728
19467
|
setupShutdownHandlers(cleanup, logger17);
|
|
@@ -18973,9 +19712,9 @@ function buildOrchestratorOptions(args) {
|
|
|
18973
19712
|
function printFirstRunHint() {
|
|
18974
19713
|
const isTTY = process.stderr.isTTY;
|
|
18975
19714
|
if (!isTTY) return;
|
|
18976
|
-
const dataDir =
|
|
18977
|
-
const hasConfig =
|
|
18978
|
-
if (
|
|
19715
|
+
const dataDir = join16(homedir4(), ".nexus-agents");
|
|
19716
|
+
const hasConfig = existsSync18("./nexus-agents.yaml") || existsSync18("./nexus-agents.yml");
|
|
19717
|
+
if (existsSync18(dataDir) || hasConfig) return;
|
|
18979
19718
|
process.stderr.write(
|
|
18980
19719
|
"\n\x1B[36mnexus-agents\x1B[0m: First time? Run \x1B[1mnexus-agents setup\x1B[0m to configure.\n\n"
|
|
18981
19720
|
);
|
|
@@ -19109,7 +19848,7 @@ async function handleVerifyCommand(args) {
|
|
|
19109
19848
|
async function handleDoctorCommand(args) {
|
|
19110
19849
|
const exitCode = await doctorCommand({ fix: args.options.fix });
|
|
19111
19850
|
if (args.options.deep) {
|
|
19112
|
-
const { runDeepDiagnostics: runDeepDiagnostics2, formatDeepDiagnostics: formatDeepDiagnostics2 } = await import("./doctor-deep-
|
|
19851
|
+
const { runDeepDiagnostics: runDeepDiagnostics2, formatDeepDiagnostics: formatDeepDiagnostics2 } = await import("./doctor-deep-LMCEAFU4.js");
|
|
19113
19852
|
const diag = runDeepDiagnostics2();
|
|
19114
19853
|
process.stdout.write(formatDeepDiagnostics2(diag) + "\n");
|
|
19115
19854
|
}
|
|
@@ -19360,25 +20099,25 @@ function handleScaffoldCommand(args) {
|
|
|
19360
20099
|
}
|
|
19361
20100
|
|
|
19362
20101
|
// src/cli/visualize-summary.ts
|
|
19363
|
-
import * as
|
|
19364
|
-
import * as
|
|
20102
|
+
import * as fs18 from "fs";
|
|
20103
|
+
import * as path22 from "path";
|
|
19365
20104
|
import { fileURLToPath } from "url";
|
|
19366
20105
|
function findPackageRoot(startDir) {
|
|
19367
20106
|
let dir = startDir;
|
|
19368
20107
|
for (let i = 0; i < 5; i++) {
|
|
19369
|
-
if (
|
|
19370
|
-
const parent =
|
|
20108
|
+
if (fs18.existsSync(path22.join(dir, "package.json"))) return dir;
|
|
20109
|
+
const parent = path22.dirname(dir);
|
|
19371
20110
|
if (parent === dir) break;
|
|
19372
20111
|
dir = parent;
|
|
19373
20112
|
}
|
|
19374
20113
|
return startDir;
|
|
19375
20114
|
}
|
|
19376
|
-
function
|
|
19377
|
-
if (!
|
|
20115
|
+
function countFiles2(dir, pattern) {
|
|
20116
|
+
if (!fs18.existsSync(dir)) return 0;
|
|
19378
20117
|
let count = 0;
|
|
19379
|
-
for (const entry of
|
|
20118
|
+
for (const entry of fs18.readdirSync(dir, { withFileTypes: true })) {
|
|
19380
20119
|
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist") {
|
|
19381
|
-
count +=
|
|
20120
|
+
count += countFiles2(path22.join(dir, entry.name), pattern);
|
|
19382
20121
|
} else if (entry.isFile() && pattern.test(entry.name)) {
|
|
19383
20122
|
count++;
|
|
19384
20123
|
}
|
|
@@ -19387,7 +20126,7 @@ function countFiles(dir, pattern) {
|
|
|
19387
20126
|
}
|
|
19388
20127
|
function readVersion(pkgRoot) {
|
|
19389
20128
|
try {
|
|
19390
|
-
const raw =
|
|
20129
|
+
const raw = fs18.readFileSync(path22.join(pkgRoot, "package.json"), "utf-8");
|
|
19391
20130
|
const pkg = JSON.parse(raw);
|
|
19392
20131
|
return pkg.version ?? "unknown";
|
|
19393
20132
|
} catch {
|
|
@@ -19408,13 +20147,13 @@ var LAYER_DIRS = [
|
|
|
19408
20147
|
];
|
|
19409
20148
|
function gatherSystemSummary() {
|
|
19410
20149
|
const thisFile = fileURLToPath(import.meta.url);
|
|
19411
|
-
const pkgRoot = findPackageRoot(
|
|
19412
|
-
const srcDir =
|
|
19413
|
-
const allTs =
|
|
19414
|
-
const testTs =
|
|
20150
|
+
const pkgRoot = findPackageRoot(path22.dirname(thisFile));
|
|
20151
|
+
const srcDir = path22.resolve(pkgRoot, "src");
|
|
20152
|
+
const allTs = countFiles2(srcDir, /\.ts$/);
|
|
20153
|
+
const testTs = countFiles2(srcDir, /\.test\.ts$/);
|
|
19415
20154
|
const layers = LAYER_DIRS.map(({ name, dir }) => ({
|
|
19416
20155
|
name,
|
|
19417
|
-
files:
|
|
20156
|
+
files: countFiles2(path22.join(srcDir, dir), /\.ts$/)
|
|
19418
20157
|
})).filter((l) => l.files > 0);
|
|
19419
20158
|
return {
|
|
19420
20159
|
version: readVersion(pkgRoot),
|
|
@@ -19664,8 +20403,8 @@ function generateDiagramOutput(subcommand, format) {
|
|
|
19664
20403
|
}
|
|
19665
20404
|
function writeOutput(output2, outputPath) {
|
|
19666
20405
|
if (outputPath !== void 0) {
|
|
19667
|
-
import("fs").then((
|
|
19668
|
-
|
|
20406
|
+
import("fs").then((fs19) => {
|
|
20407
|
+
fs19.writeFileSync(outputPath, output2 + "\n", "utf-8");
|
|
19669
20408
|
process.stdout.write(`Diagram written to ${outputPath}
|
|
19670
20409
|
`);
|
|
19671
20410
|
}).catch((e) => {
|
|
@@ -20456,7 +21195,7 @@ async function handleMemoryBenchmarkCommand(args) {
|
|
|
20456
21195
|
}
|
|
20457
21196
|
|
|
20458
21197
|
// src/cli/status-command.ts
|
|
20459
|
-
import { execFileSync } from "child_process";
|
|
21198
|
+
import { execFileSync as execFileSync2 } from "child_process";
|
|
20460
21199
|
var FITNESS_TARGET = 90;
|
|
20461
21200
|
var API_ADAPTER_CHECKS = [
|
|
20462
21201
|
{ name: "Claude", envVar: "ANTHROPIC_API_KEY" },
|
|
@@ -20478,7 +21217,7 @@ function checkApiAdapters() {
|
|
|
20478
21217
|
function detectCliTools() {
|
|
20479
21218
|
return CLI_TOOL_CHECKS.map((check) => {
|
|
20480
21219
|
try {
|
|
20481
|
-
const version =
|
|
21220
|
+
const version = execFileSync2(check.binary, ["--version"], {
|
|
20482
21221
|
timeout: CLI_SUBPROCESS_TIMEOUTS.statusProbeMs,
|
|
20483
21222
|
stdio: ["ignore", "pipe", "ignore"],
|
|
20484
21223
|
encoding: "utf-8"
|
|
@@ -20563,10 +21302,10 @@ function handleStatusCommand2(args) {
|
|
|
20563
21302
|
|
|
20564
21303
|
// src/cli/scenario-command.ts
|
|
20565
21304
|
import { readdir as readdir4 } from "fs/promises";
|
|
20566
|
-
import { join as
|
|
21305
|
+
import { join as join18, resolve as resolve13 } from "path";
|
|
20567
21306
|
|
|
20568
21307
|
// src/testing/e2e/scenario-runner.ts
|
|
20569
|
-
import { readFile as
|
|
21308
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
20570
21309
|
import * as yaml7 from "yaml";
|
|
20571
21310
|
|
|
20572
21311
|
// src/testing/e2e/types.ts
|
|
@@ -20694,9 +21433,9 @@ function checkCircularDependencies(workflow) {
|
|
|
20694
21433
|
for (const step of workflow.steps) {
|
|
20695
21434
|
stepMap.set(step.id, step);
|
|
20696
21435
|
}
|
|
20697
|
-
const visit = (stepId,
|
|
21436
|
+
const visit = (stepId, path23) => {
|
|
20698
21437
|
if (visiting.has(stepId)) {
|
|
20699
|
-
errors.push(`Circular dependency detected: ${[...
|
|
21438
|
+
errors.push(`Circular dependency detected: ${[...path23, stepId].join(" -> ")}`);
|
|
20700
21439
|
return false;
|
|
20701
21440
|
}
|
|
20702
21441
|
if (visited.has(stepId)) return true;
|
|
@@ -20704,7 +21443,7 @@ function checkCircularDependencies(workflow) {
|
|
|
20704
21443
|
const step = stepMap.get(stepId);
|
|
20705
21444
|
if (step?.dependsOn) {
|
|
20706
21445
|
for (const dep of step.dependsOn) {
|
|
20707
|
-
if (!visit(dep, [...
|
|
21446
|
+
if (!visit(dep, [...path23, stepId])) {
|
|
20708
21447
|
return false;
|
|
20709
21448
|
}
|
|
20710
21449
|
}
|
|
@@ -20723,7 +21462,7 @@ function checkCircularDependencies(workflow) {
|
|
|
20723
21462
|
var defaultStubFactory = {
|
|
20724
21463
|
createAgentStub(agentType, action) {
|
|
20725
21464
|
return async (inputs) => {
|
|
20726
|
-
await new Promise((
|
|
21465
|
+
await new Promise((resolve14) => setTimeout(resolve14, 10));
|
|
20727
21466
|
return {
|
|
20728
21467
|
stepId: `${agentType}-${action}`,
|
|
20729
21468
|
status: "success",
|
|
@@ -20748,8 +21487,8 @@ var ScenarioRunner = class {
|
|
|
20748
21487
|
/**
|
|
20749
21488
|
* Load a scenario fixture from a YAML file.
|
|
20750
21489
|
*/
|
|
20751
|
-
async loadFixture(
|
|
20752
|
-
const content = await
|
|
21490
|
+
async loadFixture(path23) {
|
|
21491
|
+
const content = await readFile7(path23, "utf-8");
|
|
20753
21492
|
const data = yaml7.parse(content);
|
|
20754
21493
|
return ScenarioFixtureSchema.parse(data);
|
|
20755
21494
|
}
|
|
@@ -20882,7 +21621,7 @@ function createScenarioRunner(stubFactory) {
|
|
|
20882
21621
|
}
|
|
20883
21622
|
|
|
20884
21623
|
// src/cli/scenario-command.ts
|
|
20885
|
-
var FIXTURES_DIR =
|
|
21624
|
+
var FIXTURES_DIR = resolve13(import.meta.dirname, "../testing/e2e/fixtures");
|
|
20886
21625
|
var SCENARIO_SUFFIX = ".scenario.yaml";
|
|
20887
21626
|
async function listScenarios() {
|
|
20888
21627
|
try {
|
|
@@ -20933,7 +21672,7 @@ async function handleRun(args) {
|
|
|
20933
21672
|
process.exit(EXIT_CODES.SERVER_START_FAILED);
|
|
20934
21673
|
}
|
|
20935
21674
|
const runner = createScenarioRunner();
|
|
20936
|
-
const fixturePath =
|
|
21675
|
+
const fixturePath = join18(FIXTURES_DIR, `${name}${SCENARIO_SUFFIX}`);
|
|
20937
21676
|
try {
|
|
20938
21677
|
const fixture = await runner.loadFixture(fixturePath);
|
|
20939
21678
|
const result = await runner.run(fixture);
|
|
@@ -21094,6 +21833,67 @@ function handleHealthCommand(args) {
|
|
|
21094
21833
|
}
|
|
21095
21834
|
}
|
|
21096
21835
|
|
|
21836
|
+
// src/cli/validate-command.ts
|
|
21837
|
+
var MIN_FITNESS_SCORE = 90;
|
|
21838
|
+
function evaluateDoctor(doctor) {
|
|
21839
|
+
const installedCount = doctor.clis.filter((c) => c.installed).length;
|
|
21840
|
+
const total = doctor.clis.length;
|
|
21841
|
+
const summary = doctor.allHealthy ? `All systems healthy (${String(installedCount)}/${String(total)} CLIs installed)` : `Issues detected (${String(installedCount)}/${String(total)} CLIs installed)`;
|
|
21842
|
+
return { name: "Doctor", passed: doctor.allHealthy, summary };
|
|
21843
|
+
}
|
|
21844
|
+
function evaluateFitness(audit) {
|
|
21845
|
+
const passed = audit.score >= MIN_FITNESS_SCORE;
|
|
21846
|
+
const summary = `Score ${String(audit.score)}/100 (minimum: ${String(MIN_FITNESS_SCORE)})`;
|
|
21847
|
+
return { name: "Fitness Audit", passed, summary };
|
|
21848
|
+
}
|
|
21849
|
+
function evaluateConfig(doctor) {
|
|
21850
|
+
const found = doctor.configFile.found;
|
|
21851
|
+
const summary = found ? `Config found: ${doctor.configFile.path ?? "unknown"}` : "No nexus-agents.yaml found (optional)";
|
|
21852
|
+
return { name: "Config", passed: true, summary };
|
|
21853
|
+
}
|
|
21854
|
+
function printReport2(result) {
|
|
21855
|
+
writeLine();
|
|
21856
|
+
writeLine(`${colors.bold}nexus-agents validate${colors.reset}`);
|
|
21857
|
+
writeLine("\u2500".repeat(50));
|
|
21858
|
+
writeLine();
|
|
21859
|
+
for (const phase of result.phases) {
|
|
21860
|
+
const icon = phase.passed ? `${colors.green}${symbols.check}${colors.reset}` : `${colors.red}${symbols.cross}${colors.reset}`;
|
|
21861
|
+
writeLine(` ${icon} ${colors.bold}${phase.name}${colors.reset}: ${phase.summary}`);
|
|
21862
|
+
}
|
|
21863
|
+
writeLine();
|
|
21864
|
+
if (result.allPassed) {
|
|
21865
|
+
writeLine(`${colors.green}${colors.bold}PASS${colors.reset}: All validation checks passed.`);
|
|
21866
|
+
} else {
|
|
21867
|
+
writeLine(
|
|
21868
|
+
`${colors.red}${colors.bold}FAIL${colors.reset}: One or more validation checks failed.`
|
|
21869
|
+
);
|
|
21870
|
+
}
|
|
21871
|
+
writeLine();
|
|
21872
|
+
}
|
|
21873
|
+
async function runValidate() {
|
|
21874
|
+
const doctor = await runDoctor();
|
|
21875
|
+
const audit = calculateFitnessScore(`v${VERSION}`);
|
|
21876
|
+
const phases = [
|
|
21877
|
+
evaluateDoctor(doctor),
|
|
21878
|
+
evaluateFitness(audit),
|
|
21879
|
+
evaluateConfig(doctor)
|
|
21880
|
+
];
|
|
21881
|
+
return {
|
|
21882
|
+
phases,
|
|
21883
|
+
allPassed: phases.every((p) => p.passed)
|
|
21884
|
+
};
|
|
21885
|
+
}
|
|
21886
|
+
async function handleValidateCommand(args) {
|
|
21887
|
+
const result = await runValidate();
|
|
21888
|
+
const isJson = args.options.format === "json";
|
|
21889
|
+
if (isJson) {
|
|
21890
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
21891
|
+
} else {
|
|
21892
|
+
printReport2(result);
|
|
21893
|
+
}
|
|
21894
|
+
process.exit(result.allPassed ? 0 : 1);
|
|
21895
|
+
}
|
|
21896
|
+
|
|
21097
21897
|
// src/cli-commands.ts
|
|
21098
21898
|
function printHelp() {
|
|
21099
21899
|
process.stdout.write(HELP_TEXT + "\n");
|
|
@@ -21175,7 +21975,9 @@ var ASYNC_COMMAND_HANDLERS = {
|
|
|
21175
21975
|
// Issue #748: Memory Benchmark Command
|
|
21176
21976
|
"memory-benchmark": handleMemoryBenchmarkCommand,
|
|
21177
21977
|
// Epic #952: Scenario Command
|
|
21178
|
-
scenario: handleScenarioCommand
|
|
21978
|
+
scenario: handleScenarioCommand,
|
|
21979
|
+
// Issue #1598: Validate Command
|
|
21980
|
+
validate: handleValidateCommand
|
|
21179
21981
|
};
|
|
21180
21982
|
async function handleAsyncCommand(args) {
|
|
21181
21983
|
const handler = ASYNC_COMMAND_HANDLERS[args.command];
|