@rch4com/agent-setup 1.0.0 → 1.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/README.md +55 -49
- package/install.mjs +101 -83
- package/lib/args.mjs +311 -266
- package/lib/bootstrap/apply.mjs +118 -8
- package/lib/bootstrap/flow.mjs +28 -12
- package/lib/bootstrap/record.mjs +142 -0
- package/lib/bootstrap/text.mjs +19 -0
- package/lib/status.mjs +92 -0
- package/lib/update.mjs +85 -0
- package/package.json +1 -1
- package/lib/items/.omc/state/sessions/a859aff7-59d9-4ff6-afc6-8cafe4471dc4/pre-tool-advisory-throttle.json +0 -14
package/README.md
CHANGED
|
@@ -1,49 +1,55 @@
|
|
|
1
|
-
# agent-setup
|
|
2
|
-
|
|
3
|
-
Claude Code, Codex, Gemini CLI, OpenCode, Kilo Code, Kiro, Kimi Code,
|
|
4
|
-
Grok Build, Antigravity, GitHub Copilot CLI, VS Code Copilot을 **한 저장소에서
|
|
5
|
-
함께** 쓰기 위한 저장소 범위 부트스트랩과 선택 항목 설치기입니다.
|
|
6
|
-
|
|
7
|
-
공통 지침은 루트 `AGENTS.md` 하나, 공통 스킬은 `.agents/skills` 하나로 두고,
|
|
8
|
-
도구별 설정 파일만 각 도구가 읽는 위치에 만듭니다.
|
|
9
|
-
|
|
10
|
-
## 사용법
|
|
11
|
-
|
|
12
|
-
Git 저장소 안에서 실행합니다.
|
|
13
|
-
|
|
14
|
-
```bash
|
|
15
|
-
# 배선 — 공통 지침·스킬·도구별 설정 파일을 만든다
|
|
16
|
-
npx @rch4com/agent-setup bootstrap
|
|
17
|
-
|
|
18
|
-
# 무엇이 만들어질지만 확인한다
|
|
19
|
-
npx @rch4com/agent-setup bootstrap --dry-run
|
|
20
|
-
|
|
21
|
-
# 플러그인·MCP·스킬·DESIGN.md를 골라 설치하는 대화형 화면
|
|
22
|
-
npx @rch4com/agent-setup
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
1
|
+
# agent-setup
|
|
2
|
+
|
|
3
|
+
Claude Code, Codex, Gemini CLI, OpenCode, Kilo Code, Kiro, Kimi Code,
|
|
4
|
+
Grok Build, Antigravity, GitHub Copilot CLI, VS Code Copilot을 **한 저장소에서
|
|
5
|
+
함께** 쓰기 위한 저장소 범위 부트스트랩과 선택 항목 설치기입니다.
|
|
6
|
+
|
|
7
|
+
공통 지침은 루트 `AGENTS.md` 하나, 공통 스킬은 `.agents/skills` 하나로 두고,
|
|
8
|
+
도구별 설정 파일만 각 도구가 읽는 위치에 만듭니다.
|
|
9
|
+
|
|
10
|
+
## 사용법
|
|
11
|
+
|
|
12
|
+
Git 저장소 안에서 실행합니다.
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
# 배선 — 공통 지침·스킬·도구별 설정 파일을 만든다
|
|
16
|
+
npx @rch4com/agent-setup bootstrap
|
|
17
|
+
|
|
18
|
+
# 무엇이 만들어질지만 확인한다
|
|
19
|
+
npx @rch4com/agent-setup bootstrap --dry-run
|
|
20
|
+
|
|
21
|
+
# 플러그인·MCP·스킬·DESIGN.md를 골라 설치하는 대화형 화면
|
|
22
|
+
npx @rch4com/agent-setup
|
|
23
|
+
|
|
24
|
+
# 관리 파일을 최신 템플릿으로 갱신한다 (사용자가 고친 파일은 건드리지 않는다)
|
|
25
|
+
npx @rch4com/agent-setup@latest update
|
|
26
|
+
|
|
27
|
+
# 의도 / 실제 / 버전을 비교한다
|
|
28
|
+
npx @rch4com/agent-setup status
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
설치하면 실행 명령은 `agent-setup`으로 짧아집니다. 이름에 스코프가 붙은 것은
|
|
32
|
+
스코프 없는 `agent-setup`이 npm의 유사 이름 제한에 걸리기 때문입니다.
|
|
33
|
+
|
|
34
|
+
## 안전 원칙
|
|
35
|
+
|
|
36
|
+
- 반드시 Git 저장소 안에서만 실행되며, 저장소 루트 밖에는 쓰지 않습니다.
|
|
37
|
+
- 홈 디렉터리의 글로벌 설정을 읽거나 수정하지 않습니다.
|
|
38
|
+
- 기존 설정 파일을 덮어쓰지 않습니다.
|
|
39
|
+
- 반복 실행할 수 있습니다.
|
|
40
|
+
|
|
41
|
+
## 요구 사항
|
|
42
|
+
|
|
43
|
+
Node.js 20 이상.
|
|
44
|
+
|
|
45
|
+
## 문서
|
|
46
|
+
|
|
47
|
+
생성되는 구조, 도구별 연결 방식, 설치 가능한 항목, DESIGN.md 라이브러리 등
|
|
48
|
+
상세 문서는 GitHub에 있습니다.
|
|
49
|
+
|
|
50
|
+
- [사용법과 생성 구조](https://github.com/rch4com/Agent-Setup/blob/main/AgentSetup-README.md)
|
|
51
|
+
- [저장소](https://github.com/rch4com/Agent-Setup)
|
|
52
|
+
|
|
53
|
+
## 라이선스
|
|
54
|
+
|
|
55
|
+
MIT
|
package/install.mjs
CHANGED
|
@@ -1,83 +1,101 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import { findRepoRoot } from './lib/context.mjs'
|
|
3
|
-
import { runBootstrap } from './lib/bootstrap/flow.mjs'
|
|
4
|
-
import { withDeps } from './lib/deps.mjs'
|
|
5
|
-
import {
|
|
6
|
-
BOOTSTRAP_USAGE, DESIGN_USAGE, ROOT_USAGE,
|
|
7
|
-
parseBootstrapArgs, parseDesignArgs, parseRootArgs,
|
|
8
|
-
} from './lib/args.mjs'
|
|
9
|
-
|
|
10
|
-
// 위 정적 import는 전부 의존성 없는 모듈이다 — 부트스트랩은 npm install 없이 돌아야 하고,
|
|
11
|
-
// bootstrap.isolation.test.mjs가 이 불변식을 지킨다. lib/ 나머지는 동적 import로만 닿는다.
|
|
12
|
-
|
|
13
|
-
const STATUS_LABEL = { installed: '설치됨', partial: '일부 설치됨', absent: '미설치' }
|
|
14
|
-
const ACTION_LABEL = { install: '설치', complete: '보완 설치', uninstall: '제거' }
|
|
15
|
-
|
|
16
|
-
// 대화형 화면은 TUI가 맡는다.
|
|
17
|
-
async function openTui(root, { dryRun, skillMode, designDirs }) {
|
|
18
|
-
const { runTui } = await withDeps(() => import('./lib/tui/run.mjs'))
|
|
19
|
-
// skillMode는 화면 안의 '부트스트랩 실행' 작업이 쓴다. design 경로에서는
|
|
20
|
-
// 없으므로 undefined가 넘어가고 runTui의 기본값(auto)이 적용된다.
|
|
21
|
-
await runTui(root, { dryRun, skillMode, designDirs })
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
// --list / --set 전용 비대화형 경로.
|
|
25
|
-
async function runClassic(root, { dryRun, listOnly, setArg }) {
|
|
26
|
-
const { loadItems } = await withDeps(() => import('./lib/catalog.mjs'))
|
|
27
|
-
const { scan, planChanges, apply } = await withDeps(() => import('./lib/engine.mjs'))
|
|
28
|
-
const items = await loadItems()
|
|
29
|
-
const states = await scan(root, items)
|
|
30
|
-
|
|
31
|
-
if (listOnly) {
|
|
32
|
-
for (const s of states) console.log(`${STATUS_LABEL[s.status].padEnd(7)} ${s.item.id} — ${s.item.label}${s.detail ? ` (${s.detail})` : ''}`)
|
|
33
|
-
return
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
const selectedIds = new Set(setArg.split(',').map((s) => s.trim()).filter(Boolean))
|
|
37
|
-
const known = new Set(items.map((i) => i.id))
|
|
38
|
-
for (const id of selectedIds) if (!known.has(id)) throw new Error(`알 수 없는 항목: ${id}`)
|
|
39
|
-
|
|
40
|
-
const changes = planChanges(states, selectedIds)
|
|
41
|
-
if (changes.length === 0) { console.log('변경할 항목이 없습니다.'); return }
|
|
42
|
-
|
|
43
|
-
const results = await apply(root, changes, { dryRun })
|
|
44
|
-
for (const r of results) {
|
|
45
|
-
console.log(`${r.ok ? '✔' : '✖'} ${ACTION_LABEL[r.action]} ${r.item.label}${r.message ? ` — ${r.message}` : ''}`)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
const after = await scan(root, items)
|
|
49
|
-
console.log('\n최종 상태:')
|
|
50
|
-
for (const s of after) console.log(` ${STATUS_LABEL[s.status].padEnd(7)} ${s.item.label}`)
|
|
51
|
-
console.log('\n설정 파일 변경 내용은 git diff로 확인할 수 있습니다.')
|
|
52
|
-
if (results.some((r) => !r.ok)) process.exitCode = 1
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
async function main() {
|
|
56
|
-
const argv = process.argv.slice(2)
|
|
57
|
-
const root = findRepoRoot()
|
|
58
|
-
|
|
59
|
-
if (argv[0] === 'bootstrap') {
|
|
60
|
-
const opts = parseBootstrapArgs(argv.slice(1))
|
|
61
|
-
if (opts.help) { console.log(BOOTSTRAP_USAGE); return }
|
|
62
|
-
const { failed } = runBootstrap(root, opts)
|
|
63
|
-
if (failed.length > 0) process.exitCode = 1
|
|
64
|
-
return
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
if (argv[0] === '
|
|
68
|
-
const opts =
|
|
69
|
-
if (opts.help) { console.log(
|
|
70
|
-
//
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import { findRepoRoot } from './lib/context.mjs'
|
|
3
|
+
import { runBootstrap } from './lib/bootstrap/flow.mjs'
|
|
4
|
+
import { withDeps } from './lib/deps.mjs'
|
|
5
|
+
import {
|
|
6
|
+
BOOTSTRAP_USAGE, DESIGN_USAGE, ROOT_USAGE, STATUS_USAGE, UPDATE_USAGE,
|
|
7
|
+
parseBootstrapArgs, parseDesignArgs, parseRootArgs, parseStatusArgs, parseUpdateArgs,
|
|
8
|
+
} from './lib/args.mjs'
|
|
9
|
+
|
|
10
|
+
// 위 정적 import는 전부 의존성 없는 모듈이다 — 부트스트랩은 npm install 없이 돌아야 하고,
|
|
11
|
+
// bootstrap.isolation.test.mjs가 이 불변식을 지킨다. lib/ 나머지는 동적 import로만 닿는다.
|
|
12
|
+
|
|
13
|
+
const STATUS_LABEL = { installed: '설치됨', partial: '일부 설치됨', absent: '미설치' }
|
|
14
|
+
const ACTION_LABEL = { install: '설치', complete: '보완 설치', uninstall: '제거' }
|
|
15
|
+
|
|
16
|
+
// 대화형 화면은 TUI가 맡는다.
|
|
17
|
+
async function openTui(root, { dryRun, skillMode, designDirs }) {
|
|
18
|
+
const { runTui } = await withDeps(() => import('./lib/tui/run.mjs'))
|
|
19
|
+
// skillMode는 화면 안의 '부트스트랩 실행' 작업이 쓴다. design 경로에서는
|
|
20
|
+
// 없으므로 undefined가 넘어가고 runTui의 기본값(auto)이 적용된다.
|
|
21
|
+
await runTui(root, { dryRun, skillMode, designDirs })
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// --list / --set 전용 비대화형 경로.
|
|
25
|
+
async function runClassic(root, { dryRun, listOnly, setArg }) {
|
|
26
|
+
const { loadItems } = await withDeps(() => import('./lib/catalog.mjs'))
|
|
27
|
+
const { scan, planChanges, apply } = await withDeps(() => import('./lib/engine.mjs'))
|
|
28
|
+
const items = await loadItems()
|
|
29
|
+
const states = await scan(root, items)
|
|
30
|
+
|
|
31
|
+
if (listOnly) {
|
|
32
|
+
for (const s of states) console.log(`${STATUS_LABEL[s.status].padEnd(7)} ${s.item.id} — ${s.item.label}${s.detail ? ` (${s.detail})` : ''}`)
|
|
33
|
+
return
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
const selectedIds = new Set(setArg.split(',').map((s) => s.trim()).filter(Boolean))
|
|
37
|
+
const known = new Set(items.map((i) => i.id))
|
|
38
|
+
for (const id of selectedIds) if (!known.has(id)) throw new Error(`알 수 없는 항목: ${id}`)
|
|
39
|
+
|
|
40
|
+
const changes = planChanges(states, selectedIds)
|
|
41
|
+
if (changes.length === 0) { console.log('변경할 항목이 없습니다.'); return }
|
|
42
|
+
|
|
43
|
+
const results = await apply(root, changes, { dryRun })
|
|
44
|
+
for (const r of results) {
|
|
45
|
+
console.log(`${r.ok ? '✔' : '✖'} ${ACTION_LABEL[r.action]} ${r.item.label}${r.message ? ` — ${r.message}` : ''}`)
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const after = await scan(root, items)
|
|
49
|
+
console.log('\n최종 상태:')
|
|
50
|
+
for (const s of after) console.log(` ${STATUS_LABEL[s.status].padEnd(7)} ${s.item.label}`)
|
|
51
|
+
console.log('\n설정 파일 변경 내용은 git diff로 확인할 수 있습니다.')
|
|
52
|
+
if (results.some((r) => !r.ok)) process.exitCode = 1
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async function main() {
|
|
56
|
+
const argv = process.argv.slice(2)
|
|
57
|
+
const root = findRepoRoot()
|
|
58
|
+
|
|
59
|
+
if (argv[0] === 'bootstrap') {
|
|
60
|
+
const opts = parseBootstrapArgs(argv.slice(1))
|
|
61
|
+
if (opts.help) { console.log(BOOTSTRAP_USAGE); return }
|
|
62
|
+
const { failed } = runBootstrap(root, opts)
|
|
63
|
+
if (failed.length > 0) process.exitCode = 1
|
|
64
|
+
return
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
if (argv[0] === 'update') {
|
|
68
|
+
const opts = parseUpdateArgs(argv.slice(1))
|
|
69
|
+
if (opts.help) { console.log(UPDATE_USAGE); return }
|
|
70
|
+
// 항목 수렴을 위해 스캔이 필요해 의존성 있는 모듈에 닿는다.
|
|
71
|
+
const { runUpdate } = await withDeps(() => import('./lib/update.mjs'))
|
|
72
|
+
await runUpdate(root, opts)
|
|
73
|
+
return
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
if (argv[0] === 'status') {
|
|
77
|
+
const opts = parseStatusArgs(argv.slice(1))
|
|
78
|
+
if (opts.help) { console.log(STATUS_USAGE); return }
|
|
79
|
+
// 항목 스캔이 필요해 의존성 있는 모듈에 닿는다.
|
|
80
|
+
const { runStatus } = await withDeps(() => import('./lib/status.mjs'))
|
|
81
|
+
await runStatus(root, opts)
|
|
82
|
+
return
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (argv[0] === 'design') {
|
|
86
|
+
const opts = parseDesignArgs(argv.slice(1))
|
|
87
|
+
if (opts.help) { console.log(DESIGN_USAGE); return }
|
|
88
|
+
// 플래그 없는 `design`은 통합 화면을 연다 — DESIGN.MD 섹션이 그 안에 있다.
|
|
89
|
+
if (opts.interactive) return openTui(root, opts)
|
|
90
|
+
const { runDesign } = await withDeps(() => import('./lib/design-md/flow.mjs'))
|
|
91
|
+
await runDesign(root, opts)
|
|
92
|
+
return
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const opts = parseRootArgs(argv)
|
|
96
|
+
if (opts.help) { console.log(ROOT_USAGE); return }
|
|
97
|
+
if (opts.interactive) return openTui(root, opts)
|
|
98
|
+
await runClassic(root, opts)
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
main().catch((err) => { console.error(err.message); process.exit(1) })
|