@slope-dev/slope 1.7.0 → 1.13.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 (164) hide show
  1. package/README.md +17 -2
  2. package/dist/cli/commands/analyze.d.ts +2 -0
  3. package/dist/cli/commands/analyze.d.ts.map +1 -0
  4. package/dist/cli/commands/analyze.js +106 -0
  5. package/dist/cli/commands/analyze.js.map +1 -0
  6. package/dist/cli/commands/guard.d.ts.map +1 -1
  7. package/dist/cli/commands/guard.js +4 -0
  8. package/dist/cli/commands/guard.js.map +1 -1
  9. package/dist/cli/commands/init.d.ts.map +1 -1
  10. package/dist/cli/commands/init.js +82 -3
  11. package/dist/cli/commands/init.js.map +1 -1
  12. package/dist/cli/commands/map.d.ts.map +1 -1
  13. package/dist/cli/commands/map.js +8 -19
  14. package/dist/cli/commands/map.js.map +1 -1
  15. package/dist/cli/commands/review-state.d.ts +6 -0
  16. package/dist/cli/commands/review-state.d.ts.map +1 -1
  17. package/dist/cli/commands/review-state.js +264 -1
  18. package/dist/cli/commands/review-state.js.map +1 -1
  19. package/dist/cli/commands/transcript.d.ts +10 -0
  20. package/dist/cli/commands/transcript.d.ts.map +1 -0
  21. package/dist/cli/commands/transcript.js +188 -0
  22. package/dist/cli/commands/transcript.js.map +1 -0
  23. package/dist/cli/commands/vision.d.ts +2 -0
  24. package/dist/cli/commands/vision.d.ts.map +1 -0
  25. package/dist/cli/commands/vision.js +53 -0
  26. package/dist/cli/commands/vision.js.map +1 -0
  27. package/dist/cli/guards/next-action.d.ts +4 -0
  28. package/dist/cli/guards/next-action.d.ts.map +1 -1
  29. package/dist/cli/guards/next-action.js +35 -0
  30. package/dist/cli/guards/next-action.js.map +1 -1
  31. package/dist/cli/guards/pr-review.d.ts +7 -0
  32. package/dist/cli/guards/pr-review.d.ts.map +1 -0
  33. package/dist/cli/guards/pr-review.js +34 -0
  34. package/dist/cli/guards/pr-review.js.map +1 -0
  35. package/dist/cli/guards/transcript.d.ts +12 -0
  36. package/dist/cli/guards/transcript.d.ts.map +1 -0
  37. package/dist/cli/guards/transcript.js +79 -0
  38. package/dist/cli/guards/transcript.js.map +1 -0
  39. package/dist/cli/index.js +28 -1
  40. package/dist/cli/index.js.map +1 -1
  41. package/dist/cli/registry.d.ts +12 -0
  42. package/dist/cli/registry.d.ts.map +1 -0
  43. package/dist/cli/registry.js +40 -0
  44. package/dist/cli/registry.js.map +1 -0
  45. package/dist/core/analyzers/backlog-merged.d.ts +13 -0
  46. package/dist/core/analyzers/backlog-merged.d.ts.map +1 -0
  47. package/dist/core/analyzers/backlog-merged.js +16 -0
  48. package/dist/core/analyzers/backlog-merged.js.map +1 -0
  49. package/dist/core/analyzers/backlog.d.ts +16 -0
  50. package/dist/core/analyzers/backlog.d.ts.map +1 -0
  51. package/dist/core/analyzers/backlog.js +103 -0
  52. package/dist/core/analyzers/backlog.js.map +1 -0
  53. package/dist/core/analyzers/ci.d.ts +6 -0
  54. package/dist/core/analyzers/ci.d.ts.map +1 -0
  55. package/dist/core/analyzers/ci.js +62 -0
  56. package/dist/core/analyzers/ci.js.map +1 -0
  57. package/dist/core/analyzers/complexity.d.ts +21 -0
  58. package/dist/core/analyzers/complexity.d.ts.map +1 -0
  59. package/dist/core/analyzers/complexity.js +76 -0
  60. package/dist/core/analyzers/complexity.js.map +1 -0
  61. package/dist/core/analyzers/docs.d.ts +6 -0
  62. package/dist/core/analyzers/docs.d.ts.map +1 -0
  63. package/dist/core/analyzers/docs.js +54 -0
  64. package/dist/core/analyzers/docs.js.map +1 -0
  65. package/dist/core/analyzers/git.d.ts +3 -0
  66. package/dist/core/analyzers/git.d.ts.map +1 -0
  67. package/dist/core/analyzers/git.js +88 -0
  68. package/dist/core/analyzers/git.js.map +1 -0
  69. package/dist/core/analyzers/github-backlog.d.ts +14 -0
  70. package/dist/core/analyzers/github-backlog.d.ts.map +1 -0
  71. package/dist/core/analyzers/github-backlog.js +43 -0
  72. package/dist/core/analyzers/github-backlog.js.map +1 -0
  73. package/dist/core/analyzers/index.d.ts +9 -0
  74. package/dist/core/analyzers/index.d.ts.map +1 -0
  75. package/dist/core/analyzers/index.js +95 -0
  76. package/dist/core/analyzers/index.js.map +1 -0
  77. package/dist/core/analyzers/stack.d.ts +3 -0
  78. package/dist/core/analyzers/stack.d.ts.map +1 -0
  79. package/dist/core/analyzers/stack.js +161 -0
  80. package/dist/core/analyzers/stack.js.map +1 -0
  81. package/dist/core/analyzers/structure.d.ts +3 -0
  82. package/dist/core/analyzers/structure.d.ts.map +1 -0
  83. package/dist/core/analyzers/structure.js +106 -0
  84. package/dist/core/analyzers/structure.js.map +1 -0
  85. package/dist/core/analyzers/testing.d.ts +3 -0
  86. package/dist/core/analyzers/testing.d.ts.map +1 -0
  87. package/dist/core/analyzers/testing.js +119 -0
  88. package/dist/core/analyzers/testing.js.map +1 -0
  89. package/dist/core/analyzers/types.d.ts +83 -0
  90. package/dist/core/analyzers/types.d.ts.map +1 -0
  91. package/dist/core/analyzers/types.js +3 -0
  92. package/dist/core/analyzers/types.js.map +1 -0
  93. package/dist/core/analyzers/walk.d.ts +11 -0
  94. package/dist/core/analyzers/walk.d.ts.map +1 -0
  95. package/dist/core/analyzers/walk.js +33 -0
  96. package/dist/core/analyzers/walk.js.map +1 -0
  97. package/dist/core/config.d.ts +3 -0
  98. package/dist/core/config.d.ts.map +1 -1
  99. package/dist/core/config.js +3 -0
  100. package/dist/core/config.js.map +1 -1
  101. package/dist/core/constants.d.ts +3 -1
  102. package/dist/core/constants.d.ts.map +1 -1
  103. package/dist/core/constants.js +8 -0
  104. package/dist/core/constants.js.map +1 -1
  105. package/dist/core/dashboard.d.ts.map +1 -1
  106. package/dist/core/dashboard.js +3 -1
  107. package/dist/core/dashboard.js.map +1 -1
  108. package/dist/core/dispersion.d.ts.map +1 -1
  109. package/dist/core/dispersion.js +6 -4
  110. package/dist/core/dispersion.js.map +1 -1
  111. package/dist/core/generators/common-issues.d.ts +9 -0
  112. package/dist/core/generators/common-issues.d.ts.map +1 -0
  113. package/dist/core/generators/common-issues.js +71 -0
  114. package/dist/core/generators/common-issues.js.map +1 -0
  115. package/dist/core/generators/config.d.ts +14 -0
  116. package/dist/core/generators/config.d.ts.map +1 -0
  117. package/dist/core/generators/config.js +45 -0
  118. package/dist/core/generators/config.js.map +1 -0
  119. package/dist/core/generators/first-sprint.d.ts +28 -0
  120. package/dist/core/generators/first-sprint.d.ts.map +1 -0
  121. package/dist/core/generators/first-sprint.js +71 -0
  122. package/dist/core/generators/first-sprint.js.map +1 -0
  123. package/dist/core/generators/roadmap.d.ts +14 -0
  124. package/dist/core/generators/roadmap.d.ts.map +1 -0
  125. package/dist/core/generators/roadmap.js +228 -0
  126. package/dist/core/generators/roadmap.js.map +1 -0
  127. package/dist/core/github.d.ts +31 -0
  128. package/dist/core/github.d.ts.map +1 -1
  129. package/dist/core/github.js +59 -0
  130. package/dist/core/github.js.map +1 -1
  131. package/dist/core/guard.d.ts +1 -1
  132. package/dist/core/guard.d.ts.map +1 -1
  133. package/dist/core/guard.js +14 -0
  134. package/dist/core/guard.js.map +1 -1
  135. package/dist/core/index.d.ts +25 -3
  136. package/dist/core/index.d.ts.map +1 -1
  137. package/dist/core/index.js +22 -1
  138. package/dist/core/index.js.map +1 -1
  139. package/dist/core/report.d.ts.map +1 -1
  140. package/dist/core/report.js +6 -4
  141. package/dist/core/report.js.map +1 -1
  142. package/dist/core/review.d.ts +41 -0
  143. package/dist/core/review.d.ts.map +1 -0
  144. package/dist/core/review.js +142 -0
  145. package/dist/core/review.js.map +1 -0
  146. package/dist/core/transcript.d.ts +23 -0
  147. package/dist/core/transcript.d.ts.map +1 -0
  148. package/dist/core/transcript.js +64 -0
  149. package/dist/core/transcript.js.map +1 -0
  150. package/dist/core/types.d.ts +40 -0
  151. package/dist/core/types.d.ts.map +1 -1
  152. package/dist/core/vision.d.ts +5 -0
  153. package/dist/core/vision.d.ts.map +1 -0
  154. package/dist/core/vision.js +58 -0
  155. package/dist/core/vision.js.map +1 -0
  156. package/dist/index.d.ts +2 -0
  157. package/dist/index.d.ts.map +1 -1
  158. package/dist/index.js +2 -0
  159. package/dist/index.js.map +1 -1
  160. package/dist/mcp/registry.d.ts +1 -1
  161. package/dist/mcp/registry.d.ts.map +1 -1
  162. package/dist/mcp/registry.js +170 -0
  163. package/dist/mcp/registry.js.map +1 -1
  164. package/package.json +1 -1
@@ -0,0 +1,62 @@
1
+ // SLOPE — CI Pipeline Analyzer
2
+ // Detects CI system from config files and parses for stage keywords.
3
+ import { readFileSync } from 'node:fs';
4
+ import { walkDir } from './walk.js';
5
+ const CI_DETECTIONS = [
6
+ { system: 'github-actions', pattern: '.github/workflows', isDir: true },
7
+ { system: 'circleci', pattern: '.circleci/config.yml', isDir: false },
8
+ { system: 'gitlab-ci', pattern: '.gitlab-ci.yml', isDir: false },
9
+ { system: 'jenkins', pattern: 'Jenkinsfile', isDir: false },
10
+ { system: 'travis', pattern: '.travis.yml', isDir: false },
11
+ ];
12
+ const TEST_KEYWORDS = /\b(test|lint|check|eslint|vitest|jest|pytest|mocha)\b/i;
13
+ const BUILD_KEYWORDS = /\b(build|compile|tsc|webpack|vite|rollup|esbuild)\b/i;
14
+ const DEPLOY_KEYWORDS = /\b(deploy|publish|release|push|upload)\b/i;
15
+ /**
16
+ * Analyze a codebase for CI pipeline configuration.
17
+ */
18
+ export function analyzeCI(cwd) {
19
+ const entries = walkDir(cwd, { maxDepth: 4 });
20
+ const configFiles = [];
21
+ let system;
22
+ for (const detection of CI_DETECTIONS) {
23
+ if (detection.isDir) {
24
+ // Look for YAML files inside the directory
25
+ const dirEntries = entries.filter(e => e.path.startsWith(detection.pattern + '/') && !e.isDirectory && /\.ya?ml$/.test(e.path));
26
+ if (dirEntries.length > 0) {
27
+ system = system ?? detection.system;
28
+ configFiles.push(...dirEntries.map(e => e.path));
29
+ }
30
+ }
31
+ else {
32
+ const match = entries.find(e => e.path === detection.pattern && !e.isDirectory);
33
+ if (match) {
34
+ system = system ?? detection.system;
35
+ configFiles.push(match.path);
36
+ }
37
+ }
38
+ }
39
+ let hasTestStage = false;
40
+ let hasBuildStage = false;
41
+ let hasDeployStage = false;
42
+ for (const file of configFiles) {
43
+ const fullPath = entries.find(e => e.path === file)?.fullPath;
44
+ if (!fullPath)
45
+ continue;
46
+ let content;
47
+ try {
48
+ content = readFileSync(fullPath, 'utf8');
49
+ }
50
+ catch {
51
+ continue;
52
+ }
53
+ if (TEST_KEYWORDS.test(content))
54
+ hasTestStage = true;
55
+ if (BUILD_KEYWORDS.test(content))
56
+ hasBuildStage = true;
57
+ if (DEPLOY_KEYWORDS.test(content))
58
+ hasDeployStage = true;
59
+ }
60
+ return { system, configFiles, hasTestStage, hasBuildStage, hasDeployStage };
61
+ }
62
+ //# sourceMappingURL=ci.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ci.js","sourceRoot":"","sources":["../../../src/core/analyzers/ci.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ import type { RepoProfile } from './types.js';
2
+ export interface ComplexityProfile {
3
+ estimatedPar: 3 | 4 | 5;
4
+ estimatedSlope: number;
5
+ slopeFactors: string[];
6
+ riskAreas: Array<{
7
+ module: string;
8
+ reason: string;
9
+ }>;
10
+ busFactor: Array<{
11
+ module: string;
12
+ topContributor: string;
13
+ pct: number;
14
+ }>;
15
+ }
16
+ /**
17
+ * Estimate complexity from a RepoProfile.
18
+ * Par is based on module count, slope from structural signals.
19
+ */
20
+ export declare function estimateComplexity(profile: RepoProfile): ComplexityProfile;
21
+ //# sourceMappingURL=complexity.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complexity.d.ts","sourceRoot":"","sources":["../../../src/core/analyzers/complexity.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAE9C,MAAM,WAAW,iBAAiB;IAChC,YAAY,EAAE,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,SAAS,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACrD,SAAS,EAAE,KAAK,CAAC;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,cAAc,EAAE,MAAM,CAAC;QAAC,GAAG,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAC3E;AAED;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,WAAW,GAAG,iBAAiB,CAgE1E"}
@@ -0,0 +1,76 @@
1
+ // SLOPE — Complexity Estimator
2
+ // Derives par, slope, risk areas, and bus factor from a RepoProfile.
3
+ /**
4
+ * Estimate complexity from a RepoProfile.
5
+ * Par is based on module count, slope from structural signals.
6
+ */
7
+ export function estimateComplexity(profile) {
8
+ // --- Par estimation from module count ---
9
+ const moduleCount = profile.structure.modules.length;
10
+ let estimatedPar;
11
+ if (moduleCount <= 2) {
12
+ estimatedPar = 3;
13
+ }
14
+ else if (moduleCount <= 4) {
15
+ estimatedPar = 4;
16
+ }
17
+ else {
18
+ estimatedPar = 5;
19
+ }
20
+ // --- Slope factors ---
21
+ const slopeFactors = [];
22
+ if (profile.structure.isMonorepo)
23
+ slopeFactors.push('monorepo');
24
+ if (profile.testing.testFileCount === 0)
25
+ slopeFactors.push('no-tests');
26
+ if (profile.git.contributors.length === 1)
27
+ slopeFactors.push('solo-developer');
28
+ if (profile.structure.largeFiles.length > 3)
29
+ slopeFactors.push('large-files');
30
+ if (profile.stack.frameworks.length > 5)
31
+ slopeFactors.push('complex-stack');
32
+ // --- Risk areas: high file count modules with no apparent test coverage ---
33
+ const testDirSet = new Set(profile.testing.testDirs.map(d => d.replace(/^tests?\//, '')));
34
+ const riskAreas = [];
35
+ for (const mod of profile.structure.modules) {
36
+ if (mod.fileCount > 20 && !testDirSet.has(mod.name)) {
37
+ riskAreas.push({ module: mod.name, reason: 'High file count with no dedicated test directory' });
38
+ }
39
+ }
40
+ // --- Bus factor: approximate from contributors ---
41
+ const busFactor = [];
42
+ const contributors = profile.git.contributors;
43
+ if (contributors.length === 1) {
44
+ // Solo developer — all modules have bus factor risk
45
+ for (const mod of profile.structure.modules) {
46
+ busFactor.push({
47
+ module: mod.name,
48
+ topContributor: contributors[0].name,
49
+ pct: 100,
50
+ });
51
+ }
52
+ }
53
+ else if (contributors.length > 0) {
54
+ // Check if top contributor dominates (>80% of commits)
55
+ const totalCommits = contributors.reduce((sum, c) => sum + c.commits, 0);
56
+ const top = contributors[0]; // already sorted by commit count from analyzer
57
+ const topPct = Math.round((top.commits / totalCommits) * 100);
58
+ if (topPct > 80) {
59
+ for (const mod of profile.structure.modules) {
60
+ busFactor.push({
61
+ module: mod.name,
62
+ topContributor: top.name,
63
+ pct: topPct,
64
+ });
65
+ }
66
+ }
67
+ }
68
+ return {
69
+ estimatedPar,
70
+ estimatedSlope: slopeFactors.length,
71
+ slopeFactors,
72
+ riskAreas,
73
+ busFactor,
74
+ };
75
+ }
76
+ //# sourceMappingURL=complexity.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"complexity.js","sourceRoot":"","sources":["../../../src/core/analyzers/complexity.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { DocsProfile } from './types.js';
2
+ /**
3
+ * Analyze a codebase for documentation presence and quality signals.
4
+ */
5
+ export declare function analyzeDocs(cwd: string): DocsProfile;
6
+ //# sourceMappingURL=docs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.d.ts","sourceRoot":"","sources":["../../../src/core/analyzers/docs.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAM9C;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,WAAW,CAwBpD"}
@@ -0,0 +1,54 @@
1
+ // SLOPE — Documentation Analyzer
2
+ // Checks for README, CONTRIBUTING, CHANGELOG, ADR, and API docs.
3
+ import { readFileSync, existsSync } from 'node:fs';
4
+ import { walkDir } from './walk.js';
5
+ const ADR_DIRS = ['docs/adr', 'docs/decisions'];
6
+ const API_DOC_DIRS = ['docs/api'];
7
+ const API_DOC_FILES = ['openapi.json', 'swagger.json', 'openapi.yaml', 'openapi.yml'];
8
+ /**
9
+ * Analyze a codebase for documentation presence and quality signals.
10
+ */
11
+ export function analyzeDocs(cwd) {
12
+ const entries = walkDir(cwd, { maxDepth: 3 });
13
+ const readmeEntry = entries.find(e => !e.isDirectory && /^readme\.md$/i.test(e.path));
14
+ const hasReadme = !!readmeEntry;
15
+ const hasContributing = entries.some(e => !e.isDirectory && /^contributing\.md$/i.test(e.path));
16
+ const hasChangelog = entries.some(e => !e.isDirectory && /^changelog\.md$/i.test(e.path));
17
+ const hasAdr = ADR_DIRS.some(dir => entries.some(e => e.isDirectory && e.path === dir));
18
+ const hasApiDocs = API_DOC_DIRS.some(dir => entries.some(e => e.isDirectory && e.path === dir)) || API_DOC_FILES.some(f => entries.some(e => !e.isDirectory && e.path === f));
19
+ let readmeSummary;
20
+ if (hasReadme && readmeEntry) {
21
+ readmeSummary = extractReadmeSummary(readmeEntry.fullPath);
22
+ }
23
+ return { hasReadme, readmeSummary, hasContributing, hasChangelog, hasAdr, hasApiDocs };
24
+ }
25
+ function extractReadmeSummary(readmePath) {
26
+ if (!existsSync(readmePath))
27
+ return undefined;
28
+ let content;
29
+ try {
30
+ content = readFileSync(readmePath, 'utf8');
31
+ }
32
+ catch {
33
+ return undefined;
34
+ }
35
+ const lines = content.split('\n');
36
+ for (const line of lines) {
37
+ const trimmed = line.trim();
38
+ // Skip headings, empty lines, badges, and HTML
39
+ if (!trimmed)
40
+ continue;
41
+ if (trimmed.startsWith('#'))
42
+ continue;
43
+ if (trimmed.startsWith('!['))
44
+ continue;
45
+ if (trimmed.startsWith('<'))
46
+ continue;
47
+ if (trimmed.startsWith('[!'))
48
+ continue;
49
+ // Found first paragraph text — take up to 200 chars
50
+ return trimmed.length > 200 ? trimmed.slice(0, 200) + '...' : trimmed;
51
+ }
52
+ return undefined;
53
+ }
54
+ //# sourceMappingURL=docs.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"docs.js","sourceRoot":"","sources":["../../../src/core/analyzers/docs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,3 @@
1
+ import type { GitProfile } from './types.js';
2
+ export declare function analyzeGit(cwd: string): Promise<GitProfile>;
3
+ //# sourceMappingURL=git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.d.ts","sourceRoot":"","sources":["../../../src/core/analyzers/git.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AAkC7C,wBAAsB,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,UAAU,CAAC,CAyDjE"}
@@ -0,0 +1,88 @@
1
+ // SLOPE — Git History Analyzer: commits, contributors, cadence, branches, releases
2
+ import { execSync } from 'node:child_process';
3
+ function git(cmd, cwd) {
4
+ try {
5
+ return execSync(`git ${cmd}`, { cwd, encoding: 'utf8', stdio: ['pipe', 'pipe', 'pipe'], timeout: 10000 }).trim();
6
+ }
7
+ catch {
8
+ return '';
9
+ }
10
+ }
11
+ function parseContributors(output) {
12
+ if (!output)
13
+ return [];
14
+ const contributors = [];
15
+ for (const line of output.split('\n')) {
16
+ const match = line.trim().match(/^\s*(\d+)\s+(.+?)\s+<([^>]+)>$/);
17
+ if (match) {
18
+ contributors.push({
19
+ commits: parseInt(match[1], 10),
20
+ name: match[2].trim(),
21
+ email: match[3].trim(),
22
+ });
23
+ }
24
+ }
25
+ return contributors.sort((a, b) => b.commits - a.commits);
26
+ }
27
+ function inferCadence(commitsPerWeek) {
28
+ if (commitsPerWeek >= 5)
29
+ return 'daily';
30
+ if (commitsPerWeek >= 2)
31
+ return 'weekly';
32
+ if (commitsPerWeek >= 0.5)
33
+ return 'biweekly';
34
+ if (commitsPerWeek >= 0.2)
35
+ return 'monthly';
36
+ return 'sporadic';
37
+ }
38
+ export async function analyzeGit(cwd) {
39
+ // Check if we're in a git repo
40
+ const isGit = git('rev-parse --is-inside-work-tree', cwd);
41
+ if (isGit !== 'true') {
42
+ return {
43
+ totalCommits: 0,
44
+ commitsLast90d: 0,
45
+ commitsPerWeek: 0,
46
+ contributors: [],
47
+ activeBranches: [],
48
+ inferredCadence: 'sporadic',
49
+ };
50
+ }
51
+ // Total commits
52
+ const totalStr = git('rev-list --count HEAD', cwd);
53
+ const totalCommits = parseInt(totalStr, 10) || 0;
54
+ // Commits in last 90 days
55
+ const recentStr = git('log --oneline --since="90 days ago"', cwd);
56
+ const commitsLast90d = recentStr ? recentStr.split('\n').length : 0;
57
+ // Commits per week (90 days ≈ 12.86 weeks)
58
+ const commitsPerWeek = commitsLast90d > 0 ? Math.round((commitsLast90d / 12.86) * 100) / 100 : 0;
59
+ // Contributors (last 90 days)
60
+ const shortlog = git('shortlog -sne --since="90 days ago" HEAD', cwd);
61
+ const contributors = parseContributors(shortlog);
62
+ // Active branches
63
+ const branchOutput = git('branch -r --no-merged', cwd);
64
+ const activeBranches = branchOutput
65
+ ? branchOutput.split('\n')
66
+ .map(b => b.trim())
67
+ .filter(b => b && !b.includes('HEAD') && !b.includes('->'))
68
+ .map(b => b.replace(/^origin\//, ''))
69
+ : [];
70
+ // Last release tag
71
+ let lastRelease;
72
+ const tag = git('describe --tags --abbrev=0', cwd);
73
+ if (tag) {
74
+ const tagDate = git(`log -1 --format=%aI ${tag}`, cwd);
75
+ lastRelease = { tag, date: tagDate || new Date().toISOString() };
76
+ }
77
+ const inferredCadence = inferCadence(commitsPerWeek);
78
+ return {
79
+ totalCommits,
80
+ commitsLast90d,
81
+ commitsPerWeek,
82
+ contributors,
83
+ activeBranches,
84
+ lastRelease,
85
+ inferredCadence,
86
+ };
87
+ }
88
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../../src/core/analyzers/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ import type { GitHubClient, GitHubIssue, GitHubMilestone } from '../github.js';
2
+ export interface GitHubBacklogAnalysis {
3
+ issues: GitHubIssue[];
4
+ issuesByLabel: Record<string, GitHubIssue[]>;
5
+ issuesByMilestone: Record<string, GitHubIssue[]>;
6
+ highPriority: GitHubIssue[];
7
+ milestones: GitHubMilestone[];
8
+ }
9
+ /**
10
+ * Analyze a GitHub repo's backlog: open issues grouped by label/milestone,
11
+ * high-priority detection, and milestone listing.
12
+ */
13
+ export declare function analyzeGitHubBacklog(owner: string, repo: string, client: GitHubClient): Promise<GitHubBacklogAnalysis>;
14
+ //# sourceMappingURL=github-backlog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-backlog.d.ts","sourceRoot":"","sources":["../../../src/core/analyzers/github-backlog.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,cAAc,CAAC;AAE/E,MAAM,WAAW,qBAAqB;IACpC,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IAC7C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,WAAW,EAAE,CAAC,CAAC;IACjD,YAAY,EAAE,WAAW,EAAE,CAAC;IAC5B,UAAU,EAAE,eAAe,EAAE,CAAC;CAC/B;AAWD;;;GAGG;AACH,wBAAsB,oBAAoB,CACxC,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,YAAY,GACnB,OAAO,CAAC,qBAAqB,CAAC,CA+BhC"}
@@ -0,0 +1,43 @@
1
+ // SLOPE — GitHub Backlog Analyzer
2
+ // Fetches open issues and milestones, groups by label and milestone.
3
+ const HIGH_PRIORITY_LABELS = ['priority', 'bug', 'security', 'critical', 'urgent', 'p0', 'p1'];
4
+ function isHighPriority(labels) {
5
+ return labels.some(label => {
6
+ const lower = label.toLowerCase();
7
+ return HIGH_PRIORITY_LABELS.some(hp => lower.includes(hp));
8
+ });
9
+ }
10
+ /**
11
+ * Analyze a GitHub repo's backlog: open issues grouped by label/milestone,
12
+ * high-priority detection, and milestone listing.
13
+ */
14
+ export async function analyzeGitHubBacklog(owner, repo, client) {
15
+ const [issues, milestones] = await Promise.all([
16
+ client.listIssues(owner, repo, { state: 'open', limit: 200 }),
17
+ client.listMilestones(owner, repo, { state: 'all' }),
18
+ ]);
19
+ const issuesByLabel = {};
20
+ const issuesByMilestone = {};
21
+ const highPriority = [];
22
+ for (const issue of issues) {
23
+ // Group by label
24
+ for (const label of issue.labels) {
25
+ if (!issuesByLabel[label])
26
+ issuesByLabel[label] = [];
27
+ issuesByLabel[label].push(issue);
28
+ }
29
+ // Group by milestone
30
+ if (issue.milestone) {
31
+ const msTitle = issue.milestone.title;
32
+ if (!issuesByMilestone[msTitle])
33
+ issuesByMilestone[msTitle] = [];
34
+ issuesByMilestone[msTitle].push(issue);
35
+ }
36
+ // High priority detection
37
+ if (isHighPriority(issue.labels)) {
38
+ highPriority.push(issue);
39
+ }
40
+ }
41
+ return { issues, issuesByLabel, issuesByMilestone, highPriority, milestones };
42
+ }
43
+ //# sourceMappingURL=github-backlog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"github-backlog.js","sourceRoot":"","sources":["../../../src/core/analyzers/github-backlog.ts"],"names":[],"mappings":"AAAA,kCAAkC;AAClC,qEAAqE;AAYrE,MAAM,oBAAoB,GAAG,CAAC,UAAU,EAAE,KAAK,EAAE,UAAU,EAAE,UAAU,EAAE,QAAQ,EAAE,IAAI,EAAE,IAAI,CAAC,CAAC;AAE/F,SAAS,cAAc,CAAC,MAAgB;IACtC,OAAO,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,EAAE;QACzB,MAAM,KAAK,GAAG,KAAK,CAAC,WAAW,EAAE,CAAC;QAClC,OAAO,oBAAoB,CAAC,IAAI,CAAC,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7D,CAAC,CAAC,CAAC;AACL,CAAC;AAED;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,KAAa,EACb,IAAY,EACZ,MAAoB;IAEpB,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC;QAC7C,MAAM,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,GAAG,EAAE,CAAC;QAC7D,MAAM,CAAC,cAAc,CAAC,KAAK,EAAE,IAAI,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC;KACrD,CAAC,CAAC;IAEH,MAAM,aAAa,GAAkC,EAAE,CAAC;IACxD,MAAM,iBAAiB,GAAkC,EAAE,CAAC;IAC5D,MAAM,YAAY,GAAkB,EAAE,CAAC;IAEvC,KAAK,MAAM,KAAK,IAAI,MAAM,EAAE,CAAC;QAC3B,iBAAiB;QACjB,KAAK,MAAM,KAAK,IAAI,KAAK,CAAC,MAAM,EAAE,CAAC;YACjC,IAAI,CAAC,aAAa,CAAC,KAAK,CAAC;gBAAE,aAAa,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC;YACrD,aAAa,CAAC,KAAK,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnC,CAAC;QAED,qBAAqB;QACrB,IAAI,KAAK,CAAC,SAAS,EAAE,CAAC;YACpB,MAAM,OAAO,GAAG,KAAK,CAAC,SAAS,CAAC,KAAK,CAAC;YACtC,IAAI,CAAC,iBAAiB,CAAC,OAAO,CAAC;gBAAE,iBAAiB,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;YACjE,iBAAiB,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACzC,CAAC;QAED,0BAA0B;QAC1B,IAAI,cAAc,CAAC,KAAK,CAAC,MAAM,CAAC,EAAE,CAAC;YACjC,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAC3B,CAAC;IACH,CAAC;IAED,OAAO,EAAE,MAAM,EAAE,aAAa,EAAE,iBAAiB,EAAE,YAAY,EAAE,UAAU,EAAE,CAAC;AAChF,CAAC"}
@@ -0,0 +1,9 @@
1
+ import type { AnalyzerName, RepoProfile } from './types.js';
2
+ export interface AnalyzerOptions {
3
+ cwd?: string;
4
+ analyzers?: AnalyzerName[];
5
+ }
6
+ export declare function runAnalyzers(opts?: AnalyzerOptions): Promise<RepoProfile>;
7
+ export declare function loadRepoProfile(cwd?: string): RepoProfile | null;
8
+ export declare function saveRepoProfile(profile: RepoProfile, cwd?: string): void;
9
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/core/analyzers/index.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EACV,YAAY,EACZ,WAAW,EAOZ,MAAM,YAAY,CAAC;AAEpB,MAAM,WAAW,eAAe;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,SAAS,CAAC,EAAE,YAAY,EAAE,CAAC;CAC5B;AA+BD,wBAAsB,YAAY,CAAC,IAAI,CAAC,EAAE,eAAe,GAAG,OAAO,CAAC,WAAW,CAAC,CAmD/E;AAED,wBAAgB,eAAe,CAAC,GAAG,CAAC,EAAE,MAAM,GAAG,WAAW,GAAG,IAAI,CAShE;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,WAAW,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,IAAI,CAQxE"}
@@ -0,0 +1,95 @@
1
+ // SLOPE — Repo Profile Analyzer Pipeline
2
+ import { readFileSync, writeFileSync, mkdirSync, existsSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ const ALL_ANALYZERS = ['stack', 'structure', 'git', 'testing', 'ci', 'docs'];
5
+ const PROFILE_FILE = 'repo-profile.json';
6
+ const SLOPE_DIR = '.slope';
7
+ function emptyStack() {
8
+ return { primaryLanguage: '', languages: {}, frameworks: [] };
9
+ }
10
+ function emptyStructure() {
11
+ return { totalFiles: 0, sourceFiles: 0, testFiles: 0, maxDepth: 0, isMonorepo: false, modules: [], largeFiles: [] };
12
+ }
13
+ function emptyGit() {
14
+ return { totalCommits: 0, commitsLast90d: 0, commitsPerWeek: 0, contributors: [], activeBranches: [], inferredCadence: 'sporadic' };
15
+ }
16
+ function emptyTesting() {
17
+ return { testFileCount: 0, hasTestScript: false, hasCoverage: false, testDirs: [] };
18
+ }
19
+ function emptyCi() {
20
+ return { configFiles: [], hasTestStage: false, hasBuildStage: false, hasDeployStage: false };
21
+ }
22
+ function emptyDocs() {
23
+ return { hasReadme: false, hasContributing: false, hasChangelog: false, hasAdr: false, hasApiDocs: false };
24
+ }
25
+ export async function runAnalyzers(opts) {
26
+ const cwd = opts?.cwd ?? process.cwd();
27
+ const requested = opts?.analyzers ?? ALL_ANALYZERS;
28
+ const profile = {
29
+ analyzedAt: new Date().toISOString(),
30
+ analyzersRun: requested,
31
+ stack: emptyStack(),
32
+ structure: emptyStructure(),
33
+ git: emptyGit(),
34
+ testing: emptyTesting(),
35
+ ci: emptyCi(),
36
+ docs: emptyDocs(),
37
+ };
38
+ for (const name of requested) {
39
+ switch (name) {
40
+ case 'stack': {
41
+ const { analyzeStack } = await import('./stack.js');
42
+ profile.stack = await analyzeStack(cwd);
43
+ break;
44
+ }
45
+ case 'structure': {
46
+ const { analyzeStructure } = await import('./structure.js');
47
+ profile.structure = await analyzeStructure(cwd);
48
+ break;
49
+ }
50
+ case 'git': {
51
+ const { analyzeGit } = await import('./git.js');
52
+ profile.git = await analyzeGit(cwd);
53
+ break;
54
+ }
55
+ case 'testing': {
56
+ const { analyzeTesting } = await import('./testing.js');
57
+ profile.testing = await analyzeTesting(cwd);
58
+ break;
59
+ }
60
+ case 'ci': {
61
+ const { analyzeCI } = await import('./ci.js');
62
+ profile.ci = analyzeCI(cwd);
63
+ break;
64
+ }
65
+ case 'docs': {
66
+ const { analyzeDocs } = await import('./docs.js');
67
+ profile.docs = analyzeDocs(cwd);
68
+ break;
69
+ }
70
+ }
71
+ }
72
+ return profile;
73
+ }
74
+ export function loadRepoProfile(cwd) {
75
+ const root = cwd ?? process.cwd();
76
+ const filePath = join(root, SLOPE_DIR, PROFILE_FILE);
77
+ if (!existsSync(filePath))
78
+ return null;
79
+ try {
80
+ return JSON.parse(readFileSync(filePath, 'utf8'));
81
+ }
82
+ catch {
83
+ return null;
84
+ }
85
+ }
86
+ export function saveRepoProfile(profile, cwd) {
87
+ const root = cwd ?? process.cwd();
88
+ const dir = join(root, SLOPE_DIR);
89
+ if (!existsSync(dir)) {
90
+ mkdirSync(dir, { recursive: true });
91
+ }
92
+ const filePath = join(dir, PROFILE_FILE);
93
+ writeFileSync(filePath, JSON.stringify(profile, null, 2) + '\n');
94
+ }
95
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/core/analyzers/index.ts"],"names":[],"mappings":"AAAA,yCAAyC;AACzC,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAiBjC,MAAM,aAAa,GAAmB,CAAC,OAAO,EAAE,WAAW,EAAE,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,CAAC,CAAC;AAE7F,MAAM,YAAY,GAAG,mBAAmB,CAAC;AACzC,MAAM,SAAS,GAAG,QAAQ,CAAC;AAE3B,SAAS,UAAU;IACjB,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,SAAS,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;AAChE,CAAC;AAED,SAAS,cAAc;IACrB,OAAO,EAAE,UAAU,EAAE,CAAC,EAAE,WAAW,EAAE,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,QAAQ,EAAE,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,EAAE,EAAE,CAAC;AACtH,CAAC;AAED,SAAS,QAAQ;IACf,OAAO,EAAE,YAAY,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,cAAc,EAAE,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,cAAc,EAAE,EAAE,EAAE,eAAe,EAAE,UAAU,EAAE,CAAC;AACtI,CAAC;AAED,SAAS,YAAY;IACnB,OAAO,EAAE,aAAa,EAAE,CAAC,EAAE,aAAa,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,EAAE,QAAQ,EAAE,EAAE,EAAE,CAAC;AACtF,CAAC;AAED,SAAS,OAAO;IACd,OAAO,EAAE,WAAW,EAAE,EAAE,EAAE,YAAY,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,CAAC;AAC/F,CAAC;AAED,SAAS,SAAS;IAChB,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,eAAe,EAAE,KAAK,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;AAC7G,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,IAAsB;IACvD,MAAM,GAAG,GAAG,IAAI,EAAE,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IACvC,MAAM,SAAS,GAAG,IAAI,EAAE,SAAS,IAAI,aAAa,CAAC;IAEnD,MAAM,OAAO,GAAgB;QAC3B,UAAU,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;QACpC,YAAY,EAAE,SAAS;QACvB,KAAK,EAAE,UAAU,EAAE;QACnB,SAAS,EAAE,cAAc,EAAE;QAC3B,GAAG,EAAE,QAAQ,EAAE;QACf,OAAO,EAAE,YAAY,EAAE;QACvB,EAAE,EAAE,OAAO,EAAE;QACb,IAAI,EAAE,SAAS,EAAE;KAClB,CAAC;IAEF,KAAK,MAAM,IAAI,IAAI,SAAS,EAAE,CAAC;QAC7B,QAAQ,IAAI,EAAE,CAAC;YACb,KAAK,OAAO,CAAC,CAAC,CAAC;gBACb,MAAM,EAAE,YAAY,EAAE,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;gBACpD,OAAO,CAAC,KAAK,GAAG,MAAM,YAAY,CAAC,GAAG,CAAC,CAAC;gBACxC,MAAM;YACR,CAAC;YACD,KAAK,WAAW,CAAC,CAAC,CAAC;gBACjB,MAAM,EAAE,gBAAgB,EAAE,GAAG,MAAM,MAAM,CAAC,gBAAgB,CAAC,CAAC;gBAC5D,OAAO,CAAC,SAAS,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;gBAChD,MAAM;YACR,CAAC;YACD,KAAK,KAAK,CAAC,CAAC,CAAC;gBACX,MAAM,EAAE,UAAU,EAAE,GAAG,MAAM,MAAM,CAAC,UAAU,CAAC,CAAC;gBAChD,OAAO,CAAC,GAAG,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;gBACpC,MAAM;YACR,CAAC;YACD,KAAK,SAAS,CAAC,CAAC,CAAC;gBACf,MAAM,EAAE,cAAc,EAAE,GAAG,MAAM,MAAM,CAAC,cAAc,CAAC,CAAC;gBACxD,OAAO,CAAC,OAAO,GAAG,MAAM,cAAc,CAAC,GAAG,CAAC,CAAC;gBAC5C,MAAM;YACR,CAAC;YACD,KAAK,IAAI,CAAC,CAAC,CAAC;gBACV,MAAM,EAAE,SAAS,EAAE,GAAG,MAAM,MAAM,CAAC,SAAS,CAAC,CAAC;gBAC9C,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC,GAAG,CAAC,CAAC;gBAC5B,MAAM;YACR,CAAC;YACD,KAAK,MAAM,CAAC,CAAC,CAAC;gBACZ,MAAM,EAAE,WAAW,EAAE,GAAG,MAAM,MAAM,CAAC,WAAW,CAAC,CAAC;gBAClD,OAAO,CAAC,IAAI,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC;gBAChC,MAAM;YACR,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,GAAY;IAC1C,MAAM,IAAI,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAClC,MAAM,QAAQ,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,EAAE,YAAY,CAAC,CAAC;IACrD,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAgB,CAAC;IACnE,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAAC,OAAoB,EAAE,GAAY;IAChE,MAAM,IAAI,GAAG,GAAG,IAAI,OAAO,CAAC,GAAG,EAAE,CAAC;IAClC,MAAM,GAAG,GAAG,IAAI,CAAC,IAAI,EAAE,SAAS,CAAC,CAAC;IAClC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,EAAE,CAAC;QACrB,SAAS,CAAC,GAAG,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAC;IACtC,CAAC;IACD,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,YAAY,CAAC,CAAC;IACzC,aAAa,CAAC,QAAQ,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,GAAG,IAAI,CAAC,CAAC;AACnE,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { StackProfile } from './types.js';
2
+ export declare function analyzeStack(cwd: string): Promise<StackProfile>;
3
+ //# sourceMappingURL=stack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"stack.d.ts","sourceRoot":"","sources":["../../../src/core/analyzers/stack.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAmD/C,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,YAAY,CAAC,CA6GrE"}