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.
Files changed (73) hide show
  1. package/LICENSE +27 -0
  2. package/README.md +11 -10
  3. package/dist/chunk-5VZLXMO7.js +838 -0
  4. package/dist/chunk-5VZLXMO7.js.map +1 -0
  5. package/dist/chunk-633WH2ML.js +127 -0
  6. package/dist/chunk-633WH2ML.js.map +1 -0
  7. package/dist/chunk-7F6HYUIY.js +327 -0
  8. package/dist/chunk-7F6HYUIY.js.map +1 -0
  9. package/dist/chunk-CLYZ7FWP.js +30 -0
  10. package/dist/chunk-CLYZ7FWP.js.map +1 -0
  11. package/dist/{chunk-WZACXZGT.js → chunk-DAMRMAM2.js} +19543 -35952
  12. package/dist/chunk-DAMRMAM2.js.map +1 -0
  13. package/dist/{chunk-VL7L56MU.js → chunk-HH5LVGEE.js} +6 -6
  14. package/dist/{chunk-6QLUXEA6.js → chunk-HWDBNDUX.js} +2 -2
  15. package/dist/chunk-I6YDS23R.js +354 -0
  16. package/dist/chunk-I6YDS23R.js.map +1 -0
  17. package/dist/{chunk-WF5CWL2N.js → chunk-IMWYKX4H.js} +4377 -4190
  18. package/dist/chunk-IMWYKX4H.js.map +1 -0
  19. package/dist/chunk-KGDG6PWZ.js +61 -0
  20. package/dist/chunk-KGDG6PWZ.js.map +1 -0
  21. package/dist/chunk-POBO4G2P.js +3700 -0
  22. package/dist/chunk-POBO4G2P.js.map +1 -0
  23. package/dist/chunk-S3BKWNST.js +13475 -0
  24. package/dist/chunk-S3BKWNST.js.map +1 -0
  25. package/dist/chunk-T7PU3NPQ.js +42 -0
  26. package/dist/chunk-T7PU3NPQ.js.map +1 -0
  27. package/dist/{chunk-4LM3YGJF.js → chunk-WSK4VSXP.js} +2 -2
  28. package/dist/{chunk-RNQRE45M.js → chunk-ZBZJHXRT.js} +181 -1119
  29. package/dist/chunk-ZBZJHXRT.js.map +1 -0
  30. package/dist/cli.d.ts +2 -2
  31. package/dist/cli.js +1410 -659
  32. package/dist/cli.js.map +1 -1
  33. package/dist/composite-router-YPRWVTRB.js +17 -0
  34. package/dist/consensus-vote-DBE6RNZG.js +23 -0
  35. package/dist/{dist-Y5F6UM2N.js → dist-7PQR2BQB.js} +1584 -1443
  36. package/dist/dist-7PQR2BQB.js.map +1 -0
  37. package/dist/{doctor-deep-EN3V4PXO.js → doctor-deep-AWE7SRU6.js} +4 -3
  38. package/dist/expert-config-FHNBQRX2.js +22 -0
  39. package/dist/expert-config-FHNBQRX2.js.map +1 -0
  40. package/dist/factory-O5C7ZBZO.js +13 -0
  41. package/dist/factory-O5C7ZBZO.js.map +1 -0
  42. package/dist/factory-PCHGQ3ZG.js +16 -0
  43. package/dist/factory-PCHGQ3ZG.js.map +1 -0
  44. package/dist/index.d.ts +3851 -9022
  45. package/dist/index.js +505 -318
  46. package/dist/index.js.map +1 -1
  47. package/dist/learning-persistence-WMWZJZ35.js +16 -0
  48. package/dist/learning-persistence-WMWZJZ35.js.map +1 -0
  49. package/dist/mcp-config-AUZQPUBY.js +12 -0
  50. package/dist/mcp-config-AUZQPUBY.js.map +1 -0
  51. package/dist/{model-capabilities-types-CSWO27YN.d.ts → model-capabilities-types-B57GZryc.d.ts} +1 -1
  52. package/dist/routing-memory-QY3XMU2R.js +13 -0
  53. package/dist/routing-memory-QY3XMU2R.js.map +1 -0
  54. package/dist/session-memory-3MBCE5KS.js +22 -0
  55. package/dist/session-memory-3MBCE5KS.js.map +1 -0
  56. package/dist/{setup-command-ZN7DSP53.js → setup-command-IQ4MD3FT.js} +8 -5
  57. package/dist/setup-command-IQ4MD3FT.js.map +1 -0
  58. package/dist/setup-config-5YUPLDXF.js +10 -0
  59. package/dist/setup-config-5YUPLDXF.js.map +1 -0
  60. package/dist/weather-report-CC2C4KAX.js +15 -0
  61. package/dist/weather-report-CC2C4KAX.js.map +1 -0
  62. package/package.json +33 -36
  63. package/dist/chunk-RNQRE45M.js.map +0 -1
  64. package/dist/chunk-WF5CWL2N.js.map +0 -1
  65. package/dist/chunk-WZACXZGT.js.map +0 -1
  66. package/dist/dist-Y5F6UM2N.js.map +0 -1
  67. package/dist/setup-config-ZOJUDXNP.js +0 -9
  68. /package/dist/{chunk-VL7L56MU.js.map → chunk-HH5LVGEE.js.map} +0 -0
  69. /package/dist/{chunk-6QLUXEA6.js.map → chunk-HWDBNDUX.js.map} +0 -0
  70. /package/dist/{chunk-4LM3YGJF.js.map → chunk-WSK4VSXP.js.map} +0 -0
  71. /package/dist/{doctor-deep-EN3V4PXO.js.map → composite-router-YPRWVTRB.js.map} +0 -0
  72. /package/dist/{setup-command-ZN7DSP53.js.map → consensus-vote-DBE6RNZG.js.map} +0 -0
  73. /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
- RestApiServer,
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
- collectRealVotes,
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
- evaluatePolicy3 as evaluatePolicy,
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-WZACXZGT.js";
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
- isRecord,
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-RNQRE45M.js";
164
- import "./chunk-4LM3YGJF.js";
188
+ } from "./chunk-633WH2ML.js";
189
+ import "./chunk-I6YDS23R.js";
190
+ import "./chunk-KGDG6PWZ.js";
165
191
  import {
166
- calculateConsensus,
167
- countByCategory,
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 "./chunk-6QLUXEA6.js";
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-WF5CWL2N.js";
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(path24) {
427
- return existsSync(path24);
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(path24) {
436
- await ensureDirectory(path24);
437
- await writeFile(path24, CONFIG_TEMPLATE, "utf-8");
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 = e instanceof Error ? e.message : String(e);
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 = e instanceof Error ? e.message : String(e);
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((resolve12) => {
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
- resolve12();
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
- resolve12();
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 [path24, evaluations] of evaluationsByComponent) {
2444
+ for (const [path23, evaluations] of evaluationsByComponent) {
2420
2445
  if (evaluations.length > 0) {
2421
- results.push(aggregator.aggregate(path24, evaluations));
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(path24, logger17) {
3958
+ function loadPolicyParameters(path23, logger17) {
3934
3959
  try {
3935
- if (fs2.existsSync(path24)) {
3936
- const content = fs2.readFileSync(path24, "utf-8");
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: path24 });
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: path24, error: message });
3968
+ logger17.warn("Failed to load policy parameters", { path: path23, error: message });
3944
3969
  }
3945
3970
  return void 0;
3946
3971
  }
3947
- function savePolicyParameters(path24, params, logger17) {
3972
+ function savePolicyParameters(path23, params, logger17) {
3948
3973
  try {
3949
- fs2.writeFileSync(path24, JSON.stringify(params, null, 2));
3950
- logger17.info("Saved policy parameters", { path: path24 });
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: path24, error: message });
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.enum([
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 and long-term memory",
6779
- "code-generation": "Self-improvement and skill learning",
6780
- "cli-tools": "External CLI integration",
6781
- orchestration: "Multi-agent coordination",
6782
- security: "Safety analysis and evaluation"
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(ResearchTopicSchema).optional().default([]),
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 fs12 from "fs/promises";
8059
- import * as path18 from "path";
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 fs9 from "fs/promises";
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 path15 from "path";
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: path15.join(opts.packageRoot, "tsconfig.json"),
9486
+ tsConfigFilePath: path14.join(opts.packageRoot, "tsconfig.json"),
9345
9487
  skipAddingFilesFromTsConfig: true
9346
9488
  });
9347
9489
  project.addSourceFilesAtPaths([
9348
- path15.join(opts.packageRoot, opts.cliCommandsPath),
9349
- path15.join(opts.packageRoot, "src/cli-types.ts"),
9350
- path15.join(opts.packageRoot, opts.mcpToolsPath, "*.ts"),
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 fs11 from "fs/promises";
9400
- import * as path17 from "path";
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 fs10 from "fs/promises";
9404
- import * as path16 from "path";
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 = path16.resolve(path16.dirname(sourceFile), filePath);
9548
+ const resolvedPath = path15.resolve(path15.dirname(sourceFile), filePath);
9411
9549
  try {
9412
- await fs10.access(resolvedPath);
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 fs10.readFile(targetFile, "utf-8");
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 ${path16.basename(targetFile)}` };
9633
+ return { valid: false, error: `Anchor '#${anchor}' not found in ${path15.basename(targetFile)}` };
9496
9634
  } catch {
9497
- return { valid: false, error: `Failed to read ${path16.basename(targetFile)} for anchor check` };
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 fs11.readdir(dir, { withFileTypes: true });
9725
+ const entries = await fs12.readdir(dir, { withFileTypes: true });
9588
9726
  for (const entry of entries) {
9589
- const fullPath = path17.join(dir, entry.name);
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 fs11.readFile(filePath, "utf-8");
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 = path18.dirname(outputPath);
9785
- await fs12.mkdir(outputDir, { recursive: true });
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 fs12.writeFile(outputPath, content, "utf-8");
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 fs12.access(indexPath);
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 fs12.readFile(indexPath, "utf-8");
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 fs12.access(indexPath);
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 fs12.readFile(indexPath, "utf-8");
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 fs12.mkdir(path18.dirname(diagramPath), { recursive: true });
9856
- await fs12.writeFile(diagramPath, diagram, "utf-8");
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 fs12.readdir(srcPath, { withFileTypes: true });
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 fs12.readFile(architecturePath, "utf-8");
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 fs12.access(srcPath);
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 = path18.dirname(outputPath);
9963
- await fs12.mkdir(outputDir, { recursive: true });
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 fs12.writeFile(outputPath, content, "utf-8");
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 + manifest.rest_endpoints.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, ${String(manifest.rest_endpoints.length)} REST)`,
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 = path18.dirname(options.output);
9984
- await fs12.mkdir(outputDir, { recursive: true });
9985
- await fs12.writeFile(options.output, output2, "utf-8");
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 = path18.dirname(options.output);
10009
- await fs12.mkdir(outputDir, { recursive: true });
10010
- await fs12.writeFile(options.output, output2, "utf-8");
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 path19 from "path";
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 loadOptions = options.limit !== void 0 ? { limit: options.limit } : {};
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 = path19.resolve(dir);
10468
- return !resolved.includes("..") && resolved === path19.normalize(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: path19.resolve(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: path19.resolve(options.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 fs13 from "fs/promises";
11070
- import * as path20 from "path";
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 = path20.isAbsolute(filePath) ? filePath : path20.resolve(base, filePath);
11339
- const normalizedResolved = path20.normalize(resolved);
11340
- const normalizedBase = path20.normalize(base);
11341
- const isWithinBase = normalizedResolved === normalizedBase || normalizedResolved.startsWith(normalizedBase + path20.sep);
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 path20.resolve(process.cwd(), `nexus-config.${extension}`);
11490
+ return path19.resolve(process.cwd(), `nexus-config.${extension}`);
11353
11491
  }
11354
11492
 
11355
11493
  // src/cli/config-command-handlers.ts
11356
- import * as fs14 from "fs/promises";
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 fs14.writeFile(filePath, content, "utf-8");
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 fs14.readFile(filePath, "utf-8");
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((resolve12, reject) => {
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
- resolve12(data);
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
- resolve12("");
12137
+ resolve14("");
12000
12138
  }
12001
12139
  });
12002
12140
  }
12003
12141
  async function readStdinWithTimeout(timeoutMs = 5e3) {
12004
- return new Promise((resolve12, reject) => {
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
- resolve12(data);
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 join14 } from "path";
12280
+ import { join as join13 } from "path";
12143
12281
  function getDefaultDbPath2() {
12144
- return join14(homedir2(), ".nexus-agents", "sessions.db");
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
- this.checks.push({
13339
- dimension: "canonicalPaths",
13340
- maxPoints: 20,
13341
- name: "Canonical Path Analysis",
13342
- check: () => this.checkCanonicalPaths()
13343
- });
13344
- this.checks.push({
13345
- dimension: "explicitBehavior",
13346
- maxPoints: 15,
13347
- name: "Explicit Behavior Analysis",
13348
- check: () => this.checkExplicitBehavior()
13349
- });
13350
- this.checks.push({
13351
- dimension: "determinism",
13352
- maxPoints: 15,
13353
- name: "Determinism Analysis",
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
- const score = 20;
13419
- return { score: Math.max(0, score), findings };
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 - penalize hidden/magic 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
- const score = 15;
13427
- return { score, findings };
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 -= 2;
13436
- findings.push({
13437
- dimension: "determinism",
13438
- severity: "info",
13439
- description: "Consensus voting uses LLM responses which are non-deterministic",
13440
- pointsDeducted: 2,
13441
- suggestion: "Consider caching/memoization for repeated proposals"
13442
- });
13443
- return { score: Math.max(0, score), findings };
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
- * Check observability - reward telemetry coverage.
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
- const score = 15;
13451
- return { score: Math.max(0, score), findings };
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
- const score = 10;
13459
- return { score: Math.max(0, score), findings };
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
- const score = 10;
13467
- return { score: Math.max(0, score), findings };
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
- const score = 10;
13475
- return { score: Math.max(0, score), findings };
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
- * Check governance integration - reward governance injection.
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
- const score = 5;
13483
- return { score, findings };
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 readFileSync7 } from "fs";
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 existsSync12, readFileSync as readFileSync6 } from "fs";
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 (existsSync12(".env")) {
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 (!existsSync12("CHANGELOG.md")) {
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 (!readFileSync6("CHANGELOG.md", "utf-8").includes(options.version)) {
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 (existsSync12(pkgCl) && !readFileSync6(pkgCl, "utf-8").includes(options.version)) {
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 (!existsSync12("README.md")) {
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 (existsSync12("CLAUDE.md")) {
14079
- const claudeMd = readFileSync6("CLAUDE.md", "utf-8");
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(readFileSync7("package.json", "utf-8"));
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 readFileSync8, existsSync as existsSync13 } from "fs";
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-Y5F6UM2N.js");
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 (!existsSync13("CHANGELOG.md")) return [];
14369
- const changelog = readFileSync8("CHANGELOG.md", "utf-8");
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(readFileSync8("package.json", "utf-8"));
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 fs15 from "fs";
14659
- import * as path21 from "path";
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 = path21.resolve(process.cwd(), file.path);
14996
- const dir = path21.dirname(fullPath);
14997
- fs15.mkdirSync(dir, { recursive: true });
14998
- if (fs15.existsSync(fullPath)) {
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
- fs15.writeFileSync(fullPath, file.content, "utf-8");
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 existsSync15, statSync, chmodSync } from "fs";
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 = statSync(filePath);
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 = existsSync15(tokenFile);
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 = existsSync15(tokenFile);
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 = existsSync15(tokenFile);
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: existsSync15(getDefaultTokenPath())
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: existsSync15(getDefaultTokenPath()),
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 - Multi-agent orchestration MCP server
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 existsSync17 } from "fs";
16425
- import { join as join17 } from "path";
16426
- import { homedir as homedir5 } from "os";
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((resolve12) => {
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
- resolve12();
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 join16 } from "path";
18367
- import { homedir as homedir4 } from "os";
18368
- var DEFAULT_AUDIT_DIR = join16(homedir4(), ".nexus-agents", "audit");
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, authInit } = await initializeSubsystems(configResult.config, logger17);
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 = join17(homedir5(), ".nexus-agents");
18977
- const hasConfig = existsSync17("./nexus-agents.yaml") || existsSync17("./nexus-agents.yml");
18978
- if (existsSync17(dataDir) || hasConfig) return;
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-EN3V4PXO.js");
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 fs17 from "fs";
19364
- import * as path23 from "path";
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 (fs17.existsSync(path23.join(dir, "package.json"))) return dir;
19370
- const parent = path23.dirname(dir);
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 countFiles(dir, pattern) {
19377
- if (!fs17.existsSync(dir)) return 0;
20064
+ function countFiles2(dir, pattern) {
20065
+ if (!fs18.existsSync(dir)) return 0;
19378
20066
  let count = 0;
19379
- for (const entry of fs17.readdirSync(dir, { withFileTypes: true })) {
20067
+ for (const entry of fs18.readdirSync(dir, { withFileTypes: true })) {
19380
20068
  if (entry.isDirectory() && entry.name !== "node_modules" && entry.name !== "dist") {
19381
- count += countFiles(path23.join(dir, entry.name), pattern);
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 = fs17.readFileSync(path23.join(pkgRoot, "package.json"), "utf-8");
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(path23.dirname(thisFile));
19412
- const srcDir = path23.resolve(pkgRoot, "src");
19413
- const allTs = countFiles(srcDir, /\.ts$/);
19414
- const testTs = countFiles(srcDir, /\.test\.ts$/);
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: countFiles(path23.join(srcDir, dir), /\.ts$/)
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((fs18) => {
19668
- fs18.writeFileSync(outputPath, output2 + "\n", "utf-8");
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 join19, resolve as resolve11 } from "path";
21254
+ import { join as join18, resolve as resolve13 } from "path";
20567
21255
 
20568
21256
  // src/testing/e2e/scenario-runner.ts
20569
- import { readFile as readFile6 } from "fs/promises";
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, path24) => {
21385
+ const visit = (stepId, path23) => {
20698
21386
  if (visiting.has(stepId)) {
20699
- errors.push(`Circular dependency detected: ${[...path24, stepId].join(" -> ")}`);
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, [...path24, stepId])) {
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((resolve12) => setTimeout(resolve12, 10));
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(path24) {
20752
- const content = await readFile6(path24, "utf-8");
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 = resolve11(import.meta.dirname, "../testing/e2e/fixtures");
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 = join19(FIXTURES_DIR, `${name}${SCENARIO_SUFFIX}`);
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];