@jatingargiitk/archmap 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jatin Garg
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 CHANGED
@@ -1,3 +1,215 @@
1
- # Temporary Holding Version
1
+ # archmap
2
2
 
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.
3
+ > **Snapshot tests for your architecture.** Deterministic module dependency graphs with lockfile diffing.
4
+
5
+ **✅ 100% Deterministic • 🔒 No API Keys • 🆓 Free**
6
+
7
+ **Your AI agent just made `ui/` import `db/`. archmap noticed.**
8
+
9
+ archmap commits a living map of your modules and flags every PR—human or AI—that adds a new dependency between them.
10
+
11
+ ---
12
+
13
+ ## Quick Start
14
+
15
+ ```bash
16
+ npx @jatingargiitk/archmap init
17
+ ```
18
+
19
+ This creates:
20
+ - ✅ `.github/workflows/archmap.yml` - GitHub Action
21
+ - ✅ `ARCHITECTURE.md` - Living architecture map ([see ours](./ARCHITECTURE.md))
22
+ - ✅ `archmap.lock.json` - Dependency lockfile
23
+
24
+ Then:
25
+
26
+ ```yaml
27
+ # .github/workflows/archmap.yml (auto-generated)
28
+ on: [pull_request, push]
29
+ permissions: { contents: write, pull-requests: write }
30
+ jobs:
31
+ archmap:
32
+ runs-on: ubuntu-latest
33
+ steps:
34
+ - uses: actions/checkout@v4
35
+ - uses: jatingargiitk/archmap@v0
36
+ ```
37
+
38
+ ---
39
+
40
+ ## What Problem Does This Solve?
41
+
42
+ **The Problem:** AI agents (and humans) quietly add dependencies that violate your architecture. You discover this months later when the codebase is unmaintainable.
43
+
44
+ **The Solution:** archmap tracks your module-level dependency graph in a lockfile. New cross-module edges trigger a required approval step, making architecture changes visible and intentional.
45
+
46
+ Think of it as snapshot testing, but for your architecture.
47
+
48
+ ---
49
+
50
+ ## Example: Before & After
51
+
52
+ ### Before archmap
53
+
54
+ ```diff
55
+ // src/ui/UserProfile.tsx
56
+ + import { getUserFromDB } from '../db/users'; // Oops! UI directly importing from DB
57
+ ```
58
+
59
+ ✅ Tests pass
60
+ ✅ Linter passes
61
+ ✅ PR approved
62
+ ❌ Architecture silently violated
63
+
64
+ ### After archmap
65
+
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.
69
+
70
+ ---
71
+
72
+ ## Features
73
+
74
+ - ✅ **100% Deterministic** - Same input, same output. No LLM or API key required
75
+ - ✅ **Fast** - Parses with `web-tree-sitter` WASM (TS/JS + Python in v0.1)
76
+ - ✅ **Lockfile Diffing** - Git-friendly JSON lockfile tracks accepted edges
77
+ - ✅ **GitHub Action** - Sticky PR comments show only added/removed edges
78
+ - ✅ **Optional Gate Mode** - Fail CI if unapproved edges are added
79
+ - ✅ **Deny Rules** - `deny: { from: "ui/**", to: "db/**" }`
80
+ - ✅ **Living Docs** - `ARCHITECTURE.md` with Mermaid diagram, auto-regenerated
81
+ - ✅ **Agent Skill** - Works with Claude Code, Cursor, and Codex
82
+
83
+ ---
84
+
85
+ ## CLI Commands
86
+
87
+ ```bash
88
+ npx @jatingargiitk/archmap init # Initialize archmap
89
+ npx @jatingargiitk/archmap render # Generate ARCHITECTURE.md and lock
90
+ npx @jatingargiitk/archmap diff # Show diff against main
91
+ npx @jatingargiitk/archmap check # Check for unapproved changes
92
+ npx @jatingargiitk/archmap accept # Accept current changes
93
+ ```
94
+
95
+ ---
96
+
97
+ ## Configuration
98
+
99
+ Optional `archmap.yml`:
100
+
101
+ ```yaml
102
+ modules:
103
+ roots: [src, packages/*, apps/*]
104
+ depth: 1 # 0 = collapse entire root, 1+ = subfolders
105
+
106
+ ignore:
107
+ - "**/*.test.*"
108
+ - "**/fixtures/**"
109
+
110
+ rules:
111
+ - deny: { from: "ui/**", to: "db/**" }
112
+ - deny: { from: "api/**", to: "ui/**" }
113
+
114
+ mode: comment # or "gate" to fail CI on unapproved edges
115
+ ```
116
+
117
+ ---
118
+
119
+ ## Agent Skill
120
+
121
+ archmap ships with a skill for Claude Code, Cursor, and Codex:
122
+
123
+ **Install:**
124
+ ```bash
125
+ npx skills add jatingargiitk/archmap
126
+ ```
127
+
128
+ The agent will now run `archmap check` before finishing tasks, catching architectural drift automatically.
129
+
130
+ ---
131
+
132
+ ## Comparison
133
+
134
+ | | **archmap** | graphify | CodeBoarding | pr-lens | dependency-cruiser |
135
+ |---|---|---|---|---|---|
136
+ | **Focus** | Architecture guarding | Agent memory | Platform + analysis | PR explainer | Linting |
137
+ | **Deterministic** | ✅ Yes | Hybrid | LLM-driven | LLM-driven | ✅ Yes |
138
+ | **No API Key** | ✅ Yes | ✅ Yes | ❌ Needs key | ❌ Needs key | ✅ Yes |
139
+ | **Lockfile Diffing** | ✅ Yes | ❌ No | ❌ No | ❌ No | ❌ No |
140
+ | **Living Docs** | ✅ Committed | Local HTML | Web platform | Per-PR only | Config file |
141
+ | **Languages** | TS/JS, Python | 40+ | 9 | General | JS/TS only |
142
+ | **Setup** | 1 workflow file | uv + skill | Hosted + telemetry | App or Action | Config wizard |
143
+
144
+ **Use archmap when:**
145
+ - You want snapshot testing for architecture
146
+ - You're reviewing AI-written code
147
+ - You need a deterministic, zero-config solution
148
+
149
+ **Use graphify when:**
150
+ - You need agent context and memory
151
+ - You want queryable knowledge graphs
152
+
153
+ ---
154
+
155
+ ## FAQ
156
+
157
+ ### Does archmap work with monorepos?
158
+
159
+ Yes! Configure `modules.roots` to match your structure:
160
+
161
+ ```yaml
162
+ modules:
163
+ roots: [packages/*, apps/*]
164
+ depth: 0 # Collapse each package into one node
165
+ ```
166
+
167
+ ### What about fork PRs?
168
+
169
+ archmap uses read-only tokens for forks. It posts a comment but cannot update files. The reviewer runs `archmap accept` locally.
170
+
171
+ ### Does anything leave my runner?
172
+
173
+ No. archmap is 100% local. The optional `explain` mode (not yet implemented) will send structural diffs (JSON) to an LLM, but never your code.
174
+
175
+ ### How is this different from Dependabot?
176
+
177
+ Dependabot tracks external package updates. archmap tracks your internal module architecture.
178
+
179
+ ### Can I use this for Go/Rust/Java?
180
+
181
+ Not yet. v0.1 supports TypeScript/JavaScript and Python. Go is planned for v0.2.
182
+
183
+ ---
184
+
185
+ ## Limitations
186
+
187
+ ### Protected Default Branch
188
+
189
+ If your default branch (e.g., `main`) has branch protection rules enabled, the GitHub Action cannot push `ARCHITECTURE.md` and `archmap.lock.json` updates directly on `push` events.
190
+
191
+ **Workaround:** Either:
192
+ 1. Disable branch protection for the `archmap[bot]` user or GitHub Actions
193
+ 2. Only run archmap on `pull_request` events (recommended)
194
+ 3. Manually run `archmap accept` locally and commit the changes
195
+
196
+ The `pull_request` workflow mode (default) is unaffected by branch protection.
197
+
198
+ ---
199
+
200
+ ## Contributing
201
+
202
+ See [CONTRIBUTING.md](./CONTRIBUTING.md) for development setup, publishing instructions, and how to regenerate the demo GIF.
203
+
204
+ ---
205
+
206
+ ## License
207
+
208
+ MIT © Jatin Garg
209
+
210
+ ---
211
+
212
+ **Made for developers reviewing AI-written code.**
213
+ Track architecture. Make drift visible. Keep understanding your codebase.
214
+
215
+ Built by [@jatingargiitk](https://x.com/jatingargiitk)
package/action.yml ADDED
@@ -0,0 +1,21 @@
1
+ name: 'archmap'
2
+ description: 'Snapshot tests for your architecture - track module dependencies'
3
+ author: 'Jatin Garg'
4
+
5
+ branding:
6
+ icon: 'map'
7
+ color: 'blue'
8
+
9
+ runs:
10
+ using: 'node20'
11
+ main: 'dist/action.js'
12
+
13
+ inputs:
14
+ mode:
15
+ description: 'Mode: comment (default) or gate'
16
+ required: false
17
+ default: 'comment'
18
+ github-token:
19
+ description: 'GitHub token for posting comments'
20
+ required: false
21
+ default: ${{ github.token }}
@@ -0,0 +1,2 @@
1
+
2
+ export { }
package/dist/action.js ADDED
@@ -0,0 +1,338 @@
1
+ #!/usr/bin/env node
2
+ import {
3
+ buildGraph,
4
+ checkRules,
5
+ diffLocks,
6
+ extractPythonImports,
7
+ extractTypeScriptImports,
8
+ findWorkspaces,
9
+ graphToLock,
10
+ loadTsConfig,
11
+ renderDeltaDiagram,
12
+ resolveImports,
13
+ shouldFailCheck,
14
+ writeArchitectureFile,
15
+ writeLockFile
16
+ } from "./chunk-UVZVBM2N.js";
17
+
18
+ // src/action.ts
19
+ import * as fs from "fs/promises";
20
+ import { readFileSync } from "fs";
21
+ import * as path from "path";
22
+ import { execSync } from "child_process";
23
+ async function runAction() {
24
+ const githubContext = getGitHubContext();
25
+ const rootDir = process.env.GITHUB_WORKSPACE || process.cwd();
26
+ console.log("\u{1F50D} Analyzing architecture...");
27
+ const config = await loadConfig(rootDir);
28
+ const currentGraph = await analyzeRepo(rootDir, config);
29
+ const currentLock = graphToLock(currentGraph);
30
+ await writeArchitectureFile(
31
+ path.join(rootDir, "ARCHITECTURE.md"),
32
+ currentGraph
33
+ );
34
+ await writeLockFile(path.join(rootDir, "archmap.lock.json"), currentLock);
35
+ if (githubContext.eventName === "pull_request") {
36
+ await handlePullRequest(rootDir, currentGraph, currentLock, config, githubContext);
37
+ } else if (githubContext.eventName === "push") {
38
+ await handlePush(rootDir, githubContext);
39
+ }
40
+ }
41
+ async function handlePullRequest(rootDir, currentGraph, currentLock, config, context) {
42
+ console.log("\u{1F4CA} Computing diff...");
43
+ let baseLock = null;
44
+ try {
45
+ const baseLockContent = execSync(
46
+ `git show origin/${context.baseRef}:archmap.lock.json`,
47
+ { cwd: rootDir, encoding: "utf-8" }
48
+ );
49
+ baseLock = JSON.parse(baseLockContent);
50
+ } catch {
51
+ console.log("\u26A0\uFE0F No lock file found in base branch");
52
+ }
53
+ const diff = diffLocks(baseLock, currentLock, currentGraph);
54
+ const violations = checkRules(currentGraph.edges, config);
55
+ const commentBody = buildCommentBody(diff, violations, currentGraph);
56
+ if (process.env.GITHUB_TOKEN && context.prNumber) {
57
+ await postOrUpdateComment(context, commentBody);
58
+ } else {
59
+ console.log("Comment preview:");
60
+ console.log(commentBody);
61
+ }
62
+ const shouldFail = shouldFailCheck(diff, violations, config);
63
+ if (shouldFail) {
64
+ console.log("\u274C Check failed");
65
+ process.exit(1);
66
+ } else {
67
+ console.log("\u2705 Check passed");
68
+ }
69
+ }
70
+ async function handlePush(rootDir, context) {
71
+ if (context.ref === `refs/heads/${context.defaultBranch}`) {
72
+ console.log("\u{1F504} Committing updated architecture files...");
73
+ try {
74
+ execSync('git config user.name "archmap[bot]"', { cwd: rootDir });
75
+ execSync('git config user.email "archmap[bot]@users.noreply.github.com"', { cwd: rootDir });
76
+ execSync("git add ARCHITECTURE.md archmap.lock.json", { cwd: rootDir });
77
+ try {
78
+ execSync('git commit -m "chore: update architecture map [skip ci]"', { cwd: rootDir });
79
+ execSync(`git push origin ${context.defaultBranch}`, { cwd: rootDir });
80
+ console.log("\u2705 Architecture files updated");
81
+ } catch {
82
+ console.log("\u2139\uFE0F No changes to commit");
83
+ }
84
+ } catch (error) {
85
+ console.log("\u26A0\uFE0F Could not commit changes:", error.message);
86
+ }
87
+ }
88
+ }
89
+ function buildCommentBody(diff, violations, _graph) {
90
+ const lines = [];
91
+ lines.push("## \u{1F5FA}\uFE0F archmap");
92
+ lines.push("");
93
+ const summary = `**${diff.added.length > 0 ? "+" : ""}${diff.added.length} new edge${diff.added.length !== 1 ? "s" : ""}, ${diff.removed.length > 0 ? "-" : ""}${diff.removed.length} removed**`;
94
+ lines.push(summary);
95
+ lines.push("");
96
+ if (violations.length > 0) {
97
+ lines.push("### \u26A0\uFE0F Rule Violations");
98
+ lines.push("");
99
+ for (const violation of violations) {
100
+ lines.push(`- ${violation.message}`);
101
+ lines.push(` - Rule: \`${violation.rule}\``);
102
+ }
103
+ lines.push("");
104
+ }
105
+ if (diff.added.length > 0 || diff.removed.length > 0) {
106
+ const touchedModules = /* @__PURE__ */ new Set();
107
+ for (const edge of diff.added) {
108
+ touchedModules.add(edge.from);
109
+ touchedModules.add(edge.to);
110
+ }
111
+ for (const edge of diff.removed) {
112
+ touchedModules.add(edge.from);
113
+ touchedModules.add(edge.to);
114
+ }
115
+ lines.push("### Changes");
116
+ lines.push("");
117
+ lines.push(renderDeltaDiagram(diff.added, diff.removed, touchedModules));
118
+ lines.push("");
119
+ if (diff.added.length > 0) {
120
+ lines.push("#### Added Dependencies");
121
+ lines.push("");
122
+ for (const edge of diff.added) {
123
+ if (edge.locations && edge.locations.length > 0) {
124
+ const loc = edge.locations[0];
125
+ lines.push(`- \`${edge.from}\` \u2192 \`${edge.to}\` ([${loc.file}:${loc.line}](${loc.file}#L${loc.line}))`);
126
+ } else {
127
+ lines.push(`- \`${edge.from}\` \u2192 \`${edge.to}\``);
128
+ }
129
+ }
130
+ lines.push("");
131
+ }
132
+ if (diff.removed.length > 0) {
133
+ lines.push("#### Removed Dependencies");
134
+ lines.push("");
135
+ for (const edge of diff.removed) {
136
+ lines.push(`- \`${edge.from}\` \u2192 \`${edge.to}\``);
137
+ }
138
+ lines.push("");
139
+ }
140
+ }
141
+ if (diff.newCycles.length > 0) {
142
+ lines.push("### \u26A0\uFE0F New Cycles");
143
+ lines.push("");
144
+ for (const cycle of diff.newCycles) {
145
+ lines.push(`- ${cycle.map((m) => `\`${m}\``).join(" \u2192 ")}`);
146
+ }
147
+ lines.push("");
148
+ }
149
+ if (diff.added.length === 0 && diff.removed.length === 0 && violations.length === 0) {
150
+ lines.push("\u2705 No architectural changes detected");
151
+ } else {
152
+ lines.push("---");
153
+ lines.push("");
154
+ lines.push("To accept these changes, run:");
155
+ lines.push("```bash");
156
+ lines.push("npx @jatingargiitk/archmap accept");
157
+ lines.push("git add ARCHITECTURE.md archmap.lock.json");
158
+ lines.push('git commit -m "chore: update architecture map"');
159
+ lines.push("```");
160
+ }
161
+ lines.push("");
162
+ lines.push("<!-- archmap-comment -->");
163
+ return lines.join("\n");
164
+ }
165
+ async function postOrUpdateComment(context, body) {
166
+ const token = process.env.GITHUB_TOKEN;
167
+ if (!token) return;
168
+ const [owner, repo] = context.repository.split("/");
169
+ const issueNumber = context.prNumber;
170
+ try {
171
+ const existingComments = await fetch(
172
+ `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,
173
+ {
174
+ headers: {
175
+ Authorization: `Bearer ${token}`,
176
+ "User-Agent": "archmap"
177
+ }
178
+ }
179
+ );
180
+ const comments = await existingComments.json();
181
+ const existingComment = comments.find(
182
+ (c) => c.body.includes("<!-- archmap-comment -->")
183
+ );
184
+ if (existingComment) {
185
+ await fetch(
186
+ `https://api.github.com/repos/${owner}/${repo}/issues/comments/${existingComment.id}`,
187
+ {
188
+ method: "PATCH",
189
+ headers: {
190
+ Authorization: `Bearer ${token}`,
191
+ "Content-Type": "application/json",
192
+ "User-Agent": "archmap"
193
+ },
194
+ body: JSON.stringify({ body })
195
+ }
196
+ );
197
+ console.log("\u2705 Updated existing comment");
198
+ } else {
199
+ await fetch(
200
+ `https://api.github.com/repos/${owner}/${repo}/issues/${issueNumber}/comments`,
201
+ {
202
+ method: "POST",
203
+ headers: {
204
+ Authorization: `Bearer ${token}`,
205
+ "Content-Type": "application/json",
206
+ "User-Agent": "archmap"
207
+ },
208
+ body: JSON.stringify({ body })
209
+ }
210
+ );
211
+ console.log("\u2705 Posted new comment");
212
+ }
213
+ } catch (error) {
214
+ console.log("\u26A0\uFE0F Could not post comment:", error.message);
215
+ }
216
+ }
217
+ function getGitHubContext() {
218
+ const eventPath = process.env.GITHUB_EVENT_PATH;
219
+ let event = {};
220
+ if (eventPath) {
221
+ try {
222
+ const eventData = readFileSync(eventPath, "utf-8");
223
+ event = JSON.parse(eventData);
224
+ } catch {
225
+ }
226
+ }
227
+ return {
228
+ eventName: process.env.GITHUB_EVENT_NAME || "",
229
+ repository: process.env.GITHUB_REPOSITORY || "",
230
+ ref: process.env.GITHUB_REF || "",
231
+ sha: process.env.GITHUB_SHA || "",
232
+ actor: process.env.GITHUB_ACTOR || "",
233
+ workflow: process.env.GITHUB_WORKFLOW || "",
234
+ defaultBranch: event.repository?.default_branch || "main",
235
+ baseRef: event.pull_request?.base?.ref || "main",
236
+ prNumber: event.pull_request?.number
237
+ };
238
+ }
239
+ async function analyzeRepo(rootDir, config) {
240
+ const files = await findSourceFiles(rootDir, config.ignore || []);
241
+ const allImports = [];
242
+ const allFiles = [];
243
+ for (const file of files) {
244
+ const fullPath = path.join(rootDir, file);
245
+ const content = await fs.readFile(fullPath, "utf-8");
246
+ let imports;
247
+ if (file.endsWith(".ts") || file.endsWith(".tsx") || file.endsWith(".js") || file.endsWith(".jsx")) {
248
+ imports = await extractTypeScriptImports(file, content);
249
+ } else if (file.endsWith(".py")) {
250
+ imports = await extractPythonImports(file, content);
251
+ } else {
252
+ continue;
253
+ }
254
+ allImports.push(imports);
255
+ allFiles.push(file);
256
+ }
257
+ const tsconfig = await loadTsConfig(rootDir);
258
+ const workspaces = await findWorkspaces(rootDir);
259
+ const resolved = await resolveImports(allImports, {
260
+ rootDir,
261
+ tsconfig: tsconfig || void 0,
262
+ workspaces: workspaces.length > 0 ? workspaces : void 0
263
+ });
264
+ const moduleConfig = config.modules || {};
265
+ const graph = buildGraph(resolved, allFiles, moduleConfig);
266
+ return graph;
267
+ }
268
+ async function findSourceFiles(rootDir, ignore) {
269
+ const files = [];
270
+ const ignorePatterns = [
271
+ "**/node_modules/**",
272
+ "**/dist/**",
273
+ "**/build/**",
274
+ "**/.git/**",
275
+ "**/coverage/**",
276
+ "**/*.test.*",
277
+ "**/*.spec.*",
278
+ "**/__tests__/**",
279
+ ...ignore
280
+ ];
281
+ async function walk(dir) {
282
+ const entries = await fs.readdir(dir, { withFileTypes: true });
283
+ for (const entry of entries) {
284
+ const fullPath = path.join(dir, entry.name);
285
+ const relativePath = path.relative(rootDir, fullPath);
286
+ if (shouldIgnore(relativePath, ignorePatterns)) {
287
+ continue;
288
+ }
289
+ if (entry.isDirectory()) {
290
+ await walk(fullPath);
291
+ } else if (entry.isFile()) {
292
+ if (isSourceFile(entry.name)) {
293
+ files.push(relativePath);
294
+ }
295
+ }
296
+ }
297
+ }
298
+ await walk(rootDir);
299
+ return files;
300
+ }
301
+ function shouldIgnore(filePath, patterns) {
302
+ const normalized = filePath.replace(/\\/g, "/");
303
+ for (const pattern of patterns) {
304
+ const regex = patternToRegex(pattern);
305
+ if (regex.test(normalized)) {
306
+ return true;
307
+ }
308
+ }
309
+ return false;
310
+ }
311
+ function patternToRegex(pattern) {
312
+ const regexStr = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*\*/g, "___DOUBLESTAR___").replace(/\*/g, "[^/]*").replace(/___DOUBLESTAR___/g, ".*");
313
+ return new RegExp(`^${regexStr}$`);
314
+ }
315
+ function isSourceFile(name) {
316
+ return /\.(ts|tsx|js|jsx|py)$/.test(name);
317
+ }
318
+ async function loadConfig(rootDir) {
319
+ const configPath = path.join(rootDir, "archmap.yml");
320
+ let config = {};
321
+ try {
322
+ const content = await fs.readFile(configPath, "utf-8");
323
+ const yaml = await import("js-yaml");
324
+ config = yaml.load(content);
325
+ } catch {
326
+ config = {};
327
+ }
328
+ const inputMode = process.env.INPUT_MODE;
329
+ if (inputMode === "comment" || inputMode === "gate") {
330
+ config.mode = inputMode;
331
+ }
332
+ return config;
333
+ }
334
+ runAction().catch((error) => {
335
+ console.error("Error:", error.message);
336
+ process.exit(1);
337
+ });
338
+ //# sourceMappingURL=action.js.map
@@ -0,0 +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":[]}