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
@@ -0,0 +1,126 @@
1
+ /**
2
+ * "여기에 새로 하나 만들려면, 그 밖에 어디를 고쳐야 하나"
3
+ *
4
+ * 🔴 온보딩 실험에서 도구가 답하지 못한 유일한 질문이 이것이었다.
5
+ *
6
+ * 맥락 없는 에이전트에게 theHarvester 를 이해시켰더니 M1(무엇을 하나)·M2(진입점)·
7
+ * M5(위험한 곳)는 전부 맞혔는데, **"새 데이터 소스를 추가하려면 어디를 고치나"** 에서
8
+ * 이렇게 적었다:
9
+ *
10
+ * > 도구는 "새 소스를 어디에 등록하는지" 를 명시적 문장으로 말해준 적은 없다 —
11
+ * > 이건 숨은 결합 점수들을 조합해서 내가 추론한 것이다.
12
+ *
13
+ * 신입이 실제로 막히는 자리가 정확히 거기다. 무엇을 하는 물건인지는 README 가
14
+ * 말해주고, 위험한 곳은 커밋 수가 말해준다. 그런데 **"내가 뭘 건드려야 하나"** 는
15
+ * 아무도 안 알려준다. 코드를 다 읽은 사람만 안다.
16
+ *
17
+ * ## 답은 이미 히스토리에 있다
18
+ *
19
+ * 누군가 전에 같은 일을 했다. `discovery/` 에 새 파일을 만든 커밋들이 있고,
20
+ * 그 커밋들이 **함께 고친 기존 파일**이 곧 등록 지점이다.
21
+ *
22
+ * discovery/ 에 새 파일을 추가한 커밋 23건
23
+ * lib/core.py 19건 (83%) ← 여기 등록한다
24
+ * __main__.py 17건 (74%) ← CLI 에 붙인다
25
+ * discovery/constants.py 11건 (48%)
26
+ *
27
+ * 추측이 아니라 관측이다. 그리고 **언어를 타지 않는다** — 파서가 못 읽는 루비에서도
28
+ * 히스토리는 똑같이 읽힌다. 코퍼스가 히스토리 기반 지표를 모든 언어에 쓰는 것과 같은 이유다.
29
+ *
30
+ * ## 여기는 순수 함수만 둔다
31
+ *
32
+ * git 호출은 server.mjs 가 한다. 그래야 커밋 목록을 손으로 만들어 경계를 검증할 수 있다.
33
+ */
34
+
35
+ /** 이 수보다 적은 커밋으로는 답하지 않는다. 두세 번은 우연이다. */
36
+ export const MIN_COMMITS = 5
37
+
38
+ /** 이 비율보다 드물게 나오는 파일은 등록 지점이라고 부르지 않는다. */
39
+ export const MIN_SHARE = 0.3
40
+
41
+ /**
42
+ * 새 파일을 추가한 커밋들에서 함께 고쳐진 기존 파일을 찾는다.
43
+ *
44
+ * @param {Array<{sha: string, added: string[], touched: string[]}>} commits
45
+ * `added` 는 이 커밋이 대상 범위 안에 **새로 만든** 파일,
46
+ * `touched` 는 그 커밋이 건드린 **모든** 파일.
47
+ * @param {object} opts
48
+ * minCommits 표본 하한 (기본 MIN_COMMITS)
49
+ * minShare 등록 지점으로 부를 최소 비율 (기본 MIN_SHARE)
50
+ * limit 최대 몇 개까지 낼지
51
+ * @returns {{answered: boolean, n: number, points: Array<{path,support,share}>,
52
+ * why: string|null, considered: number}}
53
+ */
54
+ export function registrationPoints(commits, { minCommits = MIN_COMMITS, minShare = MIN_SHARE, limit = 8 } = {}) {
55
+ const usable = (commits ?? []).filter((c) => c && c.added?.length && c.touched?.length)
56
+
57
+ if (usable.length < minCommits) {
58
+ return {
59
+ answered: false,
60
+ n: usable.length,
61
+ points: [],
62
+ considered: 0,
63
+ // 🔴 모른다고 말한다. 한 번 거짓말한 지침은 그 다음부터 전부 무시당한다.
64
+ why: usable.length === 0
65
+ ? '이 범위에 파일이 새로 추가된 적이 없습니다'
66
+ : `새 파일을 추가한 커밋이 ${usable.length}건뿐입니다 (하한 ${minCommits})`,
67
+ }
68
+ }
69
+
70
+ const support = new Map()
71
+ for (const c of usable) {
72
+ // 🔴 그 커밋이 새로 만든 파일 자신은 제외한다.
73
+ // 안 그러면 "새 파일을 만들려면 새 파일을 만들어야 합니다" 가 1위로 나온다.
74
+ const born = new Set(c.added)
75
+ // 같은 커밋에 같은 파일이 두 번 세어지지 않게 한 번 접는다.
76
+ for (const p of new Set(c.touched)) {
77
+ if (born.has(p)) continue
78
+ support.set(p, (support.get(p) ?? 0) + 1)
79
+ }
80
+ }
81
+
82
+ const points = [...support]
83
+ .map(([path, s]) => ({ path, support: s, share: s / usable.length }))
84
+ .filter((p) => p.share >= minShare)
85
+ // 같은 비율이면 경로 이름으로 갈라 답을 결정론적으로 만든다.
86
+ .sort((a, b) => b.support - a.support || (a.path < b.path ? -1 : 1))
87
+ .slice(0, limit)
88
+
89
+ return {
90
+ answered: points.length > 0,
91
+ n: usable.length,
92
+ considered: support.size,
93
+ points,
94
+ why: points.length
95
+ ? null
96
+ : `${usable.length}건을 봤지만 ${Math.round(minShare * 100)}% 이상 함께 바뀐 파일이 없습니다`
97
+ + ' — 추가할 때 정해진 등록 지점이 없는 구조일 수 있습니다',
98
+ }
99
+ }
100
+
101
+ /**
102
+ * 경로가 대상 범위 안인가.
103
+ *
104
+ * 디렉터리는 `/` 로 끝나는 접두사로 본다. `discovery` 가 `discovery_old/` 를
105
+ * 삼키면 안 되므로 경계를 명시한다.
106
+ */
107
+ export function inScope(path, scope) {
108
+ if (!scope) return true
109
+ const s = scope.endsWith('/') ? scope : `${scope}/`
110
+ return path === scope || path.startsWith(s)
111
+ }
112
+
113
+ /**
114
+ * 한 문장으로 말한다.
115
+ *
116
+ * 🔴 숫자만 주면 사람은 그것을 해석하지 못한다. 온보딩 실험에서 에이전트가
117
+ * "숨은 결합 5 n=22" 의 5 가 뭔지 모르겠다고 한 것이 같은 문제였다.
118
+ * 그래서 여기서 만드는 문장에는 **무엇을 몇 건 중 몇 건 봤는지**가 들어간다.
119
+ */
120
+ export function say(result, scope) {
121
+ const where = scope ? `${scope} 에` : '이 저장소에'
122
+ if (!result.answered) return `${where} 새로 만들 때 어디를 고치는지 아직 말할 수 없습니다 — ${result.why}`
123
+ const top = result.points[0]
124
+ return `${where} 새 파일을 추가한 커밋 ${result.n}건 중 ${top.support}건(${Math.round(top.share * 100)}%)이`
125
+ + ` ${top.path} 를 함께 고쳤습니다`
126
+ }