nexus-agents 2.27.0 → 2.29.0
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/LICENSE +27 -0
- package/README.md +11 -10
- package/dist/chunk-5VZLXMO7.js +838 -0
- package/dist/chunk-5VZLXMO7.js.map +1 -0
- package/dist/chunk-633WH2ML.js +127 -0
- package/dist/chunk-633WH2ML.js.map +1 -0
- package/dist/chunk-7F6HYUIY.js +327 -0
- package/dist/chunk-7F6HYUIY.js.map +1 -0
- package/dist/chunk-CLYZ7FWP.js +30 -0
- package/dist/chunk-CLYZ7FWP.js.map +1 -0
- package/dist/{chunk-WZACXZGT.js → chunk-DAMRMAM2.js} +19543 -35952
- package/dist/chunk-DAMRMAM2.js.map +1 -0
- package/dist/{chunk-VL7L56MU.js → chunk-HH5LVGEE.js} +6 -6
- package/dist/{chunk-6QLUXEA6.js → chunk-HWDBNDUX.js} +2 -2
- package/dist/chunk-I6YDS23R.js +354 -0
- package/dist/chunk-I6YDS23R.js.map +1 -0
- package/dist/{chunk-WF5CWL2N.js → chunk-IMWYKX4H.js} +4377 -4190
- package/dist/chunk-IMWYKX4H.js.map +1 -0
- package/dist/chunk-KGDG6PWZ.js +61 -0
- package/dist/chunk-KGDG6PWZ.js.map +1 -0
- package/dist/chunk-POBO4G2P.js +3700 -0
- package/dist/chunk-POBO4G2P.js.map +1 -0
- package/dist/chunk-S3BKWNST.js +13475 -0
- package/dist/chunk-S3BKWNST.js.map +1 -0
- package/dist/chunk-T7PU3NPQ.js +42 -0
- package/dist/chunk-T7PU3NPQ.js.map +1 -0
- package/dist/{chunk-4LM3YGJF.js → chunk-WSK4VSXP.js} +2 -2
- package/dist/{chunk-RNQRE45M.js → chunk-ZBZJHXRT.js} +181 -1119
- package/dist/chunk-ZBZJHXRT.js.map +1 -0
- package/dist/cli.d.ts +2 -2
- package/dist/cli.js +1410 -659
- package/dist/cli.js.map +1 -1
- package/dist/composite-router-YPRWVTRB.js +17 -0
- package/dist/consensus-vote-DBE6RNZG.js +23 -0
- package/dist/{dist-Y5F6UM2N.js → dist-7PQR2BQB.js} +1584 -1443
- package/dist/dist-7PQR2BQB.js.map +1 -0
- package/dist/{doctor-deep-EN3V4PXO.js → doctor-deep-AWE7SRU6.js} +4 -3
- package/dist/expert-config-FHNBQRX2.js +22 -0
- package/dist/expert-config-FHNBQRX2.js.map +1 -0
- package/dist/factory-O5C7ZBZO.js +13 -0
- package/dist/factory-O5C7ZBZO.js.map +1 -0
- package/dist/factory-PCHGQ3ZG.js +16 -0
- package/dist/factory-PCHGQ3ZG.js.map +1 -0
- package/dist/index.d.ts +3851 -9022
- package/dist/index.js +505 -318
- package/dist/index.js.map +1 -1
- package/dist/learning-persistence-WMWZJZ35.js +16 -0
- package/dist/learning-persistence-WMWZJZ35.js.map +1 -0
- package/dist/mcp-config-AUZQPUBY.js +12 -0
- package/dist/mcp-config-AUZQPUBY.js.map +1 -0
- package/dist/{model-capabilities-types-CSWO27YN.d.ts → model-capabilities-types-B57GZryc.d.ts} +1 -1
- package/dist/routing-memory-QY3XMU2R.js +13 -0
- package/dist/routing-memory-QY3XMU2R.js.map +1 -0
- package/dist/session-memory-3MBCE5KS.js +22 -0
- package/dist/session-memory-3MBCE5KS.js.map +1 -0
- package/dist/{setup-command-ZN7DSP53.js → setup-command-IQ4MD3FT.js} +8 -5
- package/dist/setup-command-IQ4MD3FT.js.map +1 -0
- package/dist/setup-config-5YUPLDXF.js +10 -0
- package/dist/setup-config-5YUPLDXF.js.map +1 -0
- package/dist/weather-report-CC2C4KAX.js +15 -0
- package/dist/weather-report-CC2C4KAX.js.map +1 -0
- package/package.json +33 -36
- package/dist/chunk-RNQRE45M.js.map +0 -1
- package/dist/chunk-WF5CWL2N.js.map +0 -1
- package/dist/chunk-WZACXZGT.js.map +0 -1
- package/dist/dist-Y5F6UM2N.js.map +0 -1
- package/dist/setup-config-ZOJUDXNP.js +0 -9
- /package/dist/{chunk-VL7L56MU.js.map → chunk-HH5LVGEE.js.map} +0 -0
- /package/dist/{chunk-6QLUXEA6.js.map → chunk-HWDBNDUX.js.map} +0 -0
- /package/dist/{chunk-4LM3YGJF.js.map → chunk-WSK4VSXP.js.map} +0 -0
- /package/dist/{doctor-deep-EN3V4PXO.js.map → composite-router-YPRWVTRB.js.map} +0 -0
- /package/dist/{setup-command-ZN7DSP53.js.map → consensus-vote-DBE6RNZG.js.map} +0 -0
- /package/dist/{setup-config-ZOJUDXNP.js.map → doctor-deep-AWE7SRU6.js.map} +0 -0
package/dist/cli.js
CHANGED
|
@@ -1,40 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import "./chunk-HWDBNDUX.js";
|
|
3
|
+
import {
|
|
4
|
+
setupCommandAsync
|
|
5
|
+
} from "./chunk-HH5LVGEE.js";
|
|
6
|
+
import "./chunk-WSK4VSXP.js";
|
|
7
|
+
import {
|
|
8
|
+
calculateConsensus,
|
|
9
|
+
countByCategory,
|
|
10
|
+
countBySeverity,
|
|
11
|
+
createFailedReview,
|
|
12
|
+
determineApproval,
|
|
13
|
+
determineDecision,
|
|
14
|
+
extractSummary,
|
|
15
|
+
formatReviewComment,
|
|
16
|
+
generateSummary,
|
|
17
|
+
parseFindings,
|
|
18
|
+
sumFindings
|
|
19
|
+
} from "./chunk-6E3NMMEY.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
41
|
classifyTrust,
|
|
30
|
-
|
|
42
|
+
createAgentStages,
|
|
31
43
|
createAnnotationsProxy,
|
|
32
44
|
createAuditLogger,
|
|
33
45
|
createCodeExpert,
|
|
34
|
-
createConsensusEngine,
|
|
35
46
|
createDefaultDeps,
|
|
36
47
|
createDefaultPolicyEngine,
|
|
37
|
-
createDefaultPolicyFirewall,
|
|
38
48
|
createEventBusBridge,
|
|
39
49
|
createExecutor,
|
|
40
50
|
createFeedbackIntegration,
|
|
@@ -48,12 +58,13 @@ import {
|
|
|
48
58
|
createRuleBasedPolicy,
|
|
49
59
|
createSecurityExpert,
|
|
50
60
|
createSkillLibrary,
|
|
61
|
+
createTaskTracker,
|
|
51
62
|
createTemplateRegistry,
|
|
52
63
|
createTestingExpert,
|
|
53
64
|
createToolObservabilityProxy,
|
|
54
65
|
createToolRateLimiterFactory,
|
|
55
|
-
createUnifiedRegistry,
|
|
56
66
|
defaultConfig,
|
|
67
|
+
detectBackend,
|
|
57
68
|
discoverDeepMind,
|
|
58
69
|
discoverGitHubRepos,
|
|
59
70
|
discoverGoogleAI,
|
|
@@ -62,8 +73,9 @@ import {
|
|
|
62
73
|
discoverOpenAlex,
|
|
63
74
|
discoverPapersWithCode,
|
|
64
75
|
discoverSemanticScholar,
|
|
65
|
-
|
|
76
|
+
evaluatePolicy2 as evaluatePolicy,
|
|
66
77
|
findOverlaps,
|
|
78
|
+
flushPipelineMemory,
|
|
67
79
|
formatOverlapResult,
|
|
68
80
|
formatResearchIssueBody,
|
|
69
81
|
formatStatusResult,
|
|
@@ -80,7 +92,6 @@ import {
|
|
|
80
92
|
getDefaultTokenPath,
|
|
81
93
|
getEventBusStats,
|
|
82
94
|
getExpertRegistry,
|
|
83
|
-
getPipelineEventBus,
|
|
84
95
|
getPipelinePluginRegistry,
|
|
85
96
|
getResearchStatus,
|
|
86
97
|
getSandboxMode,
|
|
@@ -97,16 +108,18 @@ import {
|
|
|
97
108
|
loadAllExternalPacks,
|
|
98
109
|
loadConfig,
|
|
99
110
|
loadDataset,
|
|
111
|
+
loadPapersRegistry,
|
|
112
|
+
loadSourcesRegistry,
|
|
100
113
|
loadTechniquesRegistry,
|
|
101
114
|
loadWorkflowFile,
|
|
102
115
|
parsePRUrl,
|
|
103
116
|
rankDiscoveredItems,
|
|
104
117
|
readStoredToken,
|
|
105
|
-
registerConsensusVoteTool,
|
|
106
118
|
registerCreateExpertTool,
|
|
107
119
|
registerDelegateToModelTool,
|
|
108
120
|
registerExecuteExpertTool,
|
|
109
121
|
registerExecuteSpecTool,
|
|
122
|
+
registerExtractSymbolsTool,
|
|
110
123
|
registerIssueTriageTool,
|
|
111
124
|
registerListExpertsTool,
|
|
112
125
|
registerListWorkflowsTool,
|
|
@@ -119,6 +132,7 @@ import {
|
|
|
119
132
|
registerRegistryImportTool,
|
|
120
133
|
registerRepoAnalyzeTool,
|
|
121
134
|
registerRepoSecurityPlanTool,
|
|
135
|
+
registerResearchAddSourceTool,
|
|
122
136
|
registerResearchAddTool,
|
|
123
137
|
registerResearchAnalyzeTool,
|
|
124
138
|
registerResearchCatalogReviewTool,
|
|
@@ -128,58 +142,64 @@ import {
|
|
|
128
142
|
registerResources,
|
|
129
143
|
registerRunGraphWorkflowTool,
|
|
130
144
|
registerRunWorkflowTool,
|
|
145
|
+
registerSearchCodebaseTool,
|
|
131
146
|
registerTools,
|
|
132
147
|
registerWeatherReportTool,
|
|
133
|
-
resolveToken,
|
|
134
148
|
resolveV2Config,
|
|
135
149
|
runBenchmarkInstances,
|
|
150
|
+
runDevPipeline,
|
|
136
151
|
sanitizeInput,
|
|
152
|
+
savePapersRegistry,
|
|
153
|
+
saveSourcesRegistry,
|
|
137
154
|
setGlobalToolRateLimiterFactory,
|
|
138
|
-
shutdownToolMemory,
|
|
139
155
|
synthesizeResearch,
|
|
140
156
|
validateArgs,
|
|
141
157
|
validateCommand,
|
|
142
158
|
validateNexusEnv,
|
|
143
|
-
validateTimeout,
|
|
144
159
|
validateWorkflow,
|
|
145
160
|
wrapInMarkdownFence
|
|
146
|
-
} from "./chunk-
|
|
147
|
-
import {
|
|
148
|
-
setupCommandAsync
|
|
149
|
-
} from "./chunk-VL7L56MU.js";
|
|
161
|
+
} from "./chunk-DAMRMAM2.js";
|
|
150
162
|
import {
|
|
151
163
|
VERSION,
|
|
152
|
-
capitalize,
|
|
153
|
-
capitalizeKebab,
|
|
154
164
|
closeServer,
|
|
155
165
|
connectTransport,
|
|
156
|
-
createAllAdapters,
|
|
157
166
|
createServer,
|
|
158
167
|
doctorCommand,
|
|
159
|
-
getAvailableClis,
|
|
160
168
|
initDataDirectories,
|
|
161
|
-
|
|
169
|
+
runDoctor
|
|
170
|
+
} from "./chunk-5VZLXMO7.js";
|
|
171
|
+
import {
|
|
172
|
+
DEFAULT_VOTE_TIMEOUT_MS,
|
|
173
|
+
MemoryError,
|
|
174
|
+
THRESHOLD_MAP,
|
|
175
|
+
VOTER_ROLES,
|
|
176
|
+
collectRealVotes,
|
|
177
|
+
createConsensusEngine,
|
|
178
|
+
createDefaultPolicyFirewall,
|
|
179
|
+
createUnifiedRegistry,
|
|
180
|
+
registerConsensusVoteTool,
|
|
181
|
+
shutdownToolMemory,
|
|
182
|
+
validateTimeout
|
|
183
|
+
} from "./chunk-S3BKWNST.js";
|
|
184
|
+
import {
|
|
185
|
+
capitalize,
|
|
186
|
+
capitalizeKebab,
|
|
162
187
|
truncateSentence
|
|
163
|
-
} from "./chunk-
|
|
164
|
-
import "./chunk-
|
|
188
|
+
} from "./chunk-633WH2ML.js";
|
|
189
|
+
import "./chunk-I6YDS23R.js";
|
|
190
|
+
import "./chunk-KGDG6PWZ.js";
|
|
165
191
|
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";
|
|
192
|
+
resolveToken
|
|
193
|
+
} from "./chunk-7F6HYUIY.js";
|
|
178
194
|
import {
|
|
179
195
|
CATEGORY_DISPLAY_NAMES,
|
|
180
196
|
DEFAULT_PR_REVIEW_CONFIG
|
|
181
197
|
} from "./chunk-X2M7OF27.js";
|
|
182
|
-
import
|
|
198
|
+
import {
|
|
199
|
+
createAllAdapters,
|
|
200
|
+
getAvailableClis,
|
|
201
|
+
isRecord
|
|
202
|
+
} from "./chunk-ZBZJHXRT.js";
|
|
183
203
|
import {
|
|
184
204
|
API_TIMEOUTS,
|
|
185
205
|
AgentCapability,
|
|
@@ -224,6 +244,7 @@ import {
|
|
|
224
244
|
getErrorMessage,
|
|
225
245
|
getModelCapabilities,
|
|
226
246
|
getOutcomeStore,
|
|
247
|
+
getPipelineEventBus,
|
|
227
248
|
getRandomProvider,
|
|
228
249
|
getTimeProvider,
|
|
229
250
|
logger,
|
|
@@ -239,7 +260,11 @@ import {
|
|
|
239
260
|
toError,
|
|
240
261
|
writeEmptyLine,
|
|
241
262
|
writeLine
|
|
242
|
-
} from "./chunk-
|
|
263
|
+
} from "./chunk-IMWYKX4H.js";
|
|
264
|
+
import "./chunk-CLYZ7FWP.js";
|
|
265
|
+
import {
|
|
266
|
+
BUILT_IN_EXPERTS
|
|
267
|
+
} from "./chunk-POBO4G2P.js";
|
|
243
268
|
import "./chunk-UP2VWCW5.js";
|
|
244
269
|
|
|
245
270
|
// src/cli.ts
|
|
@@ -423,8 +448,8 @@ function resolveOutputPath(output2) {
|
|
|
423
448
|
}
|
|
424
449
|
return resolve(process.cwd(), DEFAULT_CONFIG_FILE);
|
|
425
450
|
}
|
|
426
|
-
function fileExists(
|
|
427
|
-
return existsSync(
|
|
451
|
+
function fileExists(path23) {
|
|
452
|
+
return existsSync(path23);
|
|
428
453
|
}
|
|
429
454
|
async function ensureDirectory(filePath) {
|
|
430
455
|
const dir = dirname(filePath);
|
|
@@ -432,9 +457,9 @@ async function ensureDirectory(filePath) {
|
|
|
432
457
|
await mkdir(dir, { recursive: true });
|
|
433
458
|
}
|
|
434
459
|
}
|
|
435
|
-
async function writeConfigFile(
|
|
436
|
-
await ensureDirectory(
|
|
437
|
-
await writeFile(
|
|
460
|
+
async function writeConfigFile(path23) {
|
|
461
|
+
await ensureDirectory(path23);
|
|
462
|
+
await writeFile(path23, CONFIG_TEMPLATE, "utf-8");
|
|
438
463
|
}
|
|
439
464
|
async function runConfigInit(options = {}) {
|
|
440
465
|
const outputPath = resolveOutputPath(options.output);
|
|
@@ -993,14 +1018,14 @@ Hint: Ensure the file exists and the path is correct relative to ${cwd}`
|
|
|
993
1018
|
try {
|
|
994
1019
|
return JSON.parse(content);
|
|
995
1020
|
} catch (e) {
|
|
996
|
-
const msg =
|
|
1021
|
+
const msg = getErrorMessage(e);
|
|
997
1022
|
throw new Error(`Invalid JSON in input file "${inputArg}": ${msg}`);
|
|
998
1023
|
}
|
|
999
1024
|
}
|
|
1000
1025
|
try {
|
|
1001
1026
|
return JSON.parse(inputArg);
|
|
1002
1027
|
} catch (e) {
|
|
1003
|
-
const msg =
|
|
1028
|
+
const msg = getErrorMessage(e);
|
|
1004
1029
|
throw new Error(
|
|
1005
1030
|
`Invalid JSON input: ${msg}
|
|
1006
1031
|
Hint: Provide a valid JSON string or path to a .json file.`
|
|
@@ -1403,14 +1428,14 @@ ${colors.dim}Session ended.${colors.reset}
|
|
|
1403
1428
|
});
|
|
1404
1429
|
});
|
|
1405
1430
|
rl.prompt();
|
|
1406
|
-
return new Promise((
|
|
1431
|
+
return new Promise((resolve14) => {
|
|
1407
1432
|
rl.on("line", (line) => {
|
|
1408
1433
|
void (async () => {
|
|
1409
1434
|
try {
|
|
1410
1435
|
const shouldExit = await processLine(line, session, logger17);
|
|
1411
1436
|
if (shouldExit) {
|
|
1412
1437
|
rl.close();
|
|
1413
|
-
|
|
1438
|
+
resolve14();
|
|
1414
1439
|
return;
|
|
1415
1440
|
}
|
|
1416
1441
|
} catch (error) {
|
|
@@ -1422,7 +1447,7 @@ ${colors.dim}Session ended.${colors.reset}
|
|
|
1422
1447
|
})();
|
|
1423
1448
|
});
|
|
1424
1449
|
rl.on("close", () => {
|
|
1425
|
-
|
|
1450
|
+
resolve14();
|
|
1426
1451
|
});
|
|
1427
1452
|
});
|
|
1428
1453
|
}
|
|
@@ -2416,9 +2441,9 @@ async function evaluateDirectory(target, timeoutMs) {
|
|
|
2416
2441
|
}
|
|
2417
2442
|
const aggregator = createAggregator();
|
|
2418
2443
|
const results = [];
|
|
2419
|
-
for (const [
|
|
2444
|
+
for (const [path23, evaluations] of evaluationsByComponent) {
|
|
2420
2445
|
if (evaluations.length > 0) {
|
|
2421
|
-
results.push(aggregator.aggregate(
|
|
2446
|
+
results.push(aggregator.aggregate(path23, evaluations));
|
|
2422
2447
|
}
|
|
2423
2448
|
}
|
|
2424
2449
|
const priority = { deprecate: 0, refactor: 1, review: 2, retain: 3 };
|
|
@@ -3930,27 +3955,27 @@ var CliAdapterAgent = class {
|
|
|
3930
3955
|
};
|
|
3931
3956
|
|
|
3932
3957
|
// src/cli/orchestrate-puppeteer.ts
|
|
3933
|
-
function loadPolicyParameters(
|
|
3958
|
+
function loadPolicyParameters(path23, logger17) {
|
|
3934
3959
|
try {
|
|
3935
|
-
if (fs2.existsSync(
|
|
3936
|
-
const content = fs2.readFileSync(
|
|
3960
|
+
if (fs2.existsSync(path23)) {
|
|
3961
|
+
const content = fs2.readFileSync(path23, "utf-8");
|
|
3937
3962
|
const params = JSON.parse(content);
|
|
3938
|
-
logger17.info("Loaded policy parameters", { path:
|
|
3963
|
+
logger17.info("Loaded policy parameters", { path: path23 });
|
|
3939
3964
|
return params;
|
|
3940
3965
|
}
|
|
3941
3966
|
} catch (error) {
|
|
3942
3967
|
const message = getErrorMessage(error);
|
|
3943
|
-
logger17.warn("Failed to load policy parameters", { path:
|
|
3968
|
+
logger17.warn("Failed to load policy parameters", { path: path23, error: message });
|
|
3944
3969
|
}
|
|
3945
3970
|
return void 0;
|
|
3946
3971
|
}
|
|
3947
|
-
function savePolicyParameters(
|
|
3972
|
+
function savePolicyParameters(path23, params, logger17) {
|
|
3948
3973
|
try {
|
|
3949
|
-
fs2.writeFileSync(
|
|
3950
|
-
logger17.info("Saved policy parameters", { path:
|
|
3974
|
+
fs2.writeFileSync(path23, JSON.stringify(params, null, 2));
|
|
3975
|
+
logger17.info("Saved policy parameters", { path: path23 });
|
|
3951
3976
|
} catch (error) {
|
|
3952
3977
|
const message = getErrorMessage(error);
|
|
3953
|
-
logger17.warn("Failed to save policy parameters", { path:
|
|
3978
|
+
logger17.warn("Failed to save policy parameters", { path: path23, error: message });
|
|
3954
3979
|
}
|
|
3955
3980
|
}
|
|
3956
3981
|
function createAgentsFromAdapters(adapters) {
|
|
@@ -6754,15 +6779,7 @@ import * as yaml3 from "yaml";
|
|
|
6754
6779
|
|
|
6755
6780
|
// src/research/research-schemas.ts
|
|
6756
6781
|
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
|
-
]);
|
|
6782
|
+
var ResearchTopicSchema = z2.string().min(1);
|
|
6766
6783
|
var RESEARCH_TOPICS = [
|
|
6767
6784
|
"consensus",
|
|
6768
6785
|
"routing",
|
|
@@ -6770,16 +6787,26 @@ var RESEARCH_TOPICS = [
|
|
|
6770
6787
|
"code-generation",
|
|
6771
6788
|
"cli-tools",
|
|
6772
6789
|
"orchestration",
|
|
6773
|
-
"security"
|
|
6790
|
+
"security",
|
|
6791
|
+
"evaluation",
|
|
6792
|
+
"safety",
|
|
6793
|
+
"planning",
|
|
6794
|
+
"tool-use",
|
|
6795
|
+
"reasoning"
|
|
6774
6796
|
];
|
|
6775
6797
|
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: "
|
|
6798
|
+
consensus: "Multi-agent decision protocols and voting",
|
|
6799
|
+
routing: "Cost-efficient model routing and selection",
|
|
6800
|
+
memory: "Context, long-term memory, and compression",
|
|
6801
|
+
"code-generation": "Code generation, repair, and self-improvement",
|
|
6802
|
+
"cli-tools": "External CLI integration and protocols",
|
|
6803
|
+
orchestration: "Multi-agent coordination and workflows",
|
|
6804
|
+
security: "Security analysis, prompt injection defense",
|
|
6805
|
+
evaluation: "Benchmarks, metrics, and testing methodologies",
|
|
6806
|
+
safety: "AI safety, alignment, and reward hacking",
|
|
6807
|
+
planning: "Task planning, decomposition, and reasoning chains",
|
|
6808
|
+
"tool-use": "Tool augmentation, function calling, and MCP",
|
|
6809
|
+
reasoning: "Reasoning, self-reflection, and search strategies"
|
|
6783
6810
|
};
|
|
6784
6811
|
var TechniqueStatusSchema = z2.enum([
|
|
6785
6812
|
"implemented",
|
|
@@ -6814,6 +6841,7 @@ var ResearchPaperSchema = z2.object({
|
|
|
6814
6841
|
/** arXiv ID (e.g., '2501.06322') */
|
|
6815
6842
|
arxiv_id: z2.string().optional(),
|
|
6816
6843
|
/** URL to the paper */
|
|
6844
|
+
// eslint-disable-next-line @typescript-eslint/no-deprecated -- z.url() fails at runtime despite being recommended
|
|
6817
6845
|
url: z2.string().url().optional(),
|
|
6818
6846
|
/** Publication date (YYYY-MM format) */
|
|
6819
6847
|
publication_date: z2.string().optional(),
|
|
@@ -6823,7 +6851,7 @@ var ResearchPaperSchema = z2.object({
|
|
|
6823
6851
|
* Research topics (array).
|
|
6824
6852
|
* First topic is considered primary for counting purposes.
|
|
6825
6853
|
*/
|
|
6826
|
-
topics: z2.array(
|
|
6854
|
+
topics: z2.array(z2.string()).optional().default([]),
|
|
6827
6855
|
/** Tags for searching */
|
|
6828
6856
|
tags: z2.array(z2.string()).optional().default([]),
|
|
6829
6857
|
/** Date when the paper was reviewed (YYYY-MM-DD) */
|
|
@@ -6841,7 +6869,28 @@ var ResearchPaperSchema = z2.object({
|
|
|
6841
6869
|
/** Related GitHub issues */
|
|
6842
6870
|
related_issues: z2.array(z2.number().int().positive()).optional().default([]),
|
|
6843
6871
|
/** Implementation status */
|
|
6844
|
-
implementation_status: PaperStatusSchema.optional().default("not-started")
|
|
6872
|
+
implementation_status: PaperStatusSchema.optional().default("not-started"),
|
|
6873
|
+
// ── Quality Assessment (Issue #1571) ──────────────────────────
|
|
6874
|
+
/** Citation count from Semantic Scholar (auto-fetched) */
|
|
6875
|
+
citation_count: z2.number().nonnegative().optional(),
|
|
6876
|
+
/** Venue quality tier: 3=top (NeurIPS/ICML/ICLR), 2=good, 1=workshop, 0=preprint */
|
|
6877
|
+
venue_tier: z2.number().min(0).max(3).optional(),
|
|
6878
|
+
/** Whether the paper links to a code repository */
|
|
6879
|
+
has_code: z2.boolean().optional(),
|
|
6880
|
+
/** Code repository URL (from Papers With Code or manual) */
|
|
6881
|
+
code_url: z2.string().optional(),
|
|
6882
|
+
/** Rigor assessment tags (factual, verifiable signals) */
|
|
6883
|
+
rigor_tags: z2.array(
|
|
6884
|
+
z2.enum(["has-code", "has-dataset", "has-baselines", "peer-reviewed", "single-model-eval"])
|
|
6885
|
+
).optional().default([]),
|
|
6886
|
+
/** Composite quality score (0-10, auto-computed from signals) */
|
|
6887
|
+
quality_score: z2.number().min(0).max(10).optional(),
|
|
6888
|
+
/** Evidence confidence tier for extracted techniques */
|
|
6889
|
+
evidence_tier: z2.enum(["high", "medium", "low"]).optional(),
|
|
6890
|
+
/** Why this paper scored low — enables future re-review when conditions change */
|
|
6891
|
+
quality_notes: z2.string().optional(),
|
|
6892
|
+
/** When quality was last assessed (ISO date) — enables periodic re-review */
|
|
6893
|
+
last_quality_check: z2.string().optional()
|
|
6845
6894
|
});
|
|
6846
6895
|
var IntegrationFileObjectSchema = z2.object({
|
|
6847
6896
|
path: z2.string(),
|
|
@@ -6861,7 +6910,7 @@ var ResearchTechniqueSchema = z2.object({
|
|
|
6861
6910
|
/** Tags for searching */
|
|
6862
6911
|
tags: z2.array(z2.string()).optional().default([]),
|
|
6863
6912
|
/** Metrics associated with this technique */
|
|
6864
|
-
metrics: z2.record(z2.string()).optional().default({}),
|
|
6913
|
+
metrics: z2.record(z2.string(), z2.string()).optional().default({}),
|
|
6865
6914
|
/** Implementation status (required) */
|
|
6866
6915
|
status: TechniqueStatusSchema,
|
|
6867
6916
|
/** Priority level */
|
|
@@ -6879,15 +6928,25 @@ var ResearchTechniqueSchema = z2.object({
|
|
|
6879
6928
|
/** Dependent techniques */
|
|
6880
6929
|
dependencies: z2.array(z2.string()).optional().default([]),
|
|
6881
6930
|
/** Decision history */
|
|
6882
|
-
decision_history: z2.array(DecisionHistoryEntrySchema).optional().default([])
|
|
6931
|
+
decision_history: z2.array(DecisionHistoryEntrySchema).optional().default([]),
|
|
6932
|
+
// ── Evaluation Plan (Issue #1576 Wave 6) ──────────────────────
|
|
6933
|
+
/** Structured evaluation plan for keeping or removing the technique */
|
|
6934
|
+
evaluation_plan: z2.object({
|
|
6935
|
+
success_criteria: z2.string().optional(),
|
|
6936
|
+
evaluation_method: z2.string().optional(),
|
|
6937
|
+
evaluation_deadline: z2.string().optional(),
|
|
6938
|
+
removal_criteria: z2.string().optional()
|
|
6939
|
+
}).optional(),
|
|
6940
|
+
/** Evaluation status: pending (not yet evaluated), passed, failed, deferred */
|
|
6941
|
+
evaluation_status: z2.enum(["pending", "passed", "failed", "deferred"]).optional()
|
|
6883
6942
|
});
|
|
6884
6943
|
var PapersRegistrySchema = z2.object({
|
|
6885
6944
|
schema_version: z2.string(),
|
|
6886
|
-
papers: z2.record(ResearchPaperSchema)
|
|
6945
|
+
papers: z2.record(z2.string(), ResearchPaperSchema)
|
|
6887
6946
|
});
|
|
6888
6947
|
var TechniquesRegistrySchema = z2.object({
|
|
6889
6948
|
schema_version: z2.string(),
|
|
6890
|
-
techniques: z2.record(ResearchTechniqueSchema)
|
|
6949
|
+
techniques: z2.record(z2.string(), ResearchTechniqueSchema)
|
|
6891
6950
|
});
|
|
6892
6951
|
function getIntegrationFilePath(file) {
|
|
6893
6952
|
if (typeof file === "string") {
|
|
@@ -6976,7 +7035,7 @@ function generateTopicsTable(stats) {
|
|
|
6976
7035
|
const topicDisplay = capitalizeKebab(ts.topic);
|
|
6977
7036
|
const topicLink = `[${topicDisplay}](topics/${ts.topic}/README.md)`;
|
|
6978
7037
|
const desc = TOPIC_DESCRIPTIONS[ts.topic];
|
|
6979
|
-
return `| ${topicLink} | ${String(ts.papers)} | ${String(ts.techniques)} | ${desc} |`;
|
|
7038
|
+
return `| ${topicLink} | ${String(ts.papers)} | ${String(ts.techniques)} | ${desc ?? ""} |`;
|
|
6980
7039
|
});
|
|
6981
7040
|
return `## Topics
|
|
6982
7041
|
|
|
@@ -7810,6 +7869,267 @@ async function researchIndexCommand(options) {
|
|
|
7810
7869
|
}
|
|
7811
7870
|
}
|
|
7812
7871
|
|
|
7872
|
+
// src/cli/research-import-command.ts
|
|
7873
|
+
import * as fs9 from "fs/promises";
|
|
7874
|
+
import { resolve as resolve5 } from "path";
|
|
7875
|
+
var VALID_TYPES = ["paper", "repo", "tool", "blog"];
|
|
7876
|
+
function parseCsvLine(line) {
|
|
7877
|
+
const fields = [];
|
|
7878
|
+
let current = "";
|
|
7879
|
+
let inQuotes = false;
|
|
7880
|
+
for (let i = 0; i < line.length; i++) {
|
|
7881
|
+
const ch = line[i];
|
|
7882
|
+
if (inQuotes) {
|
|
7883
|
+
if (ch === '"') {
|
|
7884
|
+
const next = line[i + 1];
|
|
7885
|
+
if (next === '"') {
|
|
7886
|
+
current += '"';
|
|
7887
|
+
i++;
|
|
7888
|
+
} else {
|
|
7889
|
+
inQuotes = false;
|
|
7890
|
+
}
|
|
7891
|
+
} else {
|
|
7892
|
+
current += ch;
|
|
7893
|
+
}
|
|
7894
|
+
} else if (ch === '"') {
|
|
7895
|
+
inQuotes = true;
|
|
7896
|
+
} else if (ch === ",") {
|
|
7897
|
+
fields.push(current.trim());
|
|
7898
|
+
current = "";
|
|
7899
|
+
} else {
|
|
7900
|
+
current += ch;
|
|
7901
|
+
}
|
|
7902
|
+
}
|
|
7903
|
+
fields.push(current.trim());
|
|
7904
|
+
return fields;
|
|
7905
|
+
}
|
|
7906
|
+
function isValidUrl(value) {
|
|
7907
|
+
return /^https?:\/\/.+/.test(value);
|
|
7908
|
+
}
|
|
7909
|
+
function isValidType(value) {
|
|
7910
|
+
return VALID_TYPES.includes(value);
|
|
7911
|
+
}
|
|
7912
|
+
function validateRow(fields, colIndex, rowNum) {
|
|
7913
|
+
const title = fields[colIndex["title"]] ?? "";
|
|
7914
|
+
const url = fields[colIndex["url"]] ?? "";
|
|
7915
|
+
const rawType = (fields[colIndex["type"]] ?? "").toLowerCase();
|
|
7916
|
+
const topic = fields[colIndex["topic"]] ?? "";
|
|
7917
|
+
const description = fields[colIndex["description"]] ?? "";
|
|
7918
|
+
if (title === "") return `Row ${String(rowNum)}: missing title`;
|
|
7919
|
+
if (!isValidUrl(url)) return `Row ${String(rowNum)}: invalid URL "${url}"`;
|
|
7920
|
+
if (!isValidType(rawType)) {
|
|
7921
|
+
return `Row ${String(rowNum)}: invalid type "${rawType}" (expected: ${VALID_TYPES.join(", ")})`;
|
|
7922
|
+
}
|
|
7923
|
+
if (topic === "") return `Row ${String(rowNum)}: missing topic`;
|
|
7924
|
+
return { title, url, type: rawType, topic, description };
|
|
7925
|
+
}
|
|
7926
|
+
function parseHeader(headerLine) {
|
|
7927
|
+
const header = parseCsvLine(headerLine).map((h) => h.toLowerCase());
|
|
7928
|
+
const required = ["title", "url", "type", "topic", "description"];
|
|
7929
|
+
const missing = required.filter((col) => !header.includes(col));
|
|
7930
|
+
if (missing.length > 0) {
|
|
7931
|
+
return { ok: false, error: { message: `Missing CSV columns: ${missing.join(", ")}` } };
|
|
7932
|
+
}
|
|
7933
|
+
const colIndex = Object.fromEntries(required.map((col) => [col, header.indexOf(col)]));
|
|
7934
|
+
return { ok: true, value: colIndex };
|
|
7935
|
+
}
|
|
7936
|
+
function parseCsvContent(content) {
|
|
7937
|
+
const lines = content.split(/\r?\n/).filter((l) => l.trim() !== "");
|
|
7938
|
+
if (lines.length < 2) {
|
|
7939
|
+
return {
|
|
7940
|
+
ok: false,
|
|
7941
|
+
error: { message: "CSV must have a header row and at least one data row" }
|
|
7942
|
+
};
|
|
7943
|
+
}
|
|
7944
|
+
const headerResult = parseHeader(lines[0]);
|
|
7945
|
+
if (!headerResult.ok) return headerResult;
|
|
7946
|
+
const rows = [];
|
|
7947
|
+
const errors = [];
|
|
7948
|
+
for (let i = 1; i < lines.length; i++) {
|
|
7949
|
+
const fields = parseCsvLine(lines[i]);
|
|
7950
|
+
const result = validateRow(fields, headerResult.value, i + 1);
|
|
7951
|
+
if (typeof result === "string") {
|
|
7952
|
+
errors.push(result);
|
|
7953
|
+
} else {
|
|
7954
|
+
rows.push(result);
|
|
7955
|
+
}
|
|
7956
|
+
}
|
|
7957
|
+
if (errors.length > 0 && rows.length === 0) {
|
|
7958
|
+
return { ok: false, error: { message: errors.join("\n") } };
|
|
7959
|
+
}
|
|
7960
|
+
return { ok: true, value: rows };
|
|
7961
|
+
}
|
|
7962
|
+
function generateIdFromTitle(title) {
|
|
7963
|
+
return title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "").slice(0, 60);
|
|
7964
|
+
}
|
|
7965
|
+
function mapToSourceType(csvType) {
|
|
7966
|
+
switch (csvType) {
|
|
7967
|
+
case "repo":
|
|
7968
|
+
return "open_source_repo";
|
|
7969
|
+
case "tool":
|
|
7970
|
+
return "code_analysis";
|
|
7971
|
+
case "blog":
|
|
7972
|
+
return "research_blog";
|
|
7973
|
+
default:
|
|
7974
|
+
return "code_analysis";
|
|
7975
|
+
}
|
|
7976
|
+
}
|
|
7977
|
+
async function executeImport(options) {
|
|
7978
|
+
const csvPath = resolve5(options.csvPath);
|
|
7979
|
+
let content;
|
|
7980
|
+
try {
|
|
7981
|
+
content = await fs9.readFile(csvPath, "utf-8");
|
|
7982
|
+
} catch {
|
|
7983
|
+
return buildResult(
|
|
7984
|
+
[],
|
|
7985
|
+
[{ row: 0, title: "", status: "error", reason: `Cannot read file: ${csvPath}` }]
|
|
7986
|
+
);
|
|
7987
|
+
}
|
|
7988
|
+
const parseResult = parseCsvContent(content);
|
|
7989
|
+
if (!parseResult.ok) {
|
|
7990
|
+
return buildResult(
|
|
7991
|
+
[],
|
|
7992
|
+
[{ row: 0, title: "", status: "error", reason: parseResult.error.message }]
|
|
7993
|
+
);
|
|
7994
|
+
}
|
|
7995
|
+
const rows = parseResult.value;
|
|
7996
|
+
const outcomes = [];
|
|
7997
|
+
const rootDir = options.rootDir;
|
|
7998
|
+
const paperRows = rows.filter((r) => r.type === "paper");
|
|
7999
|
+
const sourceRows = rows.filter((r) => r.type !== "paper");
|
|
8000
|
+
if (paperRows.length > 0) {
|
|
8001
|
+
await processPaperRows(paperRows, outcomes, options.dryRun === true, rootDir);
|
|
8002
|
+
}
|
|
8003
|
+
if (sourceRows.length > 0) {
|
|
8004
|
+
await processSourceRows(sourceRows, outcomes, options.dryRun === true, rootDir);
|
|
8005
|
+
}
|
|
8006
|
+
return buildResult(rows, outcomes);
|
|
8007
|
+
}
|
|
8008
|
+
function buildPaperEntry(row) {
|
|
8009
|
+
return {
|
|
8010
|
+
title: row.title,
|
|
8011
|
+
url: row.url,
|
|
8012
|
+
source: "preprint",
|
|
8013
|
+
arxiv_id: "",
|
|
8014
|
+
publication_date: "",
|
|
8015
|
+
venue: null,
|
|
8016
|
+
reviewed_date: "",
|
|
8017
|
+
reviewed_in: "",
|
|
8018
|
+
topics: [row.topic],
|
|
8019
|
+
summary: row.description,
|
|
8020
|
+
relevance: "medium",
|
|
8021
|
+
implementation_status: "not-started",
|
|
8022
|
+
tags: [],
|
|
8023
|
+
authors: [],
|
|
8024
|
+
key_findings: [],
|
|
8025
|
+
techniques_extracted: [],
|
|
8026
|
+
related_issues: []
|
|
8027
|
+
};
|
|
8028
|
+
}
|
|
8029
|
+
function collectNewPapers(rows, existingUrls, existingTitles, outcomes) {
|
|
8030
|
+
const newPapers = {};
|
|
8031
|
+
for (const row of rows) {
|
|
8032
|
+
if (existingUrls.has(row.url) || existingTitles.has(row.title.toLowerCase())) {
|
|
8033
|
+
outcomes.push({ row: 0, title: row.title, status: "skipped", reason: "Already exists" });
|
|
8034
|
+
continue;
|
|
8035
|
+
}
|
|
8036
|
+
newPapers[generateIdFromTitle(row.title)] = buildPaperEntry(row);
|
|
8037
|
+
existingTitles.add(row.title.toLowerCase());
|
|
8038
|
+
outcomes.push({ row: 0, title: row.title, status: "added" });
|
|
8039
|
+
}
|
|
8040
|
+
return newPapers;
|
|
8041
|
+
}
|
|
8042
|
+
async function processPaperRows(rows, outcomes, dryRun, rootDir) {
|
|
8043
|
+
const loadResult = await loadPapersRegistry(rootDir);
|
|
8044
|
+
if (!loadResult.ok) {
|
|
8045
|
+
for (const row of rows) {
|
|
8046
|
+
outcomes.push({
|
|
8047
|
+
row: 0,
|
|
8048
|
+
title: row.title,
|
|
8049
|
+
status: "error",
|
|
8050
|
+
reason: "Failed to load papers registry"
|
|
8051
|
+
});
|
|
8052
|
+
}
|
|
8053
|
+
return;
|
|
8054
|
+
}
|
|
8055
|
+
const registry = loadResult.value;
|
|
8056
|
+
const existingUrls = new Set(
|
|
8057
|
+
Object.values(registry.papers).map((p) => p.url).filter(Boolean)
|
|
8058
|
+
);
|
|
8059
|
+
const existingTitles = new Set(Object.values(registry.papers).map((p) => p.title.toLowerCase()));
|
|
8060
|
+
const newPapers = collectNewPapers(rows, existingUrls, existingTitles, outcomes);
|
|
8061
|
+
if (!dryRun && Object.keys(newPapers).length > 0) {
|
|
8062
|
+
const updated = { ...registry, papers: { ...registry.papers, ...newPapers } };
|
|
8063
|
+
await savePapersRegistry(updated, rootDir);
|
|
8064
|
+
}
|
|
8065
|
+
}
|
|
8066
|
+
async function processSourceRows(rows, outcomes, dryRun, rootDir) {
|
|
8067
|
+
const loadResult = await loadSourcesRegistry(rootDir);
|
|
8068
|
+
if (!loadResult.ok) {
|
|
8069
|
+
for (const row of rows) {
|
|
8070
|
+
outcomes.push({
|
|
8071
|
+
row: 0,
|
|
8072
|
+
title: row.title,
|
|
8073
|
+
status: "error",
|
|
8074
|
+
reason: "Failed to load sources registry"
|
|
8075
|
+
});
|
|
8076
|
+
}
|
|
8077
|
+
return;
|
|
8078
|
+
}
|
|
8079
|
+
const registry = loadResult.value;
|
|
8080
|
+
const existingUrls = new Set(Object.values(registry.sources).map((s) => s.url));
|
|
8081
|
+
const newSources = {};
|
|
8082
|
+
for (const row of rows) {
|
|
8083
|
+
if (existingUrls.has(row.url)) {
|
|
8084
|
+
outcomes.push({ row: 0, title: row.title, status: "skipped", reason: "Already exists" });
|
|
8085
|
+
continue;
|
|
8086
|
+
}
|
|
8087
|
+
const sourceId = generateIdFromTitle(row.title);
|
|
8088
|
+
const today = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
8089
|
+
newSources[sourceId] = {
|
|
8090
|
+
name: row.title,
|
|
8091
|
+
type: mapToSourceType(row.type),
|
|
8092
|
+
url: row.url,
|
|
8093
|
+
topics: [row.topic],
|
|
8094
|
+
reviewed_date: today
|
|
8095
|
+
};
|
|
8096
|
+
existingUrls.add(row.url);
|
|
8097
|
+
outcomes.push({ row: 0, title: row.title, status: "added" });
|
|
8098
|
+
}
|
|
8099
|
+
if (!dryRun && Object.keys(newSources).length > 0) {
|
|
8100
|
+
const updated = {
|
|
8101
|
+
...registry,
|
|
8102
|
+
sources: { ...registry.sources, ...newSources }
|
|
8103
|
+
};
|
|
8104
|
+
await saveSourcesRegistry(updated, rootDir);
|
|
8105
|
+
}
|
|
8106
|
+
}
|
|
8107
|
+
function buildResult(_rows, outcomes) {
|
|
8108
|
+
const added = outcomes.filter((o) => o.status === "added").length;
|
|
8109
|
+
const skipped = outcomes.filter((o) => o.status === "skipped").length;
|
|
8110
|
+
const errors = outcomes.filter((o) => o.status === "error").length;
|
|
8111
|
+
const lines = [
|
|
8112
|
+
`Import complete: ${String(added)} added, ${String(skipped)} skipped, ${String(errors)} errors`
|
|
8113
|
+
];
|
|
8114
|
+
for (const o of outcomes) {
|
|
8115
|
+
const suffix = o.reason !== void 0 ? ` (${o.reason})` : "";
|
|
8116
|
+
lines.push(` [${o.status}] ${o.title}${suffix}`);
|
|
8117
|
+
}
|
|
8118
|
+
return { added, skipped, errors, outcomes, message: lines.join("\n") };
|
|
8119
|
+
}
|
|
8120
|
+
async function handleImportCommand(args, options) {
|
|
8121
|
+
const csvPath = args[0];
|
|
8122
|
+
if (csvPath === void 0 || csvPath === "") {
|
|
8123
|
+
return "Error: CSV file path is required. Usage: nexus-agents research import <csv-file> [--dryRun]";
|
|
8124
|
+
}
|
|
8125
|
+
const dryRun = options["dryRun"] === true;
|
|
8126
|
+
const result = await executeImport({ csvPath, dryRun });
|
|
8127
|
+
if (dryRun) {
|
|
8128
|
+
return `[DRY RUN] ${result.message}`;
|
|
8129
|
+
}
|
|
8130
|
+
return result.message;
|
|
8131
|
+
}
|
|
8132
|
+
|
|
7813
8133
|
// src/cli/research-command.ts
|
|
7814
8134
|
function optString(options, key) {
|
|
7815
8135
|
const val = options[key];
|
|
@@ -8023,7 +8343,8 @@ var VALID_SUBCOMMANDS = [
|
|
|
8023
8343
|
"discover",
|
|
8024
8344
|
"review",
|
|
8025
8345
|
"prioritize",
|
|
8026
|
-
"synthesize"
|
|
8346
|
+
"synthesize",
|
|
8347
|
+
"import"
|
|
8027
8348
|
];
|
|
8028
8349
|
function isValidResearchSubcommand(value) {
|
|
8029
8350
|
return value !== void 0 && VALID_SUBCOMMANDS.includes(value);
|
|
@@ -8044,7 +8365,8 @@ var SUBCOMMAND_HANDLERS = {
|
|
|
8044
8365
|
discover: handleDiscoverCommand,
|
|
8045
8366
|
review: handleReviewCommand,
|
|
8046
8367
|
prioritize: handlePrioritizeCommand,
|
|
8047
|
-
synthesize: handleSynthesizeCommand
|
|
8368
|
+
synthesize: handleSynthesizeCommand,
|
|
8369
|
+
import: handleImportCommand
|
|
8048
8370
|
};
|
|
8049
8371
|
async function researchCommand(subcommand, args, options) {
|
|
8050
8372
|
const handler = SUBCOMMAND_HANDLERS[subcommand];
|
|
@@ -8055,8 +8377,8 @@ async function researchCommand(subcommand, args, options) {
|
|
|
8055
8377
|
}
|
|
8056
8378
|
|
|
8057
8379
|
// src/cli/index-command.ts
|
|
8058
|
-
import * as
|
|
8059
|
-
import * as
|
|
8380
|
+
import * as fs13 from "fs/promises";
|
|
8381
|
+
import * as path17 from "path";
|
|
8060
8382
|
import * as yaml6 from "yaml";
|
|
8061
8383
|
|
|
8062
8384
|
// src/indexer/types.ts
|
|
@@ -8558,7 +8880,7 @@ function buildIndex(files) {
|
|
|
8558
8880
|
}
|
|
8559
8881
|
|
|
8560
8882
|
// src/indexer/generator.ts
|
|
8561
|
-
import * as
|
|
8883
|
+
import * as fs10 from "fs/promises";
|
|
8562
8884
|
import * as path11 from "path";
|
|
8563
8885
|
import * as yaml4 from "yaml";
|
|
8564
8886
|
function indexToYaml(index) {
|
|
@@ -8721,33 +9043,22 @@ var McpToolSpecSchema = z4.object({
|
|
|
8721
9043
|
source_file: z4.string(),
|
|
8722
9044
|
source_line: z4.number().int().positive()
|
|
8723
9045
|
});
|
|
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
9046
|
var EntrypointManifestSchema = z4.object({
|
|
8734
9047
|
schema_version: z4.literal(ENTRYPOINT_SCHEMA_VERSION),
|
|
8735
9048
|
generated_at: z4.string(),
|
|
8736
9049
|
cli_commands: z4.array(CliCommandSpecSchema).readonly(),
|
|
8737
|
-
mcp_tools: z4.array(McpToolSpecSchema).readonly()
|
|
8738
|
-
rest_endpoints: z4.array(RestEndpointSpecSchema).readonly()
|
|
9050
|
+
mcp_tools: z4.array(McpToolSpecSchema).readonly()
|
|
8739
9051
|
});
|
|
8740
9052
|
var DEFAULT_ENTRYPOINT_EXTRACTOR_OPTIONS = {
|
|
8741
9053
|
packageRoot: "packages/nexus-agents",
|
|
8742
9054
|
cliCommandsPath: "src/cli-commands.ts",
|
|
8743
9055
|
mcpToolsPath: "src/mcp/tools",
|
|
8744
|
-
restRoutesPath: "src/api/routes",
|
|
8745
9056
|
sanitize: true
|
|
8746
9057
|
};
|
|
8747
9058
|
|
|
8748
9059
|
// src/indexer/entrypoint-extractor.ts
|
|
8749
9060
|
import { Project as Project2 } from "ts-morph";
|
|
8750
|
-
import * as
|
|
9061
|
+
import * as path14 from "path";
|
|
8751
9062
|
import * as yaml5 from "yaml";
|
|
8752
9063
|
|
|
8753
9064
|
// src/indexer/entrypoint-sanitizer.ts
|
|
@@ -8841,22 +9152,6 @@ function sanitizeTool(tool) {
|
|
|
8841
9152
|
source_line: tool.source_line
|
|
8842
9153
|
};
|
|
8843
9154
|
}
|
|
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
9155
|
|
|
8861
9156
|
// src/indexer/entrypoint-cli-extractor.ts
|
|
8862
9157
|
import { SyntaxKind } from "ts-morph";
|
|
@@ -9173,159 +9468,6 @@ function extractParametersFromSchema(schemaArg, sourceFile) {
|
|
|
9173
9468
|
return [];
|
|
9174
9469
|
}
|
|
9175
9470
|
|
|
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
9471
|
// src/indexer/entrypoint-extractor.ts
|
|
9330
9472
|
function generateTimestamp() {
|
|
9331
9473
|
return new Date(getTimeProvider().now()).toLocaleString("en-US", {
|
|
@@ -9341,14 +9483,13 @@ function generateTimestamp() {
|
|
|
9341
9483
|
}
|
|
9342
9484
|
function createProject(opts) {
|
|
9343
9485
|
const project = new Project2({
|
|
9344
|
-
tsConfigFilePath:
|
|
9486
|
+
tsConfigFilePath: path14.join(opts.packageRoot, "tsconfig.json"),
|
|
9345
9487
|
skipAddingFilesFromTsConfig: true
|
|
9346
9488
|
});
|
|
9347
9489
|
project.addSourceFilesAtPaths([
|
|
9348
|
-
|
|
9349
|
-
|
|
9350
|
-
|
|
9351
|
-
path15.join(opts.packageRoot, opts.restRoutesPath, "*.ts")
|
|
9490
|
+
path14.join(opts.packageRoot, opts.cliCommandsPath),
|
|
9491
|
+
path14.join(opts.packageRoot, "src/cli-types.ts"),
|
|
9492
|
+
path14.join(opts.packageRoot, opts.mcpToolsPath, "*.ts")
|
|
9352
9493
|
]);
|
|
9353
9494
|
return project;
|
|
9354
9495
|
}
|
|
@@ -9365,16 +9506,13 @@ function extractEntrypoints(options = {}) {
|
|
|
9365
9506
|
"src/cli-types.ts"
|
|
9366
9507
|
);
|
|
9367
9508
|
const mcpTools = extractMcpTools(project, opts.packageRoot, opts.mcpToolsPath);
|
|
9368
|
-
const restEndpoints = extractRestEndpoints(project, opts.packageRoot, opts.restRoutesPath);
|
|
9369
9509
|
const finalCommands = opts.sanitize ? cliCommands.map(sanitizeCommand) : cliCommands;
|
|
9370
9510
|
const finalTools = opts.sanitize ? mcpTools.map(sanitizeTool) : mcpTools;
|
|
9371
|
-
const finalEndpoints = opts.sanitize ? restEndpoints.map(sanitizeEndpoint) : restEndpoints;
|
|
9372
9511
|
const manifest = {
|
|
9373
9512
|
schema_version: ENTRYPOINT_SCHEMA_VERSION,
|
|
9374
9513
|
generated_at: generateTimestamp(),
|
|
9375
9514
|
cli_commands: finalCommands,
|
|
9376
|
-
mcp_tools: finalTools
|
|
9377
|
-
rest_endpoints: finalEndpoints
|
|
9515
|
+
mcp_tools: finalTools
|
|
9378
9516
|
};
|
|
9379
9517
|
return { success: true, manifest, errors, warnings };
|
|
9380
9518
|
} catch (error) {
|
|
@@ -9396,20 +9534,20 @@ function manifestToJson(manifest) {
|
|
|
9396
9534
|
}
|
|
9397
9535
|
|
|
9398
9536
|
// src/cli/index-command-link-validator.ts
|
|
9399
|
-
import * as
|
|
9400
|
-
import * as
|
|
9537
|
+
import * as fs12 from "fs/promises";
|
|
9538
|
+
import * as path16 from "path";
|
|
9401
9539
|
|
|
9402
9540
|
// src/cli/index-command-link-validation-helpers.ts
|
|
9403
|
-
import * as
|
|
9404
|
-
import * as
|
|
9541
|
+
import * as fs11 from "fs/promises";
|
|
9542
|
+
import * as path15 from "path";
|
|
9405
9543
|
async function validateInternalLink(url, sourceFile) {
|
|
9406
9544
|
const [filePath, anchor] = url.split("#");
|
|
9407
9545
|
if (filePath === void 0 || filePath === "") {
|
|
9408
9546
|
return { valid: true };
|
|
9409
9547
|
}
|
|
9410
|
-
const resolvedPath =
|
|
9548
|
+
const resolvedPath = path15.resolve(path15.dirname(sourceFile), filePath);
|
|
9411
9549
|
try {
|
|
9412
|
-
await
|
|
9550
|
+
await fs11.access(resolvedPath);
|
|
9413
9551
|
if (anchor !== void 0 && anchor !== "") {
|
|
9414
9552
|
const anchorResult = await validateAnchorInFile(anchor, resolvedPath);
|
|
9415
9553
|
if (!anchorResult.valid) {
|
|
@@ -9483,7 +9621,7 @@ function headingToAnchor(heading) {
|
|
|
9483
9621
|
}
|
|
9484
9622
|
async function validateAnchorInFile(anchor, targetFile) {
|
|
9485
9623
|
try {
|
|
9486
|
-
const content = await
|
|
9624
|
+
const content = await fs11.readFile(targetFile, "utf-8");
|
|
9487
9625
|
const headingRegex = /^#{1,6}\s+(.+)$/gm;
|
|
9488
9626
|
let match;
|
|
9489
9627
|
while ((match = headingRegex.exec(content)) !== null) {
|
|
@@ -9492,9 +9630,9 @@ async function validateAnchorInFile(anchor, targetFile) {
|
|
|
9492
9630
|
return { valid: true };
|
|
9493
9631
|
}
|
|
9494
9632
|
}
|
|
9495
|
-
return { valid: false, error: `Anchor '#${anchor}' not found in ${
|
|
9633
|
+
return { valid: false, error: `Anchor '#${anchor}' not found in ${path15.basename(targetFile)}` };
|
|
9496
9634
|
} catch {
|
|
9497
|
-
return { valid: false, error: `Failed to read ${
|
|
9635
|
+
return { valid: false, error: `Failed to read ${path15.basename(targetFile)} for anchor check` };
|
|
9498
9636
|
}
|
|
9499
9637
|
}
|
|
9500
9638
|
async function validateAnchorLink(anchor, sourceFile) {
|
|
@@ -9584,9 +9722,9 @@ var REFERENCE_DEF_REGEX = /^\[([^\]]+)\]:\s*(.+)$/gm;
|
|
|
9584
9722
|
async function findMarkdownFiles(dir) {
|
|
9585
9723
|
const files = [];
|
|
9586
9724
|
try {
|
|
9587
|
-
const entries = await
|
|
9725
|
+
const entries = await fs12.readdir(dir, { withFileTypes: true });
|
|
9588
9726
|
for (const entry of entries) {
|
|
9589
|
-
const fullPath =
|
|
9727
|
+
const fullPath = path16.join(dir, entry.name);
|
|
9590
9728
|
if (entry.name === "node_modules" || entry.name === "dist") {
|
|
9591
9729
|
continue;
|
|
9592
9730
|
}
|
|
@@ -9680,7 +9818,7 @@ function extractLinks(content) {
|
|
|
9680
9818
|
}
|
|
9681
9819
|
async function validateFileLinks(filePath, checkExternal, counters) {
|
|
9682
9820
|
try {
|
|
9683
|
-
const content = await
|
|
9821
|
+
const content = await fs12.readFile(filePath, "utf-8");
|
|
9684
9822
|
const links = extractLinks(content);
|
|
9685
9823
|
const brokenLinksInFile = [];
|
|
9686
9824
|
for (const link of links) {
|
|
@@ -9781,10 +9919,10 @@ async function generateIndex(options) {
|
|
|
9781
9919
|
`Extracted ${String(extraction.files.length)} files in ${String(extraction.durationMs)}ms`
|
|
9782
9920
|
);
|
|
9783
9921
|
const index = buildIndex(extraction.files);
|
|
9784
|
-
const outputDir =
|
|
9785
|
-
await
|
|
9922
|
+
const outputDir = path17.dirname(outputPath);
|
|
9923
|
+
await fs13.mkdir(outputDir, { recursive: true });
|
|
9786
9924
|
const content = format === "yaml" ? indexToYaml(index) : indexToJson(index);
|
|
9787
|
-
await
|
|
9925
|
+
await fs13.writeFile(outputPath, content, "utf-8");
|
|
9788
9926
|
logger9.info(`Wrote index to ${outputPath}`);
|
|
9789
9927
|
return {
|
|
9790
9928
|
success: true,
|
|
@@ -9799,14 +9937,14 @@ async function generateIndex(options) {
|
|
|
9799
9937
|
async function checkIndex(options) {
|
|
9800
9938
|
const indexPath = options.output ?? "docs/codebase-index.yaml";
|
|
9801
9939
|
try {
|
|
9802
|
-
await
|
|
9940
|
+
await fs13.access(indexPath);
|
|
9803
9941
|
} catch {
|
|
9804
9942
|
return {
|
|
9805
9943
|
success: false,
|
|
9806
9944
|
message: `Index file not found: ${indexPath}. Run 'nexus-agents index generate' first.`
|
|
9807
9945
|
};
|
|
9808
9946
|
}
|
|
9809
|
-
const content = await
|
|
9947
|
+
const content = await fs13.readFile(indexPath, "utf-8");
|
|
9810
9948
|
let existingIndex;
|
|
9811
9949
|
try {
|
|
9812
9950
|
const parsed = indexPath.endsWith(".json") ? JSON.parse(content) : yaml6.parse(content);
|
|
@@ -9842,18 +9980,18 @@ async function generateDiagram(options) {
|
|
|
9842
9980
|
const indexPath = options.output ?? "docs/codebase-index.yaml";
|
|
9843
9981
|
const diagramPath = "docs/dependency-graph.md";
|
|
9844
9982
|
try {
|
|
9845
|
-
await
|
|
9983
|
+
await fs13.access(indexPath);
|
|
9846
9984
|
} catch {
|
|
9847
9985
|
logger9.info("Index not found, generating...");
|
|
9848
9986
|
await generateIndex({ ...options, subcommand: "generate" });
|
|
9849
9987
|
}
|
|
9850
|
-
const content = await
|
|
9988
|
+
const content = await fs13.readFile(indexPath, "utf-8");
|
|
9851
9989
|
const parsed = indexPath.endsWith(".json") ? JSON.parse(content) : yaml6.parse(content);
|
|
9852
9990
|
CodebaseIndexSchema.parse(parsed);
|
|
9853
9991
|
const index = parsed;
|
|
9854
9992
|
const diagram = generateDiagramMarkdown(index);
|
|
9855
|
-
await
|
|
9856
|
-
await
|
|
9993
|
+
await fs13.mkdir(path17.dirname(diagramPath), { recursive: true });
|
|
9994
|
+
await fs13.writeFile(diagramPath, diagram, "utf-8");
|
|
9857
9995
|
logger9.info(`Wrote diagram to ${diagramPath}`);
|
|
9858
9996
|
return {
|
|
9859
9997
|
success: true,
|
|
@@ -9882,7 +10020,7 @@ function extractDocumentedModules(architectureContent) {
|
|
|
9882
10020
|
}
|
|
9883
10021
|
async function getActualModules(srcPath) {
|
|
9884
10022
|
try {
|
|
9885
|
-
const entries = await
|
|
10023
|
+
const entries = await fs13.readdir(srcPath, { withFileTypes: true });
|
|
9886
10024
|
return entries.filter((e) => e.isDirectory() && !e.name.startsWith(".") && !e.name.startsWith("__")).map((e) => e.name);
|
|
9887
10025
|
} catch (error) {
|
|
9888
10026
|
logger9.debug("Failed to read source modules", { srcPath, error: getErrorMessage(error) });
|
|
@@ -9918,12 +10056,12 @@ async function validateArchitecture(options) {
|
|
|
9918
10056
|
const srcPath = "packages/nexus-agents/src";
|
|
9919
10057
|
let architectureContent;
|
|
9920
10058
|
try {
|
|
9921
|
-
architectureContent = await
|
|
10059
|
+
architectureContent = await fs13.readFile(architecturePath, "utf-8");
|
|
9922
10060
|
} catch {
|
|
9923
10061
|
return { success: false, message: `ARCHITECTURE.md not found at ${architecturePath}` };
|
|
9924
10062
|
}
|
|
9925
10063
|
try {
|
|
9926
|
-
await
|
|
10064
|
+
await fs13.access(srcPath);
|
|
9927
10065
|
} catch {
|
|
9928
10066
|
return { success: false, message: `Source directory not found: ${srcPath}` };
|
|
9929
10067
|
}
|
|
@@ -9945,7 +10083,6 @@ async function entrypointsCommand(options) {
|
|
|
9945
10083
|
packageRoot: "packages/nexus-agents",
|
|
9946
10084
|
cliCommandsPath: "src/cli-commands.ts",
|
|
9947
10085
|
mcpToolsPath: "src/mcp/tools",
|
|
9948
|
-
restRoutesPath: "src/api/routes",
|
|
9949
10086
|
sanitize: true
|
|
9950
10087
|
});
|
|
9951
10088
|
if (!result.success || result.manifest === void 0) {
|
|
@@ -9959,15 +10096,15 @@ async function entrypointsCommand(options) {
|
|
|
9959
10096
|
for (const warning of result.warnings) {
|
|
9960
10097
|
logger9.warn(warning);
|
|
9961
10098
|
}
|
|
9962
|
-
const outputDir =
|
|
9963
|
-
await
|
|
10099
|
+
const outputDir = path17.dirname(outputPath);
|
|
10100
|
+
await fs13.mkdir(outputDir, { recursive: true });
|
|
9964
10101
|
const content = format === "yaml" ? manifestToYaml(manifest) : manifestToJson(manifest);
|
|
9965
|
-
await
|
|
10102
|
+
await fs13.writeFile(outputPath, content, "utf-8");
|
|
9966
10103
|
logger9.info(`Wrote entrypoints manifest to ${outputPath}`);
|
|
9967
|
-
const total = manifest.cli_commands.length + manifest.mcp_tools.length
|
|
10104
|
+
const total = manifest.cli_commands.length + manifest.mcp_tools.length;
|
|
9968
10105
|
return {
|
|
9969
10106
|
success: true,
|
|
9970
|
-
message: `Extracted ${String(total)} entrypoints (${String(manifest.cli_commands.length)} CLI, ${String(manifest.mcp_tools.length)} MCP
|
|
10107
|
+
message: `Extracted ${String(total)} entrypoints (${String(manifest.cli_commands.length)} CLI, ${String(manifest.mcp_tools.length)} MCP)`,
|
|
9971
10108
|
data: {
|
|
9972
10109
|
outputPath
|
|
9973
10110
|
}
|
|
@@ -9980,9 +10117,9 @@ async function freshnessCommand(options) {
|
|
|
9980
10117
|
const hasIssues = summary.stale > 0 || summary.warning > 0;
|
|
9981
10118
|
const output2 = options.format === "json" ? formatFreshnessJson(result) : formatFreshnessTable(result);
|
|
9982
10119
|
if (options.output !== void 0) {
|
|
9983
|
-
const outputDir =
|
|
9984
|
-
await
|
|
9985
|
-
await
|
|
10120
|
+
const outputDir = path17.dirname(options.output);
|
|
10121
|
+
await fs13.mkdir(outputDir, { recursive: true });
|
|
10122
|
+
await fs13.writeFile(options.output, output2, "utf-8");
|
|
9986
10123
|
logger9.info(`Wrote freshness report to ${options.output}`);
|
|
9987
10124
|
} else {
|
|
9988
10125
|
process.stdout.write(output2 + "\n");
|
|
@@ -10005,9 +10142,9 @@ async function linksCommand(options) {
|
|
|
10005
10142
|
const hasBrokenLinks = summary.brokenLinks > 0;
|
|
10006
10143
|
const output2 = options.format === "json" ? formatLinkValidationJson(result) : formatLinkValidationTable(result);
|
|
10007
10144
|
if (options.output !== void 0) {
|
|
10008
|
-
const outputDir =
|
|
10009
|
-
await
|
|
10010
|
-
await
|
|
10145
|
+
const outputDir = path17.dirname(options.output);
|
|
10146
|
+
await fs13.mkdir(outputDir, { recursive: true });
|
|
10147
|
+
await fs13.writeFile(options.output, output2, "utf-8");
|
|
10011
10148
|
logger9.info(`Wrote link validation report to ${options.output}`);
|
|
10012
10149
|
} else {
|
|
10013
10150
|
process.stdout.write(output2 + "\n");
|
|
@@ -10348,7 +10485,7 @@ async function verifyCommand(options) {
|
|
|
10348
10485
|
|
|
10349
10486
|
// src/cli/swe-bench-command.ts
|
|
10350
10487
|
import * as os2 from "os";
|
|
10351
|
-
import * as
|
|
10488
|
+
import * as path18 from "path";
|
|
10352
10489
|
function runInfo(options) {
|
|
10353
10490
|
console.log(`
|
|
10354
10491
|
SWE-bench Dataset: ${options.variant}`);
|
|
@@ -10415,7 +10552,8 @@ Error loading dataset: ${error.message}`);
|
|
|
10415
10552
|
}
|
|
10416
10553
|
async function loadAndSelectInstances(options) {
|
|
10417
10554
|
console.log("Loading dataset...");
|
|
10418
|
-
const
|
|
10555
|
+
const hasInstanceFilter = options.instances.length > 0;
|
|
10556
|
+
const loadOptions = !hasInstanceFilter && options.limit !== void 0 ? { limit: options.limit } : {};
|
|
10419
10557
|
const loadResult = await loadDataset(options.variant, loadOptions);
|
|
10420
10558
|
if (!loadResult.ok) {
|
|
10421
10559
|
logDatasetError(loadResult.error);
|
|
@@ -10464,8 +10602,8 @@ function isValidRunId(runId) {
|
|
|
10464
10602
|
return /^[a-zA-Z0-9_-]{1,64}$/.test(runId);
|
|
10465
10603
|
}
|
|
10466
10604
|
function isValidOutputDir(dir) {
|
|
10467
|
-
const resolved =
|
|
10468
|
-
return !resolved.includes("..") && resolved ===
|
|
10605
|
+
const resolved = path18.resolve(dir);
|
|
10606
|
+
return !resolved.includes("..") && resolved === path18.normalize(resolved);
|
|
10469
10607
|
}
|
|
10470
10608
|
function formatProgress(progress) {
|
|
10471
10609
|
const pct = progress.totalCount > 0 ? Math.round(progress.completedCount / progress.totalCount * 100) : 0;
|
|
@@ -10479,12 +10617,12 @@ function buildHarnessConfig(options) {
|
|
|
10479
10617
|
const predictionsPath = options.predictions ?? options.output;
|
|
10480
10618
|
const runId = options.runId ?? `eval-${String(Date.now())}`;
|
|
10481
10619
|
return {
|
|
10482
|
-
predictionsPath:
|
|
10620
|
+
predictionsPath: path18.resolve(predictionsPath),
|
|
10483
10621
|
datasetName: options.variant,
|
|
10484
10622
|
maxWorkers: options.maxWorkers,
|
|
10485
10623
|
runId,
|
|
10486
10624
|
timeoutSeconds: 1800,
|
|
10487
|
-
outputDir:
|
|
10625
|
+
outputDir: path18.resolve(options.outputDir),
|
|
10488
10626
|
useDocker: true,
|
|
10489
10627
|
cacheLevel: options.cacheLevel
|
|
10490
10628
|
};
|
|
@@ -11066,8 +11204,8 @@ function isValidConfigAction(value) {
|
|
|
11066
11204
|
}
|
|
11067
11205
|
|
|
11068
11206
|
// src/cli/config-command-helpers.ts
|
|
11069
|
-
import * as
|
|
11070
|
-
import * as
|
|
11207
|
+
import * as fs14 from "fs/promises";
|
|
11208
|
+
import * as path19 from "path";
|
|
11071
11209
|
import { existsSync as existsSync10 } from "fs";
|
|
11072
11210
|
|
|
11073
11211
|
// src/cli/config-command-formatting.ts
|
|
@@ -11335,10 +11473,10 @@ function parseValueFromString(stringValue, defaultValue) {
|
|
|
11335
11473
|
}
|
|
11336
11474
|
function resolveFilePath(filePath, allowedBase) {
|
|
11337
11475
|
const base = allowedBase ?? process.cwd();
|
|
11338
|
-
const resolved =
|
|
11339
|
-
const normalizedResolved =
|
|
11340
|
-
const normalizedBase =
|
|
11341
|
-
const isWithinBase = normalizedResolved === normalizedBase || normalizedResolved.startsWith(normalizedBase +
|
|
11476
|
+
const resolved = path19.isAbsolute(filePath) ? filePath : path19.resolve(base, filePath);
|
|
11477
|
+
const normalizedResolved = path19.normalize(resolved);
|
|
11478
|
+
const normalizedBase = path19.normalize(base);
|
|
11479
|
+
const isWithinBase = normalizedResolved === normalizedBase || normalizedResolved.startsWith(normalizedBase + path19.sep);
|
|
11342
11480
|
if (!isWithinBase) {
|
|
11343
11481
|
throw new ConfigCommandError(
|
|
11344
11482
|
"PATH_TRAVERSAL",
|
|
@@ -11349,11 +11487,11 @@ function resolveFilePath(filePath, allowedBase) {
|
|
|
11349
11487
|
}
|
|
11350
11488
|
function getDefaultExportPath(format) {
|
|
11351
11489
|
const extension = format === "json" ? "json" : "yaml";
|
|
11352
|
-
return
|
|
11490
|
+
return path19.resolve(process.cwd(), `nexus-config.${extension}`);
|
|
11353
11491
|
}
|
|
11354
11492
|
|
|
11355
11493
|
// src/cli/config-command-handlers.ts
|
|
11356
|
-
import * as
|
|
11494
|
+
import * as fs15 from "fs/promises";
|
|
11357
11495
|
import { existsSync as existsSync11 } from "fs";
|
|
11358
11496
|
async function handleGet(key) {
|
|
11359
11497
|
const parsed = parseConfigKey(key);
|
|
@@ -11455,7 +11593,7 @@ function buildConfigEntries() {
|
|
|
11455
11593
|
}
|
|
11456
11594
|
async function writeConfigFile2(filePath, content) {
|
|
11457
11595
|
try {
|
|
11458
|
-
await
|
|
11596
|
+
await fs15.writeFile(filePath, content, "utf-8");
|
|
11459
11597
|
} catch (error) {
|
|
11460
11598
|
const message = getErrorMessage(error);
|
|
11461
11599
|
throw new ConfigCommandError("WRITE_ERROR", `Failed to write file: ${message}`);
|
|
@@ -11481,7 +11619,7 @@ async function readImportFile(filePath) {
|
|
|
11481
11619
|
}
|
|
11482
11620
|
const format = filePath.endsWith(".yaml") || filePath.endsWith(".yml") ? "yaml" : "json";
|
|
11483
11621
|
try {
|
|
11484
|
-
const content = await
|
|
11622
|
+
const content = await fs15.readFile(filePath, "utf-8");
|
|
11485
11623
|
return { content, format };
|
|
11486
11624
|
} catch (error) {
|
|
11487
11625
|
const message = getErrorMessage(error);
|
|
@@ -11764,20 +11902,20 @@ var SessionEndInputSchema = HookInputBaseSchema.extend({
|
|
|
11764
11902
|
var PreToolUseInputSchema = HookInputBaseSchema.extend({
|
|
11765
11903
|
hook_event_name: z6.literal("PreToolUse"),
|
|
11766
11904
|
tool_name: z6.string(),
|
|
11767
|
-
tool_input: z6.record(z6.unknown()),
|
|
11905
|
+
tool_input: z6.record(z6.string(), z6.unknown()),
|
|
11768
11906
|
tool_use_id: z6.string()
|
|
11769
11907
|
});
|
|
11770
11908
|
var PostToolUseInputSchema = HookInputBaseSchema.extend({
|
|
11771
11909
|
hook_event_name: z6.literal("PostToolUse"),
|
|
11772
11910
|
tool_name: z6.string(),
|
|
11773
|
-
tool_input: z6.record(z6.unknown()),
|
|
11774
|
-
tool_response: z6.record(z6.unknown()),
|
|
11911
|
+
tool_input: z6.record(z6.string(), z6.unknown()),
|
|
11912
|
+
tool_response: z6.record(z6.string(), z6.unknown()),
|
|
11775
11913
|
tool_use_id: z6.string()
|
|
11776
11914
|
});
|
|
11777
11915
|
var PostToolUseFailureInputSchema = HookInputBaseSchema.extend({
|
|
11778
11916
|
hook_event_name: z6.literal("PostToolUseFailure"),
|
|
11779
11917
|
tool_name: z6.string(),
|
|
11780
|
-
tool_input: z6.record(z6.unknown()),
|
|
11918
|
+
tool_input: z6.record(z6.string(), z6.unknown()),
|
|
11781
11919
|
tool_use_id: z6.string(),
|
|
11782
11920
|
error: z6.string().optional()
|
|
11783
11921
|
});
|
|
@@ -11825,7 +11963,7 @@ var SubagentStartInputSchema = HookInputBaseSchema.extend({
|
|
|
11825
11963
|
var PermissionRequestInputSchema = HookInputBaseSchema.extend({
|
|
11826
11964
|
hook_event_name: z6.literal("PermissionRequest"),
|
|
11827
11965
|
tool_name: z6.string(),
|
|
11828
|
-
tool_input: z6.record(z6.unknown()),
|
|
11966
|
+
tool_input: z6.record(z6.string(), z6.unknown()),
|
|
11829
11967
|
tool_use_id: z6.string()
|
|
11830
11968
|
});
|
|
11831
11969
|
var HookInputSchema = z6.discriminatedUnion("hook_event_name", [
|
|
@@ -11856,7 +11994,7 @@ var PreToolUseOutputSchema = HookOutputBaseSchema.extend({
|
|
|
11856
11994
|
hookEventName: z6.literal("PreToolUse"),
|
|
11857
11995
|
permissionDecision: PermissionDecision.optional(),
|
|
11858
11996
|
permissionDecisionReason: z6.string().optional(),
|
|
11859
|
-
updatedInput: z6.record(z6.unknown()).optional(),
|
|
11997
|
+
updatedInput: z6.record(z6.string(), z6.unknown()).optional(),
|
|
11860
11998
|
additionalContext: z6.string().optional()
|
|
11861
11999
|
}).optional()
|
|
11862
12000
|
});
|
|
@@ -11891,7 +12029,7 @@ var PermissionRequestOutputSchema = HookOutputBaseSchema.extend({
|
|
|
11891
12029
|
hookEventName: z6.literal("PermissionRequest"),
|
|
11892
12030
|
decision: z6.object({
|
|
11893
12031
|
behavior: z6.enum(["allow", "deny"]),
|
|
11894
|
-
updatedInput: z6.record(z6.unknown()).optional(),
|
|
12032
|
+
updatedInput: z6.record(z6.string(), z6.unknown()).optional(),
|
|
11895
12033
|
message: z6.string().optional(),
|
|
11896
12034
|
interrupt: z6.boolean().optional()
|
|
11897
12035
|
})
|
|
@@ -11979,7 +12117,7 @@ function writeResultAndExit(result) {
|
|
|
11979
12117
|
|
|
11980
12118
|
// src/cli/hooks/hook-router.ts
|
|
11981
12119
|
async function readStdin() {
|
|
11982
|
-
return new Promise((
|
|
12120
|
+
return new Promise((resolve14, reject) => {
|
|
11983
12121
|
let data = "";
|
|
11984
12122
|
process.stdin.setEncoding("utf8");
|
|
11985
12123
|
process.stdin.on("readable", () => {
|
|
@@ -11990,24 +12128,24 @@ async function readStdin() {
|
|
|
11990
12128
|
}
|
|
11991
12129
|
});
|
|
11992
12130
|
process.stdin.on("end", () => {
|
|
11993
|
-
|
|
12131
|
+
resolve14(data);
|
|
11994
12132
|
});
|
|
11995
12133
|
process.stdin.on("error", (err2) => {
|
|
11996
12134
|
reject(err2);
|
|
11997
12135
|
});
|
|
11998
12136
|
if (process.stdin.isTTY) {
|
|
11999
|
-
|
|
12137
|
+
resolve14("");
|
|
12000
12138
|
}
|
|
12001
12139
|
});
|
|
12002
12140
|
}
|
|
12003
12141
|
async function readStdinWithTimeout(timeoutMs = 5e3) {
|
|
12004
|
-
return new Promise((
|
|
12142
|
+
return new Promise((resolve14, reject) => {
|
|
12005
12143
|
const timeout2 = setTimeout(() => {
|
|
12006
12144
|
reject(new Error(`Stdin read timeout after ${String(timeoutMs)}ms`));
|
|
12007
12145
|
}, timeoutMs);
|
|
12008
12146
|
readStdin().then((data) => {
|
|
12009
12147
|
clearTimeout(timeout2);
|
|
12010
|
-
|
|
12148
|
+
resolve14(data);
|
|
12011
12149
|
}).catch((err2) => {
|
|
12012
12150
|
clearTimeout(timeout2);
|
|
12013
12151
|
reject(err2 instanceof Error ? err2 : new Error(String(err2)));
|
|
@@ -12139,9 +12277,9 @@ function setStringOption(options, key, value) {
|
|
|
12139
12277
|
|
|
12140
12278
|
// src/cli/hooks/handlers/handler-utils.ts
|
|
12141
12279
|
import { homedir as homedir2 } from "os";
|
|
12142
|
-
import { join as
|
|
12280
|
+
import { join as join13 } from "path";
|
|
12143
12281
|
function getDefaultDbPath2() {
|
|
12144
|
-
return
|
|
12282
|
+
return join13(homedir2(), ".nexus-agents", "sessions.db");
|
|
12145
12283
|
}
|
|
12146
12284
|
var HookEnvVars = {
|
|
12147
12285
|
/** Override the default database path */
|
|
@@ -13324,6 +13462,72 @@ async function demoCommand(subcommand, args, options) {
|
|
|
13324
13462
|
}
|
|
13325
13463
|
|
|
13326
13464
|
// src/governance/fitness-score.ts
|
|
13465
|
+
import { existsSync as existsSync12, readFileSync as readFileSync6, readdirSync, statSync } from "fs";
|
|
13466
|
+
import { join as join14 } from "path";
|
|
13467
|
+
function findPkgRoot() {
|
|
13468
|
+
let dir = import.meta.dirname;
|
|
13469
|
+
for (let i = 0; i < 10; i++) {
|
|
13470
|
+
const pkgPath = join14(dir, "package.json");
|
|
13471
|
+
if (existsSync12(pkgPath)) {
|
|
13472
|
+
const content = readFileSync6(pkgPath, "utf-8");
|
|
13473
|
+
if (content.includes('"nexus-agents"')) return dir;
|
|
13474
|
+
}
|
|
13475
|
+
dir = join14(dir, "..");
|
|
13476
|
+
}
|
|
13477
|
+
return join14(import.meta.dirname, "../..");
|
|
13478
|
+
}
|
|
13479
|
+
var PKG_ROOT = findPkgRoot();
|
|
13480
|
+
var SRC_ROOT = join14(PKG_ROOT, "src");
|
|
13481
|
+
var REPO_ROOT = join14(PKG_ROOT, "../..");
|
|
13482
|
+
var DOCS_ROOT = join14(REPO_ROOT, "docs");
|
|
13483
|
+
var DETERMINISM_EXCLUDES = [
|
|
13484
|
+
/\.test\.ts$/,
|
|
13485
|
+
/\.spec\.ts$/,
|
|
13486
|
+
/random-provider\.ts$/,
|
|
13487
|
+
/time-provider\.ts$/
|
|
13488
|
+
];
|
|
13489
|
+
function countFiles(dir, pattern) {
|
|
13490
|
+
if (!existsSync12(dir)) return 0;
|
|
13491
|
+
let count = 0;
|
|
13492
|
+
for (const entry of readdirSync(dir)) {
|
|
13493
|
+
const fullPath = join14(dir, entry);
|
|
13494
|
+
const stat2 = statSync(fullPath);
|
|
13495
|
+
if (stat2.isDirectory() && !entry.startsWith(".")) {
|
|
13496
|
+
count += countFiles(fullPath, pattern);
|
|
13497
|
+
} else if (pattern.test(entry)) {
|
|
13498
|
+
count++;
|
|
13499
|
+
}
|
|
13500
|
+
}
|
|
13501
|
+
return count;
|
|
13502
|
+
}
|
|
13503
|
+
function fileContains(filePath, pattern) {
|
|
13504
|
+
if (!existsSync12(filePath)) return false;
|
|
13505
|
+
return pattern.test(readFileSync6(filePath, "utf-8"));
|
|
13506
|
+
}
|
|
13507
|
+
function isExcluded(entry, excludePatterns) {
|
|
13508
|
+
return excludePatterns?.some((p) => p.test(entry)) ?? false;
|
|
13509
|
+
}
|
|
13510
|
+
function countMatchesInFile(fullPath, contentPattern) {
|
|
13511
|
+
const matches = readFileSync6(fullPath, "utf-8").match(contentPattern);
|
|
13512
|
+
return matches?.length ?? 0;
|
|
13513
|
+
}
|
|
13514
|
+
function countPatternInDir(dir, filePattern, contentPattern, excludePatterns) {
|
|
13515
|
+
if (!existsSync12(dir)) return 0;
|
|
13516
|
+
let count = 0;
|
|
13517
|
+
for (const entry of readdirSync(dir)) {
|
|
13518
|
+
const fullPath = join14(dir, entry);
|
|
13519
|
+
const stat2 = statSync(fullPath);
|
|
13520
|
+
if (stat2.isDirectory() && !entry.startsWith(".") && entry !== "node_modules") {
|
|
13521
|
+
count += countPatternInDir(fullPath, filePattern, contentPattern, excludePatterns);
|
|
13522
|
+
} else if (filePattern.test(entry) && !isExcluded(entry, excludePatterns)) {
|
|
13523
|
+
count += countMatchesInFile(fullPath, contentPattern);
|
|
13524
|
+
}
|
|
13525
|
+
}
|
|
13526
|
+
return count;
|
|
13527
|
+
}
|
|
13528
|
+
function clamp2(score, max) {
|
|
13529
|
+
return Math.max(0, Math.min(max, score));
|
|
13530
|
+
}
|
|
13327
13531
|
var FitnessScoreCalculator = class {
|
|
13328
13532
|
logger;
|
|
13329
13533
|
checks = [];
|
|
@@ -13331,62 +13535,26 @@ var FitnessScoreCalculator = class {
|
|
|
13331
13535
|
this.logger = logger17 ?? createLogger({ component: "FitnessScoreCalculator" });
|
|
13332
13536
|
this.registerDefaultChecks();
|
|
13333
13537
|
}
|
|
13334
|
-
/**
|
|
13335
|
-
* Register default fitness checks.
|
|
13336
|
-
*/
|
|
13538
|
+
/** Register default fitness checks. */
|
|
13337
13539
|
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
|
-
});
|
|
13540
|
+
const reg = (dimension, maxPoints, name, check) => {
|
|
13541
|
+
this.checks.push({ dimension, maxPoints, name, check });
|
|
13542
|
+
};
|
|
13543
|
+
reg("canonicalPaths", 20, "Canonical Paths", () => this.checkCanonicalPaths());
|
|
13544
|
+
reg("explicitBehavior", 15, "Explicit Behavior", () => this.checkExplicitBehavior());
|
|
13545
|
+
reg("determinism", 15, "Determinism", () => this.checkDeterminism());
|
|
13546
|
+
reg("observability", 15, "Observability", () => this.checkObservability());
|
|
13547
|
+
reg("configSimplicity", 10, "Config Simplicity", () => this.checkConfigSimplicity());
|
|
13548
|
+
reg("layerSeparation", 10, "Layer Separation", () => this.checkLayerSeparation());
|
|
13549
|
+
reg("operatorErgonomics", 10, "Operator Ergonomics", () => this.checkOperatorErgonomics());
|
|
13550
|
+
reg(
|
|
13551
|
+
"governanceIntegration",
|
|
13552
|
+
5,
|
|
13553
|
+
"Governance Integration",
|
|
13554
|
+
() => this.checkGovernanceIntegration()
|
|
13555
|
+
);
|
|
13386
13556
|
}
|
|
13387
|
-
/**
|
|
13388
|
-
* Run full fitness audit.
|
|
13389
|
-
*/
|
|
13557
|
+
/** Run full fitness audit. */
|
|
13390
13558
|
audit(version) {
|
|
13391
13559
|
const findings = [];
|
|
13392
13560
|
const dimensions = {};
|
|
@@ -13407,80 +13575,391 @@ var FitnessScoreCalculator = class {
|
|
|
13407
13575
|
};
|
|
13408
13576
|
}
|
|
13409
13577
|
// =========================================================================
|
|
13410
|
-
// Individual Checks
|
|
13578
|
+
// Individual Checks — real filesystem analysis
|
|
13411
13579
|
// =========================================================================
|
|
13412
|
-
/**
|
|
13413
|
-
* Check canonical paths - penalize duplicate paths to same workflow.
|
|
13414
|
-
* Target: 18/20 after consolidation (per Issue #574)
|
|
13415
|
-
*/
|
|
13580
|
+
/** Check canonical paths: penalize duplicate router implementations. */
|
|
13416
13581
|
checkCanonicalPaths() {
|
|
13417
13582
|
const findings = [];
|
|
13418
|
-
|
|
13419
|
-
|
|
13583
|
+
let score = 20;
|
|
13584
|
+
const routerCount = countFiles(join14(SRC_ROOT, "cli-adapters"), /router\.ts$/);
|
|
13585
|
+
if (routerCount > 5) {
|
|
13586
|
+
const excess = routerCount - 5;
|
|
13587
|
+
const deduction = Math.min(5, excess);
|
|
13588
|
+
score -= deduction;
|
|
13589
|
+
findings.push(
|
|
13590
|
+
this.finding(
|
|
13591
|
+
"canonicalPaths",
|
|
13592
|
+
"warning",
|
|
13593
|
+
`${String(routerCount)} router implementations found (target: <=5)`,
|
|
13594
|
+
deduction,
|
|
13595
|
+
"Consolidate duplicate routers into CompositeRouter"
|
|
13596
|
+
)
|
|
13597
|
+
);
|
|
13598
|
+
}
|
|
13599
|
+
if (existsSync12(join14(SRC_ROOT, "cli-adapters/composite-router.ts"))) {
|
|
13600
|
+
score += 2;
|
|
13601
|
+
} else {
|
|
13602
|
+
score -= 3;
|
|
13603
|
+
findings.push(
|
|
13604
|
+
this.finding(
|
|
13605
|
+
"canonicalPaths",
|
|
13606
|
+
"critical",
|
|
13607
|
+
"CompositeRouter missing \u2014 no unified routing entry point",
|
|
13608
|
+
3
|
|
13609
|
+
)
|
|
13610
|
+
);
|
|
13611
|
+
}
|
|
13612
|
+
score = this.checkOrchestratorInterface(score, findings);
|
|
13613
|
+
return { score: clamp2(score, 20), findings };
|
|
13614
|
+
}
|
|
13615
|
+
/** Sub-check for IOrchestrator interface and adapter wiring. */
|
|
13616
|
+
checkOrchestratorInterface(score, findings) {
|
|
13617
|
+
const orchPath = join14(SRC_ROOT, "core/types/orchestrator.ts");
|
|
13618
|
+
if (existsSync12(orchPath) && fileContains(orchPath, /interface IOrchestrator/)) {
|
|
13619
|
+
score += 3;
|
|
13620
|
+
} else {
|
|
13621
|
+
score -= 2;
|
|
13622
|
+
findings.push(
|
|
13623
|
+
this.finding("canonicalPaths", "warning", "No IOrchestrator interface in core/types", 2)
|
|
13624
|
+
);
|
|
13625
|
+
}
|
|
13626
|
+
const adapterPath = join14(SRC_ROOT, "orchestration/orchestrator-adapters.ts");
|
|
13627
|
+
if (existsSync12(adapterPath) && fileContains(adapterPath, /TechLeadAdapter|PuppeteerAdapter/)) {
|
|
13628
|
+
score += 2;
|
|
13629
|
+
}
|
|
13630
|
+
return score;
|
|
13420
13631
|
}
|
|
13421
13632
|
/**
|
|
13422
|
-
* Check explicit behavior
|
|
13633
|
+
* Check explicit behavior: penalize hidden/magic behavior.
|
|
13634
|
+
* TODO: This dimension lacks strong filesystem signals. Currently checks
|
|
13635
|
+
* for NEXUS_ALLOW_MOCK_ORCHESTRATION guard and magic routing patterns.
|
|
13636
|
+
* Future: add AST-based detection of implicit fallbacks.
|
|
13423
13637
|
*/
|
|
13424
13638
|
checkExplicitBehavior() {
|
|
13425
13639
|
const findings = [];
|
|
13426
|
-
|
|
13427
|
-
|
|
13640
|
+
let score = 15;
|
|
13641
|
+
const mockGuardCount = countPatternInDir(
|
|
13642
|
+
SRC_ROOT,
|
|
13643
|
+
/\.ts$/,
|
|
13644
|
+
/NEXUS_ALLOW_MOCK_ORCHESTRATION/g,
|
|
13645
|
+
DETERMINISM_EXCLUDES
|
|
13646
|
+
);
|
|
13647
|
+
if (mockGuardCount === 0) {
|
|
13648
|
+
score -= 3;
|
|
13649
|
+
findings.push(
|
|
13650
|
+
this.finding(
|
|
13651
|
+
"explicitBehavior",
|
|
13652
|
+
"warning",
|
|
13653
|
+
"No NEXUS_ALLOW_MOCK_ORCHESTRATION guard found \u2014 mock fallback may be implicit",
|
|
13654
|
+
3,
|
|
13655
|
+
"Require explicit env var for mock orchestration"
|
|
13656
|
+
)
|
|
13657
|
+
);
|
|
13658
|
+
}
|
|
13659
|
+
const magicRouting = countPatternInDir(
|
|
13660
|
+
SRC_ROOT,
|
|
13661
|
+
/\.ts$/,
|
|
13662
|
+
/fallback.*=.*true|implicitRoute/g,
|
|
13663
|
+
DETERMINISM_EXCLUDES
|
|
13664
|
+
);
|
|
13665
|
+
if (magicRouting > 5) {
|
|
13666
|
+
const deduction = Math.min(3, Math.floor(magicRouting / 3));
|
|
13667
|
+
score -= deduction;
|
|
13668
|
+
findings.push(
|
|
13669
|
+
this.finding(
|
|
13670
|
+
"explicitBehavior",
|
|
13671
|
+
"info",
|
|
13672
|
+
`${String(magicRouting)} implicit fallback/routing patterns detected`,
|
|
13673
|
+
deduction
|
|
13674
|
+
)
|
|
13675
|
+
);
|
|
13676
|
+
}
|
|
13677
|
+
return { score: clamp2(score, 15), findings };
|
|
13428
13678
|
}
|
|
13429
|
-
/**
|
|
13430
|
-
* Check determinism - reward deterministic execution.
|
|
13431
|
-
*/
|
|
13679
|
+
/** Check determinism: penalize unseeded random and raw Date.now(). */
|
|
13432
13680
|
checkDeterminism() {
|
|
13433
13681
|
const findings = [];
|
|
13434
13682
|
let score = 15;
|
|
13435
|
-
score
|
|
13436
|
-
|
|
13437
|
-
|
|
13438
|
-
|
|
13439
|
-
|
|
13440
|
-
|
|
13441
|
-
|
|
13442
|
-
|
|
13443
|
-
|
|
13683
|
+
score = this.checkRandomDeterminism(score, findings);
|
|
13684
|
+
score = this.checkTimeDeterminism(score, findings);
|
|
13685
|
+
return { score: clamp2(score, 15), findings };
|
|
13686
|
+
}
|
|
13687
|
+
/** Sub-check for Math.random() and injectable random provider. */
|
|
13688
|
+
checkRandomDeterminism(score, findings) {
|
|
13689
|
+
const randomCount = countPatternInDir(
|
|
13690
|
+
SRC_ROOT,
|
|
13691
|
+
/\.ts$/,
|
|
13692
|
+
/Math\.random\(\)/g,
|
|
13693
|
+
DETERMINISM_EXCLUDES
|
|
13694
|
+
);
|
|
13695
|
+
if (randomCount > 10) {
|
|
13696
|
+
const deduction = Math.min(5, Math.floor(randomCount / 5));
|
|
13697
|
+
score -= deduction;
|
|
13698
|
+
findings.push(
|
|
13699
|
+
this.finding(
|
|
13700
|
+
"determinism",
|
|
13701
|
+
"warning",
|
|
13702
|
+
`${String(randomCount)} unseeded Math.random() calls in production code`,
|
|
13703
|
+
deduction,
|
|
13704
|
+
"Use getRandomProvider() for injectable randomness"
|
|
13705
|
+
)
|
|
13706
|
+
);
|
|
13707
|
+
} else if (randomCount === 0) {
|
|
13708
|
+
score += 2;
|
|
13709
|
+
}
|
|
13710
|
+
if (existsSync12(join14(SRC_ROOT, "core/random-provider.ts"))) {
|
|
13711
|
+
score += 1;
|
|
13712
|
+
}
|
|
13713
|
+
const randomUsage = countPatternInDir(
|
|
13714
|
+
SRC_ROOT,
|
|
13715
|
+
/\.ts$/,
|
|
13716
|
+
/getRandomProvider\(\)/g,
|
|
13717
|
+
DETERMINISM_EXCLUDES
|
|
13718
|
+
);
|
|
13719
|
+
if (randomUsage > 5) {
|
|
13720
|
+
score += 1;
|
|
13721
|
+
}
|
|
13722
|
+
return score;
|
|
13444
13723
|
}
|
|
13445
|
-
/**
|
|
13446
|
-
|
|
13447
|
-
|
|
13724
|
+
/** Sub-check for Date.now() and injectable time provider. */
|
|
13725
|
+
checkTimeDeterminism(score, findings) {
|
|
13726
|
+
const dateNowCount = countPatternInDir(
|
|
13727
|
+
SRC_ROOT,
|
|
13728
|
+
/\.ts$/,
|
|
13729
|
+
/Date\.now\(\)/g,
|
|
13730
|
+
DETERMINISM_EXCLUDES
|
|
13731
|
+
);
|
|
13732
|
+
if (dateNowCount > 50) {
|
|
13733
|
+
score -= 2;
|
|
13734
|
+
findings.push(
|
|
13735
|
+
this.finding(
|
|
13736
|
+
"determinism",
|
|
13737
|
+
"info",
|
|
13738
|
+
`${String(dateNowCount)} Date.now() calls in production code`,
|
|
13739
|
+
2,
|
|
13740
|
+
"Use getTimeProvider() for injectable time"
|
|
13741
|
+
)
|
|
13742
|
+
);
|
|
13743
|
+
}
|
|
13744
|
+
if (existsSync12(join14(SRC_ROOT, "core/time-provider.ts"))) {
|
|
13745
|
+
score += 1;
|
|
13746
|
+
}
|
|
13747
|
+
const timeUsage = countPatternInDir(
|
|
13748
|
+
SRC_ROOT,
|
|
13749
|
+
/\.ts$/,
|
|
13750
|
+
/getTimeProvider\(\)/g,
|
|
13751
|
+
DETERMINISM_EXCLUDES
|
|
13752
|
+
);
|
|
13753
|
+
if (timeUsage > 10) {
|
|
13754
|
+
score += 1;
|
|
13755
|
+
}
|
|
13756
|
+
return score;
|
|
13757
|
+
}
|
|
13758
|
+
/** Check observability: reward tracing, logging, and audit coverage. */
|
|
13448
13759
|
checkObservability() {
|
|
13449
13760
|
const findings = [];
|
|
13450
|
-
|
|
13451
|
-
|
|
13761
|
+
let score = 15;
|
|
13762
|
+
if (existsSync12(join14(SRC_ROOT, "observability/swarm-observer.ts"))) {
|
|
13763
|
+
score += 3;
|
|
13764
|
+
} else {
|
|
13765
|
+
score -= 3;
|
|
13766
|
+
findings.push(
|
|
13767
|
+
this.finding(
|
|
13768
|
+
"observability",
|
|
13769
|
+
"warning",
|
|
13770
|
+
"No SwarmObserver found",
|
|
13771
|
+
3,
|
|
13772
|
+
"Add observability/swarm-observer.ts"
|
|
13773
|
+
)
|
|
13774
|
+
);
|
|
13775
|
+
}
|
|
13776
|
+
if (existsSync12(join14(SRC_ROOT, "core/trace.ts"))) {
|
|
13777
|
+
score += 2;
|
|
13778
|
+
}
|
|
13779
|
+
const loggerCount = countPatternInDir(SRC_ROOT, /\.ts$/, /createLogger\(/g);
|
|
13780
|
+
if (loggerCount > 50) {
|
|
13781
|
+
score += 2;
|
|
13782
|
+
} else {
|
|
13783
|
+
findings.push(
|
|
13784
|
+
this.finding(
|
|
13785
|
+
"observability",
|
|
13786
|
+
"info",
|
|
13787
|
+
`Only ${String(loggerCount)} createLogger() calls (target: >50)`,
|
|
13788
|
+
0
|
|
13789
|
+
)
|
|
13790
|
+
);
|
|
13791
|
+
}
|
|
13792
|
+
if (existsSync12(join14(SRC_ROOT, "audit"))) {
|
|
13793
|
+
score += 2;
|
|
13794
|
+
}
|
|
13795
|
+
return { score: clamp2(score, 15), findings };
|
|
13452
13796
|
}
|
|
13453
|
-
/**
|
|
13454
|
-
* Check config simplicity - penalize too many config surfaces.
|
|
13455
|
-
*/
|
|
13797
|
+
/** Check config simplicity: penalize excessive schema sprawl. */
|
|
13456
13798
|
checkConfigSimplicity() {
|
|
13457
13799
|
const findings = [];
|
|
13458
|
-
|
|
13459
|
-
|
|
13800
|
+
let score = 10;
|
|
13801
|
+
const schemaCount = countFiles(join14(SRC_ROOT, "config"), /schema.*\.ts$/);
|
|
13802
|
+
if (schemaCount > 10) {
|
|
13803
|
+
score -= 2;
|
|
13804
|
+
findings.push(
|
|
13805
|
+
this.finding(
|
|
13806
|
+
"configSimplicity",
|
|
13807
|
+
"info",
|
|
13808
|
+
`${String(schemaCount)} config schemas (target: <=10)`,
|
|
13809
|
+
2,
|
|
13810
|
+
"Consolidate related schemas"
|
|
13811
|
+
)
|
|
13812
|
+
);
|
|
13813
|
+
} else {
|
|
13814
|
+
score += 1;
|
|
13815
|
+
}
|
|
13816
|
+
if (existsSync12(join14(SRC_ROOT, "config/config-loader.ts"))) {
|
|
13817
|
+
score += 2;
|
|
13818
|
+
}
|
|
13819
|
+
if (existsSync12(join14(SRC_ROOT, "config/config-manager.ts"))) {
|
|
13820
|
+
score += 1;
|
|
13821
|
+
}
|
|
13822
|
+
return { score: clamp2(score, 10), findings };
|
|
13460
13823
|
}
|
|
13461
|
-
/**
|
|
13462
|
-
* Check layer separation - penalize cross-layer coupling.
|
|
13463
|
-
*/
|
|
13824
|
+
/** Check layer separation: penalize cross-layer imports. */
|
|
13464
13825
|
checkLayerSeparation() {
|
|
13465
13826
|
const findings = [];
|
|
13466
|
-
|
|
13467
|
-
|
|
13827
|
+
let score = 10;
|
|
13828
|
+
const adapterAgentImports = countPatternInDir(
|
|
13829
|
+
join14(SRC_ROOT, "adapters"),
|
|
13830
|
+
/\.ts$/,
|
|
13831
|
+
/from ['"]\.\.\/agents\//g
|
|
13832
|
+
);
|
|
13833
|
+
if (adapterAgentImports > 0) {
|
|
13834
|
+
const deduction = Math.min(5, adapterAgentImports);
|
|
13835
|
+
score -= deduction;
|
|
13836
|
+
findings.push(
|
|
13837
|
+
this.finding(
|
|
13838
|
+
"layerSeparation",
|
|
13839
|
+
"warning",
|
|
13840
|
+
`${String(adapterAgentImports)} adapter->agent import violations`,
|
|
13841
|
+
deduction,
|
|
13842
|
+
"Adapters should not import from agents layer"
|
|
13843
|
+
)
|
|
13844
|
+
);
|
|
13845
|
+
} else {
|
|
13846
|
+
score += 2;
|
|
13847
|
+
}
|
|
13848
|
+
const coreMcpImports = countPatternInDir(
|
|
13849
|
+
join14(SRC_ROOT, "core"),
|
|
13850
|
+
/\.ts$/,
|
|
13851
|
+
/from ['"]\.\.\/mcp\//g
|
|
13852
|
+
);
|
|
13853
|
+
if (coreMcpImports > 0) {
|
|
13854
|
+
const deduction = Math.min(3, coreMcpImports);
|
|
13855
|
+
score -= deduction;
|
|
13856
|
+
findings.push(
|
|
13857
|
+
this.finding(
|
|
13858
|
+
"layerSeparation",
|
|
13859
|
+
"critical",
|
|
13860
|
+
`${String(coreMcpImports)} core->MCP import violations`,
|
|
13861
|
+
deduction,
|
|
13862
|
+
"Core must not depend on MCP layer"
|
|
13863
|
+
)
|
|
13864
|
+
);
|
|
13865
|
+
} else {
|
|
13866
|
+
score += 1;
|
|
13867
|
+
}
|
|
13868
|
+
return { score: clamp2(score, 10), findings };
|
|
13468
13869
|
}
|
|
13469
|
-
/**
|
|
13470
|
-
* Check operator ergonomics - reward minimal CLI steps.
|
|
13471
|
-
*/
|
|
13870
|
+
/** Check operator ergonomics: reward rich CLI commands. */
|
|
13472
13871
|
checkOperatorErgonomics() {
|
|
13473
13872
|
const findings = [];
|
|
13474
|
-
|
|
13475
|
-
|
|
13873
|
+
let score = 10;
|
|
13874
|
+
const commandCount = countFiles(join14(SRC_ROOT, "cli"), /\.ts$/);
|
|
13875
|
+
if (commandCount >= 20) {
|
|
13876
|
+
score += 3;
|
|
13877
|
+
} else {
|
|
13878
|
+
findings.push(
|
|
13879
|
+
this.finding(
|
|
13880
|
+
"operatorErgonomics",
|
|
13881
|
+
"info",
|
|
13882
|
+
`${String(commandCount)} CLI commands (target: >=20)`,
|
|
13883
|
+
0
|
|
13884
|
+
)
|
|
13885
|
+
);
|
|
13886
|
+
}
|
|
13887
|
+
score = this.checkCliCommands(score, findings);
|
|
13888
|
+
return { score: clamp2(score, 10), findings };
|
|
13476
13889
|
}
|
|
13477
|
-
/**
|
|
13478
|
-
|
|
13479
|
-
|
|
13890
|
+
/** Sub-check for essential CLI commands (doctor, setup, demo, config). */
|
|
13891
|
+
checkCliCommands(score, findings) {
|
|
13892
|
+
const commands = [
|
|
13893
|
+
["cli/doctor.ts", "Doctor command", 2],
|
|
13894
|
+
["cli/setup-command.ts", "Setup wizard", 2],
|
|
13895
|
+
["cli/demo-command.ts", "Demo command", 1],
|
|
13896
|
+
["cli/config-command.ts", "Config command", 1]
|
|
13897
|
+
];
|
|
13898
|
+
for (const [path23, name, bonus] of commands) {
|
|
13899
|
+
if (existsSync12(join14(SRC_ROOT, path23))) {
|
|
13900
|
+
score += bonus;
|
|
13901
|
+
} else {
|
|
13902
|
+
findings.push(this.finding("operatorErgonomics", "info", `Missing ${name} (${path23})`, 0));
|
|
13903
|
+
}
|
|
13904
|
+
}
|
|
13905
|
+
return score;
|
|
13906
|
+
}
|
|
13907
|
+
/** Check governance integration: policy firewall, rate limiter, docs. */
|
|
13480
13908
|
checkGovernanceIntegration() {
|
|
13481
13909
|
const findings = [];
|
|
13482
|
-
|
|
13483
|
-
|
|
13910
|
+
let score = 5;
|
|
13911
|
+
score = this.checkGovernanceDocs(score, findings);
|
|
13912
|
+
score = this.checkGovernanceInfra(score, findings);
|
|
13913
|
+
return { score: clamp2(score, 5), findings };
|
|
13914
|
+
}
|
|
13915
|
+
/** Sub-check for governance documentation artifacts. */
|
|
13916
|
+
checkGovernanceDocs(score, findings) {
|
|
13917
|
+
if (!existsSync12(join14(REPO_ROOT, "CLAUDE.md"))) {
|
|
13918
|
+
score -= 3;
|
|
13919
|
+
findings.push(
|
|
13920
|
+
this.finding("governanceIntegration", "critical", "No CLAUDE.md governance document", 3)
|
|
13921
|
+
);
|
|
13922
|
+
} else {
|
|
13923
|
+
score += 1;
|
|
13924
|
+
}
|
|
13925
|
+
if (existsSync12(join14(DOCS_ROOT, "architecture/wiring-graph.json"))) {
|
|
13926
|
+
score += 1;
|
|
13927
|
+
}
|
|
13928
|
+
if (existsSync12(join14(DOCS_ROOT, "adr"))) {
|
|
13929
|
+
score += 1;
|
|
13930
|
+
} else {
|
|
13931
|
+
findings.push(this.finding("governanceIntegration", "info", "No ADR directory", 0));
|
|
13932
|
+
}
|
|
13933
|
+
return score;
|
|
13934
|
+
}
|
|
13935
|
+
/** Sub-check for governance runtime infrastructure. */
|
|
13936
|
+
checkGovernanceInfra(score, findings) {
|
|
13937
|
+
const hasPolicyFirewall = countPatternInDir(join14(SRC_ROOT, "security"), /\.ts$/, /PolicyGate|policyFirewall/g) > 0;
|
|
13938
|
+
if (hasPolicyFirewall) {
|
|
13939
|
+
score += 1;
|
|
13940
|
+
} else {
|
|
13941
|
+
findings.push(
|
|
13942
|
+
this.finding(
|
|
13943
|
+
"governanceIntegration",
|
|
13944
|
+
"warning",
|
|
13945
|
+
"No policy firewall detected in security layer",
|
|
13946
|
+
0
|
|
13947
|
+
)
|
|
13948
|
+
);
|
|
13949
|
+
}
|
|
13950
|
+
const hasRateLimiter = countPatternInDir(SRC_ROOT, /\.ts$/, /RateLimiter|rateLimiter/g) > 0;
|
|
13951
|
+
if (hasRateLimiter) {
|
|
13952
|
+
score += 1;
|
|
13953
|
+
}
|
|
13954
|
+
return score;
|
|
13955
|
+
}
|
|
13956
|
+
/** Helper to create a FitnessFinding with defaults. */
|
|
13957
|
+
finding(dimension, severity, description, pointsDeducted, suggestion) {
|
|
13958
|
+
const base = { dimension, severity, description, pointsDeducted };
|
|
13959
|
+
if (suggestion !== void 0) {
|
|
13960
|
+
return { ...base, suggestion };
|
|
13961
|
+
}
|
|
13962
|
+
return base;
|
|
13484
13963
|
}
|
|
13485
13964
|
};
|
|
13486
13965
|
function createFitnessScoreCalculator(logger17) {
|
|
@@ -13920,11 +14399,11 @@ async function releaseNotesCommand(args) {
|
|
|
13920
14399
|
}
|
|
13921
14400
|
|
|
13922
14401
|
// src/cli/release-validate-command.ts
|
|
13923
|
-
import { readFileSync as
|
|
14402
|
+
import { readFileSync as readFileSync8 } from "fs";
|
|
13924
14403
|
|
|
13925
14404
|
// src/cli/release-validate-helpers.ts
|
|
13926
14405
|
import { execSync as execSync3 } from "child_process";
|
|
13927
|
-
import { existsSync as
|
|
14406
|
+
import { existsSync as existsSync13, readFileSync as readFileSync7 } from "fs";
|
|
13928
14407
|
async function validateSecurity(options) {
|
|
13929
14408
|
const startTime = Date.now();
|
|
13930
14409
|
const findings = [];
|
|
@@ -13943,7 +14422,7 @@ async function validateSecurity(options) {
|
|
|
13943
14422
|
remediation: "Run npm audit fix or review and update vulnerable dependencies."
|
|
13944
14423
|
});
|
|
13945
14424
|
}
|
|
13946
|
-
if (
|
|
14425
|
+
if (existsSync13(".env")) {
|
|
13947
14426
|
findings.push({
|
|
13948
14427
|
severity: "error",
|
|
13949
14428
|
category: "security",
|
|
@@ -14039,7 +14518,7 @@ async function validateArchitecture2(options) {
|
|
|
14039
14518
|
async function validateDocumentation(options) {
|
|
14040
14519
|
const startTime = Date.now();
|
|
14041
14520
|
const findings = [];
|
|
14042
|
-
if (!
|
|
14521
|
+
if (!existsSync13("CHANGELOG.md")) {
|
|
14043
14522
|
findings.push({
|
|
14044
14523
|
severity: "error",
|
|
14045
14524
|
category: "docs",
|
|
@@ -14047,7 +14526,7 @@ async function validateDocumentation(options) {
|
|
|
14047
14526
|
description: "No CHANGELOG.md found in repository root.",
|
|
14048
14527
|
remediation: "Create CHANGELOG.md following Keep a Changelog format."
|
|
14049
14528
|
});
|
|
14050
|
-
} else if (!
|
|
14529
|
+
} else if (!readFileSync7("CHANGELOG.md", "utf-8").includes(options.version)) {
|
|
14051
14530
|
findings.push({
|
|
14052
14531
|
severity: "warning",
|
|
14053
14532
|
category: "docs",
|
|
@@ -14057,7 +14536,7 @@ async function validateDocumentation(options) {
|
|
|
14057
14536
|
});
|
|
14058
14537
|
}
|
|
14059
14538
|
const pkgCl = "packages/nexus-agents/CHANGELOG.md";
|
|
14060
|
-
if (
|
|
14539
|
+
if (existsSync13(pkgCl) && !readFileSync7(pkgCl, "utf-8").includes(options.version)) {
|
|
14061
14540
|
findings.push({
|
|
14062
14541
|
severity: "warning",
|
|
14063
14542
|
category: "docs",
|
|
@@ -14066,7 +14545,7 @@ async function validateDocumentation(options) {
|
|
|
14066
14545
|
remediation: "Run pnpm changeset:version to generate changelog from pending changesets."
|
|
14067
14546
|
});
|
|
14068
14547
|
}
|
|
14069
|
-
if (!
|
|
14548
|
+
if (!existsSync13("README.md")) {
|
|
14070
14549
|
findings.push({
|
|
14071
14550
|
severity: "error",
|
|
14072
14551
|
category: "docs",
|
|
@@ -14075,8 +14554,8 @@ async function validateDocumentation(options) {
|
|
|
14075
14554
|
remediation: "Create README.md with project overview and usage instructions."
|
|
14076
14555
|
});
|
|
14077
14556
|
}
|
|
14078
|
-
if (
|
|
14079
|
-
const claudeMd =
|
|
14557
|
+
if (existsSync13("CLAUDE.md")) {
|
|
14558
|
+
const claudeMd = readFileSync7("CLAUDE.md", "utf-8");
|
|
14080
14559
|
const match = claudeMd.match(/Governance Version: (\d{4}-\d{2}-\d{2})/);
|
|
14081
14560
|
if (match?.[1]) {
|
|
14082
14561
|
const governanceDate = new Date(match[1]);
|
|
@@ -14184,7 +14663,7 @@ var DEFAULT_OPTIONS2 = {
|
|
|
14184
14663
|
};
|
|
14185
14664
|
function readVersionFromPackage() {
|
|
14186
14665
|
try {
|
|
14187
|
-
const raw = JSON.parse(
|
|
14666
|
+
const raw = JSON.parse(readFileSync8("package.json", "utf-8"));
|
|
14188
14667
|
if (typeof raw === "object" && raw !== null && "version" in raw) {
|
|
14189
14668
|
const pkg = raw;
|
|
14190
14669
|
return typeof pkg.version === "string" ? pkg.version : "unknown";
|
|
@@ -14298,7 +14777,7 @@ async function releaseValidateCommand(args) {
|
|
|
14298
14777
|
}
|
|
14299
14778
|
|
|
14300
14779
|
// src/cli/release-announce-command.ts
|
|
14301
|
-
import { readFileSync as
|
|
14780
|
+
import { readFileSync as readFileSync9, existsSync as existsSync14 } from "fs";
|
|
14302
14781
|
|
|
14303
14782
|
// src/cli/release-announce-types.ts
|
|
14304
14783
|
var BLUESKY_LIMITS = {
|
|
@@ -14323,7 +14802,7 @@ function getBlueskyConfig() {
|
|
|
14323
14802
|
}
|
|
14324
14803
|
async function loadAtproto() {
|
|
14325
14804
|
try {
|
|
14326
|
-
const mod = await import("./dist-
|
|
14805
|
+
const mod = await import("./dist-7PQR2BQB.js");
|
|
14327
14806
|
return { AtpAgent: mod.AtpAgent, RichText: mod.RichText };
|
|
14328
14807
|
} catch {
|
|
14329
14808
|
return void 0;
|
|
@@ -14365,8 +14844,8 @@ var DEFAULT_OPTIONS3 = {
|
|
|
14365
14844
|
verbose: false
|
|
14366
14845
|
};
|
|
14367
14846
|
function extractHighlightsFromChangelog(version) {
|
|
14368
|
-
if (!
|
|
14369
|
-
const changelog =
|
|
14847
|
+
if (!existsSync14("CHANGELOG.md")) return [];
|
|
14848
|
+
const changelog = readFileSync9("CHANGELOG.md", "utf-8");
|
|
14370
14849
|
const escaped = version.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
14371
14850
|
const versionRegex = new RegExp(`## \\[${escaped}\\][^#]*`, "s");
|
|
14372
14851
|
const match = changelog.match(versionRegex);
|
|
@@ -14628,7 +15107,7 @@ async function releaseAnnounceCommand(args) {
|
|
|
14628
15107
|
version = args.options.version;
|
|
14629
15108
|
} else {
|
|
14630
15109
|
try {
|
|
14631
|
-
const pkg = JSON.parse(
|
|
15110
|
+
const pkg = JSON.parse(readFileSync9("package.json", "utf-8"));
|
|
14632
15111
|
if (!pkg.version) {
|
|
14633
15112
|
console.error(`${colors.red}Error: Could not determine version${colors.reset}`);
|
|
14634
15113
|
return 1;
|
|
@@ -14655,8 +15134,8 @@ async function releaseAnnounceCommand(args) {
|
|
|
14655
15134
|
}
|
|
14656
15135
|
|
|
14657
15136
|
// src/cli/scaffold.ts
|
|
14658
|
-
import * as
|
|
14659
|
-
import * as
|
|
15137
|
+
import * as fs16 from "fs";
|
|
15138
|
+
import * as path20 from "path";
|
|
14660
15139
|
|
|
14661
15140
|
// src/cli/scaffold-templates.ts
|
|
14662
15141
|
function toolImportsAndSchema(name, pascal) {
|
|
@@ -14683,7 +15162,7 @@ import { createSecureHandler, type HandlerContext } from '../middleware/secure-h
|
|
|
14683
15162
|
*/
|
|
14684
15163
|
export const ${pascal}InputSchema = z.object({
|
|
14685
15164
|
input: z.string().min(1).describe('Primary input for ${name} operation'),
|
|
14686
|
-
options: z.record(z.unknown()).optional().describe('Additional options'),
|
|
15165
|
+
options: z.record(z.string(), z.unknown()).optional().describe('Additional options'),
|
|
14687
15166
|
});
|
|
14688
15167
|
|
|
14689
15168
|
export type ${pascal}Input = z.infer<typeof ${pascal}InputSchema>;
|
|
@@ -14992,17 +15471,17 @@ ${lines}`;
|
|
|
14992
15471
|
function writeFiles(files) {
|
|
14993
15472
|
const createdPaths = [];
|
|
14994
15473
|
for (const file of files) {
|
|
14995
|
-
const fullPath =
|
|
14996
|
-
const dir =
|
|
14997
|
-
|
|
14998
|
-
if (
|
|
15474
|
+
const fullPath = path20.resolve(process.cwd(), file.path);
|
|
15475
|
+
const dir = path20.dirname(fullPath);
|
|
15476
|
+
fs16.mkdirSync(dir, { recursive: true });
|
|
15477
|
+
if (fs16.existsSync(fullPath)) {
|
|
14999
15478
|
return {
|
|
15000
15479
|
success: false,
|
|
15001
15480
|
filesCreated: createdPaths,
|
|
15002
15481
|
message: `File already exists: ${file.path}. Use a different name.`
|
|
15003
15482
|
};
|
|
15004
15483
|
}
|
|
15005
|
-
|
|
15484
|
+
fs16.writeFileSync(fullPath, file.content, "utf-8");
|
|
15006
15485
|
createdPaths.push(file.path);
|
|
15007
15486
|
}
|
|
15008
15487
|
const lines = createdPaths.map((p) => ` ${p}`).join("\n");
|
|
@@ -15549,13 +16028,13 @@ function formatEvalDetails(baseline, reflective, improvement, datasetSize) {
|
|
|
15549
16028
|
}
|
|
15550
16029
|
|
|
15551
16030
|
// src/cli/auth-command.ts
|
|
15552
|
-
import { existsSync as
|
|
16031
|
+
import { existsSync as existsSync16, statSync as statSync2, chmodSync } from "fs";
|
|
15553
16032
|
function isValidAuthSubcommand(value) {
|
|
15554
16033
|
return value === "init" || value === "show" || value === "rotate" || value === "help";
|
|
15555
16034
|
}
|
|
15556
16035
|
function getFilePermissions(filePath) {
|
|
15557
16036
|
try {
|
|
15558
|
-
const stats =
|
|
16037
|
+
const stats = statSync2(filePath);
|
|
15559
16038
|
return (stats.mode & 511).toString(8);
|
|
15560
16039
|
} catch {
|
|
15561
16040
|
return void 0;
|
|
@@ -15569,7 +16048,7 @@ function fixPermissions(filePath) {
|
|
|
15569
16048
|
}
|
|
15570
16049
|
function runAuthInit(options) {
|
|
15571
16050
|
const tokenFile = options.tokenFile ?? getDefaultTokenPath();
|
|
15572
|
-
const tokenExists =
|
|
16051
|
+
const tokenExists = existsSync16(tokenFile);
|
|
15573
16052
|
if (tokenExists && options.force !== true) {
|
|
15574
16053
|
return {
|
|
15575
16054
|
success: false,
|
|
@@ -15593,7 +16072,7 @@ function runAuthInit(options) {
|
|
|
15593
16072
|
}
|
|
15594
16073
|
function runAuthShow(options) {
|
|
15595
16074
|
const tokenFile = options.tokenFile ?? getDefaultTokenPath();
|
|
15596
|
-
const tokenExists =
|
|
16075
|
+
const tokenExists = existsSync16(tokenFile);
|
|
15597
16076
|
const token = readStoredToken(tokenFile);
|
|
15598
16077
|
if (!tokenExists || token === void 0) {
|
|
15599
16078
|
return {
|
|
@@ -15614,7 +16093,7 @@ function runAuthShow(options) {
|
|
|
15614
16093
|
}
|
|
15615
16094
|
function runAuthRotate(options) {
|
|
15616
16095
|
const tokenFile = options.tokenFile ?? getDefaultTokenPath();
|
|
15617
|
-
const tokenExists =
|
|
16096
|
+
const tokenExists = existsSync16(tokenFile);
|
|
15618
16097
|
if (!tokenExists) {
|
|
15619
16098
|
return {
|
|
15620
16099
|
success: false,
|
|
@@ -15650,14 +16129,14 @@ function runAuthCommand(options) {
|
|
|
15650
16129
|
success: true,
|
|
15651
16130
|
operation: "help",
|
|
15652
16131
|
tokenFile: getDefaultTokenPath(),
|
|
15653
|
-
tokenExists:
|
|
16132
|
+
tokenExists: existsSync16(getDefaultTokenPath())
|
|
15654
16133
|
};
|
|
15655
16134
|
default:
|
|
15656
16135
|
return {
|
|
15657
16136
|
success: false,
|
|
15658
16137
|
operation: "help",
|
|
15659
16138
|
tokenFile: getDefaultTokenPath(),
|
|
15660
|
-
tokenExists:
|
|
16139
|
+
tokenExists: existsSync16(getDefaultTokenPath()),
|
|
15661
16140
|
error: `Unknown subcommand: ${String(subcommand)}`
|
|
15662
16141
|
};
|
|
15663
16142
|
}
|
|
@@ -15773,7 +16252,7 @@ function authCommand(subcommand, options) {
|
|
|
15773
16252
|
|
|
15774
16253
|
// src/cli-help-text.ts
|
|
15775
16254
|
var HELP_TEXT = `
|
|
15776
|
-
nexus-agents -
|
|
16255
|
+
nexus-agents - Intelligent orchestration platform for AI coding tools
|
|
15777
16256
|
|
|
15778
16257
|
USAGE:
|
|
15779
16258
|
nexus-agents [OPTIONS]
|
|
@@ -15814,6 +16293,7 @@ COMMANDS:
|
|
|
15814
16293
|
capabilities Show model capabilities matrix (modalities, tools, features)
|
|
15815
16294
|
status At-a-glance project health dashboard (fitness, adapters, version)
|
|
15816
16295
|
health Swarm health metrics dashboard (utilization, routing accuracy, failures)
|
|
16296
|
+
validate Run unified validation (doctor + fitness + config)
|
|
15817
16297
|
auth Manage MCP authentication tokens (init, show, rotate)
|
|
15818
16298
|
|
|
15819
16299
|
OPTIONS:
|
|
@@ -16415,15 +16895,123 @@ function isValidCommand(value) {
|
|
|
16415
16895
|
"e2e-eval",
|
|
16416
16896
|
"routing-ab",
|
|
16417
16897
|
"memory-eval",
|
|
16418
|
-
"health"
|
|
16898
|
+
"health",
|
|
16899
|
+
"validate"
|
|
16419
16900
|
];
|
|
16420
16901
|
return validCommands.includes(value);
|
|
16421
16902
|
}
|
|
16422
16903
|
|
|
16423
16904
|
// src/cli-commands-handlers.ts
|
|
16424
|
-
import { existsSync as
|
|
16425
|
-
import { join as
|
|
16426
|
-
import { homedir as
|
|
16905
|
+
import { existsSync as existsSync18 } from "fs";
|
|
16906
|
+
import { join as join16 } from "path";
|
|
16907
|
+
import { homedir as homedir4 } from "os";
|
|
16908
|
+
|
|
16909
|
+
// src/mcp/tools/dev-pipeline-tool.ts
|
|
16910
|
+
import { z as z7 } from "zod";
|
|
16911
|
+
import * as fs17 from "fs";
|
|
16912
|
+
import * as path21 from "path";
|
|
16913
|
+
var DevPipelineInputSchema = z7.object({
|
|
16914
|
+
/** Direct task instructions. */
|
|
16915
|
+
task: z7.string().max(1e4).optional().describe("Direct task instructions (what to build)"),
|
|
16916
|
+
/** Path to a plan file (.md, .yaml, .txt) to use as input. */
|
|
16917
|
+
planFile: z7.string().max(500).optional().describe("Path to a plan/spec file to use as input"),
|
|
16918
|
+
/** Whether to run in dry-run mode (plan+vote only, no implementation). */
|
|
16919
|
+
dryRun: z7.boolean().default(false).describe("If true, stop after plan+vote (no implementation)"),
|
|
16920
|
+
/** Maximum vote iterations before proceeding (default: 3). */
|
|
16921
|
+
maxVoteIterations: z7.number().int().min(1).max(5).default(3).describe("Max plan\u2192vote iterations"),
|
|
16922
|
+
/** Maximum QA iterations per task (default: 3). */
|
|
16923
|
+
maxQaIterations: z7.number().int().min(1).max(5).default(3).describe("Max QA review iterations per task"),
|
|
16924
|
+
/** Working directory for the pipeline (default: cwd). Used for security scan and context. */
|
|
16925
|
+
workingDir: z7.string().max(500).optional().describe("Working directory (default: cwd)"),
|
|
16926
|
+
/** GitHub issue number to track progress on. Updates posted as comments. */
|
|
16927
|
+
issueNumber: z7.number().int().positive().optional().describe("GitHub issue to post progress to"),
|
|
16928
|
+
/** GitHub repo (owner/name) for issue tracking. */
|
|
16929
|
+
repo: z7.string().max(200).optional().describe("GitHub repo for issue tracking (e.g., owner/repo)"),
|
|
16930
|
+
/** Task tracking backend: github, gitlab, or json (default: json). */
|
|
16931
|
+
trackerBackend: z7.enum(["github", "gitlab", "json"]).default("json").describe("Task tracking backend for issue creation"),
|
|
16932
|
+
/** Labels to apply to created issues. */
|
|
16933
|
+
labels: z7.array(z7.string()).optional().describe("Labels for created issues"),
|
|
16934
|
+
/** Session ID for checkpoint/resume. Enables crash recovery. */
|
|
16935
|
+
sessionId: z7.string().max(128).regex(/^[a-zA-Z0-9_-]+$/).optional().describe("Session ID for checkpoint/resume (crash recovery)"),
|
|
16936
|
+
/** When true, use simulated votes instead of real CLI consensus (for testing). */
|
|
16937
|
+
simulateVotes: z7.boolean().default(false).describe("Use simulated votes (for testing without real CLIs)"),
|
|
16938
|
+
/** Pipeline execution mode. */
|
|
16939
|
+
mode: z7.enum(["autonomous", "harness"]).default("autonomous").describe(
|
|
16940
|
+
"'autonomous': full pipeline. 'harness': stops after decompose, returns tasks for caller to implement."
|
|
16941
|
+
)
|
|
16942
|
+
});
|
|
16943
|
+
function resolveTaskInput(input) {
|
|
16944
|
+
if (input.task !== void 0 && input.task.trim() !== "") {
|
|
16945
|
+
return input.task;
|
|
16946
|
+
}
|
|
16947
|
+
if (input.planFile !== void 0) {
|
|
16948
|
+
const resolved = path21.resolve(input.planFile);
|
|
16949
|
+
if (!fs17.existsSync(resolved)) {
|
|
16950
|
+
throw new Error(`Plan file not found: ${resolved}`);
|
|
16951
|
+
}
|
|
16952
|
+
return fs17.readFileSync(resolved, "utf-8");
|
|
16953
|
+
}
|
|
16954
|
+
throw new Error("Either task or planFile must be provided");
|
|
16955
|
+
}
|
|
16956
|
+
async function createStages(input) {
|
|
16957
|
+
const backendChoice = input.trackerBackend;
|
|
16958
|
+
const backend = backendChoice === "json" && input.repo !== void 0 ? await detectBackend() : backendChoice;
|
|
16959
|
+
const tracker = input.repo !== void 0 ? createTaskTracker({ backend, repo: input.repo, labels: input.labels }) : void 0;
|
|
16960
|
+
return createAgentStages({
|
|
16961
|
+
scanTarget: input.workingDir,
|
|
16962
|
+
simulateVotes: input.simulateVotes,
|
|
16963
|
+
issueNumber: input.issueNumber,
|
|
16964
|
+
repo: input.repo,
|
|
16965
|
+
tracker
|
|
16966
|
+
});
|
|
16967
|
+
}
|
|
16968
|
+
function buildStructuredOutput(result) {
|
|
16969
|
+
return {
|
|
16970
|
+
completed: result.completed,
|
|
16971
|
+
securityPassed: result.securityPassed,
|
|
16972
|
+
voteIterations: result.voteIterations,
|
|
16973
|
+
qaIterations: result.qaIterations,
|
|
16974
|
+
plan: result.plan,
|
|
16975
|
+
tasks: result.tasks.map((t) => ({
|
|
16976
|
+
id: t.id,
|
|
16977
|
+
title: t.title,
|
|
16978
|
+
status: t.status,
|
|
16979
|
+
implementation: t.implementation ?? null,
|
|
16980
|
+
feedback: t.feedback ?? null
|
|
16981
|
+
}))
|
|
16982
|
+
};
|
|
16983
|
+
}
|
|
16984
|
+
function registerDevPipelineTool(server, _deps) {
|
|
16985
|
+
server.tool("run_dev_pipeline", DevPipelineInputSchema.shape, async (args) => {
|
|
16986
|
+
const input = DevPipelineInputSchema.parse(args);
|
|
16987
|
+
try {
|
|
16988
|
+
const taskText = resolveTaskInput(input);
|
|
16989
|
+
const stages = await createStages(input);
|
|
16990
|
+
const pipelineOptions = {
|
|
16991
|
+
...input.sessionId !== void 0 ? { sessionId: input.sessionId } : {},
|
|
16992
|
+
...input.dryRun ? { dryRun: true } : {},
|
|
16993
|
+
...input.mode === "harness" ? { mode: "harness" } : {}
|
|
16994
|
+
};
|
|
16995
|
+
const hasOptions = Object.keys(pipelineOptions).length > 0;
|
|
16996
|
+
const result = await runDevPipeline(
|
|
16997
|
+
taskText,
|
|
16998
|
+
stages,
|
|
16999
|
+
hasOptions ? pipelineOptions : void 0
|
|
17000
|
+
);
|
|
17001
|
+
flushPipelineMemory();
|
|
17002
|
+
const structured = buildStructuredOutput(result);
|
|
17003
|
+
return {
|
|
17004
|
+
content: [{ type: "text", text: JSON.stringify(structured, null, 2) }],
|
|
17005
|
+
structuredContent: structured
|
|
17006
|
+
};
|
|
17007
|
+
} catch (error) {
|
|
17008
|
+
return {
|
|
17009
|
+
content: [{ type: "text", text: `Pipeline error: ${getErrorMessage(error)}` }],
|
|
17010
|
+
isError: true
|
|
17011
|
+
};
|
|
17012
|
+
}
|
|
17013
|
+
});
|
|
17014
|
+
}
|
|
16427
17015
|
|
|
16428
17016
|
// src/cli-server-stpa.ts
|
|
16429
17017
|
var TOOL_DEFINITIONS = [
|
|
@@ -16605,6 +17193,206 @@ function runStpaSafetyAnalysis(logger17, failOnHighSeverity) {
|
|
|
16605
17193
|
}
|
|
16606
17194
|
}
|
|
16607
17195
|
|
|
17196
|
+
// src/mcp/gateway/upstream-client.ts
|
|
17197
|
+
import { Client } from "@modelcontextprotocol/sdk/client/index.js";
|
|
17198
|
+
import { StdioClientTransport } from "@modelcontextprotocol/sdk/client/stdio.js";
|
|
17199
|
+
function resolveEnv(env) {
|
|
17200
|
+
if (env === void 0) return {};
|
|
17201
|
+
const resolved = {};
|
|
17202
|
+
for (const [key, val] of Object.entries(env)) {
|
|
17203
|
+
const match = /^\{env:(\w+)\}$/.exec(val);
|
|
17204
|
+
if (match?.[1] !== void 0) {
|
|
17205
|
+
const envKey = match[1];
|
|
17206
|
+
const envVal = process.env[envKey];
|
|
17207
|
+
if (envVal !== void 0) resolved[key] = envVal;
|
|
17208
|
+
} else {
|
|
17209
|
+
resolved[key] = val;
|
|
17210
|
+
}
|
|
17211
|
+
}
|
|
17212
|
+
return resolved;
|
|
17213
|
+
}
|
|
17214
|
+
var UpstreamClient = class {
|
|
17215
|
+
name;
|
|
17216
|
+
config;
|
|
17217
|
+
log;
|
|
17218
|
+
client = null;
|
|
17219
|
+
transport = null;
|
|
17220
|
+
state = "disconnected";
|
|
17221
|
+
tools = [];
|
|
17222
|
+
constructor(config, logger17) {
|
|
17223
|
+
this.name = config.name;
|
|
17224
|
+
this.config = config;
|
|
17225
|
+
this.log = logger17 ?? createLogger({ component: `upstream:${config.name}` });
|
|
17226
|
+
}
|
|
17227
|
+
/** Connect to the upstream server. Idempotent. */
|
|
17228
|
+
async connect() {
|
|
17229
|
+
if (this.state === "connected") return;
|
|
17230
|
+
if (this.state === "connecting") return;
|
|
17231
|
+
this.state = "connecting";
|
|
17232
|
+
try {
|
|
17233
|
+
const resolvedEnv = resolveEnv(this.config.env);
|
|
17234
|
+
const baseEnv = {};
|
|
17235
|
+
for (const [k, v] of Object.entries(process.env)) {
|
|
17236
|
+
if (v !== void 0) baseEnv[k] = v;
|
|
17237
|
+
}
|
|
17238
|
+
this.transport = new StdioClientTransport({
|
|
17239
|
+
command: this.config.command,
|
|
17240
|
+
args: [...this.config.args],
|
|
17241
|
+
env: { ...baseEnv, ...resolvedEnv }
|
|
17242
|
+
});
|
|
17243
|
+
this.client = new Client(
|
|
17244
|
+
{ name: `nexus-upstream-${this.name}`, version: "1.0.0" },
|
|
17245
|
+
{ capabilities: {} }
|
|
17246
|
+
);
|
|
17247
|
+
await this.client.connect(this.transport);
|
|
17248
|
+
const result = await this.client.listTools();
|
|
17249
|
+
this.tools = result.tools;
|
|
17250
|
+
this.state = "connected";
|
|
17251
|
+
this.log.info("Upstream server connected", {
|
|
17252
|
+
name: this.name,
|
|
17253
|
+
toolCount: this.tools.length,
|
|
17254
|
+
tools: this.tools.map((t) => t.name)
|
|
17255
|
+
});
|
|
17256
|
+
} catch (error) {
|
|
17257
|
+
this.state = "failed";
|
|
17258
|
+
const msg = error instanceof Error ? error.message : String(error);
|
|
17259
|
+
this.log.warn("Upstream server connection failed", {
|
|
17260
|
+
name: this.name,
|
|
17261
|
+
error: msg
|
|
17262
|
+
});
|
|
17263
|
+
throw error;
|
|
17264
|
+
}
|
|
17265
|
+
}
|
|
17266
|
+
/** Get available tools (prefixed with server name). */
|
|
17267
|
+
getTools() {
|
|
17268
|
+
return this.tools.map((t) => ({
|
|
17269
|
+
...t,
|
|
17270
|
+
name: `${this.name}.${t.name}`,
|
|
17271
|
+
description: `[${this.name}] ${t.description ?? ""}`
|
|
17272
|
+
}));
|
|
17273
|
+
}
|
|
17274
|
+
/** Call a tool on the upstream server. Returns the raw SDK result. */
|
|
17275
|
+
async callTool(toolName, args) {
|
|
17276
|
+
if (this.client === null || this.state !== "connected") {
|
|
17277
|
+
if (this.config.lazy) {
|
|
17278
|
+
await this.connect();
|
|
17279
|
+
} else {
|
|
17280
|
+
throw new Error(`Upstream server ${this.name} not connected`);
|
|
17281
|
+
}
|
|
17282
|
+
}
|
|
17283
|
+
const activeClient = this.client;
|
|
17284
|
+
if (activeClient === null) throw new Error(`Upstream server ${this.name} not connected`);
|
|
17285
|
+
return activeClient.callTool({ name: toolName, arguments: args });
|
|
17286
|
+
}
|
|
17287
|
+
/** Check if the server is healthy. */
|
|
17288
|
+
async ping() {
|
|
17289
|
+
if (this.client === null || this.state !== "connected") return false;
|
|
17290
|
+
try {
|
|
17291
|
+
await this.client.ping();
|
|
17292
|
+
return true;
|
|
17293
|
+
} catch {
|
|
17294
|
+
return false;
|
|
17295
|
+
}
|
|
17296
|
+
}
|
|
17297
|
+
/** Gracefully shut down the connection. */
|
|
17298
|
+
async shutdown() {
|
|
17299
|
+
if (this.client !== null) {
|
|
17300
|
+
try {
|
|
17301
|
+
await this.client.close();
|
|
17302
|
+
} catch {
|
|
17303
|
+
}
|
|
17304
|
+
this.client = null;
|
|
17305
|
+
}
|
|
17306
|
+
if (this.transport !== null) {
|
|
17307
|
+
try {
|
|
17308
|
+
await this.transport.close();
|
|
17309
|
+
} catch {
|
|
17310
|
+
}
|
|
17311
|
+
this.transport = null;
|
|
17312
|
+
}
|
|
17313
|
+
this.state = "disconnected";
|
|
17314
|
+
this.tools = [];
|
|
17315
|
+
this.log.info("Upstream server disconnected", { name: this.name });
|
|
17316
|
+
}
|
|
17317
|
+
/** Get current connection state. */
|
|
17318
|
+
getState() {
|
|
17319
|
+
return this.state;
|
|
17320
|
+
}
|
|
17321
|
+
};
|
|
17322
|
+
var UpstreamClientManager = class {
|
|
17323
|
+
clients = /* @__PURE__ */ new Map();
|
|
17324
|
+
log;
|
|
17325
|
+
constructor(logger17) {
|
|
17326
|
+
this.log = logger17 ?? createLogger({ component: "UpstreamClientManager" });
|
|
17327
|
+
}
|
|
17328
|
+
/** Register upstream servers from config. */
|
|
17329
|
+
registerServers(configs) {
|
|
17330
|
+
for (const config of configs) {
|
|
17331
|
+
if (this.clients.has(config.name)) {
|
|
17332
|
+
this.log.warn("Duplicate upstream server name", { name: config.name });
|
|
17333
|
+
continue;
|
|
17334
|
+
}
|
|
17335
|
+
this.clients.set(config.name, new UpstreamClient(config, this.log));
|
|
17336
|
+
this.log.info("Registered upstream server", {
|
|
17337
|
+
name: config.name,
|
|
17338
|
+
command: config.command,
|
|
17339
|
+
lazy: config.lazy
|
|
17340
|
+
});
|
|
17341
|
+
}
|
|
17342
|
+
}
|
|
17343
|
+
/** Connect all non-lazy servers. */
|
|
17344
|
+
async connectEager() {
|
|
17345
|
+
for (const client of this.clients.values()) {
|
|
17346
|
+
if (client.getState() === "disconnected") {
|
|
17347
|
+
try {
|
|
17348
|
+
await client.connect();
|
|
17349
|
+
} catch {
|
|
17350
|
+
}
|
|
17351
|
+
}
|
|
17352
|
+
}
|
|
17353
|
+
}
|
|
17354
|
+
/** Get all available tools across all connected upstream servers. */
|
|
17355
|
+
getAllTools() {
|
|
17356
|
+
const tools = [];
|
|
17357
|
+
for (const client of this.clients.values()) {
|
|
17358
|
+
if (client.getState() === "connected") {
|
|
17359
|
+
tools.push(...client.getTools());
|
|
17360
|
+
}
|
|
17361
|
+
}
|
|
17362
|
+
return tools;
|
|
17363
|
+
}
|
|
17364
|
+
/** Route a prefixed tool call to the appropriate upstream server. */
|
|
17365
|
+
async callTool(prefixedName, args) {
|
|
17366
|
+
const dotIndex = prefixedName.indexOf(".");
|
|
17367
|
+
if (dotIndex === -1) return null;
|
|
17368
|
+
const serverName = prefixedName.slice(0, dotIndex);
|
|
17369
|
+
const toolName = prefixedName.slice(dotIndex + 1);
|
|
17370
|
+
const client = this.clients.get(serverName);
|
|
17371
|
+
if (client === void 0) return null;
|
|
17372
|
+
return client.callTool(toolName, args);
|
|
17373
|
+
}
|
|
17374
|
+
/** Check if a tool name is an upstream prefixed tool. */
|
|
17375
|
+
isUpstreamTool(name) {
|
|
17376
|
+
const dotIndex = name.indexOf(".");
|
|
17377
|
+
if (dotIndex === -1) return false;
|
|
17378
|
+
return this.clients.has(name.slice(0, dotIndex));
|
|
17379
|
+
}
|
|
17380
|
+
/** Shut down all upstream connections. */
|
|
17381
|
+
async shutdownAll() {
|
|
17382
|
+
const promises = [...this.clients.values()].map((c) => c.shutdown());
|
|
17383
|
+
await Promise.allSettled(promises);
|
|
17384
|
+
this.clients.clear();
|
|
17385
|
+
this.log.info("All upstream servers shut down");
|
|
17386
|
+
}
|
|
17387
|
+
/** Get health status of all upstream servers. */
|
|
17388
|
+
getStatus() {
|
|
17389
|
+
return [...this.clients.entries()].map(([name, client]) => ({
|
|
17390
|
+
name,
|
|
17391
|
+
state: client.getState()
|
|
17392
|
+
}));
|
|
17393
|
+
}
|
|
17394
|
+
};
|
|
17395
|
+
|
|
16608
17396
|
// src/cli-server-tools.ts
|
|
16609
17397
|
var OrchestratorUnavailableError = class extends NexusError {
|
|
16610
17398
|
constructor(message) {
|
|
@@ -16718,6 +17506,10 @@ function registerResearchTools(ctx) {
|
|
|
16718
17506
|
...researchDeps,
|
|
16719
17507
|
rateLimiter: ctx.rateLimiterFactory.getForTool("research_add")
|
|
16720
17508
|
});
|
|
17509
|
+
registerResearchAddSourceTool(ctx.server, {
|
|
17510
|
+
...researchDeps,
|
|
17511
|
+
rateLimiter: ctx.rateLimiterFactory.getForTool("research_add_source")
|
|
17512
|
+
});
|
|
16721
17513
|
registerResearchDiscoverTool(ctx.server, {
|
|
16722
17514
|
...researchDeps,
|
|
16723
17515
|
rateLimiter: ctx.rateLimiterFactory.getForTool("research_discover")
|
|
@@ -16785,6 +17577,33 @@ function registerOrchestrateToolSafe(ctx) {
|
|
|
16785
17577
|
});
|
|
16786
17578
|
}
|
|
16787
17579
|
}
|
|
17580
|
+
async function initUpstreamServers(gatewayConfig, server, logger17) {
|
|
17581
|
+
const upstreamServers = gatewayConfig?.["upstreamServers"];
|
|
17582
|
+
if (upstreamServers === void 0 || upstreamServers.length === 0) return;
|
|
17583
|
+
const manager = new UpstreamClientManager(logger17);
|
|
17584
|
+
manager.registerServers(upstreamServers);
|
|
17585
|
+
await manager.connectEager();
|
|
17586
|
+
const tools = manager.getAllTools();
|
|
17587
|
+
logger17.info("Upstream MCP servers initialized", {
|
|
17588
|
+
servers: upstreamServers.length,
|
|
17589
|
+
tools: tools.length
|
|
17590
|
+
});
|
|
17591
|
+
const { z: z8 } = await import("zod");
|
|
17592
|
+
const passthroughSchema = z8.looseObject({});
|
|
17593
|
+
for (const tool of tools) {
|
|
17594
|
+
const toolName = tool.name;
|
|
17595
|
+
const desc = tool.description ?? `Upstream tool: ${toolName}`;
|
|
17596
|
+
server.registerTool(
|
|
17597
|
+
toolName,
|
|
17598
|
+
{ description: desc, inputSchema: passthroughSchema },
|
|
17599
|
+
async (args) => {
|
|
17600
|
+
const upstreamResult = await manager.callTool(toolName, args);
|
|
17601
|
+
const text = upstreamResult !== null ? JSON.stringify(upstreamResult) : "Upstream tool not found";
|
|
17602
|
+
return { content: [{ type: "text", text }] };
|
|
17603
|
+
}
|
|
17604
|
+
);
|
|
17605
|
+
}
|
|
17606
|
+
}
|
|
16788
17607
|
function maybeRunStpaAnalysis(options, logger17) {
|
|
16789
17608
|
const enableStpa = options.enableStpaSafetyAnalysis ?? false;
|
|
16790
17609
|
if (!enableStpa) return;
|
|
@@ -16859,7 +17678,10 @@ var STANDALONE_TOOLS = [
|
|
|
16859
17678
|
{ name: "run_graph_workflow", register: registerRunGraphWorkflowTool },
|
|
16860
17679
|
{ name: "execute_spec", register: registerExecuteSpecTool },
|
|
16861
17680
|
{ name: "list_experts", register: registerListExpertsTool },
|
|
16862
|
-
{ name: "query_trace", register: registerQueryTraceTool }
|
|
17681
|
+
{ name: "query_trace", register: registerQueryTraceTool },
|
|
17682
|
+
{ name: "extract_symbols", register: registerExtractSymbolsTool },
|
|
17683
|
+
{ name: "search_codebase", register: registerSearchCodebaseTool },
|
|
17684
|
+
{ name: "run_dev_pipeline", register: registerDevPipelineTool }
|
|
16863
17685
|
];
|
|
16864
17686
|
function registerToolCategories(ctx) {
|
|
16865
17687
|
const allowlist = ctx.toolAllowlist;
|
|
@@ -16914,6 +17736,7 @@ function registerMcpTools(options) {
|
|
|
16914
17736
|
const gatewayOptions = { ...options, server: observableServer };
|
|
16915
17737
|
const ctx = createToolContext(gatewayOptions, toolInfra, rateLimiterFactory);
|
|
16916
17738
|
registerToolCategories(ctx);
|
|
17739
|
+
void initUpstreamServers(gatewayConfig, observableServer, logger17);
|
|
16917
17740
|
registerPrompts(observableServer, logger17);
|
|
16918
17741
|
registerResources(observableServer, logger17);
|
|
16919
17742
|
logToolRegistration(logger17, ctx.toolAllowlist, {
|
|
@@ -17017,7 +17840,7 @@ function logFinalEventBusStats(logger17) {
|
|
|
17017
17840
|
// src/cli-orchestrator.ts
|
|
17018
17841
|
import * as readline2 from "readline";
|
|
17019
17842
|
function runOrchestratorRepl(options, logger17) {
|
|
17020
|
-
return new Promise((
|
|
17843
|
+
return new Promise((resolve14) => {
|
|
17021
17844
|
const rl = readline2.createInterface({
|
|
17022
17845
|
input: process.stdin,
|
|
17023
17846
|
output: process.stdout,
|
|
@@ -17053,7 +17876,7 @@ function runOrchestratorRepl(options, logger17) {
|
|
|
17053
17876
|
});
|
|
17054
17877
|
rl.on("close", () => {
|
|
17055
17878
|
logger17.info("Orchestrator REPL closed");
|
|
17056
|
-
|
|
17879
|
+
resolve14();
|
|
17057
17880
|
});
|
|
17058
17881
|
});
|
|
17059
17882
|
}
|
|
@@ -18198,127 +19021,6 @@ function initializeFeedbackIntegration(options) {
|
|
|
18198
19021
|
}
|
|
18199
19022
|
}
|
|
18200
19023
|
|
|
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
19024
|
// src/cli-server-auth.ts
|
|
18323
19025
|
function resolveAuthEnabled(config) {
|
|
18324
19026
|
const envValue = process.env["NEXUS_AUTH_ENABLED"];
|
|
@@ -18363,9 +19065,9 @@ function initializeAuth(config, logger17) {
|
|
|
18363
19065
|
}
|
|
18364
19066
|
|
|
18365
19067
|
// src/cli-server-audit.ts
|
|
18366
|
-
import { join as
|
|
18367
|
-
import { homedir as
|
|
18368
|
-
var DEFAULT_AUDIT_DIR =
|
|
19068
|
+
import { join as join15 } from "path";
|
|
19069
|
+
import { homedir as homedir3 } from "os";
|
|
19070
|
+
var DEFAULT_AUDIT_DIR = join15(homedir3(), ".nexus-agents", "audit");
|
|
18369
19071
|
function initializeAuditLogger(securityConfig, logger17) {
|
|
18370
19072
|
if (securityConfig?.audit?.enabled !== true) {
|
|
18371
19073
|
logger17.debug("Audit logging disabled (set security.audit.enabled: true to enable)");
|
|
@@ -18535,18 +19237,8 @@ function loadAndLogConfig(logger17) {
|
|
|
18535
19237
|
return configResult;
|
|
18536
19238
|
}
|
|
18537
19239
|
function createShutdownCleanup(options) {
|
|
18538
|
-
const {
|
|
18539
|
-
eventBusBridge,
|
|
18540
|
-
observer,
|
|
18541
|
-
eventContext,
|
|
18542
|
-
server,
|
|
18543
|
-
serverLogger,
|
|
18544
|
-
logger: logger17,
|
|
18545
|
-
restServer,
|
|
18546
|
-
auditLogger
|
|
18547
|
-
} = options;
|
|
19240
|
+
const { eventBusBridge, observer, eventContext, server, serverLogger, logger: logger17, auditLogger } = options;
|
|
18548
19241
|
return async () => {
|
|
18549
|
-
await stopRestApiServer(restServer, logger17);
|
|
18550
19242
|
await shutdownAuditLogger(auditLogger, logger17);
|
|
18551
19243
|
if (eventBusBridge.initialized) {
|
|
18552
19244
|
logFinalEventBusStats(logger17);
|
|
@@ -18703,11 +19395,8 @@ async function startServer(verbose, mode, modeWasExplicit = false, orchestratorO
|
|
|
18703
19395
|
const configResult = loadAndLogConfig(logger17);
|
|
18704
19396
|
applyLoggingConfig(logger17, verbose, configResult.config);
|
|
18705
19397
|
validateNexusEnv(logger17);
|
|
18706
|
-
const { server, serverLogger, observer, eventBusBridge, auditLogger
|
|
19398
|
+
const { server, serverLogger, observer, eventBusBridge, auditLogger } = await initializeSubsystems(configResult.config, logger17);
|
|
18707
19399
|
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
19400
|
const eventContext = recordServerStartup(observer);
|
|
18712
19401
|
const stdinMonitor = getStdinLifecycleMonitor();
|
|
18713
19402
|
stdinMonitor.start();
|
|
@@ -18722,7 +19411,6 @@ async function startServer(verbose, mode, modeWasExplicit = false, orchestratorO
|
|
|
18722
19411
|
server,
|
|
18723
19412
|
serverLogger,
|
|
18724
19413
|
logger: logger17,
|
|
18725
|
-
restServer,
|
|
18726
19414
|
auditLogger
|
|
18727
19415
|
});
|
|
18728
19416
|
setupShutdownHandlers(cleanup, logger17);
|
|
@@ -18973,9 +19661,9 @@ function buildOrchestratorOptions(args) {
|
|
|
18973
19661
|
function printFirstRunHint() {
|
|
18974
19662
|
const isTTY = process.stderr.isTTY;
|
|
18975
19663
|
if (!isTTY) return;
|
|
18976
|
-
const dataDir =
|
|
18977
|
-
const hasConfig =
|
|
18978
|
-
if (
|
|
19664
|
+
const dataDir = join16(homedir4(), ".nexus-agents");
|
|
19665
|
+
const hasConfig = existsSync18("./nexus-agents.yaml") || existsSync18("./nexus-agents.yml");
|
|
19666
|
+
if (existsSync18(dataDir) || hasConfig) return;
|
|
18979
19667
|
process.stderr.write(
|
|
18980
19668
|
"\n\x1B[36mnexus-agents\x1B[0m: First time? Run \x1B[1mnexus-agents setup\x1B[0m to configure.\n\n"
|
|
18981
19669
|
);
|
|
@@ -19109,7 +19797,7 @@ async function handleVerifyCommand(args) {
|
|
|
19109
19797
|
async function handleDoctorCommand(args) {
|
|
19110
19798
|
const exitCode = await doctorCommand({ fix: args.options.fix });
|
|
19111
19799
|
if (args.options.deep) {
|
|
19112
|
-
const { runDeepDiagnostics: runDeepDiagnostics2, formatDeepDiagnostics: formatDeepDiagnostics2 } = await import("./doctor-deep-
|
|
19800
|
+
const { runDeepDiagnostics: runDeepDiagnostics2, formatDeepDiagnostics: formatDeepDiagnostics2 } = await import("./doctor-deep-AWE7SRU6.js");
|
|
19113
19801
|
const diag = runDeepDiagnostics2();
|
|
19114
19802
|
process.stdout.write(formatDeepDiagnostics2(diag) + "\n");
|
|
19115
19803
|
}
|
|
@@ -19360,25 +20048,25 @@ function handleScaffoldCommand(args) {
|
|
|
19360
20048
|
}
|
|
19361
20049
|
|
|
19362
20050
|
// src/cli/visualize-summary.ts
|
|
19363
|
-
import * as
|
|
19364
|
-
import * as
|
|
20051
|
+
import * as fs18 from "fs";
|
|
20052
|
+
import * as path22 from "path";
|
|
19365
20053
|
import { fileURLToPath } from "url";
|
|
19366
20054
|
function findPackageRoot(startDir) {
|
|
19367
20055
|
let dir = startDir;
|
|
19368
20056
|
for (let i = 0; i < 5; i++) {
|
|
19369
|
-
if (
|
|
19370
|
-
const parent =
|
|
20057
|
+
if (fs18.existsSync(path22.join(dir, "package.json"))) return dir;
|
|
20058
|
+
const parent = path22.dirname(dir);
|
|
19371
20059
|
if (parent === dir) break;
|
|
19372
20060
|
dir = parent;
|
|
19373
20061
|
}
|
|
19374
20062
|
return startDir;
|
|
19375
20063
|
}
|
|
19376
|
-
function
|
|
19377
|
-
if (!
|
|
20064
|
+
function countFiles2(dir, pattern) {
|
|
20065
|
+
if (!fs18.existsSync(dir)) return 0;
|
|
19378
20066
|
let count = 0;
|
|
19379
|
-
for (const entry of
|
|
20067
|
+
for (const entry of fs18.readdirSync(dir, { withFileTypes: true })) {
|
|
19380
20068
|
if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist") {
|
|
19381
|
-
count +=
|
|
20069
|
+
count += countFiles2(path22.join(dir, entry.name), pattern);
|
|
19382
20070
|
} else if (entry.isFile() && pattern.test(entry.name)) {
|
|
19383
20071
|
count++;
|
|
19384
20072
|
}
|
|
@@ -19387,7 +20075,7 @@ function countFiles(dir, pattern) {
|
|
|
19387
20075
|
}
|
|
19388
20076
|
function readVersion(pkgRoot) {
|
|
19389
20077
|
try {
|
|
19390
|
-
const raw =
|
|
20078
|
+
const raw = fs18.readFileSync(path22.join(pkgRoot, "package.json"), "utf-8");
|
|
19391
20079
|
const pkg = JSON.parse(raw);
|
|
19392
20080
|
return pkg.version ?? "unknown";
|
|
19393
20081
|
} catch {
|
|
@@ -19408,13 +20096,13 @@ var LAYER_DIRS = [
|
|
|
19408
20096
|
];
|
|
19409
20097
|
function gatherSystemSummary() {
|
|
19410
20098
|
const thisFile = fileURLToPath(import.meta.url);
|
|
19411
|
-
const pkgRoot = findPackageRoot(
|
|
19412
|
-
const srcDir =
|
|
19413
|
-
const allTs =
|
|
19414
|
-
const testTs =
|
|
20099
|
+
const pkgRoot = findPackageRoot(path22.dirname(thisFile));
|
|
20100
|
+
const srcDir = path22.resolve(pkgRoot, "src");
|
|
20101
|
+
const allTs = countFiles2(srcDir, /\.ts$/);
|
|
20102
|
+
const testTs = countFiles2(srcDir, /\.test\.ts$/);
|
|
19415
20103
|
const layers = LAYER_DIRS.map(({ name, dir }) => ({
|
|
19416
20104
|
name,
|
|
19417
|
-
files:
|
|
20105
|
+
files: countFiles2(path22.join(srcDir, dir), /\.ts$/)
|
|
19418
20106
|
})).filter((l) => l.files > 0);
|
|
19419
20107
|
return {
|
|
19420
20108
|
version: readVersion(pkgRoot),
|
|
@@ -19664,8 +20352,8 @@ function generateDiagramOutput(subcommand, format) {
|
|
|
19664
20352
|
}
|
|
19665
20353
|
function writeOutput(output2, outputPath) {
|
|
19666
20354
|
if (outputPath !== void 0) {
|
|
19667
|
-
import("fs").then((
|
|
19668
|
-
|
|
20355
|
+
import("fs").then((fs19) => {
|
|
20356
|
+
fs19.writeFileSync(outputPath, output2 + "\n", "utf-8");
|
|
19669
20357
|
process.stdout.write(`Diagram written to ${outputPath}
|
|
19670
20358
|
`);
|
|
19671
20359
|
}).catch((e) => {
|
|
@@ -20563,10 +21251,10 @@ function handleStatusCommand2(args) {
|
|
|
20563
21251
|
|
|
20564
21252
|
// src/cli/scenario-command.ts
|
|
20565
21253
|
import { readdir as readdir4 } from "fs/promises";
|
|
20566
|
-
import { join as
|
|
21254
|
+
import { join as join18, resolve as resolve13 } from "path";
|
|
20567
21255
|
|
|
20568
21256
|
// src/testing/e2e/scenario-runner.ts
|
|
20569
|
-
import { readFile as
|
|
21257
|
+
import { readFile as readFile7 } from "fs/promises";
|
|
20570
21258
|
import * as yaml7 from "yaml";
|
|
20571
21259
|
|
|
20572
21260
|
// src/testing/e2e/types.ts
|
|
@@ -20694,9 +21382,9 @@ function checkCircularDependencies(workflow) {
|
|
|
20694
21382
|
for (const step of workflow.steps) {
|
|
20695
21383
|
stepMap.set(step.id, step);
|
|
20696
21384
|
}
|
|
20697
|
-
const visit = (stepId,
|
|
21385
|
+
const visit = (stepId, path23) => {
|
|
20698
21386
|
if (visiting.has(stepId)) {
|
|
20699
|
-
errors.push(`Circular dependency detected: ${[...
|
|
21387
|
+
errors.push(`Circular dependency detected: ${[...path23, stepId].join(" -> ")}`);
|
|
20700
21388
|
return false;
|
|
20701
21389
|
}
|
|
20702
21390
|
if (visited.has(stepId)) return true;
|
|
@@ -20704,7 +21392,7 @@ function checkCircularDependencies(workflow) {
|
|
|
20704
21392
|
const step = stepMap.get(stepId);
|
|
20705
21393
|
if (step?.dependsOn) {
|
|
20706
21394
|
for (const dep of step.dependsOn) {
|
|
20707
|
-
if (!visit(dep, [...
|
|
21395
|
+
if (!visit(dep, [...path23, stepId])) {
|
|
20708
21396
|
return false;
|
|
20709
21397
|
}
|
|
20710
21398
|
}
|
|
@@ -20723,7 +21411,7 @@ function checkCircularDependencies(workflow) {
|
|
|
20723
21411
|
var defaultStubFactory = {
|
|
20724
21412
|
createAgentStub(agentType, action) {
|
|
20725
21413
|
return async (inputs) => {
|
|
20726
|
-
await new Promise((
|
|
21414
|
+
await new Promise((resolve14) => setTimeout(resolve14, 10));
|
|
20727
21415
|
return {
|
|
20728
21416
|
stepId: `${agentType}-${action}`,
|
|
20729
21417
|
status: "success",
|
|
@@ -20748,8 +21436,8 @@ var ScenarioRunner = class {
|
|
|
20748
21436
|
/**
|
|
20749
21437
|
* Load a scenario fixture from a YAML file.
|
|
20750
21438
|
*/
|
|
20751
|
-
async loadFixture(
|
|
20752
|
-
const content = await
|
|
21439
|
+
async loadFixture(path23) {
|
|
21440
|
+
const content = await readFile7(path23, "utf-8");
|
|
20753
21441
|
const data = yaml7.parse(content);
|
|
20754
21442
|
return ScenarioFixtureSchema.parse(data);
|
|
20755
21443
|
}
|
|
@@ -20882,7 +21570,7 @@ function createScenarioRunner(stubFactory) {
|
|
|
20882
21570
|
}
|
|
20883
21571
|
|
|
20884
21572
|
// src/cli/scenario-command.ts
|
|
20885
|
-
var FIXTURES_DIR =
|
|
21573
|
+
var FIXTURES_DIR = resolve13(import.meta.dirname, "../testing/e2e/fixtures");
|
|
20886
21574
|
var SCENARIO_SUFFIX = ".scenario.yaml";
|
|
20887
21575
|
async function listScenarios() {
|
|
20888
21576
|
try {
|
|
@@ -20933,7 +21621,7 @@ async function handleRun(args) {
|
|
|
20933
21621
|
process.exit(EXIT_CODES.SERVER_START_FAILED);
|
|
20934
21622
|
}
|
|
20935
21623
|
const runner = createScenarioRunner();
|
|
20936
|
-
const fixturePath =
|
|
21624
|
+
const fixturePath = join18(FIXTURES_DIR, `${name}${SCENARIO_SUFFIX}`);
|
|
20937
21625
|
try {
|
|
20938
21626
|
const fixture = await runner.loadFixture(fixturePath);
|
|
20939
21627
|
const result = await runner.run(fixture);
|
|
@@ -21094,6 +21782,67 @@ function handleHealthCommand(args) {
|
|
|
21094
21782
|
}
|
|
21095
21783
|
}
|
|
21096
21784
|
|
|
21785
|
+
// src/cli/validate-command.ts
|
|
21786
|
+
var MIN_FITNESS_SCORE = 90;
|
|
21787
|
+
function evaluateDoctor(doctor) {
|
|
21788
|
+
const installedCount = doctor.clis.filter((c) => c.installed).length;
|
|
21789
|
+
const total = doctor.clis.length;
|
|
21790
|
+
const summary = doctor.allHealthy ? `All systems healthy (${String(installedCount)}/${String(total)} CLIs installed)` : `Issues detected (${String(installedCount)}/${String(total)} CLIs installed)`;
|
|
21791
|
+
return { name: "Doctor", passed: doctor.allHealthy, summary };
|
|
21792
|
+
}
|
|
21793
|
+
function evaluateFitness(audit) {
|
|
21794
|
+
const passed = audit.score >= MIN_FITNESS_SCORE;
|
|
21795
|
+
const summary = `Score ${String(audit.score)}/100 (minimum: ${String(MIN_FITNESS_SCORE)})`;
|
|
21796
|
+
return { name: "Fitness Audit", passed, summary };
|
|
21797
|
+
}
|
|
21798
|
+
function evaluateConfig(doctor) {
|
|
21799
|
+
const found = doctor.configFile.found;
|
|
21800
|
+
const summary = found ? `Config found: ${doctor.configFile.path ?? "unknown"}` : "No nexus-agents.yaml found (optional)";
|
|
21801
|
+
return { name: "Config", passed: true, summary };
|
|
21802
|
+
}
|
|
21803
|
+
function printReport2(result) {
|
|
21804
|
+
writeLine();
|
|
21805
|
+
writeLine(`${colors.bold}nexus-agents validate${colors.reset}`);
|
|
21806
|
+
writeLine("\u2500".repeat(50));
|
|
21807
|
+
writeLine();
|
|
21808
|
+
for (const phase of result.phases) {
|
|
21809
|
+
const icon = phase.passed ? `${colors.green}${symbols.check}${colors.reset}` : `${colors.red}${symbols.cross}${colors.reset}`;
|
|
21810
|
+
writeLine(` ${icon} ${colors.bold}${phase.name}${colors.reset}: ${phase.summary}`);
|
|
21811
|
+
}
|
|
21812
|
+
writeLine();
|
|
21813
|
+
if (result.allPassed) {
|
|
21814
|
+
writeLine(`${colors.green}${colors.bold}PASS${colors.reset}: All validation checks passed.`);
|
|
21815
|
+
} else {
|
|
21816
|
+
writeLine(
|
|
21817
|
+
`${colors.red}${colors.bold}FAIL${colors.reset}: One or more validation checks failed.`
|
|
21818
|
+
);
|
|
21819
|
+
}
|
|
21820
|
+
writeLine();
|
|
21821
|
+
}
|
|
21822
|
+
async function runValidate() {
|
|
21823
|
+
const doctor = await runDoctor();
|
|
21824
|
+
const audit = calculateFitnessScore(`v${VERSION}`);
|
|
21825
|
+
const phases = [
|
|
21826
|
+
evaluateDoctor(doctor),
|
|
21827
|
+
evaluateFitness(audit),
|
|
21828
|
+
evaluateConfig(doctor)
|
|
21829
|
+
];
|
|
21830
|
+
return {
|
|
21831
|
+
phases,
|
|
21832
|
+
allPassed: phases.every((p) => p.passed)
|
|
21833
|
+
};
|
|
21834
|
+
}
|
|
21835
|
+
async function handleValidateCommand(args) {
|
|
21836
|
+
const result = await runValidate();
|
|
21837
|
+
const isJson = args.options.format === "json";
|
|
21838
|
+
if (isJson) {
|
|
21839
|
+
writeLine(JSON.stringify(result, null, 2));
|
|
21840
|
+
} else {
|
|
21841
|
+
printReport2(result);
|
|
21842
|
+
}
|
|
21843
|
+
process.exit(result.allPassed ? 0 : 1);
|
|
21844
|
+
}
|
|
21845
|
+
|
|
21097
21846
|
// src/cli-commands.ts
|
|
21098
21847
|
function printHelp() {
|
|
21099
21848
|
process.stdout.write(HELP_TEXT + "\n");
|
|
@@ -21175,7 +21924,9 @@ var ASYNC_COMMAND_HANDLERS = {
|
|
|
21175
21924
|
// Issue #748: Memory Benchmark Command
|
|
21176
21925
|
"memory-benchmark": handleMemoryBenchmarkCommand,
|
|
21177
21926
|
// Epic #952: Scenario Command
|
|
21178
|
-
scenario: handleScenarioCommand
|
|
21927
|
+
scenario: handleScenarioCommand,
|
|
21928
|
+
// Issue #1598: Validate Command
|
|
21929
|
+
validate: handleValidateCommand
|
|
21179
21930
|
};
|
|
21180
21931
|
async function handleAsyncCommand(args) {
|
|
21181
21932
|
const handler = ASYNC_COMMAND_HANDLERS[args.command];
|