@jatingargiitk/archmap 0.1.0 → 0.1.2

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/README.md CHANGED
@@ -8,6 +8,8 @@
8
8
 
9
9
  archmap commits a living map of your modules and flags every PR—human or AI—that adds a new dependency between them.
10
10
 
11
+ ![archmap demo](docs/demo.gif)
12
+
11
13
  ---
12
14
 
13
15
  ## Quick Start
@@ -21,17 +23,25 @@ This creates:
21
23
  - ✅ `ARCHITECTURE.md` - Living architecture map ([see ours](./ARCHITECTURE.md))
22
24
  - ✅ `archmap.lock.json` - Dependency lockfile
23
25
 
24
- Then:
26
+ The generated workflow:
25
27
 
26
28
  ```yaml
27
- # .github/workflows/archmap.yml (auto-generated)
28
- on: [pull_request, push]
29
- permissions: { contents: write, pull-requests: write }
29
+ name: archmap
30
+
31
+ on:
32
+ pull_request:
33
+
34
+ permissions:
35
+ contents: write
36
+ pull-requests: write
37
+
30
38
  jobs:
31
39
  archmap:
32
40
  runs-on: ubuntu-latest
33
41
  steps:
34
42
  - uses: actions/checkout@v4
43
+ with:
44
+ fetch-depth: 0
35
45
  - uses: jatingargiitk/archmap@v0
36
46
  ```
37
47
 
@@ -63,9 +73,7 @@ Think of it as snapshot testing, but for your architecture.
63
73
 
64
74
  ### After archmap
65
75
 
66
- ![archmap demo](docs/demo.gif)
67
-
68
- The PR gets a sticky comment showing the new dependency and file location. Now the reviewer knows exactly what changed architecturally, and the decision is explicit.
76
+ The PR gets a sticky comment showing the new dependency with the exact file and line number where the import was added. Now the reviewer knows exactly what changed architecturally, and the decision is explicit.
69
77
 
70
78
  ---
71
79
 
@@ -74,7 +82,7 @@ The PR gets a sticky comment showing the new dependency and file location. Now t
74
82
  - ✅ **100% Deterministic** - Same input, same output. No LLM or API key required
75
83
  - ✅ **Fast** - Parses with `web-tree-sitter` WASM (TS/JS + Python in v0.1)
76
84
  - ✅ **Lockfile Diffing** - Git-friendly JSON lockfile tracks accepted edges
77
- - ✅ **GitHub Action** - Sticky PR comments show only added/removed edges
85
+ - ✅ **GitHub Action** - Sticky PR comments show added/removed edges with file locations
78
86
  - ✅ **Optional Gate Mode** - Fail CI if unapproved edges are added
79
87
  - ✅ **Deny Rules** - `deny: { from: "ui/**", to: "db/**" }`
80
88
  - ✅ **Living Docs** - `ARCHITECTURE.md` with Mermaid diagram, auto-regenerated
@@ -166,7 +174,7 @@ modules:
166
174
 
167
175
  ### What about fork PRs?
168
176
 
169
- archmap uses read-only tokens for forks. It posts a comment but cannot update files. The reviewer runs `archmap accept` locally.
177
+ Fork PRs cannot post comments with the default `GITHUB_TOKEN` due to GitHub security restrictions. The workflow will run the check successfully, but comment posting will be skipped. The same architecture diff is visible in the action logs.
170
178
 
171
179
  ### Does anything leave my runner?
172
180
 
package/action.yml CHANGED
@@ -7,14 +7,30 @@ branding:
7
7
  color: 'blue'
8
8
 
9
9
  runs:
10
- using: 'node20'
11
- main: 'dist/action.js'
10
+ using: 'composite'
11
+ steps:
12
+ - name: Setup Node
13
+ uses: actions/setup-node@v4
14
+ with:
15
+ node-version: '20'
16
+
17
+ - name: Install dependencies
18
+ shell: bash
19
+ run: |
20
+ cd ${{ github.action_path }}
21
+ npm ci --omit=dev
22
+
23
+ - name: Run archmap
24
+ shell: bash
25
+ run: node ${{ github.action_path }}/dist/action.js
26
+ env:
27
+ INPUT_MODE: ${{ inputs.mode }}
28
+ GITHUB_TOKEN: ${{ inputs.github-token }}
12
29
 
13
30
  inputs:
14
31
  mode:
15
32
  description: 'Mode: comment (default) or gate'
16
33
  required: false
17
- default: 'comment'
18
34
  github-token:
19
35
  description: 'GitHub token for posting comments'
20
36
  required: false
package/dist/action.js CHANGED
@@ -13,7 +13,7 @@ import {
13
13
  shouldFailCheck,
14
14
  writeArchitectureFile,
15
15
  writeLockFile
16
- } from "./chunk-UVZVBM2N.js";
16
+ } from "./chunk-QUFBKU64.js";
17
17
 
18
18
  // src/action.ts
19
19
  import * as fs from "fs/promises";
@@ -42,8 +42,19 @@ async function handlePullRequest(rootDir, currentGraph, currentLock, config, con
42
42
  console.log("\u{1F4CA} Computing diff...");
43
43
  let baseLock = null;
44
44
  try {
45
+ const baseRef = `origin/${context.baseRef}`;
46
+ try {
47
+ execSync(`git rev-parse --verify ${baseRef}`, { cwd: rootDir, stdio: "ignore" });
48
+ } catch {
49
+ console.log(`Fetching base ref ${context.baseRef}...`);
50
+ try {
51
+ execSync(`git fetch --no-tags --depth=1 origin ${context.baseRef}`, { cwd: rootDir, stdio: "pipe" });
52
+ } catch (fetchError) {
53
+ console.log(`\u26A0\uFE0F Could not fetch base ref: ${fetchError.message}`);
54
+ }
55
+ }
45
56
  const baseLockContent = execSync(
46
- `git show origin/${context.baseRef}:archmap.lock.json`,
57
+ `git show ${baseRef}:archmap.lock.json`,
47
58
  { cwd: rootDir, encoding: "utf-8" }
48
59
  );
49
60
  baseLock = JSON.parse(baseLockContent);
@@ -52,7 +63,7 @@ async function handlePullRequest(rootDir, currentGraph, currentLock, config, con
52
63
  }
53
64
  const diff = diffLocks(baseLock, currentLock, currentGraph);
54
65
  const violations = checkRules(currentGraph.edges, config);
55
- const commentBody = buildCommentBody(diff, violations, currentGraph);
66
+ const commentBody = buildCommentBody(diff, violations, currentGraph, context);
56
67
  if (process.env.GITHUB_TOKEN && context.prNumber) {
57
68
  await postOrUpdateComment(context, commentBody);
58
69
  } else {
@@ -86,7 +97,7 @@ async function handlePush(rootDir, context) {
86
97
  }
87
98
  }
88
99
  }
89
- function buildCommentBody(diff, violations, _graph) {
100
+ function buildCommentBody(diff, violations, _graph, context) {
90
101
  const lines = [];
91
102
  lines.push("## \u{1F5FA}\uFE0F archmap");
92
103
  lines.push("");
@@ -122,7 +133,9 @@ function buildCommentBody(diff, violations, _graph) {
122
133
  for (const edge of diff.added) {
123
134
  if (edge.locations && edge.locations.length > 0) {
124
135
  const loc = edge.locations[0];
125
- lines.push(`- \`${edge.from}\` \u2192 \`${edge.to}\` ([${loc.file}:${loc.line}](${loc.file}#L${loc.line}))`);
136
+ const [owner, repo] = context.repository.split("/");
137
+ const blobUrl = `https://github.com/${owner}/${repo}/blob/${context.sha}/${loc.file}#L${loc.line}`;
138
+ lines.push(`- \`${edge.from}\` \u2192 \`${edge.to}\` ([${loc.file}:${loc.line}](${blobUrl}))`);
126
139
  } else {
127
140
  lines.push(`- \`${edge.from}\` \u2192 \`${edge.to}\``);
128
141
  }
@@ -326,7 +339,7 @@ async function loadConfig(rootDir) {
326
339
  config = {};
327
340
  }
328
341
  const inputMode = process.env.INPUT_MODE;
329
- if (inputMode === "comment" || inputMode === "gate") {
342
+ if (inputMode && (inputMode === "comment" || inputMode === "gate")) {
330
343
  config.mode = inputMode;
331
344
  }
332
345
  return config;
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/action.ts"],"sourcesContent":["import * as fs from 'node:fs/promises';\nimport { readFileSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { extractTypeScriptImports, extractPythonImports } from './parser/index.js';\nimport { resolveImports, loadTsConfig, findWorkspaces } from './resolver/index.js';\nimport { buildGraph } from './graph/index.js';\nimport { writeArchitectureFile, renderDeltaDiagram } from './render/index.js';\nimport { graphToLock, writeLockFile, diffLocks } from './lock/index.js';\nimport { checkRules, shouldFailCheck } from './rules/index.js';\nimport type { Config } from './types.js';\n\nasync function runAction(): Promise<void> {\n const githubContext = getGitHubContext();\n const rootDir = process.env.GITHUB_WORKSPACE || process.cwd();\n\n console.log('🔍 Analyzing architecture...');\n\n const config = await loadConfig(rootDir);\n const currentGraph = await analyzeRepo(rootDir, config);\n const currentLock = graphToLock(currentGraph);\n\n await writeArchitectureFile(\n path.join(rootDir, 'ARCHITECTURE.md'),\n currentGraph\n );\n\n await writeLockFile(path.join(rootDir, 'archmap.lock.json'), currentLock);\n\n if (githubContext.eventName === 'pull_request') {\n await handlePullRequest(rootDir, currentGraph, currentLock, config, githubContext);\n } else if (githubContext.eventName === 'push') {\n await handlePush(rootDir, githubContext);\n }\n}\n\nasync function handlePullRequest(\n rootDir: string,\n currentGraph: any,\n currentLock: any,\n config: Config,\n context: any\n): Promise<void> {\n console.log('📊 Computing diff...');\n\n let baseLock = null;\n try {\n const baseLockContent = execSync(\n `git show origin/${context.baseRef}:archmap.lock.json`,\n { cwd: rootDir, encoding: 'utf-8' }\n );\n baseLock = JSON.parse(baseLockContent);\n } catch {\n console.log('⚠️ No lock file found in base branch');\n }\n\n const diff = diffLocks(baseLock, currentLock, currentGraph);\n const violations = checkRules(currentGraph.edges, config);\n\n const commentBody = buildCommentBody(diff, violations, currentGraph);\n\n if (process.env.GITHUB_TOKEN && context.prNumber) {\n await postOrUpdateComment(context, commentBody);\n } else {\n console.log('Comment preview:');\n console.log(commentBody);\n }\n\n const shouldFail = shouldFailCheck(diff, violations, config);\n\n if (shouldFail) {\n console.log('❌ Check failed');\n process.exit(1);\n } else {\n console.log('✅ Check passed');\n }\n}\n\nasync function handlePush(rootDir: string, context: any): Promise<void> {\n if (context.ref === `refs/heads/${context.defaultBranch}`) {\n console.log('🔄 Committing updated architecture files...');\n\n try {\n execSync('git config user.name \"archmap[bot]\"', { cwd: rootDir });\n execSync('git config user.email \"archmap[bot]@users.noreply.github.com\"', { cwd: rootDir });\n execSync('git add ARCHITECTURE.md archmap.lock.json', { cwd: rootDir });\n \n try {\n execSync('git commit -m \"chore: update architecture map [skip ci]\"', { cwd: rootDir });\n execSync(`git push origin ${context.defaultBranch}`, { cwd: rootDir });\n console.log('✅ Architecture files updated');\n } catch {\n console.log('ℹ️ No changes to commit');\n }\n } catch (error: any) {\n console.log('⚠️ Could not commit changes:', error.message);\n }\n }\n}\n\nfunction buildCommentBody(diff: any, violations: any[], _graph: any): string {\n const lines: string[] = [];\n\n lines.push('## 🗺️ archmap');\n lines.push('');\n\n const summary = `**${diff.added.length > 0 ? '+' : ''}${diff.added.length} new edge${diff.added.length !== 1 ? 's' : ''}, ${diff.removed.length > 0 ? '-' : ''}${diff.removed.length} removed**`;\n lines.push(summary);\n lines.push('');\n\n if (violations.length > 0) {\n lines.push('### ⚠️ Rule Violations');\n lines.push('');\n for (const violation of violations) {\n lines.push(`- ${violation.message}`);\n lines.push(` - Rule: \\`${violation.rule}\\``);\n }\n lines.push('');\n }\n\n if (diff.added.length > 0 || diff.removed.length > 0) {\n const touchedModules = new Set<string>();\n for (const edge of diff.added) {\n touchedModules.add(edge.from);\n touchedModules.add(edge.to);\n }\n for (const edge of diff.removed) {\n touchedModules.add(edge.from);\n touchedModules.add(edge.to);\n }\n\n lines.push('### Changes');\n lines.push('');\n lines.push(renderDeltaDiagram(diff.added, diff.removed, touchedModules));\n lines.push('');\n\n if (diff.added.length > 0) {\n lines.push('#### Added Dependencies');\n lines.push('');\n for (const edge of diff.added) {\n // Show locations (B6)\n if (edge.locations && edge.locations.length > 0) {\n const loc = edge.locations[0];\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\` ([${loc.file}:${loc.line}](${loc.file}#L${loc.line}))`);\n } else {\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\``);\n }\n }\n lines.push('');\n }\n\n if (diff.removed.length > 0) {\n lines.push('#### Removed Dependencies');\n lines.push('');\n for (const edge of diff.removed) {\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\``);\n }\n lines.push('');\n }\n }\n\n if (diff.newCycles.length > 0) {\n lines.push('### ⚠️ New Cycles');\n lines.push('');\n for (const cycle of diff.newCycles) {\n lines.push(`- ${cycle.map((m: string) => `\\`${m}\\``).join(' → ')}`);\n }\n lines.push('');\n }\n\n if (diff.added.length === 0 && diff.removed.length === 0 && violations.length === 0) {\n lines.push('✅ No architectural changes detected');\n } else {\n lines.push('---');\n lines.push('');\n lines.push('To accept these changes, run:');\n lines.push('```bash');\n lines.push('npx @jatingargiitk/archmap accept');\n lines.push('git add ARCHITECTURE.md archmap.lock.json');\n lines.push('git commit -m \"chore: update architecture map\"');\n lines.push('```');\n }\n\n lines.push('');\n lines.push('<!-- archmap-comment -->');\n\n return lines.join('\\n');\n}\n\nasync function postOrUpdateComment(context: any, body: string): Promise<void> {\n const token = process.env.GITHUB_TOKEN;\n if (!token) return;\n\n const [owner, repo] = context.repository.split('/');\n const issueNumber = context.prNumber;\n\n try {\n const existingComments = await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n 'User-Agent': 'archmap',\n },\n }\n );\n\n const comments = (await existingComments.json()) as any[];\n const existingComment = comments.find((c: any) =>\n c.body.includes('<!-- archmap-comment -->')\n );\n\n if (existingComment) {\n await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/comments/${existingComment.id}`,\n {\n method: 'PATCH',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'archmap',\n },\n body: JSON.stringify({ body }),\n }\n );\n console.log('✅ Updated existing comment');\n } else {\n await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'archmap',\n },\n body: JSON.stringify({ body }),\n }\n );\n console.log('✅ Posted new comment');\n }\n } catch (error: any) {\n console.log('⚠️ Could not post comment:', error.message);\n }\n}\n\nfunction getGitHubContext(): any {\n const eventPath = process.env.GITHUB_EVENT_PATH;\n let event = {};\n\n if (eventPath) {\n try {\n const eventData = readFileSync(eventPath, 'utf-8');\n event = JSON.parse(eventData);\n } catch {\n // Ignore\n }\n }\n\n return {\n eventName: process.env.GITHUB_EVENT_NAME || '',\n repository: process.env.GITHUB_REPOSITORY || '',\n ref: process.env.GITHUB_REF || '',\n sha: process.env.GITHUB_SHA || '',\n actor: process.env.GITHUB_ACTOR || '',\n workflow: process.env.GITHUB_WORKFLOW || '',\n defaultBranch: (event as any).repository?.default_branch || 'main',\n baseRef: (event as any).pull_request?.base?.ref || 'main',\n prNumber: (event as any).pull_request?.number,\n };\n}\n\nasync function analyzeRepo(rootDir: string, config: Config): Promise<any> {\n const files = await findSourceFiles(rootDir, config.ignore || []);\n\n const allImports = [];\n const allFiles: string[] = [];\n\n for (const file of files) {\n const fullPath = path.join(rootDir, file);\n const content = await fs.readFile(fullPath, 'utf-8');\n\n let imports;\n if (file.endsWith('.ts') || file.endsWith('.tsx') || file.endsWith('.js') || file.endsWith('.jsx')) {\n imports = await extractTypeScriptImports(file, content);\n } else if (file.endsWith('.py')) {\n imports = await extractPythonImports(file, content);\n } else {\n continue;\n }\n\n allImports.push(imports);\n allFiles.push(file);\n }\n\n const tsconfig = await loadTsConfig(rootDir);\n const workspaces = await findWorkspaces(rootDir);\n\n const resolved = await resolveImports(allImports, {\n rootDir,\n tsconfig: tsconfig || undefined,\n workspaces: workspaces.length > 0 ? workspaces : undefined,\n });\n\n const moduleConfig = config.modules || {};\n const graph = buildGraph(resolved, allFiles, moduleConfig);\n\n return graph;\n}\n\nasync function findSourceFiles(rootDir: string, ignore: string[]): Promise<string[]> {\n const files: string[] = [];\n const ignorePatterns = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.git/**',\n '**/coverage/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/__tests__/**',\n ...ignore,\n ];\n\n async function walk(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(rootDir, fullPath);\n\n if (shouldIgnore(relativePath, ignorePatterns)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.isFile()) {\n if (isSourceFile(entry.name)) {\n files.push(relativePath);\n }\n }\n }\n }\n\n await walk(rootDir);\n return files;\n}\n\nfunction shouldIgnore(filePath: string, patterns: string[]): boolean {\n const normalized = filePath.replace(/\\\\/g, '/');\n\n for (const pattern of patterns) {\n const regex = patternToRegex(pattern);\n if (regex.test(normalized)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction patternToRegex(pattern: string): RegExp {\n const regexStr = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '___DOUBLESTAR___')\n .replace(/\\*/g, '[^/]*')\n .replace(/___DOUBLESTAR___/g, '.*');\n\n return new RegExp(`^${regexStr}$`);\n}\n\nfunction isSourceFile(name: string): boolean {\n return /\\.(ts|tsx|js|jsx|py)$/.test(name);\n}\n\nasync function loadConfig(rootDir: string): Promise<Config> {\n const configPath = path.join(rootDir, 'archmap.yml');\n\n let config: Config = {};\n try {\n const content = await fs.readFile(configPath, 'utf-8');\n const yaml = await import('js-yaml');\n config = yaml.load(content) as Config;\n } catch {\n config = {};\n }\n\n // Override with INPUT_MODE if provided (B7)\n const inputMode = process.env.INPUT_MODE;\n if (inputMode === 'comment' || inputMode === 'gate') {\n config.mode = inputMode;\n }\n\n return config;\n}\n\nrunAction().catch((error) => {\n console.error('Error:', error.message);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}
1
+ {"version":3,"sources":["../src/action.ts"],"sourcesContent":["import * as fs from 'node:fs/promises';\nimport { readFileSync } from 'node:fs';\nimport * as path from 'node:path';\nimport { execSync } from 'node:child_process';\nimport { extractTypeScriptImports, extractPythonImports } from './parser/index.js';\nimport { resolveImports, loadTsConfig, findWorkspaces } from './resolver/index.js';\nimport { buildGraph } from './graph/index.js';\nimport { writeArchitectureFile, renderDeltaDiagram } from './render/index.js';\nimport { graphToLock, writeLockFile, diffLocks } from './lock/index.js';\nimport { checkRules, shouldFailCheck } from './rules/index.js';\nimport type { Config } from './types.js';\n\nasync function runAction(): Promise<void> {\n const githubContext = getGitHubContext();\n const rootDir = process.env.GITHUB_WORKSPACE || process.cwd();\n\n console.log('🔍 Analyzing architecture...');\n\n const config = await loadConfig(rootDir);\n const currentGraph = await analyzeRepo(rootDir, config);\n const currentLock = graphToLock(currentGraph);\n\n await writeArchitectureFile(\n path.join(rootDir, 'ARCHITECTURE.md'),\n currentGraph\n );\n\n await writeLockFile(path.join(rootDir, 'archmap.lock.json'), currentLock);\n\n if (githubContext.eventName === 'pull_request') {\n await handlePullRequest(rootDir, currentGraph, currentLock, config, githubContext);\n } else if (githubContext.eventName === 'push') {\n await handlePush(rootDir, githubContext);\n }\n}\n\nasync function handlePullRequest(\n rootDir: string,\n currentGraph: any,\n currentLock: any,\n config: Config,\n context: any\n): Promise<void> {\n console.log('📊 Computing diff...');\n\n let baseLock = null;\n try {\n // Ensure we have the base ref, even in shallow clones\n const baseRef = `origin/${context.baseRef}`;\n try {\n execSync(`git rev-parse --verify ${baseRef}`, { cwd: rootDir, stdio: 'ignore' });\n } catch {\n // Base ref not found, try to fetch it\n console.log(`Fetching base ref ${context.baseRef}...`);\n try {\n execSync(`git fetch --no-tags --depth=1 origin ${context.baseRef}`, { cwd: rootDir, stdio: 'pipe' });\n } catch (fetchError: any) {\n console.log(`⚠️ Could not fetch base ref: ${fetchError.message}`);\n }\n }\n\n const baseLockContent = execSync(\n `git show ${baseRef}:archmap.lock.json`,\n { cwd: rootDir, encoding: 'utf-8' }\n );\n baseLock = JSON.parse(baseLockContent);\n } catch {\n console.log('⚠️ No lock file found in base branch');\n }\n\n const diff = diffLocks(baseLock, currentLock, currentGraph);\n const violations = checkRules(currentGraph.edges, config);\n\n const commentBody = buildCommentBody(diff, violations, currentGraph, context);\n\n if (process.env.GITHUB_TOKEN && context.prNumber) {\n await postOrUpdateComment(context, commentBody);\n } else {\n console.log('Comment preview:');\n console.log(commentBody);\n }\n\n const shouldFail = shouldFailCheck(diff, violations, config);\n\n if (shouldFail) {\n console.log('❌ Check failed');\n process.exit(1);\n } else {\n console.log('✅ Check passed');\n }\n}\n\nasync function handlePush(rootDir: string, context: any): Promise<void> {\n if (context.ref === `refs/heads/${context.defaultBranch}`) {\n console.log('🔄 Committing updated architecture files...');\n\n try {\n execSync('git config user.name \"archmap[bot]\"', { cwd: rootDir });\n execSync('git config user.email \"archmap[bot]@users.noreply.github.com\"', { cwd: rootDir });\n execSync('git add ARCHITECTURE.md archmap.lock.json', { cwd: rootDir });\n \n try {\n execSync('git commit -m \"chore: update architecture map [skip ci]\"', { cwd: rootDir });\n execSync(`git push origin ${context.defaultBranch}`, { cwd: rootDir });\n console.log('✅ Architecture files updated');\n } catch {\n console.log('ℹ️ No changes to commit');\n }\n } catch (error: unknown) {\n console.log('⚠️ Could not commit changes:', (error as Error).message);\n }\n }\n}\n\nfunction buildCommentBody(diff: any, violations: any[], _graph: any, context: any): string {\n const lines: string[] = [];\n\n lines.push('## 🗺️ archmap');\n lines.push('');\n\n const summary = `**${diff.added.length > 0 ? '+' : ''}${diff.added.length} new edge${diff.added.length !== 1 ? 's' : ''}, ${diff.removed.length > 0 ? '-' : ''}${diff.removed.length} removed**`;\n lines.push(summary);\n lines.push('');\n\n if (violations.length > 0) {\n lines.push('### ⚠️ Rule Violations');\n lines.push('');\n for (const violation of violations) {\n lines.push(`- ${violation.message}`);\n lines.push(` - Rule: \\`${violation.rule}\\``);\n }\n lines.push('');\n }\n\n if (diff.added.length > 0 || diff.removed.length > 0) {\n const touchedModules = new Set<string>();\n for (const edge of diff.added) {\n touchedModules.add(edge.from);\n touchedModules.add(edge.to);\n }\n for (const edge of diff.removed) {\n touchedModules.add(edge.from);\n touchedModules.add(edge.to);\n }\n\n lines.push('### Changes');\n lines.push('');\n lines.push(renderDeltaDiagram(diff.added, diff.removed, touchedModules));\n lines.push('');\n\n if (diff.added.length > 0) {\n lines.push('#### Added Dependencies');\n lines.push('');\n for (const edge of diff.added) {\n // Show locations (B6) with absolute blob URLs\n if (edge.locations && edge.locations.length > 0) {\n const loc = edge.locations[0];\n const [owner, repo] = context.repository.split('/');\n const blobUrl = `https://github.com/${owner}/${repo}/blob/${context.sha}/${loc.file}#L${loc.line}`;\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\` ([${loc.file}:${loc.line}](${blobUrl}))`);\n } else {\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\``);\n }\n }\n lines.push('');\n }\n\n if (diff.removed.length > 0) {\n lines.push('#### Removed Dependencies');\n lines.push('');\n for (const edge of diff.removed) {\n lines.push(`- \\`${edge.from}\\` → \\`${edge.to}\\``);\n }\n lines.push('');\n }\n }\n\n if (diff.newCycles.length > 0) {\n lines.push('### ⚠️ New Cycles');\n lines.push('');\n for (const cycle of diff.newCycles) {\n lines.push(`- ${cycle.map((m: string) => `\\`${m}\\``).join(' → ')}`);\n }\n lines.push('');\n }\n\n if (diff.added.length === 0 && diff.removed.length === 0 && violations.length === 0) {\n lines.push('✅ No architectural changes detected');\n } else {\n lines.push('---');\n lines.push('');\n lines.push('To accept these changes, run:');\n lines.push('```bash');\n lines.push('npx @jatingargiitk/archmap accept');\n lines.push('git add ARCHITECTURE.md archmap.lock.json');\n lines.push('git commit -m \"chore: update architecture map\"');\n lines.push('```');\n }\n\n lines.push('');\n lines.push('<!-- archmap-comment -->');\n\n return lines.join('\\n');\n}\n\nasync function postOrUpdateComment(context: any, body: string): Promise<void> {\n const token = process.env.GITHUB_TOKEN;\n if (!token) return;\n\n const [owner, repo] = context.repository.split('/');\n const issueNumber = context.prNumber;\n\n try {\n const existingComments = await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,\n {\n headers: {\n Authorization: `Bearer ${token}`,\n 'User-Agent': 'archmap',\n },\n }\n );\n\n const comments = (await existingComments.json()) as any[];\n const existingComment = comments.find((c: any) =>\n c.body.includes('<!-- archmap-comment -->')\n );\n\n if (existingComment) {\n await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/comments/${existingComment.id}`,\n {\n method: 'PATCH',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'archmap',\n },\n body: JSON.stringify({ body }),\n }\n );\n console.log('✅ Updated existing comment');\n } else {\n await fetch(\n `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,\n {\n method: 'POST',\n headers: {\n Authorization: `Bearer ${token}`,\n 'Content-Type': 'application/json',\n 'User-Agent': 'archmap',\n },\n body: JSON.stringify({ body }),\n }\n );\n console.log('✅ Posted new comment');\n }\n } catch (error: unknown) {\n console.log('⚠️ Could not post comment:', (error as Error).message);\n }\n}\n\nfunction getGitHubContext(): any {\n const eventPath = process.env.GITHUB_EVENT_PATH;\n let event = {};\n\n if (eventPath) {\n try {\n const eventData = readFileSync(eventPath, 'utf-8');\n event = JSON.parse(eventData);\n } catch {\n // Ignore\n }\n }\n\n return {\n eventName: process.env.GITHUB_EVENT_NAME || '',\n repository: process.env.GITHUB_REPOSITORY || '',\n ref: process.env.GITHUB_REF || '',\n sha: process.env.GITHUB_SHA || '',\n actor: process.env.GITHUB_ACTOR || '',\n workflow: process.env.GITHUB_WORKFLOW || '',\n defaultBranch: (event as any).repository?.default_branch || 'main',\n baseRef: (event as any).pull_request?.base?.ref || 'main',\n prNumber: (event as any).pull_request?.number,\n };\n}\n\nasync function analyzeRepo(rootDir: string, config: Config): Promise<any> {\n const files = await findSourceFiles(rootDir, config.ignore || []);\n\n const allImports = [];\n const allFiles: string[] = [];\n\n for (const file of files) {\n const fullPath = path.join(rootDir, file);\n const content = await fs.readFile(fullPath, 'utf-8');\n\n let imports;\n if (file.endsWith('.ts') || file.endsWith('.tsx') || file.endsWith('.js') || file.endsWith('.jsx')) {\n imports = await extractTypeScriptImports(file, content);\n } else if (file.endsWith('.py')) {\n imports = await extractPythonImports(file, content);\n } else {\n continue;\n }\n\n allImports.push(imports);\n allFiles.push(file);\n }\n\n const tsconfig = await loadTsConfig(rootDir);\n const workspaces = await findWorkspaces(rootDir);\n\n const resolved = await resolveImports(allImports, {\n rootDir,\n tsconfig: tsconfig || undefined,\n workspaces: workspaces.length > 0 ? workspaces : undefined,\n });\n\n const moduleConfig = config.modules || {};\n const graph = buildGraph(resolved, allFiles, moduleConfig);\n\n return graph;\n}\n\nasync function findSourceFiles(rootDir: string, ignore: string[]): Promise<string[]> {\n const files: string[] = [];\n const ignorePatterns = [\n '**/node_modules/**',\n '**/dist/**',\n '**/build/**',\n '**/.git/**',\n '**/coverage/**',\n '**/*.test.*',\n '**/*.spec.*',\n '**/__tests__/**',\n ...ignore,\n ];\n\n async function walk(dir: string): Promise<void> {\n const entries = await fs.readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = path.join(dir, entry.name);\n const relativePath = path.relative(rootDir, fullPath);\n\n if (shouldIgnore(relativePath, ignorePatterns)) {\n continue;\n }\n\n if (entry.isDirectory()) {\n await walk(fullPath);\n } else if (entry.isFile()) {\n if (isSourceFile(entry.name)) {\n files.push(relativePath);\n }\n }\n }\n }\n\n await walk(rootDir);\n return files;\n}\n\nfunction shouldIgnore(filePath: string, patterns: string[]): boolean {\n const normalized = filePath.replace(/\\\\/g, '/');\n\n for (const pattern of patterns) {\n const regex = patternToRegex(pattern);\n if (regex.test(normalized)) {\n return true;\n }\n }\n\n return false;\n}\n\nfunction patternToRegex(pattern: string): RegExp {\n const regexStr = pattern\n .replace(/[.+?^${}()|[\\]\\\\]/g, '\\\\$&')\n .replace(/\\*\\*/g, '___DOUBLESTAR___')\n .replace(/\\*/g, '[^/]*')\n .replace(/___DOUBLESTAR___/g, '.*');\n\n return new RegExp(`^${regexStr}$`);\n}\n\nfunction isSourceFile(name: string): boolean {\n return /\\.(ts|tsx|js|jsx|py)$/.test(name);\n}\n\nasync function loadConfig(rootDir: string): Promise<Config> {\n const configPath = path.join(rootDir, 'archmap.yml');\n\n let config: Config = {};\n try {\n const content = await fs.readFile(configPath, 'utf-8');\n const yaml = await import('js-yaml');\n config = yaml.load(content) as Config;\n } catch {\n config = {};\n }\n\n // Override with INPUT_MODE if explicitly provided (B7)\n const inputMode = process.env.INPUT_MODE;\n if (inputMode && (inputMode === 'comment' || inputMode === 'gate')) {\n config.mode = inputMode;\n }\n\n return config;\n}\n\nrunAction().catch((error) => {\n console.error('Error:', error.message);\n process.exit(1);\n});\n"],"mappings":"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","names":[]}