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/mcp/install.sh ADDED
@@ -0,0 +1,339 @@
1
+ #!/usr/bin/env bash
2
+ #
3
+ # axMap MCP 전역 설치 (macOS / Linux) — 팀 저장소 밖의 사람도 이 한 줄로 붙는다.
4
+ #
5
+ # bash axmap/mcp/install.sh
6
+ #
7
+ # ── 무엇을 하나 ─────────────────────────────────────────────────────────────
8
+ #
9
+ # 1. node 20 이상이 있는지 본다
10
+ # 2. 이 PC 에서 쓸 이름을 정한다 (사람마다 달라야 한다)
11
+ # 3. axMap 을 ~/.axmap/app 으로 복사한다
12
+ # 4. node ~/.axmap/app/tools/mcp-register.mjs 로 이 PC 의 AI CLI 에 등록한다
13
+ # 5. CLI 마다 붙었는지 한 줄씩 찍고, 하나라도 실패하면 종료 코드 1 을 낸다
14
+ #
15
+ # ── 왜 홈 디렉터리에 복사하나 ───────────────────────────────────────────────
16
+ #
17
+ # 목표가 "아무 저장소에서나 붙는다" 이기 때문이다. 작업할 저장소 안에 axMap 파일을
18
+ # 하나도 남기지 않는다 — 남기면 남의 저장소에 우리 파일이 커밋되거나, 남의
19
+ # .gitignore 를 고치게 만든다. 프로그램은 홈에, 데이터(장부)는 대상 저장소에.
20
+ #
21
+ # git clone 이 아니라 복사인 이유: git 이 없어도 설치가 끝나야 한다. 여기까지 왔다는
22
+ # 것은 이미 axMap 파일을 어떤 방법으로든 손에 넣었다는 뜻이다.
23
+ #
24
+ # ── 🔴 AXMAP_REPO 는 어디에도 적지 않는다 ───────────────────────────────────
25
+ #
26
+ # MCP 서버는 도구를 부를 때마다 대상 저장소를 다시 정한다(axmap/docs/DECISIONS.md 의
27
+ # D19). 그 순서의 맨 위가 AXMAP_REPO 다. 전역 등록인데 여기에 한 폴더를 박아 두면
28
+ # 다른 저장소를 열어도 아무 오류 없이 엉뚱한 장부에 claim 이 쌓인다.
29
+ # 비워 두면 CLI 를 켠 폴더가 그대로 대상이 된다.
30
+ #
31
+ # ── 🔴 AXMAP_AGENT 는 반대로 반드시 적는다 ──────────────────────────────────
32
+ #
33
+ # tools/mcp-register.mjs 는 이름을 어디에도 적지 않는다. 그건 저장소에 커밋되는
34
+ # .mcp.json 에 이름이 들어가면 clone 한 모두가 한 사람이 되기 때문이고, 그 판단은
35
+ # 옳다. 여기는 사정이 다르다 — 우리가 쓰는 곳은 홈 디렉터리 아래의 개인 설정이라
36
+ # 커밋되지 않는다. 그리고 전역 설치는 팀 저장소가 아닌 폴더에서도 뜨는데, 그런
37
+ # 폴더에는 git config user.name 이 없을 수 있다. 그러면 서버가 이름을 못 정하고
38
+ # 그냥 죽는다(exit 1). 그래서 설치할 때 사람마다 다른 이름을 한 번 박아 둔다.
39
+ #
40
+ # ── 이 파일과 install.ps1 은 짝이다 ─────────────────────────────────────────
41
+ #
42
+ # 한쪽만 고치면 두 OS 의 사용자가 서로 다른 설정을 갖게 된다. 아래 세 개의 JS
43
+ # 조각은 install.ps1 의 것과 글자까지 같아야 한다 — 판단은 node 에 몰아넣고 셸은
44
+ # 부르기만 한다. (한 파일로 합치지 못한 이유: 이 작업은 파일 넷 안에서 끝나야 했다.)
45
+
46
+ set -u
47
+
48
+ SOURCE=''
49
+ DEST=''
50
+ NAME=''
51
+
52
+ while [ $# -gt 0 ]; do
53
+ case "$1" in
54
+ --source) SOURCE="${2:-}"; shift 2 ;;
55
+ --dest) DEST="${2:-}"; shift 2 ;;
56
+ --name) NAME="${2:-}"; shift 2 ;;
57
+ -h|--help)
58
+ echo "사용법: bash install.sh [--source <axmap 폴더>] [--dest <설치 위치>] [--name <내 이름>]"
59
+ exit 0 ;;
60
+ *) echo "모르는 인자: $1"; exit 1 ;;
61
+ esac
62
+ done
63
+
64
+ if [ -t 1 ]; then C_G=$'\033[32m'; C_Y=$'\033[33m'; C_R=$'\033[31m'; C_0=$'\033[0m'
65
+ else C_G=''; C_Y=''; C_R=''; C_0=''; fi
66
+
67
+ say() { printf '%s\n' "$*"; }
68
+ ok() { printf '%s OK %s%s\n' "$C_G" "$*" "$C_0"; }
69
+ hm() { printf '%s ~~ %s%s\n' "$C_Y" "$*" "$C_0"; }
70
+ bad() { printf '%s !! %s%s\n' "$C_R" "$*" "$C_0"; }
71
+ fail() { printf '\n%s중단: %s%s\n\n' "$C_R" "$*" "$C_0"; exit 1; }
72
+
73
+ # 이름이 쓸 수 있는 이름인지 — 저장소의 검사기를 그대로 부른다. 여기서 다시 짜면
74
+ # 설치기가 통과시킨 이름을 서버가 거부하는 일이 생긴다.
75
+ JS_NAME=$(cat <<'JSEOF'
76
+ const { pathToFileURL } = require('url')
77
+ import(pathToFileURL(process.argv[1]).href).then((m) => {
78
+ const err = m.agentNameError(process.argv[2])
79
+ if (err) { console.error(err); process.exit(1) }
80
+ })
81
+ JSEOF
82
+ )
83
+
84
+ # Claude Code 의 user 범위 등록(~/.claude.json)을 들여다본다.
85
+ # NONE 없다 / OTHER 다른 것이 들어 있다 / MATCH 원하는 그대로 / UNREADABLE 못 읽었다
86
+ JS_CLAUDE=$(cat <<'JSEOF'
87
+ const fs = require('fs'), path = require('path'), os = require('os')
88
+ const file = path.join(os.homedir(), '.claude.json')
89
+ const want = process.argv[1], name = process.argv[2]
90
+ let raw = null
91
+ try { raw = fs.readFileSync(file, 'utf8') } catch (e) { console.log('NONE'); process.exit(0) }
92
+ let cfg = null
93
+ try { cfg = JSON.parse(raw) } catch (e) { console.log('UNREADABLE'); process.exit(0) }
94
+ const s = cfg && cfg.mcpServers && cfg.mcpServers.axmap
95
+ if (!s) { console.log('NONE'); process.exit(0) }
96
+ const argsOk = String((s.args || [])[0]) === want
97
+ const nameOk = (s.env || {}).AXMAP_AGENT === name
98
+ console.log(argsOk && nameOk ? 'MATCH' : 'OTHER')
99
+ JSEOF
100
+ )
101
+
102
+ # Antigravity(agy) 의 전역 설정에 이름만 덧쓴다.
103
+ # 항목 자체는 mcp-register 가 만든다. 여기서는 AXMAP_AGENT 한 줄을 더할 뿐이다.
104
+ # 🔴 항목이 우리 서버를 가리킬 때만 손댄다. 남이 만든 axmap 항목을 덮어쓰지 않는다.
105
+ JS_AGY=$(cat <<'JSEOF'
106
+ const fs = require('fs'), path = require('path'), os = require('os')
107
+ const file = path.join(os.homedir(), '.gemini', 'config', 'mcp_config.json')
108
+ const want = process.argv[1], name = process.argv[2]
109
+ let raw = null
110
+ try { raw = fs.readFileSync(file, 'utf8') } catch (e) { console.log('NONE'); process.exit(0) }
111
+ let cfg = null
112
+ try { cfg = JSON.parse(raw) } catch (e) { console.log('UNREADABLE'); process.exit(0) }
113
+ const s = cfg && cfg.mcpServers && cfg.mcpServers.axmap
114
+ if (!s) { console.log('NONE'); process.exit(0) }
115
+ if (String((s.args || [])[0]) !== want) { console.log('OTHER'); process.exit(0) }
116
+ s.env = s.env || {}
117
+ if (s.env.AXMAP_AGENT === name) { console.log('MATCH'); process.exit(0) }
118
+ s.env.AXMAP_AGENT = name
119
+ try { fs.writeFileSync(file, JSON.stringify(cfg, null, 2) + '\n') }
120
+ catch (e) { console.log('WRITEFAIL'); process.exit(0) }
121
+ console.log('STAMPED')
122
+ JSEOF
123
+ )
124
+
125
+ say ''
126
+ say 'axMap MCP 전역 설치'
127
+ say ''
128
+
129
+ # ── 1. node ─────────────────────────────────────────────────────────────────
130
+ command -v node >/dev/null 2>&1 || fail 'node 를 찾을 수 없습니다. Node 20 이상을 설치한 뒤 다시 실행하세요.'
131
+ VER=$(node --version | sed 's/^v//')
132
+ MAJOR=${VER%%.*}
133
+ [ "$MAJOR" -ge 20 ] 2>/dev/null || fail "Node $VER 입니다. 20 이상이 필요합니다."
134
+ ok "node $VER"
135
+
136
+ # ── 2. 원본 ─────────────────────────────────────────────────────────────────
137
+ #
138
+ # 기본값은 이 스크립트가 들어 있는 axmap/ 폴더다. 저장소 루트를 --source 로 줘도
139
+ # 알아듣는다 — 사람이 둘을 자주 헷갈리는데, 여기서 틀리면 "파일이 없습니다" 만 보인다.
140
+ HERE=$(cd "$(dirname "$0")" && pwd)
141
+ [ -n "$SOURCE" ] || SOURCE=$(dirname "$HERE")
142
+ [ -d "$SOURCE" ] || fail "원본 폴더가 없습니다: $SOURCE"
143
+ SOURCE=$(cd "$SOURCE" && pwd)
144
+ if [ ! -f "$SOURCE/bin/axmap.mjs" ] && [ -f "$SOURCE/axmap/bin/axmap.mjs" ]; then
145
+ SOURCE=$(cd "$SOURCE/axmap" && pwd)
146
+ fi
147
+ for need in bin/axmap.mjs mcp/server.mjs src/protocol.mjs tools/mcp-register.mjs app/lib/agentcli.mjs package.json; do
148
+ if [ ! -e "$SOURCE/$need" ]; then
149
+ fail "axMap 원본이 온전하지 않습니다. 없는 파일: $need
150
+ 본 곳: $SOURCE
151
+
152
+ 이 스크립트만 따로 내려받으면 설치할 수 없습니다. 저장소를 통째로 받은 뒤
153
+ 그 안의 axmap/mcp/install.sh 를 실행하거나, --source 로 axmap/ 폴더를 지정하세요."
154
+ fi
155
+ done
156
+ ok "원본 $SOURCE"
157
+
158
+ # ── 3. 이름 ─────────────────────────────────────────────────────────────────
159
+ #
160
+ # [!] 여기서 모두에게 같은 기본값을 주면 안 된다. 장부에 한 명만 존재하게 되고,
161
+ # 겹침 판정은 자기 claim 을 겹침으로 보지 않으므로 팀 전원이 서로의 영역을
162
+ # 아무 경고 없이 덮어쓴다. 락이 조용히 여러 명에게 발급되는 것이고, 그게 이
163
+ # 도구가 막으려는 사고 그 자체다.
164
+ #
165
+ # 그래서 이 PC 에서 사람마다 이미 다른 값만 후보로 쓴다. 사람에게 묻지는
166
+ # 않는다 — 물어보는 순간 AI 에이전트가 대신 지어낸다.
167
+ #
168
+ # 🔴 이름은 이 스크립트를 켠 폴더의 git 설정에서 나온다. git 은 저장소 안이면
169
+ # 그 저장소의 user.name 을, 밖이면 전역 user.name 을 답한다. 그래서 앞으로
170
+ # 일할 저장소 안에서 실행하는 것이 맞다 — 그래야 MCP 가 쓰는 이름과, 같은
171
+ # 저장소에서 손으로 axmap 을 칠 때의 이름이 같아진다. 다르면 한 사람이 장부에서
172
+ # 둘이 되고, 자기가 잡은 것을 자기가 반납하지 못한다(release 종료 코드 5).
173
+ NAME_FROM='--name 인자'
174
+ if [ -z "$NAME" ] && command -v git >/dev/null 2>&1; then
175
+ G=$(git config user.name 2>/dev/null || true)
176
+ if [ -n "$G" ]; then NAME="$G"; NAME_FROM='git config user.name'; fi
177
+ fi
178
+ if [ -z "$NAME" ]; then
179
+ U=$(id -un 2>/dev/null || true)
180
+ if [ -n "$U" ]; then NAME="$U"; NAME_FROM='OS 사용자 이름'; fi
181
+ fi
182
+ if [ -z "$NAME" ]; then
183
+ H=$(hostname 2>/dev/null || true)
184
+ if [ -n "$H" ]; then NAME="$H"; NAME_FROM='호스트 이름'; fi
185
+ fi
186
+ [ -n "$NAME" ] || fail '이 PC 에서 쓸 이름을 정하지 못했습니다. --name "홍길동" 으로 직접 주세요.'
187
+
188
+ node -e "$JS_NAME" "$SOURCE/src/protocol.mjs" "$NAME" \
189
+ || fail "이 이름은 장부에서 쓸 수 없습니다: $NAME (--name 으로 다른 이름을 주세요)"
190
+ ok "이름 $NAME ($NAME_FROM)"
191
+
192
+ # ── 4. 복사 ─────────────────────────────────────────────────────────────────
193
+ #
194
+ # [!] 이미 설치돼 있어도 묻지 않고 갱신한다. 물어보는 설치기는 AI 에이전트가
195
+ # 실행할 수 없다 — 프롬프트 앞에서 멈춰 버린다. 대신 무엇을 갱신했는지 찍는다.
196
+ [ -n "$DEST" ] || DEST="$HOME/.axmap/app"
197
+ say ''
198
+ say "복사합니다 -> $DEST"
199
+ mkdir -p "$DEST" || fail "설치 폴더를 만들지 못했습니다: $DEST"
200
+
201
+ # desktop/ 은 뺀다. Electron 껍데기라 MCP 에 필요 없고 node_modules 가 수백 MB 다.
202
+ for item in bin src mcp tools app package.json LICENSE; do
203
+ from="$SOURCE/$item"
204
+ to="$DEST/$item"
205
+ if [ ! -e "$from" ]; then hm "$item 없음 — 건너뜁니다"; continue; fi
206
+
207
+ verb='새로'
208
+ if [ -e "$to" ]; then verb='갱신'; rm -rf "$to"; fi
209
+ cp -R "$from" "$to" || fail "복사하지 못했습니다: $item"
210
+
211
+ if [ -d "$to" ]; then
212
+ # 혹시 원본에 node_modules 가 딸려 있으면 여기서 뗀다. 크고, 쓰지도 않는다.
213
+ find "$to" -type d -name node_modules -prune -exec rm -rf {} + 2>/dev/null || true
214
+ n=$(find "$to" -type f | wc -l | tr -d ' ')
215
+ ok "$verb $item ($n 개 파일)"
216
+ else
217
+ ok "$verb $item"
218
+ fi
219
+ done
220
+
221
+ SERVER="$DEST/mcp/server.mjs"
222
+ [ -f "$SERVER" ] || fail "복사가 끝났는데 서버가 없습니다: $SERVER"
223
+
224
+ # ── 5. 등록 ─────────────────────────────────────────────────────────────────
225
+ #
226
+ # 이 PC 에 어떤 CLI 가 있는지 찾고 각자의 설정에 넣는 일은 tools/mcp-register.mjs 가
227
+ # 이미 한다. 여기서 다시 짜지 않는다.
228
+ say ''
229
+ node "$DEST/tools/mcp-register.mjs"
230
+ say ' (위에 claude 가 ".mcp.json 이 없습니다" 로 나왔다면, 그건 "저장소 안의'
231
+ say ' .mcp.json" 을 찾는 검사입니다. 전역 설치에는 그 파일이 없는 것이 정상이고,'
232
+ say ' Claude Code 는 바로 아래에서 따로 등록합니다.)'
233
+
234
+ # ── 6. CLI 마다 붙었는지 확인한다 ────────────────────────────────────────────
235
+ #
236
+ # 🔴 여기가 이 스크립트가 mcp-register 위에 얹는 유일한 층이다.
237
+ #
238
+ # mcp-register 의 안내는 if (!any) 라서 셋 중 하나라도 찾으면 아무 말이 없다.
239
+ # Claude Code 만 깔린 PC 에서 Codex 로 일하는 사람은 "OK claude" 만 보고 자기
240
+ # 도구는 안 붙은 채 넘어간다. 그래서 여기서 CLI 마다 한 줄씩 찍는다.
241
+ #
242
+ # 그리고 mcp-register 는 Claude Code 에 아무것도 등록하지 않는다 — 저장소의
243
+ # .mcp.json 이 알아서 발견되는 것에 기대기 때문이다. 전역 설치에는 그 파일이
244
+ # 없으므로 여기서 user 범위(이 PC 의 모든 폴더에 적용되는 개인 설정)로 직접 넣는다.
245
+ ST_CLAUDE=''; WHY_CLAUDE=''
246
+ ST_AGY=''; WHY_AGY=''
247
+ ST_CODEX=''; WHY_CODEX=''
248
+
249
+ if ! command -v claude >/dev/null 2>&1; then
250
+ ST_CLAUDE='skip'; WHY_CLAUDE='이 PC 에 claude 가 없습니다'
251
+ else
252
+ BEFORE=$(node -e "$JS_CLAUDE" "$SERVER" "$NAME")
253
+ if [ "$BEFORE" = 'UNREADABLE' ]; then
254
+ ST_CLAUDE='fail'; WHY_CLAUDE='~/.claude.json 을 읽지 못했습니다 — 건드리지 않았습니다'
255
+ elif [ "$BEFORE" = 'MATCH' ]; then
256
+ ST_CLAUDE='ok'; WHY_CLAUDE='이미 그대로 등록돼 있습니다 (user 범위)'
257
+ else
258
+ if [ "$BEFORE" = 'OTHER' ]; then claude mcp remove -s user axmap >/dev/null 2>&1 || true; fi
259
+ claude mcp add -s user axmap \
260
+ -e "AXMAP_AGENT=$NAME" -e 'AXMAP_ACTOR=agent' -e 'AXMAP_TTL=45m' \
261
+ -- node "$SERVER" >/dev/null 2>&1 || true
262
+ AFTER=$(node -e "$JS_CLAUDE" "$SERVER" "$NAME")
263
+ if [ "$AFTER" = 'MATCH' ]; then
264
+ ST_CLAUDE='ok'; WHY_CLAUDE='user 범위에 등록했습니다 (~/.claude.json)'
265
+ else
266
+ ST_CLAUDE='fail'; WHY_CLAUDE="claude mcp add 뒤에도 확인되지 않습니다 ($AFTER)"
267
+ fi
268
+ fi
269
+ fi
270
+
271
+ # Antigravity (agy) — 항목은 mcp-register 가 만들고, 이름만 여기서 덧쓴다
272
+ if ! command -v agy >/dev/null 2>&1; then
273
+ ST_AGY='skip'; WHY_AGY='이 PC 에 agy 가 없습니다'
274
+ else
275
+ R=$(node -e "$JS_AGY" "$SERVER" "$NAME")
276
+ case "$R" in
277
+ STAMPED|MATCH) ST_AGY='ok'; WHY_AGY='~/.gemini/config/mcp_config.json (전역) — 이름까지 적었습니다' ;;
278
+ NONE) ST_AGY='fail'; WHY_AGY='mcp_config.json 에 axmap 항목이 없습니다 — 위 mcp-register 줄을 보세요' ;;
279
+ OTHER) ST_AGY='fail'; WHY_AGY='axmap 항목이 다른 서버를 가리킵니다 — 건드리지 않았습니다' ;;
280
+ WRITEFAIL) ST_AGY='fail'; WHY_AGY='mcp_config.json 에 쓰지 못했습니다 (권한 또는 잠금)' ;;
281
+ *) ST_AGY='fail'; WHY_AGY='mcp_config.json 을 읽지 못했습니다 — 건드리지 않았습니다' ;;
282
+ esac
283
+ fi
284
+
285
+ # Codex — 등록은 mcp-register 가 codex 자신의 명령으로 한다. 여기서는 확인만 한다.
286
+ #
287
+ # 🔴 이름(AXMAP_AGENT)은 codex 설정에 넣지 못한다. mcp-register 가 codex mcp add 를
288
+ # 부를 때 이름을 빼고 부르고, 이미 등록돼 있으면 다시 부르지도 않기 때문이다.
289
+ # 확인 안 된 도구(이 팀에 codex 가 있는 PC 가 아직 없다)의 설정을 우리가 추측으로
290
+ # 덮어쓰는 것보다, 한 줄을 손으로 넣게 하는 편이 낫다.
291
+ if ! command -v codex >/dev/null 2>&1; then
292
+ ST_CODEX='skip'; WHY_CODEX='이 PC 에 codex 가 없습니다'
293
+ else
294
+ if codex mcp list 2>/dev/null | grep -q axmap; then
295
+ ST_CODEX='ok'
296
+ WHY_CODEX="~/.codex/config.toml (codex mcp list 로 확인). 이름은 손으로 넣으세요: [mcp_servers.axmap.env] 아래에 AXMAP_AGENT = \"$NAME\""
297
+ else
298
+ ST_CODEX='fail'; WHY_CODEX='codex mcp list 에 axmap 이 없습니다 — 위 TOML 조각을 손으로 넣으세요'
299
+ fi
300
+ fi
301
+
302
+ # ── 7. 보고 ─────────────────────────────────────────────────────────────────
303
+ #
304
+ # [!] "설치했습니다" 한 줄로 끝내지 않는다. 이 도구의 실패는 대부분 조용해서,
305
+ # 오류가 안 났다는 것이 붙었다는 뜻이 아니다. CLI 마다 한 줄씩 찍는다.
306
+ line() {
307
+ case "$2" in
308
+ ok) ok "$(printf '%-8s' "$1")$3" ;;
309
+ skip) hm "$(printf '%-8s' "$1")$3 (건너뜀 — 실패가 아닙니다)" ;;
310
+ *) bad "$(printf '%-8s' "$1")$3" ;;
311
+ esac
312
+ }
313
+
314
+ say ''
315
+ say '등록 결과'
316
+ line 'claude' "$ST_CLAUDE" "$WHY_CLAUDE"
317
+ line 'agy' "$ST_AGY" "$WHY_AGY"
318
+ line 'codex' "$ST_CODEX" "$WHY_CODEX"
319
+
320
+ say ''
321
+ say "설치 위치: $DEST"
322
+ say "이 PC 의 이름: $NAME ($NAME_FROM)"
323
+ say ''
324
+
325
+ if [ "$ST_CLAUDE" = 'fail' ] || [ "$ST_AGY" = 'fail' ] || [ "$ST_CODEX" = 'fail' ]; then
326
+ say '🔴 위의 !! 줄이 남아 있습니다. 그 CLI 에서는 axMap 이 뜨지 않습니다.'
327
+ say ''
328
+ exit 1
329
+ fi
330
+
331
+ if [ "$ST_CLAUDE" != 'ok' ] && [ "$ST_AGY" != 'ok' ] && [ "$ST_CODEX" != 'ok' ]; then
332
+ say '🔴 이 PC 에서 AI CLI 를 하나도 찾지 못했습니다. 설치한 뒤 이 스크립트를 다시 실행하세요.'
333
+ say ''
334
+ exit 1
335
+ fi
336
+
337
+ say '다음: AI CLI 를 완전히 껐다가 다시 켠 뒤, 작업할 저장소에서 "ax_status 로 지금 누가 뭘 잡고 있는지 보여줘" 라고 시켜 보세요.'
338
+ say ''
339
+ exit 0