@ash-technologia/fathom 0.1.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/CHANGELOG.md +62 -0
  2. package/LICENSE +21 -0
  3. package/README.md +725 -0
  4. package/dist/analyzers/architecture/graph.d.ts +29 -0
  5. package/dist/analyzers/architecture/graph.d.ts.map +1 -0
  6. package/dist/analyzers/architecture/graph.js +347 -0
  7. package/dist/analyzers/architecture/graph.js.map +1 -0
  8. package/dist/analyzers/architecture/imports.d.ts +24 -0
  9. package/dist/analyzers/architecture/imports.d.ts.map +1 -0
  10. package/dist/analyzers/architecture/imports.js +155 -0
  11. package/dist/analyzers/architecture/imports.js.map +1 -0
  12. package/dist/analyzers/architecture/index.d.ts +13 -0
  13. package/dist/analyzers/architecture/index.d.ts.map +1 -0
  14. package/dist/analyzers/architecture/index.js +217 -0
  15. package/dist/analyzers/architecture/index.js.map +1 -0
  16. package/dist/analyzers/architecture/types.d.ts +65 -0
  17. package/dist/analyzers/architecture/types.d.ts.map +1 -0
  18. package/dist/analyzers/architecture/types.js +2 -0
  19. package/dist/analyzers/architecture/types.js.map +1 -0
  20. package/dist/analyzers/cicd/index.d.ts +11 -0
  21. package/dist/analyzers/cicd/index.d.ts.map +1 -0
  22. package/dist/analyzers/cicd/index.js +157 -0
  23. package/dist/analyzers/cicd/index.js.map +1 -0
  24. package/dist/analyzers/dependencies/index.d.ts +11 -0
  25. package/dist/analyzers/dependencies/index.d.ts.map +1 -0
  26. package/dist/analyzers/dependencies/index.js +403 -0
  27. package/dist/analyzers/dependencies/index.js.map +1 -0
  28. package/dist/analyzers/dependencies/lockfiles.d.ts +19 -0
  29. package/dist/analyzers/dependencies/lockfiles.d.ts.map +1 -0
  30. package/dist/analyzers/dependencies/lockfiles.js +172 -0
  31. package/dist/analyzers/dependencies/lockfiles.js.map +1 -0
  32. package/dist/analyzers/dependencies/online.d.ts +19 -0
  33. package/dist/analyzers/dependencies/online.d.ts.map +1 -0
  34. package/dist/analyzers/dependencies/online.js +116 -0
  35. package/dist/analyzers/dependencies/online.js.map +1 -0
  36. package/dist/analyzers/dependencies/types.d.ts +49 -0
  37. package/dist/analyzers/dependencies/types.d.ts.map +1 -0
  38. package/dist/analyzers/dependencies/types.js +2 -0
  39. package/dist/analyzers/dependencies/types.js.map +1 -0
  40. package/dist/analyzers/documentation/index.d.ts +11 -0
  41. package/dist/analyzers/documentation/index.d.ts.map +1 -0
  42. package/dist/analyzers/documentation/index.js +166 -0
  43. package/dist/analyzers/documentation/index.js.map +1 -0
  44. package/dist/analyzers/git/index.d.ts +11 -0
  45. package/dist/analyzers/git/index.d.ts.map +1 -0
  46. package/dist/analyzers/git/index.js +180 -0
  47. package/dist/analyzers/git/index.js.map +1 -0
  48. package/dist/analyzers/project/index.d.ts +11 -0
  49. package/dist/analyzers/project/index.d.ts.map +1 -0
  50. package/dist/analyzers/project/index.js +95 -0
  51. package/dist/analyzers/project/index.js.map +1 -0
  52. package/dist/analyzers/quality/index.d.ts +11 -0
  53. package/dist/analyzers/quality/index.d.ts.map +1 -0
  54. package/dist/analyzers/quality/index.js +198 -0
  55. package/dist/analyzers/quality/index.js.map +1 -0
  56. package/dist/analyzers/security/index.d.ts +11 -0
  57. package/dist/analyzers/security/index.d.ts.map +1 -0
  58. package/dist/analyzers/security/index.js +236 -0
  59. package/dist/analyzers/security/index.js.map +1 -0
  60. package/dist/analyzers/testing/index.d.ts +11 -0
  61. package/dist/analyzers/testing/index.d.ts.map +1 -0
  62. package/dist/analyzers/testing/index.js +134 -0
  63. package/dist/analyzers/testing/index.js.map +1 -0
  64. package/dist/baseline/baseline.d.ts +20 -0
  65. package/dist/baseline/baseline.d.ts.map +1 -0
  66. package/dist/baseline/baseline.js +134 -0
  67. package/dist/baseline/baseline.js.map +1 -0
  68. package/dist/baseline/compare.d.ts +14 -0
  69. package/dist/baseline/compare.d.ts.map +1 -0
  70. package/dist/baseline/compare.js +113 -0
  71. package/dist/baseline/compare.js.map +1 -0
  72. package/dist/baseline/types.d.ts +25 -0
  73. package/dist/baseline/types.d.ts.map +1 -0
  74. package/dist/baseline/types.js +2 -0
  75. package/dist/baseline/types.js.map +1 -0
  76. package/dist/cli/commands.d.ts +39 -0
  77. package/dist/cli/commands.d.ts.map +1 -0
  78. package/dist/cli/commands.js +277 -0
  79. package/dist/cli/commands.js.map +1 -0
  80. package/dist/cli/index.d.ts +3 -0
  81. package/dist/cli/index.d.ts.map +1 -0
  82. package/dist/cli/index.js +29 -0
  83. package/dist/cli/index.js.map +1 -0
  84. package/dist/cli/options.d.ts +55 -0
  85. package/dist/cli/options.d.ts.map +1 -0
  86. package/dist/cli/options.js +258 -0
  87. package/dist/cli/options.js.map +1 -0
  88. package/dist/core/analyzer.d.ts +33 -0
  89. package/dist/core/analyzer.d.ts.map +1 -0
  90. package/dist/core/analyzer.js +19 -0
  91. package/dist/core/analyzer.js.map +1 -0
  92. package/dist/core/context.d.ts +90 -0
  93. package/dist/core/context.d.ts.map +1 -0
  94. package/dist/core/context.js +159 -0
  95. package/dist/core/context.js.map +1 -0
  96. package/dist/core/errors.d.ts +74 -0
  97. package/dist/core/errors.d.ts.map +1 -0
  98. package/dist/core/errors.js +109 -0
  99. package/dist/core/errors.js.map +1 -0
  100. package/dist/core/findings.d.ts +59 -0
  101. package/dist/core/findings.d.ts.map +1 -0
  102. package/dist/core/findings.js +34 -0
  103. package/dist/core/findings.js.map +1 -0
  104. package/dist/core/orchestrator.d.ts +44 -0
  105. package/dist/core/orchestrator.d.ts.map +1 -0
  106. package/dist/core/orchestrator.js +160 -0
  107. package/dist/core/orchestrator.js.map +1 -0
  108. package/dist/core/result.d.ts +169 -0
  109. package/dist/core/result.d.ts.map +1 -0
  110. package/dist/core/result.js +2 -0
  111. package/dist/core/result.js.map +1 -0
  112. package/dist/detectors/framework.d.ts +22 -0
  113. package/dist/detectors/framework.d.ts.map +1 -0
  114. package/dist/detectors/framework.js +147 -0
  115. package/dist/detectors/framework.js.map +1 -0
  116. package/dist/detectors/language.d.ts +13 -0
  117. package/dist/detectors/language.d.ts.map +1 -0
  118. package/dist/detectors/language.js +100 -0
  119. package/dist/detectors/language.js.map +1 -0
  120. package/dist/detectors/package-manager.d.ts +17 -0
  121. package/dist/detectors/package-manager.d.ts.map +1 -0
  122. package/dist/detectors/package-manager.js +67 -0
  123. package/dist/detectors/package-manager.js.map +1 -0
  124. package/dist/detectors/project-type.d.ts +20 -0
  125. package/dist/detectors/project-type.d.ts.map +1 -0
  126. package/dist/detectors/project-type.js +48 -0
  127. package/dist/detectors/project-type.js.map +1 -0
  128. package/dist/diff/analyzer.d.ts +16 -0
  129. package/dist/diff/analyzer.d.ts.map +1 -0
  130. package/dist/diff/analyzer.js +296 -0
  131. package/dist/diff/analyzer.js.map +1 -0
  132. package/dist/diff/git-diff.d.ts +44 -0
  133. package/dist/diff/git-diff.d.ts.map +1 -0
  134. package/dist/diff/git-diff.js +256 -0
  135. package/dist/diff/git-diff.js.map +1 -0
  136. package/dist/diff/types.d.ts +75 -0
  137. package/dist/diff/types.d.ts.map +1 -0
  138. package/dist/diff/types.js +2 -0
  139. package/dist/diff/types.js.map +1 -0
  140. package/dist/integrations/github/context.d.ts +22 -0
  141. package/dist/integrations/github/context.d.ts.map +1 -0
  142. package/dist/integrations/github/context.js +115 -0
  143. package/dist/integrations/github/context.js.map +1 -0
  144. package/dist/integrations/github/types.d.ts +30 -0
  145. package/dist/integrations/github/types.d.ts.map +1 -0
  146. package/dist/integrations/github/types.js +2 -0
  147. package/dist/integrations/github/types.js.map +1 -0
  148. package/dist/plugins/context.d.ts +13 -0
  149. package/dist/plugins/context.d.ts.map +1 -0
  150. package/dist/plugins/context.js +102 -0
  151. package/dist/plugins/context.js.map +1 -0
  152. package/dist/plugins/examples/react.d.ts +8 -0
  153. package/dist/plugins/examples/react.d.ts.map +1 -0
  154. package/dist/plugins/examples/react.js +129 -0
  155. package/dist/plugins/examples/react.js.map +1 -0
  156. package/dist/plugins/index.d.ts +5 -0
  157. package/dist/plugins/index.d.ts.map +1 -0
  158. package/dist/plugins/index.js +4 -0
  159. package/dist/plugins/index.js.map +1 -0
  160. package/dist/plugins/registry.d.ts +45 -0
  161. package/dist/plugins/registry.d.ts.map +1 -0
  162. package/dist/plugins/registry.js +236 -0
  163. package/dist/plugins/registry.js.map +1 -0
  164. package/dist/plugins/types.d.ts +148 -0
  165. package/dist/plugins/types.d.ts.map +1 -0
  166. package/dist/plugins/types.js +2 -0
  167. package/dist/plugins/types.js.map +1 -0
  168. package/dist/reporters/html.d.ts +11 -0
  169. package/dist/reporters/html.d.ts.map +1 -0
  170. package/dist/reporters/html.js +1352 -0
  171. package/dist/reporters/html.js.map +1 -0
  172. package/dist/reporters/json.d.ts +10 -0
  173. package/dist/reporters/json.d.ts.map +1 -0
  174. package/dist/reporters/json.js +11 -0
  175. package/dist/reporters/json.js.map +1 -0
  176. package/dist/reporters/markdown.d.ts +7 -0
  177. package/dist/reporters/markdown.d.ts.map +1 -0
  178. package/dist/reporters/markdown.js +74 -0
  179. package/dist/reporters/markdown.js.map +1 -0
  180. package/dist/reporters/sarif.d.ts +133 -0
  181. package/dist/reporters/sarif.d.ts.map +1 -0
  182. package/dist/reporters/sarif.js +268 -0
  183. package/dist/reporters/sarif.js.map +1 -0
  184. package/dist/reporters/terminal.d.ts +17 -0
  185. package/dist/reporters/terminal.d.ts.map +1 -0
  186. package/dist/reporters/terminal.js +423 -0
  187. package/dist/reporters/terminal.js.map +1 -0
  188. package/dist/rules/categories.d.ts +8 -0
  189. package/dist/rules/categories.d.ts.map +1 -0
  190. package/dist/rules/categories.js +23 -0
  191. package/dist/rules/categories.js.map +1 -0
  192. package/dist/rules/definitions.d.ts +25 -0
  193. package/dist/rules/definitions.d.ts.map +1 -0
  194. package/dist/rules/definitions.js +429 -0
  195. package/dist/rules/definitions.js.map +1 -0
  196. package/dist/rules/registry.d.ts +36 -0
  197. package/dist/rules/registry.d.ts.map +1 -0
  198. package/dist/rules/registry.js +64 -0
  199. package/dist/rules/registry.js.map +1 -0
  200. package/dist/rules/severity.d.ts +13 -0
  201. package/dist/rules/severity.d.ts.map +1 -0
  202. package/dist/rules/severity.js +15 -0
  203. package/dist/rules/severity.js.map +1 -0
  204. package/dist/scoring/confidence.d.ts +12 -0
  205. package/dist/scoring/confidence.d.ts.map +1 -0
  206. package/dist/scoring/confidence.js +22 -0
  207. package/dist/scoring/confidence.js.map +1 -0
  208. package/dist/scoring/score.d.ts +27 -0
  209. package/dist/scoring/score.d.ts.map +1 -0
  210. package/dist/scoring/score.js +81 -0
  211. package/dist/scoring/score.js.map +1 -0
  212. package/dist/scoring/weights.d.ts +11 -0
  213. package/dist/scoring/weights.d.ts.map +1 -0
  214. package/dist/scoring/weights.js +26 -0
  215. package/dist/scoring/weights.js.map +1 -0
  216. package/dist/utils/filesystem.d.ts +65 -0
  217. package/dist/utils/filesystem.d.ts.map +1 -0
  218. package/dist/utils/filesystem.js +299 -0
  219. package/dist/utils/filesystem.js.map +1 -0
  220. package/dist/utils/git.d.ts +47 -0
  221. package/dist/utils/git.d.ts.map +1 -0
  222. package/dist/utils/git.js +125 -0
  223. package/dist/utils/git.js.map +1 -0
  224. package/dist/utils/logger.d.ts +14 -0
  225. package/dist/utils/logger.d.ts.map +1 -0
  226. package/dist/utils/logger.js +25 -0
  227. package/dist/utils/logger.js.map +1 -0
  228. package/dist/utils/paths.d.ts +30 -0
  229. package/dist/utils/paths.d.ts.map +1 -0
  230. package/dist/utils/paths.js +48 -0
  231. package/dist/utils/paths.js.map +1 -0
  232. package/dist/utils/timing.d.ts +12 -0
  233. package/dist/utils/timing.d.ts.map +1 -0
  234. package/dist/utils/timing.js +19 -0
  235. package/dist/utils/timing.js.map +1 -0
  236. package/package.json +73 -0
@@ -0,0 +1,125 @@
1
+ import { execFile } from 'node:child_process';
2
+ import { promisify } from 'node:util';
3
+ import path from 'node:path';
4
+ const execFileAsync = promisify(execFile);
5
+ /**
6
+ * Execute a Git command safely in the given directory.
7
+ * Never throws — returns exit code and output.
8
+ *
9
+ * Security: Only pre-approved git subcommands are executed.
10
+ * Arguments are passed as an array, never interpolated into a shell string.
11
+ */
12
+ async function runGit(cwd, args) {
13
+ try {
14
+ const { stdout, stderr } = await execFileAsync('git', args, {
15
+ cwd,
16
+ timeout: 10_000,
17
+ maxBuffer: 1024 * 1024, // 1MB
18
+ });
19
+ return { stdout: stdout.trim(), stderr: stderr.trim(), exitCode: 0 };
20
+ }
21
+ catch (err) {
22
+ if (err && typeof err === 'object' && 'stdout' in err && 'stderr' in err && 'code' in err) {
23
+ const e = err;
24
+ return {
25
+ stdout: (e.stdout ?? '').trim(),
26
+ stderr: (e.stderr ?? '').trim(),
27
+ exitCode: e.code ?? 1,
28
+ };
29
+ }
30
+ return { stdout: '', stderr: String(err), exitCode: 1 };
31
+ }
32
+ }
33
+ /**
34
+ * Check whether the directory is a Git repository.
35
+ *
36
+ * We use `--absolute-git-dir` and verify the returned path is within
37
+ * `root` so that subdirectories nested inside a parent repo are not
38
+ * mistakenly reported as repos (e.g. test fixtures living inside Fathom).
39
+ */
40
+ export async function isGitRepository(root) {
41
+ const result = await runGit(root, ['rev-parse', '--absolute-git-dir']);
42
+ if (result.exitCode !== 0)
43
+ return false;
44
+ // Normalise both paths with trailing sep so prefix matching is unambiguous
45
+ const normalizedRoot = path.resolve(root) + path.sep;
46
+ const gitDir = path.resolve(result.stdout) + path.sep;
47
+ return gitDir.startsWith(normalizedRoot);
48
+ }
49
+ /**
50
+ * Check whether the repository has any commits.
51
+ */
52
+ export async function hasCommits(root) {
53
+ const result = await runGit(root, ['log', '--oneline', '-1']);
54
+ return result.exitCode === 0 && result.stdout.length > 0;
55
+ }
56
+ /**
57
+ * Get the list of files tracked by Git.
58
+ * Returns an empty array if not a Git repository.
59
+ */
60
+ export async function getTrackedFiles(root) {
61
+ const result = await runGit(root, ['ls-files']);
62
+ if (result.exitCode !== 0)
63
+ return [];
64
+ return result.stdout.split('\n').filter(Boolean);
65
+ }
66
+ /**
67
+ * Check whether the working directory has uncommitted changes.
68
+ */
69
+ export async function hasUncommittedChanges(root) {
70
+ const result = await runGit(root, ['status', '--porcelain']);
71
+ return result.exitCode === 0 && result.stdout.length > 0;
72
+ }
73
+ /**
74
+ * Get the list of large tracked files above a size threshold (bytes).
75
+ * Uses git cat-file to check sizes of tracked blobs.
76
+ */
77
+ export async function getLargeTrackedFiles(root, thresholdBytes) {
78
+ // git ls-files with size information
79
+ const result = await runGit(root, ['ls-files', '-s']);
80
+ if (result.exitCode !== 0)
81
+ return [];
82
+ const largeFiles = [];
83
+ for (const line of result.stdout.split('\n')) {
84
+ // Format: mode SP hash SP stage TAB filename
85
+ const match = /^\S+\s+(\S+)\s+\S+\t(.+)$/.exec(line);
86
+ if (!match)
87
+ continue;
88
+ const [, hash, filePath] = match;
89
+ if (!hash || !filePath)
90
+ continue;
91
+ // Get blob size
92
+ const sizeResult = await runGit(root, ['cat-file', '-s', hash]);
93
+ if (sizeResult.exitCode !== 0)
94
+ continue;
95
+ const size = parseInt(sizeResult.stdout, 10);
96
+ if (!isNaN(size) && size >= thresholdBytes) {
97
+ largeFiles.push({ file: path.normalize(filePath), sizeBytes: size });
98
+ }
99
+ }
100
+ return largeFiles;
101
+ }
102
+ /**
103
+ * Read the raw content of .gitignore. Returns null if not found.
104
+ */
105
+ export async function readGitignoreContent(root) {
106
+ const { readFileSafe } = await import('./filesystem.js');
107
+ return readFileSafe(path.join(root, '.gitignore'));
108
+ }
109
+ /**
110
+ * Check whether a given path pattern is mentioned in .gitignore content.
111
+ * This is a simplified heuristic — not a full gitignore pattern parser.
112
+ */
113
+ export function isPathMentionedInGitignore(gitignoreContent, targetPattern) {
114
+ const lines = gitignoreContent
115
+ .split('\n')
116
+ .map((l) => l.trim())
117
+ .filter((l) => l && !l.startsWith('#'));
118
+ return lines.some((line) => {
119
+ // Normalize both for comparison
120
+ const normalized = line.replace(/^\//, '').replace(/\/$/, '');
121
+ const target = targetPattern.replace(/^\//, '').replace(/\/$/, '');
122
+ return normalized === target || line === `/${target}` || line === target + '/';
123
+ });
124
+ }
125
+ //# sourceMappingURL=git.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"git.js","sourceRoot":"","sources":["../../src/utils/git.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * Minimal structured logger.
3
+ *
4
+ * - Respects NO_COLOR env variable.
5
+ * - When --json mode is active, all log output goes to stderr.
6
+ * - Debug logs only appear when FATHOM_DEBUG=1.
7
+ */
8
+ export declare const logger: {
9
+ debug(message: string): void;
10
+ info(message: string): void;
11
+ warn(message: string): void;
12
+ error(message: string): void;
13
+ };
14
+ //# sourceMappingURL=logger.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.d.ts","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH,eAAO,MAAM,MAAM;mBACF,MAAM,GAAG,IAAI;kBAId,MAAM,GAAG,IAAI;kBAGb,MAAM,GAAG,IAAI;mBAGZ,MAAM,GAAG,IAAI;CAG7B,CAAC"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Minimal structured logger.
3
+ *
4
+ * - Respects NO_COLOR env variable.
5
+ * - When --json mode is active, all log output goes to stderr.
6
+ * - Debug logs only appear when FATHOM_DEBUG=1.
7
+ */
8
+ const isDebug = process.env['FATHOM_DEBUG'] === '1';
9
+ export const logger = {
10
+ debug(message) {
11
+ if (!isDebug)
12
+ return;
13
+ process.stderr.write(`[debug] ${message}\n`);
14
+ },
15
+ info(message) {
16
+ process.stderr.write(`[info] ${message}\n`);
17
+ },
18
+ warn(message) {
19
+ process.stderr.write(`[warn] ${message}\n`);
20
+ },
21
+ error(message) {
22
+ process.stderr.write(`[error] ${message}\n`);
23
+ },
24
+ };
25
+ //# sourceMappingURL=logger.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"logger.js","sourceRoot":"","sources":["../../src/utils/logger.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,cAAc,CAAC,KAAK,GAAG,CAAC;AAEpD,MAAM,CAAC,MAAM,MAAM,GAAG;IACpB,KAAK,CAAC,OAAe;QACnB,IAAI,CAAC,OAAO;YAAE,OAAO;QACrB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,OAAO,IAAI,CAAC,CAAC;IAC/C,CAAC;IACD,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;IAC9C,CAAC;IACD,IAAI,CAAC,OAAe;QAClB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,UAAU,OAAO,IAAI,CAAC,CAAC;IAC9C,CAAC;IACD,KAAK,CAAC,OAAe;QACnB,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,WAAW,OAAO,IAAI,CAAC,CAAC;IAC/C,CAAC;CACF,CAAC"}
@@ -0,0 +1,30 @@
1
+ /**
2
+ * Convert an absolute path to a repository-relative path.
3
+ */
4
+ export declare function toRelativePath(root: string, absolutePath: string): string;
5
+ /**
6
+ * Normalize a path to use forward slashes regardless of platform.
7
+ * Used for consistent output across operating systems.
8
+ */
9
+ export declare function normalizeSlashes(p: string): string;
10
+ /**
11
+ * Get the extension of a file path (lowercase, with dot).
12
+ */
13
+ export declare function getExtension(filePath: string): string;
14
+ /**
15
+ * Get the filename without the directory.
16
+ */
17
+ export declare function getBasename(filePath: string): string;
18
+ /**
19
+ * Get the filename without the directory and extension.
20
+ */
21
+ export declare function getBasenameStem(filePath: string): string;
22
+ /**
23
+ * Compute the directory depth of a relative path.
24
+ */
25
+ export declare function computeDepth(relativePath: string): number;
26
+ /**
27
+ * Check whether a path (relative or absolute) contains a given directory component.
28
+ */
29
+ export declare function pathContainsDirectory(filePath: string, dirName: string): boolean;
30
+ //# sourceMappingURL=paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.d.ts","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAEA;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,MAAM,GAAG,MAAM,CAEzE;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAElD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAErD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEpD;AAED;;GAEG;AACH,wBAAgB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAIxD;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAGhF"}
@@ -0,0 +1,48 @@
1
+ import path from 'node:path';
2
+ /**
3
+ * Convert an absolute path to a repository-relative path.
4
+ */
5
+ export function toRelativePath(root, absolutePath) {
6
+ return normalizeSlashes(path.relative(root, absolutePath));
7
+ }
8
+ /**
9
+ * Normalize a path to use forward slashes regardless of platform.
10
+ * Used for consistent output across operating systems.
11
+ */
12
+ export function normalizeSlashes(p) {
13
+ return p.replace(/\\/g, '/');
14
+ }
15
+ /**
16
+ * Get the extension of a file path (lowercase, with dot).
17
+ */
18
+ export function getExtension(filePath) {
19
+ return path.extname(filePath).toLowerCase();
20
+ }
21
+ /**
22
+ * Get the filename without the directory.
23
+ */
24
+ export function getBasename(filePath) {
25
+ return path.basename(filePath);
26
+ }
27
+ /**
28
+ * Get the filename without the directory and extension.
29
+ */
30
+ export function getBasenameStem(filePath) {
31
+ const base = path.basename(filePath);
32
+ const ext = path.extname(base);
33
+ return base.slice(0, base.length - ext.length);
34
+ }
35
+ /**
36
+ * Compute the directory depth of a relative path.
37
+ */
38
+ export function computeDepth(relativePath) {
39
+ return normalizeSlashes(relativePath).split('/').length - 1;
40
+ }
41
+ /**
42
+ * Check whether a path (relative or absolute) contains a given directory component.
43
+ */
44
+ export function pathContainsDirectory(filePath, dirName) {
45
+ const parts = normalizeSlashes(filePath).split('/');
46
+ return parts.includes(dirName);
47
+ }
48
+ //# sourceMappingURL=paths.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paths.js","sourceRoot":"","sources":["../../src/utils/paths.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAE7B;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,IAAY,EAAE,YAAoB;IAC/D,OAAO,gBAAgB,CAAC,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,YAAY,CAAC,CAAC,CAAC;AAC7D,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,CAAS;IACxC,OAAO,CAAC,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC;AAC/B,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,QAAgB;IAC3C,OAAO,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;AAC9C,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,WAAW,CAAC,QAAgB;IAC1C,OAAO,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;AACjC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,eAAe,CAAC,QAAgB;IAC9C,MAAM,IAAI,GAAG,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;IACrC,MAAM,GAAG,GAAG,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC,CAAC;AACjD,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,YAAY,CAAC,YAAoB;IAC/C,OAAO,gBAAgB,CAAC,YAAY,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;AAC9D,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB,EAAE,OAAe;IACrE,MAAM,KAAK,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACpD,OAAO,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC"}
@@ -0,0 +1,12 @@
1
+ /**
2
+ * Timing utilities for measuring analysis duration.
3
+ */
4
+ /**
5
+ * Create a timer that returns elapsed milliseconds when called.
6
+ */
7
+ export declare function createTimer(): () => number;
8
+ /**
9
+ * Format milliseconds as a human-readable string.
10
+ */
11
+ export declare function formatDuration(ms: number): string;
12
+ //# sourceMappingURL=timing.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.d.ts","sourceRoot":"","sources":["../../src/utils/timing.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,WAAW,IAAI,MAAM,MAAM,CAG1C;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,EAAE,EAAE,MAAM,GAAG,MAAM,CAGjD"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * Timing utilities for measuring analysis duration.
3
+ */
4
+ /**
5
+ * Create a timer that returns elapsed milliseconds when called.
6
+ */
7
+ export function createTimer() {
8
+ const start = Date.now();
9
+ return () => Date.now() - start;
10
+ }
11
+ /**
12
+ * Format milliseconds as a human-readable string.
13
+ */
14
+ export function formatDuration(ms) {
15
+ if (ms < 1000)
16
+ return `${ms}ms`;
17
+ return `${(ms / 1000).toFixed(1)}s`;
18
+ }
19
+ //# sourceMappingURL=timing.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"timing.js","sourceRoot":"","sources":["../../src/utils/timing.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,MAAM,UAAU,WAAW;IACzB,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;IACzB,OAAO,GAAG,EAAE,CAAC,IAAI,CAAC,GAAG,EAAE,GAAG,KAAK,CAAC;AAClC,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,EAAU;IACvC,IAAI,EAAE,GAAG,IAAI;QAAE,OAAO,GAAG,EAAE,IAAI,CAAC;IAChC,OAAO,GAAG,CAAC,EAAE,GAAG,IAAI,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC;AACtC,CAAC"}
package/package.json ADDED
@@ -0,0 +1,73 @@
1
+ {
2
+ "name": "@ash-technologia/fathom",
3
+ "version": "0.1.0",
4
+ "description": "Local-first repository intelligence CLI. Know what's beneath the surface.",
5
+ "keywords": [
6
+ "repository",
7
+ "analysis",
8
+ "security",
9
+ "developer-tool",
10
+ "cli",
11
+ "code-quality",
12
+ "git",
13
+ "health-score"
14
+ ],
15
+ "homepage": "https://github.com/Ash-Technologia/Fathom#readme",
16
+ "bugs": {
17
+ "url": "https://github.com/Ash-Technologia/Fathom/issues"
18
+ },
19
+ "repository": {
20
+ "type": "git",
21
+ "url": "git+https://github.com/Ash-Technologia/Fathom.git"
22
+ },
23
+ "license": "MIT",
24
+ "author": "Fathom Contributors",
25
+ "type": "module",
26
+ "main": "./dist/cli/index.js",
27
+ "bin": {
28
+ "fathom": "./dist/cli/index.js"
29
+ },
30
+ "files": [
31
+ "dist",
32
+ "README.md",
33
+ "LICENSE",
34
+ "CHANGELOG.md"
35
+ ],
36
+ "scripts": {
37
+ "build": "tsc --project tsconfig.json",
38
+ "build:watch": "tsc --project tsconfig.json --watch",
39
+ "clean": "node scripts/clean.js",
40
+ "dev": "npm run build && node dist/cli/index.js",
41
+ "format": "prettier --write \"src/**/*.ts\" \"tests/**/*.ts\"",
42
+ "format:check": "prettier --check \"src/**/*.ts\" \"tests/**/*.ts\"",
43
+ "lint": "eslint \"src/**/*.ts\" \"tests/**/*.ts\"",
44
+ "lint:fix": "eslint --fix \"src/**/*.ts\" \"tests/**/*.ts\"",
45
+ "prepublishOnly": "npm run build && npm test",
46
+ "test": "vitest run",
47
+ "test:coverage": "vitest run --coverage",
48
+ "test:watch": "vitest",
49
+ "typecheck": "tsc --noEmit"
50
+ },
51
+ "dependencies": {
52
+ "chalk": "^5.3.0",
53
+ "commander": "^12.1.0",
54
+ "fast-glob": "^3.3.2",
55
+ "ora": "^8.1.1"
56
+ },
57
+ "devDependencies": {
58
+ "@eslint/js": "^9.11.1",
59
+ "@types/node": "^22.7.4",
60
+ "@vitest/coverage-v8": "^2.1.1",
61
+ "eslint": "^9.11.1",
62
+ "prettier": "^3.3.3",
63
+ "typescript": "^5.6.2",
64
+ "typescript-eslint": "^8.7.0",
65
+ "vitest": "^2.1.1"
66
+ },
67
+ "engines": {
68
+ "node": ">=18.0.0"
69
+ },
70
+ "publishConfig": {
71
+ "access": "public"
72
+ }
73
+ }