@mrpatronz/nexusflow 0.2.19 → 1.3.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 (236) hide show
  1. package/README.md +27 -3
  2. package/dist/analyzers/index.d.ts +23 -0
  3. package/dist/analyzers/index.d.ts.map +1 -1
  4. package/dist/analyzers/index.js +62 -0
  5. package/dist/analyzers/index.js.map +1 -1
  6. package/dist/commands/commands.test.js +20 -6
  7. package/dist/commands/commands.test.js.map +1 -1
  8. package/dist/commands/commit.d.ts +2 -0
  9. package/dist/commands/commit.d.ts.map +1 -1
  10. package/dist/commands/commit.js +27 -5
  11. package/dist/commands/commit.js.map +1 -1
  12. package/dist/commands/diff.d.ts +9 -2
  13. package/dist/commands/diff.d.ts.map +1 -1
  14. package/dist/commands/diff.js +36 -12
  15. package/dist/commands/diff.js.map +1 -1
  16. package/dist/commands/doctor.js +2 -2
  17. package/dist/commands/doctor.js.map +1 -1
  18. package/dist/commands/handoff.js +2 -2
  19. package/dist/commands/handoff.js.map +1 -1
  20. package/dist/commands/refresh.d.ts +4 -2
  21. package/dist/commands/refresh.d.ts.map +1 -1
  22. package/dist/commands/refresh.js +20 -36
  23. package/dist/commands/refresh.js.map +1 -1
  24. package/dist/commands/schedule.d.ts +35 -0
  25. package/dist/commands/schedule.d.ts.map +1 -0
  26. package/dist/commands/schedule.js +151 -0
  27. package/dist/commands/schedule.js.map +1 -0
  28. package/dist/commands/sync.d.ts.map +1 -1
  29. package/dist/commands/sync.js +28 -7
  30. package/dist/commands/sync.js.map +1 -1
  31. package/dist/core/analysis-cache.d.ts +58 -0
  32. package/dist/core/analysis-cache.d.ts.map +1 -0
  33. package/dist/core/analysis-cache.js +116 -0
  34. package/dist/core/analysis-cache.js.map +1 -0
  35. package/dist/core/analysis-cache.test.d.ts +2 -0
  36. package/dist/core/analysis-cache.test.d.ts.map +1 -0
  37. package/dist/core/analysis-cache.test.js +108 -0
  38. package/dist/core/analysis-cache.test.js.map +1 -0
  39. package/dist/core/refresh.d.ts +41 -0
  40. package/dist/core/refresh.d.ts.map +1 -0
  41. package/dist/core/refresh.js +60 -0
  42. package/dist/core/refresh.js.map +1 -0
  43. package/dist/core/scheduler.d.ts +117 -0
  44. package/dist/core/scheduler.d.ts.map +1 -0
  45. package/dist/core/scheduler.js +247 -0
  46. package/dist/core/scheduler.js.map +1 -0
  47. package/dist/core/scheduler.test.d.ts +2 -0
  48. package/dist/core/scheduler.test.d.ts.map +1 -0
  49. package/dist/core/scheduler.test.js +111 -0
  50. package/dist/core/scheduler.test.js.map +1 -0
  51. package/dist/core/sync.d.ts +6 -0
  52. package/dist/core/sync.d.ts.map +1 -1
  53. package/dist/core/sync.js +15 -5
  54. package/dist/core/sync.js.map +1 -1
  55. package/dist/generators/index.d.ts +7 -1
  56. package/dist/generators/index.d.ts.map +1 -1
  57. package/dist/generators/index.js +14 -1
  58. package/dist/generators/index.js.map +1 -1
  59. package/dist/index.js +107 -4
  60. package/dist/index.js.map +1 -1
  61. package/dist/server.d.ts.map +1 -1
  62. package/dist/server.js +81 -0
  63. package/dist/server.js.map +1 -1
  64. package/dist/server.test.js +66 -0
  65. package/dist/server.test.js.map +1 -1
  66. package/dist/utils/multi-git.d.ts +21 -0
  67. package/dist/utils/multi-git.d.ts.map +1 -1
  68. package/dist/utils/multi-git.js +40 -4
  69. package/dist/utils/multi-git.js.map +1 -1
  70. package/package.json +13 -1
  71. package/.agents/skills/ccc/SKILL.md +0 -71
  72. package/.agents/skills/ccc/references/management.md +0 -110
  73. package/.agents/skills/ccc/references/settings.md +0 -126
  74. package/.github/dependabot.yml +0 -52
  75. package/.github/workflows/ci.yml +0 -44
  76. package/.github/workflows/dependabot-merge.yml +0 -53
  77. package/.github/workflows/release-desktop.yml +0 -67
  78. package/.github/workflows/release-npm.yml +0 -65
  79. package/.github/workflows/release-vscode.yml +0 -61
  80. package/.vscode/launch.json +0 -17
  81. package/.vscode/tasks.json +0 -17
  82. package/GETTING_STARTED.md +0 -114
  83. package/desktop/build-installer.js +0 -228
  84. package/desktop/build.js +0 -118
  85. package/desktop/installer/nexusflow.iss +0 -35
  86. package/desktop/neutralino.config.json +0 -39
  87. package/desktop/package-lock.json +0 -1060
  88. package/desktop/package.json +0 -14
  89. package/extension/media/icon.svg +0 -1
  90. package/extension/package-lock.json +0 -6044
  91. package/extension/package.json +0 -117
  92. package/extension/src/extension.ts +0 -653
  93. package/extension/tsconfig.json +0 -21
  94. package/gui/README.md +0 -73
  95. package/gui/e2e/dashboard.spec.ts +0 -61
  96. package/gui/e2e/wizard.spec.ts +0 -346
  97. package/gui/eslint.config.js +0 -26
  98. package/gui/index.html +0 -17
  99. package/gui/package-lock.json +0 -3137
  100. package/gui/package.json +0 -37
  101. package/gui/playwright.config.ts +0 -41
  102. package/gui/public/app_logo.png +0 -0
  103. package/gui/public/favicon.svg +0 -1
  104. package/gui/public/icons.svg +0 -24
  105. package/gui/src/App.css +0 -1
  106. package/gui/src/App.tsx +0 -3573
  107. package/gui/src/app/AppSidebar.tsx +0 -69
  108. package/gui/src/assets/hero.png +0 -0
  109. package/gui/src/assets/react.svg +0 -1
  110. package/gui/src/assets/vite.svg +0 -1
  111. package/gui/src/components/AddRepoPicker.tsx +0 -81
  112. package/gui/src/components/ui/Button.tsx +0 -40
  113. package/gui/src/components/ui/Card.tsx +0 -6
  114. package/gui/src/components/ui/EmptyState.tsx +0 -22
  115. package/gui/src/components/ui/Input.tsx +0 -21
  116. package/gui/src/components/ui/Menu.tsx +0 -71
  117. package/gui/src/components/ui/Modal.tsx +0 -39
  118. package/gui/src/components/ui/PageHeader.tsx +0 -21
  119. package/gui/src/components/ui/RepoStatusStrip.tsx +0 -33
  120. package/gui/src/components/ui/Skeleton.tsx +0 -5
  121. package/gui/src/components/ui/StatusPill.tsx +0 -36
  122. package/gui/src/components/ui/Tabs.tsx +0 -40
  123. package/gui/src/components/ui/cn.ts +0 -4
  124. package/gui/src/components/ui/index.ts +0 -16
  125. package/gui/src/features/changes/ChangesViewer.tsx +0 -388
  126. package/gui/src/features/knowledge/KnowledgeBase.tsx +0 -84
  127. package/gui/src/features/onboarding/OnboardingWizard.tsx +0 -230
  128. package/gui/src/features/plan/ImplementationPlan.tsx +0 -32
  129. package/gui/src/features/services/ServiceConsole.tsx +0 -215
  130. package/gui/src/features/sessions/SessionHistory.tsx +0 -195
  131. package/gui/src/index.css +0 -167
  132. package/gui/src/lib/status.ts +0 -24
  133. package/gui/src/main.tsx +0 -94
  134. package/gui/src/pages/DashboardPage.tsx +0 -129
  135. package/gui/src/pages/WorkspacesPage.tsx +0 -352
  136. package/gui/src/types.ts +0 -81
  137. package/gui/tsconfig.app.json +0 -25
  138. package/gui/tsconfig.json +0 -7
  139. package/gui/tsconfig.node.json +0 -24
  140. package/gui/vite.config.ts +0 -12
  141. package/resources/workflows/plan-implement-review.md +0 -8
  142. package/resources/workflows/research-verify.md +0 -6
  143. package/resources/workflows/solo-developer.md +0 -3
  144. package/scripts/simulate-workspaces.ts +0 -55
  145. package/skills-lock.json +0 -11
  146. package/src/analyzers/detect-apis.ts +0 -290
  147. package/src/analyzers/detect-deps.ts +0 -315
  148. package/src/analyzers/detect-existing.ts +0 -74
  149. package/src/analyzers/detect-ports.ts +0 -110
  150. package/src/analyzers/index.ts +0 -97
  151. package/src/analyzers/messaging-analyzer.ts +0 -254
  152. package/src/analyzers/readme-summarizer.ts +0 -102
  153. package/src/analyzers/run-analyzer.ts +0 -269
  154. package/src/analyzers/tech-stack.ts +0 -283
  155. package/src/commands/adapter.ts +0 -348
  156. package/src/commands/add-repo.ts +0 -156
  157. package/src/commands/commands.test.ts +0 -156
  158. package/src/commands/commit.ts +0 -131
  159. package/src/commands/config.ts +0 -52
  160. package/src/commands/create.ts +0 -204
  161. package/src/commands/desktop.ts +0 -128
  162. package/src/commands/diff.ts +0 -125
  163. package/src/commands/doctor.ts +0 -370
  164. package/src/commands/handoff.ts +0 -266
  165. package/src/commands/init.ts +0 -134
  166. package/src/commands/list.ts +0 -46
  167. package/src/commands/logs.ts +0 -63
  168. package/src/commands/mcp.ts +0 -94
  169. package/src/commands/open.ts +0 -129
  170. package/src/commands/refresh.ts +0 -137
  171. package/src/commands/remove.ts +0 -98
  172. package/src/commands/start.ts +0 -117
  173. package/src/commands/status.ts +0 -54
  174. package/src/commands/stop.ts +0 -55
  175. package/src/commands/sync.ts +0 -125
  176. package/src/commands/tui.ts +0 -484
  177. package/src/commands/ui.ts +0 -111
  178. package/src/core/adapters/local-storage.ts +0 -66
  179. package/src/core/adapters/obsidian-storage.ts +0 -151
  180. package/src/core/adapters/registry.ts +0 -44
  181. package/src/core/adapters/storage.test.ts +0 -174
  182. package/src/core/adapters/vault-storage.ts +0 -86
  183. package/src/core/config.test.ts +0 -96
  184. package/src/core/config.ts +0 -140
  185. package/src/core/graph.ts +0 -344
  186. package/src/core/plugins/index.ts +0 -17
  187. package/src/core/plugins/loader.ts +0 -36
  188. package/src/core/ports/storage.ts +0 -71
  189. package/src/core/scanner.test.ts +0 -61
  190. package/src/core/scanner.ts +0 -91
  191. package/src/core/storage.ts +0 -37
  192. package/src/core/sync.ts +0 -121
  193. package/src/core/workspace-state.test.ts +0 -108
  194. package/src/core/workspace-state.ts +0 -130
  195. package/src/core/workspace.ts +0 -487
  196. package/src/core/worktree.ts +0 -120
  197. package/src/generators/antigravity.ts +0 -32
  198. package/src/generators/base.ts +0 -243
  199. package/src/generators/claude.ts +0 -34
  200. package/src/generators/codex.ts +0 -48
  201. package/src/generators/copilot.ts +0 -56
  202. package/src/generators/cursor.ts +0 -44
  203. package/src/generators/diff-context.ts +0 -75
  204. package/src/generators/index.ts +0 -322
  205. package/src/generators/map-generator.test.ts +0 -159
  206. package/src/generators/map-generator.ts +0 -539
  207. package/src/generators/plan-generator.ts +0 -483
  208. package/src/generators/skills-generator.ts +0 -259
  209. package/src/index.ts +0 -539
  210. package/src/mcp/server.ts +0 -338
  211. package/src/orchestration/detect.ts +0 -313
  212. package/src/orchestration/index.ts +0 -7
  213. package/src/orchestration/runner.ts +0 -283
  214. package/src/server.test.ts +0 -618
  215. package/src/server.ts +0 -1402
  216. package/src/types.ts +0 -451
  217. package/src/utils/detect-ai.test.ts +0 -44
  218. package/src/utils/detect-ai.ts +0 -84
  219. package/src/utils/detect-editors.test.ts +0 -48
  220. package/src/utils/detect-editors.ts +0 -57
  221. package/src/utils/git.test.ts +0 -74
  222. package/src/utils/git.ts +0 -117
  223. package/src/utils/local-ai.test.ts +0 -130
  224. package/src/utils/local-ai.ts +0 -116
  225. package/src/utils/multi-git.test.ts +0 -158
  226. package/src/utils/multi-git.ts +0 -405
  227. package/src/utils/prompts.ts +0 -209
  228. package/src/utils/session-finder.ts +0 -483
  229. package/src/utils/system-scanner.test.ts +0 -89
  230. package/src/utils/system-scanner.ts +0 -96
  231. package/src/utils/update-check.ts +0 -286
  232. package/src/utils/workflow-advisor.ts +0 -118
  233. package/src/utils/workflows.test.ts +0 -114
  234. package/src/utils/workflows.ts +0 -178
  235. package/tsconfig.json +0 -19
  236. package/vitest.config.ts +0 -14
@@ -1,110 +0,0 @@
1
- /**
2
- * @module analyzers/detect-ports
3
- * Detects configured ports and services in a repository by inspecting
4
- * environment files, Docker configs, and framework-specific settings.
5
- */
6
-
7
- import * as fs from 'node:fs/promises';
8
- import * as path from 'node:path';
9
-
10
- import type { ServicePort } from '../types.js';
11
-
12
- /**
13
- * Detects ports and service endpoints configured in a repository.
14
- *
15
- * Scans:
16
- * - .env / .env.* files for PORT= patterns
17
- * - docker-compose.yml for port mappings
18
- * - launchSettings.json (.NET) for applicationUrl
19
- * - package.json scripts for --port flags
20
- *
21
- * @param repoPath - Absolute path to the repository root.
22
- * @returns Array of detected {@link ServicePort} objects.
23
- */
24
- export async function detectPorts(repoPath: string): Promise<ServicePort[]> {
25
- const ports: ServicePort[] = [];
26
- const seen = new Set<number>();
27
-
28
- const addPort = (port: number, protocol: 'http' | 'https' | 'grpc' | 'other', source: string) => {
29
- if (!seen.has(port)) {
30
- seen.add(port);
31
- ports.push({ port, protocol, source });
32
- }
33
- };
34
-
35
- // ── .env files ────────────────────────────────────────────────────
36
- const envFiles = ['.env', '.env.local', '.env.development'];
37
- for (const envFile of envFiles) {
38
- try {
39
- const content = await fs.readFile(path.join(repoPath, envFile), 'utf-8');
40
- const portMatch = content.match(/PORT\s*=\s*(\d+)/i);
41
- if (portMatch) {
42
- addPort(parseInt(portMatch[1]!, 10), 'http', envFile);
43
- }
44
- } catch {
45
- // File doesn't exist
46
- }
47
- }
48
-
49
- // ── docker-compose.yml ────────────────────────────────────────────
50
- const composeFiles = ['docker-compose.yml', 'docker-compose.yaml', 'compose.yaml', 'compose.yml'];
51
- for (const composeFile of composeFiles) {
52
- try {
53
- const content = await fs.readFile(path.join(repoPath, composeFile), 'utf-8');
54
- // Simple regex to extract port mappings like "3000:3000" or "- 8080:80"
55
- const portRegex = /["']?(\d+):(\d+)["']?/g;
56
- let match: RegExpExecArray | null;
57
- while ((match = portRegex.exec(content)) !== null) {
58
- addPort(parseInt(match[1]!, 10), 'http', composeFile);
59
- }
60
- } catch {
61
- // File doesn't exist
62
- }
63
- }
64
-
65
- // ── launchSettings.json (.NET) ────────────────────────────────────
66
- const launchSettingsPath = path.join(repoPath, 'Properties', 'launchSettings.json');
67
- try {
68
- const raw = await fs.readFile(launchSettingsPath, 'utf-8');
69
- const settings = JSON.parse(raw) as Record<string, unknown>;
70
- const profiles = settings.profiles as Record<string, Record<string, unknown>> | undefined;
71
-
72
- if (profiles) {
73
- for (const profile of Object.values(profiles)) {
74
- const appUrl = profile.applicationUrl as string | undefined;
75
- if (appUrl) {
76
- const urlParts = appUrl.split(';');
77
- for (const url of urlParts) {
78
- const portMatch = url.match(/:(\d+)/);
79
- if (portMatch) {
80
- const protocol = url.startsWith('https') ? 'https' : 'http';
81
- addPort(parseInt(portMatch[1]!, 10), protocol, 'launchSettings.json');
82
- }
83
- }
84
- }
85
- }
86
- }
87
- } catch {
88
- // No launchSettings.json
89
- }
90
-
91
- // ── package.json scripts ──────────────────────────────────────────
92
- try {
93
- const raw = await fs.readFile(path.join(repoPath, 'package.json'), 'utf-8');
94
- const pkg = JSON.parse(raw) as Record<string, unknown>;
95
- const scripts = pkg.scripts as Record<string, string> | undefined;
96
-
97
- if (scripts) {
98
- for (const script of Object.values(scripts)) {
99
- const portMatch = script.match(/--port\s+(\d+)/i) ?? script.match(/-p\s+(\d+)/);
100
- if (portMatch) {
101
- addPort(parseInt(portMatch[1]!, 10), 'http', 'package.json scripts');
102
- }
103
- }
104
- }
105
- } catch {
106
- // No package.json
107
- }
108
-
109
- return ports;
110
- }
@@ -1,97 +0,0 @@
1
- /**
2
- * @module analyzers/index
3
- * Orchestrates all project analyzers and produces a full ProjectAnalysis
4
- * for each repository.
5
- */
6
-
7
- import chalk from 'chalk';
8
- import ora from 'ora';
9
-
10
- import type { ProjectAnalysis, RepoInfo } from '../types.js';
11
- import { detectTechStack } from './tech-stack.js';
12
- import { detectApis } from './detect-apis.js';
13
- import { detectDependencies, detectProducedPackages, detectNuGetFeeds } from './detect-deps.js';
14
- import { detectPorts } from './detect-ports.js';
15
- import { detectExistingAIConfigs } from './detect-existing.js';
16
- import { extractReadmeSummary } from './readme-summarizer.js';
17
- import { analyzeMessaging } from './messaging-analyzer.js';
18
- import { analyzeRunConfig } from './run-analyzer.js';
19
-
20
- /**
21
- * Runs all analyzers against a single repository and returns
22
- * a complete {@link ProjectAnalysis}.
23
- *
24
- * @param repo - The repo metadata.
25
- * @returns Full analysis result.
26
- */
27
- export async function analyzeRepo(repo: RepoInfo): Promise<ProjectAnalysis> {
28
- const [techStack, endpoints, dependencies, produces, nugetFeeds, ports, existingAIConfigs, readmeSummary, messaging, runConfig] =
29
- await Promise.all([
30
- detectTechStack(repo.path),
31
- detectApis(repo.path),
32
- detectDependencies(repo.path),
33
- detectProducedPackages(repo.path),
34
- detectNuGetFeeds(repo.path),
35
- detectPorts(repo.path),
36
- detectExistingAIConfigs(repo.path),
37
- extractReadmeSummary(repo.path),
38
- analyzeMessaging(repo.path),
39
- analyzeRunConfig(repo.path),
40
- ]);
41
-
42
- return {
43
- name: repo.name,
44
- path: repo.path,
45
- techStack,
46
- endpoints,
47
- dependencies,
48
- produces,
49
- nugetFeeds,
50
- ports,
51
- existingAIConfigs,
52
- readmeSummary,
53
- messaging,
54
- runConfig,
55
- };
56
- }
57
-
58
- /**
59
- * Analyzes all repos in a list and returns a Map of path → analysis.
60
- * Shows a progress spinner while running.
61
- *
62
- * @param repos - Array of repos to analyze.
63
- * @returns Map of repo path to {@link ProjectAnalysis}.
64
- */
65
- export async function analyzeAllRepos(
66
- repos: RepoInfo[],
67
- ): Promise<Map<string, ProjectAnalysis>> {
68
- const spinner = ora('Analyzing projects...').start();
69
- const results = new Map<string, ProjectAnalysis>();
70
-
71
- for (let i = 0; i < repos.length; i++) {
72
- const repo = repos[i]!;
73
- spinner.text = `Analyzing ${chalk.bold(repo.name)} (${i + 1}/${repos.length})...`;
74
-
75
- try {
76
- const analysis = await analyzeRepo(repo);
77
- results.set(repo.path, analysis);
78
- } catch (error) {
79
- const msg = error instanceof Error ? error.message : String(error);
80
- spinner.warn(`Failed to analyze ${repo.name}: ${msg}`);
81
- spinner.start();
82
- }
83
- }
84
-
85
- spinner.succeed(`Analyzed ${chalk.bold(results.size)} projects`);
86
- return results;
87
- }
88
-
89
- // Re-export individual analyzers
90
- export { detectTechStack } from './tech-stack.js';
91
- export { detectApis } from './detect-apis.js';
92
- export { detectDependencies, findInterRepoDependencies, detectNuGetFeeds } from './detect-deps.js';
93
- export { detectPorts } from './detect-ports.js';
94
- export { detectExistingAIConfigs } from './detect-existing.js';
95
- export { extractReadmeSummary } from './readme-summarizer.js';
96
- export { analyzeMessaging } from './messaging-analyzer.js';
97
- export { analyzeRunConfig } from './run-analyzer.js';
@@ -1,254 +0,0 @@
1
- /**
2
- * @module analyzers/messaging-analyzer
3
- * Detects publishers and subscribers for pub/sub messaging topologies
4
- * across multiple programming languages and frameworks.
5
- */
6
-
7
- import * as fs from 'node:fs/promises';
8
- import * as path from 'node:path';
9
- import { globby } from 'globby';
10
- import type { MessagingTopology, MessagePublisher, MessageSubscriber } from '../types.js';
11
-
12
- /**
13
- * Analyzes messaging/event topology in a repository.
14
- *
15
- * Scans C#, JS/TS, Python, and Go source files for publish and subscribe
16
- * patterns and extracts message/event contracts.
17
- *
18
- * @param repoPath - Absolute path to the repository root.
19
- * @returns Detected publishers and subscribers.
20
- */
21
- export async function analyzeMessaging(repoPath: string): Promise<MessagingTopology> {
22
- const publishers: MessagePublisher[] = [];
23
- const subscribers: MessageSubscriber[] = [];
24
-
25
- try {
26
- const files = await globby(
27
- ['**/*.ts', '**/*.js', '**/*.cs', '**/*.py', '**/*.go'],
28
- {
29
- cwd: repoPath,
30
- absolute: true,
31
- ignore: ['**/node_modules/**', '**/bin/**', '**/obj/**', '**/dist/**', '**/out/**', '**/.git/**'],
32
- }
33
- );
34
-
35
- for (const file of files) {
36
- try {
37
- const stat = await fs.stat(file);
38
- if (!stat.isFile() || stat.size > 200_000) continue; // Skip large files
39
-
40
- const content = await fs.readFile(file, 'utf-8');
41
- const relPath = path.relative(repoPath, file).replace(/\\/g, '/');
42
-
43
- if (file.endsWith('.cs')) {
44
- // ── C# Messaging Patterns ────────────────────────────────────────
45
-
46
- // MediatR notification handler
47
- // INotificationHandler<MyNotification>
48
- const mediatrSubRegex = /:\s*INotificationHandler\s*<\s*(\w+)\s*>/g;
49
- let match: RegExpExecArray | null;
50
- while ((match = mediatrSubRegex.exec(content)) !== null) {
51
- subscribers.push({
52
- contractType: match[1]!,
53
- handlerFile: relPath,
54
- registrationFile: relPath,
55
- });
56
- }
57
-
58
- // MediatR command/query handler
59
- // IRequestHandler<MyRequest, MyResponse> or IRequestHandler<MyRequest>
60
- const requestHandlerRegex = /:\s*IRequestHandler\s*<\s*(\w+)(?:\s*,\s*\w+)?\s*>/g;
61
- while ((match = requestHandlerRegex.exec(content)) !== null) {
62
- subscribers.push({
63
- contractType: match[1]!,
64
- handlerFile: relPath,
65
- registrationFile: relPath,
66
- });
67
- }
68
-
69
- // MassTransit Consumer: IConsumer<MyMessage>
70
- const massTransitSubRegex = /:\s*IConsumer\s*<\s*(\w+)\s*>/g;
71
- while ((match = massTransitSubRegex.exec(content)) !== null) {
72
- subscribers.push({
73
- contractType: match[1]!,
74
- handlerFile: relPath,
75
- registrationFile: relPath,
76
- });
77
- }
78
-
79
- // NServiceBus Handler: IHandleMessages<MyMessage>
80
- const nserviceBusSubRegex = /:\s*IHandleMessages\s*<\s*(\w+)\s*>/g;
81
- while ((match = nserviceBusSubRegex.exec(content)) !== null) {
82
- subscribers.push({
83
- contractType: match[1]!,
84
- handlerFile: relPath,
85
- registrationFile: relPath,
86
- });
87
- }
88
-
89
- // Azure Service Bus triggers: [ServiceBusTrigger("queueOrTopicName")]
90
- const sbtRegex = /\[ServiceBusTrigger\s*\(\s*"([^"]+)"(?:\s*,\s*"[^"]+")?\s*\)\]/g;
91
- while ((match = sbtRegex.exec(content)) !== null) {
92
- subscribers.push({
93
- contractType: 'ServiceBusMessage',
94
- handlerFile: relPath,
95
- registrationFile: relPath,
96
- });
97
- }
98
-
99
- // Publishers in C#
100
- // .Publish<MyEvent>( or .PublishAsync<MyEvent>( or .Send<MyCommand>( or .SendAsync<MyCommand>(
101
- const csPubRegex = /\b(?:Publish|PublishAsync|Send|SendAsync)\s*<\s*(\w+)\s*>\s*\(/g;
102
- while ((match = csPubRegex.exec(content)) !== null) {
103
- publishers.push({
104
- contractType: match[1]!,
105
- topicOrQueue: 'direct/inferred',
106
- publisherFile: relPath,
107
- });
108
- }
109
-
110
- const mediatorSendRegex = /\bmediator\s*\.\s*(?:Send|Publish)\s*\(\s*new\s+(\w+)\s*\(/gi;
111
- while ((match = mediatorSendRegex.exec(content)) !== null) {
112
- publishers.push({
113
- contractType: match[1]!,
114
- topicOrQueue: 'mediator',
115
- publisherFile: relPath,
116
- });
117
- }
118
-
119
- } else if (file.endsWith('.ts') || file.endsWith('.js')) {
120
- // ── TS/JS Messaging Patterns ─────────────────────────────────────
121
-
122
- // EventEmitter emitters: emit('event-name', ...)
123
- const tsEmitRegex = /\bemit\s*\(\s*['"`]([^'"`]+)['"`]/g;
124
- let match: RegExpExecArray | null;
125
- while ((match = tsEmitRegex.exec(content)) !== null) {
126
- publishers.push({
127
- contractType: match[1]!,
128
- topicOrQueue: 'EventEmitter',
129
- publisherFile: relPath,
130
- });
131
- }
132
-
133
- // EventEmitter listeners: on('event-name', ...) or addListener('event-name', ...)
134
- const tsOnRegex = /\b(?:on|addListener)\s*\(\s*['"`]([^'"`]+)['"`]/g;
135
- while ((match = tsOnRegex.exec(content)) !== null) {
136
- subscribers.push({
137
- contractType: match[1]!,
138
- handlerFile: relPath,
139
- registrationFile: relPath,
140
- });
141
- }
142
-
143
- // Kafka/RabbitMQ subscribe: topic: 'topic-name' inside subscription object
144
- const tsSubTopicRegex = /topic\s*:\s*['"`]([^'"`]+)['"`]/g;
145
- while ((match = tsSubTopicRegex.exec(content)) !== null) {
146
- subscribers.push({
147
- contractType: 'Kafka/MQ Message',
148
- handlerFile: relPath,
149
- registrationFile: relPath,
150
- });
151
- }
152
-
153
- // BullMQ add: queue.add('job-name', ...)
154
- const bullmqAddRegex = /\badd\s*\(\s*['"`]([^'"`]+)['"`]/g;
155
- while ((match = bullmqAddRegex.exec(content)) !== null) {
156
- publishers.push({
157
- contractType: match[1]!,
158
- topicOrQueue: 'BullMQ',
159
- publisherFile: relPath,
160
- });
161
- }
162
-
163
- } else if (file.endsWith('.py')) {
164
- // ── Python Messaging Patterns ────────────────────────────────────
165
-
166
- // Celery task definitions: @app.task or @shared_task
167
- const celeryTaskRegex = /@(?:[a-zA-Z0-9_]+\.)?(?:task|shared_task)(?:\([^)]*\))?\s*\n\s*def\s+(\w+)\s*\(/g;
168
- let match: RegExpExecArray | null;
169
- while ((match = celeryTaskRegex.exec(content)) !== null) {
170
- subscribers.push({
171
- contractType: match[1]!,
172
- handlerFile: relPath,
173
- registrationFile: relPath,
174
- });
175
- }
176
-
177
- // Celery task invocations: task.delay(...) or task.apply_async(...)
178
- const celeryDelayRegex = /\b(\w+)\s*\.\s*(?:delay|apply_async)\s*\(/g;
179
- while ((match = celeryDelayRegex.exec(content)) !== null) {
180
- publishers.push({
181
- contractType: match[1]!,
182
- topicOrQueue: 'Celery',
183
- publisherFile: relPath,
184
- });
185
- }
186
-
187
- // RabbitMQ basic_publish: routing_key='key'
188
- const pikaPublishRegex = /routing_key\s*=\s*['"`]([^'"`]+)['"`]/g;
189
- while ((match = pikaPublishRegex.exec(content)) !== null) {
190
- publishers.push({
191
- contractType: match[1]!,
192
- topicOrQueue: 'RabbitMQ',
193
- publisherFile: relPath,
194
- });
195
- }
196
-
197
- } else if (file.endsWith('.go')) {
198
- // ── Go Messaging Patterns ────────────────────────────────────────
199
-
200
- // Go publish/produce calls: Publish("topic", ...) or Produce("topic", ...)
201
- const goPubRegex = /\b(?:Publish|Produce|Send)\s*\(\s*['"`]([^'"`]+)['"`]/g;
202
- let match: RegExpExecArray | null;
203
- while ((match = goPubRegex.exec(content)) !== null) {
204
- publishers.push({
205
- contractType: 'GoMessage',
206
- topicOrQueue: match[1]!,
207
- publisherFile: relPath,
208
- });
209
- }
210
-
211
- // Go subscribe/consume calls: Subscribe("topic", ...) or Consume("topic", ...)
212
- const goSubRegex = /\b(?:Subscribe|Consume)\s*\(\s*['"`]([^'"`]+)['"`]/g;
213
- while ((match = goSubRegex.exec(content)) !== null) {
214
- subscribers.push({
215
- contractType: 'GoMessage',
216
- handlerFile: relPath,
217
- registrationFile: relPath,
218
- });
219
- }
220
- }
221
- } catch {
222
- // Skip unreadable files
223
- }
224
- }
225
- } catch {
226
- // Ignore errors
227
- }
228
-
229
- // De-duplicate publishers & subscribers
230
- const uniquePublishers: MessagePublisher[] = [];
231
- const seenPub = new Set<string>();
232
- for (const p of publishers) {
233
- const key = `${p.contractType}:${p.topicOrQueue}:${p.publisherFile}`;
234
- if (!seenPub.has(key)) {
235
- seenPub.add(key);
236
- uniquePublishers.push(p);
237
- }
238
- }
239
-
240
- const uniqueSubscribers: MessageSubscriber[] = [];
241
- const seenSub = new Set<string>();
242
- for (const s of subscribers) {
243
- const key = `${s.contractType}:${s.handlerFile}:${s.registrationFile}`;
244
- if (!seenSub.has(key)) {
245
- seenSub.add(key);
246
- uniqueSubscribers.push(s);
247
- }
248
- }
249
-
250
- return {
251
- publishers: uniquePublishers,
252
- subscribers: uniqueSubscribers,
253
- };
254
- }
@@ -1,102 +0,0 @@
1
- /**
2
- * @module analyzers/readme-summarizer
3
- * Extracts a summary from a repository's README.md file.
4
- */
5
-
6
- import * as fs from 'node:fs/promises';
7
- import * as path from 'node:path';
8
-
9
- /**
10
- * Reads a repository's README.md and extracts the first meaningful
11
- * prose paragraph as a summary. Looks for README.md (case-insensitive).
12
- *
13
- * It automatically strips badges, TOC lists, headers, HTML tags, and redacts ClientIDs/Secrets.
14
- *
15
- * @param repoPath - Absolute path to the repository root.
16
- * @returns The extracted summary text, or null if no README is found.
17
- */
18
- export async function extractReadmeSummary(
19
- repoPath: string,
20
- ): Promise<string | null> {
21
- // Try common README filenames
22
- const candidates = ['README.md', 'readme.md', 'Readme.md', 'README.MD', 'README'];
23
-
24
- let content: string | null = null;
25
-
26
- for (const filename of candidates) {
27
- try {
28
- content = await fs.readFile(path.join(repoPath, filename), 'utf-8');
29
- break;
30
- } catch {
31
- continue;
32
- }
33
- }
34
-
35
- if (!content) return null;
36
-
37
- // 1. Strip code blocks to avoid false matching on secrets/prose
38
- content = content.replace(/```[\s\S]*?```/g, '');
39
-
40
- // 2. Scrub ClientIDs/Secrets/Credentials
41
- // Match standard secret/ID patterns: GUIDs, client_id/secret variables, hex keys
42
- const clientSecretsRegex = /(client_id|client_secret|appid|secret|password|key|token|credential)\s*[:=]\s*["']?[a-zA-Z0-9-_\/\+\.]{16,}["']?/gi;
43
- const guidRegex = /[a-fA-F0-9]{8}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{4}-[a-fA-F0-9]{12}/g;
44
-
45
- content = content
46
- .replace(clientSecretsRegex, (match, p1) => `${p1}: [REDACTED]`)
47
- .replace(guidRegex, '[REDACTED_ID]');
48
-
49
- // 3. Process line-by-line to find the first prose paragraph
50
- const lines = content.split('\n');
51
- const proseLines: string[] = [];
52
-
53
- for (const line of lines) {
54
- const trimmed = line.trim();
55
-
56
- // Skip empty lines
57
- if (!trimmed) {
58
- if (proseLines.length > 0) {
59
- // We found a paragraph and hit an empty line. Let's finish!
60
- break;
61
- }
62
- continue;
63
- }
64
-
65
- // Skip HTML tags
66
- if (trimmed.startsWith('<') && trimmed.endsWith('>')) continue;
67
-
68
- // Skip badges
69
- if (trimmed.startsWith('[![') || (trimmed.startsWith('![') && trimmed.includes('http'))) continue;
70
-
71
- // Skip horizontal rules
72
- if (/^(-{3,}|={3,}|\*{3,})$/.test(trimmed)) continue;
73
-
74
- // Skip headers (Markdown # )
75
- if (trimmed.startsWith('#')) continue;
76
-
77
- // Detect Table of Contents (TOC) lists
78
- // Skip lines that look like: - [About](#about) or 1. [About](#about) or * 1. [Section](#section)
79
- if (/^(?:[-*+]\s*|\d+\.\s*)(\d+\.)?\s*\[[^\]]+\]\(#[^)]+\)/.test(trimmed)) {
80
- continue;
81
- }
82
-
83
- // Skip link-only lines (e.g., "[API Docs](https://...)")
84
- if (/^\[[^\]]+\]\([^)]+\)$/.test(trimmed)) {
85
- continue;
86
- }
87
-
88
- // Skip standard bullet and numbered lists if we are searching for prose (e.g. at the top of README before prose)
89
- if (proseLines.length === 0 && (/^[-*+]\s+/.test(trimmed) || /^\d+\.\s+/.test(trimmed))) {
90
- continue;
91
- }
92
-
93
- // Accumulate prose lines
94
- proseLines.push(line);
95
- }
96
-
97
- const summary = proseLines.join(' ').replace(/\s+/g, ' ').trim();
98
- if (!summary) return null;
99
-
100
- // Limit the summary to a concise paragraph (first 250 characters)
101
- return summary.length > 250 ? summary.slice(0, 250) + '...' : summary;
102
- }