axmap-cli 1.2.5 → 1.3.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.
- package/.claude/commands/ax-setup.md +9 -12
- package/.claude/commands/ax-start.md +2 -1
- package/CLAUDE.md +20 -73
- package/README.ko.md +122 -0
- package/README.md +79 -174
- package/app/lib/ssot.mjs +1 -1
- package/bin/axmap.mjs +62 -33
- package/governance/GOVERNANCE.md +7 -668
- package/governance/policy.example.json +31 -0
- package/mcp/README.md +16 -63
- package/mcp/SETUP-FOR-AI.md +13 -175
- package/mcp/install.ps1 +2 -1
- package/mcp/install.sh +2 -2
- package/mcp/server.mjs +123 -100
- package/package.json +6 -2
- package/src/overlap.mjs +1 -1
- package/src/repolock.mjs +31 -0
- package/src/repotarget.mjs +3 -0
- package/tools/mcp-register.mjs +44 -14
- package/tools/setup.mjs +29 -66
- package/tools/smoke.mjs +9 -1
package/tools/setup.mjs
CHANGED
|
@@ -1,43 +1,9 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
7
|
-
* npx -y axmap-cli@latest setup --remote https://lab.ssafy.com/우리팀/우리저장소.git
|
|
8
|
-
*
|
|
9
|
-
* ── 이 파일이 메우는 구멍 ───────────────────────────────────────────────────
|
|
10
|
-
*
|
|
11
|
-
* `mcp/install.sh` · `mcp/install.ps1` 은 이미 "홈에 복사하고 이 PC 의 AI CLI 에
|
|
12
|
-
* 등록한다" 를 한다. 다만 **axMap 파일이 이미 손에 있어야** 시작할 수 있다 —
|
|
13
|
-
* 스크립트만 따로 내려받으면 대놓고 거절한다. 그래서 지금까지 설치의 첫 단계는
|
|
14
|
-
* 언제나 "저장소를 통째로 clone" 이었다.
|
|
15
|
-
*
|
|
16
|
-
* npm 꾸러미가 그 첫 단계를 대신한다. `npx` 가 꾸러미를 받아 풀어 주므로,
|
|
17
|
-
* 이 파일이 도는 시점에는 axMap 원본이 이미 옆에 있다. 여기서는 그 뒤를 잇는다.
|
|
18
|
-
*
|
|
19
|
-
* 1. 이 PC 에 프로그램을 둔다 → mcp/install.{sh,ps1} 에 맡긴다 (다시 안 짠다)
|
|
20
|
-
* 2. 이 PC 의 AI CLI 에 등록한다 → 위와 같음
|
|
21
|
-
* 3. 대상 저장소에 장부를 만든다 → axmap init
|
|
22
|
-
* 4. 커밋 훅을 심는다 → axmap hook install
|
|
23
|
-
* 5. 슬래시 명령을 놓는다 → ~/.claude/commands/ (또는 저장소 안)
|
|
24
|
-
*
|
|
25
|
-
* ── 🔴 git 을 떼어내지 않는다 ───────────────────────────────────────────────
|
|
26
|
-
*
|
|
27
|
-
* 없앤 것은 **"axMap 코드를 받는 수단으로서의 git"**(clone·벤더링·git pull)이지
|
|
28
|
-
* **"협업 백엔드로서의 git"** 이 아니다. 장부와 쪽지는 그대로 고아 브랜치
|
|
29
|
-
* (`axmap/claims` · `axmap/bus`)로 오간다. git push 의 ref CAS(Compare-And-Swap —
|
|
30
|
-
* "내가 읽은 뒤로 바뀐 게 없을 때만 쓴다") 가 직렬화를 공짜로 주기 때문이고,
|
|
31
|
-
* 그것을 버리면 락 서버를 따로 세워야 한다. 그건 다른 제품이다.
|
|
32
|
-
*
|
|
33
|
-
* 그래서 대상이 git 저장소가 아니면 **여기서 멈춘다.** 반쯤 되는 상태로 두면
|
|
34
|
-
* claim 이 아무에게도 안 가면서 조용히 성공한다 — 이 도구에서 제일 나쁜 실패다.
|
|
35
|
-
*
|
|
36
|
-
* ── 종료 코드 ───────────────────────────────────────────────────────────────
|
|
37
|
-
*
|
|
38
|
-
* 0 다 됐다
|
|
39
|
-
* 1 시작할 수 없다 (node 가 낮다 · git 저장소가 아니다 · 이름을 못 정했다)
|
|
40
|
-
* 2 일부가 안 됐다. 무엇이 안 됐는지 !! 줄에 있다
|
|
3
|
+
* Install axMap globally from any directory. Installation is machine state;
|
|
4
|
+
* repository ledgers are prepared at runtime in the caller's working repo.
|
|
5
|
+
* Only explicit project options (--repo / --remote / --commands project)
|
|
6
|
+
* select a repository during setup. Commit enforcement remains opt-in.
|
|
41
7
|
*/
|
|
42
8
|
|
|
43
9
|
import { spawnSync } from 'node:child_process'
|
|
@@ -84,21 +50,21 @@ const has = (n) => argv.includes(n)
|
|
|
84
50
|
|
|
85
51
|
if (has('-h') || has('--help')) {
|
|
86
52
|
say(`
|
|
87
|
-
axmap setup —
|
|
53
|
+
axmap setup — 어느 폴더에서든 이 PC에 axMap을 전역 설치·등록한다
|
|
88
54
|
|
|
89
55
|
npx -y axmap-cli@latest setup
|
|
90
56
|
npx -y axmap-cli@latest setup --remote <이름 또는 git 주소>
|
|
91
57
|
|
|
92
|
-
--repo <경로>
|
|
58
|
+
--repo <경로> 선택 사항: 이 저장소의 장부·커밋 훅도 명시적으로 준비한다
|
|
93
59
|
--remote <이름|URL> 장부를 둘 원격. git 주소를 주면, 그 주소를 가리키는 원격이
|
|
94
60
|
없을 때 'axmap' 이라는 이름으로 하나 더한다
|
|
95
|
-
--name <이름>
|
|
61
|
+
--name <이름> 내 이름 (기본: 전역 git config user.name, 명시적 repo는 로컬 값)
|
|
96
62
|
--dest <경로> 프로그램을 둘 자리 (기본: ~/.axmap/app)
|
|
97
63
|
--commands <곳> 슬래시 명령을 둘 자리: global(기본) · project · none
|
|
98
64
|
--hook 프롬프트마다 쪽지를 받는 하네스 훅을 settings.json 에 더한다
|
|
99
65
|
(기본은 안 더한다 — 매 턴 도는 명령이라 사람이 고를 일이다)
|
|
100
66
|
--no-register 이 PC 의 AI CLI 등록을 건너뛴다
|
|
101
|
-
--no-ledger
|
|
67
|
+
--no-ledger --repo를 지정했어도 장부·커밋 훅을 건드리지 않는다
|
|
102
68
|
--dry-run 아무것도 쓰지 않고 무엇을 할지만 찍는다
|
|
103
69
|
|
|
104
70
|
종료 코드: 0 성공 · 1 시작 불가 · 2 일부 실패
|
|
@@ -132,24 +98,19 @@ ok('node', `v${process.versions.node}`)
|
|
|
132
98
|
|
|
133
99
|
// ── 2. 대상 저장소 ──────────────────────────────────────────────────────────
|
|
134
100
|
//
|
|
135
|
-
//
|
|
136
|
-
const
|
|
137
|
-
if (!
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
` 먼저 작업할 저장소 안으로 들어간 뒤 다시 실행하세요:\n` +
|
|
148
|
-
` cd <저장소> && npx -y axmap-cli@latest setup`,
|
|
149
|
-
)
|
|
101
|
+
// Installation has no working repository. Only explicit project options use one.
|
|
102
|
+
const projectRequested = has('--repo') || has('--remote') || commandsWhere === 'project'
|
|
103
|
+
if (has('--repo') && !flag('--repo')) die('--repo 뒤에 저장소 경로를 지정하세요.')
|
|
104
|
+
let REPO = null
|
|
105
|
+
if (projectRequested) {
|
|
106
|
+
const where = path.resolve(flag('--repo') ?? process.cwd())
|
|
107
|
+
const top = git(['rev-parse', '--show-toplevel'], where)
|
|
108
|
+
if (top.code !== 0) die(`프로젝트 옵션의 대상이 Git 저장소가 아닙니다: ${where}\n전역 설치만 하려면 --repo, --remote, --commands project 없이 axmap setup을 실행하세요.`)
|
|
109
|
+
REPO = path.resolve(top.out)
|
|
110
|
+
ok('저장소', `${REPO} (명시적 프로젝트 설정)`)
|
|
111
|
+
} else {
|
|
112
|
+
ok('설치 범위', '이 PC 전체 — 작업 repo는 이후 CLI·MCP 실행 위치에서 결정합니다')
|
|
150
113
|
}
|
|
151
|
-
const REPO = path.resolve(top.out)
|
|
152
|
-
ok('저장소', REPO)
|
|
153
114
|
|
|
154
115
|
// ── 3. 이름 ─────────────────────────────────────────────────────────────────
|
|
155
116
|
//
|
|
@@ -159,8 +120,8 @@ ok('저장소', REPO)
|
|
|
159
120
|
let name = flag('--name')
|
|
160
121
|
let nameFrom = '--name'
|
|
161
122
|
if (!name) {
|
|
162
|
-
const g = git(['config', 'user.name'], REPO)
|
|
163
|
-
if (g.code === 0 && g.out) { name = g.out; nameFrom = 'git config user.name' }
|
|
123
|
+
const g = git(REPO ? ['config', 'user.name'] : ['config', '--global', 'user.name'], REPO ?? os.homedir())
|
|
124
|
+
if (g.code === 0 && g.out) { name = g.out; nameFrom = REPO ? 'git config user.name' : 'git config --global user.name' }
|
|
164
125
|
}
|
|
165
126
|
if (!name) {
|
|
166
127
|
try { name = os.userInfo().username; nameFrom = 'OS 사용자 이름' } catch { /* 아래 */ }
|
|
@@ -230,7 +191,7 @@ function configureRemote(spec) {
|
|
|
230
191
|
else ok('장부 원격', `git config axmap.remote = ${picked}`)
|
|
231
192
|
}
|
|
232
193
|
|
|
233
|
-
configureRemote(flag('--remote'))
|
|
194
|
+
if (REPO) configureRemote(flag('--remote'))
|
|
234
195
|
|
|
235
196
|
// ── 5. 프로그램을 이 PC 에 둔다 + AI CLI 에 등록한다 ────────────────────────
|
|
236
197
|
//
|
|
@@ -250,8 +211,8 @@ function installAndRegister() {
|
|
|
250
211
|
|
|
251
212
|
say('')
|
|
252
213
|
const r = win
|
|
253
|
-
? spawnSync('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', script, '-Source', SOURCE_ROOT, '-Dest', DEST, '-Name', name], { stdio: 'inherit', windowsHide: true })
|
|
254
|
-
: spawnSync('bash', [script, '--source', SOURCE_ROOT, '--dest', DEST, '--name', name], { stdio: 'inherit' })
|
|
214
|
+
? spawnSync('powershell', ['-NoProfile', '-ExecutionPolicy', 'Bypass', '-File', script, '-Source', SOURCE_ROOT, '-Dest', DEST, '-Name', name], { cwd: os.homedir(), stdio: 'inherit', windowsHide: true })
|
|
215
|
+
: spawnSync('bash', [script, '--source', SOURCE_ROOT, '--dest', DEST, '--name', name], { cwd: os.homedir(), stdio: 'inherit' })
|
|
255
216
|
say('')
|
|
256
217
|
|
|
257
218
|
if (r.status === 0) ok('설치·등록', DEST)
|
|
@@ -266,6 +227,7 @@ installAndRegister()
|
|
|
266
227
|
// 부르면 `hook install` 이 그 임시 경로를 훅에 박고, npx 캐시가 비워지는 순간
|
|
267
228
|
// 훅이 없는 파일을 가리킨다 — 커밋할 때마다 오류가 나는데 원인은 안 보인다.
|
|
268
229
|
function ledgerAndHook() {
|
|
230
|
+
if (!REPO) { hm('장부·커밋 훅', '작업 repo의 장부는 첫 사용 때 자동 준비합니다. 커밋 훅은 axmap hook install로 선택합니다.'); return }
|
|
269
231
|
if (has('--no-ledger')) { hm('장부', '--no-ledger — 건너뜁니다'); return }
|
|
270
232
|
|
|
271
233
|
const installed = path.join(DEST, 'bin', 'axmap.mjs')
|
|
@@ -482,9 +444,10 @@ say('설치가 끝났습니다.')
|
|
|
482
444
|
say('')
|
|
483
445
|
say('다음:')
|
|
484
446
|
say(' 1. AI CLI 를 완전히 껐다가 다시 켭니다')
|
|
485
|
-
say(
|
|
447
|
+
say(' 2. 원하는 Git repo에서 AI CLI를 실행합니다. 실행한 repo에 자동으로 연결됩니다.')
|
|
448
|
+
say(' 3. "ax_status로 지금 누가 뭘 잡고 있는지 보여줘"로 시작합니다. repo마다 setup이나 init을 반복하지 않습니다.')
|
|
486
449
|
say('')
|
|
487
450
|
say(`${C.d} 파일을 고치기 전에 claim → 끝나면 release. 그게 전부입니다.${C.x}`)
|
|
488
|
-
say(`${C.d}
|
|
451
|
+
say(`${C.d} 조회: 원하는 Git repo에서 axmap status${C.x}`)
|
|
489
452
|
say('')
|
|
490
453
|
process.exit(EXIT.OK)
|
package/tools/smoke.mjs
CHANGED
|
@@ -26,6 +26,7 @@ import { spawn, spawnSync, execFileSync } from 'node:child_process'
|
|
|
26
26
|
import fs from 'node:fs'
|
|
27
27
|
import path from 'node:path'
|
|
28
28
|
import { fileURLToPath } from 'node:url'
|
|
29
|
+
import { detect } from '../app/lib/agentcli.mjs'
|
|
29
30
|
|
|
30
31
|
const ROOT = path.resolve(path.dirname(fileURLToPath(import.meta.url)), '..')
|
|
31
32
|
const PORT = Number(process.env.AXMAP_SMOKE_PORT ?? 7912)
|
|
@@ -92,9 +93,16 @@ const OPENED = [
|
|
|
92
93
|
{ what: '온보딩이 닫혔다', re: /id="onboard" hidden/ },
|
|
93
94
|
]
|
|
94
95
|
|
|
96
|
+
// 검사 PC의 설치된 CLI를 선택한다. 특정 팀의 Claude 설치에 의존하지 않는다.
|
|
97
|
+
const agent = detect().find((a) => a.installed)
|
|
98
|
+
if (!agent) {
|
|
99
|
+
console.error('온보딩 이후 화면 검증에 필요한 AI CLI가 없습니다. Claude Code, agy 또는 Codex CLI를 설치하세요.')
|
|
100
|
+
process.exit(1)
|
|
101
|
+
}
|
|
102
|
+
|
|
95
103
|
const PAGES = [
|
|
96
104
|
{ path: '/', what: '처음 오는 사람', must: FIRST },
|
|
97
|
-
{ path:
|
|
105
|
+
{ path: `/?agent=${encodeURIComponent(agent.id)}`, what: '고르기를 끝낸 사람', must: OPENED },
|
|
98
106
|
]
|
|
99
107
|
|
|
100
108
|
async function waitUp(ms = 120_000) {
|