axmap-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (78) hide show
  1. package/.claude/commands/ax-done.md +10 -0
  2. package/.claude/commands/ax-setup.md +31 -0
  3. package/.claude/commands/ax-start.md +15 -0
  4. package/.claude/commands/ax-tell.md +14 -0
  5. package/.claude/commands/ax-update.md +19 -0
  6. package/.claude/commands/ax.md +13 -0
  7. package/CLAUDE.md +309 -0
  8. package/LICENSE +20 -0
  9. package/README.md +207 -0
  10. package/app/README.md +366 -0
  11. package/app/eval/edges.mjs +242 -0
  12. package/app/lib/adjacent.mjs +125 -0
  13. package/app/lib/agentcli.mjs +153 -0
  14. package/app/lib/analyze.mjs +1159 -0
  15. package/app/lib/cochange.mjs +421 -0
  16. package/app/lib/datanodes.mjs +127 -0
  17. package/app/lib/entry.mjs +192 -0
  18. package/app/lib/featuregraph.mjs +389 -0
  19. package/app/lib/features.mjs +645 -0
  20. package/app/lib/fetchrepo-run.mjs +37 -0
  21. package/app/lib/fetchrepo.mjs +164 -0
  22. package/app/lib/flow.mjs +1089 -0
  23. package/app/lib/ladder.mjs +387 -0
  24. package/app/lib/langs.mjs +630 -0
  25. package/app/lib/live.mjs +346 -0
  26. package/app/lib/llm.mjs +594 -0
  27. package/app/lib/newfile.mjs +126 -0
  28. package/app/lib/prdiff.mjs +651 -0
  29. package/app/lib/reveal.mjs +316 -0
  30. package/app/lib/roots.mjs +186 -0
  31. package/app/lib/scope.mjs +342 -0
  32. package/app/lib/session.mjs +389 -0
  33. package/app/lib/slots.mjs +233 -0
  34. package/app/lib/ssot.mjs +277 -0
  35. package/app/lib/teamview.mjs +962 -0
  36. package/app/lib/terms.ko.mjs +169 -0
  37. package/app/server.mjs +1959 -0
  38. package/app/web/shell.css +538 -0
  39. package/app/web/shell.html +197 -0
  40. package/app/web/shell.js +638 -0
  41. package/app/web/stage.js +347 -0
  42. package/app/web/words.js +85 -0
  43. package/bin/axmap.mjs +1918 -0
  44. package/governance/GOVERNANCE.md +433 -0
  45. package/governance/gate.mjs +526 -0
  46. package/governance/vote.mjs +501 -0
  47. package/mcp/README.md +254 -0
  48. package/mcp/SETUP-FOR-AI.md +186 -0
  49. package/mcp/install.ps1 +341 -0
  50. package/mcp/install.sh +339 -0
  51. package/mcp/server.mjs +969 -0
  52. package/package.json +48 -0
  53. package/src/closure.mjs +343 -0
  54. package/src/governance.mjs +839 -0
  55. package/src/invariants.mjs +226 -0
  56. package/src/mrtarget.mjs +284 -0
  57. package/src/promote.mjs +177 -0
  58. package/src/protocol.mjs +423 -0
  59. package/src/repotarget.mjs +81 -0
  60. package/src/update.mjs +177 -0
  61. package/src/version.mjs +186 -0
  62. package/tools/bus.mjs +520 -0
  63. package/tools/cluster-experiment.mjs +256 -0
  64. package/tools/cluster-sweep.mjs +226 -0
  65. package/tools/make-icon.mjs +108 -0
  66. package/tools/mcp-register.mjs +269 -0
  67. package/tools/mr-target.mjs +49 -0
  68. package/tools/persona-bench.mjs +362 -0
  69. package/tools/pick-repo.mjs +229 -0
  70. package/tools/promote.mjs +550 -0
  71. package/tools/reveal-demo.mjs +158 -0
  72. package/tools/run-tests.mjs +42 -0
  73. package/tools/setup.mjs +490 -0
  74. package/tools/shortcut.mjs +121 -0
  75. package/tools/smoke.mjs +166 -0
  76. package/tools/topicgraph.py +154 -0
  77. package/tools/vendor.mjs +382 -0
  78. package/tools/version.mjs +115 -0
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "//name": "๐Ÿ”ด npm ์˜ 'axmap' ์€ 2022๋…„๋ถ€ํ„ฐ ๋‚จ์ด ์“ฐ๊ณ  ์žˆ๋Š” ๋‹ค๋ฅธ ๊พธ๋Ÿฌ๋ฏธ๋‹ค(leozin_ ์˜ Map ๋ผ์ด๋ธŒ๋Ÿฌ๋ฆฌ, 1.1.2). ๊ทธ๋ž˜์„œ `npx axmap` ์€ ์šฐ๋ฆฌ ๊ฒƒ์ด ์•„๋‹ˆ๊ณ , ๋ˆ„๊ตฌ์—๊ฒŒ๋„ ๊ทธ๋ ‡๊ฒŒ ์•ˆ๋‚ดํ•˜๋ฉด ์•ˆ ๋œ๋‹ค. 2026-08-28 ์— axmap-cli ๋กœ ์ •ํ–ˆ๋‹ค โ€” ์Šค์ฝ”ํ”„(@์‚ฌ์šฉ์ž/์ด๋ฆ„)๋ฅผ ์“ฐ์ง€ ์•Š๋Š” ์ด์œ ๋Š” ์Šค์ฝ”ํ”„๊ฐ€ npm ๊ณ„์ • ์ด๋ฆ„์— ๋ฌถ์—ฌ์„œ ๋‚˜์ค‘์— ํŒ€์ด๋‚˜ ์กฐ์ง์œผ๋กœ ์˜ฎ๊ธธ ๋•Œ ์ด๋ฆ„์ด ํ†ต์งธ๋กœ ๋ฐ”๋€Œ๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์‚ฌ๋žŒ์ด ์น˜๋Š” **๋ช…๋ น** ์ด๋ฆ„์€ ๊ทธ๋Œ€๋กœ axmap ์ด๋‹ค (์•„๋ž˜ bin) โ€” ๊พธ๋Ÿฌ๋ฏธ ์ด๋ฆ„๊ณผ ๋ช…๋ น ์ด๋ฆ„์€ ๋‹ฌ๋ผ๋„ ๋œ๋‹ค.",
3
+ "name": "axmap-cli",
4
+ "version": "0.0.1",
5
+ "//private": "๐Ÿ”ด ์—ฌ๊ธฐ ์žˆ๋˜ \"private\": true ๋ฅผ 2026-08-28 ์— ์ง€์› ๋‹ค. ๊ทธ ์ค„์ด ์žˆ๋Š” ๋™์•ˆ npm publish ๋Š” ๊ฑฐ๋ถ€๋๋‹ค. ๋˜๋Œ๋ฆฌ๋ ค๋ฉด ๋‹ค์‹œ ๋„ฃ์œผ๋ฉด ๋˜์ง€๋งŒ, ์ด๋ฏธ ์˜ฌ๋ผ๊ฐ„ ๋ฒ„์ „์€ ๊ทธ๋ž˜๋„ ์•ˆ ์‚ฌ๋ผ์ง„๋‹ค โ€” npm ์€ ๊ฐ™์€ ๋ฒˆํ˜ธ๋ฅผ ๋ฎ์–ด์“ฐ์ง€ ๋ชปํ•˜๊ณ  ์‚ญ์ œ๋„ 72์‹œ๊ฐ„ ์•ˆ์—๋งŒ ๋œ๋‹ค. ์ฆ‰ ์ด ์ค„์„ ๋˜์‚ด๋ฆฌ๋Š” ๊ฒƒ์€ '์•ž์œผ๋กœ ์•ˆ ์˜ฌ๋ฆฐ๋‹ค' ๋Š” ๋œป์ด์ง€ '์˜ฌ๋ฆฐ ๊ฒƒ์„ ์—†์•ค๋‹ค' ๋Š” ๋œป์ด ์•„๋‹ˆ๋‹ค.",
6
+ "//publishConfig": "axmap-cli ๋Š” ์Šค์ฝ”ํ”„๊ฐ€ ์—†์–ด์„œ ๊ธฐ๋ณธ์ด ์ด๋ฏธ ๊ณต๊ฐœ๋‹ค. ์ฆ‰ ์ง€๊ธˆ์€ ์—†์–ด๋„ ๋œ๋‹ค. ๊ทธ๋ž˜๋„ ๋‚จ๊ฒจ ๋‘๋Š” ์ด์œ : ๋‚˜์ค‘์— @์กฐ์ง/axmap ์ฒ˜๋Ÿผ ์Šค์ฝ”ํ”„๋ฅผ ๋ถ™์ด๋Š” ๋‚  ์ด ์ค„์ด ์—†์œผ๋ฉด publish ๊ฐ€ '์œ ๋ฃŒ ํ”Œ๋žœ์ด ํ•„์š”ํ•˜๋‹ค'๋Š” ์—‰๋šฑํ•œ ๋ง๋กœ ์‹คํŒจํ•œ๋‹ค.",
7
+ "publishConfig": {
8
+ "access": "public"
9
+ },
10
+ "type": "module",
11
+ "description": "axMap โ€” AI Experience ์˜ ์ง€๋„์ด์ž ๊ฐœ๋ฐœ ํ™˜๊ฒฝ์˜ AI Transformation. AI ์—์ด์ „ํŠธ๋“ค์ด ์„œ๋กœ์˜ ์ž‘์—… ์˜์—ญ์„ ์นจ๋ฒ”ํ•˜์ง€ ์•Š๋„๋ก, ์ฝ”๋“œ ์ถฉ๋Œ ๋Œ€์‹  ์˜๋„ ์ถฉ๋Œ์„ ๋จผ์ € ํ„ฐ๋œจ๋ฆฌ๋Š” git ์œ„์˜ ์„ ์  ํ”„๋กœํ† ์ฝœ",
12
+ "license": "MIT",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "git+https://lab.ssafy.com/rleaderjoon/axmap.git"
16
+ },
17
+ "keywords": ["ai-agent", "claim", "lock", "git", "mcp", "collaboration", "preemption"],
18
+ "bin": {
19
+ "axmap": "bin/axmap.mjs"
20
+ },
21
+ "//files": "npm ๊พธ๋Ÿฌ๋ฏธ์— ๋“ค์–ด๊ฐˆ ๊ฒƒ. ์ด ๋ชฉ๋ก์ด ์—†์œผ๋ฉด ๋ฒค์น˜ ์‚ฐ์ถœ๋ฌผยท๋ฐ๋ชจยทํ…Œ์ŠคํŠธ๊นŒ์ง€ ์‹ค๋ ค ๊พธ๋Ÿฌ๋ฏธ๊ฐ€ ์ˆ˜์‹ญ MB ๊ฐ€ ๋œ๋‹ค. ์—ฌ๊ธฐ์„œ ๋นผ๋Š” ๊ฒƒ๋“ค(testยทdemoยทcorpusยทdesktopยทdocs)์€ ์„ค์น˜ํ•œ ์‚ฌ๋žŒ์—๊ฒŒ ํ•„์š” ์—†๋‹ค โ€” ๊ทธ๊ฑด ์ด ์ €์žฅ์†Œ๋ฅผ clone ํ•œ ์‚ฌ๋žŒ์˜ ๊ฒƒ์ด๋‹ค.",
22
+ "files": [
23
+ "bin/",
24
+ "src/",
25
+ "mcp/",
26
+ "tools/",
27
+ "app/",
28
+ "governance/",
29
+ ".claude/",
30
+ "CLAUDE.md"
31
+ ],
32
+ "scripts": {
33
+ "test": "node tools/run-tests.mjs",
34
+ "setup": "node tools/setup.mjs",
35
+ "demo": "bash demo/run-demo.sh",
36
+ "demo:compare": "bash demo/conflict-vs-parser.sh",
37
+ "demo:chaos": "bash demo/chaos.sh",
38
+ "app": "node app/server.mjs",
39
+ "smoke": "node tools/smoke.mjs",
40
+ "desktop": "npm --prefix desktop start",
41
+ "desktop:smoke": "npm --prefix desktop run selftest",
42
+ "dist": "npm --prefix desktop run dist",
43
+ "shortcut": "node tools/shortcut.mjs --dev"
44
+ },
45
+ "engines": {
46
+ "node": ">=20"
47
+ }
48
+ }
@@ -0,0 +1,343 @@
1
+ /**
2
+ * **import ํํฌ**(closure โ€” ์ง„์ž…์ ์—์„œ import ๋ฅผ ๋”ฐ๋ผ ๋๊นŒ์ง€ ๊ฐ”์„ ๋•Œ ๋‹ฟ๋Š” ํŒŒ์ผ ์ „๋ถ€)๋ฅผ
3
+ * ๊ตฌํ•˜๋Š” ์ˆœ์ˆ˜ ๊ณ„์‚ฐ.
4
+ *
5
+ * ํ•˜๋‚˜๋ผ๋„ ๋น ์ง€๋ฉด ์‹คํ–‰์ด ์•ˆ ๋˜๊ณ , ํ•˜๋‚˜๋ผ๋„ ๋‚จ์œผ๋ฉด ์•ˆ ์“ฐ๋Š” ํŒŒ์ผ์„ ๋‚จ์˜ ์ €์žฅ์†Œ์—
6
+ * ๋ณต์‚ฌํ•˜๊ฒŒ ๋œ๋‹ค. ๊ทธ๋ž˜์„œ **๋ชฉ๋ก์„ ์†์œผ๋กœ ์ ์ง€ ์•Š๊ณ  ์ฝ”๋“œ์—์„œ ๊ตฌํ•œ๋‹ค.**
7
+ *
8
+ * โ”€โ”€ ์™œ ์ด ํŒŒ์ผ์ด ๋”ฐ๋กœ ์žˆ๋Š”๊ฐ€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
9
+ *
10
+ * ์ด ๋ชฉ๋ก์„ ์“ฐ๋Š” ๊ณณ์ด ๋‘˜์ด๋‹ค.
11
+ *
12
+ * `tools/vendor.mjs` ๋ฒค๋”๋ง(vendoring โ€” ๋‚จ์˜ ์ฝ”๋“œ๋ฅผ ๋‚ด ์ €์žฅ์†Œ ์•ˆ์— ๋ณต์‚ฌํ•ด
13
+ * ๋‘๊ณ  ๊ทธ ์‚ฌ๋ณธ์œผ๋กœ ๋Œ๋ฆฌ๋Š” ๊ฒƒ) ํ•  ํŒŒ์ผ์„ ๊ณ ๋ฅธ๋‹ค
14
+ * `test/closure.test.mjs` ๊ทธ ๋ชฉ๋ก์ด ๋Š˜๊ฑฐ๋‚˜ ์ค„์ง€ ์•Š์•˜๋Š”์ง€ ๋ณธ๋‹ค
15
+ *
16
+ * ๐Ÿ”ด ๋‘ ๋ฒŒ๋กœ ์ ์œผ๋ฉด **์–ด๊ธ‹๋‚œ ์ˆœ๊ฐ„ ํ…Œ์ŠคํŠธ๊ฐ€ ํ†ต๊ณผํ•˜๋ฉด์„œ ์‚ฌ๋ณธ์ด ๊นจ์ง„๋‹ค.** ํ…Œ์ŠคํŠธ๊ฐ€
17
+ * ์ž๊ธฐ ๋ชฉ๋ก์„ ๋ณด๊ณ  ์ดˆ๋ก์„ ๋‚ด๋Š”๋ฐ ๋ฒค๋”๋Ÿฌ๋Š” ๋‹ค๋ฅธ ๋ชฉ๋ก์„ ๋ณต์‚ฌํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค.
18
+ * ํ•œ ํ•จ์ˆ˜๋ฅผ ๋‘˜์ด ๊ฐ€์ ธ๋‹ค ์“ด๋‹ค. `src/version.mjs` ๋ฅผ `tools/version.mjs` ์™€
19
+ * `src/mrtarget.mjs` ๊ฐ€ ํ•จ๊ป˜ ์“ฐ๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ด์œ ๋‹ค.
20
+ *
21
+ * ๐Ÿ”ด ์—ฌ๊ธฐ์—๋Š” ํŒŒ์ผ์‹œ์Šคํ…œ์ด ์—†๋‹ค. ์ฝ๊ธฐ๋Š” `readFile` ์ธ์ž๋กœ ๋ฐ›๋Š”๋‹ค.
22
+ * `src/governance.mjs` ๊ฐ€ git ์„ ์ธ์ž๋กœ ๋ฐ›์ง€ ์•Š๋Š” ๊ฒƒ๊ณผ ๊ฐ™์€ ์ด์œ  โ€”
23
+ * ํŒ์ •์„ ๊ฒ€์ฆํ•˜๋ ค๊ณ  ์ง„์งœ ์ €์žฅ์†Œ๋ฅผ ๋งŒ๋“ค ํ•„์š”๊ฐ€ ์—†์–ด์•ผ ํ•œ๋‹ค.
24
+ */
25
+
26
+ /**
27
+ * CI ๊ฐ€ ์ง์ ‘ ๋ถ€๋ฅด๋Š” ํŒŒ์ผ๋“ค. `.gitlab-ci.yml` ์˜ `script:` ์— ์ ํžŒ ๊ฒƒ๊ณผ ์Šน๊ฒฉ ๋ด‡์ด
28
+ * ์ž์‹ ํ”„๋กœ์„ธ์Šค๋กœ ๋„์šฐ๋Š” ๊ฒƒ์„ ํ•ฉ์นœ ์ž๋ฆฌ๋‹ค.
29
+ *
30
+ * ๐Ÿ”ด **์ด ๋ชฉ๋ก๋งŒ ์†์œผ๋กœ ์ ๋Š”๋‹ค.** ๋‚˜๋จธ์ง€๋Š” ์—ฌ๊ธฐ์„œ import ๋ฅผ ๋”ฐ๋ผ๊ฐ€ ๊ตฌํ•œ๋‹ค.
31
+ * ์ง„์ž…์ ์€ ์ฝ”๋“œ๊ฐ€ ์•Œ ์ˆ˜ ์—†๋‹ค โ€” ๋ฌด์—‡์„ CI ๊ฐ€ ๋ถ€๋ฅด๊ธฐ๋กœ ํ–ˆ๋Š”์ง€๋Š” ์‚ฌ๋žŒ์˜ ๊ฒฐ์ •์ด๋‹ค.
32
+ *
33
+ * `governance/vote.mjs` ๋Š” ์ง€๊ธˆ `.gitlab-ci.yml` ์— ์—†์ง€๋งŒ ๋„ฃ๋Š”๋‹ค. ์‚ฌ๋žŒ์ด ํ‘œ๋ฅผ
34
+ * ์ค„ ๋•Œ ๋ถ€๋ฅด๋Š” ์ž๋ฆฌ์ด๊ณ , ์‚ฌ๋ณธ์— ์—†์œผ๋ฉด ํŒ€ ์ €์žฅ์†Œ๋ฅผ clone ํ•œ ์‚ฌ๋žŒ์€ ํˆฌํ‘œ๋ฅผ ํ•  ์ˆ˜
35
+ * ์—†๋‹ค โ€” ํ‘œ๋ฅผ ์ €์žฅ์†Œ ์•ˆ์— ๋‘” ์ด์œ ๊ฐ€ ํ†ต์งธ๋กœ ์‚ฌ๋ผ์ง„๋‹ค.
36
+ *
37
+ * โ”€โ”€ ๐Ÿ”ด `bin/axmap.mjs` ์™€ `mcp/server.mjs` ๊ฐ€ ์—ฌ๊ธฐ ์žˆ๋Š” ์ด์œ  โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
38
+ *
39
+ * ์ด ๋‘˜์€ CI ๊ฐ€ ์•„๋‹ˆ๋ผ **์‚ฌ๋žŒ๊ณผ ์—์ด์ „ํŠธ๊ฐ€** ๋ถ€๋ฅธ๋‹ค. ๊ทธ๋Ÿฐ๋ฐ๋„ ๋„ฃ๋Š” ์ด์œ ๋Š” ํ•˜๋‚˜๋‹ค:
40
+ *
41
+ * **ํŒ€ ์ €์žฅ์†Œ๋งŒ clone ํ•œ ์‚ฌ๋žŒ์—๊ฒŒ๋„ ์„ ์ (claim)์ด ์žˆ์–ด์•ผ ํ•œ๋‹ค.**
42
+ *
43
+ * ์—†์œผ๋ฉด ์ถฉ๋Œ ๋ฐฉ์ง€๊ฐ€ "๊ฐ์ž axMap ์„ ๋”ฐ๋กœ ์„ค์น˜ํ–ˆ๋Š”๊ฐ€" ์— ๋‹ฌ๋ฆฌ๊ฒŒ ๋œ๋‹ค. ์•ˆ ํ•œ ์‚ฌ๋žŒ
44
+ * ํ•˜๋‚˜๊ฐ€ ๊ทธ๋‚ ์˜ ๋ชจ๋“  ๋ณดํ˜ธ๋ฅผ ๋ฌดํšจ๋กœ ๋งŒ๋“ค๊ณ , ํŒ€ ๊ทœ์น™์ด ํŒ€ ์ €์žฅ์†Œ ๋ฐ–์˜ ๋ฌด์–ธ๊ฐ€์—
45
+ * ์ธ์งˆ๋กœ ์žกํžŒ๋‹ค. ์žฅ๋ถ€(`axmap/claims`)๋Š” ํŒ€ ์ €์žฅ์†Œ์— ์žˆ๋Š”๋ฐ ๊ทธ๊ฒƒ์„ ์ฝ๊ณ  ์“ธ ๋„๊ตฌ๊ฐ€
46
+ * ์—†๋Š” ์ƒํƒœ๊ฐ€ ์ œ์ผ ์ด์ƒํ•˜๋‹ค โ€” ๋ฐ์ดํ„ฐ๋Š” ์žˆ๊ณ  ์—ด์‡ ๊ฐ€ ์—†๋Š” ๊ฒƒ์ด๋‹ค.
47
+ *
48
+ * bin/axmap.mjs claim ยท release ยท status ยท verify(ํ›…) ยท audit
49
+ * mcp/server.mjs AI ๋„๊ตฌ๊ฐ€ ๋ถ™๋Š” ์ฐฝ๊ตฌ. clone ๋งŒ์œผ๋กœ `ax_claim` ์ด ๋œฌ๋‹ค
50
+ * tools/bus.mjs ์—์ด์ „ํŠธ๋ผ๋ฆฌ ์ชฝ์ง€ (`ax_inbox` ยท `ax_send`)
51
+ *
52
+ * ๐Ÿ”ด `tools/bus.mjs` ๋ฅผ ๋†“์ณค๋‹ค๊ฐ€ ์‹ค์ œ๋กœ ๊นจ๋œจ๋ ธ๋‹ค. ์ด ํํฌ๋Š” **`import` ๋งŒ ๋”ฐ๋ผ๊ฐ„๋‹ค.**
53
+ * ๊ทธ๋Ÿฐ๋ฐ `mcp/server.mjs` ๋Š” bus ๋ฅผ import ํ•˜์ง€ ์•Š๊ณ  **์ž์‹ ํ”„๋กœ์„ธ์Šค๋กœ ๋„์šด๋‹ค**
54
+ * (`path.join(SELF, 'tools', 'bus.mjs')`). ๊ทธ๋ž˜์„œ ์‚ฌ๋ณธ์— ์•ˆ ๋“ค์–ด๊ฐ”๊ณ ,
55
+ * `ax_inbox` ๋ฅผ ๋ถ€๋ฅด๋ฉด "Cannot find module" ๋กœ ์ฃฝ์—ˆ๋‹ค.
56
+ *
57
+ * ๊ตํ›ˆ: **spawn ๋˜๋Š” ํŒŒ์ผ์€ import ๊ทธ๋ž˜ํ”„์— ์•ˆ ์žกํžŒ๋‹ค.** ์ง„์ž…์  ๋ชฉ๋ก์„ ์†์œผ๋กœ
58
+ * ์ ๋Š” ์ด์œ ๊ฐ€ ์ •ํ™•ํžˆ ์ด๊ฒƒ์ด๋‹ค โ€” ๋ฌด์—‡์ด ์‹คํ–‰๋˜๋Š”์ง€๋Š” ์ฝ”๋“œ๊ฐ€ ์•Œ ์ˆ˜ ์—†๋‹ค.
59
+ * ์ƒˆ ํŒŒ์ผ์„ spawn ํ•˜๊ฒŒ ๋˜๋ฉด ์—ฌ๊ธฐ์—๋„ ๋”ํ•ด์•ผ ํ•œ๋‹ค.
60
+ *
61
+ * ์ „์—ญ ์„ค์น˜(`~/.axmap/app`)๋ฅผ ๋Œ€์‹ ํ•˜๋Š” ๊ฒƒ์ด ์•„๋‹ˆ๋‹ค. ๋‘˜์€ ๊ฐ™์ด ์‚ฐ๋‹ค โ€” ํŒ€ ์ €์žฅ์†Œ
62
+ * ์•ˆ์—์„œ๋Š” `.mcp.json` ์ด ์ด ์‚ฌ๋ณธ์„ ๊ฐ€๋ฆฌํ‚ค๊ณ , ๋‹ค๋ฅธ ์ €์žฅ์†Œ์—์„œ๋Š” ์ „์—ญ์ด ๋ฐ›๋Š”๋‹ค.
63
+ *
64
+ * ๋Œ€๊ฐ€๋Š” ์ •์งํ•˜๊ฒŒ ์ ์–ด ๋‘”๋‹ค: **`bin/axmap.mjs` ๋Š” ์ด ์ €์žฅ์†Œ์—์„œ ์ œ์ผ ์ž์ฃผ ๋ฐ”๋€Œ๋Š”
65
+ * ํŒŒ์ผ์ด๋ผ, ์‚ฌ๋ณธ์„ ๋‹ค์‹œ ๋งŒ๋“ค ์ผ์ด ๊ทธ๋งŒํผ ์ž์ฃผ ์ƒ๊ธด๋‹ค.** ๊ทธ ๋น„์šฉ์„ ๋‚ด๊ณ  ์‚ฌ๋Š” ๊ฒƒ์ด
66
+ * "์„ค์น˜ ์•ˆ ํ•œ ์‚ฌ๋žŒ ๋•Œ๋ฌธ์— ๋ณดํ˜ธ๊ฐ€ ์‚ฌ๋ผ์ง€๋Š” ๊ฒƒ" ๋ณด๋‹ค ๋‚ซ๋‹ค๊ณ  ํŒ๋‹จํ–ˆ๋‹ค.
67
+ */
68
+ export const CI_ENTRYPOINTS = Object.freeze([
69
+ 'governance/gate.mjs',
70
+ 'governance/vote.mjs',
71
+ 'tools/mr-target.mjs',
72
+ 'tools/promote.mjs',
73
+ 'tools/version.mjs',
74
+ 'bin/axmap.mjs',
75
+ 'mcp/server.mjs',
76
+ 'tools/bus.mjs',
77
+ ])
78
+
79
+ /** ์Šฌ๋ž˜์‹œ ํ•˜๋‚˜๊ฐ€ `/` ๋กœ ์‹œ์ž‘ํ•˜๋ฉด ๋‚˜๋ˆ—์…ˆ์ด ์•„๋‹ˆ๋ผ ์ •๊ทœ์‹์ด ์˜ฌ ์ˆ˜ ์žˆ๋Š” ์ž๋ฆฌ๋‹ค. */
80
+ const REGEX_MAY_FOLLOW = new Set(
81
+ ['', '(', ',', '=', ':', '[', '!', '&', '|', '?', '{', '}', ';', '+', '-', '*', '%', '<', '>', '~', '^', '\n'],
82
+ )
83
+
84
+ /** ๊ฐ™์€ ํŒ๋‹จ์„ ๋‚ฑ๊ธ€์ž๊ฐ€ ์•„๋‹ˆ๋ผ ๋‚ฑ๋ง๋กœ ํ•ด์•ผ ํ•˜๋Š” ๊ฒฝ์šฐ (`return /re/` ์ฒ˜๋Ÿผ). */
85
+ const REGEX_MAY_FOLLOW_WORD = new Set(
86
+ ['return', 'typeof', 'case', 'in', 'of', 'delete', 'void', 'instanceof', 'new', 'do', 'else', 'yield', 'await'],
87
+ )
88
+
89
+ const isWordChar = (c) => /[A-Za-z0-9_$]/.test(c)
90
+
91
+ /**
92
+ * ์ฃผ์„์„ ์ง€์šด๋‹ค. ๋ฌธ์ž์—ดยทํ…œํ”Œ๋ฆฟยท์ •๊ทœ์‹ ๋ฆฌํ„ฐ๋Ÿด **์•ˆ**์€ ๊ฑด๋“œ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค.
93
+ *
94
+ * โ”€โ”€ ์™œ ์ง€์šฐ๊ณ  ์‹œ์ž‘ํ•˜๋Š”๊ฐ€ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
95
+ *
96
+ * ์ด ์ €์žฅ์†Œ์˜ ํŒŒ์ผ๋“ค์€ ๋จธ๋ฆฌ๋ง ์ฃผ์„์— ์‚ฌ์šฉ๋ฒ•์„ ์ ์–ด ๋‘”๋‹ค. ๊ฑฐ๊ธฐ์—
97
+ * `node axmap/governance/gate.mjs` ๊ฐ™์€ ์ค„์ด ๊ทธ๋Œ€๋กœ ์žˆ๊ณ , `import` ๋ผ๋Š” ๋‚ฑ๋ง๋„
98
+ * ์„ค๋ช…๋ฌธ์— ๋‚˜์˜จ๋‹ค. ์ฃผ์„์„ ๋‚จ๊ฒจ ๋‘๊ณ  ์ •๊ทœ์‹์„ ๋Œ๋ฆฌ๋ฉด **์ฃผ์„์— ์ ํžŒ ์˜ˆ์‹œ๋ฅผ ์ง„์งœ
99
+ * import ๋กœ ์„ธ์–ด** ์žˆ์ง€๋„ ์•Š์€ ํŒŒ์ผ์„ ๋ณต์‚ฌ ๋ชฉ๋ก์— ๋„ฃ๊ฒŒ ๋œ๋‹ค.
100
+ *
101
+ * ๐Ÿ”ด ๋ฐ˜๋Œ€๋กœ ๋ฌธ์ž์—ด์€ ์ง€์šฐ๋ฉด ์•ˆ ๋œ๋‹ค. ์šฐ๋ฆฌ๊ฐ€ ์ฐพ๋Š” ๊ฒƒ์ด ๋ฐ”๋กœ ๋ฌธ์ž์—ด์ด๋‹ค.
102
+ * ๊ทธ๋ž˜์„œ "์ฃผ์„๋งŒ" ์ง€์šด๋‹ค. ์ •๊ทœ์‹ ๋ฆฌํ„ฐ๋Ÿด์„ ๋”ฐ๋กœ ์•Œ์•„๋ณด๋Š” ์ด์œ ๋„ ํ•˜๋‚˜๋ฟ์ด๋‹ค โ€”
103
+ * `/[a-z]\/\//` ๊ฐ™์€ ๊ฒƒ์„ ์ฃผ์„ ์‹œ์ž‘์œผ๋กœ ์ž˜๋ชป ์ฝ์œผ๋ฉด ๊ทธ ๋’ค๊ฐ€ ํ†ต์งธ๋กœ ์‚ฌ๋ผ์ง„๋‹ค.
104
+ */
105
+ export function stripComments(source) {
106
+ const src = String(source ?? '')
107
+ let out = ''
108
+ let i = 0
109
+ let prev = '' // ์ง์ „์˜ ๊ณต๋ฐฑ ์•„๋‹Œ ์ฝ”๋“œ ๊ธ€์ž
110
+ let word = '' // ๊ทธ ๊ธ€์ž๋กœ ๋๋‚˜๋Š” ๋‚ฑ๋ง (์—†์œผ๋ฉด ๋นˆ ๋ฌธ์ž์—ด)
111
+
112
+ const regexMayFollow = () => REGEX_MAY_FOLLOW.has(prev) || REGEX_MAY_FOLLOW_WORD.has(word)
113
+ const advance = (c) => {
114
+ if (/\s/.test(c)) return // ๊ณต๋ฐฑ์€ ๋‚ฑ๋ง์„ ๋Š์ง€ ์•Š๋Š”๋‹ค. `return /re/` ์„ ์•Œ์•„๋ณด๋ ค๋ฉด ํ•„์š”ํ•˜๋‹ค
115
+ if (isWordChar(c)) word += c
116
+ else word = ''
117
+ prev = c
118
+ }
119
+
120
+ while (i < src.length) {
121
+ const c = src[i]
122
+ const d = src[i + 1]
123
+
124
+ if (c === '/' && d === '/') {
125
+ while (i < src.length && src[i] !== '\n') i++
126
+ continue
127
+ }
128
+
129
+ if (c === '/' && d === '*') {
130
+ i += 2
131
+ while (i < src.length && !(src[i] === '*' && src[i + 1] === '/')) i++
132
+ i += 2
133
+ out += ' ' // ์ฃผ์„์ด ๋ถ™์–ด ์žˆ๋˜ ๋‘ ํ† ํฐ์ด ํ•œ ๋‚ฑ๋ง๋กœ ๋ถ™์ง€ ์•Š๊ฒŒ ์ž๋ฆฌ๋ฅผ ๋‚จ๊ธด๋‹ค
134
+ prev = ''
135
+ word = ''
136
+ continue
137
+ }
138
+
139
+ if (c === '"' || c === "'" || c === '`') {
140
+ out += c
141
+ i++
142
+ while (i < src.length) {
143
+ if (src[i] === '\\') {
144
+ out += src[i] + (src[i + 1] ?? '')
145
+ i += 2
146
+ continue
147
+ }
148
+ const ch = src[i]
149
+ out += ch
150
+ i++
151
+ if (ch === c) break
152
+ }
153
+ prev = c
154
+ word = ''
155
+ continue
156
+ }
157
+
158
+ if (c === '/' && regexMayFollow()) {
159
+ out += c
160
+ i++
161
+ let inClass = false
162
+ while (i < src.length) {
163
+ const ch = src[i]
164
+ if (ch === '\\') {
165
+ out += ch + (src[i + 1] ?? '')
166
+ i += 2
167
+ continue
168
+ }
169
+ if (ch === '\n') break // ์ค„์„ ๋„˜๋Š” ์ •๊ทœ์‹ ๋ฆฌํ„ฐ๋Ÿด์€ ์—†๋‹ค. ์ž˜๋ชป ์งš์—ˆ๋‹ค๋Š” ๋œป์ด๋‹ˆ ๋ฉˆ์ถ˜๋‹ค
170
+ out += ch
171
+ i++
172
+ if (ch === '[') inClass = true
173
+ else if (ch === ']') inClass = false
174
+ else if (ch === '/' && !inClass) break
175
+ }
176
+ prev = '/'
177
+ word = ''
178
+ continue
179
+ }
180
+
181
+ out += c
182
+ advance(c)
183
+ i++
184
+ }
185
+
186
+ return out
187
+ }
188
+
189
+ /*
190
+ * import ๋ฅผ ์•Œ์•„๋ณด๋Š” ์„ธ ๊ฐ€์ง€ ๋ชจ์–‘.
191
+ *
192
+ * `[^'";()]*?` ๋กœ ๊ฐ€์šด๋ฐ๋ฅผ ๋ง‰์•„ ๋‘” ๊ฒƒ์€ ํ•œ ๋ฌธ์žฅ์„ ๋„˜์–ด๊ฐ€์ง€ ๋ชปํ•˜๊ฒŒ ํ•˜๊ธฐ ์œ„ํ•ด์„œ๋‹ค.
193
+ * ์ด๊ฑธ `[\s\S]*?` ๋กœ ๋‘๋ฉด ์•ž ์ค„์˜ `import` ์™€ ํ•œ์ฐธ ๋’ค์˜ `from` ์ด ์ง์ง€์–ด์ ธ ์—‰๋šฑํ•œ
194
+ * ๋ฌธ์ž์—ด์„ ๊ฒฝ๋กœ๋กœ ์ฝ๋Š”๋‹ค.
195
+ *
196
+ * ์•Œ๋ ค์ง„ ํ•œ๊ณ„: `export const from = './x.mjs'` ์ฒ˜๋Ÿผ `from` ์„ ๋ณ€์ˆ˜ ์ด๋ฆ„์œผ๋กœ ์“ฐ๋ฉด
197
+ * ๊ฑฐ์ง“ ์–‘์„ฑ์ด ๋‚œ๋‹ค. ๊ทธ๋•Œ๋Š” ๊ทธ ๊ฒฝ๋กœ๊ฐ€ bare ๋‚˜ ์—†๋Š” ํŒŒ์ผ๋กœ ์žกํ˜€ ํ…Œ์ŠคํŠธ๊ฐ€ ๋นจ๊ฐœ์ง„๋‹ค โ€”
198
+ * ์กฐ์šฉํžˆ ์ง€๋‚˜๊ฐ€์ง€๋Š” ์•Š๋Š”๋‹ค.
199
+ */
200
+ const RE_FROM = /(?:^|[\s;})])(?:import|export)\s[^'";()]*?\bfrom\s*(['"])([^'"]+)\1/gm
201
+ const RE_SIDE_EFFECT = /(?:^|[\s;})])import\s*(['"])([^'"]+)\1/gm
202
+ const RE_DYNAMIC = /\bimport\s*\(\s*(['"])([^'"]+)\1\s*\)/gm
203
+
204
+ /**
205
+ * ์†Œ์Šค ํ•œ ๋ฒŒ์—์„œ import ๋Œ€์ƒ ๋ฌธ์ž์—ด์„ ์ „๋ถ€ ๋ฝ‘๋Š”๋‹ค. ์ฃผ์„์€ ๋จผ์ € ์ง€์šด๋‹ค.
206
+ *
207
+ * `import.meta.url` ์€ ๊ฑธ๋ฆฌ์ง€ ์•Š๋Š”๋‹ค โ€” ์„ธ ์ •๊ทœ์‹ ๋ชจ๋‘ `import` ๋’ค์— ๊ณต๋ฐฑ์ด๋‚˜
208
+ * ์—ฌ๋Š” ๊ด„ํ˜ธ๋ฅผ ์š”๊ตฌํ•˜๊ณ , `.` ์€ ์–ด๋А ์ชฝ๋„ ์•„๋‹ˆ๋‹ค.
209
+ */
210
+ export function importSpecifiers(source) {
211
+ const code = stripComments(source)
212
+ const found = []
213
+ for (const re of [RE_FROM, RE_SIDE_EFFECT, RE_DYNAMIC]) {
214
+ re.lastIndex = 0
215
+ let m
216
+ while ((m = re.exec(code)) !== null) found.push(m[2])
217
+ }
218
+ return found
219
+ }
220
+
221
+ /**
222
+ * import ๋Œ€์ƒ์„ ์…‹์œผ๋กœ ๊ฐ€๋ฅธ๋‹ค.
223
+ *
224
+ * `builtin` `node:fs` ์ฒ˜๋Ÿผ Node ๊ฐ€ ๋“ค๊ณ  ์žˆ๋Š” ๊ฒƒ. ๋ณต์‚ฌํ•  ๊ฒƒ์ด ์—†๋‹ค
225
+ * `relative` `./x.mjs` ยท `../src/y.mjs`. ๋”ฐ๋ผ๊ฐ€์„œ ๊ฐ™์ด ๋ณต์‚ฌํ•ด์•ผ ํ•œ๋‹ค
226
+ * `bare` ๊ทธ ๋ฐ– ์ „๋ถ€. npm ํŒจํ‚ค์ง€์ด๊ฑฐ๋‚˜ `fs` ์ฒ˜๋Ÿผ `node:` ๋ฅผ ๋น ๋œจ๋ฆฐ ๊ฒƒ
227
+ *
228
+ * ๐Ÿ”ด `fs` ๋ฅผ `bare` ๋กœ ์„ธ๋Š” ๊ฒƒ์€ ์‹ค์ˆ˜๊ฐ€ ์•„๋‹ˆ๋‹ค. ์ ‘๋‘์‚ฌ ์—†๋Š” ๋‚ด์žฅ ์ด๋ฆ„์€ ๊ฐ™์€
229
+ * ์ด๋ฆ„์˜ npm ํŒจํ‚ค์ง€์— ๊ฐ€๋ ค์งˆ ์ˆ˜ ์žˆ๊ณ , ๊ทธ๋Ÿฌ๋ฉด ์–ด๋А ์ชฝ์ด ๋กœ๋“œ๋๋Š”์ง€ ์ฝ”๋“œ๋งŒ
230
+ * ๋ณด๊ณ ๋Š” ์•Œ ์ˆ˜ ์—†๋‹ค. ์ด ์ €์žฅ์†Œ๋Š” `node:` ๋ฅผ ๋ถ™์ด๊ธฐ๋กœ ํ–ˆ์œผ๋ฏ€๋กœ ์•ˆ ๋ถ™์€ ๊ฒƒ์€
231
+ * ๊ตฌ๋ถ„ ์—†์ด ๋นจ๊ฐ›๊ฒŒ ๋งŒ๋“ ๋‹ค.
232
+ */
233
+ export function classifySpecifier(spec) {
234
+ const s = String(spec ?? '')
235
+ if (s.startsWith('node:')) return 'builtin'
236
+ if (s === '.' || s === '..' || s.startsWith('./') || s.startsWith('../')) return 'relative'
237
+ return 'bare'
238
+ }
239
+
240
+ /** posix ๊ฒฝ๋กœ ๊ณ„์‚ฐ. `path` ๋ชจ๋“ˆ์„ ์•ˆ ์“ฐ๋Š” ์ด์œ ๋Š” Windows ์—์„œ `\` ๊ฐ€ ์„ž์ด๋ฉด ์•ˆ ๋˜์–ด์„œ๋‹ค. */
241
+ function posixDirname(rel) {
242
+ const i = rel.lastIndexOf('/')
243
+ return i < 0 ? '' : rel.slice(0, i)
244
+ }
245
+
246
+ /**
247
+ * `fromRel` ์•ˆ์˜ `spec` ์ด ๊ฐ€๋ฆฌํ‚ค๋Š” ํŒŒ์ผ์„ ๋ฃจํŠธ ๊ธฐ์ค€ ์ƒ๋Œ€ ๊ฒฝ๋กœ๋กœ ๋Œ๋ ค์ค€๋‹ค.
248
+ * ๋ฃจํŠธ ๋ฐ–์œผ๋กœ ๋‚˜๊ฐ€๋ฉด `null` โ€” ๋ณต์‚ฌํ•  ์ˆ˜ ์—†๋Š” ๊ฒƒ์ด๋ฏ€๋กœ ๋ถ€๋ฅด๋Š” ์ชฝ์ด ์•Œ์•„์•ผ ํ•œ๋‹ค.
249
+ */
250
+ export function resolveImport(fromRel, spec) {
251
+ const parts = `${posixDirname(String(fromRel))}/${String(spec)}`.split('/')
252
+ const stack = []
253
+ for (const p of parts) {
254
+ if (p === '' || p === '.') continue
255
+ if (p === '..') {
256
+ if (stack.length === 0) return null // ๋ฃจํŠธ๋ฅผ ๋šซ๊ณ  ์˜ฌ๋ผ๊ฐ”๋‹ค
257
+ stack.pop()
258
+ continue
259
+ }
260
+ stack.push(p)
261
+ }
262
+ return stack.length === 0 ? null : stack.join('/')
263
+ }
264
+
265
+ /**
266
+ * ์ง„์ž…์ ์—์„œ ์ถœ๋ฐœํ•ด ๋‹ฟ๋Š” ํŒŒ์ผ ์ „๋ถ€๋ฅผ ๋ชจ์€๋‹ค.
267
+ *
268
+ * @param {object} opts
269
+ * @param {string[]} [opts.entries] ๋ฃจํŠธ ๊ธฐ์ค€ ์ƒ๋Œ€ ๊ฒฝ๋กœ. ๊ธฐ๋ณธ์€ `CI_ENTRYPOINTS`
270
+ * @param {(rel: string) => (string|null)} opts.readFile
271
+ * ํŒŒ์ผ ๋‚ด์šฉ. ์—†์œผ๋ฉด `null` ์„ ์ค€๋‹ค (๋˜์ง€์ง€ ์•Š๋Š”๋‹ค โ€” ์—†๋Š” ํŒŒ์ผ๋„ ๊ฒฐ๊ณผ์˜ ์ผ๋ถ€๋‹ค)
272
+ * @returns {{files: string[], bare: {file: string, spec: string}[],
273
+ * missing: string[], outside: {file: string, spec: string}[], entries: string[]}}
274
+ *
275
+ * ๐Ÿ”ด ๋ชป ์ฝ์€ ํŒŒ์ผ์„ ์˜ˆ์™ธ๋กœ ํ„ฐ๋œจ๋ฆฌ์ง€ ์•Š๊ณ  `missing` ์— ๋‹ด๋Š” ์ด์œ : ๋ถ€๋ฅด๋Š” ์ชฝ์ด
276
+ * "๋ฌด์—‡์ด ์™œ ์•ˆ ๋๋Š”์ง€" ๋ฅผ ํ•œ ๋ฒˆ์— ๋ณด๊ณ ํ•ด์•ผ ํ•˜๊ธฐ ๋•Œ๋ฌธ์ด๋‹ค. ์ฒซ ๋ฒˆ์งธ์—์„œ ์ฃฝ์œผ๋ฉด
277
+ * ๋‘ ๋ฒˆ์งธ ๋ฌธ์ œ๋Š” ๋‹ค์Œ ์‹คํ–‰์—์„œ์•ผ ๋ณด์ธ๋‹ค.
278
+ */
279
+ export function importClosure({ entries = CI_ENTRYPOINTS, readFile } = {}) {
280
+ if (typeof readFile !== 'function') throw new TypeError('importClosure: readFile ํ•จ์ˆ˜๊ฐ€ ํ•„์š”ํ•ฉ๋‹ˆ๋‹ค')
281
+
282
+ const entryList = [...entries]
283
+ const files = new Set()
284
+ const bare = []
285
+ const missing = []
286
+ const outside = []
287
+ const seen = new Set()
288
+ const queue = [...entryList]
289
+
290
+ while (queue.length > 0) {
291
+ const rel = queue.shift()
292
+ if (seen.has(rel)) continue
293
+ seen.add(rel)
294
+
295
+ const source = readFile(rel)
296
+ if (source == null) {
297
+ missing.push(rel)
298
+ continue
299
+ }
300
+ files.add(rel)
301
+
302
+ for (const spec of importSpecifiers(source)) {
303
+ const kind = classifySpecifier(spec)
304
+ if (kind === 'builtin') continue
305
+ if (kind === 'bare') {
306
+ bare.push({ file: rel, spec })
307
+ continue
308
+ }
309
+ const next = resolveImport(rel, spec)
310
+ if (next === null) {
311
+ outside.push({ file: rel, spec })
312
+ continue
313
+ }
314
+ queue.push(next)
315
+ }
316
+ }
317
+
318
+ return { files: [...files].sort(), bare, missing, outside, entries: entryList }
319
+ }
320
+
321
+ /**
322
+ * ํํฌ๋ฅผ ๊ตฌํ•˜๋‹ค ๋งŒ๋‚œ ๋ฌธ์ œ๋ฅผ ์‚ฌ๋žŒ์ด ์ฝ๋Š” ์ค„๋กœ ๋ฐ”๊พผ๋‹ค. ๋ฌธ์ œ๊ฐ€ ์—†์œผ๋ฉด ๋นˆ ๋ฐฐ์—ด.
323
+ *
324
+ * ๋ฒค๋”๋Ÿฌ์™€ ํ…Œ์ŠคํŠธ๊ฐ€ ๊ฐ™์€ ๋ฌธ์žฅ์„ ๋‚ด์•ผ ํ•œ๋‹ค โ€” ํ•œ์ชฝ์—์„œ ๋ณธ ๋ฉ”์‹œ์ง€๋ฅผ ๋‹ค๋ฅธ ์ชฝ์—์„œ
325
+ * ๊ฒ€์ƒ‰ํ–ˆ์„ ๋•Œ ์•ˆ ๋‚˜์˜ค๋ฉด ๊ฐ™์€ ๋ฌธ์ œ์ธ์ง€ ์•Œ ์ˆ˜ ์—†๋‹ค.
326
+ */
327
+ export function formatClosureProblems(closure) {
328
+ const lines = []
329
+ for (const rel of closure.missing) {
330
+ lines.push(`์—†๋Š” ํŒŒ์ผ์„ import ํ•ฉ๋‹ˆ๋‹ค: ${rel}`)
331
+ }
332
+ for (const { file, spec } of closure.outside) {
333
+ lines.push(`๋ฃจํŠธ ๋ฐ–์„ import ํ•ฉ๋‹ˆ๋‹ค: ${file} โ†’ ${spec} (๋ณต์‚ฌํ•  ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค)`)
334
+ }
335
+ for (const { file, spec } of closure.bare) {
336
+ lines.push(
337
+ `Node ๋‚ด์žฅ์ด ์•„๋‹Œ ๊ฒƒ์„ import ํ•ฉ๋‹ˆ๋‹ค: ${file} โ†’ '${spec}'\n` +
338
+ ` ๋‚ด์žฅ์ด๋ผ๋ฉด 'node:${spec}' ์ฒ˜๋Ÿผ ์ ‘๋‘์‚ฌ๋ฅผ ๋ถ™์ด์„ธ์š”.\n` +
339
+ ` ํŒจํ‚ค์ง€๋ผ๋ฉด CI ์ง„์ž…์ ์—์„œ ๋‹ฟ๋Š” ์ž๋ฆฌ์— ๋‘˜ ์ˆ˜ ์—†์Šต๋‹ˆ๋‹ค โ€” ์‚ฌ๋ณธ์—๋Š” node_modules ๊ฐ€ ์—†์Šต๋‹ˆ๋‹ค.`,
340
+ )
341
+ }
342
+ return lines
343
+ }