@codesentinel/codesentinel 0.1.1 → 1.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.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Aleix Alonso
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,102 @@
1
+ # CodeSentinel
2
+
3
+ CodeSentinel is a structural and evolutionary risk analysis engine for modern TypeScript/JavaScript codebases. It turns architecture, change history, and dependency health into a unified risk model that helps engineering teams spot fragility before it becomes failure.
4
+
5
+ This repository contains the initial monorepo scaffolding and CLI foundation. The analysis engines are intentionally lean right now, but the structure is designed to scale cleanly as the system grows.
6
+
7
+ ## Vision
8
+
9
+ CodeSentinel combines three signals into a single, explainable risk profile:
10
+
11
+ - **Structural risk**: dependency graph topology, cycles, coupling, fan-in/fan-out, boundary violations.
12
+ - **Evolutionary risk**: change frequency, hotspots, bus factor, volatility.
13
+ - **External risk**: transitive dependency exposure, maintainer risk, staleness and abandonment indicators.
14
+
15
+ The goal is a practical, engineering-grade model that supports both strategic architecture decisions and daily code review workflows.
16
+
17
+ ## Monorepo Layout
18
+
19
+ - `packages/core`: shared domain types and cross-cutting services.
20
+ - `packages/code-graph`: source graph analysis primitives.
21
+ - `packages/git-analyzer`: Git history and evolutionary signals.
22
+ - `packages/dependency-firewall`: external dependency and supply chain signals.
23
+ - `packages/risk-engine`: risk aggregation and scoring model.
24
+ - `packages/reporter`: structured report output (console, JSON, CI).
25
+ - `packages/cli`: user-facing CLI entrypoint.
26
+
27
+ Each package is standalone, ESM-only, TypeScript-first, and built with `tsup`. The CLI depends on `core`; domain packages are kept decoupled to avoid circular dependencies.
28
+
29
+ ## Requirements
30
+
31
+ - Node.js 24
32
+ - pnpm
33
+
34
+ ## Commands
35
+
36
+ - `pnpm install`
37
+ - `pnpm build`
38
+ - `pnpm dev`
39
+ - `pnpm test`
40
+ - `pnpm release`
41
+
42
+ ## CLI
43
+
44
+ Install globally with npm:
45
+
46
+ ```bash
47
+ npm install -g @codesentinel/codesentinel
48
+ ```
49
+
50
+ Then run:
51
+
52
+ ```bash
53
+ codesentinel analyze [path]
54
+ ```
55
+
56
+ Examples:
57
+
58
+ ```bash
59
+ codesentinel analyze
60
+ codesentinel analyze .
61
+ codesentinel analyze ../project
62
+ ```
63
+
64
+ When running through pnpm, pass CLI arguments after `--`:
65
+
66
+ ```bash
67
+ pnpm dev -- analyze
68
+ pnpm dev -- analyze .
69
+ pnpm dev -- analyze ../project
70
+ ```
71
+
72
+ ## Release Automation
73
+
74
+ - Pull requests to `main` run build and tests via `.github/workflows/ci.yml`.
75
+ - Merges to `main` run semantic-release via `.github/workflows/release.yml`.
76
+ - semantic-release bumps `packages/cli/package.json`, creates a GitHub release, publishes to npm, and pushes the version-bump commit back to `main`.
77
+ - Dependabot is configured monthly in `.github/dependabot.yml` for npm and GitHub Actions updates.
78
+
79
+ Trusted Publisher setup (no `NPM_TOKEN` secret):
80
+
81
+ - In npm package settings for `@codesentinel/codesentinel`, add a Trusted Publisher.
82
+ - Provider: `GitHub Actions`.
83
+ - Repository: `getcodesentinel/codesentinel`.
84
+ - Workflow filename: `release.yml`.
85
+ - Environment name: leave empty unless you explicitly use a GitHub Actions environment in this workflow.
86
+
87
+ Commit messages on `main` should follow Conventional Commits (example: `feat:`, `fix:`, `chore:`) so semantic-release can calculate versions automatically.
88
+
89
+ ## Contributing
90
+
91
+ This project aims to be production-grade and minimal. If you add new dependencies or abstractions, justify them clearly and keep the architecture clean.
92
+
93
+ ## ESM Import Policy
94
+
95
+ - The workspace uses `TypeScript` with `moduleResolution: "NodeNext"` and ESM output.
96
+ - For local relative imports, use `.js` specifiers in source files (example: `import { x } from "./x.js"`).
97
+ - Do not use `.ts` specifiers for runtime imports in package source files.
98
+ - This keeps emitted code and runtime resolution aligned with Node.js ESM behavior.
99
+
100
+ ## License
101
+
102
+ MIT
package/dist/index.js CHANGED
@@ -2,6 +2,12 @@
2
2
 
3
3
  // src/index.ts
4
4
  import { Command } from "commander";
5
+ import { readFileSync } from "fs";
6
+ import { dirname, resolve as resolve3 } from "path";
7
+ import { fileURLToPath } from "url";
8
+
9
+ // src/application/run-analyze-command.ts
10
+ import { resolve as resolve2 } from "path";
5
11
 
6
12
  // ../code-graph/dist/index.js
7
13
  import { extname, isAbsolute, relative, resolve } from "path";
@@ -442,24 +448,372 @@ var buildProjectGraphSummary = (input) => {
442
448
  return createGraphAnalysisSummary(input.projectPath, graphData);
443
449
  };
444
450
 
445
- // ../core/dist/index.js
446
- import { resolve as resolve2 } from "path";
447
- var resolveTargetPath = (inputPath, cwd = process.cwd()) => {
448
- const absolutePath = resolve2(cwd, inputPath ?? ".");
449
- return { absolutePath };
451
+ // ../git-analyzer/dist/index.js
452
+ import { execFileSync } from "child_process";
453
+ var pairKey = (a, b) => `${a}\0${b}`;
454
+ var round4 = (value) => Number(value.toFixed(4));
455
+ var computeBusFactor = (authorDistribution, threshold) => {
456
+ if (authorDistribution.length === 0) {
457
+ return 0;
458
+ }
459
+ let coveredShare = 0;
460
+ for (let i = 0; i < authorDistribution.length; i += 1) {
461
+ const entry = authorDistribution[i];
462
+ if (entry === void 0) {
463
+ continue;
464
+ }
465
+ coveredShare += entry.share;
466
+ if (coveredShare >= threshold) {
467
+ return i + 1;
468
+ }
469
+ }
470
+ return authorDistribution.length;
471
+ };
472
+ var finalizeAuthorDistribution = (authorCommits) => {
473
+ const totalCommits = [...authorCommits.values()].reduce((sum, value) => sum + value, 0);
474
+ if (totalCommits === 0) {
475
+ return [];
476
+ }
477
+ return [...authorCommits.entries()].map(([authorId, commits]) => ({
478
+ authorId,
479
+ commits,
480
+ share: round4(commits / totalCommits)
481
+ })).sort((a, b) => b.commits - a.commits || a.authorId.localeCompare(b.authorId));
482
+ };
483
+ var buildCouplingMatrix = (coChangeByPair, fileCommitCount, consideredCommits, skippedLargeCommits, maxCouplingPairs) => {
484
+ const allPairs = [];
485
+ for (const [key, coChangeCommits] of coChangeByPair.entries()) {
486
+ const [fileA, fileB] = key.split("\0");
487
+ if (fileA === void 0 || fileB === void 0) {
488
+ continue;
489
+ }
490
+ const fileACommits = fileCommitCount.get(fileA) ?? 0;
491
+ const fileBCommits = fileCommitCount.get(fileB) ?? 0;
492
+ const denominator = fileACommits + fileBCommits - coChangeCommits;
493
+ const couplingScore = denominator === 0 ? 0 : round4(coChangeCommits / denominator);
494
+ allPairs.push({
495
+ fileA,
496
+ fileB,
497
+ coChangeCommits,
498
+ couplingScore
499
+ });
500
+ }
501
+ allPairs.sort(
502
+ (a, b) => b.coChangeCommits - a.coChangeCommits || b.couplingScore - a.couplingScore || a.fileA.localeCompare(b.fileA) || a.fileB.localeCompare(b.fileB)
503
+ );
504
+ const truncated = allPairs.length > maxCouplingPairs;
505
+ return {
506
+ pairs: truncated ? allPairs.slice(0, maxCouplingPairs) : allPairs,
507
+ totalPairCount: allPairs.length,
508
+ consideredCommits,
509
+ skippedLargeCommits,
510
+ truncated
511
+ };
512
+ };
513
+ var selectHotspots = (files, config) => {
514
+ if (files.length === 0) {
515
+ return { hotspots: [], threshold: 0 };
516
+ }
517
+ const sorted = [...files].sort(
518
+ (a, b) => b.commitCount - a.commitCount || b.churnTotal - a.churnTotal || a.filePath.localeCompare(b.filePath)
519
+ );
520
+ const hotspotCount = Math.max(config.hotspotMinFiles, Math.ceil(sorted.length * config.hotspotTopPercent));
521
+ const selected = sorted.slice(0, hotspotCount);
522
+ const hotspots = selected.map((file, index) => ({
523
+ filePath: file.filePath,
524
+ rank: index + 1,
525
+ commitCount: file.commitCount,
526
+ churnTotal: file.churnTotal
527
+ }));
528
+ const threshold = selected[selected.length - 1]?.commitCount ?? 0;
529
+ return { hotspots, threshold };
530
+ };
531
+ var computeRepositoryEvolutionSummary = (targetPath, commits, config) => {
532
+ const fileStats = /* @__PURE__ */ new Map();
533
+ const coChangeByPair = /* @__PURE__ */ new Map();
534
+ const headCommitTimestamp = commits.length === 0 ? null : commits[commits.length - 1]?.authoredAtUnix ?? null;
535
+ const recentWindowStart = headCommitTimestamp === null ? Number.NEGATIVE_INFINITY : headCommitTimestamp - config.recentWindowDays * 24 * 60 * 60;
536
+ let consideredCommits = 0;
537
+ let skippedLargeCommits = 0;
538
+ for (const commit of commits) {
539
+ const uniqueFiles = /* @__PURE__ */ new Set();
540
+ for (const fileChange of commit.fileChanges) {
541
+ uniqueFiles.add(fileChange.filePath);
542
+ const current = fileStats.get(fileChange.filePath) ?? {
543
+ commitCount: 0,
544
+ recentCommitCount: 0,
545
+ churnAdded: 0,
546
+ churnDeleted: 0,
547
+ authors: /* @__PURE__ */ new Map()
548
+ };
549
+ current.churnAdded += fileChange.additions;
550
+ current.churnDeleted += fileChange.deletions;
551
+ fileStats.set(fileChange.filePath, current);
552
+ }
553
+ for (const filePath of uniqueFiles) {
554
+ const current = fileStats.get(filePath);
555
+ if (current === void 0) {
556
+ continue;
557
+ }
558
+ current.commitCount += 1;
559
+ if (commit.authoredAtUnix >= recentWindowStart) {
560
+ current.recentCommitCount += 1;
561
+ }
562
+ current.authors.set(commit.authorId, (current.authors.get(commit.authorId) ?? 0) + 1);
563
+ }
564
+ const orderedFiles = [...uniqueFiles].sort((a, b) => a.localeCompare(b));
565
+ if (orderedFiles.length > 1) {
566
+ if (orderedFiles.length <= config.maxFilesPerCommitForCoupling) {
567
+ consideredCommits += 1;
568
+ for (let i = 0; i < orderedFiles.length - 1; i += 1) {
569
+ for (let j = i + 1; j < orderedFiles.length; j += 1) {
570
+ const fileA = orderedFiles[i];
571
+ const fileB = orderedFiles[j];
572
+ if (fileA === void 0 || fileB === void 0) {
573
+ continue;
574
+ }
575
+ const key = pairKey(fileA, fileB);
576
+ coChangeByPair.set(key, (coChangeByPair.get(key) ?? 0) + 1);
577
+ }
578
+ }
579
+ } else {
580
+ skippedLargeCommits += 1;
581
+ }
582
+ }
583
+ }
584
+ const files = [...fileStats.entries()].map(([filePath, stats]) => {
585
+ const authorDistribution = finalizeAuthorDistribution(stats.authors);
586
+ const topAuthorShare = authorDistribution[0]?.share ?? 0;
587
+ return {
588
+ filePath,
589
+ commitCount: stats.commitCount,
590
+ frequencyPer100Commits: commits.length === 0 ? 0 : round4(stats.commitCount / commits.length * 100),
591
+ churnAdded: stats.churnAdded,
592
+ churnDeleted: stats.churnDeleted,
593
+ churnTotal: stats.churnAdded + stats.churnDeleted,
594
+ recentCommitCount: stats.recentCommitCount,
595
+ recentVolatility: stats.commitCount === 0 ? 0 : round4(stats.recentCommitCount / stats.commitCount),
596
+ topAuthorShare,
597
+ busFactor: computeBusFactor(authorDistribution, config.busFactorCoverageThreshold),
598
+ authorDistribution
599
+ };
600
+ }).sort((a, b) => a.filePath.localeCompare(b.filePath));
601
+ const fileCommitCount = new Map(files.map((file) => [file.filePath, file.commitCount]));
602
+ const coupling = buildCouplingMatrix(
603
+ coChangeByPair,
604
+ fileCommitCount,
605
+ consideredCommits,
606
+ skippedLargeCommits,
607
+ config.maxCouplingPairs
608
+ );
609
+ const { hotspots, threshold } = selectHotspots(files, config);
610
+ return {
611
+ targetPath,
612
+ available: true,
613
+ files,
614
+ hotspots,
615
+ coupling,
616
+ metrics: {
617
+ totalCommits: commits.length,
618
+ totalFiles: files.length,
619
+ headCommitTimestamp,
620
+ recentWindowDays: config.recentWindowDays,
621
+ hotspotTopPercent: config.hotspotTopPercent,
622
+ hotspotThresholdCommitCount: threshold
623
+ }
624
+ };
625
+ };
626
+ var DEFAULT_EVOLUTION_CONFIG = {
627
+ recentWindowDays: 30,
628
+ hotspotTopPercent: 0.1,
629
+ hotspotMinFiles: 1,
630
+ maxFilesPerCommitForCoupling: 200,
631
+ maxCouplingPairs: 500,
632
+ busFactorCoverageThreshold: 0.6
633
+ };
634
+ var createEffectiveConfig = (overrides) => ({
635
+ ...DEFAULT_EVOLUTION_CONFIG,
636
+ ...overrides
637
+ });
638
+ var analyzeRepositoryEvolution = (input, historyProvider) => {
639
+ if (!historyProvider.isGitRepository(input.repositoryPath)) {
640
+ return {
641
+ targetPath: input.repositoryPath,
642
+ available: false,
643
+ reason: "not_git_repository"
644
+ };
645
+ }
646
+ const commits = historyProvider.getCommitHistory(input.repositoryPath);
647
+ const config = createEffectiveConfig(input.config);
648
+ return computeRepositoryEvolutionSummary(input.repositoryPath, commits, config);
649
+ };
650
+ var GitCommandError = class extends Error {
651
+ args;
652
+ constructor(message, args) {
653
+ super(message);
654
+ this.name = "GitCommandError";
655
+ this.args = args;
656
+ }
657
+ };
658
+ var ExecGitCommandClient = class {
659
+ run(repositoryPath, args) {
660
+ try {
661
+ return execFileSync("git", ["-C", repositoryPath, ...args], {
662
+ encoding: "utf8",
663
+ maxBuffer: 1024 * 1024 * 64,
664
+ stdio: ["ignore", "pipe", "pipe"]
665
+ });
666
+ } catch (error) {
667
+ const message = error instanceof Error ? error.message : "Unknown git execution error";
668
+ throw new GitCommandError(message, args);
669
+ }
670
+ }
671
+ };
672
+ var COMMIT_RECORD_SEPARATOR = "";
673
+ var COMMIT_FIELD_SEPARATOR = "";
674
+ var GIT_LOG_FORMAT = `%x1e%H%x1f%at%x1f%an%x1f%ae`;
675
+ var parseInteger = (value) => {
676
+ if (value.length === 0) {
677
+ return null;
678
+ }
679
+ const parsed = Number.parseInt(value, 10);
680
+ if (Number.isNaN(parsed)) {
681
+ return null;
682
+ }
683
+ return parsed;
684
+ };
685
+ var parseRenamedPath = (pathSpec) => {
686
+ if (!pathSpec.includes(" => ")) {
687
+ return pathSpec;
688
+ }
689
+ const braceRenameMatch = pathSpec.match(/^(.*)\{(.+) => (.+)\}(.*)$/);
690
+ if (braceRenameMatch !== null) {
691
+ const [, prefix, , renamedTo, suffix] = braceRenameMatch;
692
+ return `${prefix}${renamedTo}${suffix}`;
693
+ }
694
+ const parts = pathSpec.split(" => ");
695
+ const finalPart = parts[parts.length - 1];
696
+ return finalPart ?? pathSpec;
697
+ };
698
+ var parseNumstatLine = (line) => {
699
+ const parts = line.split(" ");
700
+ if (parts.length < 3) {
701
+ return null;
702
+ }
703
+ const additionsRaw = parts[0];
704
+ const deletionsRaw = parts[1];
705
+ const pathRaw = parts.slice(2).join(" ");
706
+ if (additionsRaw === void 0 || deletionsRaw === void 0) {
707
+ return null;
708
+ }
709
+ const additions = additionsRaw === "-" ? 0 : parseInteger(additionsRaw);
710
+ const deletions = deletionsRaw === "-" ? 0 : parseInteger(deletionsRaw);
711
+ if (additions === null || deletions === null) {
712
+ return null;
713
+ }
714
+ const filePath = parseRenamedPath(pathRaw);
715
+ return {
716
+ filePath,
717
+ additions,
718
+ deletions
719
+ };
720
+ };
721
+ var parseGitLog = (rawLog) => {
722
+ const records = rawLog.split(COMMIT_RECORD_SEPARATOR).map((record) => record.trim()).filter((record) => record.length > 0);
723
+ const commits = [];
724
+ for (const record of records) {
725
+ const lines = record.split("\n").map((line) => line.trimEnd()).filter((line) => line.length > 0);
726
+ if (lines.length === 0) {
727
+ continue;
728
+ }
729
+ const headerParts = lines[0]?.split(COMMIT_FIELD_SEPARATOR) ?? [];
730
+ if (headerParts.length !== 4) {
731
+ continue;
732
+ }
733
+ const [hash, authoredAtRaw, authorName, authorEmail] = headerParts;
734
+ if (hash === void 0 || authoredAtRaw === void 0 || authorName === void 0 || authorEmail === void 0) {
735
+ continue;
736
+ }
737
+ const authoredAtUnix = parseInteger(authoredAtRaw);
738
+ if (authoredAtUnix === null) {
739
+ continue;
740
+ }
741
+ const fileChanges = [];
742
+ for (const line of lines.slice(1)) {
743
+ const parsedLine = parseNumstatLine(line);
744
+ if (parsedLine !== null) {
745
+ fileChanges.push(parsedLine);
746
+ }
747
+ }
748
+ commits.push({
749
+ hash,
750
+ authorId: authorEmail.toLowerCase(),
751
+ authorName,
752
+ authoredAtUnix,
753
+ fileChanges
754
+ });
755
+ }
756
+ commits.sort((a, b) => a.authoredAtUnix - b.authoredAtUnix || a.hash.localeCompare(b.hash));
757
+ return commits;
758
+ };
759
+ var NON_GIT_CODES = ["not a git repository", "not in a git directory"];
760
+ var isNotGitError = (error) => {
761
+ const lower = error.message.toLowerCase();
762
+ return NON_GIT_CODES.some((code) => lower.includes(code));
763
+ };
764
+ var GitCliHistoryProvider = class {
765
+ constructor(gitClient) {
766
+ this.gitClient = gitClient;
767
+ }
768
+ isGitRepository(repositoryPath) {
769
+ try {
770
+ const output = this.gitClient.run(repositoryPath, ["rev-parse", "--is-inside-work-tree"]);
771
+ return output.trim() === "true";
772
+ } catch (error) {
773
+ if (error instanceof GitCommandError && isNotGitError(error)) {
774
+ return false;
775
+ }
776
+ throw error;
777
+ }
778
+ }
779
+ getCommitHistory(repositoryPath) {
780
+ const output = this.gitClient.run(repositoryPath, [
781
+ "-c",
782
+ "core.quotepath=false",
783
+ "log",
784
+ "--no-merges",
785
+ "--date=unix",
786
+ `--pretty=format:${GIT_LOG_FORMAT}`,
787
+ "--numstat",
788
+ "--find-renames"
789
+ ]);
790
+ return parseGitLog(output);
791
+ }
792
+ };
793
+ var analyzeRepositoryEvolutionFromGit = (input) => {
794
+ const historyProvider = new GitCliHistoryProvider(new ExecGitCommandClient());
795
+ return analyzeRepositoryEvolution(input, historyProvider);
450
796
  };
451
797
 
452
798
  // src/application/run-analyze-command.ts
799
+ var resolveTargetPath = (inputPath, cwd) => resolve2(cwd, inputPath ?? ".");
453
800
  var runAnalyzeCommand = (inputPath) => {
454
801
  const invocationCwd = process.env["INIT_CWD"] ?? process.cwd();
455
- const target = resolveTargetPath(inputPath, invocationCwd);
456
- const summary = buildProjectGraphSummary({ projectPath: target.absolutePath });
802
+ const targetPath = resolveTargetPath(inputPath, invocationCwd);
803
+ const structural = buildProjectGraphSummary({ projectPath: targetPath });
804
+ const evolution = analyzeRepositoryEvolutionFromGit({ repositoryPath: targetPath });
805
+ const summary = {
806
+ structural,
807
+ evolution
808
+ };
457
809
  return JSON.stringify(summary, null, 2);
458
810
  };
459
811
 
460
812
  // src/index.ts
461
813
  var program = new Command();
462
- program.name("codesentinel").description("Structural and evolutionary risk analysis for TypeScript/JavaScript codebases").version("0.1.1");
814
+ var packageJsonPath = resolve3(dirname(fileURLToPath(import.meta.url)), "../package.json");
815
+ var { version } = JSON.parse(readFileSync(packageJsonPath, "utf8"));
816
+ program.name("codesentinel").description("Structural and evolutionary risk analysis for TypeScript/JavaScript codebases").version(version);
463
817
  program.command("analyze").argument("[path]", "path to the project to analyze").action((path) => {
464
818
  const output = runAnalyzeCommand(path);
465
819
  process.stdout.write(`${output}
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/index.ts","../../code-graph/src/domain/graph-model.ts","../../code-graph/src/domain/tarjan.ts","../../code-graph/src/domain/graph-metrics.ts","../../code-graph/src/infrastructure/typescript-project.ts","../../code-graph/src/application/build-project-graph-summary.ts","../../core/src/index.ts","../src/application/run-analyze-command.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { runAnalyzeCommand } from \"./application/run-analyze-command.js\";\n\nconst program = new Command();\n\nprogram\n .name(\"codesentinel\")\n .description(\"Structural and evolutionary risk analysis for TypeScript/JavaScript codebases\")\n .version(\"0.1.1\");\n\nprogram\n .command(\"analyze\")\n .argument(\"[path]\", \"path to the project to analyze\")\n .action((path?: string) => {\n const output = runAnalyzeCommand(path);\n process.stdout.write(`${output}\\n`);\n });\n\nif (process.argv.length <= 2) {\n program.outputHelp();\n process.exit(0);\n}\n\nprogram.parse(process.argv);\n","export type NodeRecord = {\n id: string;\n absolutePath: string;\n relativePath: string;\n};\n\nexport type EdgeRecord = {\n from: string;\n to: string;\n};\n\nexport type GraphData = {\n nodes: readonly NodeRecord[];\n edges: readonly EdgeRecord[];\n adjacencyById: ReadonlyMap<string, readonly string[]>;\n};\n\nconst edgeKey = (from: string, to: string): string => `${from}\\u0000${to}`;\n\nexport const createGraphData = (\n nodes: readonly NodeRecord[],\n rawEdges: readonly EdgeRecord[],\n): GraphData => {\n const sortedNodes = [...nodes].sort((a, b) => a.id.localeCompare(b.id));\n const knownNodeIds = new Set(sortedNodes.map((node) => node.id));\n\n const uniqueEdgeMap = new Map<string, EdgeRecord>();\n for (const edge of rawEdges) {\n if (edge.from === edge.to) {\n continue;\n }\n\n if (!knownNodeIds.has(edge.from) || !knownNodeIds.has(edge.to)) {\n continue;\n }\n\n uniqueEdgeMap.set(edgeKey(edge.from, edge.to), edge);\n }\n\n const sortedEdges = [...uniqueEdgeMap.values()].sort((a, b) => {\n const fromCompare = a.from.localeCompare(b.from);\n if (fromCompare !== 0) {\n return fromCompare;\n }\n\n return a.to.localeCompare(b.to);\n });\n\n const adjacency = new Map<string, string[]>();\n for (const node of sortedNodes) {\n adjacency.set(node.id, []);\n }\n\n for (const edge of sortedEdges) {\n adjacency.get(edge.from)?.push(edge.to);\n }\n\n const adjacencyById = new Map<string, readonly string[]>();\n for (const [nodeId, targets] of adjacency.entries()) {\n adjacencyById.set(nodeId, [...targets]);\n }\n\n return {\n nodes: sortedNodes,\n edges: sortedEdges,\n adjacencyById,\n };\n};\n","type TarjanResult = {\n components: readonly (readonly string[])[];\n};\n\nexport const runTarjanScc = (adjacencyById: ReadonlyMap<string, readonly string[]>): TarjanResult => {\n let index = 0;\n const indices = new Map<string, number>();\n const lowLink = new Map<string, number>();\n const stack: string[] = [];\n const onStack = new Set<string>();\n const components: string[][] = [];\n\n const strongConnect = (nodeId: string): void => {\n indices.set(nodeId, index);\n lowLink.set(nodeId, index);\n index += 1;\n\n stack.push(nodeId);\n onStack.add(nodeId);\n\n const neighbors = adjacencyById.get(nodeId) ?? [];\n for (const nextId of neighbors) {\n if (!indices.has(nextId)) {\n strongConnect(nextId);\n const nodeLowLink = lowLink.get(nodeId);\n const nextLowLink = lowLink.get(nextId);\n if (nodeLowLink !== undefined && nextLowLink !== undefined && nextLowLink < nodeLowLink) {\n lowLink.set(nodeId, nextLowLink);\n }\n continue;\n }\n\n if (onStack.has(nextId)) {\n const nodeLowLink = lowLink.get(nodeId);\n const nextIndex = indices.get(nextId);\n if (nodeLowLink !== undefined && nextIndex !== undefined && nextIndex < nodeLowLink) {\n lowLink.set(nodeId, nextIndex);\n }\n }\n }\n\n const nodeLowLink = lowLink.get(nodeId);\n const nodeIndex = indices.get(nodeId);\n if (nodeLowLink === undefined || nodeIndex === undefined || nodeLowLink !== nodeIndex) {\n return;\n }\n\n const component: string[] = [];\n for (;;) {\n const popped = stack.pop();\n if (popped === undefined) {\n break;\n }\n\n onStack.delete(popped);\n component.push(popped);\n if (popped === nodeId) {\n break;\n }\n }\n\n component.sort((a, b) => a.localeCompare(b));\n components.push(component);\n };\n\n const nodeIds = [...adjacencyById.keys()].sort((a, b) => a.localeCompare(b));\n for (const nodeId of nodeIds) {\n if (!indices.has(nodeId)) {\n strongConnect(nodeId);\n }\n }\n\n components.sort((a, b) => {\n const firstA = a[0] ?? \"\";\n const firstB = b[0] ?? \"\";\n return firstA.localeCompare(firstB);\n });\n\n return { components };\n};\n","import type { FileDependency, GraphAnalysisSummary, GraphCycle, GraphMetrics } from \"@codesentinel/core\";\nimport type { GraphData } from \"./graph-model.js\";\nimport { runTarjanScc } from \"./tarjan.js\";\n\ntype DepthComputation = {\n depthByNodeId: ReadonlyMap<string, number>;\n graphDepth: number;\n cycles: readonly GraphCycle[];\n};\n\nconst hasSelfLoop = (nodeId: string, adjacencyById: ReadonlyMap<string, readonly string[]>): boolean => {\n const targets = adjacencyById.get(nodeId) ?? [];\n return targets.includes(nodeId);\n};\n\nconst computeCyclesAndDepth = (graph: GraphData): DepthComputation => {\n const { components } = runTarjanScc(graph.adjacencyById);\n\n const cycles: GraphCycle[] = [];\n const componentByNodeId = new Map<string, number>();\n components.forEach((component, index) => {\n for (const nodeId of component) {\n componentByNodeId.set(nodeId, index);\n }\n\n if (component.length > 1) {\n cycles.push({ nodes: [...component] });\n return;\n }\n\n const onlyNode = component[0];\n if (onlyNode !== undefined && hasSelfLoop(onlyNode, graph.adjacencyById)) {\n cycles.push({ nodes: [...component] });\n }\n });\n\n const dagOutgoing = new Map<number, Set<number>>();\n const inDegree = new Map<number, number>();\n\n for (let i = 0; i < components.length; i += 1) {\n dagOutgoing.set(i, new Set());\n inDegree.set(i, 0);\n }\n\n for (const edge of graph.edges) {\n const fromComponent = componentByNodeId.get(edge.from);\n const toComponent = componentByNodeId.get(edge.to);\n\n if (fromComponent === undefined || toComponent === undefined || fromComponent === toComponent) {\n continue;\n }\n\n const outgoing = dagOutgoing.get(fromComponent);\n if (outgoing?.has(toComponent) === true) {\n continue;\n }\n\n outgoing?.add(toComponent);\n inDegree.set(toComponent, (inDegree.get(toComponent) ?? 0) + 1);\n }\n\n const queue: number[] = [];\n const depthByComponent = new Map<number, number>();\n\n for (let i = 0; i < components.length; i += 1) {\n if ((inDegree.get(i) ?? 0) === 0) {\n queue.push(i);\n depthByComponent.set(i, 0);\n }\n }\n\n let cursor = 0;\n while (cursor < queue.length) {\n const componentId = queue[cursor];\n cursor += 1;\n\n if (componentId === undefined) {\n continue;\n }\n\n const currentDepth = depthByComponent.get(componentId) ?? 0;\n const outgoing = dagOutgoing.get(componentId) ?? new Set<number>();\n\n for (const nextComponent of outgoing) {\n const nextDepth = depthByComponent.get(nextComponent) ?? 0;\n if (currentDepth + 1 > nextDepth) {\n depthByComponent.set(nextComponent, currentDepth + 1);\n }\n\n const remainingIncoming = (inDegree.get(nextComponent) ?? 0) - 1;\n inDegree.set(nextComponent, remainingIncoming);\n if (remainingIncoming === 0) {\n queue.push(nextComponent);\n }\n }\n }\n\n const depthByNodeId = new Map<string, number>();\n let graphDepth = 0;\n\n components.forEach((component, componentId) => {\n const componentDepth = depthByComponent.get(componentId) ?? 0;\n if (componentDepth > graphDepth) {\n graphDepth = componentDepth;\n }\n\n for (const nodeId of component) {\n depthByNodeId.set(nodeId, componentDepth);\n }\n });\n\n cycles.sort((a, b) => {\n const firstA = a.nodes[0] ?? \"\";\n const firstB = b.nodes[0] ?? \"\";\n return firstA.localeCompare(firstB);\n });\n\n return {\n depthByNodeId,\n graphDepth,\n cycles,\n };\n};\n\nexport const createGraphAnalysisSummary = (\n targetPath: string,\n graph: GraphData,\n): GraphAnalysisSummary => {\n const fanInById = new Map<string, number>();\n const fanOutById = new Map<string, number>();\n\n for (const node of graph.nodes) {\n fanInById.set(node.id, 0);\n fanOutById.set(node.id, graph.adjacencyById.get(node.id)?.length ?? 0);\n }\n\n for (const edge of graph.edges) {\n fanInById.set(edge.to, (fanInById.get(edge.to) ?? 0) + 1);\n }\n\n const { cycles, depthByNodeId, graphDepth } = computeCyclesAndDepth(graph);\n\n let maxFanIn = 0;\n let maxFanOut = 0;\n\n const files: FileDependency[] = graph.nodes.map((node) => {\n const fanIn = fanInById.get(node.id) ?? 0;\n const fanOut = fanOutById.get(node.id) ?? 0;\n\n if (fanIn > maxFanIn) {\n maxFanIn = fanIn;\n }\n\n if (fanOut > maxFanOut) {\n maxFanOut = fanOut;\n }\n\n return {\n id: node.id,\n relativePath: node.relativePath,\n directDependencies: graph.adjacencyById.get(node.id) ?? [],\n fanIn,\n fanOut,\n depth: depthByNodeId.get(node.id) ?? 0,\n };\n });\n\n const metrics: GraphMetrics = {\n nodeCount: graph.nodes.length,\n edgeCount: graph.edges.length,\n cycleCount: cycles.length,\n graphDepth,\n maxFanIn,\n maxFanOut,\n };\n\n return {\n targetPath,\n nodes: graph.nodes,\n edges: graph.edges,\n cycles,\n files,\n metrics,\n };\n};\n","import { extname, isAbsolute, relative, resolve } from \"node:path\";\nimport * as ts from \"typescript\";\nimport type { EdgeRecord, NodeRecord } from \"../domain/graph-model.js\";\n\ntype ParsedProject = {\n nodes: readonly NodeRecord[];\n edges: readonly EdgeRecord[];\n};\n\nconst SOURCE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".mts\", \".cts\", \".js\", \".jsx\", \".mjs\", \".cjs\"]);\n\nconst normalizePath = (pathValue: string): string => pathValue.replaceAll(\"\\\\\", \"/\");\n\nconst isProjectSourceFile = (filePath: string, projectRoot: string): boolean => {\n const extension = extname(filePath);\n if (!SOURCE_EXTENSIONS.has(extension)) {\n return false;\n }\n\n const relativePath = relative(projectRoot, filePath);\n if (relativePath.startsWith(\"..\")) {\n return false;\n }\n\n return !relativePath.includes(\"node_modules\");\n};\n\nconst findProjectFiles = (projectRoot: string): readonly string[] => {\n const files = ts.sys.readDirectory(projectRoot, [...SOURCE_EXTENSIONS], undefined, undefined);\n return files.map((filePath) => resolve(filePath));\n};\n\nconst parseTsConfig = (projectRoot: string): { fileNames: readonly string[]; options: ts.CompilerOptions } => {\n const configPath = ts.findConfigFile(projectRoot, ts.sys.fileExists, \"tsconfig.json\");\n if (configPath === undefined) {\n return {\n fileNames: findProjectFiles(projectRoot),\n options: {\n allowJs: true,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n },\n };\n }\n\n const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(\n configPath,\n {},\n {\n ...ts.sys,\n onUnRecoverableConfigFileDiagnostic: () => {\n throw new Error(`Failed to parse TypeScript configuration at ${configPath}`);\n },\n },\n );\n\n if (parsedCommandLine === undefined) {\n throw new Error(`Failed to parse TypeScript configuration at ${configPath}`);\n }\n\n const fileNames = parsedCommandLine.fileNames.map((filePath) => resolve(filePath));\n if (fileNames.length === 0) {\n return {\n fileNames: findProjectFiles(projectRoot),\n options: parsedCommandLine.options,\n };\n }\n\n return {\n fileNames,\n options: parsedCommandLine.options,\n };\n};\n\nconst getSpecifierFromExpression = (expression: ts.Expression): string | undefined => {\n if (ts.isStringLiteral(expression)) {\n return expression.text;\n }\n\n if (ts.isNoSubstitutionTemplateLiteral(expression)) {\n return expression.text;\n }\n\n return undefined;\n};\n\nconst hasRuntimeImport = (importDeclaration: ts.ImportDeclaration): boolean => {\n const importClause = importDeclaration.importClause;\n if (importClause === undefined) {\n return true;\n }\n\n if (importClause.isTypeOnly) {\n return false;\n }\n\n if (importClause.name !== undefined) {\n return true;\n }\n\n const namedBindings = importClause.namedBindings;\n if (namedBindings === undefined) {\n return false;\n }\n\n if (ts.isNamespaceImport(namedBindings)) {\n return true;\n }\n\n if (namedBindings.elements.length === 0) {\n return true;\n }\n\n return namedBindings.elements.some((element) => !element.isTypeOnly);\n};\n\nconst extractModuleSpecifiers = (sourceFile: ts.SourceFile): readonly string[] => {\n const specifiers = new Set<string>();\n\n const visit = (node: ts.Node): void => {\n if (ts.isImportDeclaration(node)) {\n if (hasRuntimeImport(node) && node.moduleSpecifier !== undefined) {\n const specifier = getSpecifierFromExpression(node.moduleSpecifier);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n return;\n }\n\n if (ts.isExportDeclaration(node)) {\n if (!node.isTypeOnly && node.moduleSpecifier !== undefined) {\n const specifier = getSpecifierFromExpression(node.moduleSpecifier);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n return;\n }\n\n if (ts.isCallExpression(node)) {\n if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length > 0) {\n const firstArgument = node.arguments[0];\n if (firstArgument !== undefined) {\n const specifier = getSpecifierFromExpression(firstArgument);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n\n if (ts.isIdentifier(node.expression) && node.expression.text === \"require\" && node.arguments.length > 0) {\n const firstArgument = node.arguments[0];\n if (firstArgument !== undefined) {\n const specifier = getSpecifierFromExpression(firstArgument);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n };\n\n visit(sourceFile);\n return [...specifiers];\n};\n\nexport const parseTypescriptProject = (projectPath: string): ParsedProject => {\n const projectRoot = isAbsolute(projectPath) ? projectPath : resolve(projectPath);\n const { fileNames, options } = parseTsConfig(projectRoot);\n\n const sourceFilePaths = fileNames\n .filter((filePath) => isProjectSourceFile(filePath, projectRoot))\n .map((filePath) => normalizePath(resolve(filePath)));\n\n const uniqueSourceFilePaths = [...new Set(sourceFilePaths)].sort((a, b) => a.localeCompare(b));\n const sourceFilePathSet = new Set(uniqueSourceFilePaths);\n\n const program = ts.createProgram({\n rootNames: uniqueSourceFilePaths,\n options,\n });\n\n const nodeByAbsolutePath = new Map<string, NodeRecord>();\n for (const sourcePath of uniqueSourceFilePaths) {\n const relativePath = normalizePath(relative(projectRoot, sourcePath));\n const nodeId = relativePath;\n nodeByAbsolutePath.set(sourcePath, {\n id: nodeId,\n absolutePath: sourcePath,\n relativePath,\n });\n }\n\n const resolverCache = new Map<string, string | undefined>();\n const edges: EdgeRecord[] = [];\n\n for (const sourcePath of uniqueSourceFilePaths) {\n const sourceFile = program.getSourceFile(sourcePath);\n if (sourceFile === undefined) {\n continue;\n }\n\n const fromNode = nodeByAbsolutePath.get(sourcePath);\n if (fromNode === undefined) {\n continue;\n }\n\n const moduleSpecifiers = extractModuleSpecifiers(sourceFile);\n for (const specifier of moduleSpecifiers) {\n const cacheKey = `${sourcePath}\\u0000${specifier}`;\n let resolvedPath = resolverCache.get(cacheKey);\n\n if (resolvedPath === undefined && !resolverCache.has(cacheKey)) {\n const resolved = ts.resolveModuleName(specifier, sourcePath, options, ts.sys).resolvedModule;\n if (resolved !== undefined) {\n resolvedPath = normalizePath(resolve(resolved.resolvedFileName));\n }\n resolverCache.set(cacheKey, resolvedPath);\n }\n\n if (resolvedPath === undefined || !sourceFilePathSet.has(resolvedPath)) {\n continue;\n }\n\n const toNode = nodeByAbsolutePath.get(resolvedPath);\n if (toNode === undefined) {\n continue;\n }\n\n edges.push({ from: fromNode.id, to: toNode.id });\n }\n }\n\n return {\n nodes: [...nodeByAbsolutePath.values()],\n edges,\n };\n};\n","import type { GraphAnalysisSummary } from \"@codesentinel/core\";\nimport { createGraphData } from \"../domain/graph-model.js\";\nimport { createGraphAnalysisSummary } from \"../domain/graph-metrics.js\";\nimport { parseTypescriptProject } from \"../infrastructure/typescript-project.js\";\n\nexport type BuildProjectGraphSummaryInput = {\n projectPath: string;\n};\n\nexport const buildProjectGraphSummary = (\n input: BuildProjectGraphSummaryInput,\n): GraphAnalysisSummary => {\n const parsedProject = parseTypescriptProject(input.projectPath);\n const graphData = createGraphData(parsedProject.nodes, parsedProject.edges);\n return createGraphAnalysisSummary(input.projectPath, graphData);\n};\n","import { resolve } from \"node:path\";\n\nexport type AnalyzeTarget = {\n absolutePath: string;\n};\n\nexport type GraphNode = {\n id: string;\n absolutePath: string;\n relativePath: string;\n};\n\nexport type GraphEdge = {\n from: string;\n to: string;\n};\n\nexport type GraphCycle = {\n nodes: readonly string[];\n};\n\nexport type FileDependency = {\n id: string;\n relativePath: string;\n directDependencies: readonly string[];\n fanIn: number;\n fanOut: number;\n depth: number;\n};\n\nexport type GraphMetrics = {\n nodeCount: number;\n edgeCount: number;\n cycleCount: number;\n graphDepth: number;\n maxFanIn: number;\n maxFanOut: number;\n};\n\nexport type GraphAnalysisSummary = {\n targetPath: string;\n nodes: readonly GraphNode[];\n edges: readonly GraphEdge[];\n cycles: readonly GraphCycle[];\n files: readonly FileDependency[];\n metrics: GraphMetrics;\n};\n\nexport const resolveTargetPath = (\n inputPath: string | undefined,\n cwd: string = process.cwd(),\n): AnalyzeTarget => {\n const absolutePath = resolve(cwd, inputPath ?? \".\");\n return { absolutePath };\n};\n","import { buildProjectGraphSummary } from \"@codesentinel/code-graph\";\nimport { resolveTargetPath } from \"@codesentinel/core\";\n\nexport const runAnalyzeCommand = (inputPath: string | undefined): string => {\n const invocationCwd = process.env[\"INIT_CWD\"] ?? process.cwd();\n const target = resolveTargetPath(inputPath, invocationCwd);\n const summary = buildProjectGraphSummary({ projectPath: target.absolutePath });\n return JSON.stringify(summary, null, 2);\n};\n"],"mappings":"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","names":["nodeLowLink","program","resolve"]}
1
+ {"version":3,"sources":["../src/index.ts","../src/application/run-analyze-command.ts","../../code-graph/src/domain/graph-model.ts","../../code-graph/src/domain/tarjan.ts","../../code-graph/src/domain/graph-metrics.ts","../../code-graph/src/infrastructure/typescript-project.ts","../../code-graph/src/application/build-project-graph-summary.ts","../../git-analyzer/src/domain/evolution-metrics.ts","../../git-analyzer/src/domain/evolution-types.ts","../../git-analyzer/src/application/analyze-repository-evolution.ts","../../git-analyzer/src/infrastructure/git-command-client.ts","../../git-analyzer/src/domain/git-log-format.ts","../../git-analyzer/src/parsing/git-log-parser.ts","../../git-analyzer/src/infrastructure/git-history-provider.ts","../../git-analyzer/src/index.ts"],"sourcesContent":["import { Command } from \"commander\";\nimport { readFileSync } from \"node:fs\";\nimport { dirname, resolve } from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { runAnalyzeCommand } from \"./application/run-analyze-command.js\";\n\nconst program = new Command();\nconst packageJsonPath = resolve(dirname(fileURLToPath(import.meta.url)), \"../package.json\");\nconst { version } = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version: string };\n\nprogram\n .name(\"codesentinel\")\n .description(\"Structural and evolutionary risk analysis for TypeScript/JavaScript codebases\")\n .version(version);\n\nprogram\n .command(\"analyze\")\n .argument(\"[path]\", \"path to the project to analyze\")\n .action((path?: string) => {\n const output = runAnalyzeCommand(path);\n process.stdout.write(`${output}\\n`);\n });\n\nif (process.argv.length <= 2) {\n program.outputHelp();\n process.exit(0);\n}\n\nprogram.parse(process.argv);\n","import { resolve } from \"node:path\";\nimport { buildProjectGraphSummary } from \"@codesentinel/code-graph\";\nimport { analyzeRepositoryEvolutionFromGit } from \"@codesentinel/git-analyzer\";\nimport type { AnalyzeSummary } from \"@codesentinel/core\";\n\nconst resolveTargetPath = (inputPath: string | undefined, cwd: string): string =>\n resolve(cwd, inputPath ?? \".\");\n\nexport const runAnalyzeCommand = (inputPath: string | undefined): string => {\n const invocationCwd = process.env[\"INIT_CWD\"] ?? process.cwd();\n const targetPath = resolveTargetPath(inputPath, invocationCwd);\n\n const structural = buildProjectGraphSummary({ projectPath: targetPath });\n const evolution = analyzeRepositoryEvolutionFromGit({ repositoryPath: targetPath });\n\n const summary: AnalyzeSummary = {\n structural,\n evolution,\n };\n\n return JSON.stringify(summary, null, 2);\n};\n","export type NodeRecord = {\n id: string;\n absolutePath: string;\n relativePath: string;\n};\n\nexport type EdgeRecord = {\n from: string;\n to: string;\n};\n\nexport type GraphData = {\n nodes: readonly NodeRecord[];\n edges: readonly EdgeRecord[];\n adjacencyById: ReadonlyMap<string, readonly string[]>;\n};\n\nconst edgeKey = (from: string, to: string): string => `${from}\\u0000${to}`;\n\nexport const createGraphData = (\n nodes: readonly NodeRecord[],\n rawEdges: readonly EdgeRecord[],\n): GraphData => {\n const sortedNodes = [...nodes].sort((a, b) => a.id.localeCompare(b.id));\n const knownNodeIds = new Set(sortedNodes.map((node) => node.id));\n\n const uniqueEdgeMap = new Map<string, EdgeRecord>();\n for (const edge of rawEdges) {\n if (edge.from === edge.to) {\n continue;\n }\n\n if (!knownNodeIds.has(edge.from) || !knownNodeIds.has(edge.to)) {\n continue;\n }\n\n uniqueEdgeMap.set(edgeKey(edge.from, edge.to), edge);\n }\n\n const sortedEdges = [...uniqueEdgeMap.values()].sort((a, b) => {\n const fromCompare = a.from.localeCompare(b.from);\n if (fromCompare !== 0) {\n return fromCompare;\n }\n\n return a.to.localeCompare(b.to);\n });\n\n const adjacency = new Map<string, string[]>();\n for (const node of sortedNodes) {\n adjacency.set(node.id, []);\n }\n\n for (const edge of sortedEdges) {\n adjacency.get(edge.from)?.push(edge.to);\n }\n\n const adjacencyById = new Map<string, readonly string[]>();\n for (const [nodeId, targets] of adjacency.entries()) {\n adjacencyById.set(nodeId, [...targets]);\n }\n\n return {\n nodes: sortedNodes,\n edges: sortedEdges,\n adjacencyById,\n };\n};\n","type TarjanResult = {\n components: readonly (readonly string[])[];\n};\n\nexport const runTarjanScc = (adjacencyById: ReadonlyMap<string, readonly string[]>): TarjanResult => {\n let index = 0;\n const indices = new Map<string, number>();\n const lowLink = new Map<string, number>();\n const stack: string[] = [];\n const onStack = new Set<string>();\n const components: string[][] = [];\n\n const strongConnect = (nodeId: string): void => {\n indices.set(nodeId, index);\n lowLink.set(nodeId, index);\n index += 1;\n\n stack.push(nodeId);\n onStack.add(nodeId);\n\n const neighbors = adjacencyById.get(nodeId) ?? [];\n for (const nextId of neighbors) {\n if (!indices.has(nextId)) {\n strongConnect(nextId);\n const nodeLowLink = lowLink.get(nodeId);\n const nextLowLink = lowLink.get(nextId);\n if (nodeLowLink !== undefined && nextLowLink !== undefined && nextLowLink < nodeLowLink) {\n lowLink.set(nodeId, nextLowLink);\n }\n continue;\n }\n\n if (onStack.has(nextId)) {\n const nodeLowLink = lowLink.get(nodeId);\n const nextIndex = indices.get(nextId);\n if (nodeLowLink !== undefined && nextIndex !== undefined && nextIndex < nodeLowLink) {\n lowLink.set(nodeId, nextIndex);\n }\n }\n }\n\n const nodeLowLink = lowLink.get(nodeId);\n const nodeIndex = indices.get(nodeId);\n if (nodeLowLink === undefined || nodeIndex === undefined || nodeLowLink !== nodeIndex) {\n return;\n }\n\n const component: string[] = [];\n for (;;) {\n const popped = stack.pop();\n if (popped === undefined) {\n break;\n }\n\n onStack.delete(popped);\n component.push(popped);\n if (popped === nodeId) {\n break;\n }\n }\n\n component.sort((a, b) => a.localeCompare(b));\n components.push(component);\n };\n\n const nodeIds = [...adjacencyById.keys()].sort((a, b) => a.localeCompare(b));\n for (const nodeId of nodeIds) {\n if (!indices.has(nodeId)) {\n strongConnect(nodeId);\n }\n }\n\n components.sort((a, b) => {\n const firstA = a[0] ?? \"\";\n const firstB = b[0] ?? \"\";\n return firstA.localeCompare(firstB);\n });\n\n return { components };\n};\n","import type { FileDependency, GraphAnalysisSummary, GraphCycle, GraphMetrics } from \"@codesentinel/core\";\nimport type { GraphData } from \"./graph-model.js\";\nimport { runTarjanScc } from \"./tarjan.js\";\n\ntype DepthComputation = {\n depthByNodeId: ReadonlyMap<string, number>;\n graphDepth: number;\n cycles: readonly GraphCycle[];\n};\n\nconst hasSelfLoop = (nodeId: string, adjacencyById: ReadonlyMap<string, readonly string[]>): boolean => {\n const targets = adjacencyById.get(nodeId) ?? [];\n return targets.includes(nodeId);\n};\n\nconst computeCyclesAndDepth = (graph: GraphData): DepthComputation => {\n const { components } = runTarjanScc(graph.adjacencyById);\n\n const cycles: GraphCycle[] = [];\n const componentByNodeId = new Map<string, number>();\n components.forEach((component, index) => {\n for (const nodeId of component) {\n componentByNodeId.set(nodeId, index);\n }\n\n if (component.length > 1) {\n cycles.push({ nodes: [...component] });\n return;\n }\n\n const onlyNode = component[0];\n if (onlyNode !== undefined && hasSelfLoop(onlyNode, graph.adjacencyById)) {\n cycles.push({ nodes: [...component] });\n }\n });\n\n const dagOutgoing = new Map<number, Set<number>>();\n const inDegree = new Map<number, number>();\n\n for (let i = 0; i < components.length; i += 1) {\n dagOutgoing.set(i, new Set());\n inDegree.set(i, 0);\n }\n\n for (const edge of graph.edges) {\n const fromComponent = componentByNodeId.get(edge.from);\n const toComponent = componentByNodeId.get(edge.to);\n\n if (fromComponent === undefined || toComponent === undefined || fromComponent === toComponent) {\n continue;\n }\n\n const outgoing = dagOutgoing.get(fromComponent);\n if (outgoing?.has(toComponent) === true) {\n continue;\n }\n\n outgoing?.add(toComponent);\n inDegree.set(toComponent, (inDegree.get(toComponent) ?? 0) + 1);\n }\n\n const queue: number[] = [];\n const depthByComponent = new Map<number, number>();\n\n for (let i = 0; i < components.length; i += 1) {\n if ((inDegree.get(i) ?? 0) === 0) {\n queue.push(i);\n depthByComponent.set(i, 0);\n }\n }\n\n let cursor = 0;\n while (cursor < queue.length) {\n const componentId = queue[cursor];\n cursor += 1;\n\n if (componentId === undefined) {\n continue;\n }\n\n const currentDepth = depthByComponent.get(componentId) ?? 0;\n const outgoing = dagOutgoing.get(componentId) ?? new Set<number>();\n\n for (const nextComponent of outgoing) {\n const nextDepth = depthByComponent.get(nextComponent) ?? 0;\n if (currentDepth + 1 > nextDepth) {\n depthByComponent.set(nextComponent, currentDepth + 1);\n }\n\n const remainingIncoming = (inDegree.get(nextComponent) ?? 0) - 1;\n inDegree.set(nextComponent, remainingIncoming);\n if (remainingIncoming === 0) {\n queue.push(nextComponent);\n }\n }\n }\n\n const depthByNodeId = new Map<string, number>();\n let graphDepth = 0;\n\n components.forEach((component, componentId) => {\n const componentDepth = depthByComponent.get(componentId) ?? 0;\n if (componentDepth > graphDepth) {\n graphDepth = componentDepth;\n }\n\n for (const nodeId of component) {\n depthByNodeId.set(nodeId, componentDepth);\n }\n });\n\n cycles.sort((a, b) => {\n const firstA = a.nodes[0] ?? \"\";\n const firstB = b.nodes[0] ?? \"\";\n return firstA.localeCompare(firstB);\n });\n\n return {\n depthByNodeId,\n graphDepth,\n cycles,\n };\n};\n\nexport const createGraphAnalysisSummary = (\n targetPath: string,\n graph: GraphData,\n): GraphAnalysisSummary => {\n const fanInById = new Map<string, number>();\n const fanOutById = new Map<string, number>();\n\n for (const node of graph.nodes) {\n fanInById.set(node.id, 0);\n fanOutById.set(node.id, graph.adjacencyById.get(node.id)?.length ?? 0);\n }\n\n for (const edge of graph.edges) {\n fanInById.set(edge.to, (fanInById.get(edge.to) ?? 0) + 1);\n }\n\n const { cycles, depthByNodeId, graphDepth } = computeCyclesAndDepth(graph);\n\n let maxFanIn = 0;\n let maxFanOut = 0;\n\n const files: FileDependency[] = graph.nodes.map((node) => {\n const fanIn = fanInById.get(node.id) ?? 0;\n const fanOut = fanOutById.get(node.id) ?? 0;\n\n if (fanIn > maxFanIn) {\n maxFanIn = fanIn;\n }\n\n if (fanOut > maxFanOut) {\n maxFanOut = fanOut;\n }\n\n return {\n id: node.id,\n relativePath: node.relativePath,\n directDependencies: graph.adjacencyById.get(node.id) ?? [],\n fanIn,\n fanOut,\n depth: depthByNodeId.get(node.id) ?? 0,\n };\n });\n\n const metrics: GraphMetrics = {\n nodeCount: graph.nodes.length,\n edgeCount: graph.edges.length,\n cycleCount: cycles.length,\n graphDepth,\n maxFanIn,\n maxFanOut,\n };\n\n return {\n targetPath,\n nodes: graph.nodes,\n edges: graph.edges,\n cycles,\n files,\n metrics,\n };\n};\n","import { extname, isAbsolute, relative, resolve } from \"node:path\";\nimport * as ts from \"typescript\";\nimport type { EdgeRecord, NodeRecord } from \"../domain/graph-model.js\";\n\ntype ParsedProject = {\n nodes: readonly NodeRecord[];\n edges: readonly EdgeRecord[];\n};\n\nconst SOURCE_EXTENSIONS = new Set([\".ts\", \".tsx\", \".mts\", \".cts\", \".js\", \".jsx\", \".mjs\", \".cjs\"]);\n\nconst normalizePath = (pathValue: string): string => pathValue.replaceAll(\"\\\\\", \"/\");\n\nconst isProjectSourceFile = (filePath: string, projectRoot: string): boolean => {\n const extension = extname(filePath);\n if (!SOURCE_EXTENSIONS.has(extension)) {\n return false;\n }\n\n const relativePath = relative(projectRoot, filePath);\n if (relativePath.startsWith(\"..\")) {\n return false;\n }\n\n return !relativePath.includes(\"node_modules\");\n};\n\nconst findProjectFiles = (projectRoot: string): readonly string[] => {\n const files = ts.sys.readDirectory(projectRoot, [...SOURCE_EXTENSIONS], undefined, undefined);\n return files.map((filePath) => resolve(filePath));\n};\n\nconst parseTsConfig = (projectRoot: string): { fileNames: readonly string[]; options: ts.CompilerOptions } => {\n const configPath = ts.findConfigFile(projectRoot, ts.sys.fileExists, \"tsconfig.json\");\n if (configPath === undefined) {\n return {\n fileNames: findProjectFiles(projectRoot),\n options: {\n allowJs: true,\n moduleResolution: ts.ModuleResolutionKind.NodeNext,\n },\n };\n }\n\n const parsedCommandLine = ts.getParsedCommandLineOfConfigFile(\n configPath,\n {},\n {\n ...ts.sys,\n onUnRecoverableConfigFileDiagnostic: () => {\n throw new Error(`Failed to parse TypeScript configuration at ${configPath}`);\n },\n },\n );\n\n if (parsedCommandLine === undefined) {\n throw new Error(`Failed to parse TypeScript configuration at ${configPath}`);\n }\n\n const fileNames = parsedCommandLine.fileNames.map((filePath) => resolve(filePath));\n if (fileNames.length === 0) {\n return {\n fileNames: findProjectFiles(projectRoot),\n options: parsedCommandLine.options,\n };\n }\n\n return {\n fileNames,\n options: parsedCommandLine.options,\n };\n};\n\nconst getSpecifierFromExpression = (expression: ts.Expression): string | undefined => {\n if (ts.isStringLiteral(expression)) {\n return expression.text;\n }\n\n if (ts.isNoSubstitutionTemplateLiteral(expression)) {\n return expression.text;\n }\n\n return undefined;\n};\n\nconst hasRuntimeImport = (importDeclaration: ts.ImportDeclaration): boolean => {\n const importClause = importDeclaration.importClause;\n if (importClause === undefined) {\n return true;\n }\n\n if (importClause.isTypeOnly) {\n return false;\n }\n\n if (importClause.name !== undefined) {\n return true;\n }\n\n const namedBindings = importClause.namedBindings;\n if (namedBindings === undefined) {\n return false;\n }\n\n if (ts.isNamespaceImport(namedBindings)) {\n return true;\n }\n\n if (namedBindings.elements.length === 0) {\n return true;\n }\n\n return namedBindings.elements.some((element) => !element.isTypeOnly);\n};\n\nconst extractModuleSpecifiers = (sourceFile: ts.SourceFile): readonly string[] => {\n const specifiers = new Set<string>();\n\n const visit = (node: ts.Node): void => {\n if (ts.isImportDeclaration(node)) {\n if (hasRuntimeImport(node) && node.moduleSpecifier !== undefined) {\n const specifier = getSpecifierFromExpression(node.moduleSpecifier);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n return;\n }\n\n if (ts.isExportDeclaration(node)) {\n if (!node.isTypeOnly && node.moduleSpecifier !== undefined) {\n const specifier = getSpecifierFromExpression(node.moduleSpecifier);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n return;\n }\n\n if (ts.isCallExpression(node)) {\n if (node.expression.kind === ts.SyntaxKind.ImportKeyword && node.arguments.length > 0) {\n const firstArgument = node.arguments[0];\n if (firstArgument !== undefined) {\n const specifier = getSpecifierFromExpression(firstArgument);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n\n if (ts.isIdentifier(node.expression) && node.expression.text === \"require\" && node.arguments.length > 0) {\n const firstArgument = node.arguments[0];\n if (firstArgument !== undefined) {\n const specifier = getSpecifierFromExpression(firstArgument);\n if (specifier !== undefined) {\n specifiers.add(specifier);\n }\n }\n }\n }\n\n ts.forEachChild(node, visit);\n };\n\n visit(sourceFile);\n return [...specifiers];\n};\n\nexport const parseTypescriptProject = (projectPath: string): ParsedProject => {\n const projectRoot = isAbsolute(projectPath) ? projectPath : resolve(projectPath);\n const { fileNames, options } = parseTsConfig(projectRoot);\n\n const sourceFilePaths = fileNames\n .filter((filePath) => isProjectSourceFile(filePath, projectRoot))\n .map((filePath) => normalizePath(resolve(filePath)));\n\n const uniqueSourceFilePaths = [...new Set(sourceFilePaths)].sort((a, b) => a.localeCompare(b));\n const sourceFilePathSet = new Set(uniqueSourceFilePaths);\n\n const program = ts.createProgram({\n rootNames: uniqueSourceFilePaths,\n options,\n });\n\n const nodeByAbsolutePath = new Map<string, NodeRecord>();\n for (const sourcePath of uniqueSourceFilePaths) {\n const relativePath = normalizePath(relative(projectRoot, sourcePath));\n const nodeId = relativePath;\n nodeByAbsolutePath.set(sourcePath, {\n id: nodeId,\n absolutePath: sourcePath,\n relativePath,\n });\n }\n\n const resolverCache = new Map<string, string | undefined>();\n const edges: EdgeRecord[] = [];\n\n for (const sourcePath of uniqueSourceFilePaths) {\n const sourceFile = program.getSourceFile(sourcePath);\n if (sourceFile === undefined) {\n continue;\n }\n\n const fromNode = nodeByAbsolutePath.get(sourcePath);\n if (fromNode === undefined) {\n continue;\n }\n\n const moduleSpecifiers = extractModuleSpecifiers(sourceFile);\n for (const specifier of moduleSpecifiers) {\n const cacheKey = `${sourcePath}\\u0000${specifier}`;\n let resolvedPath = resolverCache.get(cacheKey);\n\n if (resolvedPath === undefined && !resolverCache.has(cacheKey)) {\n const resolved = ts.resolveModuleName(specifier, sourcePath, options, ts.sys).resolvedModule;\n if (resolved !== undefined) {\n resolvedPath = normalizePath(resolve(resolved.resolvedFileName));\n }\n resolverCache.set(cacheKey, resolvedPath);\n }\n\n if (resolvedPath === undefined || !sourceFilePathSet.has(resolvedPath)) {\n continue;\n }\n\n const toNode = nodeByAbsolutePath.get(resolvedPath);\n if (toNode === undefined) {\n continue;\n }\n\n edges.push({ from: fromNode.id, to: toNode.id });\n }\n }\n\n return {\n nodes: [...nodeByAbsolutePath.values()],\n edges,\n };\n};\n","import type { GraphAnalysisSummary } from \"@codesentinel/core\";\nimport { createGraphData } from \"../domain/graph-model.js\";\nimport { createGraphAnalysisSummary } from \"../domain/graph-metrics.js\";\nimport { parseTypescriptProject } from \"../infrastructure/typescript-project.js\";\n\nexport type BuildProjectGraphSummaryInput = {\n projectPath: string;\n};\n\nexport const buildProjectGraphSummary = (\n input: BuildProjectGraphSummaryInput,\n): GraphAnalysisSummary => {\n const parsedProject = parseTypescriptProject(input.projectPath);\n const graphData = createGraphData(parsedProject.nodes, parsedProject.edges);\n return createGraphAnalysisSummary(input.projectPath, graphData);\n};\n","import type {\n CouplingMatrix,\n FileAuthorShare,\n FileCoupling,\n FileEvolutionMetrics,\n Hotspot,\n RepositoryEvolutionSummary,\n} from \"@codesentinel/core\";\nimport type { EvolutionComputationConfig, GitCommitRecord } from \"./evolution-types.js\";\n\ntype FileAccumulator = {\n commitCount: number;\n recentCommitCount: number;\n churnAdded: number;\n churnDeleted: number;\n authors: Map<string, number>;\n};\n\nconst pairKey = (a: string, b: string): string => `${a}\\u0000${b}`;\n\nconst round4 = (value: number): number => Number(value.toFixed(4));\n\nconst computeBusFactor = (\n authorDistribution: readonly FileAuthorShare[],\n threshold: number,\n): number => {\n if (authorDistribution.length === 0) {\n return 0;\n }\n\n let coveredShare = 0;\n for (let i = 0; i < authorDistribution.length; i += 1) {\n const entry = authorDistribution[i];\n if (entry === undefined) {\n continue;\n }\n\n coveredShare += entry.share;\n if (coveredShare >= threshold) {\n return i + 1;\n }\n }\n\n return authorDistribution.length;\n};\n\nconst finalizeAuthorDistribution = (authorCommits: ReadonlyMap<string, number>): readonly FileAuthorShare[] => {\n const totalCommits = [...authorCommits.values()].reduce((sum, value) => sum + value, 0);\n if (totalCommits === 0) {\n return [];\n }\n\n return [...authorCommits.entries()]\n .map(([authorId, commits]) => ({\n authorId,\n commits,\n share: round4(commits / totalCommits),\n }))\n .sort((a, b) => b.commits - a.commits || a.authorId.localeCompare(b.authorId));\n};\n\nconst buildCouplingMatrix = (\n coChangeByPair: ReadonlyMap<string, number>,\n fileCommitCount: ReadonlyMap<string, number>,\n consideredCommits: number,\n skippedLargeCommits: number,\n maxCouplingPairs: number,\n): CouplingMatrix => {\n const allPairs: FileCoupling[] = [];\n\n for (const [key, coChangeCommits] of coChangeByPair.entries()) {\n const [fileA, fileB] = key.split(\"\\u0000\");\n if (fileA === undefined || fileB === undefined) {\n continue;\n }\n\n const fileACommits = fileCommitCount.get(fileA) ?? 0;\n const fileBCommits = fileCommitCount.get(fileB) ?? 0;\n const denominator = fileACommits + fileBCommits - coChangeCommits;\n const couplingScore = denominator === 0 ? 0 : round4(coChangeCommits / denominator);\n\n allPairs.push({\n fileA,\n fileB,\n coChangeCommits,\n couplingScore,\n });\n }\n\n allPairs.sort(\n (a, b) =>\n b.coChangeCommits - a.coChangeCommits ||\n b.couplingScore - a.couplingScore ||\n a.fileA.localeCompare(b.fileA) ||\n a.fileB.localeCompare(b.fileB),\n );\n\n const truncated = allPairs.length > maxCouplingPairs;\n\n return {\n pairs: truncated ? allPairs.slice(0, maxCouplingPairs) : allPairs,\n totalPairCount: allPairs.length,\n consideredCommits,\n skippedLargeCommits,\n truncated,\n };\n};\n\nconst selectHotspots = (\n files: readonly FileEvolutionMetrics[],\n config: EvolutionComputationConfig,\n): { hotspots: readonly Hotspot[]; threshold: number } => {\n if (files.length === 0) {\n return { hotspots: [], threshold: 0 };\n }\n\n const sorted = [...files].sort(\n (a, b) =>\n b.commitCount - a.commitCount || b.churnTotal - a.churnTotal || a.filePath.localeCompare(b.filePath),\n );\n\n const hotspotCount = Math.max(config.hotspotMinFiles, Math.ceil(sorted.length * config.hotspotTopPercent));\n const selected = sorted.slice(0, hotspotCount);\n\n const hotspots = selected.map((file, index) => ({\n filePath: file.filePath,\n rank: index + 1,\n commitCount: file.commitCount,\n churnTotal: file.churnTotal,\n }));\n\n const threshold = selected[selected.length - 1]?.commitCount ?? 0;\n return { hotspots, threshold };\n};\n\nexport const computeRepositoryEvolutionSummary = (\n targetPath: string,\n commits: readonly GitCommitRecord[],\n config: EvolutionComputationConfig,\n): RepositoryEvolutionSummary => {\n const fileStats = new Map<string, FileAccumulator>();\n const coChangeByPair = new Map<string, number>();\n\n const headCommitTimestamp = commits.length === 0 ? null : commits[commits.length - 1]?.authoredAtUnix ?? null;\n const recentWindowStart =\n headCommitTimestamp === null\n ? Number.NEGATIVE_INFINITY\n : headCommitTimestamp - config.recentWindowDays * 24 * 60 * 60;\n\n let consideredCommits = 0;\n let skippedLargeCommits = 0;\n\n for (const commit of commits) {\n const uniqueFiles = new Set<string>();\n\n for (const fileChange of commit.fileChanges) {\n uniqueFiles.add(fileChange.filePath);\n const current = fileStats.get(fileChange.filePath) ?? {\n commitCount: 0,\n recentCommitCount: 0,\n churnAdded: 0,\n churnDeleted: 0,\n authors: new Map<string, number>(),\n };\n\n current.churnAdded += fileChange.additions;\n current.churnDeleted += fileChange.deletions;\n fileStats.set(fileChange.filePath, current);\n }\n\n for (const filePath of uniqueFiles) {\n const current = fileStats.get(filePath);\n if (current === undefined) {\n continue;\n }\n\n current.commitCount += 1;\n if (commit.authoredAtUnix >= recentWindowStart) {\n current.recentCommitCount += 1;\n }\n\n current.authors.set(commit.authorId, (current.authors.get(commit.authorId) ?? 0) + 1);\n }\n\n const orderedFiles = [...uniqueFiles].sort((a, b) => a.localeCompare(b));\n if (orderedFiles.length > 1) {\n if (orderedFiles.length <= config.maxFilesPerCommitForCoupling) {\n consideredCommits += 1;\n for (let i = 0; i < orderedFiles.length - 1; i += 1) {\n for (let j = i + 1; j < orderedFiles.length; j += 1) {\n const fileA = orderedFiles[i];\n const fileB = orderedFiles[j];\n if (fileA === undefined || fileB === undefined) {\n continue;\n }\n\n const key = pairKey(fileA, fileB);\n coChangeByPair.set(key, (coChangeByPair.get(key) ?? 0) + 1);\n }\n }\n } else {\n skippedLargeCommits += 1;\n }\n }\n }\n\n const files: FileEvolutionMetrics[] = [...fileStats.entries()]\n .map(([filePath, stats]) => {\n const authorDistribution = finalizeAuthorDistribution(stats.authors);\n const topAuthorShare = authorDistribution[0]?.share ?? 0;\n return {\n filePath,\n commitCount: stats.commitCount,\n frequencyPer100Commits: commits.length === 0 ? 0 : round4((stats.commitCount / commits.length) * 100),\n churnAdded: stats.churnAdded,\n churnDeleted: stats.churnDeleted,\n churnTotal: stats.churnAdded + stats.churnDeleted,\n recentCommitCount: stats.recentCommitCount,\n recentVolatility: stats.commitCount === 0 ? 0 : round4(stats.recentCommitCount / stats.commitCount),\n topAuthorShare,\n busFactor: computeBusFactor(authorDistribution, config.busFactorCoverageThreshold),\n authorDistribution,\n };\n })\n .sort((a, b) => a.filePath.localeCompare(b.filePath));\n\n const fileCommitCount = new Map(files.map((file) => [file.filePath, file.commitCount]));\n const coupling = buildCouplingMatrix(\n coChangeByPair,\n fileCommitCount,\n consideredCommits,\n skippedLargeCommits,\n config.maxCouplingPairs,\n );\n\n const { hotspots, threshold } = selectHotspots(files, config);\n\n return {\n targetPath,\n available: true,\n files,\n hotspots,\n coupling,\n metrics: {\n totalCommits: commits.length,\n totalFiles: files.length,\n headCommitTimestamp,\n recentWindowDays: config.recentWindowDays,\n hotspotTopPercent: config.hotspotTopPercent,\n hotspotThresholdCommitCount: threshold,\n },\n };\n};\n","export type GitFileChange = {\n filePath: string;\n additions: number;\n deletions: number;\n};\n\nexport type GitCommitRecord = {\n hash: string;\n authorId: string;\n authorName: string;\n authoredAtUnix: number;\n fileChanges: readonly GitFileChange[];\n};\n\nexport type EvolutionComputationConfig = {\n recentWindowDays: number;\n hotspotTopPercent: number;\n hotspotMinFiles: number;\n maxFilesPerCommitForCoupling: number;\n maxCouplingPairs: number;\n busFactorCoverageThreshold: number;\n};\n\nexport const DEFAULT_EVOLUTION_CONFIG: EvolutionComputationConfig = {\n recentWindowDays: 30,\n hotspotTopPercent: 0.1,\n hotspotMinFiles: 1,\n maxFilesPerCommitForCoupling: 200,\n maxCouplingPairs: 500,\n busFactorCoverageThreshold: 0.6,\n};\n","import type { RepositoryEvolutionSummary } from \"@codesentinel/core\";\nimport { computeRepositoryEvolutionSummary } from \"../domain/evolution-metrics.js\";\nimport {\n DEFAULT_EVOLUTION_CONFIG,\n type EvolutionComputationConfig,\n} from \"../domain/evolution-types.js\";\nimport type { GitHistoryProvider } from \"./git-history-provider.js\";\n\nexport type AnalyzeRepositoryEvolutionInput = {\n repositoryPath: string;\n config?: Partial<EvolutionComputationConfig>;\n};\n\nconst createEffectiveConfig = (\n overrides: Partial<EvolutionComputationConfig> | undefined,\n): EvolutionComputationConfig => ({\n ...DEFAULT_EVOLUTION_CONFIG,\n ...overrides,\n});\n\nexport const analyzeRepositoryEvolution = (\n input: AnalyzeRepositoryEvolutionInput,\n historyProvider: GitHistoryProvider,\n): RepositoryEvolutionSummary => {\n if (!historyProvider.isGitRepository(input.repositoryPath)) {\n return {\n targetPath: input.repositoryPath,\n available: false,\n reason: \"not_git_repository\",\n };\n }\n\n const commits = historyProvider.getCommitHistory(input.repositoryPath);\n const config = createEffectiveConfig(input.config);\n\n return computeRepositoryEvolutionSummary(input.repositoryPath, commits, config);\n};\n","import { execFileSync } from \"node:child_process\";\n\nexport class GitCommandError extends Error {\n readonly args: readonly string[];\n\n constructor(message: string, args: readonly string[]) {\n super(message);\n this.name = \"GitCommandError\";\n this.args = args;\n }\n}\n\nexport interface GitCommandClient {\n run(repositoryPath: string, args: readonly string[]): string;\n}\n\nexport class ExecGitCommandClient implements GitCommandClient {\n run(repositoryPath: string, args: readonly string[]): string {\n try {\n return execFileSync(\"git\", [\"-C\", repositoryPath, ...args], {\n encoding: \"utf8\",\n maxBuffer: 1024 * 1024 * 64,\n stdio: [\"ignore\", \"pipe\", \"pipe\"],\n });\n } catch (error) {\n const message = error instanceof Error ? error.message : \"Unknown git execution error\";\n throw new GitCommandError(message, args);\n }\n }\n}\n","export const COMMIT_RECORD_SEPARATOR = \"\\u001e\";\nexport const COMMIT_FIELD_SEPARATOR = \"\\u001f\";\n\nexport const GIT_LOG_FORMAT =\n `%x1e%H%x1f%at%x1f%an%x1f%ae`;\n","import { COMMIT_FIELD_SEPARATOR, COMMIT_RECORD_SEPARATOR } from \"../domain/git-log-format.js\";\nimport type { GitCommitRecord, GitFileChange } from \"../domain/evolution-types.js\";\n\nconst parseInteger = (value: string): number | null => {\n if (value.length === 0) {\n return null;\n }\n\n const parsed = Number.parseInt(value, 10);\n if (Number.isNaN(parsed)) {\n return null;\n }\n\n return parsed;\n};\n\nconst parseRenamedPath = (pathSpec: string): string => {\n if (!pathSpec.includes(\" => \")) {\n return pathSpec;\n }\n\n const braceRenameMatch = pathSpec.match(/^(.*)\\{(.+) => (.+)\\}(.*)$/);\n if (braceRenameMatch !== null) {\n const [, prefix, , renamedTo, suffix] = braceRenameMatch;\n return `${prefix}${renamedTo}${suffix}`;\n }\n\n const parts = pathSpec.split(\" => \");\n const finalPart = parts[parts.length - 1];\n return finalPart ?? pathSpec;\n};\n\nconst parseNumstatLine = (line: string): GitFileChange | null => {\n const parts = line.split(\"\\t\");\n if (parts.length < 3) {\n return null;\n }\n\n const additionsRaw = parts[0];\n const deletionsRaw = parts[1];\n const pathRaw = parts.slice(2).join(\"\\t\");\n\n if (additionsRaw === undefined || deletionsRaw === undefined) {\n return null;\n }\n\n const additions = additionsRaw === \"-\" ? 0 : parseInteger(additionsRaw);\n const deletions = deletionsRaw === \"-\" ? 0 : parseInteger(deletionsRaw);\n\n if (additions === null || deletions === null) {\n return null;\n }\n\n const filePath = parseRenamedPath(pathRaw);\n return {\n filePath,\n additions,\n deletions,\n };\n};\n\nexport const parseGitLog = (rawLog: string): readonly GitCommitRecord[] => {\n const records = rawLog\n .split(COMMIT_RECORD_SEPARATOR)\n .map((record) => record.trim())\n .filter((record) => record.length > 0);\n\n const commits: GitCommitRecord[] = [];\n\n for (const record of records) {\n const lines = record\n .split(\"\\n\")\n .map((line) => line.trimEnd())\n .filter((line) => line.length > 0);\n\n if (lines.length === 0) {\n continue;\n }\n\n const headerParts = lines[0]?.split(COMMIT_FIELD_SEPARATOR) ?? [];\n if (headerParts.length !== 4) {\n continue;\n }\n\n const [hash, authoredAtRaw, authorName, authorEmail] = headerParts;\n if (hash === undefined || authoredAtRaw === undefined || authorName === undefined || authorEmail === undefined) {\n continue;\n }\n\n const authoredAtUnix = parseInteger(authoredAtRaw);\n if (authoredAtUnix === null) {\n continue;\n }\n\n const fileChanges: GitFileChange[] = [];\n for (const line of lines.slice(1)) {\n const parsedLine = parseNumstatLine(line);\n if (parsedLine !== null) {\n fileChanges.push(parsedLine);\n }\n }\n\n commits.push({\n hash,\n authorId: authorEmail.toLowerCase(),\n authorName,\n authoredAtUnix,\n fileChanges,\n });\n }\n\n commits.sort((a, b) => a.authoredAtUnix - b.authoredAtUnix || a.hash.localeCompare(b.hash));\n return commits;\n};\n","import { GIT_LOG_FORMAT } from \"../domain/git-log-format.js\";\nimport type { GitCommitRecord } from \"../domain/evolution-types.js\";\nimport type { GitHistoryProvider } from \"../application/git-history-provider.js\";\nimport { GitCommandError, type GitCommandClient } from \"./git-command-client.js\";\nimport { parseGitLog } from \"../parsing/git-log-parser.js\";\n\nconst NON_GIT_CODES = [\"not a git repository\", \"not in a git directory\"];\n\nconst isNotGitError = (error: GitCommandError): boolean => {\n const lower = error.message.toLowerCase();\n return NON_GIT_CODES.some((code) => lower.includes(code));\n};\n\nexport class GitCliHistoryProvider implements GitHistoryProvider {\n constructor(private readonly gitClient: GitCommandClient) {}\n\n isGitRepository(repositoryPath: string): boolean {\n try {\n const output = this.gitClient.run(repositoryPath, [\"rev-parse\", \"--is-inside-work-tree\"]);\n return output.trim() === \"true\";\n } catch (error) {\n if (error instanceof GitCommandError && isNotGitError(error)) {\n return false;\n }\n\n throw error;\n }\n }\n\n getCommitHistory(repositoryPath: string): readonly GitCommitRecord[] {\n const output = this.gitClient.run(repositoryPath, [\n \"-c\",\n \"core.quotepath=false\",\n \"log\",\n \"--no-merges\",\n \"--date=unix\",\n `--pretty=format:${GIT_LOG_FORMAT}`,\n \"--numstat\",\n \"--find-renames\",\n ]);\n\n return parseGitLog(output);\n }\n}\n","import type { RepositoryEvolutionSummary } from \"@codesentinel/core\";\nimport {\n analyzeRepositoryEvolution,\n type AnalyzeRepositoryEvolutionInput,\n} from \"./application/analyze-repository-evolution.js\";\nimport { ExecGitCommandClient } from \"./infrastructure/git-command-client.js\";\nimport { GitCliHistoryProvider } from \"./infrastructure/git-history-provider.js\";\n\nexport type { AnalyzeRepositoryEvolutionInput } from \"./application/analyze-repository-evolution.js\";\n\nexport const analyzeRepositoryEvolutionFromGit = (\n input: AnalyzeRepositoryEvolutionInput,\n): RepositoryEvolutionSummary => {\n const historyProvider = new GitCliHistoryProvider(new ExecGitCommandClient());\n return analyzeRepositoryEvolution(input, historyProvider);\n};\n"],"mappings":"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","names":["resolve","resolve","nodeLowLink","program","resolve","resolve"]}
package/package.json CHANGED
@@ -1,8 +1,16 @@
1
1
  {
2
2
  "name": "@codesentinel/codesentinel",
3
- "version": "0.1.1",
3
+ "version": "1.1.0",
4
4
  "type": "module",
5
5
  "description": "Command-line interface for CodeSentinel.",
6
+ "repository": {
7
+ "type": "git",
8
+ "url": "https://github.com/getcodesentinel/codesentinel"
9
+ },
10
+ "homepage": "https://github.com/getcodesentinel/codesentinel#readme",
11
+ "bugs": {
12
+ "url": "https://github.com/getcodesentinel/codesentinel/issues"
13
+ },
6
14
  "license": "MIT",
7
15
  "engines": {
8
16
  "node": ">=24"
@@ -13,11 +21,16 @@
13
21
  "scripts": {
14
22
  "build": "tsup",
15
23
  "dev": "node --import tsx/esm src/index.ts",
16
- "prepack": "pnpm --filter @codesentinel/core build && pnpm --filter @codesentinel/code-graph build && pnpm run build",
17
- "test": "vitest run"
24
+ "prepack": "cp ../../README.md ./README.md && cp ../../LICENSE ./LICENSE && pnpm --filter @codesentinel/core build && pnpm --filter @codesentinel/code-graph build && pnpm --filter @codesentinel/git-analyzer build && pnpm run build",
25
+ "test": "vitest run --passWithNoTests"
18
26
  },
19
27
  "dependencies": {
20
- "commander": "^12.1.0",
28
+ "@codesentinel/code-graph": "workspace:*",
29
+ "@codesentinel/core": "workspace:*",
30
+ "@codesentinel/git-analyzer": "workspace:*",
31
+ "commander": "^14.0.3"
32
+ },
33
+ "devDependencies": {
21
34
  "typescript": "^5.6.3"
22
35
  },
23
36
  "exports": {
@@ -28,7 +41,9 @@
28
41
  },
29
42
  "types": "./dist/index.d.ts",
30
43
  "files": [
31
- "dist"
44
+ "dist",
45
+ "README.md",
46
+ "LICENSE"
32
47
  ],
33
48
  "publishConfig": {
34
49
  "access": "public"